@morscherlab/mint-sdk 1.0.0-beta.6 → 1.0.0-rc.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/__tests__/composables/useProtocolTemplates.test.d.ts +1 -0
- package/dist/__tests__/stores/settings.test.d.ts +1 -0
- package/dist/{auth-QQj2kkze.js → auth-CBG3bWEc.js} +50 -20
- package/dist/auth-CBG3bWEc.js.map +1 -0
- package/dist/components/SettingsModal.vue.d.ts +5 -0
- package/dist/components/index.js +2 -2
- package/dist/{components-DihbSJjU.js → components-5KSfsVqf.js} +49 -29
- package/dist/components-5KSfsVqf.js.map +1 -0
- package/dist/composables/index.js +3 -3
- package/dist/{composables-BcgZ6diz.js → composables-D4Myb30a.js} +3 -3
- package/dist/{composables-BcgZ6diz.js.map → composables-D4Myb30a.js.map} +1 -1
- package/dist/index.js +5 -5
- package/dist/install.js +2 -2
- package/dist/stores/index.js +1 -1
- package/dist/styles.css +16 -0
- package/dist/templates/index.js +1 -1
- package/dist/{templates-Cyt0Suwf.js → templates-BSlxwV2c.js} +12 -8
- package/dist/templates-BSlxwV2c.js.map +1 -0
- package/dist/{useExperimentData-CM6Y0u5L.js → useExperimentData-BbbdI5xT.js} +97 -25
- package/dist/useExperimentData-BbbdI5xT.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/components/GroupAssigner.test.ts +18 -0
- package/src/__tests__/composables/useApi.test.ts +45 -0
- package/src/__tests__/composables/useAuth.test.ts +20 -0
- package/src/__tests__/composables/useProtocolTemplates.test.ts +64 -0
- package/src/__tests__/stores/settings.test.ts +78 -0
- package/src/components/AppAvatarMenu.vue +6 -3
- package/src/components/AppTopBar.vue +15 -10
- package/src/components/AuditTrail.vue +1 -1
- package/src/components/Calendar.vue +6 -2
- package/src/components/ConcentrationInput.vue +3 -2
- package/src/components/GroupAssigner.vue +8 -3
- package/src/components/NumberInput.vue +5 -3
- package/src/components/SampleHierarchyTree.vue +3 -2
- package/src/components/SettingsModal.vue +7 -0
- package/src/components/UnitInput.vue +6 -2
- package/src/components/WellPlate.vue +3 -3
- package/src/composables/useApi.ts +113 -16
- package/src/composables/useAutoGroup.ts +13 -8
- package/src/composables/useProtocolTemplates.ts +13 -1
- package/src/composables/useRackEditor.ts +3 -2
- package/src/stores/auth.ts +48 -23
- package/src/stores/settings.ts +10 -0
- package/src/styles/components/settings-modal.css +9 -0
- package/dist/auth-QQj2kkze.js.map +0 -1
- package/dist/components-DihbSJjU.js.map +0 -1
- package/dist/templates-Cyt0Suwf.js.map +0 -1
- package/dist/useExperimentData-CM6Y0u5L.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components-5KSfsVqf.js","names":["$slots","$slots","$slots","$attrs","$slots","$slots","$slots","$slots","computePosition","flip","offset","shift","getComputedStyle","$slots","$slots"],"sources":["../src/components/BaseButton.vue","../src/components/BaseButton.vue","../src/components/ColorSlider.vue","../src/components/ColorSlider.vue","../src/utils/items.ts","../src/components/BaseTabs.vue","../src/components/BaseTabs.vue","../src/components/BaseModal.vue","../src/components/BaseModal.vue","../src/components/SegmentedControl.vue","../src/components/SegmentedControl.vue","../src/components/BasePill.vue","../src/components/BasePill.vue","../src/components/DropdownButton.vue","../src/components/DropdownButton.vue","../src/components/Calendar.vue","../src/components/Calendar.vue","../src/components/DataFrame.vue","../src/components/DataFrame.vue","../src/components/FormField.vue","../src/components/FormField.vue","../src/components/AlertBox.vue","../src/components/AlertBox.vue","../src/components/AppToastContainer.vue","../src/components/AppToastContainer.vue","../src/components/IconButton.vue","../src/components/IconButton.vue","../src/components/ThemeToggle.vue","../src/components/ThemeToggle.vue","../src/components/CollapsibleCard.vue","../src/components/CollapsibleCard.vue","../src/components/internal/FormFieldRendererInternal.vue","../src/components/internal/FormFieldRendererInternal.vue","../src/utils/formModelSync.ts","../src/components/SettingsModal.vue","../src/components/SettingsModal.vue","../src/components/ConfirmDialog.vue","../src/components/ConfirmDialog.vue","../src/components/ExperimentPopover.vue","../src/components/ExperimentPopover.vue","../src/components/Skeleton.vue","../src/components/Skeleton.vue","../src/components/EmptyState.vue","../src/components/EmptyState.vue","../src/components/ExperimentCodeBadge.vue","../src/components/ExperimentCodeBadge.vue","../src/components/ExperimentSelectorModal.vue","../src/components/ExperimentSelectorModal.vue","../src/utils/pluginIcon.ts","../src/components/internal/ActionItemInternal.vue","../src/components/internal/ActionItemInternal.vue","../src/components/PluginIcon.vue","../src/components/PluginIcon.vue","../src/components/internal/AppTopBarPageSelectorInternal.vue","../src/components/internal/AppTopBarPageSelectorInternal.vue","../src/components/internal/AppTopBarPillNavInternal.vue","../src/components/internal/AppTopBarPillNavInternal.vue","../src/components/AppAvatarMenu.vue","../src/components/AppAvatarMenu.vue","../src/components/AppPluginSwitcher.vue","../src/components/AppPluginSwitcher.vue","../src/components/AppTopBar.vue","../src/components/AppTopBar.vue","../src/components/StepWizard.vue","../src/components/StepWizard.vue","../src/components/internal/FormSectionRenderer.vue","../src/components/internal/FormSectionRenderer.vue","../src/components/FormActions.vue","../src/components/FormActions.vue","../src/components/FormBuilder.vue","../src/components/FormBuilder.vue","../src/components/AppSidebar.vue","../src/components/AppSidebar.vue","../src/components/AppLayout.vue","../src/components/AppLayout.vue","../src/components/PluginWorkspaceView.vue","../src/components/PluginWorkspaceView.vue","../src/components/DoseCalculator.vue","../src/components/DoseCalculator.vue","../src/components/ExperimentTimeline.vue","../src/components/ExperimentTimeline.vue","../src/components/internal/WellEditPopupInternal.vue","../src/components/internal/WellEditPopupInternal.vue","../src/components/WellPlate.vue","../src/components/WellPlate.vue","../src/components/SampleLegend.vue","../src/components/SampleLegend.vue","../src/components/PlateMapEditor.vue","../src/components/PlateMapEditor.vue","../src/components/ReagentList.vue","../src/components/ReagentList.vue","../src/components/LoadingSpinner.vue","../src/components/LoadingSpinner.vue","../src/components/AutoGroupModal.vue","../src/components/AutoGroupModal.vue","../src/components/SampleSelector.vue","../src/components/SampleSelector.vue","../src/components/ScheduleCalendar.vue","../src/components/ScheduleCalendar.vue","../src/components/ComponentBindingRenderer.vue","../src/components/ComponentBindingRenderer.vue","../src/components/ControlWorkspaceView.vue","../src/components/ControlWorkspaceView.vue","../src/components/DoseDesignWorkspaceView.vue","../src/components/DoseDesignWorkspaceView.vue","../src/components/AppContainer.vue","../src/components/AppContainer.vue","../src/components/Divider.vue","../src/components/Divider.vue","../src/components/StatusIndicator.vue","../src/components/StatusIndicator.vue","../src/components/ProgressBar.vue","../src/components/ProgressBar.vue","../src/components/Avatar.vue","../src/components/Avatar.vue","../src/components/Breadcrumb.vue","../src/components/Breadcrumb.vue","../../../node_modules/.bun/@floating-ui+utils@0.2.11/node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs","../../../node_modules/.bun/@floating-ui+core@1.7.5/node_modules/@floating-ui/core/dist/floating-ui.core.mjs","../../../node_modules/.bun/@floating-ui+utils@0.2.11/node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.mjs","../../../node_modules/.bun/@floating-ui+dom@1.7.6/node_modules/@floating-ui/dom/dist/floating-ui.dom.mjs","../src/components/Tooltip.vue","../src/components/Tooltip.vue","../src/components/ChartContainer.vue","../src/components/ChartContainer.vue","../src/components/BioTemplateRenderer.vue","../src/components/BioTemplateRenderer.vue","../src/components/BioTemplateExperimentWorkspaceView.vue","../src/components/BioTemplateExperimentWorkspaceView.vue","../src/components/BioTemplatePackWorkspaceView.vue","../src/components/BioTemplatePackWorkspaceView.vue","../src/components/BioTemplatePresetWorkspaceView.vue","../src/components/BioTemplatePresetWorkspaceView.vue","../src/components/RackEditor.vue","../src/components/RackEditor.vue","../src/components/GroupAssigner.vue","../src/components/GroupAssigner.vue","../src/components/ReagentEditor.vue","../src/components/ReagentEditor.vue","../src/components/SampleHierarchyTree.vue","../src/components/SampleHierarchyTree.vue","../src/components/ProtocolStepEditor.vue","../src/components/ProtocolStepEditor.vue","../src/components/ScientificNumber.vue","../src/components/ScientificNumber.vue","../src/components/ChemicalFormula.vue","../src/components/ChemicalFormula.vue","../src/components/AuditTrail.vue","../src/components/AuditTrail.vue","../src/components/BatchProgressList.vue","../src/components/BatchProgressList.vue","../src/components/ExperimentDataViewer.vue","../src/components/ExperimentDataViewer.vue","../src/components/TimeRangeInput.vue","../src/components/TimeRangeInput.vue","../src/components/ResourceCard.vue","../src/components/ResourceCard.vue","../src/components/FitPanel.vue","../src/components/FitPanel.vue","../src/components/index.ts"],"sourcesContent":["<script setup lang=\"ts\">\n/** Renders a styled `<button>` with variant, size, loading spinner, and full-width options. */\nimport type { ButtonVariant, ButtonSize } from '../types'\n\ninterface Props {\n variant?: ButtonVariant\n size?: ButtonSize\n disabled?: boolean\n loading?: boolean\n type?: 'button' | 'submit' | 'reset'\n fullWidth?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'primary',\n size: 'md',\n disabled: false,\n loading: false,\n type: 'button',\n fullWidth: false,\n})\n\nconst emit = defineEmits<{\n click: [event: MouseEvent]\n}>()\n\nfunction handleClick(event: MouseEvent) {\n if (!props.disabled && !props.loading) {\n emit('click', event)\n }\n}\n</script>\n\n<template>\n <button\n :type=\"type\"\n :disabled=\"disabled || loading\"\n :class=\"[\n 'mint-button',\n `mint-button--${variant}`,\n `mint-button--${size}`,\n fullWidth ? 'mint-button--full-width' : '',\n (disabled || loading) ? 'mint-button--disabled' : '',\n ]\"\n @click=\"handleClick\"\n >\n <svg\n v-if=\"loading\"\n class=\"mint-button__spinner\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n style=\"opacity: 0.25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n />\n <path\n style=\"opacity: 0.75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n <slot />\n </button>\n</template>\n\n<style>\n@import '../styles/components/button.css';\n</style>\n","<script setup lang=\"ts\">\n/** Renders a styled `<button>` with variant, size, loading spinner, and full-width options. */\nimport type { ButtonVariant, ButtonSize } from '../types'\n\ninterface Props {\n variant?: ButtonVariant\n size?: ButtonSize\n disabled?: boolean\n loading?: boolean\n type?: 'button' | 'submit' | 'reset'\n fullWidth?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'primary',\n size: 'md',\n disabled: false,\n loading: false,\n type: 'button',\n fullWidth: false,\n})\n\nconst emit = defineEmits<{\n click: [event: MouseEvent]\n}>()\n\nfunction handleClick(event: MouseEvent) {\n if (!props.disabled && !props.loading) {\n emit('click', event)\n }\n}\n</script>\n\n<template>\n <button\n :type=\"type\"\n :disabled=\"disabled || loading\"\n :class=\"[\n 'mint-button',\n `mint-button--${variant}`,\n `mint-button--${size}`,\n fullWidth ? 'mint-button--full-width' : '',\n (disabled || loading) ? 'mint-button--disabled' : '',\n ]\"\n @click=\"handleClick\"\n >\n <svg\n v-if=\"loading\"\n class=\"mint-button__spinner\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n style=\"opacity: 0.25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n />\n <path\n style=\"opacity: 0.75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n <slot />\n </button>\n</template>\n\n<style>\n@import '../styles/components/button.css';\n</style>\n","<script setup lang=\"ts\">\n/** Range slider with a gradient color track and color-coded value badge for threshold visualization. */\nimport { computed } from 'vue'\n\ninterface ColorStop {\n value: number\n color: string\n}\n\ninterface Props {\n modelValue?: number\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n showValue?: boolean\n showLabels?: boolean\n minLabel?: string\n maxLabel?: string\n size?: 'sm' | 'md' | 'lg'\n /** Color stops for the gradient. Default: green (low) → yellow (mid) → red (high) */\n colorStops?: ColorStop[]\n /** Thresholds for value badge color. Default: green ≤ threshold1, yellow ≤ threshold2, red above */\n thresholds?: [number, number]\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n min: 0,\n max: 100,\n step: 1,\n disabled: false,\n showValue: true,\n showLabels: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number]\n}>()\n\n// Default color stops: green at start, yellow at 30%, red at end\nconst defaultColorStops: ColorStop[] = [\n { value: 0, color: '#22c55e' },\n { value: 30, color: '#eab308' },\n { value: 100, color: '#ef4444' },\n]\n\n// Default thresholds: green ≤ 10%, yellow ≤ 30%, red above\nconst defaultThresholds: [number, number] = [10, 30]\n\nconst colorStops = computed(() => props.colorStops ?? defaultColorStops)\nconst thresholds = computed(() => props.thresholds ?? defaultThresholds)\n\nconst sizeConfig = computed(() => {\n switch (props.size) {\n case 'sm': return { thumb: 16, badgeSize: '24px', fontSize: '11px', labelFontSize: '0.625rem' }\n case 'lg': return { thumb: 22, badgeSize: '32px', fontSize: '14px', labelFontSize: '0.75rem' }\n default: return { thumb: 18, badgeSize: '28px', fontSize: '12px', labelFontSize: '0.625rem' }\n }\n})\n\nconst currentValue = computed(() => props.modelValue ?? props.min)\n\nconst percentage = computed(() => {\n const range = props.max - props.min\n if (range === 0) return 0\n return ((currentValue.value - props.min) / range) * 100\n})\n\n// Generate CSS gradient from color stops\nconst gradientStyle = computed(() => {\n const stops = colorStops.value\n .map(stop => `${stop.color} ${stop.value}%`)\n .join(', ')\n return `linear-gradient(to right, ${stops})`\n})\n\n// Value badge style based on thresholds\nconst valueBadgeStyle = computed(() => {\n const pct = percentage.value\n const [t1, t2] = thresholds.value\n\n if (pct <= t1) {\n return {\n backgroundColor: 'rgba(34, 197, 94, 0.15)',\n color: '#22c55e',\n border: '1px solid rgba(34, 197, 94, 0.3)'\n }\n }\n if (pct <= t2) {\n return {\n backgroundColor: 'rgba(234, 179, 8, 0.15)',\n color: '#eab308',\n border: '1px solid rgba(234, 179, 8, 0.3)'\n }\n }\n return {\n backgroundColor: 'rgba(239, 68, 68, 0.15)',\n color: '#ef4444',\n border: '1px solid rgba(239, 68, 68, 0.3)'\n }\n})\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n emit('update:modelValue', Number(target.value))\n}\n</script>\n\n<template>\n <div :class=\"['mint-color-slider', { 'mint-color-slider--disabled': disabled }]\">\n <div class=\"mint-color-slider__row\">\n <!-- Slider track with gradient -->\n <div class=\"mint-color-slider__track\">\n <input\n type=\"range\"\n :value=\"currentValue\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"disabled\"\n :aria-label=\"`Value: ${currentValue}`\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-valuenow=\"currentValue\"\n class=\"color-slider mint-color-slider__input\"\n :style=\"{\n '--slider-gradient': gradientStyle,\n '--thumb-size': `${sizeConfig.thumb}px`,\n }\"\n @input=\"handleInput\"\n />\n </div>\n\n <!-- Value badge -->\n <div\n v-if=\"showValue\"\n class=\"mint-color-slider__badge\"\n :style=\"{\n ...valueBadgeStyle,\n minWidth: sizeConfig.badgeSize,\n height: sizeConfig.badgeSize,\n fontSize: sizeConfig.fontSize,\n }\"\n >\n {{ currentValue }}\n </div>\n </div>\n\n <!-- Min/Max labels -->\n <div\n v-if=\"showLabels\"\n class=\"mint-color-slider__labels\"\n :style=\"{ fontSize: sizeConfig.labelFontSize }\"\n >\n <span>{{ minLabel ?? min }}</span>\n <span>{{ maxLabel ?? max }}</span>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/color-slider.css';\n</style>\n","<script setup lang=\"ts\">\n/** Range slider with a gradient color track and color-coded value badge for threshold visualization. */\nimport { computed } from 'vue'\n\ninterface ColorStop {\n value: number\n color: string\n}\n\ninterface Props {\n modelValue?: number\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n showValue?: boolean\n showLabels?: boolean\n minLabel?: string\n maxLabel?: string\n size?: 'sm' | 'md' | 'lg'\n /** Color stops for the gradient. Default: green (low) → yellow (mid) → red (high) */\n colorStops?: ColorStop[]\n /** Thresholds for value badge color. Default: green ≤ threshold1, yellow ≤ threshold2, red above */\n thresholds?: [number, number]\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n min: 0,\n max: 100,\n step: 1,\n disabled: false,\n showValue: true,\n showLabels: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number]\n}>()\n\n// Default color stops: green at start, yellow at 30%, red at end\nconst defaultColorStops: ColorStop[] = [\n { value: 0, color: '#22c55e' },\n { value: 30, color: '#eab308' },\n { value: 100, color: '#ef4444' },\n]\n\n// Default thresholds: green ≤ 10%, yellow ≤ 30%, red above\nconst defaultThresholds: [number, number] = [10, 30]\n\nconst colorStops = computed(() => props.colorStops ?? defaultColorStops)\nconst thresholds = computed(() => props.thresholds ?? defaultThresholds)\n\nconst sizeConfig = computed(() => {\n switch (props.size) {\n case 'sm': return { thumb: 16, badgeSize: '24px', fontSize: '11px', labelFontSize: '0.625rem' }\n case 'lg': return { thumb: 22, badgeSize: '32px', fontSize: '14px', labelFontSize: '0.75rem' }\n default: return { thumb: 18, badgeSize: '28px', fontSize: '12px', labelFontSize: '0.625rem' }\n }\n})\n\nconst currentValue = computed(() => props.modelValue ?? props.min)\n\nconst percentage = computed(() => {\n const range = props.max - props.min\n if (range === 0) return 0\n return ((currentValue.value - props.min) / range) * 100\n})\n\n// Generate CSS gradient from color stops\nconst gradientStyle = computed(() => {\n const stops = colorStops.value\n .map(stop => `${stop.color} ${stop.value}%`)\n .join(', ')\n return `linear-gradient(to right, ${stops})`\n})\n\n// Value badge style based on thresholds\nconst valueBadgeStyle = computed(() => {\n const pct = percentage.value\n const [t1, t2] = thresholds.value\n\n if (pct <= t1) {\n return {\n backgroundColor: 'rgba(34, 197, 94, 0.15)',\n color: '#22c55e',\n border: '1px solid rgba(34, 197, 94, 0.3)'\n }\n }\n if (pct <= t2) {\n return {\n backgroundColor: 'rgba(234, 179, 8, 0.15)',\n color: '#eab308',\n border: '1px solid rgba(234, 179, 8, 0.3)'\n }\n }\n return {\n backgroundColor: 'rgba(239, 68, 68, 0.15)',\n color: '#ef4444',\n border: '1px solid rgba(239, 68, 68, 0.3)'\n }\n})\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n emit('update:modelValue', Number(target.value))\n}\n</script>\n\n<template>\n <div :class=\"['mint-color-slider', { 'mint-color-slider--disabled': disabled }]\">\n <div class=\"mint-color-slider__row\">\n <!-- Slider track with gradient -->\n <div class=\"mint-color-slider__track\">\n <input\n type=\"range\"\n :value=\"currentValue\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"disabled\"\n :aria-label=\"`Value: ${currentValue}`\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-valuenow=\"currentValue\"\n class=\"color-slider mint-color-slider__input\"\n :style=\"{\n '--slider-gradient': gradientStyle,\n '--thumb-size': `${sizeConfig.thumb}px`,\n }\"\n @input=\"handleInput\"\n />\n </div>\n\n <!-- Value badge -->\n <div\n v-if=\"showValue\"\n class=\"mint-color-slider__badge\"\n :style=\"{\n ...valueBadgeStyle,\n minWidth: sizeConfig.badgeSize,\n height: sizeConfig.badgeSize,\n fontSize: sizeConfig.fontSize,\n }\"\n >\n {{ currentValue }}\n </div>\n </div>\n\n <!-- Min/Max labels -->\n <div\n v-if=\"showLabels\"\n class=\"mint-color-slider__labels\"\n :style=\"{ fontSize: sizeConfig.labelFontSize }\"\n >\n <span>{{ minLabel ?? min }}</span>\n <span>{{ maxLabel ?? max }}</span>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/color-slider.css';\n</style>\n","export type ItemWithLabel = {\n label: string\n}\n\nexport type ItemWithId = ItemWithLabel & {\n id: string\n}\n\nexport function normalizeItemInput<TItem extends ItemWithId>(\n item: TItem | string,\n): TItem {\n if (typeof item === 'object') return item\n\n return {\n id: item,\n label: item,\n } as TItem\n}\n\nexport function normalizeLabelItemInput<TItem extends ItemWithLabel>(\n item: TItem | string,\n): TItem {\n if (typeof item === 'object') return item\n\n return {\n label: item,\n } as TItem\n}\n","<script setup lang=\"ts\">\n/** Renders a tab bar with underline or pills variant, badges, icons, and disabled tab support. */\nimport { computed } from 'vue'\nimport type { TabItem, TabItemInput } from '../types'\nimport { normalizeItemInput } from '../utils/items'\n\ninterface Props {\n modelValue: string\n tabs: TabItemInput[]\n variant?: 'underline' | 'pills'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'underline',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n}>()\n\nconst activeTab = computed(() => props.modelValue)\nconst normalizedTabs = computed<TabItem[]>(() => props.tabs.map(normalizeItemInput))\n\nfunction selectTab(tabId: string) {\n const tab = normalizedTabs.value.find(t => t.id === tabId)\n if (tab && !tab.disabled) {\n emit('update:modelValue', tabId)\n }\n}\n</script>\n\n<template>\n <div :class=\"['mint-tabs', `mint-tabs--${variant}`]\" role=\"tablist\">\n <button\n v-for=\"tab in normalizedTabs\"\n :key=\"tab.id\"\n type=\"button\"\n role=\"tab\"\n :aria-selected=\"activeTab === tab.id\"\n :aria-disabled=\"tab.disabled\"\n :class=\"[\n 'mint-tab',\n activeTab === tab.id ? 'mint-tab--active' : '',\n tab.disabled ? 'mint-tab--disabled' : '',\n ]\"\n @click=\"selectTab(tab.id)\"\n >\n <span class=\"mint-tab__content\">\n <span v-if=\"tab.icon\" class=\"mint-tab__icon\">{{ tab.icon }}</span>\n {{ tab.label }}\n <span v-if=\"tab.badge !== undefined\" class=\"mint-tab__badge\">\n {{ tab.badge }}\n </span>\n </span>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/tabs.css';\n</style>\n","<script setup lang=\"ts\">\n/** Renders a tab bar with underline or pills variant, badges, icons, and disabled tab support. */\nimport { computed } from 'vue'\nimport type { TabItem, TabItemInput } from '../types'\nimport { normalizeItemInput } from '../utils/items'\n\ninterface Props {\n modelValue: string\n tabs: TabItemInput[]\n variant?: 'underline' | 'pills'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'underline',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n}>()\n\nconst activeTab = computed(() => props.modelValue)\nconst normalizedTabs = computed<TabItem[]>(() => props.tabs.map(normalizeItemInput))\n\nfunction selectTab(tabId: string) {\n const tab = normalizedTabs.value.find(t => t.id === tabId)\n if (tab && !tab.disabled) {\n emit('update:modelValue', tabId)\n }\n}\n</script>\n\n<template>\n <div :class=\"['mint-tabs', `mint-tabs--${variant}`]\" role=\"tablist\">\n <button\n v-for=\"tab in normalizedTabs\"\n :key=\"tab.id\"\n type=\"button\"\n role=\"tab\"\n :aria-selected=\"activeTab === tab.id\"\n :aria-disabled=\"tab.disabled\"\n :class=\"[\n 'mint-tab',\n activeTab === tab.id ? 'mint-tab--active' : '',\n tab.disabled ? 'mint-tab--disabled' : '',\n ]\"\n @click=\"selectTab(tab.id)\"\n >\n <span class=\"mint-tab__content\">\n <span v-if=\"tab.icon\" class=\"mint-tab__icon\">{{ tab.icon }}</span>\n {{ tab.label }}\n <span v-if=\"tab.badge !== undefined\" class=\"mint-tab__badge\">\n {{ tab.badge }}\n </span>\n </span>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/tabs.css';\n</style>\n","<script setup lang=\"ts\">\n/** Modal dialog with backdrop, focus trap, Escape-to-close, and configurable size/variant. */\nimport { ref, watch, nextTick, onUnmounted } from 'vue'\nimport type { ModalSize, ModalVariant } from '../types'\nimport { useEventListener } from '../composables/useEventListener'\n\ninterface Props {\n modelValue: boolean\n title?: string\n subtitle?: string\n size?: ModalSize\n variant?: ModalVariant\n closable?: boolean\n closeOnOverlay?: boolean\n closeOnEscape?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n variant: 'centered',\n closable: true,\n closeOnOverlay: false,\n closeOnEscape: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n close: []\n}>()\n\nconst containerRef = ref<HTMLElement | null>(null)\nlet previouslyFocused: HTMLElement | null = null\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(', ')\n\nfunction getFocusableElements(): HTMLElement[] {\n if (!containerRef.value) return []\n return Array.from(containerRef.value.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR))\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape' && props.closeOnEscape && props.modelValue) {\n close()\n return\n }\n\n if (event.key !== 'Tab' || !containerRef.value) return\n\n const focusable = getFocusableElements()\n if (focusable.length === 0) {\n event.preventDefault()\n return\n }\n\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n\n if (event.shiftKey && document.activeElement === first) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault()\n first.focus()\n }\n}\n\nfunction close() {\n if (props.closable) {\n emit('update:modelValue', false)\n emit('close')\n }\n}\n\nfunction handleOverlayClick(event: MouseEvent) {\n if (props.closeOnOverlay && event.target === event.currentTarget) {\n close()\n }\n}\n\nwatch(() => props.modelValue, async (isOpen) => {\n if (isOpen) {\n previouslyFocused = document.activeElement as HTMLElement | null\n document.body.style.overflow = 'hidden'\n await nextTick()\n const focusable = getFocusableElements()\n if (focusable.length > 0) {\n focusable[0].focus()\n } else {\n containerRef.value?.focus()\n }\n } else {\n document.body.style.overflow = ''\n previouslyFocused?.focus()\n previouslyFocused = null\n }\n})\n\nuseEventListener(() => document, 'keydown', handleKeydown)\n\nonUnmounted(() => {\n document.body.style.overflow = ''\n})\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition :name=\"`modal-${variant}`\">\n <div\n v-if=\"modelValue\"\n :class=\"['mint-modal', `mint-modal--${variant}`]\"\n @click=\"handleOverlayClick\"\n >\n <!-- Overlay -->\n <div class=\"mint-modal__overlay\" />\n\n <!-- Modal -->\n <div\n ref=\"containerRef\"\n :class=\"[\n 'mint-modal__container',\n `mint-modal__container--${size}`,\n `mint-modal__container--${variant}`,\n ]\"\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n >\n <!-- Sheet grab-hint bar (sheet variant only) -->\n <div v-if=\"variant === 'sheet'\" class=\"mint-modal__grab-hint\" aria-hidden=\"true\" />\n\n <!-- Header -->\n <div v-if=\"title || subtitle || closable || $slots.header\" class=\"mint-modal__header\">\n <div v-if=\"title || subtitle || $slots.header\" class=\"mint-modal__header-text\">\n <slot name=\"header\">\n <h3 v-if=\"title\" class=\"mint-modal__title\">\n {{ title }}\n </h3>\n <p v-if=\"subtitle\" class=\"mint-modal__subtitle\">\n {{ subtitle }}\n </p>\n </slot>\n </div>\n <button\n v-if=\"closable\"\n type=\"button\"\n class=\"mint-modal__close\"\n aria-label=\"Close modal\"\n @click=\"close\"\n >\n <svg class=\"mint-modal__close-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"mint-modal__body\">\n <slot />\n </div>\n\n <!-- Footer -->\n <div v-if=\"$slots.footer\" class=\"mint-modal__footer\">\n <slot name=\"footer\" />\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style>\n@import '../styles/components/modal.css';\n</style>\n","<script setup lang=\"ts\">\n/** Modal dialog with backdrop, focus trap, Escape-to-close, and configurable size/variant. */\nimport { ref, watch, nextTick, onUnmounted } from 'vue'\nimport type { ModalSize, ModalVariant } from '../types'\nimport { useEventListener } from '../composables/useEventListener'\n\ninterface Props {\n modelValue: boolean\n title?: string\n subtitle?: string\n size?: ModalSize\n variant?: ModalVariant\n closable?: boolean\n closeOnOverlay?: boolean\n closeOnEscape?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n variant: 'centered',\n closable: true,\n closeOnOverlay: false,\n closeOnEscape: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n close: []\n}>()\n\nconst containerRef = ref<HTMLElement | null>(null)\nlet previouslyFocused: HTMLElement | null = null\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(', ')\n\nfunction getFocusableElements(): HTMLElement[] {\n if (!containerRef.value) return []\n return Array.from(containerRef.value.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR))\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (event.key === 'Escape' && props.closeOnEscape && props.modelValue) {\n close()\n return\n }\n\n if (event.key !== 'Tab' || !containerRef.value) return\n\n const focusable = getFocusableElements()\n if (focusable.length === 0) {\n event.preventDefault()\n return\n }\n\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n\n if (event.shiftKey && document.activeElement === first) {\n event.preventDefault()\n last.focus()\n } else if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault()\n first.focus()\n }\n}\n\nfunction close() {\n if (props.closable) {\n emit('update:modelValue', false)\n emit('close')\n }\n}\n\nfunction handleOverlayClick(event: MouseEvent) {\n if (props.closeOnOverlay && event.target === event.currentTarget) {\n close()\n }\n}\n\nwatch(() => props.modelValue, async (isOpen) => {\n if (isOpen) {\n previouslyFocused = document.activeElement as HTMLElement | null\n document.body.style.overflow = 'hidden'\n await nextTick()\n const focusable = getFocusableElements()\n if (focusable.length > 0) {\n focusable[0].focus()\n } else {\n containerRef.value?.focus()\n }\n } else {\n document.body.style.overflow = ''\n previouslyFocused?.focus()\n previouslyFocused = null\n }\n})\n\nuseEventListener(() => document, 'keydown', handleKeydown)\n\nonUnmounted(() => {\n document.body.style.overflow = ''\n})\n</script>\n\n<template>\n <Teleport to=\"body\">\n <Transition :name=\"`modal-${variant}`\">\n <div\n v-if=\"modelValue\"\n :class=\"['mint-modal', `mint-modal--${variant}`]\"\n @click=\"handleOverlayClick\"\n >\n <!-- Overlay -->\n <div class=\"mint-modal__overlay\" />\n\n <!-- Modal -->\n <div\n ref=\"containerRef\"\n :class=\"[\n 'mint-modal__container',\n `mint-modal__container--${size}`,\n `mint-modal__container--${variant}`,\n ]\"\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n >\n <!-- Sheet grab-hint bar (sheet variant only) -->\n <div v-if=\"variant === 'sheet'\" class=\"mint-modal__grab-hint\" aria-hidden=\"true\" />\n\n <!-- Header -->\n <div v-if=\"title || subtitle || closable || $slots.header\" class=\"mint-modal__header\">\n <div v-if=\"title || subtitle || $slots.header\" class=\"mint-modal__header-text\">\n <slot name=\"header\">\n <h3 v-if=\"title\" class=\"mint-modal__title\">\n {{ title }}\n </h3>\n <p v-if=\"subtitle\" class=\"mint-modal__subtitle\">\n {{ subtitle }}\n </p>\n </slot>\n </div>\n <button\n v-if=\"closable\"\n type=\"button\"\n class=\"mint-modal__close\"\n aria-label=\"Close modal\"\n @click=\"close\"\n >\n <svg class=\"mint-modal__close-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"mint-modal__body\">\n <slot />\n </div>\n\n <!-- Footer -->\n <div v-if=\"$slots.footer\" class=\"mint-modal__footer\">\n <slot name=\"footer\" />\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n</template>\n\n<style>\n@import '../styles/components/modal.css';\n</style>\n","<script setup lang=\"ts\">\n/** Renders a segmented button group for single-option selection, with simple or solid variants. */\nimport { computed } from 'vue'\nimport type { SegmentedOption, SegmentedOptionInput, SegmentedControlVariant, SegmentedControlSize } from '../types'\nimport { normalizeOptionInput } from '../utils/options'\n\ninterface Props {\n modelValue: string | number\n options: SegmentedOptionInput[]\n variant?: SegmentedControlVariant\n size?: SegmentedControlSize\n fullWidth?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'simple',\n size: 'md',\n fullWidth: false,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number]\n}>()\n\nconst normalizedOptions = computed<SegmentedOption[]>(() => props.options.map(normalizeOptionInput))\n\nfunction handleSelect(option: SegmentedOption) {\n if (props.disabled || option.disabled) return\n emit('update:modelValue', option.value)\n}\n\nfunction handleKeydown(event: KeyboardEvent, option: SegmentedOption) {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleSelect(option)\n }\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-segmented-control',\n `mint-segmented-control--${variant}`,\n `mint-segmented-control--${size}`,\n fullWidth ? 'mint-segmented-control--full-width' : '',\n disabled ? 'mint-segmented-control--disabled' : '',\n ]\"\n role=\"radiogroup\"\n >\n <button\n v-for=\"option in normalizedOptions\"\n :key=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"modelValue === option.value\"\n :disabled=\"disabled || option.disabled\"\n :class=\"[\n 'mint-segmented-control__option',\n `mint-segmented-control__option--${variant}`,\n `mint-segmented-control__option--${size}`,\n modelValue === option.value ? 'mint-segmented-control__option--active' : '',\n option.disabled ? 'mint-segmented-control__option--disabled' : '',\n ]\"\n @click=\"handleSelect(option)\"\n @keydown=\"handleKeydown($event, option)\"\n >\n <span class=\"mint-segmented-control__label\">{{ option.label }}</span>\n <span\n v-if=\"option.description && variant === 'card'\"\n class=\"mint-segmented-control__description\"\n >\n {{ option.description }}\n </span>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/segmented-control.css';\n</style>\n","<script setup lang=\"ts\">\n/** Renders a segmented button group for single-option selection, with simple or solid variants. */\nimport { computed } from 'vue'\nimport type { SegmentedOption, SegmentedOptionInput, SegmentedControlVariant, SegmentedControlSize } from '../types'\nimport { normalizeOptionInput } from '../utils/options'\n\ninterface Props {\n modelValue: string | number\n options: SegmentedOptionInput[]\n variant?: SegmentedControlVariant\n size?: SegmentedControlSize\n fullWidth?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'simple',\n size: 'md',\n fullWidth: false,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number]\n}>()\n\nconst normalizedOptions = computed<SegmentedOption[]>(() => props.options.map(normalizeOptionInput))\n\nfunction handleSelect(option: SegmentedOption) {\n if (props.disabled || option.disabled) return\n emit('update:modelValue', option.value)\n}\n\nfunction handleKeydown(event: KeyboardEvent, option: SegmentedOption) {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleSelect(option)\n }\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-segmented-control',\n `mint-segmented-control--${variant}`,\n `mint-segmented-control--${size}`,\n fullWidth ? 'mint-segmented-control--full-width' : '',\n disabled ? 'mint-segmented-control--disabled' : '',\n ]\"\n role=\"radiogroup\"\n >\n <button\n v-for=\"option in normalizedOptions\"\n :key=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"modelValue === option.value\"\n :disabled=\"disabled || option.disabled\"\n :class=\"[\n 'mint-segmented-control__option',\n `mint-segmented-control__option--${variant}`,\n `mint-segmented-control__option--${size}`,\n modelValue === option.value ? 'mint-segmented-control__option--active' : '',\n option.disabled ? 'mint-segmented-control__option--disabled' : '',\n ]\"\n @click=\"handleSelect(option)\"\n @keydown=\"handleKeydown($event, option)\"\n >\n <span class=\"mint-segmented-control__label\">{{ option.label }}</span>\n <span\n v-if=\"option.description && variant === 'card'\"\n class=\"mint-segmented-control__description\"\n >\n {{ option.description }}\n </span>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/segmented-control.css';\n</style>\n","<script setup lang=\"ts\">\n/** Compact label for tags, status indicators, and badges; supports removable and icon slots. */\nimport type { PillVariant, PillColor, PillSize } from '../types'\n\n/**\n * BasePill - Compact label component for tags, status indicators, and badges.\n *\n * @example\n * ```vue\n * <BasePill variant=\"success\">Active</BasePill>\n * <BasePill variant=\"warning\" removable @remove=\"handleRemove\">Tag</BasePill>\n * <BasePill :icon=\"true\">\n * <template #icon><CheckIcon /></template>\n * Verified\n * </BasePill>\n * ```\n */\ninterface Props {\n /** Visual style variant */\n variant?: PillVariant\n /** Semantic color modifier — use with variant=\"outline\" for colored outlines */\n color?: PillColor\n /** Size of the pill */\n size?: PillSize\n /** Show remove button */\n removable?: boolean\n /** Disable interaction */\n disabled?: boolean\n /** Show icon slot */\n icon?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'default',\n color: undefined,\n size: 'md',\n removable: false,\n disabled: false,\n icon: false,\n})\n\n/**\n * @event remove - Emitted when the remove button is clicked\n */\nconst emit = defineEmits<{\n remove: []\n}>()\n\nfunction handleRemove(event: MouseEvent) {\n event.stopPropagation()\n if (!props.disabled) {\n emit('remove')\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mint-pill',\n `mint-pill--${variant}`,\n color && `mint-pill--${color}`,\n `mint-pill--${size}`,\n { 'mint-pill--disabled': disabled, 'mint-pill--with-icon': icon },\n ]\"\n >\n <span v-if=\"icon\" class=\"mint-pill__icon\">\n <slot name=\"icon\" />\n </span>\n <span class=\"mint-pill__label\">\n <slot />\n </span>\n <button\n v-if=\"removable && !disabled\"\n type=\"button\"\n class=\"mint-pill__remove\"\n aria-label=\"Remove\"\n @click=\"handleRemove\"\n >\n <svg class=\"mint-pill__remove-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </span>\n</template>\n\n<style>\n@import '../styles/components/pill.css';\n</style>\n","<script setup lang=\"ts\">\n/** Compact label for tags, status indicators, and badges; supports removable and icon slots. */\nimport type { PillVariant, PillColor, PillSize } from '../types'\n\n/**\n * BasePill - Compact label component for tags, status indicators, and badges.\n *\n * @example\n * ```vue\n * <BasePill variant=\"success\">Active</BasePill>\n * <BasePill variant=\"warning\" removable @remove=\"handleRemove\">Tag</BasePill>\n * <BasePill :icon=\"true\">\n * <template #icon><CheckIcon /></template>\n * Verified\n * </BasePill>\n * ```\n */\ninterface Props {\n /** Visual style variant */\n variant?: PillVariant\n /** Semantic color modifier — use with variant=\"outline\" for colored outlines */\n color?: PillColor\n /** Size of the pill */\n size?: PillSize\n /** Show remove button */\n removable?: boolean\n /** Disable interaction */\n disabled?: boolean\n /** Show icon slot */\n icon?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'default',\n color: undefined,\n size: 'md',\n removable: false,\n disabled: false,\n icon: false,\n})\n\n/**\n * @event remove - Emitted when the remove button is clicked\n */\nconst emit = defineEmits<{\n remove: []\n}>()\n\nfunction handleRemove(event: MouseEvent) {\n event.stopPropagation()\n if (!props.disabled) {\n emit('remove')\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mint-pill',\n `mint-pill--${variant}`,\n color && `mint-pill--${color}`,\n `mint-pill--${size}`,\n { 'mint-pill--disabled': disabled, 'mint-pill--with-icon': icon },\n ]\"\n >\n <span v-if=\"icon\" class=\"mint-pill__icon\">\n <slot name=\"icon\" />\n </span>\n <span class=\"mint-pill__label\">\n <slot />\n </span>\n <button\n v-if=\"removable && !disabled\"\n type=\"button\"\n class=\"mint-pill__remove\"\n aria-label=\"Remove\"\n @click=\"handleRemove\"\n >\n <svg class=\"mint-pill__remove-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </span>\n</template>\n\n<style>\n@import '../styles/components/pill.css';\n</style>\n","<script setup lang=\"ts\">\n/** Button that opens a dropdown menu for selecting one option from a list. */\nimport { computed } from 'vue'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport type { SelectOption, SelectOptionInput, ButtonVariant, ButtonSize } from '../types'\nimport { normalizeOptionInput } from '../utils/options'\n\n/**\n * DropdownButton - Button-style select with dropdown menu for option selection.\n *\n * @example\n * ```vue\n * <DropdownButton\n * v-model=\"selectedValue\"\n * :options=\"[\n * { value: 'a', label: 'Option A' },\n * { value: 'b', label: 'Option B', description: 'Additional info' }\n * ]\"\n * variant=\"secondary\"\n * />\n * ```\n */\ninterface Props {\n /** Selected option value */\n modelValue?: string | number\n /** Available options */\n options: SelectOptionInput<string | number>[]\n /** Placeholder text when no option is selected */\n placeholder?: string\n /** Button style variant */\n variant?: ButtonVariant\n /** Button size */\n size?: ButtonSize\n /** Disable interaction */\n disabled?: boolean\n /** Show loading spinner */\n loading?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'secondary',\n size: 'md',\n disabled: false,\n loading: false,\n})\n\n/**\n * @event update:modelValue - Emitted when an option is selected (v-model support)\n * @event select - Emitted when an option is selected, includes full option object\n */\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number]\n select: [option: SelectOption<string | number>]\n}>()\n\nconst { isOpen, rootRef, close, toggle: toggleDropdown } = useDropdownState()\nconst normalizedOptions = computed<SelectOption<string | number>[]>(() =>\n props.options.map(normalizeOptionInput)\n)\n\nconst selectedLabel = computed(() => {\n if (props.modelValue === undefined) return props.placeholder ?? 'Select...'\n const found = normalizedOptions.value.find(o => o.value === props.modelValue)\n return found?.label ?? String(props.modelValue)\n})\n\nfunction toggle() {\n if (props.disabled || props.loading) return\n toggleDropdown()\n}\n\nfunction selectOption(option: SelectOption<string | number>) {\n if (option.disabled) return\n emit('update:modelValue', option.value)\n emit('select', option)\n close()\n}\n</script>\n\n<template>\n <div ref=\"rootRef\" class=\"mint-dropdown-button\">\n <button\n type=\"button\"\n :disabled=\"disabled || loading\"\n :class=\"[\n 'mint-dropdown-button__trigger',\n `mint-dropdown-button__trigger--${variant}`,\n `mint-dropdown-button__trigger--${size}`,\n {\n 'mint-dropdown-button__trigger--disabled': disabled || loading,\n 'mint-dropdown-button__trigger--open': isOpen,\n },\n ]\"\n :aria-expanded=\"isOpen\"\n aria-haspopup=\"listbox\"\n @click=\"toggle\"\n >\n <svg v-if=\"loading\" class=\"mint-dropdown-button__spinner\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"3\" opacity=\"0.25\" />\n <path d=\"M12 2a10 10 0 0 1 10 10\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" />\n </svg>\n <span class=\"mint-dropdown-button__label\">{{ selectedLabel }}</span>\n <svg\n :class=\"['mint-dropdown-button__chevron', { 'mint-dropdown-button__chevron--open': isOpen }]\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <Transition name=\"mint-dropdown-button-menu\">\n <ul\n v-if=\"isOpen\"\n class=\"mint-dropdown-button__menu\"\n role=\"listbox\"\n :aria-activedescendant=\"modelValue !== undefined ? `mint-dropdown-option-${modelValue}` : undefined\"\n >\n <li\n v-for=\"option in normalizedOptions\"\n :id=\"`mint-dropdown-option-${option.value}`\"\n :key=\"String(option.value)\"\n role=\"option\"\n :aria-selected=\"option.value === modelValue\"\n :aria-disabled=\"option.disabled\"\n :class=\"[\n 'mint-dropdown-button__option',\n {\n 'mint-dropdown-button__option--selected': option.value === modelValue,\n 'mint-dropdown-button__option--disabled': option.disabled,\n },\n ]\"\n @click=\"selectOption(option)\"\n >\n <span class=\"mint-dropdown-button__option-label\">{{ option.label }}</span>\n <span v-if=\"option.description\" class=\"mint-dropdown-button__option-description\">\n {{ option.description }}\n </span>\n <svg\n v-if=\"option.value === modelValue\"\n class=\"mint-dropdown-button__option-check\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </li>\n </ul>\n </Transition>\n </div>\n</template>\n\n<style>\n@import '../styles/components/dropdown-button.css';\n</style>\n","<script setup lang=\"ts\">\n/** Button that opens a dropdown menu for selecting one option from a list. */\nimport { computed } from 'vue'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport type { SelectOption, SelectOptionInput, ButtonVariant, ButtonSize } from '../types'\nimport { normalizeOptionInput } from '../utils/options'\n\n/**\n * DropdownButton - Button-style select with dropdown menu for option selection.\n *\n * @example\n * ```vue\n * <DropdownButton\n * v-model=\"selectedValue\"\n * :options=\"[\n * { value: 'a', label: 'Option A' },\n * { value: 'b', label: 'Option B', description: 'Additional info' }\n * ]\"\n * variant=\"secondary\"\n * />\n * ```\n */\ninterface Props {\n /** Selected option value */\n modelValue?: string | number\n /** Available options */\n options: SelectOptionInput<string | number>[]\n /** Placeholder text when no option is selected */\n placeholder?: string\n /** Button style variant */\n variant?: ButtonVariant\n /** Button size */\n size?: ButtonSize\n /** Disable interaction */\n disabled?: boolean\n /** Show loading spinner */\n loading?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'secondary',\n size: 'md',\n disabled: false,\n loading: false,\n})\n\n/**\n * @event update:modelValue - Emitted when an option is selected (v-model support)\n * @event select - Emitted when an option is selected, includes full option object\n */\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number]\n select: [option: SelectOption<string | number>]\n}>()\n\nconst { isOpen, rootRef, close, toggle: toggleDropdown } = useDropdownState()\nconst normalizedOptions = computed<SelectOption<string | number>[]>(() =>\n props.options.map(normalizeOptionInput)\n)\n\nconst selectedLabel = computed(() => {\n if (props.modelValue === undefined) return props.placeholder ?? 'Select...'\n const found = normalizedOptions.value.find(o => o.value === props.modelValue)\n return found?.label ?? String(props.modelValue)\n})\n\nfunction toggle() {\n if (props.disabled || props.loading) return\n toggleDropdown()\n}\n\nfunction selectOption(option: SelectOption<string | number>) {\n if (option.disabled) return\n emit('update:modelValue', option.value)\n emit('select', option)\n close()\n}\n</script>\n\n<template>\n <div ref=\"rootRef\" class=\"mint-dropdown-button\">\n <button\n type=\"button\"\n :disabled=\"disabled || loading\"\n :class=\"[\n 'mint-dropdown-button__trigger',\n `mint-dropdown-button__trigger--${variant}`,\n `mint-dropdown-button__trigger--${size}`,\n {\n 'mint-dropdown-button__trigger--disabled': disabled || loading,\n 'mint-dropdown-button__trigger--open': isOpen,\n },\n ]\"\n :aria-expanded=\"isOpen\"\n aria-haspopup=\"listbox\"\n @click=\"toggle\"\n >\n <svg v-if=\"loading\" class=\"mint-dropdown-button__spinner\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"3\" opacity=\"0.25\" />\n <path d=\"M12 2a10 10 0 0 1 10 10\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" />\n </svg>\n <span class=\"mint-dropdown-button__label\">{{ selectedLabel }}</span>\n <svg\n :class=\"['mint-dropdown-button__chevron', { 'mint-dropdown-button__chevron--open': isOpen }]\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <Transition name=\"mint-dropdown-button-menu\">\n <ul\n v-if=\"isOpen\"\n class=\"mint-dropdown-button__menu\"\n role=\"listbox\"\n :aria-activedescendant=\"modelValue !== undefined ? `mint-dropdown-option-${modelValue}` : undefined\"\n >\n <li\n v-for=\"option in normalizedOptions\"\n :id=\"`mint-dropdown-option-${option.value}`\"\n :key=\"String(option.value)\"\n role=\"option\"\n :aria-selected=\"option.value === modelValue\"\n :aria-disabled=\"option.disabled\"\n :class=\"[\n 'mint-dropdown-button__option',\n {\n 'mint-dropdown-button__option--selected': option.value === modelValue,\n 'mint-dropdown-button__option--disabled': option.disabled,\n },\n ]\"\n @click=\"selectOption(option)\"\n >\n <span class=\"mint-dropdown-button__option-label\">{{ option.label }}</span>\n <span v-if=\"option.description\" class=\"mint-dropdown-button__option-description\">\n {{ option.description }}\n </span>\n <svg\n v-if=\"option.value === modelValue\"\n class=\"mint-dropdown-button__option-check\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </li>\n </ul>\n </Transition>\n </div>\n</template>\n\n<style>\n@import '../styles/components/dropdown-button.css';\n</style>\n","<script setup lang=\"ts\">\n/** Month calendar for single date, multi-date, or range selection with markers and navigation. */\nimport { ref, computed, watch } from 'vue'\nimport type { CalendarSelectionMode, CalendarMarker, CalendarDayContext } from '../types'\n\n/**\n * Calendar - Month calendar with date selection, range selection, and custom markers.\n *\n * @example\n * ```vue\n * <!-- Single date selection -->\n * <Calendar v-model=\"selectedDate\" />\n *\n * <!-- Range selection -->\n * <Calendar v-model=\"dateRange\" selection-mode=\"range\" />\n *\n * <!-- With markers -->\n * <Calendar :markers=\"[{ date: '2024-01-15', color: '#ff0000', label: 'Event' }]\" />\n * ```\n */\ninterface Props {\n /** Selected date(s) - type depends on selectionMode */\n modelValue?: Date | Date[] | { start: Date; end: Date } | null\n /** Selection behavior mode */\n selectionMode?: CalendarSelectionMode\n /** Display month (0-11) */\n month?: number\n /** Display year */\n year?: number\n /** Always show 6 weeks (42 days) */\n fixedWeeks?: boolean\n /** First day of week (0=Sunday, 1=Monday, etc.) */\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6\n /** Show days from adjacent months */\n showOutsideDays?: boolean\n /** Show month navigation buttons */\n showNavigation?: boolean\n /** Visual markers on specific dates */\n markers?: CalendarMarker[]\n /** Earliest selectable date */\n minDate?: Date | string\n /** Latest selectable date */\n maxDate?: Date | string\n /** Explicitly disabled dates */\n disabledDates?: Array<Date | string>\n /** Custom disabled date logic */\n isDateDisabled?: (date: Date) => boolean\n /** Locale for date formatting */\n locale?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n selectionMode: 'single',\n fixedWeeks: true,\n weekStartsOn: 1,\n showOutsideDays: true,\n showNavigation: true,\n markers: () => [],\n disabledDates: () => [],\n locale: 'en-US',\n})\n\n/**\n * @event update:modelValue - Emitted when date selection changes (v-model support)\n * @event update:month - Emitted when displayed month changes\n * @event update:year - Emitted when displayed year changes\n * @event day-click - Emitted when a day is clicked, includes day context\n * @event navigate - Emitted when month navigation occurs\n */\nconst emit = defineEmits<{\n 'update:modelValue': [value: Date | Date[] | { start: Date; end: Date } | null]\n 'update:month': [month: number]\n 'update:year': [year: number]\n 'day-click': [context: CalendarDayContext]\n navigate: [direction: 'prev' | 'next', month: number, year: number]\n}>()\n\nconst today = new Date()\ntoday.setHours(0, 0, 0, 0)\n\nconst currentMonth = ref(props.month ?? today.getMonth())\nconst currentYear = ref(props.year ?? today.getFullYear())\n\n// Range hover tracking\nconst hoveredDate = ref<Date | null>(null)\n\nwatch(() => props.month, (val) => { if (val !== undefined) currentMonth.value = val })\nwatch(() => props.year, (val) => { if (val !== undefined) currentYear.value = val })\n\nconst monthLabel = computed(() => {\n const date = new Date(currentYear.value, currentMonth.value, 1)\n return date.toLocaleDateString(props.locale, { month: 'long', year: 'numeric' })\n})\n\nconst weekDayLabels = computed(() => {\n const labels: string[] = []\n // Use Jan 7, 2024 (Sunday) as reference, then offset by weekStartsOn\n const base = new Date(2024, 0, 7 + props.weekStartsOn)\n for (let i = 0; i < 7; i++) {\n const d = new Date(base)\n d.setDate(d.getDate() + i)\n labels.push(d.toLocaleDateString(props.locale, { weekday: 'short' }))\n }\n return labels\n})\n\nfunction toDateKey(d: Date): string {\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`\n}\n\nfunction parseDate(val: Date | string): Date {\n if (val instanceof Date) return val\n const d = new Date(val + 'T00:00:00')\n return d\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n}\n\nconst disabledDateSet = computed(() => {\n const set = new Set<string>()\n for (const d of props.disabledDates) {\n const parsed = parseDate(d)\n set.add(toDateKey(parsed))\n }\n return set\n})\n\nconst markerMap = computed(() => {\n const map = new Map<string, CalendarMarker[]>()\n for (const marker of props.markers) {\n const date = parseDate(marker.date)\n const key = toDateKey(date)\n const markers = map.get(key)\n if (markers) {\n markers.push(marker)\n } else {\n map.set(key, [marker])\n }\n }\n return map\n})\n\nfunction isDateSelected(date: Date): boolean {\n const val = props.modelValue\n if (!val) return false\n if (val instanceof Date) return isSameDay(date, val)\n if (Array.isArray(val)) return val.some(d => isSameDay(date, d))\n if ('start' in val && 'end' in val) {\n return isSameDay(date, val.start) || isSameDay(date, val.end)\n }\n return false\n}\n\nfunction isInRange(date: Date): boolean {\n if (props.selectionMode !== 'range') return false\n const val = props.modelValue\n if (!val || !('start' in (val as object))) return false\n const range = val as { start: Date; end: Date }\n\n const t = date.getTime()\n const start = range.start?.getTime()\n if (!start) return false\n\n // If hovering during selection (before end is set)\n if (!range.end && hoveredDate.value) {\n const hov = hoveredDate.value.getTime()\n const [lo, hi] = start <= hov ? [start, hov] : [hov, start]\n return t > lo && t < hi\n }\n\n // Complete range\n if (range.end) {\n const end = range.end.getTime()\n return t > start && t < end\n }\n\n return false\n}\n\nfunction isDisabled(date: Date): boolean {\n if (disabledDateSet.value.has(toDateKey(date))) return true\n if (props.isDateDisabled?.(date)) return true\n if (props.minDate) {\n const min = parseDate(props.minDate)\n if (date < min) return true\n }\n if (props.maxDate) {\n const max = parseDate(props.maxDate)\n if (date > max) return true\n }\n return false\n}\n\nconst calendarDays = computed<CalendarDayContext[]>(() => {\n const first = new Date(currentYear.value, currentMonth.value, 1)\n const startDow = first.getDay()\n const offset = (startDow - props.weekStartsOn + 7) % 7\n\n const totalCells = props.fixedWeeks ? 42 : (() => {\n const daysInMonth = new Date(currentYear.value, currentMonth.value + 1, 0).getDate()\n const needed = offset + daysInMonth\n return Math.ceil(needed / 7) * 7\n })()\n\n const days: CalendarDayContext[] = []\n for (let i = 0; i < totalCells; i++) {\n const date = new Date(currentYear.value, currentMonth.value, 1 - offset + i)\n date.setHours(0, 0, 0, 0)\n const key = toDateKey(date)\n days.push({\n date,\n dayOfMonth: date.getDate(),\n isToday: isSameDay(date, today),\n isSelected: isDateSelected(date),\n isInRange: isInRange(date),\n isDisabled: isDisabled(date),\n isOutsideMonth: date.getMonth() !== currentMonth.value,\n markers: markerMap.value.get(key) ?? [],\n })\n }\n return days\n})\n\n// Range selection state\nconst rangeStart = ref<Date | null>(null)\n\nfunction handleDayClick(day: CalendarDayContext) {\n if (day.isDisabled) return\n if (!props.showOutsideDays && day.isOutsideMonth) return\n\n emit('day-click', day)\n\n if (props.selectionMode === 'none') return\n\n if (props.selectionMode === 'single') {\n emit('update:modelValue', day.date)\n } else if (props.selectionMode === 'multiple') {\n const current = (props.modelValue as Date[] | undefined) ?? []\n const idx = current.findIndex(d => isSameDay(d, day.date))\n if (idx >= 0) {\n const next = [...current]\n next.splice(idx, 1)\n emit('update:modelValue', next)\n } else {\n emit('update:modelValue', [...current, day.date])\n }\n } else if (props.selectionMode === 'range') {\n if (!rangeStart.value) {\n rangeStart.value = day.date\n } else {\n const start = rangeStart.value\n const end = day.date\n rangeStart.value = null\n const ordered = start.getTime() <= end.getTime()\n ? { start, end }\n : { start: end, end: start }\n emit('update:modelValue', ordered)\n }\n }\n}\n\nfunction handleDayHover(day: CalendarDayContext) {\n if (props.selectionMode === 'range' && rangeStart.value) {\n hoveredDate.value = day.date\n }\n}\n\nfunction prevMonth() {\n if (currentMonth.value === 0) {\n currentMonth.value = 11\n currentYear.value--\n emit('update:year', currentYear.value)\n } else {\n currentMonth.value--\n }\n emit('update:month', currentMonth.value)\n emit('navigate', 'prev', currentMonth.value, currentYear.value)\n}\n\nfunction nextMonth() {\n if (currentMonth.value === 11) {\n currentMonth.value = 0\n currentYear.value++\n emit('update:year', currentYear.value)\n } else {\n currentMonth.value++\n }\n emit('update:month', currentMonth.value)\n emit('navigate', 'next', currentMonth.value, currentYear.value)\n}\n</script>\n\n<template>\n <div class=\"mint-calendar\">\n <div v-if=\"showNavigation\" class=\"mint-calendar__header\">\n <button type=\"button\" class=\"mint-calendar__nav-btn\" aria-label=\"Previous month\" @click=\"prevMonth\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <slot name=\"header\" :month=\"currentMonth\" :year=\"currentYear\" :prev-month=\"prevMonth\" :next-month=\"nextMonth\">\n <span class=\"mint-calendar__title\">{{ monthLabel }}</span>\n </slot>\n <button type=\"button\" class=\"mint-calendar__nav-btn\" aria-label=\"Next month\" @click=\"nextMonth\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n </div>\n\n <div class=\"mint-calendar__weekdays\">\n <span\n v-for=\"(label, i) in weekDayLabels\"\n :key=\"i\"\n class=\"mint-calendar__weekday\"\n >\n <slot name=\"week-day\" :day-name=\"label\" :index=\"i\">{{ label }}</slot>\n </span>\n </div>\n\n <div class=\"mint-calendar__grid\">\n <button\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n type=\"button\"\n :disabled=\"day.isDisabled\"\n :class=\"[\n 'mint-calendar__day',\n {\n 'mint-calendar__day--today': day.isToday,\n 'mint-calendar__day--selected': day.isSelected,\n 'mint-calendar__day--in-range': day.isInRange,\n 'mint-calendar__day--disabled': day.isDisabled,\n 'mint-calendar__day--outside': day.isOutsideMonth,\n 'mint-calendar__day--interactive': selectionMode !== 'none' && !day.isDisabled,\n },\n ]\"\n @click=\"handleDayClick(day)\"\n @mouseenter=\"handleDayHover(day)\"\n >\n <slot name=\"day-content\" :day=\"day\">\n <span class=\"mint-calendar__day-number\">{{ day.dayOfMonth }}</span>\n <div v-if=\"day.markers.length > 0\" class=\"mint-calendar__markers\">\n <span\n v-for=\"(marker, mi) in day.markers.slice(0, 3)\"\n :key=\"mi\"\n :class=\"[\n 'mint-calendar__marker',\n `mint-calendar__marker--${marker.type ?? 'dot'}`,\n ]\"\n :style=\"marker.color ? { '--marker-color': marker.color } : {}\"\n :title=\"marker.label\"\n />\n </div>\n </slot>\n </button>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/calendar.css';\n</style>\n","<script setup lang=\"ts\">\n/** Month calendar for single date, multi-date, or range selection with markers and navigation. */\nimport { ref, computed, watch } from 'vue'\nimport type { CalendarSelectionMode, CalendarMarker, CalendarDayContext } from '../types'\n\n/**\n * Calendar - Month calendar with date selection, range selection, and custom markers.\n *\n * @example\n * ```vue\n * <!-- Single date selection -->\n * <Calendar v-model=\"selectedDate\" />\n *\n * <!-- Range selection -->\n * <Calendar v-model=\"dateRange\" selection-mode=\"range\" />\n *\n * <!-- With markers -->\n * <Calendar :markers=\"[{ date: '2024-01-15', color: '#ff0000', label: 'Event' }]\" />\n * ```\n */\ninterface Props {\n /** Selected date(s) - type depends on selectionMode */\n modelValue?: Date | Date[] | { start: Date; end: Date } | null\n /** Selection behavior mode */\n selectionMode?: CalendarSelectionMode\n /** Display month (0-11) */\n month?: number\n /** Display year */\n year?: number\n /** Always show 6 weeks (42 days) */\n fixedWeeks?: boolean\n /** First day of week (0=Sunday, 1=Monday, etc.) */\n weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6\n /** Show days from adjacent months */\n showOutsideDays?: boolean\n /** Show month navigation buttons */\n showNavigation?: boolean\n /** Visual markers on specific dates */\n markers?: CalendarMarker[]\n /** Earliest selectable date */\n minDate?: Date | string\n /** Latest selectable date */\n maxDate?: Date | string\n /** Explicitly disabled dates */\n disabledDates?: Array<Date | string>\n /** Custom disabled date logic */\n isDateDisabled?: (date: Date) => boolean\n /** Locale for date formatting */\n locale?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n selectionMode: 'single',\n fixedWeeks: true,\n weekStartsOn: 1,\n showOutsideDays: true,\n showNavigation: true,\n markers: () => [],\n disabledDates: () => [],\n locale: 'en-US',\n})\n\n/**\n * @event update:modelValue - Emitted when date selection changes (v-model support)\n * @event update:month - Emitted when displayed month changes\n * @event update:year - Emitted when displayed year changes\n * @event day-click - Emitted when a day is clicked, includes day context\n * @event navigate - Emitted when month navigation occurs\n */\nconst emit = defineEmits<{\n 'update:modelValue': [value: Date | Date[] | { start: Date; end: Date } | null]\n 'update:month': [month: number]\n 'update:year': [year: number]\n 'day-click': [context: CalendarDayContext]\n navigate: [direction: 'prev' | 'next', month: number, year: number]\n}>()\n\nconst today = new Date()\ntoday.setHours(0, 0, 0, 0)\n\nconst currentMonth = ref(props.month ?? today.getMonth())\nconst currentYear = ref(props.year ?? today.getFullYear())\n\n// Range hover tracking\nconst hoveredDate = ref<Date | null>(null)\n\nwatch(() => props.month, (val) => { if (val !== undefined) currentMonth.value = val })\nwatch(() => props.year, (val) => { if (val !== undefined) currentYear.value = val })\n\nconst monthLabel = computed(() => {\n const date = new Date(currentYear.value, currentMonth.value, 1)\n return date.toLocaleDateString(props.locale, { month: 'long', year: 'numeric' })\n})\n\nconst weekDayLabels = computed(() => {\n const labels: string[] = []\n // Use Jan 7, 2024 (Sunday) as reference, then offset by weekStartsOn\n const base = new Date(2024, 0, 7 + props.weekStartsOn)\n for (let i = 0; i < 7; i++) {\n const d = new Date(base)\n d.setDate(d.getDate() + i)\n labels.push(d.toLocaleDateString(props.locale, { weekday: 'short' }))\n }\n return labels\n})\n\nfunction toDateKey(d: Date): string {\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`\n}\n\nfunction parseDate(val: Date | string): Date {\n if (val instanceof Date) return val\n const d = new Date(val + 'T00:00:00')\n return d\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n}\n\nconst disabledDateSet = computed(() => {\n const set = new Set<string>()\n for (const d of props.disabledDates) {\n const parsed = parseDate(d)\n set.add(toDateKey(parsed))\n }\n return set\n})\n\nconst markerMap = computed(() => {\n const map = new Map<string, CalendarMarker[]>()\n for (const marker of props.markers) {\n const date = parseDate(marker.date)\n const key = toDateKey(date)\n const markers = map.get(key)\n if (markers) {\n markers.push(marker)\n } else {\n map.set(key, [marker])\n }\n }\n return map\n})\n\nfunction isDateSelected(date: Date): boolean {\n const val = props.modelValue\n if (!val) return false\n if (val instanceof Date) return isSameDay(date, val)\n if (Array.isArray(val)) return val.some(d => isSameDay(date, d))\n if ('start' in val && 'end' in val) {\n return isSameDay(date, val.start) || isSameDay(date, val.end)\n }\n return false\n}\n\nfunction isInRange(date: Date): boolean {\n if (props.selectionMode !== 'range') return false\n const val = props.modelValue\n if (!val || !('start' in (val as object))) return false\n const range = val as { start: Date; end: Date }\n\n const t = date.getTime()\n const start = range.start?.getTime()\n if (!start) return false\n\n // If hovering during selection (before end is set)\n if (!range.end && hoveredDate.value) {\n const hov = hoveredDate.value.getTime()\n const [lo, hi] = start <= hov ? [start, hov] : [hov, start]\n return t > lo && t < hi\n }\n\n // Complete range\n if (range.end) {\n const end = range.end.getTime()\n return t > start && t < end\n }\n\n return false\n}\n\nfunction isDisabled(date: Date): boolean {\n if (disabledDateSet.value.has(toDateKey(date))) return true\n if (props.isDateDisabled?.(date)) return true\n if (props.minDate) {\n const min = parseDate(props.minDate)\n if (date < min) return true\n }\n if (props.maxDate) {\n const max = parseDate(props.maxDate)\n if (date > max) return true\n }\n return false\n}\n\nconst calendarDays = computed<CalendarDayContext[]>(() => {\n const first = new Date(currentYear.value, currentMonth.value, 1)\n const startDow = first.getDay()\n const offset = (startDow - props.weekStartsOn + 7) % 7\n\n const totalCells = props.fixedWeeks ? 42 : (() => {\n const daysInMonth = new Date(currentYear.value, currentMonth.value + 1, 0).getDate()\n const needed = offset + daysInMonth\n return Math.ceil(needed / 7) * 7\n })()\n\n const days: CalendarDayContext[] = []\n for (let i = 0; i < totalCells; i++) {\n const date = new Date(currentYear.value, currentMonth.value, 1 - offset + i)\n date.setHours(0, 0, 0, 0)\n const key = toDateKey(date)\n days.push({\n date,\n dayOfMonth: date.getDate(),\n isToday: isSameDay(date, today),\n isSelected: isDateSelected(date),\n isInRange: isInRange(date),\n isDisabled: isDisabled(date),\n isOutsideMonth: date.getMonth() !== currentMonth.value,\n markers: markerMap.value.get(key) ?? [],\n })\n }\n return days\n})\n\n// Range selection state\nconst rangeStart = ref<Date | null>(null)\n\nfunction handleDayClick(day: CalendarDayContext) {\n if (day.isDisabled) return\n if (!props.showOutsideDays && day.isOutsideMonth) return\n\n emit('day-click', day)\n\n if (props.selectionMode === 'none') return\n\n if (props.selectionMode === 'single') {\n emit('update:modelValue', day.date)\n } else if (props.selectionMode === 'multiple') {\n const current = (props.modelValue as Date[] | undefined) ?? []\n const idx = current.findIndex(d => isSameDay(d, day.date))\n if (idx >= 0) {\n const next = [...current]\n next.splice(idx, 1)\n emit('update:modelValue', next)\n } else {\n emit('update:modelValue', [...current, day.date])\n }\n } else if (props.selectionMode === 'range') {\n if (!rangeStart.value) {\n rangeStart.value = day.date\n } else {\n const start = rangeStart.value\n const end = day.date\n rangeStart.value = null\n const ordered = start.getTime() <= end.getTime()\n ? { start, end }\n : { start: end, end: start }\n emit('update:modelValue', ordered)\n }\n }\n}\n\nfunction handleDayHover(day: CalendarDayContext) {\n if (props.selectionMode === 'range' && rangeStart.value) {\n hoveredDate.value = day.date\n }\n}\n\nfunction prevMonth() {\n if (currentMonth.value === 0) {\n currentMonth.value = 11\n currentYear.value--\n emit('update:year', currentYear.value)\n } else {\n currentMonth.value--\n }\n emit('update:month', currentMonth.value)\n emit('navigate', 'prev', currentMonth.value, currentYear.value)\n}\n\nfunction nextMonth() {\n if (currentMonth.value === 11) {\n currentMonth.value = 0\n currentYear.value++\n emit('update:year', currentYear.value)\n } else {\n currentMonth.value++\n }\n emit('update:month', currentMonth.value)\n emit('navigate', 'next', currentMonth.value, currentYear.value)\n}\n</script>\n\n<template>\n <div class=\"mint-calendar\">\n <div v-if=\"showNavigation\" class=\"mint-calendar__header\">\n <button type=\"button\" class=\"mint-calendar__nav-btn\" aria-label=\"Previous month\" @click=\"prevMonth\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <slot name=\"header\" :month=\"currentMonth\" :year=\"currentYear\" :prev-month=\"prevMonth\" :next-month=\"nextMonth\">\n <span class=\"mint-calendar__title\">{{ monthLabel }}</span>\n </slot>\n <button type=\"button\" class=\"mint-calendar__nav-btn\" aria-label=\"Next month\" @click=\"nextMonth\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n </div>\n\n <div class=\"mint-calendar__weekdays\">\n <span\n v-for=\"(label, i) in weekDayLabels\"\n :key=\"i\"\n class=\"mint-calendar__weekday\"\n >\n <slot name=\"week-day\" :day-name=\"label\" :index=\"i\">{{ label }}</slot>\n </span>\n </div>\n\n <div class=\"mint-calendar__grid\">\n <button\n v-for=\"(day, i) in calendarDays\"\n :key=\"i\"\n type=\"button\"\n :disabled=\"day.isDisabled\"\n :class=\"[\n 'mint-calendar__day',\n {\n 'mint-calendar__day--today': day.isToday,\n 'mint-calendar__day--selected': day.isSelected,\n 'mint-calendar__day--in-range': day.isInRange,\n 'mint-calendar__day--disabled': day.isDisabled,\n 'mint-calendar__day--outside': day.isOutsideMonth,\n 'mint-calendar__day--interactive': selectionMode !== 'none' && !day.isDisabled,\n },\n ]\"\n @click=\"handleDayClick(day)\"\n @mouseenter=\"handleDayHover(day)\"\n >\n <slot name=\"day-content\" :day=\"day\">\n <span class=\"mint-calendar__day-number\">{{ day.dayOfMonth }}</span>\n <div v-if=\"day.markers.length > 0\" class=\"mint-calendar__markers\">\n <span\n v-for=\"(marker, mi) in day.markers.slice(0, 3)\"\n :key=\"mi\"\n :class=\"[\n 'mint-calendar__marker',\n `mint-calendar__marker--${marker.type ?? 'dot'}`,\n ]\"\n :style=\"marker.color ? { '--marker-color': marker.color } : {}\"\n :title=\"marker.label\"\n />\n </div>\n </slot>\n </button>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/calendar.css';\n</style>\n","<script setup lang=\"ts\">\n/** Data table with sortable columns, search, pagination, and optional row selection. */\nimport { ref, computed } from 'vue'\nimport type { DataFrameColumn, SortState, PaginationState } from '../types'\nimport { useSortedItems } from '../composables/useSortedItems'\nimport { useTextSearch } from '../composables/useTextSearch'\nimport { useListSelection } from '../composables/useListSelection'\n\n/**\n * DataFrame - Data table with sorting, searching, pagination, and row selection.\n *\n * @example\n * ```vue\n * <DataFrame\n * :data=\"rows\"\n * :columns=\"[\n * { key: 'name', label: 'Name', sortable: true },\n * { key: 'age', label: 'Age', align: 'right' }\n * ]\"\n * sortable\n * searchable\n * :pagination=\"{ page: 1, pageSize: 10, total: 100 }\"\n * />\n * ```\n */\ninterface Props {\n /** Array of data objects */\n data: Record<string, unknown>[]\n /** Column definitions */\n columns: DataFrameColumn[]\n /** Unique key for each row (property name or function) */\n rowKey?: string | ((row: Record<string, unknown>) => string | number)\n /** Alternate row background colors */\n striped?: boolean\n /** Show table borders */\n bordered?: boolean\n /** Table size */\n size?: 'sm' | 'md' | 'lg'\n /** Make header sticky on scroll */\n stickyHeader?: boolean\n /** Maximum table height (enables vertical scroll) */\n maxHeight?: string | number\n /** Show loading overlay */\n loading?: boolean\n /** Empty state message */\n emptyText?: string\n /** Enable column sorting */\n sortable?: boolean\n /** Current sort state (controlled mode) */\n sort?: SortState | null\n /** Pagination state or false to disable */\n pagination?: PaginationState | false\n /** Enable search input */\n searchable?: boolean\n /** Search input placeholder */\n searchPlaceholder?: string\n /** Column keys to search (defaults to all) */\n searchKeys?: string[]\n /** Enable row selection with checkboxes */\n selectable?: boolean\n /** Currently selected row keys */\n selectedKeys?: (string | number)[]\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n striped: true,\n bordered: true,\n size: 'md',\n stickyHeader: false,\n loading: false,\n emptyText: 'No data',\n sortable: false,\n pagination: false,\n searchable: false,\n searchPlaceholder: 'Search...',\n selectable: false,\n selectedKeys: () => [],\n})\n\n/**\n * @event update:sort - Emitted when sort state changes\n * @event update:pagination - Emitted when page changes\n * @event update:selectedKeys - Emitted when row selection changes\n * @event row-click - Emitted when a row is clicked\n * @event cell-click - Emitted when a cell is clicked\n */\nconst emit = defineEmits<{\n 'update:sort': [sort: SortState | null]\n 'update:pagination': [pagination: PaginationState]\n 'update:selectedKeys': [keys: (string | number)[]]\n 'row-click': [row: Record<string, unknown>, index: number]\n 'cell-click': [value: unknown, column: DataFrameColumn, row: Record<string, unknown>]\n}>()\n\nconst searchQuery = ref('')\nconst internalSort = ref<SortState | null>(null)\n\nconst activeSort = computed(() => props.sort !== undefined ? props.sort : internalSort.value)\n\nfunction getRowKey(row: Record<string, unknown>, index: number): string | number {\n if (!props.rowKey) return index\n if (typeof props.rowKey === 'function') return props.rowKey(row)\n return row[props.rowKey] as string | number\n}\n\nfunction getCellValue(row: Record<string, unknown>, key: string): unknown {\n return key.split('.').reduce((obj: unknown, k) => {\n if (obj && typeof obj === 'object') return (obj as Record<string, unknown>)[k]\n return undefined\n }, row)\n}\n\nfunction formatCell(col: DataFrameColumn, row: Record<string, unknown>, index: number): string {\n const value = getCellValue(row, col.key)\n if (col.formatter) return col.formatter(value, row, index)\n if (value === null || value === undefined) return ''\n return String(value)\n}\n\nconst textSearch = useTextSearch({\n items: () => props.data,\n query: searchQuery,\n enabled: () => props.searchable,\n getText: (row) => {\n const keys = props.searchKeys ?? props.columns.map(c => c.key)\n return keys.map(key => getCellValue(row, key))\n },\n})\nconst filteredData = textSearch.filteredItems\n\nconst tableSort = useSortedItems({\n items: filteredData,\n sort: activeSort,\n enabled: () => {\n const sort = activeSort.value\n return Boolean(sort?.direction && props.columns.some(c => c.key === sort.key))\n },\n getValue: (row, key) => getCellValue(row, key),\n})\nconst sortedData = tableSort.sortedItems\n\n// Pagination\nconst paginatedData = computed(() => {\n if (!props.pagination) return sortedData.value\n const { page, pageSize } = props.pagination\n const start = (page - 1) * pageSize\n return sortedData.value.slice(start, start + pageSize)\n})\n\nconst totalPages = computed(() => {\n if (!props.pagination) return 1\n return Math.max(1, Math.ceil(sortedData.value.length / props.pagination.pageSize))\n})\n\nfunction handleSort(col: DataFrameColumn) {\n if (!col.sortable && !props.sortable) return\n\n const current = activeSort.value\n let next: SortState | null\n\n if (current?.key === col.key) {\n if (current.direction === 'asc') {\n next = { key: col.key, direction: 'desc' }\n } else if (current.direction === 'desc') {\n next = null\n } else {\n next = { key: col.key, direction: 'asc' }\n }\n } else {\n next = { key: col.key, direction: 'asc' }\n }\n\n if (props.sort === undefined) {\n internalSort.value = next\n }\n emit('update:sort', next)\n}\n\nfunction handlePageChange(page: number) {\n if (!props.pagination) return\n emit('update:pagination', { ...props.pagination, page })\n}\n\nfunction isColumnSortable(col: DataFrameColumn): boolean {\n return col.sortable === true || (props.sortable && col.sortable !== false)\n}\n\n// Selection\nconst pageRowKeys = computed(() =>\n paginatedData.value.map((row, i) => getRowKey(row, i))\n)\nconst rowSelection = useListSelection({\n selected: () => props.selectedKeys,\n items: pageRowKeys,\n})\nconst allSelected = computed(() => {\n return props.selectable && rowSelection.isAllSelected.value\n})\n\nconst selectIndeterminate = computed(() => {\n return props.selectedKeys.length > 0 && !allSelected.value\n})\n\nfunction toggleSelectAll() {\n if (allSelected.value) {\n emit('update:selectedKeys', [])\n } else {\n emit('update:selectedKeys', rowSelection.itemValues.value)\n }\n}\n\nfunction toggleRowSelect(key: string | number) {\n emit('update:selectedKeys', rowSelection.toggleValue(key))\n}\n\nconst wrapperStyle = computed(() => {\n if (!props.maxHeight) return undefined\n const maxHeight = typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight\n return { maxHeight, overflowY: 'auto' as const }\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-dataframe',\n { 'mint-dataframe--bordered': bordered },\n ]\"\n >\n <!-- Search header -->\n <div v-if=\"searchable\" class=\"mint-dataframe__header\">\n <div class=\"mint-dataframe__search-wrapper\">\n <svg class=\"mint-dataframe__search-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\" /><path d=\"m21 21-4.34-4.34\" />\n </svg>\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n class=\"mint-dataframe__search\"\n :placeholder=\"searchPlaceholder\"\n />\n </div>\n </div>\n\n <!-- Table wrapper -->\n <div class=\"mint-dataframe__table-wrapper\" :style=\"wrapperStyle\">\n <table class=\"mint-dataframe__table\">\n <thead :class=\"['mint-dataframe__thead', { 'mint-dataframe__thead--sticky': stickyHeader }]\">\n <tr>\n <th v-if=\"selectable\" class=\"mint-dataframe__th mint-dataframe__th--checkbox\">\n <input\n type=\"checkbox\"\n :checked=\"allSelected\"\n :indeterminate=\"selectIndeterminate\"\n class=\"mint-dataframe__checkbox\"\n @change=\"toggleSelectAll\"\n />\n </th>\n <th\n v-for=\"col in columns\"\n :key=\"col.key\"\n :class=\"[\n 'mint-dataframe__th',\n `mint-dataframe__th--${size}`,\n `mint-dataframe__th--align-${col.align ?? 'left'}`,\n {\n 'mint-dataframe__th--sortable': isColumnSortable(col),\n 'mint-dataframe__th--sorted': activeSort?.key === col.key && activeSort?.direction,\n },\n ]\"\n :style=\"{\n width: col.width ? (typeof col.width === 'number' ? `${col.width}px` : col.width) : undefined,\n minWidth: col.minWidth ? (typeof col.minWidth === 'number' ? `${col.minWidth}px` : col.minWidth) : undefined,\n }\"\n @click=\"isColumnSortable(col) ? handleSort(col) : undefined\"\n >\n <slot :name=\"`header-${col.key}`\" :column=\"col\">\n <div class=\"mint-dataframe__th-content\">\n <span>{{ col.label }}</span>\n <svg\n v-if=\"isColumnSortable(col)\"\n :class=\"[\n 'mint-dataframe__sort-icon',\n {\n 'mint-dataframe__sort-icon--asc': activeSort?.key === col.key && activeSort?.direction === 'asc',\n 'mint-dataframe__sort-icon--desc': activeSort?.key === col.key && activeSort?.direction === 'desc',\n },\n ]\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m21 16-4 4-4-4\" /><path d=\"M17 20V4\" /><path d=\"m3 8 4-4 4 4\" /><path d=\"M7 4v16\" />\n </svg>\n </div>\n </slot>\n </th>\n </tr>\n </thead>\n\n <tbody>\n <tr\n v-for=\"(row, rowIndex) in paginatedData\"\n :key=\"getRowKey(row, rowIndex)\"\n :class=\"[\n 'mint-dataframe__row',\n {\n 'mint-dataframe__row--striped': striped && rowIndex % 2 === 1,\n 'mint-dataframe__row--selected': rowSelection.isSelected(getRowKey(row, rowIndex)),\n },\n ]\"\n @click=\"emit('row-click', row, rowIndex)\"\n >\n <td v-if=\"selectable\" class=\"mint-dataframe__td mint-dataframe__td--checkbox\">\n <input\n type=\"checkbox\"\n :checked=\"rowSelection.isSelected(getRowKey(row, rowIndex))\"\n class=\"mint-dataframe__checkbox\"\n @click.stop\n @change=\"toggleRowSelect(getRowKey(row, rowIndex))\"\n />\n </td>\n <td\n v-for=\"col in columns\"\n :key=\"col.key\"\n :class=\"[\n 'mint-dataframe__td',\n `mint-dataframe__td--${size}`,\n `mint-dataframe__td--align-${col.align ?? 'left'}`,\n { 'mint-dataframe__td--ellipsis': col.ellipsis },\n ]\"\n @click.stop=\"emit('cell-click', getCellValue(row, col.key), col, row)\"\n >\n <slot :name=\"`cell-${col.key}`\" :value=\"getCellValue(row, col.key)\" :row=\"row\" :column=\"col\" :index=\"rowIndex\">\n {{ formatCell(col, row, rowIndex) }}\n </slot>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- Empty state -->\n <div v-if=\"paginatedData.length === 0 && !loading\" class=\"mint-dataframe__empty\">\n <slot name=\"empty\">\n <svg class=\"mint-dataframe__empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"22 12 16 12 14 15 10 15 8 12 2 12\" /><path d=\"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z\" />\n </svg>\n <p class=\"mint-dataframe__empty-text\">{{ emptyText }}</p>\n </slot>\n </div>\n\n <!-- Loading overlay -->\n <div v-if=\"loading\" class=\"mint-dataframe__loading\">\n <slot name=\"loading\">\n <svg class=\"mint-dataframe__loading-spinner\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"3\" opacity=\"0.25\" />\n <path d=\"M12 2a10 10 0 0 1 10 10\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" />\n </svg>\n </slot>\n </div>\n </div>\n\n <!-- Footer / Pagination -->\n <div v-if=\"pagination\" class=\"mint-dataframe__footer\">\n <slot name=\"footer\">\n <span class=\"mint-dataframe__page-info\">\n {{ (pagination.page - 1) * pagination.pageSize + 1 }}–{{ Math.min(pagination.page * pagination.pageSize, sortedData.length) }}\n of {{ sortedData.length }}\n </span>\n <div class=\"mint-dataframe__page-controls\">\n <button\n type=\"button\"\n class=\"mint-dataframe__page-btn\"\n :disabled=\"pagination.page <= 1\"\n @click=\"handlePageChange(pagination.page - 1)\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <span class=\"mint-dataframe__page-number\">{{ pagination.page }} / {{ totalPages }}</span>\n <button\n type=\"button\"\n class=\"mint-dataframe__page-btn\"\n :disabled=\"pagination.page >= totalPages\"\n @click=\"handlePageChange(pagination.page + 1)\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n </div>\n </slot>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/dataframe.css';\n</style>\n","<script setup lang=\"ts\">\n/** Data table with sortable columns, search, pagination, and optional row selection. */\nimport { ref, computed } from 'vue'\nimport type { DataFrameColumn, SortState, PaginationState } from '../types'\nimport { useSortedItems } from '../composables/useSortedItems'\nimport { useTextSearch } from '../composables/useTextSearch'\nimport { useListSelection } from '../composables/useListSelection'\n\n/**\n * DataFrame - Data table with sorting, searching, pagination, and row selection.\n *\n * @example\n * ```vue\n * <DataFrame\n * :data=\"rows\"\n * :columns=\"[\n * { key: 'name', label: 'Name', sortable: true },\n * { key: 'age', label: 'Age', align: 'right' }\n * ]\"\n * sortable\n * searchable\n * :pagination=\"{ page: 1, pageSize: 10, total: 100 }\"\n * />\n * ```\n */\ninterface Props {\n /** Array of data objects */\n data: Record<string, unknown>[]\n /** Column definitions */\n columns: DataFrameColumn[]\n /** Unique key for each row (property name or function) */\n rowKey?: string | ((row: Record<string, unknown>) => string | number)\n /** Alternate row background colors */\n striped?: boolean\n /** Show table borders */\n bordered?: boolean\n /** Table size */\n size?: 'sm' | 'md' | 'lg'\n /** Make header sticky on scroll */\n stickyHeader?: boolean\n /** Maximum table height (enables vertical scroll) */\n maxHeight?: string | number\n /** Show loading overlay */\n loading?: boolean\n /** Empty state message */\n emptyText?: string\n /** Enable column sorting */\n sortable?: boolean\n /** Current sort state (controlled mode) */\n sort?: SortState | null\n /** Pagination state or false to disable */\n pagination?: PaginationState | false\n /** Enable search input */\n searchable?: boolean\n /** Search input placeholder */\n searchPlaceholder?: string\n /** Column keys to search (defaults to all) */\n searchKeys?: string[]\n /** Enable row selection with checkboxes */\n selectable?: boolean\n /** Currently selected row keys */\n selectedKeys?: (string | number)[]\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n striped: true,\n bordered: true,\n size: 'md',\n stickyHeader: false,\n loading: false,\n emptyText: 'No data',\n sortable: false,\n pagination: false,\n searchable: false,\n searchPlaceholder: 'Search...',\n selectable: false,\n selectedKeys: () => [],\n})\n\n/**\n * @event update:sort - Emitted when sort state changes\n * @event update:pagination - Emitted when page changes\n * @event update:selectedKeys - Emitted when row selection changes\n * @event row-click - Emitted when a row is clicked\n * @event cell-click - Emitted when a cell is clicked\n */\nconst emit = defineEmits<{\n 'update:sort': [sort: SortState | null]\n 'update:pagination': [pagination: PaginationState]\n 'update:selectedKeys': [keys: (string | number)[]]\n 'row-click': [row: Record<string, unknown>, index: number]\n 'cell-click': [value: unknown, column: DataFrameColumn, row: Record<string, unknown>]\n}>()\n\nconst searchQuery = ref('')\nconst internalSort = ref<SortState | null>(null)\n\nconst activeSort = computed(() => props.sort !== undefined ? props.sort : internalSort.value)\n\nfunction getRowKey(row: Record<string, unknown>, index: number): string | number {\n if (!props.rowKey) return index\n if (typeof props.rowKey === 'function') return props.rowKey(row)\n return row[props.rowKey] as string | number\n}\n\nfunction getCellValue(row: Record<string, unknown>, key: string): unknown {\n return key.split('.').reduce((obj: unknown, k) => {\n if (obj && typeof obj === 'object') return (obj as Record<string, unknown>)[k]\n return undefined\n }, row)\n}\n\nfunction formatCell(col: DataFrameColumn, row: Record<string, unknown>, index: number): string {\n const value = getCellValue(row, col.key)\n if (col.formatter) return col.formatter(value, row, index)\n if (value === null || value === undefined) return ''\n return String(value)\n}\n\nconst textSearch = useTextSearch({\n items: () => props.data,\n query: searchQuery,\n enabled: () => props.searchable,\n getText: (row) => {\n const keys = props.searchKeys ?? props.columns.map(c => c.key)\n return keys.map(key => getCellValue(row, key))\n },\n})\nconst filteredData = textSearch.filteredItems\n\nconst tableSort = useSortedItems({\n items: filteredData,\n sort: activeSort,\n enabled: () => {\n const sort = activeSort.value\n return Boolean(sort?.direction && props.columns.some(c => c.key === sort.key))\n },\n getValue: (row, key) => getCellValue(row, key),\n})\nconst sortedData = tableSort.sortedItems\n\n// Pagination\nconst paginatedData = computed(() => {\n if (!props.pagination) return sortedData.value\n const { page, pageSize } = props.pagination\n const start = (page - 1) * pageSize\n return sortedData.value.slice(start, start + pageSize)\n})\n\nconst totalPages = computed(() => {\n if (!props.pagination) return 1\n return Math.max(1, Math.ceil(sortedData.value.length / props.pagination.pageSize))\n})\n\nfunction handleSort(col: DataFrameColumn) {\n if (!col.sortable && !props.sortable) return\n\n const current = activeSort.value\n let next: SortState | null\n\n if (current?.key === col.key) {\n if (current.direction === 'asc') {\n next = { key: col.key, direction: 'desc' }\n } else if (current.direction === 'desc') {\n next = null\n } else {\n next = { key: col.key, direction: 'asc' }\n }\n } else {\n next = { key: col.key, direction: 'asc' }\n }\n\n if (props.sort === undefined) {\n internalSort.value = next\n }\n emit('update:sort', next)\n}\n\nfunction handlePageChange(page: number) {\n if (!props.pagination) return\n emit('update:pagination', { ...props.pagination, page })\n}\n\nfunction isColumnSortable(col: DataFrameColumn): boolean {\n return col.sortable === true || (props.sortable && col.sortable !== false)\n}\n\n// Selection\nconst pageRowKeys = computed(() =>\n paginatedData.value.map((row, i) => getRowKey(row, i))\n)\nconst rowSelection = useListSelection({\n selected: () => props.selectedKeys,\n items: pageRowKeys,\n})\nconst allSelected = computed(() => {\n return props.selectable && rowSelection.isAllSelected.value\n})\n\nconst selectIndeterminate = computed(() => {\n return props.selectedKeys.length > 0 && !allSelected.value\n})\n\nfunction toggleSelectAll() {\n if (allSelected.value) {\n emit('update:selectedKeys', [])\n } else {\n emit('update:selectedKeys', rowSelection.itemValues.value)\n }\n}\n\nfunction toggleRowSelect(key: string | number) {\n emit('update:selectedKeys', rowSelection.toggleValue(key))\n}\n\nconst wrapperStyle = computed(() => {\n if (!props.maxHeight) return undefined\n const maxHeight = typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight\n return { maxHeight, overflowY: 'auto' as const }\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-dataframe',\n { 'mint-dataframe--bordered': bordered },\n ]\"\n >\n <!-- Search header -->\n <div v-if=\"searchable\" class=\"mint-dataframe__header\">\n <div class=\"mint-dataframe__search-wrapper\">\n <svg class=\"mint-dataframe__search-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\" /><path d=\"m21 21-4.34-4.34\" />\n </svg>\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n class=\"mint-dataframe__search\"\n :placeholder=\"searchPlaceholder\"\n />\n </div>\n </div>\n\n <!-- Table wrapper -->\n <div class=\"mint-dataframe__table-wrapper\" :style=\"wrapperStyle\">\n <table class=\"mint-dataframe__table\">\n <thead :class=\"['mint-dataframe__thead', { 'mint-dataframe__thead--sticky': stickyHeader }]\">\n <tr>\n <th v-if=\"selectable\" class=\"mint-dataframe__th mint-dataframe__th--checkbox\">\n <input\n type=\"checkbox\"\n :checked=\"allSelected\"\n :indeterminate=\"selectIndeterminate\"\n class=\"mint-dataframe__checkbox\"\n @change=\"toggleSelectAll\"\n />\n </th>\n <th\n v-for=\"col in columns\"\n :key=\"col.key\"\n :class=\"[\n 'mint-dataframe__th',\n `mint-dataframe__th--${size}`,\n `mint-dataframe__th--align-${col.align ?? 'left'}`,\n {\n 'mint-dataframe__th--sortable': isColumnSortable(col),\n 'mint-dataframe__th--sorted': activeSort?.key === col.key && activeSort?.direction,\n },\n ]\"\n :style=\"{\n width: col.width ? (typeof col.width === 'number' ? `${col.width}px` : col.width) : undefined,\n minWidth: col.minWidth ? (typeof col.minWidth === 'number' ? `${col.minWidth}px` : col.minWidth) : undefined,\n }\"\n @click=\"isColumnSortable(col) ? handleSort(col) : undefined\"\n >\n <slot :name=\"`header-${col.key}`\" :column=\"col\">\n <div class=\"mint-dataframe__th-content\">\n <span>{{ col.label }}</span>\n <svg\n v-if=\"isColumnSortable(col)\"\n :class=\"[\n 'mint-dataframe__sort-icon',\n {\n 'mint-dataframe__sort-icon--asc': activeSort?.key === col.key && activeSort?.direction === 'asc',\n 'mint-dataframe__sort-icon--desc': activeSort?.key === col.key && activeSort?.direction === 'desc',\n },\n ]\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m21 16-4 4-4-4\" /><path d=\"M17 20V4\" /><path d=\"m3 8 4-4 4 4\" /><path d=\"M7 4v16\" />\n </svg>\n </div>\n </slot>\n </th>\n </tr>\n </thead>\n\n <tbody>\n <tr\n v-for=\"(row, rowIndex) in paginatedData\"\n :key=\"getRowKey(row, rowIndex)\"\n :class=\"[\n 'mint-dataframe__row',\n {\n 'mint-dataframe__row--striped': striped && rowIndex % 2 === 1,\n 'mint-dataframe__row--selected': rowSelection.isSelected(getRowKey(row, rowIndex)),\n },\n ]\"\n @click=\"emit('row-click', row, rowIndex)\"\n >\n <td v-if=\"selectable\" class=\"mint-dataframe__td mint-dataframe__td--checkbox\">\n <input\n type=\"checkbox\"\n :checked=\"rowSelection.isSelected(getRowKey(row, rowIndex))\"\n class=\"mint-dataframe__checkbox\"\n @click.stop\n @change=\"toggleRowSelect(getRowKey(row, rowIndex))\"\n />\n </td>\n <td\n v-for=\"col in columns\"\n :key=\"col.key\"\n :class=\"[\n 'mint-dataframe__td',\n `mint-dataframe__td--${size}`,\n `mint-dataframe__td--align-${col.align ?? 'left'}`,\n { 'mint-dataframe__td--ellipsis': col.ellipsis },\n ]\"\n @click.stop=\"emit('cell-click', getCellValue(row, col.key), col, row)\"\n >\n <slot :name=\"`cell-${col.key}`\" :value=\"getCellValue(row, col.key)\" :row=\"row\" :column=\"col\" :index=\"rowIndex\">\n {{ formatCell(col, row, rowIndex) }}\n </slot>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- Empty state -->\n <div v-if=\"paginatedData.length === 0 && !loading\" class=\"mint-dataframe__empty\">\n <slot name=\"empty\">\n <svg class=\"mint-dataframe__empty-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"22 12 16 12 14 15 10 15 8 12 2 12\" /><path d=\"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z\" />\n </svg>\n <p class=\"mint-dataframe__empty-text\">{{ emptyText }}</p>\n </slot>\n </div>\n\n <!-- Loading overlay -->\n <div v-if=\"loading\" class=\"mint-dataframe__loading\">\n <slot name=\"loading\">\n <svg class=\"mint-dataframe__loading-spinner\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"3\" opacity=\"0.25\" />\n <path d=\"M12 2a10 10 0 0 1 10 10\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" />\n </svg>\n </slot>\n </div>\n </div>\n\n <!-- Footer / Pagination -->\n <div v-if=\"pagination\" class=\"mint-dataframe__footer\">\n <slot name=\"footer\">\n <span class=\"mint-dataframe__page-info\">\n {{ (pagination.page - 1) * pagination.pageSize + 1 }}–{{ Math.min(pagination.page * pagination.pageSize, sortedData.length) }}\n of {{ sortedData.length }}\n </span>\n <div class=\"mint-dataframe__page-controls\">\n <button\n type=\"button\"\n class=\"mint-dataframe__page-btn\"\n :disabled=\"pagination.page <= 1\"\n @click=\"handlePageChange(pagination.page - 1)\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <span class=\"mint-dataframe__page-number\">{{ pagination.page }} / {{ totalPages }}</span>\n <button\n type=\"button\"\n class=\"mint-dataframe__page-btn\"\n :disabled=\"pagination.page >= totalPages\"\n @click=\"handlePageChange(pagination.page + 1)\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n </div>\n </slot>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/dataframe.css';\n</style>\n","<script setup lang=\"ts\">\n/** Wraps a form control with a label, optional hint text, and validation error display. */\nimport { computed } from 'vue'\n\ninterface Props {\n label?: string\n error?: string\n hint?: string\n required?: boolean\n showOptional?: boolean\n htmlFor?: string\n fieldId?: string\n}\n\nconst props = defineProps<Props>()\n\nconst errorId = computed(() =>\n props.error && props.fieldId ? `${props.fieldId}-error` : undefined\n)\n\nconst hintId = computed(() =>\n !props.error && props.hint && props.fieldId ? `${props.fieldId}-hint` : undefined\n)\n\nconst describedBy = computed(() => errorId.value ?? hintId.value)\n</script>\n\n<template>\n <div class=\"mint-form-field\">\n <div v-if=\"label\" class=\"mint-form-field__label-row\">\n <label\n :for=\"htmlFor\"\n class=\"mint-form-field__label\"\n >\n {{ label }}\n <span v-if=\"required\" class=\"mint-form-field__required\" aria-hidden=\"true\">*</span>\n </label>\n <span\n v-if=\"!required && showOptional\"\n class=\"mint-form-field__optional\"\n >\n optional\n </span>\n </div>\n\n <slot :described-by=\"describedBy\" />\n\n <p v-if=\"error\" :id=\"errorId\" class=\"mint-form-field__error\" role=\"alert\">\n {{ error }}\n </p>\n <p v-else-if=\"hint\" :id=\"hintId\" class=\"mint-form-field__hint\">\n {{ hint }}\n </p>\n </div>\n</template>\n\n<style>\n@import '../styles/components/form-field.css';\n</style>\n","<script setup lang=\"ts\">\n/** Wraps a form control with a label, optional hint text, and validation error display. */\nimport { computed } from 'vue'\n\ninterface Props {\n label?: string\n error?: string\n hint?: string\n required?: boolean\n showOptional?: boolean\n htmlFor?: string\n fieldId?: string\n}\n\nconst props = defineProps<Props>()\n\nconst errorId = computed(() =>\n props.error && props.fieldId ? `${props.fieldId}-error` : undefined\n)\n\nconst hintId = computed(() =>\n !props.error && props.hint && props.fieldId ? `${props.fieldId}-hint` : undefined\n)\n\nconst describedBy = computed(() => errorId.value ?? hintId.value)\n</script>\n\n<template>\n <div class=\"mint-form-field\">\n <div v-if=\"label\" class=\"mint-form-field__label-row\">\n <label\n :for=\"htmlFor\"\n class=\"mint-form-field__label\"\n >\n {{ label }}\n <span v-if=\"required\" class=\"mint-form-field__required\" aria-hidden=\"true\">*</span>\n </label>\n <span\n v-if=\"!required && showOptional\"\n class=\"mint-form-field__optional\"\n >\n optional\n </span>\n </div>\n\n <slot :described-by=\"describedBy\" />\n\n <p v-if=\"error\" :id=\"errorId\" class=\"mint-form-field__error\" role=\"alert\">\n {{ error }}\n </p>\n <p v-else-if=\"hint\" :id=\"hintId\" class=\"mint-form-field__hint\">\n {{ hint }}\n </p>\n </div>\n</template>\n\n<style>\n@import '../styles/components/form-field.css';\n</style>\n","<script setup lang=\"ts\">\n/** Inline alert banner with semantic icon, title, message slot, optional action button, and optional dismiss button. */\nimport { useSlots } from 'vue'\nimport type { AlertType } from '../types'\n\ninterface Props {\n type?: AlertType\n title?: string\n dismissible?: boolean\n actionLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n type: 'info',\n dismissible: false,\n})\n\nconst emit = defineEmits<{\n dismiss: []\n action: []\n}>()\n\nconst slots = useSlots()\nconst hasActions = () => !!props.actionLabel || !!slots.actions\n</script>\n\n<template>\n <div\n class=\"mint-alert\"\n :class=\"`mint-alert--${props.type}`\"\n role=\"alert\"\n >\n <!-- Lucide circle-check -->\n <svg v-if=\"props.type === 'success'\" class=\"mint-alert__icon mint-alert__icon--success\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m9 12 2 2 4-4\" />\n </svg>\n <!-- Lucide circle-x -->\n <svg v-else-if=\"props.type === 'error'\" class=\"mint-alert__icon mint-alert__icon--error\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m15 9-6 6\" /><path d=\"m9 9 6 6\" />\n </svg>\n <!-- Lucide triangle-alert -->\n <svg v-else-if=\"props.type === 'warning'\" class=\"mint-alert__icon mint-alert__icon--warning\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" /><path d=\"M12 9v4\" /><path d=\"M12 17h.01\" />\n </svg>\n <!-- Lucide info -->\n <svg v-else class=\"mint-alert__icon mint-alert__icon--info\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 16v-4\" /><path d=\"M12 8h.01\" />\n </svg>\n\n <div class=\"mint-alert__content\">\n <h4 v-if=\"props.title\" class=\"mint-alert__title\">\n {{ props.title }}\n </h4>\n <div class=\"mint-alert__message\">\n <slot />\n </div>\n </div>\n\n <div v-if=\"hasActions()\" class=\"mint-alert__actions\">\n <slot name=\"actions\">\n <button\n type=\"button\"\n class=\"mint-alert__action-btn\"\n :class=\"`mint-alert__action-btn--${props.type}`\"\n @click=\"emit('action')\"\n >\n {{ props.actionLabel }}\n </button>\n </slot>\n </div>\n\n <button\n v-if=\"props.dismissible\"\n type=\"button\"\n class=\"mint-alert__dismiss\"\n aria-label=\"Dismiss\"\n @click=\"emit('dismiss')\"\n >\n <svg class=\"mint-alert__dismiss-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/alert-box.css';\n</style>\n","<script setup lang=\"ts\">\n/** Inline alert banner with semantic icon, title, message slot, optional action button, and optional dismiss button. */\nimport { useSlots } from 'vue'\nimport type { AlertType } from '../types'\n\ninterface Props {\n type?: AlertType\n title?: string\n dismissible?: boolean\n actionLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n type: 'info',\n dismissible: false,\n})\n\nconst emit = defineEmits<{\n dismiss: []\n action: []\n}>()\n\nconst slots = useSlots()\nconst hasActions = () => !!props.actionLabel || !!slots.actions\n</script>\n\n<template>\n <div\n class=\"mint-alert\"\n :class=\"`mint-alert--${props.type}`\"\n role=\"alert\"\n >\n <!-- Lucide circle-check -->\n <svg v-if=\"props.type === 'success'\" class=\"mint-alert__icon mint-alert__icon--success\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m9 12 2 2 4-4\" />\n </svg>\n <!-- Lucide circle-x -->\n <svg v-else-if=\"props.type === 'error'\" class=\"mint-alert__icon mint-alert__icon--error\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m15 9-6 6\" /><path d=\"m9 9 6 6\" />\n </svg>\n <!-- Lucide triangle-alert -->\n <svg v-else-if=\"props.type === 'warning'\" class=\"mint-alert__icon mint-alert__icon--warning\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" /><path d=\"M12 9v4\" /><path d=\"M12 17h.01\" />\n </svg>\n <!-- Lucide info -->\n <svg v-else class=\"mint-alert__icon mint-alert__icon--info\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 16v-4\" /><path d=\"M12 8h.01\" />\n </svg>\n\n <div class=\"mint-alert__content\">\n <h4 v-if=\"props.title\" class=\"mint-alert__title\">\n {{ props.title }}\n </h4>\n <div class=\"mint-alert__message\">\n <slot />\n </div>\n </div>\n\n <div v-if=\"hasActions()\" class=\"mint-alert__actions\">\n <slot name=\"actions\">\n <button\n type=\"button\"\n class=\"mint-alert__action-btn\"\n :class=\"`mint-alert__action-btn--${props.type}`\"\n @click=\"emit('action')\"\n >\n {{ props.actionLabel }}\n </button>\n </slot>\n </div>\n\n <button\n v-if=\"props.dismissible\"\n type=\"button\"\n class=\"mint-alert__dismiss\"\n aria-label=\"Dismiss\"\n @click=\"emit('dismiss')\"\n >\n <svg class=\"mint-alert__dismiss-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/alert-box.css';\n</style>\n","<script setup lang=\"ts\">\n/** Preferred toast container driven by useToast, with auto-dismiss progress bar and click-to-close on each notification. */\nimport { useToast } from '../composables/useToast'\nimport type { Toast } from '../types'\n\nconst { toasts, dismiss } = useToast()\n\nconst accentColors: Record<Toast['type'], string> = {\n success: '#10B981',\n error: '#EF4444',\n warning: '#F59E0B',\n info: '#3B82F6',\n}\n</script>\n\n<template>\n <Teleport to=\"body\">\n <div class=\"mint-toast__container\">\n <TransitionGroup name=\"toast\">\n <div\n v-for=\"toast in toasts\"\n :key=\"toast.id\"\n :class=\"['mint-toast__item', `mint-toast__item--${toast.type}`]\"\n :style=\"{\n borderLeftColor: accentColors[toast.type],\n }\"\n role=\"alert\"\n @click=\"dismiss(toast.id)\"\n >\n <!-- Lucide circle-check -->\n <svg v-if=\"toast.type === 'success'\" class=\"mint-toast__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" :style=\"{ color: accentColors[toast.type] }\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m9 12 2 2 4-4\" />\n </svg>\n <!-- Lucide circle-x -->\n <svg v-else-if=\"toast.type === 'error'\" class=\"mint-toast__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" :style=\"{ color: accentColors[toast.type] }\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m15 9-6 6\" /><path d=\"m9 9 6 6\" />\n </svg>\n <!-- Lucide triangle-alert -->\n <svg v-else-if=\"toast.type === 'warning'\" class=\"mint-toast__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" :style=\"{ color: accentColors[toast.type] }\">\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" /><path d=\"M12 9v4\" /><path d=\"M12 17h.01\" />\n </svg>\n <!-- Lucide info -->\n <svg v-else class=\"mint-toast__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" :style=\"{ color: accentColors[toast.type] }\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 16v-4\" /><path d=\"M12 8h.01\" />\n </svg>\n <span class=\"mint-toast__message\">{{ toast.message }}</span>\n <span\n class=\"mint-toast__progress\"\n :style=\"{\n backgroundColor: accentColors[toast.type],\n animationDuration: `${toast.duration ?? 3500}ms`,\n }\"\n />\n </div>\n </TransitionGroup>\n </div>\n </Teleport>\n</template>\n\n<style>\n@import '../styles/components/toast.css';\n</style>\n","<script setup lang=\"ts\">\n/** Preferred toast container driven by useToast, with auto-dismiss progress bar and click-to-close on each notification. */\nimport { useToast } from '../composables/useToast'\nimport type { Toast } from '../types'\n\nconst { toasts, dismiss } = useToast()\n\nconst accentColors: Record<Toast['type'], string> = {\n success: '#10B981',\n error: '#EF4444',\n warning: '#F59E0B',\n info: '#3B82F6',\n}\n</script>\n\n<template>\n <Teleport to=\"body\">\n <div class=\"mint-toast__container\">\n <TransitionGroup name=\"toast\">\n <div\n v-for=\"toast in toasts\"\n :key=\"toast.id\"\n :class=\"['mint-toast__item', `mint-toast__item--${toast.type}`]\"\n :style=\"{\n borderLeftColor: accentColors[toast.type],\n }\"\n role=\"alert\"\n @click=\"dismiss(toast.id)\"\n >\n <!-- Lucide circle-check -->\n <svg v-if=\"toast.type === 'success'\" class=\"mint-toast__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" :style=\"{ color: accentColors[toast.type] }\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m9 12 2 2 4-4\" />\n </svg>\n <!-- Lucide circle-x -->\n <svg v-else-if=\"toast.type === 'error'\" class=\"mint-toast__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" :style=\"{ color: accentColors[toast.type] }\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"m15 9-6 6\" /><path d=\"m9 9 6 6\" />\n </svg>\n <!-- Lucide triangle-alert -->\n <svg v-else-if=\"toast.type === 'warning'\" class=\"mint-toast__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" :style=\"{ color: accentColors[toast.type] }\">\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" /><path d=\"M12 9v4\" /><path d=\"M12 17h.01\" />\n </svg>\n <!-- Lucide info -->\n <svg v-else class=\"mint-toast__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" :style=\"{ color: accentColors[toast.type] }\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 16v-4\" /><path d=\"M12 8h.01\" />\n </svg>\n <span class=\"mint-toast__message\">{{ toast.message }}</span>\n <span\n class=\"mint-toast__progress\"\n :style=\"{\n backgroundColor: accentColors[toast.type],\n animationDuration: `${toast.duration ?? 3500}ms`,\n }\"\n />\n </div>\n </TransitionGroup>\n </div>\n </Teleport>\n</template>\n\n<style>\n@import '../styles/components/toast.css';\n</style>\n","<script setup lang=\"ts\">\n/** Square icon-only button with required aria-label, loading spinner state, and ghost/primary/danger variants. */\nimport type { ButtonVariant, ButtonSize } from '../types'\n\ninterface Props {\n variant?: ButtonVariant\n size?: ButtonSize\n disabled?: boolean\n loading?: boolean\n label: string // Required for accessibility\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'ghost',\n size: 'md',\n disabled: false,\n loading: false,\n})\n\nconst emit = defineEmits<{\n click: [event: MouseEvent]\n}>()\n\nfunction handleClick(event: MouseEvent) {\n if (!props.disabled && !props.loading) {\n emit('click', event)\n }\n}\n</script>\n\n<template>\n <button\n type=\"button\"\n :disabled=\"disabled || loading\"\n :aria-label=\"label\"\n :title=\"label\"\n :class=\"[\n 'mint-icon-button',\n `mint-icon-button--${variant}`,\n `mint-icon-button--${size}`,\n (disabled || loading) ? 'mint-icon-button--disabled' : '',\n ]\"\n @click=\"handleClick\"\n >\n <svg\n v-if=\"loading\"\n :class=\"['mint-icon-button__spinner', `mint-icon-button__icon--${size}`]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n style=\"opacity: 0.25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n />\n <path\n style=\"opacity: 0.75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n <span v-else :class=\"`mint-icon-button__icon--${size}`\">\n <slot />\n </span>\n </button>\n</template>\n\n<style>\n@import '../styles/components/icon-button.css';\n</style>\n","<script setup lang=\"ts\">\n/** Square icon-only button with required aria-label, loading spinner state, and ghost/primary/danger variants. */\nimport type { ButtonVariant, ButtonSize } from '../types'\n\ninterface Props {\n variant?: ButtonVariant\n size?: ButtonSize\n disabled?: boolean\n loading?: boolean\n label: string // Required for accessibility\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'ghost',\n size: 'md',\n disabled: false,\n loading: false,\n})\n\nconst emit = defineEmits<{\n click: [event: MouseEvent]\n}>()\n\nfunction handleClick(event: MouseEvent) {\n if (!props.disabled && !props.loading) {\n emit('click', event)\n }\n}\n</script>\n\n<template>\n <button\n type=\"button\"\n :disabled=\"disabled || loading\"\n :aria-label=\"label\"\n :title=\"label\"\n :class=\"[\n 'mint-icon-button',\n `mint-icon-button--${variant}`,\n `mint-icon-button--${size}`,\n (disabled || loading) ? 'mint-icon-button--disabled' : '',\n ]\"\n @click=\"handleClick\"\n >\n <svg\n v-if=\"loading\"\n :class=\"['mint-icon-button__spinner', `mint-icon-button__icon--${size}`]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n style=\"opacity: 0.25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n />\n <path\n style=\"opacity: 0.75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n <span v-else :class=\"`mint-icon-button__icon--${size}`\">\n <slot />\n </span>\n </button>\n</template>\n\n<style>\n@import '../styles/components/icon-button.css';\n</style>\n","<script setup lang=\"ts\">\n/** Icon button that toggles between light and dark mode, swapping sun/moon icons to reflect the active theme. */\nimport { useTheme } from '../composables/useTheme'\nimport IconButton from './IconButton.vue'\n\ninterface Props {\n size?: 'sm' | 'md' | 'lg'\n}\n\nwithDefaults(defineProps<Props>(), {\n size: 'md',\n})\n\nconst { isDark, toggleTheme } = useTheme()\n</script>\n\n<template>\n <IconButton\n class=\"mint-theme-toggle\"\n variant=\"ghost\"\n :size=\"size\"\n :label=\"isDark ? 'Switch to light mode' : 'Switch to dark mode'\"\n @click=\"toggleTheme\"\n >\n <!-- Lucide sun -->\n <svg\n v-if=\"isDark\"\n class=\"mint-theme-toggle__icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"4\" /><path d=\"M12 2v2\" /><path d=\"M12 20v2\" /><path d=\"m4.93 4.93 1.41 1.41\" /><path d=\"m17.66 17.66 1.41 1.41\" /><path d=\"M2 12h2\" /><path d=\"M20 12h2\" /><path d=\"m6.34 17.66-1.41 1.41\" /><path d=\"m19.07 4.93-1.41 1.41\" />\n </svg>\n <!-- Lucide moon -->\n <svg\n v-else\n class=\"mint-theme-toggle__icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401\" />\n </svg>\n </IconButton>\n</template>\n\n<style>\n@import '../styles/components/theme-toggle.css';\n</style>\n","<script setup lang=\"ts\">\n/** Icon button that toggles between light and dark mode, swapping sun/moon icons to reflect the active theme. */\nimport { useTheme } from '../composables/useTheme'\nimport IconButton from './IconButton.vue'\n\ninterface Props {\n size?: 'sm' | 'md' | 'lg'\n}\n\nwithDefaults(defineProps<Props>(), {\n size: 'md',\n})\n\nconst { isDark, toggleTheme } = useTheme()\n</script>\n\n<template>\n <IconButton\n class=\"mint-theme-toggle\"\n variant=\"ghost\"\n :size=\"size\"\n :label=\"isDark ? 'Switch to light mode' : 'Switch to dark mode'\"\n @click=\"toggleTheme\"\n >\n <!-- Lucide sun -->\n <svg\n v-if=\"isDark\"\n class=\"mint-theme-toggle__icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"4\" /><path d=\"M12 2v2\" /><path d=\"M12 20v2\" /><path d=\"m4.93 4.93 1.41 1.41\" /><path d=\"m17.66 17.66 1.41 1.41\" /><path d=\"M2 12h2\" /><path d=\"M20 12h2\" /><path d=\"m6.34 17.66-1.41 1.41\" /><path d=\"m19.07 4.93-1.41 1.41\" />\n </svg>\n <!-- Lucide moon -->\n <svg\n v-else\n class=\"mint-theme-toggle__icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401\" />\n </svg>\n </IconButton>\n</template>\n\n<style>\n@import '../styles/components/theme-toggle.css';\n</style>\n","<script setup lang=\"ts\">\n/** Card whose body collapses with a chevron-rotate animation, with an optional inline toggle switch independent of expand state. */\nimport { ref, computed } from 'vue'\n\ninterface Props {\n title: string\n subtitle?: string\n defaultOpen?: boolean\n disabled?: boolean\n dense?: boolean\n icon?: string | string[]\n iconColor?: string\n iconBg?: string\n showToggle?: boolean\n toggleValue?: boolean\n toggleColor?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n defaultOpen: false,\n disabled: false,\n dense: false,\n showToggle: false,\n toggleValue: false,\n toggleColor: '',\n})\n\nconst emit = defineEmits<{\n 'update:toggleValue': [value: boolean]\n}>()\n\nconst isOpen = ref(props.defaultOpen)\n\nconst toggle = () => {\n if (!props.disabled) {\n isOpen.value = !isOpen.value\n }\n}\n\nconst handleToggleClick = (event: Event) => {\n event.stopPropagation()\n emit('update:toggleValue', !props.toggleValue)\n}\n\nconst headerClasses = computed(() => [\n 'mint-collapsible-card__header',\n props.disabled ? 'mint-collapsible-card__header--disabled' : '',\n])\n\nconst iconBgStyle = computed(() => ({\n backgroundColor: props.iconBg || 'var(--color-primary-soft)',\n}))\n\nconst iconColorStyle = computed(() => ({\n color: props.iconColor || 'var(--color-primary)',\n}))\n\nconst isSvgIcon = computed(() => {\n if (!props.icon) return false\n return Array.isArray(props.icon) || props.icon.startsWith('M') || props.icon.startsWith('m')\n})\n\nconst toggleTrackStyle = computed(() => {\n if (!props.toggleValue || !props.toggleColor) return {}\n return {\n backgroundColor: props.toggleColor,\n borderColor: props.toggleColor,\n }\n})\n</script>\n\n<template>\n <div :class=\"['mint-collapsible-card', dense ? 'mint-collapsible-card--dense' : '']\">\n <button\n type=\"button\"\n :class=\"headerClasses\"\n :disabled=\"disabled\"\n :aria-expanded=\"isOpen\"\n @click=\"toggle\"\n >\n <div class=\"mint-collapsible-card__title-section\">\n <!-- Icon badge -->\n <div v-if=\"icon\" class=\"mint-collapsible-card__icon-badge\" :style=\"iconBgStyle\">\n <svg\n v-if=\"isSvgIcon\"\n class=\"mint-collapsible-card__icon\"\n :style=\"iconColorStyle\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <template v-if=\"Array.isArray(icon)\">\n <path v-for=\"(d, i) in icon\" :key=\"i\" :d=\"d\" />\n </template>\n <path v-else :d=\"icon\" />\n </svg>\n <span v-else class=\"mint-collapsible-card__icon-text\" :style=\"iconColorStyle\">{{ icon }}</span>\n </div>\n\n <div class=\"mint-collapsible-card__titles\">\n <h3 class=\"mint-collapsible-card__title\">{{ title }}</h3>\n <p v-if=\"subtitle\" class=\"mint-collapsible-card__subtitle\">{{ subtitle }}</p>\n </div>\n </div>\n\n <div class=\"mint-collapsible-card__actions\">\n <!-- Toggle switch -->\n <div\n v-if=\"showToggle\"\n class=\"mint-collapsible-card__toggle\"\n @click=\"handleToggleClick\"\n >\n <div\n role=\"switch\"\n tabindex=\"0\"\n :aria-checked=\"toggleValue\"\n :class=\"[\n 'mint-collapsible-card__toggle-track',\n toggleValue ? 'mint-collapsible-card__toggle-track--on' : '',\n ]\"\n :style=\"toggleTrackStyle\"\n @keydown.enter.prevent=\"handleToggleClick\"\n @keydown.space.prevent=\"handleToggleClick\"\n >\n <span\n :class=\"[\n 'mint-collapsible-card__toggle-knob',\n toggleValue ? 'mint-collapsible-card__toggle-knob--on' : '',\n ]\"\n />\n </div>\n </div>\n\n <!-- Chevron -->\n <svg\n :class=\"[\n 'mint-collapsible-card__chevron',\n isOpen ? 'mint-collapsible-card__chevron--open' : '',\n ]\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n viewBox=\"0 0 24 24\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </div>\n </button>\n\n <Transition name=\"collapse\">\n <div v-show=\"isOpen\" class=\"mint-collapsible-card__body\">\n <div class=\"mint-collapsible-card__content\">\n <slot />\n </div>\n </div>\n </Transition>\n </div>\n</template>\n\n<style>\n@import '../styles/components/collapsible-card.css';\n</style>\n","<script setup lang=\"ts\">\n/** Card whose body collapses with a chevron-rotate animation, with an optional inline toggle switch independent of expand state. */\nimport { ref, computed } from 'vue'\n\ninterface Props {\n title: string\n subtitle?: string\n defaultOpen?: boolean\n disabled?: boolean\n dense?: boolean\n icon?: string | string[]\n iconColor?: string\n iconBg?: string\n showToggle?: boolean\n toggleValue?: boolean\n toggleColor?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n defaultOpen: false,\n disabled: false,\n dense: false,\n showToggle: false,\n toggleValue: false,\n toggleColor: '',\n})\n\nconst emit = defineEmits<{\n 'update:toggleValue': [value: boolean]\n}>()\n\nconst isOpen = ref(props.defaultOpen)\n\nconst toggle = () => {\n if (!props.disabled) {\n isOpen.value = !isOpen.value\n }\n}\n\nconst handleToggleClick = (event: Event) => {\n event.stopPropagation()\n emit('update:toggleValue', !props.toggleValue)\n}\n\nconst headerClasses = computed(() => [\n 'mint-collapsible-card__header',\n props.disabled ? 'mint-collapsible-card__header--disabled' : '',\n])\n\nconst iconBgStyle = computed(() => ({\n backgroundColor: props.iconBg || 'var(--color-primary-soft)',\n}))\n\nconst iconColorStyle = computed(() => ({\n color: props.iconColor || 'var(--color-primary)',\n}))\n\nconst isSvgIcon = computed(() => {\n if (!props.icon) return false\n return Array.isArray(props.icon) || props.icon.startsWith('M') || props.icon.startsWith('m')\n})\n\nconst toggleTrackStyle = computed(() => {\n if (!props.toggleValue || !props.toggleColor) return {}\n return {\n backgroundColor: props.toggleColor,\n borderColor: props.toggleColor,\n }\n})\n</script>\n\n<template>\n <div :class=\"['mint-collapsible-card', dense ? 'mint-collapsible-card--dense' : '']\">\n <button\n type=\"button\"\n :class=\"headerClasses\"\n :disabled=\"disabled\"\n :aria-expanded=\"isOpen\"\n @click=\"toggle\"\n >\n <div class=\"mint-collapsible-card__title-section\">\n <!-- Icon badge -->\n <div v-if=\"icon\" class=\"mint-collapsible-card__icon-badge\" :style=\"iconBgStyle\">\n <svg\n v-if=\"isSvgIcon\"\n class=\"mint-collapsible-card__icon\"\n :style=\"iconColorStyle\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <template v-if=\"Array.isArray(icon)\">\n <path v-for=\"(d, i) in icon\" :key=\"i\" :d=\"d\" />\n </template>\n <path v-else :d=\"icon\" />\n </svg>\n <span v-else class=\"mint-collapsible-card__icon-text\" :style=\"iconColorStyle\">{{ icon }}</span>\n </div>\n\n <div class=\"mint-collapsible-card__titles\">\n <h3 class=\"mint-collapsible-card__title\">{{ title }}</h3>\n <p v-if=\"subtitle\" class=\"mint-collapsible-card__subtitle\">{{ subtitle }}</p>\n </div>\n </div>\n\n <div class=\"mint-collapsible-card__actions\">\n <!-- Toggle switch -->\n <div\n v-if=\"showToggle\"\n class=\"mint-collapsible-card__toggle\"\n @click=\"handleToggleClick\"\n >\n <div\n role=\"switch\"\n tabindex=\"0\"\n :aria-checked=\"toggleValue\"\n :class=\"[\n 'mint-collapsible-card__toggle-track',\n toggleValue ? 'mint-collapsible-card__toggle-track--on' : '',\n ]\"\n :style=\"toggleTrackStyle\"\n @keydown.enter.prevent=\"handleToggleClick\"\n @keydown.space.prevent=\"handleToggleClick\"\n >\n <span\n :class=\"[\n 'mint-collapsible-card__toggle-knob',\n toggleValue ? 'mint-collapsible-card__toggle-knob--on' : '',\n ]\"\n />\n </div>\n </div>\n\n <!-- Chevron -->\n <svg\n :class=\"[\n 'mint-collapsible-card__chevron',\n isOpen ? 'mint-collapsible-card__chevron--open' : '',\n ]\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n viewBox=\"0 0 24 24\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </div>\n </button>\n\n <Transition name=\"collapse\">\n <div v-show=\"isOpen\" class=\"mint-collapsible-card__body\">\n <div class=\"mint-collapsible-card__content\">\n <slot />\n </div>\n </div>\n </Transition>\n </div>\n</template>\n\n<style>\n@import '../styles/components/collapsible-card.css';\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { FormFieldSchema } from '../../types/form-builder'\nimport type { UseFormReturn } from '../../composables/useForm'\nimport { getFieldRegistryEntry } from '../../composables/formBuilderRegistry'\nimport FormField from '../FormField.vue'\n\ninterface Props {\n field: FormFieldSchema\n resolvedProps: Record<string, unknown>\n form: UseFormReturn<Record<string, unknown>>\n}\n\nconst props = defineProps<Props>()\n\nconst entry = computed(() => getFieldRegistryEntry(props.field.type))\n\nconst errorMessage = computed(() => {\n const name = props.field.name\n return props.form.touched[name] ? props.form.errors[name] : null\n})\n\nfunction handleUpload(files: File[]) {\n props.form.setFieldValue(props.field.name, files)\n}\n</script>\n\n<template>\n <FormField\n :label=\"field.label\"\n :error=\"errorMessage ?? undefined\"\n :hint=\"field.hint\"\n :required=\"!!field.validation?.required\"\n :html-for=\"field.name\"\n >\n <slot :name=\"`field:${field.name}`\" :field=\"field\" :form=\"form\" :field-props=\"form.getFieldProps(field.name)\">\n <component\n :is=\"entry.component\"\n v-if=\"entry.vModel\"\n v-bind=\"resolvedProps\"\n />\n <component\n :is=\"entry.component\"\n v-else\n v-bind=\"resolvedProps\"\n @upload=\"handleUpload\"\n />\n </slot>\n </FormField>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { FormFieldSchema } from '../../types/form-builder'\nimport type { UseFormReturn } from '../../composables/useForm'\nimport { getFieldRegistryEntry } from '../../composables/formBuilderRegistry'\nimport FormField from '../FormField.vue'\n\ninterface Props {\n field: FormFieldSchema\n resolvedProps: Record<string, unknown>\n form: UseFormReturn<Record<string, unknown>>\n}\n\nconst props = defineProps<Props>()\n\nconst entry = computed(() => getFieldRegistryEntry(props.field.type))\n\nconst errorMessage = computed(() => {\n const name = props.field.name\n return props.form.touched[name] ? props.form.errors[name] : null\n})\n\nfunction handleUpload(files: File[]) {\n props.form.setFieldValue(props.field.name, files)\n}\n</script>\n\n<template>\n <FormField\n :label=\"field.label\"\n :error=\"errorMessage ?? undefined\"\n :hint=\"field.hint\"\n :required=\"!!field.validation?.required\"\n :html-for=\"field.name\"\n >\n <slot :name=\"`field:${field.name}`\" :field=\"field\" :form=\"form\" :field-props=\"form.getFieldProps(field.name)\">\n <component\n :is=\"entry.component\"\n v-if=\"entry.vModel\"\n v-bind=\"resolvedProps\"\n />\n <component\n :is=\"entry.component\"\n v-else\n v-bind=\"resolvedProps\"\n @upload=\"handleUpload\"\n />\n </slot>\n </FormField>\n</template>\n","import type { FormSchema } from '../types/form-builder'\n\nexport function recordValuesEqualForKeys(\n left: Record<string, unknown>,\n right: Record<string, unknown>,\n keys: readonly string[],\n): boolean {\n for (const key of keys) {\n if (!valuesEqual(left[key], right[key])) return false\n }\n return true\n}\n\nexport function pickRecordKeys(\n source: Record<string, unknown>,\n keys: readonly string[],\n): Record<string, unknown> {\n return Object.fromEntries(keys.map(key => [key, source[key]]))\n}\n\nexport function pickExistingRecordKeys(\n source: Record<string, unknown>,\n keys: readonly string[],\n): Record<string, unknown> {\n return Object.fromEntries(\n keys\n .filter(key => Object.prototype.hasOwnProperty.call(source, key))\n .map(key => [key, source[key]]),\n )\n}\n\nexport function formSchemaFieldNames(schema: FormSchema): string[] {\n const sections = schema.steps\n ? schema.steps.flatMap(step => step.sections)\n : schema.sections\n return sections.flatMap(section => section.fields.map(field => field.name))\n}\n\nfunction valuesEqual(left: unknown, right: unknown): boolean {\n if (Object.is(left, right)) return true\n if (Array.isArray(left) && Array.isArray(right)) {\n return left.length === right.length && left.every((item, index) => valuesEqual(item, right[index]))\n }\n if (isRecord(left) && isRecord(right)) {\n return recordValuesEqualForKeys(left, right, [...new Set([...Object.keys(left), ...Object.keys(right)])])\n }\n return false\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","<script setup lang=\"ts\">\n/**\n * Tabbed settings modal with three usage modes:\n *\n * 1. Schema-driven (recommended) — pass `schema`, compact `controls`, or a\n * complete `defineControlModel()` result plus `v-model:values`. Each\n * group/section becomes a tab; fields auto-render via the SDK form field\n * registry (text, select, number, toggle, molecule, concentration, …).\n * Conditional visibility, validation, and dynamic options come for free.\n *\n * 2. Manual tabs + slots — pass `tabs` and a `<template #tab-{id}>` slot\n * per tab. Use this when you need bespoke widgets the form-builder\n * registry doesn't cover (or for legacy plugins).\n *\n * 3. Appearance only — `showAppearance` (default true) renders the built-in\n * theme / palette / table-density tab. Works alongside both modes above.\n *\n * Layout: `horizontal` (underline tabs, default) or `vertical` (sidebar rail\n * with optional icons + descriptions, recommended for 5+ groups).\n */\nimport { ref, computed, watch } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport FormFieldRendererInternal from './internal/FormFieldRendererInternal.vue'\nimport { useFormBuilder } from '../composables/useFormBuilder'\nimport {\n controlsToSettingsSchema,\n defineControlModel,\n mergeControlWorkspaceOptions,\n type ControlModel,\n type ControlModelBinding,\n type ControlSchema,\n type ControlWorkspaceOptions,\n} from '../composables/useControlSchema'\nimport { useSettingsStore, colorPalettes } from '../stores/settings'\nimport {\n formSchemaFieldNames,\n pickExistingRecordKeys,\n pickRecordKeys,\n recordValuesEqualForKeys,\n} from '../utils/formModelSync'\nimport type {\n ThemeMode,\n ColorPalette,\n TableDensity,\n SettingsTab,\n SettingsTabInput,\n SettingsModalLayout,\n SettingsModalSchema,\n FormSchema,\n FormSectionSchema,\n FormEnhancements,\n} from '../types'\nimport { normalizeItemInput } from '../utils/items'\n\n// Map our settings groups onto the form-builder's flat-section shape.\n// `title: ''` because the rail/pane header already shows the group name.\nfunction buildFlatSchema(schema: SettingsModalSchema): FormSchema {\n return {\n sections: schema.groups.map<FormSectionSchema>((g) => ({\n id: g.id,\n title: '',\n fields: g.fields,\n columns: g.columns,\n condition: g.condition,\n })),\n }\n}\n\ninterface Props {\n modelValue: boolean\n title?: string\n /** Manual tab descriptors. Ignored when `schema` is set (groups become tabs). */\n tabs?: SettingsTabInput[]\n showAppearance?: boolean\n size?: 'md' | 'lg' | 'xl'\n layout?: SettingsModalLayout\n /** Declarative schema — fields auto-render via SDK form components. */\n schema?: SettingsModalSchema\n /** Model returned by defineControlModel(), or a raw nested ControlModel for one-step settings generation. */\n model?: ControlModel | ControlModelBinding\n /** Compact controls model — converted to `schema` when `schema` is not set. */\n controls?: ControlSchema\n /** Conversion options for `controls`, such as section labels, columns, and shared initialValues. */\n controlOptions?: ControlWorkspaceOptions\n /** Two-way bound values when `schema`, `model`, or `controls` is set. */\n values?: Record<string, unknown>\n /** Optional dynamic enhancements (validators, dynamic options, callbacks). */\n enhancements?: FormEnhancements<Record<string, unknown>>\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: 'Settings',\n tabs: () => [],\n showAppearance: true,\n size: 'lg',\n layout: 'horizontal',\n model: undefined,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n 'update:values': [data: Record<string, unknown>]\n close: []\n}>()\n\nconst settings = useSettingsStore()\n\nconst APPEARANCE_TAB_ID = 'appearance'\n\nconst APPEARANCE_TAB: SettingsTab = {\n id: APPEARANCE_TAB_ID,\n label: 'Appearance',\n description: 'Theme, color palette, and table density',\n}\n\nconst isVertical = computed(() => props.layout === 'vertical')\n\n// Per-instance prefix for ARIA tab/tabpanel id wiring. Stable across renders.\nconst uid = `mint-settings-${Math.random().toString(36).slice(2, 9)}`\nconst tabId = (id: string) => `${uid}-tab-${id}`\nconst panelId = (id: string) => `${uid}-panel-${id}`\n\nconst resolvedModel = computed<ControlModelBinding | undefined>(() => {\n if (props.model === undefined) return undefined\n return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)\n})\nconst resolvedControls = computed<ControlSchema | undefined>(() =>\n props.controls ?? resolvedModel.value?.controls,\n)\nconst resolvedControlOptions = computed<ControlWorkspaceOptions>(() =>\n mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, props.controlOptions)\n)\nconst settingsSchema = computed<SettingsModalSchema | undefined>(() =>\n props.schema ?? (\n resolvedControls.value\n ? controlsToSettingsSchema(resolvedControls.value, resolvedControlOptions.value)\n : undefined\n ),\n)\nconst isSchemaDriven = computed(() => !!settingsSchema.value)\nconst resolvedValues = computed<Record<string, unknown>>(() => ({\n ...(resolvedControlOptions.value.initialValues ?? {}),\n ...(props.values ?? {}),\n}))\n\n// Schema-driven mode keeps one form-builder instance and syncs schema changes\n// into it, so callers can swap groups/fields without remounting the modal.\nconst builder = useFormBuilder(\n settingsSchema.value ? buildFlatSchema(settingsSchema.value) : { sections: [] },\n resolvedValues.value,\n props.enhancements,\n)\n\nwatch(\n () => settingsSchema.value,\n (schema) => {\n if (!schema) {\n builder.updateSchema({ sections: [] }, {})\n return\n }\n\n const flatSchema = buildFlatSchema(schema)\n const fieldNames = formSchemaFieldNames(flatSchema)\n const sourceValues = props.values === undefined\n ? {\n ...resolvedValues.value,\n ...(builder.form.data as Record<string, unknown>),\n }\n : resolvedValues.value\n\n builder.updateSchema(flatSchema, pickExistingRecordKeys(sourceValues ?? {}, fieldNames))\n },\n { deep: true },\n)\n\nwatch(\n () => ({ ...resolvedValues.value }),\n (data) => {\n if (!settingsSchema.value) return\n const fieldNames = builderFieldNames()\n if (recordValuesEqualForKeys(data, builder.form.data as Record<string, unknown>, fieldNames)) return\n builder.reset(pickRecordKeys(data, fieldNames))\n },\n { deep: true },\n)\n\nwatch(\n () => ({ ...builder.form.data }),\n (data) => {\n if (settingsSchema.value) emit('update:values', data as Record<string, unknown>)\n },\n { deep: true },\n)\n\n// Schema groups whose `condition` evaluates false against the current data are\n// dropped from the rail entirely — same semantics as section visibility in\n// FormBuilder. Manual `tabs` have no condition mechanism, so they pass through.\nconst visibleSchemaGroups = computed(() =>\n settingsSchema.value\n ? settingsSchema.value.groups.filter((g) => builder.isSectionVisible(g.id))\n : [],\n)\n\nconst manualTabs = computed<SettingsTab[]>(() => props.tabs.map(normalizeItemInput))\n\nconst allTabs = computed<SettingsTab[]>(() => {\n const base: SettingsTab[] = settingsSchema.value\n ? visibleSchemaGroups.value.map((g) => ({ id: g.id, label: g.label, icon: g.icon, description: g.description }))\n : manualTabs.value\n return props.showAppearance ? [...base, APPEARANCE_TAB] : base\n})\n\nconst activeTab = ref(allTabs.value[0]?.id || APPEARANCE_TAB_ID)\n\nconst activeTabMeta = computed<SettingsTab | undefined>(() =>\n allTabs.value.find((t) => t.id === activeTab.value),\n)\n\n// If the active tab vanishes (group hidden by condition), drop back to the first available.\nwatch(allTabs, (tabs) => {\n if (!tabs.some((t) => t.id === activeTab.value)) {\n activeTab.value = tabs[0]?.id ?? APPEARANCE_TAB_ID\n }\n})\n\nconst activeGroup = computed(() =>\n visibleSchemaGroups.value.find((g) => g.id === activeTab.value),\n)\n\nconst activeGroupVisibleFields = computed(() => {\n if (!activeGroup.value) return []\n return activeGroup.value.fields.filter((f) => builder.isFieldVisible(f.name))\n})\n\nconst themeOptions: { value: ThemeMode; label: string }[] = [\n { value: 'light', label: 'Light' },\n { value: 'dark', label: 'Dark' },\n { value: 'system', label: 'System' },\n]\n\nconst densityOptions: { value: TableDensity; label: string }[] = [\n { value: 'compact', label: 'Compact' },\n { value: 'normal', label: 'Normal' },\n { value: 'comfortable', label: 'Comfortable' },\n]\n\nfunction handleClose() {\n emit('update:modelValue', false)\n emit('close')\n}\n\nfunction builderFieldNames(): string[] {\n return builder.fields.map(field => field.name)\n}\n\nfunction isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {\n return 'controls' in model && 'controlOptions' in model\n}\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :size=\"size\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @close=\"handleClose\"\n >\n <div :class=\"['mint-settings-modal', `mint-settings-modal--${layout}`]\">\n <div\n v-if=\"!isVertical && allTabs.length > 1\"\n class=\"mint-settings-modal__tabs\"\n >\n <button\n v-for=\"tab in allTabs\"\n :key=\"tab.id\"\n type=\"button\"\n :class=\"['mint-settings-modal__tab', { 'mint-settings-modal__tab--active': activeTab === tab.id }]\"\n @click=\"activeTab = tab.id\"\n >\n {{ tab.label }}\n </button>\n </div>\n\n <div\n v-else-if=\"isVertical\"\n class=\"mint-settings-modal__rail\"\n role=\"tablist\"\n aria-orientation=\"vertical\"\n aria-label=\"Settings sections\"\n >\n <button\n v-for=\"tab in allTabs\"\n :id=\"tabId(tab.id)\"\n :key=\"tab.id\"\n type=\"button\"\n role=\"tab\"\n :class=\"['mint-settings-modal__rail-item', { 'mint-settings-modal__rail-item--active': activeTab === tab.id }]\"\n :aria-selected=\"activeTab === tab.id\"\n :aria-controls=\"panelId(tab.id)\"\n :tabindex=\"activeTab === tab.id ? 0 : -1\"\n @click=\"activeTab = tab.id\"\n >\n <span class=\"mint-settings-modal__rail-item-icon\" aria-hidden=\"true\">\n <span v-if=\"tab.icon\" v-html=\"tab.icon\" />\n </span>\n <span class=\"mint-settings-modal__rail-item-text\">\n <span class=\"mint-settings-modal__rail-item-label\">{{ tab.label }}</span>\n <span v-if=\"tab.description\" class=\"mint-settings-modal__rail-item-description\">\n {{ tab.description }}\n </span>\n </span>\n </button>\n </div>\n\n <!-- Vertical mode wraps content in a tabpanel <section> with a header bar; horizontal mode stays a plain <div>. -->\n <component\n :is=\"isVertical ? 'section' : 'div'\"\n :class=\"isVertical ? 'mint-settings-modal__pane' : 'mint-settings-modal__content'\"\n :id=\"isVertical && activeTabMeta ? panelId(activeTabMeta.id) : undefined\"\n :role=\"isVertical ? 'tabpanel' : undefined\"\n :aria-labelledby=\"isVertical && activeTabMeta ? tabId(activeTabMeta.id) : undefined\"\n :tabindex=\"isVertical ? 0 : undefined\"\n >\n <header\n v-if=\"isVertical && activeTabMeta?.label\"\n class=\"mint-settings-modal__pane-header\"\n >\n <h4 class=\"mint-settings-modal__pane-title\">{{ activeTabMeta.label }}</h4>\n <p v-if=\"activeTabMeta.description\" class=\"mint-settings-modal__pane-subtitle\">\n {{ activeTabMeta.description }}\n </p>\n </header>\n\n <div :class=\"isVertical ? 'mint-settings-modal__pane-body' : null\">\n <div\n v-if=\"isSchemaDriven && activeGroup\"\n class=\"mint-settings-modal__group-grid\"\n :style=\"{ '--mint-settings-cols': activeGroup.columns ?? 1 }\"\n >\n <template v-for=\"field in activeGroupVisibleFields\" :key=\"field.name\">\n <div :style=\"field.colSpan ? { gridColumn: `span ${field.colSpan}` } : undefined\">\n <slot\n :name=\"`field:${field.name}`\"\n :field=\"field\"\n :form=\"builder.form\"\n :field-props=\"builder.form.getFieldProps(field.name)\"\n >\n <FormFieldRendererInternal\n :field=\"field\"\n :resolved-props=\"builder.getResolvedFieldProps(field)\"\n :form=\"builder.form\"\n />\n </slot>\n </div>\n </template>\n </div>\n\n <template v-else-if=\"!isSchemaDriven\">\n <template v-for=\"tab in manualTabs\" :key=\"tab.id\">\n <div v-show=\"activeTab === tab.id\">\n <slot :name=\"`tab-${tab.id}`\" />\n </div>\n </template>\n </template>\n\n <div v-if=\"showAppearance\" v-show=\"activeTab === APPEARANCE_TAB_ID\">\n <div class=\"mint-settings-modal__section\">\n <div class=\"mint-settings-modal__section-label\">Theme</div>\n <div class=\"mint-settings-modal__option-group\">\n <button\n v-for=\"opt in themeOptions\"\n :key=\"opt.value\"\n type=\"button\"\n :class=\"['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.theme === opt.value }]\"\n @click=\"settings.theme = opt.value\"\n >\n {{ opt.label }}\n </button>\n </div>\n </div>\n\n <div class=\"mint-settings-modal__section\">\n <div class=\"mint-settings-modal__section-label\">Color Palette</div>\n <div class=\"mint-settings-modal__option-group\">\n <button\n v-for=\"(palette, key) in colorPalettes\"\n :key=\"key\"\n type=\"button\"\n :class=\"['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.colorPalette === key }]\"\n @click=\"settings.colorPalette = key as ColorPalette\"\n >\n {{ palette.name }}\n </button>\n </div>\n </div>\n\n <div class=\"mint-settings-modal__section\">\n <div class=\"mint-settings-modal__section-label\">Table Density</div>\n <div class=\"mint-settings-modal__option-group\">\n <button\n v-for=\"opt in densityOptions\"\n :key=\"opt.value\"\n type=\"button\"\n :class=\"['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.tableDensity === opt.value }]\"\n @click=\"settings.tableDensity = opt.value\"\n >\n {{ opt.label }}\n </button>\n </div>\n <p class=\"mint-settings-modal__note\">Adjusts row height in data tables.</p>\n </div>\n\n <slot name=\"appearance\" />\n </div>\n </div>\n </component>\n </div>\n <div v-if=\"$slots.footer\" class=\"mint-settings-modal__footer\">\n <slot\n name=\"footer\"\n :values=\"builder.form.data\"\n :close=\"handleClose\"\n />\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/settings-modal.css';\n</style>\n","<script setup lang=\"ts\">\n/**\n * Tabbed settings modal with three usage modes:\n *\n * 1. Schema-driven (recommended) — pass `schema`, compact `controls`, or a\n * complete `defineControlModel()` result plus `v-model:values`. Each\n * group/section becomes a tab; fields auto-render via the SDK form field\n * registry (text, select, number, toggle, molecule, concentration, …).\n * Conditional visibility, validation, and dynamic options come for free.\n *\n * 2. Manual tabs + slots — pass `tabs` and a `<template #tab-{id}>` slot\n * per tab. Use this when you need bespoke widgets the form-builder\n * registry doesn't cover (or for legacy plugins).\n *\n * 3. Appearance only — `showAppearance` (default true) renders the built-in\n * theme / palette / table-density tab. Works alongside both modes above.\n *\n * Layout: `horizontal` (underline tabs, default) or `vertical` (sidebar rail\n * with optional icons + descriptions, recommended for 5+ groups).\n */\nimport { ref, computed, watch } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport FormFieldRendererInternal from './internal/FormFieldRendererInternal.vue'\nimport { useFormBuilder } from '../composables/useFormBuilder'\nimport {\n controlsToSettingsSchema,\n defineControlModel,\n mergeControlWorkspaceOptions,\n type ControlModel,\n type ControlModelBinding,\n type ControlSchema,\n type ControlWorkspaceOptions,\n} from '../composables/useControlSchema'\nimport { useSettingsStore, colorPalettes } from '../stores/settings'\nimport {\n formSchemaFieldNames,\n pickExistingRecordKeys,\n pickRecordKeys,\n recordValuesEqualForKeys,\n} from '../utils/formModelSync'\nimport type {\n ThemeMode,\n ColorPalette,\n TableDensity,\n SettingsTab,\n SettingsTabInput,\n SettingsModalLayout,\n SettingsModalSchema,\n FormSchema,\n FormSectionSchema,\n FormEnhancements,\n} from '../types'\nimport { normalizeItemInput } from '../utils/items'\n\n// Map our settings groups onto the form-builder's flat-section shape.\n// `title: ''` because the rail/pane header already shows the group name.\nfunction buildFlatSchema(schema: SettingsModalSchema): FormSchema {\n return {\n sections: schema.groups.map<FormSectionSchema>((g) => ({\n id: g.id,\n title: '',\n fields: g.fields,\n columns: g.columns,\n condition: g.condition,\n })),\n }\n}\n\ninterface Props {\n modelValue: boolean\n title?: string\n /** Manual tab descriptors. Ignored when `schema` is set (groups become tabs). */\n tabs?: SettingsTabInput[]\n showAppearance?: boolean\n size?: 'md' | 'lg' | 'xl'\n layout?: SettingsModalLayout\n /** Declarative schema — fields auto-render via SDK form components. */\n schema?: SettingsModalSchema\n /** Model returned by defineControlModel(), or a raw nested ControlModel for one-step settings generation. */\n model?: ControlModel | ControlModelBinding\n /** Compact controls model — converted to `schema` when `schema` is not set. */\n controls?: ControlSchema\n /** Conversion options for `controls`, such as section labels, columns, and shared initialValues. */\n controlOptions?: ControlWorkspaceOptions\n /** Two-way bound values when `schema`, `model`, or `controls` is set. */\n values?: Record<string, unknown>\n /** Optional dynamic enhancements (validators, dynamic options, callbacks). */\n enhancements?: FormEnhancements<Record<string, unknown>>\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: 'Settings',\n tabs: () => [],\n showAppearance: true,\n size: 'lg',\n layout: 'horizontal',\n model: undefined,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n 'update:values': [data: Record<string, unknown>]\n close: []\n}>()\n\nconst settings = useSettingsStore()\n\nconst APPEARANCE_TAB_ID = 'appearance'\n\nconst APPEARANCE_TAB: SettingsTab = {\n id: APPEARANCE_TAB_ID,\n label: 'Appearance',\n description: 'Theme, color palette, and table density',\n}\n\nconst isVertical = computed(() => props.layout === 'vertical')\n\n// Per-instance prefix for ARIA tab/tabpanel id wiring. Stable across renders.\nconst uid = `mint-settings-${Math.random().toString(36).slice(2, 9)}`\nconst tabId = (id: string) => `${uid}-tab-${id}`\nconst panelId = (id: string) => `${uid}-panel-${id}`\n\nconst resolvedModel = computed<ControlModelBinding | undefined>(() => {\n if (props.model === undefined) return undefined\n return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)\n})\nconst resolvedControls = computed<ControlSchema | undefined>(() =>\n props.controls ?? resolvedModel.value?.controls,\n)\nconst resolvedControlOptions = computed<ControlWorkspaceOptions>(() =>\n mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, props.controlOptions)\n)\nconst settingsSchema = computed<SettingsModalSchema | undefined>(() =>\n props.schema ?? (\n resolvedControls.value\n ? controlsToSettingsSchema(resolvedControls.value, resolvedControlOptions.value)\n : undefined\n ),\n)\nconst isSchemaDriven = computed(() => !!settingsSchema.value)\nconst resolvedValues = computed<Record<string, unknown>>(() => ({\n ...(resolvedControlOptions.value.initialValues ?? {}),\n ...(props.values ?? {}),\n}))\n\n// Schema-driven mode keeps one form-builder instance and syncs schema changes\n// into it, so callers can swap groups/fields without remounting the modal.\nconst builder = useFormBuilder(\n settingsSchema.value ? buildFlatSchema(settingsSchema.value) : { sections: [] },\n resolvedValues.value,\n props.enhancements,\n)\n\nwatch(\n () => settingsSchema.value,\n (schema) => {\n if (!schema) {\n builder.updateSchema({ sections: [] }, {})\n return\n }\n\n const flatSchema = buildFlatSchema(schema)\n const fieldNames = formSchemaFieldNames(flatSchema)\n const sourceValues = props.values === undefined\n ? {\n ...resolvedValues.value,\n ...(builder.form.data as Record<string, unknown>),\n }\n : resolvedValues.value\n\n builder.updateSchema(flatSchema, pickExistingRecordKeys(sourceValues ?? {}, fieldNames))\n },\n { deep: true },\n)\n\nwatch(\n () => ({ ...resolvedValues.value }),\n (data) => {\n if (!settingsSchema.value) return\n const fieldNames = builderFieldNames()\n if (recordValuesEqualForKeys(data, builder.form.data as Record<string, unknown>, fieldNames)) return\n builder.reset(pickRecordKeys(data, fieldNames))\n },\n { deep: true },\n)\n\nwatch(\n () => ({ ...builder.form.data }),\n (data) => {\n if (settingsSchema.value) emit('update:values', data as Record<string, unknown>)\n },\n { deep: true },\n)\n\n// Schema groups whose `condition` evaluates false against the current data are\n// dropped from the rail entirely — same semantics as section visibility in\n// FormBuilder. Manual `tabs` have no condition mechanism, so they pass through.\nconst visibleSchemaGroups = computed(() =>\n settingsSchema.value\n ? settingsSchema.value.groups.filter((g) => builder.isSectionVisible(g.id))\n : [],\n)\n\nconst manualTabs = computed<SettingsTab[]>(() => props.tabs.map(normalizeItemInput))\n\nconst allTabs = computed<SettingsTab[]>(() => {\n const base: SettingsTab[] = settingsSchema.value\n ? visibleSchemaGroups.value.map((g) => ({ id: g.id, label: g.label, icon: g.icon, description: g.description }))\n : manualTabs.value\n return props.showAppearance ? [...base, APPEARANCE_TAB] : base\n})\n\nconst activeTab = ref(allTabs.value[0]?.id || APPEARANCE_TAB_ID)\n\nconst activeTabMeta = computed<SettingsTab | undefined>(() =>\n allTabs.value.find((t) => t.id === activeTab.value),\n)\n\n// If the active tab vanishes (group hidden by condition), drop back to the first available.\nwatch(allTabs, (tabs) => {\n if (!tabs.some((t) => t.id === activeTab.value)) {\n activeTab.value = tabs[0]?.id ?? APPEARANCE_TAB_ID\n }\n})\n\nconst activeGroup = computed(() =>\n visibleSchemaGroups.value.find((g) => g.id === activeTab.value),\n)\n\nconst activeGroupVisibleFields = computed(() => {\n if (!activeGroup.value) return []\n return activeGroup.value.fields.filter((f) => builder.isFieldVisible(f.name))\n})\n\nconst themeOptions: { value: ThemeMode; label: string }[] = [\n { value: 'light', label: 'Light' },\n { value: 'dark', label: 'Dark' },\n { value: 'system', label: 'System' },\n]\n\nconst densityOptions: { value: TableDensity; label: string }[] = [\n { value: 'compact', label: 'Compact' },\n { value: 'normal', label: 'Normal' },\n { value: 'comfortable', label: 'Comfortable' },\n]\n\nfunction handleClose() {\n emit('update:modelValue', false)\n emit('close')\n}\n\nfunction builderFieldNames(): string[] {\n return builder.fields.map(field => field.name)\n}\n\nfunction isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {\n return 'controls' in model && 'controlOptions' in model\n}\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :size=\"size\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @close=\"handleClose\"\n >\n <div :class=\"['mint-settings-modal', `mint-settings-modal--${layout}`]\">\n <div\n v-if=\"!isVertical && allTabs.length > 1\"\n class=\"mint-settings-modal__tabs\"\n >\n <button\n v-for=\"tab in allTabs\"\n :key=\"tab.id\"\n type=\"button\"\n :class=\"['mint-settings-modal__tab', { 'mint-settings-modal__tab--active': activeTab === tab.id }]\"\n @click=\"activeTab = tab.id\"\n >\n {{ tab.label }}\n </button>\n </div>\n\n <div\n v-else-if=\"isVertical\"\n class=\"mint-settings-modal__rail\"\n role=\"tablist\"\n aria-orientation=\"vertical\"\n aria-label=\"Settings sections\"\n >\n <button\n v-for=\"tab in allTabs\"\n :id=\"tabId(tab.id)\"\n :key=\"tab.id\"\n type=\"button\"\n role=\"tab\"\n :class=\"['mint-settings-modal__rail-item', { 'mint-settings-modal__rail-item--active': activeTab === tab.id }]\"\n :aria-selected=\"activeTab === tab.id\"\n :aria-controls=\"panelId(tab.id)\"\n :tabindex=\"activeTab === tab.id ? 0 : -1\"\n @click=\"activeTab = tab.id\"\n >\n <span class=\"mint-settings-modal__rail-item-icon\" aria-hidden=\"true\">\n <span v-if=\"tab.icon\" v-html=\"tab.icon\" />\n </span>\n <span class=\"mint-settings-modal__rail-item-text\">\n <span class=\"mint-settings-modal__rail-item-label\">{{ tab.label }}</span>\n <span v-if=\"tab.description\" class=\"mint-settings-modal__rail-item-description\">\n {{ tab.description }}\n </span>\n </span>\n </button>\n </div>\n\n <!-- Vertical mode wraps content in a tabpanel <section> with a header bar; horizontal mode stays a plain <div>. -->\n <component\n :is=\"isVertical ? 'section' : 'div'\"\n :class=\"isVertical ? 'mint-settings-modal__pane' : 'mint-settings-modal__content'\"\n :id=\"isVertical && activeTabMeta ? panelId(activeTabMeta.id) : undefined\"\n :role=\"isVertical ? 'tabpanel' : undefined\"\n :aria-labelledby=\"isVertical && activeTabMeta ? tabId(activeTabMeta.id) : undefined\"\n :tabindex=\"isVertical ? 0 : undefined\"\n >\n <header\n v-if=\"isVertical && activeTabMeta?.label\"\n class=\"mint-settings-modal__pane-header\"\n >\n <h4 class=\"mint-settings-modal__pane-title\">{{ activeTabMeta.label }}</h4>\n <p v-if=\"activeTabMeta.description\" class=\"mint-settings-modal__pane-subtitle\">\n {{ activeTabMeta.description }}\n </p>\n </header>\n\n <div :class=\"isVertical ? 'mint-settings-modal__pane-body' : null\">\n <div\n v-if=\"isSchemaDriven && activeGroup\"\n class=\"mint-settings-modal__group-grid\"\n :style=\"{ '--mint-settings-cols': activeGroup.columns ?? 1 }\"\n >\n <template v-for=\"field in activeGroupVisibleFields\" :key=\"field.name\">\n <div :style=\"field.colSpan ? { gridColumn: `span ${field.colSpan}` } : undefined\">\n <slot\n :name=\"`field:${field.name}`\"\n :field=\"field\"\n :form=\"builder.form\"\n :field-props=\"builder.form.getFieldProps(field.name)\"\n >\n <FormFieldRendererInternal\n :field=\"field\"\n :resolved-props=\"builder.getResolvedFieldProps(field)\"\n :form=\"builder.form\"\n />\n </slot>\n </div>\n </template>\n </div>\n\n <template v-else-if=\"!isSchemaDriven\">\n <template v-for=\"tab in manualTabs\" :key=\"tab.id\">\n <div v-show=\"activeTab === tab.id\">\n <slot :name=\"`tab-${tab.id}`\" />\n </div>\n </template>\n </template>\n\n <div v-if=\"showAppearance\" v-show=\"activeTab === APPEARANCE_TAB_ID\">\n <div class=\"mint-settings-modal__section\">\n <div class=\"mint-settings-modal__section-label\">Theme</div>\n <div class=\"mint-settings-modal__option-group\">\n <button\n v-for=\"opt in themeOptions\"\n :key=\"opt.value\"\n type=\"button\"\n :class=\"['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.theme === opt.value }]\"\n @click=\"settings.theme = opt.value\"\n >\n {{ opt.label }}\n </button>\n </div>\n </div>\n\n <div class=\"mint-settings-modal__section\">\n <div class=\"mint-settings-modal__section-label\">Color Palette</div>\n <div class=\"mint-settings-modal__option-group\">\n <button\n v-for=\"(palette, key) in colorPalettes\"\n :key=\"key\"\n type=\"button\"\n :class=\"['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.colorPalette === key }]\"\n @click=\"settings.colorPalette = key as ColorPalette\"\n >\n {{ palette.name }}\n </button>\n </div>\n </div>\n\n <div class=\"mint-settings-modal__section\">\n <div class=\"mint-settings-modal__section-label\">Table Density</div>\n <div class=\"mint-settings-modal__option-group\">\n <button\n v-for=\"opt in densityOptions\"\n :key=\"opt.value\"\n type=\"button\"\n :class=\"['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.tableDensity === opt.value }]\"\n @click=\"settings.tableDensity = opt.value\"\n >\n {{ opt.label }}\n </button>\n </div>\n <p class=\"mint-settings-modal__note\">Adjusts row height in data tables.</p>\n </div>\n\n <slot name=\"appearance\" />\n </div>\n </div>\n </component>\n </div>\n <div v-if=\"$slots.footer\" class=\"mint-settings-modal__footer\">\n <slot\n name=\"footer\"\n :values=\"builder.form.data\"\n :close=\"handleClose\"\n />\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/settings-modal.css';\n</style>\n","<script setup lang=\"ts\">\n/** Confirm/cancel dialog with danger, warning, and info variants; blocks close while loading. */\nimport BaseModal from './BaseModal.vue'\n\ninterface Props {\n modelValue: boolean\n title?: string\n subtitle?: string\n message?: string\n variant?: 'danger' | 'warning' | 'info'\n confirmLabel?: string\n cancelLabel?: string\n loading?: boolean\n}\n\nwithDefaults(defineProps<Props>(), {\n title: 'Confirm',\n variant: 'danger',\n confirmLabel: 'Confirm',\n cancelLabel: 'Cancel',\n loading: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n confirm: []\n cancel: []\n}>()\n\nfunction handleCancel() {\n emit('update:modelValue', false)\n emit('cancel')\n}\n\nfunction handleConfirm() {\n emit('confirm')\n}\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :subtitle=\"subtitle\"\n size=\"sm\"\n :closable=\"!loading\"\n :close-on-overlay=\"!loading\"\n :close-on-escape=\"!loading\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mint-confirm\">\n <!-- Icon is now opt-in via slot. Refresh design keeps the body focused on\n the message; variant intent is carried by the confirm button's color. -->\n <div\n v-if=\"$slots.icon\"\n :class=\"['mint-confirm__icon', `mint-confirm__icon--${variant}`]\"\n >\n <slot name=\"icon\" />\n </div>\n <p v-if=\"message\" class=\"mint-confirm__message\">{{ message }}</p>\n <slot />\n </div>\n\n <template #footer>\n <div class=\"mint-confirm__footer\">\n <button\n type=\"button\"\n class=\"mint-confirm__btn-cancel\"\n :disabled=\"loading\"\n @click=\"handleCancel\"\n >\n {{ cancelLabel }}\n </button>\n <button\n type=\"button\"\n :class=\"['mint-confirm__btn-confirm', `mint-confirm__btn-confirm--${variant}`]\"\n :disabled=\"loading\"\n @click=\"handleConfirm\"\n >\n <svg v-if=\"loading\" class=\"mint-confirm__btn-spinner\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle style=\"opacity: 0.25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\" />\n <path style=\"opacity: 0.75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\" />\n </svg>\n {{ confirmLabel }}\n </button>\n </div>\n </template>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/confirm-dialog.css';\n</style>\n","<script setup lang=\"ts\">\n/** Confirm/cancel dialog with danger, warning, and info variants; blocks close while loading. */\nimport BaseModal from './BaseModal.vue'\n\ninterface Props {\n modelValue: boolean\n title?: string\n subtitle?: string\n message?: string\n variant?: 'danger' | 'warning' | 'info'\n confirmLabel?: string\n cancelLabel?: string\n loading?: boolean\n}\n\nwithDefaults(defineProps<Props>(), {\n title: 'Confirm',\n variant: 'danger',\n confirmLabel: 'Confirm',\n cancelLabel: 'Cancel',\n loading: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n confirm: []\n cancel: []\n}>()\n\nfunction handleCancel() {\n emit('update:modelValue', false)\n emit('cancel')\n}\n\nfunction handleConfirm() {\n emit('confirm')\n}\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :subtitle=\"subtitle\"\n size=\"sm\"\n :closable=\"!loading\"\n :close-on-overlay=\"!loading\"\n :close-on-escape=\"!loading\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mint-confirm\">\n <!-- Icon is now opt-in via slot. Refresh design keeps the body focused on\n the message; variant intent is carried by the confirm button's color. -->\n <div\n v-if=\"$slots.icon\"\n :class=\"['mint-confirm__icon', `mint-confirm__icon--${variant}`]\"\n >\n <slot name=\"icon\" />\n </div>\n <p v-if=\"message\" class=\"mint-confirm__message\">{{ message }}</p>\n <slot />\n </div>\n\n <template #footer>\n <div class=\"mint-confirm__footer\">\n <button\n type=\"button\"\n class=\"mint-confirm__btn-cancel\"\n :disabled=\"loading\"\n @click=\"handleCancel\"\n >\n {{ cancelLabel }}\n </button>\n <button\n type=\"button\"\n :class=\"['mint-confirm__btn-confirm', `mint-confirm__btn-confirm--${variant}`]\"\n :disabled=\"loading\"\n @click=\"handleConfirm\"\n >\n <svg v-if=\"loading\" class=\"mint-confirm__btn-spinner\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle style=\"opacity: 0.25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\" />\n <path style=\"opacity: 0.75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\" />\n </svg>\n {{ confirmLabel }}\n </button>\n </div>\n </template>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/confirm-dialog.css';\n</style>\n","<script setup lang=\"ts\">\n/** Floating popover showing the active experiment with save, detach, and select actions. */\nimport { ref, watch, onUnmounted } from 'vue'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport { formatExperimentStatus } from '../composables/experiment-utils'\nimport ConfirmDialog from './ConfirmDialog.vue'\n\ninterface Props {\n experimentName?: string\n experimentCode?: string\n experimentStatus?: string\n showSave?: boolean\n showDetach?: boolean\n saveDisabled?: boolean\n saveLoading?: boolean\n saveSuccessMessage?: string\n saveDisabledMessage?: string\n confirmSave?: boolean\n confirmTitle?: string\n confirmMessage?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showSave: false,\n showDetach: false,\n saveDisabled: false,\n saveLoading: false,\n confirmSave: true,\n})\n\nconst emit = defineEmits<{\n select: []\n save: []\n detach: []\n}>()\n\nconst { isOpen, rootRef: popoverRef, close, toggle } = useDropdownState({\n closeOnEscape: false,\n})\nconst showSuccess = ref(false)\nconst showConfirm = ref(false)\n\nfunction handleSelect() {\n emit('select')\n close()\n}\n\nfunction handleSave() {\n if (props.saveDisabled || props.saveLoading) return\n if (props.confirmSave) {\n showConfirm.value = true\n } else {\n emit('save')\n }\n}\n\nfunction handleConfirmSave() {\n showConfirm.value = false\n emit('save')\n}\n\nfunction handleDetach() {\n emit('detach')\n close()\n}\n\nlet successTimer: ReturnType<typeof setTimeout> | null = null\n\n// Show success state when saveSuccessMessage changes from empty to a value\nwatch(() => props.saveSuccessMessage, (msg) => {\n if (successTimer) clearTimeout(successTimer)\n if (msg) {\n showSuccess.value = true\n successTimer = setTimeout(() => {\n showSuccess.value = false\n successTimer = null\n }, 3000)\n }\n})\n\nonUnmounted(() => {\n if (successTimer) clearTimeout(successTimer)\n})\n</script>\n\n<template>\n <div ref=\"popoverRef\" class=\"mint-experiment-popover\">\n <!-- Split trigger: experiment pill + inline save -->\n <div\n :class=\"[\n 'mint-experiment-popover__split',\n { 'mint-experiment-popover__split--with-save': showSave && experimentName },\n ]\"\n >\n <!-- Left: experiment trigger (opens popover) — shows only the code for\n maximum topbar compactness; full name + status live in the panel. -->\n <button\n type=\"button\"\n :class=\"[\n 'mint-experiment-popover__trigger',\n { 'mint-experiment-popover__trigger--active': isOpen },\n { 'mint-experiment-popover__trigger--empty': !experimentCode && !experimentName },\n ]\"\n :title=\"experimentName || undefined\"\n @click.stop=\"toggle\"\n >\n <!-- Flask icon -->\n <svg class=\"mint-experiment-popover__trigger-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n <!-- Code preferred, name as fallback, \"No experiment\" as empty state -->\n <span v-if=\"experimentCode\" class=\"mint-experiment-popover__trigger-code\">{{ experimentCode }}</span>\n <span v-else-if=\"experimentName\" class=\"mint-experiment-popover__trigger-text\">{{ experimentName }}</span>\n <span v-else class=\"mint-experiment-popover__trigger-text\">No experiment</span>\n <!-- Chevron -->\n <svg class=\"mint-experiment-popover__trigger-chevron\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <!-- Right: inline save button (direct action) -->\n <button\n v-if=\"showSave && experimentName\"\n type=\"button\"\n :class=\"[\n 'mint-experiment-popover__save-trigger',\n { 'mint-experiment-popover__save-trigger--loading': saveLoading },\n { 'mint-experiment-popover__save-trigger--success': showSuccess },\n { 'mint-experiment-popover__save-trigger--disabled': saveDisabled && !showSuccess },\n ]\"\n :disabled=\"saveDisabled && !showSuccess\"\n :title=\"saveDisabled && saveDisabledMessage ? saveDisabledMessage : showSuccess && saveSuccessMessage ? saveSuccessMessage : 'Save to Experiment'\"\n @click.stop=\"handleSave\"\n >\n <!-- Loading spinner -->\n <span v-if=\"saveLoading\" class=\"mint-experiment-popover__spinner--inline\" />\n <!-- Success check -->\n <svg v-else-if=\"showSuccess\" class=\"mint-experiment-popover__save-trigger-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2.5\" d=\"M5 13l4 4L19 7\" />\n </svg>\n <!-- Save icon -->\n <svg v-else class=\"mint-experiment-popover__save-trigger-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4\" />\n </svg>\n </button>\n </div>\n\n <!-- Popover panel -->\n <div v-if=\"isOpen\" class=\"mint-experiment-popover__panel\">\n <!-- Header -->\n <div class=\"mint-experiment-popover__header\">\n <div class=\"mint-experiment-popover__title\">Experiment</div>\n <div class=\"mint-experiment-popover__subtitle\">\n {{ experimentName ? 'Linked experiment context' : 'Link to an MINT experiment' }}\n </div>\n </div>\n\n <!-- No experiment selected -->\n <div v-if=\"!experimentName\" class=\"mint-experiment-popover__body\">\n <button type=\"button\" class=\"mint-experiment-popover__select-btn\" @click=\"handleSelect\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n Select Experiment\n </button>\n </div>\n\n <!-- Experiment selected -->\n <div v-else class=\"mint-experiment-popover__body\">\n <div class=\"mint-experiment-popover__card\">\n <div class=\"mint-experiment-popover__card-icon\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n </div>\n <div class=\"mint-experiment-popover__card-info\">\n <span v-if=\"experimentCode\" class=\"mint-experiment-popover__card-code\">{{ experimentCode }}</span>\n <div class=\"mint-experiment-popover__card-name\">{{ experimentName }}</div>\n <div v-if=\"experimentStatus\" class=\"mint-experiment-popover__card-status\">\n {{ formatExperimentStatus(experimentStatus) }}\n </div>\n </div>\n </div>\n <div class=\"mint-experiment-popover__card-actions\">\n <button type=\"button\" class=\"mint-experiment-popover__change-btn\" @click=\"handleSelect\">\n Change\n </button>\n <button v-if=\"showDetach\" type=\"button\" class=\"mint-experiment-popover__detach-btn\" @click=\"handleDetach\">\n Detach\n </button>\n </div>\n </div>\n </div>\n\n <!-- Save confirmation dialog -->\n <ConfirmDialog\n v-model=\"showConfirm\"\n :title=\"confirmTitle ?? 'Save to Experiment'\"\n :message=\"confirmMessage ?? `Save current data to ${experimentName}?`\"\n variant=\"info\"\n confirm-label=\"Save\"\n :loading=\"saveLoading\"\n @confirm=\"handleConfirmSave\"\n />\n </div>\n</template>\n\n<style>\n@import '../styles/components/experiment-popover.css';\n</style>\n","<script setup lang=\"ts\">\n/** Floating popover showing the active experiment with save, detach, and select actions. */\nimport { ref, watch, onUnmounted } from 'vue'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport { formatExperimentStatus } from '../composables/experiment-utils'\nimport ConfirmDialog from './ConfirmDialog.vue'\n\ninterface Props {\n experimentName?: string\n experimentCode?: string\n experimentStatus?: string\n showSave?: boolean\n showDetach?: boolean\n saveDisabled?: boolean\n saveLoading?: boolean\n saveSuccessMessage?: string\n saveDisabledMessage?: string\n confirmSave?: boolean\n confirmTitle?: string\n confirmMessage?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showSave: false,\n showDetach: false,\n saveDisabled: false,\n saveLoading: false,\n confirmSave: true,\n})\n\nconst emit = defineEmits<{\n select: []\n save: []\n detach: []\n}>()\n\nconst { isOpen, rootRef: popoverRef, close, toggle } = useDropdownState({\n closeOnEscape: false,\n})\nconst showSuccess = ref(false)\nconst showConfirm = ref(false)\n\nfunction handleSelect() {\n emit('select')\n close()\n}\n\nfunction handleSave() {\n if (props.saveDisabled || props.saveLoading) return\n if (props.confirmSave) {\n showConfirm.value = true\n } else {\n emit('save')\n }\n}\n\nfunction handleConfirmSave() {\n showConfirm.value = false\n emit('save')\n}\n\nfunction handleDetach() {\n emit('detach')\n close()\n}\n\nlet successTimer: ReturnType<typeof setTimeout> | null = null\n\n// Show success state when saveSuccessMessage changes from empty to a value\nwatch(() => props.saveSuccessMessage, (msg) => {\n if (successTimer) clearTimeout(successTimer)\n if (msg) {\n showSuccess.value = true\n successTimer = setTimeout(() => {\n showSuccess.value = false\n successTimer = null\n }, 3000)\n }\n})\n\nonUnmounted(() => {\n if (successTimer) clearTimeout(successTimer)\n})\n</script>\n\n<template>\n <div ref=\"popoverRef\" class=\"mint-experiment-popover\">\n <!-- Split trigger: experiment pill + inline save -->\n <div\n :class=\"[\n 'mint-experiment-popover__split',\n { 'mint-experiment-popover__split--with-save': showSave && experimentName },\n ]\"\n >\n <!-- Left: experiment trigger (opens popover) — shows only the code for\n maximum topbar compactness; full name + status live in the panel. -->\n <button\n type=\"button\"\n :class=\"[\n 'mint-experiment-popover__trigger',\n { 'mint-experiment-popover__trigger--active': isOpen },\n { 'mint-experiment-popover__trigger--empty': !experimentCode && !experimentName },\n ]\"\n :title=\"experimentName || undefined\"\n @click.stop=\"toggle\"\n >\n <!-- Flask icon -->\n <svg class=\"mint-experiment-popover__trigger-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n <!-- Code preferred, name as fallback, \"No experiment\" as empty state -->\n <span v-if=\"experimentCode\" class=\"mint-experiment-popover__trigger-code\">{{ experimentCode }}</span>\n <span v-else-if=\"experimentName\" class=\"mint-experiment-popover__trigger-text\">{{ experimentName }}</span>\n <span v-else class=\"mint-experiment-popover__trigger-text\">No experiment</span>\n <!-- Chevron -->\n <svg class=\"mint-experiment-popover__trigger-chevron\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <!-- Right: inline save button (direct action) -->\n <button\n v-if=\"showSave && experimentName\"\n type=\"button\"\n :class=\"[\n 'mint-experiment-popover__save-trigger',\n { 'mint-experiment-popover__save-trigger--loading': saveLoading },\n { 'mint-experiment-popover__save-trigger--success': showSuccess },\n { 'mint-experiment-popover__save-trigger--disabled': saveDisabled && !showSuccess },\n ]\"\n :disabled=\"saveDisabled && !showSuccess\"\n :title=\"saveDisabled && saveDisabledMessage ? saveDisabledMessage : showSuccess && saveSuccessMessage ? saveSuccessMessage : 'Save to Experiment'\"\n @click.stop=\"handleSave\"\n >\n <!-- Loading spinner -->\n <span v-if=\"saveLoading\" class=\"mint-experiment-popover__spinner--inline\" />\n <!-- Success check -->\n <svg v-else-if=\"showSuccess\" class=\"mint-experiment-popover__save-trigger-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2.5\" d=\"M5 13l4 4L19 7\" />\n </svg>\n <!-- Save icon -->\n <svg v-else class=\"mint-experiment-popover__save-trigger-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4\" />\n </svg>\n </button>\n </div>\n\n <!-- Popover panel -->\n <div v-if=\"isOpen\" class=\"mint-experiment-popover__panel\">\n <!-- Header -->\n <div class=\"mint-experiment-popover__header\">\n <div class=\"mint-experiment-popover__title\">Experiment</div>\n <div class=\"mint-experiment-popover__subtitle\">\n {{ experimentName ? 'Linked experiment context' : 'Link to an MINT experiment' }}\n </div>\n </div>\n\n <!-- No experiment selected -->\n <div v-if=\"!experimentName\" class=\"mint-experiment-popover__body\">\n <button type=\"button\" class=\"mint-experiment-popover__select-btn\" @click=\"handleSelect\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n Select Experiment\n </button>\n </div>\n\n <!-- Experiment selected -->\n <div v-else class=\"mint-experiment-popover__body\">\n <div class=\"mint-experiment-popover__card\">\n <div class=\"mint-experiment-popover__card-icon\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n </div>\n <div class=\"mint-experiment-popover__card-info\">\n <span v-if=\"experimentCode\" class=\"mint-experiment-popover__card-code\">{{ experimentCode }}</span>\n <div class=\"mint-experiment-popover__card-name\">{{ experimentName }}</div>\n <div v-if=\"experimentStatus\" class=\"mint-experiment-popover__card-status\">\n {{ formatExperimentStatus(experimentStatus) }}\n </div>\n </div>\n </div>\n <div class=\"mint-experiment-popover__card-actions\">\n <button type=\"button\" class=\"mint-experiment-popover__change-btn\" @click=\"handleSelect\">\n Change\n </button>\n <button v-if=\"showDetach\" type=\"button\" class=\"mint-experiment-popover__detach-btn\" @click=\"handleDetach\">\n Detach\n </button>\n </div>\n </div>\n </div>\n\n <!-- Save confirmation dialog -->\n <ConfirmDialog\n v-model=\"showConfirm\"\n :title=\"confirmTitle ?? 'Save to Experiment'\"\n :message=\"confirmMessage ?? `Save current data to ${experimentName}?`\"\n variant=\"info\"\n confirm-label=\"Save\"\n :loading=\"saveLoading\"\n @confirm=\"handleConfirmSave\"\n />\n </div>\n</template>\n\n<style>\n@import '../styles/components/experiment-popover.css';\n</style>\n","<script setup lang=\"ts\">\n/** Animated loading placeholder in text, circular, rectangular, or rounded variants with pulse or wave animation. */\nimport { computed } from 'vue'\n\ninterface Props {\n variant?: 'text' | 'circular' | 'rectangular' | 'rounded'\n width?: string | number\n height?: string | number\n animation?: 'pulse' | 'wave' | 'none'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'text',\n animation: 'wave',\n})\n\nconst style = computed(() => {\n const s: Record<string, string> = {}\n\n if (props.width) {\n s.width = typeof props.width === 'number' ? `${props.width}px` : props.width\n }\n\n if (props.height) {\n s.height = typeof props.height === 'number' ? `${props.height}px` : props.height\n }\n\n return s\n})\n\nconst classes = computed(() => {\n const base = ['bg-bg-hover']\n\n switch (props.variant) {\n case 'circular':\n base.push('rounded-full')\n if (!props.width && !props.height) {\n base.push('w-10 h-10')\n }\n break\n case 'rectangular':\n base.push('rounded-none')\n if (!props.height) base.push('h-24')\n break\n case 'rounded':\n base.push('rounded-mint')\n if (!props.height) base.push('h-24')\n break\n default:\n base.push('rounded')\n if (!props.height) base.push('h-4')\n if (!props.width) base.push('w-full')\n }\n\n switch (props.animation) {\n case 'pulse':\n base.push('animate-pulse')\n break\n case 'wave':\n base.push('skeleton-wave')\n break\n }\n\n return base\n})\n</script>\n\n<template>\n <div :class=\"classes\" :style=\"style\" />\n</template>\n\n<style>\n@import '../styles/components/skeleton.css';\n</style>\n","<script setup lang=\"ts\">\n/** Animated loading placeholder in text, circular, rectangular, or rounded variants with pulse or wave animation. */\nimport { computed } from 'vue'\n\ninterface Props {\n variant?: 'text' | 'circular' | 'rectangular' | 'rounded'\n width?: string | number\n height?: string | number\n animation?: 'pulse' | 'wave' | 'none'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'text',\n animation: 'wave',\n})\n\nconst style = computed(() => {\n const s: Record<string, string> = {}\n\n if (props.width) {\n s.width = typeof props.width === 'number' ? `${props.width}px` : props.width\n }\n\n if (props.height) {\n s.height = typeof props.height === 'number' ? `${props.height}px` : props.height\n }\n\n return s\n})\n\nconst classes = computed(() => {\n const base = ['bg-bg-hover']\n\n switch (props.variant) {\n case 'circular':\n base.push('rounded-full')\n if (!props.width && !props.height) {\n base.push('w-10 h-10')\n }\n break\n case 'rectangular':\n base.push('rounded-none')\n if (!props.height) base.push('h-24')\n break\n case 'rounded':\n base.push('rounded-mint')\n if (!props.height) base.push('h-24')\n break\n default:\n base.push('rounded')\n if (!props.height) base.push('h-4')\n if (!props.width) base.push('w-full')\n }\n\n switch (props.animation) {\n case 'pulse':\n base.push('animate-pulse')\n break\n case 'wave':\n base.push('skeleton-wave')\n break\n }\n\n return base\n})\n</script>\n\n<template>\n <div :class=\"classes\" :style=\"style\" />\n</template>\n\n<style>\n@import '../styles/components/skeleton.css';\n</style>\n","<script setup lang=\"ts\">\n/** Empty-state placeholder with icon badge, headline, description, default slot, and optional CTA button. */\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n title?: string\n description?: string\n iconPath?: string\n color?: 'primary' | 'cta' | 'success' | 'warning' | 'error' | 'muted'\n size?: 'sm' | 'md' | 'lg'\n variant?: 'illustrated' | 'inline'\n actionLabel?: string\n}\n\nwithDefaults(defineProps<Props>(), {\n color: 'primary',\n size: 'md',\n variant: 'illustrated',\n})\n\nconst emit = defineEmits<{\n action: []\n}>()\n\nconst defaultIconPaths = [\n 'M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z',\n 'M7 11h10',\n 'M7 15h6',\n 'M7 7h8',\n]\n</script>\n\n<template>\n <div :class=\"['mint-empty-state', `mint-empty-state--${variant}`, `mint-empty-state--${size}`]\">\n <div :class=\"['mint-empty-state__icon-wrapper', `mint-empty-state__icon-wrapper--${color}`]\">\n <slot name=\"icon\">\n <svg\n class=\"mint-empty-state__icon\"\n :class=\"`mint-empty-state__icon--${color}`\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <template v-if=\"iconPath\">\n <path :d=\"iconPath\" />\n </template>\n <template v-else>\n <path v-for=\"(d, i) in defaultIconPaths\" :key=\"i\" :d=\"d\" />\n </template>\n </svg>\n </slot>\n </div>\n <div class=\"mint-empty-state__body\">\n <h3 v-if=\"title\" class=\"mint-empty-state__title\">{{ title }}</h3>\n <p v-if=\"description\" class=\"mint-empty-state__description\">{{ description }}</p>\n <slot />\n </div>\n <div v-if=\"actionLabel\" class=\"mint-empty-state__action\">\n <BaseButton @click=\"emit('action')\">\n {{ actionLabel }}\n </BaseButton>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/empty-state.css';\n</style>\n","<script setup lang=\"ts\">\n/** Empty-state placeholder with icon badge, headline, description, default slot, and optional CTA button. */\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n title?: string\n description?: string\n iconPath?: string\n color?: 'primary' | 'cta' | 'success' | 'warning' | 'error' | 'muted'\n size?: 'sm' | 'md' | 'lg'\n variant?: 'illustrated' | 'inline'\n actionLabel?: string\n}\n\nwithDefaults(defineProps<Props>(), {\n color: 'primary',\n size: 'md',\n variant: 'illustrated',\n})\n\nconst emit = defineEmits<{\n action: []\n}>()\n\nconst defaultIconPaths = [\n 'M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z',\n 'M7 11h10',\n 'M7 15h6',\n 'M7 7h8',\n]\n</script>\n\n<template>\n <div :class=\"['mint-empty-state', `mint-empty-state--${variant}`, `mint-empty-state--${size}`]\">\n <div :class=\"['mint-empty-state__icon-wrapper', `mint-empty-state__icon-wrapper--${color}`]\">\n <slot name=\"icon\">\n <svg\n class=\"mint-empty-state__icon\"\n :class=\"`mint-empty-state__icon--${color}`\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <template v-if=\"iconPath\">\n <path :d=\"iconPath\" />\n </template>\n <template v-else>\n <path v-for=\"(d, i) in defaultIconPaths\" :key=\"i\" :d=\"d\" />\n </template>\n </svg>\n </slot>\n </div>\n <div class=\"mint-empty-state__body\">\n <h3 v-if=\"title\" class=\"mint-empty-state__title\">{{ title }}</h3>\n <p v-if=\"description\" class=\"mint-empty-state__description\">{{ description }}</p>\n <slot />\n </div>\n <div v-if=\"actionLabel\" class=\"mint-empty-state__action\">\n <BaseButton @click=\"emit('action')\">\n {{ actionLabel }}\n </BaseButton>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/empty-state.css';\n</style>\n","<script setup lang=\"ts\">\n/** Inline badge that displays an experiment code and copies it to the clipboard on click. */\nimport { ref } from 'vue'\n\ninterface Props {\n code: string\n size?: 'sm' | 'md' | 'lg'\n copyable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n copyable: true,\n})\n\nconst emit = defineEmits<{\n copy: [code: string]\n}>()\n\nconst copied = ref(false)\nlet copyTimeout: ReturnType<typeof setTimeout> | null = null\n\nasync function handleCopy() {\n if (!props.copyable) return\n try {\n await navigator.clipboard.writeText(props.code)\n copied.value = true\n emit('copy', props.code)\n if (copyTimeout) clearTimeout(copyTimeout)\n copyTimeout = setTimeout(() => { copied.value = false }, 1500)\n } catch {\n // Clipboard API not available (e.g. insecure context)\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!props.copyable) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleCopy()\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mint-exp-code',\n `mint-exp-code--${size}`,\n { 'mint-exp-code--copyable': copyable, 'mint-exp-code--copied': copied },\n ]\"\n :role=\"copyable ? 'button' : undefined\"\n :tabindex=\"copyable ? 0 : undefined\"\n :title=\"copyable ? (copied ? 'Copied!' : 'Click to copy') : undefined\"\n @click=\"handleCopy\"\n @keydown=\"handleKeydown\"\n >\n {{ copied ? 'Copied!' : code }}\n </span>\n</template>\n\n<style>\n@import '../styles/components/experiment-code-badge.css';\n</style>\n","<script setup lang=\"ts\">\n/** Inline badge that displays an experiment code and copies it to the clipboard on click. */\nimport { ref } from 'vue'\n\ninterface Props {\n code: string\n size?: 'sm' | 'md' | 'lg'\n copyable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n copyable: true,\n})\n\nconst emit = defineEmits<{\n copy: [code: string]\n}>()\n\nconst copied = ref(false)\nlet copyTimeout: ReturnType<typeof setTimeout> | null = null\n\nasync function handleCopy() {\n if (!props.copyable) return\n try {\n await navigator.clipboard.writeText(props.code)\n copied.value = true\n emit('copy', props.code)\n if (copyTimeout) clearTimeout(copyTimeout)\n copyTimeout = setTimeout(() => { copied.value = false }, 1500)\n } catch {\n // Clipboard API not available (e.g. insecure context)\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!props.copyable) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleCopy()\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mint-exp-code',\n `mint-exp-code--${size}`,\n { 'mint-exp-code--copyable': copyable, 'mint-exp-code--copied': copied },\n ]\"\n :role=\"copyable ? 'button' : undefined\"\n :tabindex=\"copyable ? 0 : undefined\"\n :title=\"copyable ? (copied ? 'Copied!' : 'Click to copy') : undefined\"\n @click=\"handleCopy\"\n @keydown=\"handleKeydown\"\n >\n {{ copied ? 'Copied!' : code }}\n </span>\n</template>\n\n<style>\n@import '../styles/components/experiment-code-badge.css';\n</style>\n","<script setup lang=\"ts\">\n/** Modal for searching and selecting an experiment from the platform list with filters and keyboard nav. */\nimport { ref, reactive, computed, watch, nextTick } from 'vue'\nimport type { ModalSize, ExperimentSummary, ExperimentFilters } from '../types'\nimport { useExperimentSelector } from '../composables/useExperimentSelector'\nimport {\n formatExperimentDate,\n formatExperimentStatus,\n getExperimentStatusVariant,\n EXPERIMENT_STATUS_OPTIONS,\n DATE_PRESET_OPTIONS,\n SORT_OPTIONS,\n} from '../composables/experiment-utils'\nimport BaseModal from './BaseModal.vue'\nimport BaseInput from './BaseInput.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport BasePill from './BasePill.vue'\nimport Skeleton from './Skeleton.vue'\nimport EmptyState from './EmptyState.vue'\nimport ExperimentCodeBadge from './ExperimentCodeBadge.vue'\n\ninterface Props {\n modelValue: boolean\n experimentType?: string\n currentExperimentId?: number | null\n title?: string\n size?: ModalSize\n groupByProject?: boolean\n showFilters?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n currentExperimentId: null,\n title: 'Select Experiment',\n size: 'full',\n groupByProject: false,\n showFilters: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n select: [experiment: ExperimentSummary]\n deselect: []\n}>()\n\nconst {\n experiments,\n filters,\n isLoading,\n error,\n sortKey,\n experimentTypes,\n projects,\n groupedByProject,\n fetch: fetchExperiments,\n fetchFilterOptions,\n} = useExperimentSelector({\n experimentType: props.experimentType,\n})\n\nconst activeIndex = ref(-1)\nconst listRef = ref<HTMLElement | null>(null)\nconst showAdvanced = ref(props.showFilters)\nconst groupToggle = ref(props.groupByProject)\n\n// Track whether any advanced filter is active (for badge dot)\nconst hasActiveAdvancedFilters = computed(() =>\n !!(filters.project || filters.experimentType || filters.datePreset || sortKey.value !== 'created_at:desc'),\n)\n\n// Build type filter options from fetched experiment types\nconst typeFilterOptions = computed(() => [\n { value: '', label: 'All types' },\n ...experimentTypes.value.map(t => ({ value: t.value, label: t.label })),\n])\n\n// Build project filter options from fetched projects\nconst projectFilterOptions = computed(() => [\n { value: '', label: 'All projects' },\n ...projects.value,\n])\n\n// Flat list of experiments for keyboard navigation (works across groups too)\nconst flatExperiments = computed(() => {\n if (!groupToggle.value) return experiments.value\n return groupedByProject.value.flatMap(([, exps]) => exps)\n})\n\nfunction setFilter<K extends keyof ExperimentFilters>(key: K, value: string | number) {\n ;(filters as Record<string, unknown>)[key] = String(value) || undefined\n}\n\nfunction handleSortChange(value: string | number) {\n sortKey.value = String(value) || 'created_at:desc'\n}\n\nfunction handleSelect(experiment: ExperimentSummary) {\n emit('select', experiment)\n emit('update:modelValue', false)\n}\n\nfunction handleDeselect() {\n emit('deselect')\n emit('update:modelValue', false)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n const list = flatExperiments.value\n if (!list.length) return\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n activeIndex.value = Math.min(activeIndex.value + 1, list.length - 1)\n scrollActiveIntoView()\n break\n case 'ArrowUp':\n event.preventDefault()\n activeIndex.value = Math.max(activeIndex.value - 1, 0)\n scrollActiveIntoView()\n break\n case 'Enter':\n event.preventDefault()\n if (activeIndex.value >= 0 && activeIndex.value < list.length) {\n handleSelect(list[activeIndex.value])\n }\n break\n }\n}\n\nfunction scrollActiveIntoView() {\n nextTick(() => {\n const row = listRef.value?.querySelector('.mint-experiment-selector__row--focused')\n row?.scrollIntoView({ block: 'nearest' })\n })\n}\n\n// Precomputed id → flat index for O(1) lookup in grouped mode\nconst flatIndexMap = computed(() => {\n const map = new Map<number, number>()\n flatExperiments.value.forEach((exp, i) => map.set(exp.id, i))\n return map\n})\n\nfunction getFlatIndex(experiment: ExperimentSummary): number {\n return flatIndexMap.value.get(experiment.id) ?? -1\n}\n\n// Track collapsed groups (reactive Set tracks .add/.delete/.has automatically)\nconst collapsedGroups = reactive(new Set<string>())\n\nfunction toggleGroup(groupName: string) {\n if (collapsedGroups.has(groupName)) {\n collapsedGroups.delete(groupName)\n } else {\n collapsedGroups.add(groupName)\n }\n}\n\n// Reset active index when experiments change\nwatch(experiments, () => { activeIndex.value = -1 })\n\n// Fetch on open\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (isOpen) {\n activeIndex.value = -1\n collapsedGroups.clear()\n fetchFilterOptions()\n fetchExperiments()\n }\n },\n)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :size=\"size\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mint-experiment-selector\" @keydown=\"handleKeydown\">\n <!-- Filter bar row 1 -->\n <div class=\"mint-experiment-selector__filters-row\">\n <div class=\"mint-experiment-selector__search\">\n <BaseInput\n v-model=\"filters.search\"\n placeholder=\"Search experiments...\"\n size=\"sm\"\n type=\"search\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.status ?? ''\"\n :options=\"EXPERIMENT_STATUS_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('status', v)\"\n />\n </div>\n <div v-if=\"typeFilterOptions.length > 1\" class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.experimentType ?? ''\"\n :options=\"typeFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('experimentType', v)\"\n />\n </div>\n <button\n class=\"mint-experiment-selector__filters-toggle\"\n :class=\"{ 'mint-experiment-selector__filters-toggle--active': hasActiveAdvancedFilters }\"\n type=\"button\"\n @click=\"showAdvanced = !showAdvanced\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\" /><line x1=\"8\" y1=\"12\" x2=\"20\" y2=\"12\" /><line x1=\"12\" y1=\"18\" x2=\"20\" y2=\"18\" />\n <circle cx=\"6\" cy=\"12\" r=\"2\" /><circle cx=\"10\" cy=\"18\" r=\"2\" /><circle cx=\"6\" cy=\"6\" r=\"2\" />\n </svg>\n Filters\n <span v-if=\"hasActiveAdvancedFilters\" class=\"mint-experiment-selector__filters-dot\" />\n </button>\n </div>\n\n <!-- Filter bar row 2 (advanced, collapsible) -->\n <div v-if=\"showAdvanced\" class=\"mint-experiment-selector__filters-advanced\">\n <div v-if=\"projectFilterOptions.length > 1\" class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.project ?? ''\"\n :options=\"projectFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('project', v)\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.datePreset ?? ''\"\n :options=\"DATE_PRESET_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('datePreset', v)\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"sortKey\"\n :options=\"SORT_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"handleSortChange\"\n />\n </div>\n <label class=\"mint-experiment-selector__group-toggle\">\n <input\n v-model=\"groupToggle\"\n type=\"checkbox\"\n class=\"mint-experiment-selector__group-checkbox\"\n />\n Group by project\n </label>\n </div>\n\n <!-- Loading skeleton -->\n <div v-if=\"isLoading\" class=\"mint-experiment-selector__skeleton\">\n <div v-for=\"n in 4\" :key=\"n\" class=\"mint-experiment-selector__skeleton-row\">\n <div class=\"mint-experiment-selector__skeleton-content\">\n <Skeleton :width=\"120 + n * 20\" height=\"14px\" />\n <Skeleton width=\"80px\" height=\"10px\" />\n </div>\n <Skeleton width=\"60px\" height=\"20px\" variant=\"rounded\" />\n </div>\n </div>\n\n <!-- Error -->\n <div v-else-if=\"error\" class=\"mint-experiment-selector__error\">\n {{ error }}\n </div>\n\n <!-- Empty -->\n <EmptyState\n v-else-if=\"experiments.length === 0\"\n title=\"No experiments found\"\n description=\"Try adjusting your search or filters.\"\n size=\"sm\"\n />\n\n <!-- Experiment list: grouped mode -->\n <div v-else-if=\"groupToggle\" ref=\"listRef\" class=\"mint-experiment-selector__list\">\n <template v-for=\"([groupName, groupExps]) in groupedByProject\" :key=\"groupName\">\n <button\n type=\"button\"\n class=\"mint-experiment-selector__group-header\"\n @click=\"toggleGroup(groupName)\"\n >\n <svg\n class=\"mint-experiment-selector__group-chevron\"\n :class=\"{ 'mint-experiment-selector__group-chevron--collapsed': collapsedGroups.has(groupName) }\"\n width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n <span class=\"mint-experiment-selector__group-name\">{{ groupName }}</span>\n <span class=\"mint-experiment-selector__group-count\">{{ groupExps.length }}</span>\n </button>\n <template v-if=\"!collapsedGroups.has(groupName)\">\n <div\n v-for=\"exp in groupExps\"\n :key=\"exp.id\"\n class=\"mint-experiment-selector__row\"\n :class=\"{\n 'mint-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mint-experiment-selector__row--focused': getFlatIndex(exp) === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = getFlatIndex(exp)\"\n >\n <div class=\"mint-experiment-selector__row-content\">\n <div class=\"mint-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mint-experiment-selector__meta\">\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"getExperimentStatusVariant(exp.status)\" size=\"sm\">\n {{ formatExperimentStatus(exp.status) }}\n </BasePill>\n </div>\n </template>\n </template>\n </div>\n\n <!-- Experiment list: flat mode -->\n <div v-else ref=\"listRef\" class=\"mint-experiment-selector__list\">\n <div\n v-for=\"(exp, idx) in experiments\"\n :key=\"exp.id\"\n class=\"mint-experiment-selector__row\"\n :class=\"{\n 'mint-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mint-experiment-selector__row--focused': idx === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <div class=\"mint-experiment-selector__row-content\">\n <div class=\"mint-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mint-experiment-selector__meta\">\n <span v-if=\"exp.project_name || exp.project\">{{ exp.project_name || exp.project }}</span>\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"getExperimentStatusVariant(exp.status)\" size=\"sm\">\n {{ formatExperimentStatus(exp.status) }}\n </BasePill>\n </div>\n </div>\n\n <!-- Footer: clear selection -->\n <div v-if=\"currentExperimentId != null\" class=\"mint-experiment-selector__footer\">\n <button\n type=\"button\"\n class=\"mint-experiment-selector__clear-btn\"\n @click=\"handleDeselect\"\n >\n Clear selection\n </button>\n </div>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/experiment-selector-modal.css';\n</style>\n","<script setup lang=\"ts\">\n/** Modal for searching and selecting an experiment from the platform list with filters and keyboard nav. */\nimport { ref, reactive, computed, watch, nextTick } from 'vue'\nimport type { ModalSize, ExperimentSummary, ExperimentFilters } from '../types'\nimport { useExperimentSelector } from '../composables/useExperimentSelector'\nimport {\n formatExperimentDate,\n formatExperimentStatus,\n getExperimentStatusVariant,\n EXPERIMENT_STATUS_OPTIONS,\n DATE_PRESET_OPTIONS,\n SORT_OPTIONS,\n} from '../composables/experiment-utils'\nimport BaseModal from './BaseModal.vue'\nimport BaseInput from './BaseInput.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport BasePill from './BasePill.vue'\nimport Skeleton from './Skeleton.vue'\nimport EmptyState from './EmptyState.vue'\nimport ExperimentCodeBadge from './ExperimentCodeBadge.vue'\n\ninterface Props {\n modelValue: boolean\n experimentType?: string\n currentExperimentId?: number | null\n title?: string\n size?: ModalSize\n groupByProject?: boolean\n showFilters?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n currentExperimentId: null,\n title: 'Select Experiment',\n size: 'full',\n groupByProject: false,\n showFilters: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n select: [experiment: ExperimentSummary]\n deselect: []\n}>()\n\nconst {\n experiments,\n filters,\n isLoading,\n error,\n sortKey,\n experimentTypes,\n projects,\n groupedByProject,\n fetch: fetchExperiments,\n fetchFilterOptions,\n} = useExperimentSelector({\n experimentType: props.experimentType,\n})\n\nconst activeIndex = ref(-1)\nconst listRef = ref<HTMLElement | null>(null)\nconst showAdvanced = ref(props.showFilters)\nconst groupToggle = ref(props.groupByProject)\n\n// Track whether any advanced filter is active (for badge dot)\nconst hasActiveAdvancedFilters = computed(() =>\n !!(filters.project || filters.experimentType || filters.datePreset || sortKey.value !== 'created_at:desc'),\n)\n\n// Build type filter options from fetched experiment types\nconst typeFilterOptions = computed(() => [\n { value: '', label: 'All types' },\n ...experimentTypes.value.map(t => ({ value: t.value, label: t.label })),\n])\n\n// Build project filter options from fetched projects\nconst projectFilterOptions = computed(() => [\n { value: '', label: 'All projects' },\n ...projects.value,\n])\n\n// Flat list of experiments for keyboard navigation (works across groups too)\nconst flatExperiments = computed(() => {\n if (!groupToggle.value) return experiments.value\n return groupedByProject.value.flatMap(([, exps]) => exps)\n})\n\nfunction setFilter<K extends keyof ExperimentFilters>(key: K, value: string | number) {\n ;(filters as Record<string, unknown>)[key] = String(value) || undefined\n}\n\nfunction handleSortChange(value: string | number) {\n sortKey.value = String(value) || 'created_at:desc'\n}\n\nfunction handleSelect(experiment: ExperimentSummary) {\n emit('select', experiment)\n emit('update:modelValue', false)\n}\n\nfunction handleDeselect() {\n emit('deselect')\n emit('update:modelValue', false)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n const list = flatExperiments.value\n if (!list.length) return\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n activeIndex.value = Math.min(activeIndex.value + 1, list.length - 1)\n scrollActiveIntoView()\n break\n case 'ArrowUp':\n event.preventDefault()\n activeIndex.value = Math.max(activeIndex.value - 1, 0)\n scrollActiveIntoView()\n break\n case 'Enter':\n event.preventDefault()\n if (activeIndex.value >= 0 && activeIndex.value < list.length) {\n handleSelect(list[activeIndex.value])\n }\n break\n }\n}\n\nfunction scrollActiveIntoView() {\n nextTick(() => {\n const row = listRef.value?.querySelector('.mint-experiment-selector__row--focused')\n row?.scrollIntoView({ block: 'nearest' })\n })\n}\n\n// Precomputed id → flat index for O(1) lookup in grouped mode\nconst flatIndexMap = computed(() => {\n const map = new Map<number, number>()\n flatExperiments.value.forEach((exp, i) => map.set(exp.id, i))\n return map\n})\n\nfunction getFlatIndex(experiment: ExperimentSummary): number {\n return flatIndexMap.value.get(experiment.id) ?? -1\n}\n\n// Track collapsed groups (reactive Set tracks .add/.delete/.has automatically)\nconst collapsedGroups = reactive(new Set<string>())\n\nfunction toggleGroup(groupName: string) {\n if (collapsedGroups.has(groupName)) {\n collapsedGroups.delete(groupName)\n } else {\n collapsedGroups.add(groupName)\n }\n}\n\n// Reset active index when experiments change\nwatch(experiments, () => { activeIndex.value = -1 })\n\n// Fetch on open\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (isOpen) {\n activeIndex.value = -1\n collapsedGroups.clear()\n fetchFilterOptions()\n fetchExperiments()\n }\n },\n)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :size=\"size\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mint-experiment-selector\" @keydown=\"handleKeydown\">\n <!-- Filter bar row 1 -->\n <div class=\"mint-experiment-selector__filters-row\">\n <div class=\"mint-experiment-selector__search\">\n <BaseInput\n v-model=\"filters.search\"\n placeholder=\"Search experiments...\"\n size=\"sm\"\n type=\"search\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.status ?? ''\"\n :options=\"EXPERIMENT_STATUS_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('status', v)\"\n />\n </div>\n <div v-if=\"typeFilterOptions.length > 1\" class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.experimentType ?? ''\"\n :options=\"typeFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('experimentType', v)\"\n />\n </div>\n <button\n class=\"mint-experiment-selector__filters-toggle\"\n :class=\"{ 'mint-experiment-selector__filters-toggle--active': hasActiveAdvancedFilters }\"\n type=\"button\"\n @click=\"showAdvanced = !showAdvanced\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\" /><line x1=\"8\" y1=\"12\" x2=\"20\" y2=\"12\" /><line x1=\"12\" y1=\"18\" x2=\"20\" y2=\"18\" />\n <circle cx=\"6\" cy=\"12\" r=\"2\" /><circle cx=\"10\" cy=\"18\" r=\"2\" /><circle cx=\"6\" cy=\"6\" r=\"2\" />\n </svg>\n Filters\n <span v-if=\"hasActiveAdvancedFilters\" class=\"mint-experiment-selector__filters-dot\" />\n </button>\n </div>\n\n <!-- Filter bar row 2 (advanced, collapsible) -->\n <div v-if=\"showAdvanced\" class=\"mint-experiment-selector__filters-advanced\">\n <div v-if=\"projectFilterOptions.length > 1\" class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.project ?? ''\"\n :options=\"projectFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('project', v)\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.datePreset ?? ''\"\n :options=\"DATE_PRESET_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('datePreset', v)\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"sortKey\"\n :options=\"SORT_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"handleSortChange\"\n />\n </div>\n <label class=\"mint-experiment-selector__group-toggle\">\n <input\n v-model=\"groupToggle\"\n type=\"checkbox\"\n class=\"mint-experiment-selector__group-checkbox\"\n />\n Group by project\n </label>\n </div>\n\n <!-- Loading skeleton -->\n <div v-if=\"isLoading\" class=\"mint-experiment-selector__skeleton\">\n <div v-for=\"n in 4\" :key=\"n\" class=\"mint-experiment-selector__skeleton-row\">\n <div class=\"mint-experiment-selector__skeleton-content\">\n <Skeleton :width=\"120 + n * 20\" height=\"14px\" />\n <Skeleton width=\"80px\" height=\"10px\" />\n </div>\n <Skeleton width=\"60px\" height=\"20px\" variant=\"rounded\" />\n </div>\n </div>\n\n <!-- Error -->\n <div v-else-if=\"error\" class=\"mint-experiment-selector__error\">\n {{ error }}\n </div>\n\n <!-- Empty -->\n <EmptyState\n v-else-if=\"experiments.length === 0\"\n title=\"No experiments found\"\n description=\"Try adjusting your search or filters.\"\n size=\"sm\"\n />\n\n <!-- Experiment list: grouped mode -->\n <div v-else-if=\"groupToggle\" ref=\"listRef\" class=\"mint-experiment-selector__list\">\n <template v-for=\"([groupName, groupExps]) in groupedByProject\" :key=\"groupName\">\n <button\n type=\"button\"\n class=\"mint-experiment-selector__group-header\"\n @click=\"toggleGroup(groupName)\"\n >\n <svg\n class=\"mint-experiment-selector__group-chevron\"\n :class=\"{ 'mint-experiment-selector__group-chevron--collapsed': collapsedGroups.has(groupName) }\"\n width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n <span class=\"mint-experiment-selector__group-name\">{{ groupName }}</span>\n <span class=\"mint-experiment-selector__group-count\">{{ groupExps.length }}</span>\n </button>\n <template v-if=\"!collapsedGroups.has(groupName)\">\n <div\n v-for=\"exp in groupExps\"\n :key=\"exp.id\"\n class=\"mint-experiment-selector__row\"\n :class=\"{\n 'mint-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mint-experiment-selector__row--focused': getFlatIndex(exp) === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = getFlatIndex(exp)\"\n >\n <div class=\"mint-experiment-selector__row-content\">\n <div class=\"mint-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mint-experiment-selector__meta\">\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"getExperimentStatusVariant(exp.status)\" size=\"sm\">\n {{ formatExperimentStatus(exp.status) }}\n </BasePill>\n </div>\n </template>\n </template>\n </div>\n\n <!-- Experiment list: flat mode -->\n <div v-else ref=\"listRef\" class=\"mint-experiment-selector__list\">\n <div\n v-for=\"(exp, idx) in experiments\"\n :key=\"exp.id\"\n class=\"mint-experiment-selector__row\"\n :class=\"{\n 'mint-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mint-experiment-selector__row--focused': idx === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <div class=\"mint-experiment-selector__row-content\">\n <div class=\"mint-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mint-experiment-selector__meta\">\n <span v-if=\"exp.project_name || exp.project\">{{ exp.project_name || exp.project }}</span>\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"getExperimentStatusVariant(exp.status)\" size=\"sm\">\n {{ formatExperimentStatus(exp.status) }}\n </BasePill>\n </div>\n </div>\n\n <!-- Footer: clear selection -->\n <div v-if=\"currentExperimentId != null\" class=\"mint-experiment-selector__footer\">\n <button\n type=\"button\"\n class=\"mint-experiment-selector__clear-btn\"\n @click=\"handleDeselect\"\n >\n Clear selection\n </button>\n </div>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/experiment-selector-modal.css';\n</style>\n","export type PluginIconFormat = 'path' | 'data-url' | 'https-url' | 'fallback'\n\nexport interface DetectedPluginIcon {\n format: PluginIconFormat\n value: string\n}\n\n// MINT canonical plugin placeholder — page-with-corner-fold glyph.\nexport const PLUGIN_ICON_FALLBACK_PATH = 'M14 7v4a1 1 0 0 0 1 1h4M5 3h9l5 5v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z'\n\nconst PATH_REGEX = /^[Mm][\\s,\\d\\-.]/\nconst RASTER_DATA_URL_REGEX = /^data:image\\/(png|jpeg|jpg|gif|webp);/\n\nexport function normalizePluginIconSource(icon?: string): string {\n return (icon ?? '').replace(/^[\\s]+/, '')\n}\n\nexport function detectPluginIcon(icon?: string): DetectedPluginIcon {\n const raw = normalizePluginIconSource(icon)\n if (!raw) return { format: 'fallback', value: PLUGIN_ICON_FALLBACK_PATH }\n if (PATH_REGEX.test(raw)) return { format: 'path', value: raw }\n if (RASTER_DATA_URL_REGEX.test(raw)) return { format: 'data-url', value: raw }\n if (raw.startsWith('https://')) return { format: 'https-url', value: raw }\n return { format: 'fallback', value: PLUGIN_ICON_FALLBACK_PATH }\n}\n\nexport function isPluginIconFormat(icon?: string): boolean {\n const raw = normalizePluginIconSource(icon)\n return !!raw && detectPluginIcon(raw).format !== 'fallback'\n}\n","<script setup lang=\"ts\">\n/** Private routable action primitive for SDK nav and menu components. */\ndefineOptions({ inheritAttrs: false })\n\ninterface Props {\n href?: string\n to?: string | Record<string, unknown>\n disabled?: boolean\n type?: 'button' | 'submit' | 'reset'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n type: 'button',\n})\n\nconst emit = defineEmits<{\n click: [event: MouseEvent]\n}>()\n\ntype RouterNavigate = (event?: MouseEvent) => unknown\n\nfunction blockDisabledEvent(event: MouseEvent) {\n event.preventDefault()\n event.stopImmediatePropagation()\n}\n\nfunction handleClick(event: MouseEvent) {\n if (props.disabled) {\n blockDisabledEvent(event)\n return\n }\n emit('click', event)\n}\n\nfunction handleRouterClick(event: MouseEvent, navigate: RouterNavigate) {\n if (props.disabled) {\n blockDisabledEvent(event)\n return\n }\n emit('click', event)\n void navigate(event)\n}\n</script>\n\n<template>\n <a\n v-if=\"href\"\n v-bind=\"$attrs\"\n :href=\"disabled ? undefined : href\"\n :aria-disabled=\"disabled || undefined\"\n :tabindex=\"disabled ? -1 : undefined\"\n @click=\"handleClick\"\n >\n <slot />\n </a>\n <router-link\n v-else-if=\"to\"\n :to=\"to\"\n custom\n v-slot=\"{ href: routerHref, navigate }\"\n >\n <a\n v-bind=\"$attrs\"\n :href=\"disabled ? undefined : routerHref\"\n :aria-disabled=\"disabled || undefined\"\n :tabindex=\"disabled ? -1 : undefined\"\n @click=\"handleRouterClick($event, navigate)\"\n >\n <slot />\n </a>\n </router-link>\n <button\n v-else\n v-bind=\"$attrs\"\n :type=\"type\"\n :disabled=\"disabled\"\n @click=\"handleClick\"\n >\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\n/** Private routable action primitive for SDK nav and menu components. */\ndefineOptions({ inheritAttrs: false })\n\ninterface Props {\n href?: string\n to?: string | Record<string, unknown>\n disabled?: boolean\n type?: 'button' | 'submit' | 'reset'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n type: 'button',\n})\n\nconst emit = defineEmits<{\n click: [event: MouseEvent]\n}>()\n\ntype RouterNavigate = (event?: MouseEvent) => unknown\n\nfunction blockDisabledEvent(event: MouseEvent) {\n event.preventDefault()\n event.stopImmediatePropagation()\n}\n\nfunction handleClick(event: MouseEvent) {\n if (props.disabled) {\n blockDisabledEvent(event)\n return\n }\n emit('click', event)\n}\n\nfunction handleRouterClick(event: MouseEvent, navigate: RouterNavigate) {\n if (props.disabled) {\n blockDisabledEvent(event)\n return\n }\n emit('click', event)\n void navigate(event)\n}\n</script>\n\n<template>\n <a\n v-if=\"href\"\n v-bind=\"$attrs\"\n :href=\"disabled ? undefined : href\"\n :aria-disabled=\"disabled || undefined\"\n :tabindex=\"disabled ? -1 : undefined\"\n @click=\"handleClick\"\n >\n <slot />\n </a>\n <router-link\n v-else-if=\"to\"\n :to=\"to\"\n custom\n v-slot=\"{ href: routerHref, navigate }\"\n >\n <a\n v-bind=\"$attrs\"\n :href=\"disabled ? undefined : routerHref\"\n :aria-disabled=\"disabled || undefined\"\n :tabindex=\"disabled ? -1 : undefined\"\n @click=\"handleRouterClick($event, navigate)\"\n >\n <slot />\n </a>\n </router-link>\n <button\n v-else\n v-bind=\"$attrs\"\n :type=\"type\"\n :disabled=\"disabled\"\n @click=\"handleClick\"\n >\n <slot />\n </button>\n</template>\n","<script setup lang=\"ts\">\n/** Renders a plugin's icon as a sized chip. Auto-detects format:\n * - SVG path data (e.g. \"M13 10V3...\") → inline <svg><path>\n * - Raster data URL (data:image/png|jpeg|jpg|gif|webp;...) → <img>\n * - https:// URL → <img> with no-referrer + lazy loading\n * - Anything else → MINT canonical plugin placeholder path\n *\n * http:// URLs and data:image/svg+xml URLs are deliberately rejected\n * (mixed-content + XSS, see spec 2026-05-04-plugin-icon-component-design.md). */\nimport { computed } from 'vue'\nimport { detectPluginIcon, type DetectedPluginIcon } from '../utils/pluginIcon'\n\ndefineOptions({ name: 'PluginIcon' })\n\ninterface Props {\n icon?: string\n size?: 'sm' | 'md' | 'lg'\n variant?: 'solid' | 'tinted'\n tone?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n variant: 'solid',\n})\n\nconst detected = computed<DetectedPluginIcon>(() => detectPluginIcon(props.icon))\n\nconst rootClasses = computed(() => [\n 'mint-plugin-icon',\n `mint-plugin-icon--${props.size}`,\n `mint-plugin-icon--${props.variant}`,\n])\n\nconst rootStyle = computed(() =>\n props.tone ? { '--mint-plugin-icon-tone': props.tone } : undefined,\n)\n</script>\n\n<template>\n <span :class=\"rootClasses\" :style=\"rootStyle\">\n <svg\n v-if=\"detected.format === 'path' || detected.format === 'fallback'\"\n class=\"mint-plugin-icon__svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path :d=\"detected.value\" />\n </svg>\n <img\n v-else\n class=\"mint-plugin-icon__img\"\n :src=\"detected.value\"\n alt=\"\"\n referrerpolicy=\"no-referrer\"\n loading=\"lazy\"\n />\n </span>\n</template>\n\n<style>\n@import '../styles/components/plugin-icon.css';\n</style>\n","<script setup lang=\"ts\">\n/** Renders a plugin's icon as a sized chip. Auto-detects format:\n * - SVG path data (e.g. \"M13 10V3...\") → inline <svg><path>\n * - Raster data URL (data:image/png|jpeg|jpg|gif|webp;...) → <img>\n * - https:// URL → <img> with no-referrer + lazy loading\n * - Anything else → MINT canonical plugin placeholder path\n *\n * http:// URLs and data:image/svg+xml URLs are deliberately rejected\n * (mixed-content + XSS, see spec 2026-05-04-plugin-icon-component-design.md). */\nimport { computed } from 'vue'\nimport { detectPluginIcon, type DetectedPluginIcon } from '../utils/pluginIcon'\n\ndefineOptions({ name: 'PluginIcon' })\n\ninterface Props {\n icon?: string\n size?: 'sm' | 'md' | 'lg'\n variant?: 'solid' | 'tinted'\n tone?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n variant: 'solid',\n})\n\nconst detected = computed<DetectedPluginIcon>(() => detectPluginIcon(props.icon))\n\nconst rootClasses = computed(() => [\n 'mint-plugin-icon',\n `mint-plugin-icon--${props.size}`,\n `mint-plugin-icon--${props.variant}`,\n])\n\nconst rootStyle = computed(() =>\n props.tone ? { '--mint-plugin-icon-tone': props.tone } : undefined,\n)\n</script>\n\n<template>\n <span :class=\"rootClasses\" :style=\"rootStyle\">\n <svg\n v-if=\"detected.format === 'path' || detected.format === 'fallback'\"\n class=\"mint-plugin-icon__svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path :d=\"detected.value\" />\n </svg>\n <img\n v-else\n class=\"mint-plugin-icon__img\"\n :src=\"detected.value\"\n alt=\"\"\n referrerpolicy=\"no-referrer\"\n loading=\"lazy\"\n />\n </span>\n</template>\n\n<style>\n@import '../styles/components/plugin-icon.css';\n</style>\n","<script setup lang=\"ts\">\n/** Dropdown trigger that switches between named pages, closing on outside click or Escape. */\nimport { computed } from 'vue'\nimport { useDropdownState } from '../../composables/useDropdownState'\nimport type { PageSelectorItem, PageSelectorItemInput } from '../../types/components'\nimport { normalizeItemInput } from '../../utils/items'\nimport { isPluginIconFormat } from '../../utils/pluginIcon'\nimport ActionItemInternal from './ActionItemInternal.vue'\nimport PluginIcon from '../PluginIcon.vue'\n\ninterface Props {\n pages: PageSelectorItemInput[]\n currentPageId?: string\n placeholder?: string\n minWidth?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n placeholder: 'Go to…',\n minWidth: '11.25rem',\n})\n\nconst emit = defineEmits<{\n select: [page: PageSelectorItem]\n}>()\n\nconst { isOpen, rootRef, close, toggle } = useDropdownState()\nconst normalizedPages = computed<PageSelectorItem[]>(() => props.pages.map(normalizeItemInput))\n\nconst current = computed<PageSelectorItem | undefined>(() =>\n normalizedPages.value.find((p) => p.id === props.currentPageId) ?? normalizedPages.value[0],\n)\n\nfunction handleSelect(page: PageSelectorItem) {\n if (page.disabled) return\n if (page.href || page.to) {\n close()\n return\n }\n emit('select', page)\n close()\n}\n</script>\n\n<template>\n <div ref=\"rootRef\" class=\"mint-page-selector\" :style=\"{ minWidth }\">\n <button\n type=\"button\"\n class=\"mint-page-selector__trigger\"\n :class=\"{ 'mint-page-selector__trigger--open': isOpen }\"\n :aria-expanded=\"isOpen\"\n aria-haspopup=\"menu\"\n @click.stop=\"toggle\"\n >\n <span\n v-if=\"current\"\n class=\"mint-page-selector__icon\"\n :class=\"{ 'mint-page-selector__icon--metadata': isPluginIconFormat(current.icon) }\"\n >\n <slot name=\"icon\" :page=\"current\">\n <PluginIcon\n v-if=\"isPluginIconFormat(current.icon)\"\n class=\"mint-page-selector__trigger-metadata-icon\"\n :icon=\"current.icon\"\n size=\"sm\"\n variant=\"tinted\"\n />\n <span v-else class=\"mint-page-selector__icon-fallback\">{{ current.label.charAt(0) }}</span>\n </slot>\n </span>\n <span class=\"mint-page-selector__label\">{{ current?.label ?? placeholder }}</span>\n <span class=\"mint-page-selector__chevron-wrap\" aria-hidden=\"true\">\n <svg\n class=\"mint-page-selector__chevron\"\n :class=\"{ 'mint-page-selector__chevron--open': isOpen }\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </span>\n </button>\n\n <div v-if=\"isOpen\" class=\"mint-page-selector__menu\" role=\"menu\">\n <div class=\"mint-page-selector__menu-title\">Go to</div>\n <ActionItemInternal\n v-for=\"page in normalizedPages\"\n :key=\"page.id\"\n :href=\"page.href\"\n :to=\"page.to\"\n :disabled=\"page.disabled\"\n :class=\"[\n 'mint-page-selector__item',\n { 'mint-page-selector__item--active': page.id === current?.id },\n { 'mint-page-selector__item--disabled': page.disabled },\n ]\"\n role=\"menuitem\"\n @click=\"handleSelect(page)\"\n >\n <span\n class=\"mint-page-selector__item-icon\"\n :class=\"{ 'mint-page-selector__item-icon--metadata': isPluginIconFormat(page.icon) }\"\n >\n <slot name=\"item-icon\" :page=\"page\">\n <PluginIcon\n v-if=\"isPluginIconFormat(page.icon)\"\n class=\"mint-page-selector__metadata-icon\"\n :icon=\"page.icon\"\n size=\"sm\"\n variant=\"tinted\"\n />\n <span v-else class=\"mint-page-selector__icon-fallback\">{{ page.label.charAt(0) }}</span>\n </slot>\n </span>\n <span class=\"mint-page-selector__item-label\">{{ page.label }}</span>\n <span v-if=\"page.hint\" class=\"mint-page-selector__item-hint\">{{ page.hint }}</span>\n </ActionItemInternal>\n </div>\n </div>\n</template>\n\n<style>\n@import '../../styles/components/app-page-selector.css';\n</style>\n","<script setup lang=\"ts\">\n/** Dropdown trigger that switches between named pages, closing on outside click or Escape. */\nimport { computed } from 'vue'\nimport { useDropdownState } from '../../composables/useDropdownState'\nimport type { PageSelectorItem, PageSelectorItemInput } from '../../types/components'\nimport { normalizeItemInput } from '../../utils/items'\nimport { isPluginIconFormat } from '../../utils/pluginIcon'\nimport ActionItemInternal from './ActionItemInternal.vue'\nimport PluginIcon from '../PluginIcon.vue'\n\ninterface Props {\n pages: PageSelectorItemInput[]\n currentPageId?: string\n placeholder?: string\n minWidth?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n placeholder: 'Go to…',\n minWidth: '11.25rem',\n})\n\nconst emit = defineEmits<{\n select: [page: PageSelectorItem]\n}>()\n\nconst { isOpen, rootRef, close, toggle } = useDropdownState()\nconst normalizedPages = computed<PageSelectorItem[]>(() => props.pages.map(normalizeItemInput))\n\nconst current = computed<PageSelectorItem | undefined>(() =>\n normalizedPages.value.find((p) => p.id === props.currentPageId) ?? normalizedPages.value[0],\n)\n\nfunction handleSelect(page: PageSelectorItem) {\n if (page.disabled) return\n if (page.href || page.to) {\n close()\n return\n }\n emit('select', page)\n close()\n}\n</script>\n\n<template>\n <div ref=\"rootRef\" class=\"mint-page-selector\" :style=\"{ minWidth }\">\n <button\n type=\"button\"\n class=\"mint-page-selector__trigger\"\n :class=\"{ 'mint-page-selector__trigger--open': isOpen }\"\n :aria-expanded=\"isOpen\"\n aria-haspopup=\"menu\"\n @click.stop=\"toggle\"\n >\n <span\n v-if=\"current\"\n class=\"mint-page-selector__icon\"\n :class=\"{ 'mint-page-selector__icon--metadata': isPluginIconFormat(current.icon) }\"\n >\n <slot name=\"icon\" :page=\"current\">\n <PluginIcon\n v-if=\"isPluginIconFormat(current.icon)\"\n class=\"mint-page-selector__trigger-metadata-icon\"\n :icon=\"current.icon\"\n size=\"sm\"\n variant=\"tinted\"\n />\n <span v-else class=\"mint-page-selector__icon-fallback\">{{ current.label.charAt(0) }}</span>\n </slot>\n </span>\n <span class=\"mint-page-selector__label\">{{ current?.label ?? placeholder }}</span>\n <span class=\"mint-page-selector__chevron-wrap\" aria-hidden=\"true\">\n <svg\n class=\"mint-page-selector__chevron\"\n :class=\"{ 'mint-page-selector__chevron--open': isOpen }\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </span>\n </button>\n\n <div v-if=\"isOpen\" class=\"mint-page-selector__menu\" role=\"menu\">\n <div class=\"mint-page-selector__menu-title\">Go to</div>\n <ActionItemInternal\n v-for=\"page in normalizedPages\"\n :key=\"page.id\"\n :href=\"page.href\"\n :to=\"page.to\"\n :disabled=\"page.disabled\"\n :class=\"[\n 'mint-page-selector__item',\n { 'mint-page-selector__item--active': page.id === current?.id },\n { 'mint-page-selector__item--disabled': page.disabled },\n ]\"\n role=\"menuitem\"\n @click=\"handleSelect(page)\"\n >\n <span\n class=\"mint-page-selector__item-icon\"\n :class=\"{ 'mint-page-selector__item-icon--metadata': isPluginIconFormat(page.icon) }\"\n >\n <slot name=\"item-icon\" :page=\"page\">\n <PluginIcon\n v-if=\"isPluginIconFormat(page.icon)\"\n class=\"mint-page-selector__metadata-icon\"\n :icon=\"page.icon\"\n size=\"sm\"\n variant=\"tinted\"\n />\n <span v-else class=\"mint-page-selector__icon-fallback\">{{ page.label.charAt(0) }}</span>\n </slot>\n </span>\n <span class=\"mint-page-selector__item-label\">{{ page.label }}</span>\n <span v-if=\"page.hint\" class=\"mint-page-selector__item-hint\">{{ page.hint }}</span>\n </ActionItemInternal>\n </div>\n </div>\n</template>\n\n<style>\n@import '../../styles/components/app-page-selector.css';\n</style>\n","<script setup lang=\"ts\">\n/** Horizontal pill-style navigation bar that emits select events and supports href, router-link, button, or dropdown items. */\nimport { computed, ref } from 'vue'\nimport type { PillNavItem, PillNavItemInput, PillNavOption, PillNavOptionInput } from '../../types/components'\nimport { normalizeItemInput } from '../../utils/items'\nimport { useEventListener } from '../../composables/useEventListener'\nimport ActionItemInternal from './ActionItemInternal.vue'\n\ninterface Props {\n items: PillNavItemInput[]\n currentItemId?: string\n}\n\nconst props = defineProps<Props>()\n\nconst emit = defineEmits<{\n select: [item: PillNavItem]\n 'option-select': [option: PillNavOption, item: PillNavItem]\n}>()\n\nconst openItemId = ref<string | null>(null)\nconst itemRefs = ref<Map<string, HTMLElement>>(new Map())\n\nconst normalizedItems = computed<PillNavItem[]>(() => props.items.map(normalizePillNavItem))\n\nfunction handleClick(item: PillNavItem) {\n if (item.disabled) return\n if (item.children?.length) {\n toggleDropdown(item.id)\n return\n }\n if (item.href || item.to) return\n emit('select', item)\n}\n\nfunction handleOptionClick(option: PillNavOption, item: PillNavItem) {\n if (option.disabled) return\n if (!option.href && !option.to) emit('option-select', option, item)\n openItemId.value = null\n}\n\nfunction normalizePillNavItem(item: PillNavItemInput): PillNavItem {\n const normalized = normalizeItemInput<Omit<PillNavItem, 'children'> & { children?: PillNavOptionInput[] }>(item)\n return {\n ...normalized,\n children: normalized.children?.map(normalizeItemInput),\n }\n}\n\nfunction toggleDropdown(itemId: string) {\n openItemId.value = openItemId.value === itemId ? null : itemId\n}\n\nfunction setItemRef(el: HTMLElement | null, itemId: string) {\n if (el) {\n itemRefs.value.set(itemId, el)\n } else {\n itemRefs.value.delete(itemId)\n }\n}\n\nfunction handleClickOutside(event: MouseEvent) {\n const target = event.target as Node\n const clickedInside = Array.from(itemRefs.value.values()).some(el => el.contains(target))\n if (!clickedInside) openItemId.value = null\n}\n\nconst hasActiveChild = (item: PillNavItem) =>\n item.children?.some(child => child.id === props.currentItemId) ?? false\n\nfunction isSvgIcon(icon: PillNavItem['icon']): icon is string | string[] {\n if (!icon) return false\n return Array.isArray(icon) || icon.startsWith('M') || icon.startsWith('m')\n}\n\nuseEventListener(() => document, 'click', handleClickOutside)\n</script>\n\n<template>\n <nav class=\"mint-pill-nav\" aria-label=\"Primary\">\n <div\n v-for=\"item in normalizedItems\"\n :key=\"item.id\"\n :ref=\"(el) => setItemRef(el as HTMLElement | null, item.id)\"\n class=\"mint-pill-nav__item-wrap\"\n >\n <button\n v-if=\"item.children?.length\"\n type=\"button\"\n :class=\"[\n 'mint-pill-nav__item',\n { 'mint-pill-nav__item--active': item.id === currentItemId || hasActiveChild(item) },\n { 'mint-pill-nav__item--disabled': item.disabled },\n ]\"\n :disabled=\"item.disabled\"\n :aria-expanded=\"openItemId === item.id\"\n aria-haspopup=\"menu\"\n @click.stop=\"handleClick(item)\"\n >\n <svg\n v-if=\"isSvgIcon(item.icon)\"\n class=\"mint-pill-nav__icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <template v-if=\"Array.isArray(item.icon)\">\n <path v-for=\"(d, index) in item.icon\" :key=\"index\" :d=\"d\" />\n </template>\n <path v-else :d=\"item.icon\" />\n </svg>\n {{ item.label }}\n <svg\n class=\"mint-pill-nav__chevron\"\n :class=\"{ 'mint-pill-nav__chevron--open': openItemId === item.id }\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <ActionItemInternal\n v-else\n :href=\"item.href\"\n :to=\"item.to\"\n :disabled=\"item.disabled\"\n :class=\"[\n 'mint-pill-nav__item',\n { 'mint-pill-nav__item--active': item.id === currentItemId },\n { 'mint-pill-nav__item--disabled': item.disabled },\n ]\"\n @click=\"handleClick(item)\"\n >\n <svg\n v-if=\"isSvgIcon(item.icon)\"\n class=\"mint-pill-nav__icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <template v-if=\"Array.isArray(item.icon)\">\n <path v-for=\"(d, index) in item.icon\" :key=\"index\" :d=\"d\" />\n </template>\n <path v-else :d=\"item.icon\" />\n </svg>\n {{ item.label }}\n </ActionItemInternal>\n\n <div\n v-if=\"item.children?.length && openItemId === item.id\"\n class=\"mint-pill-nav__dropdown\"\n role=\"menu\"\n >\n <ActionItemInternal\n v-for=\"option in item.children\"\n :key=\"option.id\"\n :href=\"option.href\"\n :to=\"option.to\"\n :disabled=\"option.disabled\"\n :class=\"[\n 'mint-pill-nav__dropdown-item',\n { 'mint-pill-nav__dropdown-item--active': option.id === currentItemId },\n { 'mint-pill-nav__dropdown-item--disabled': option.disabled },\n ]\"\n role=\"menuitem\"\n @click=\"handleOptionClick(option, item)\"\n >\n <span class=\"mint-pill-nav__dropdown-label\">{{ option.label }}</span>\n <span v-if=\"option.description\" class=\"mint-pill-nav__dropdown-description\">\n {{ option.description }}\n </span>\n </ActionItemInternal>\n </div>\n </div>\n </nav>\n</template>\n\n<style>\n@import '../../styles/components/app-pill-nav.css';\n</style>\n","<script setup lang=\"ts\">\n/** Horizontal pill-style navigation bar that emits select events and supports href, router-link, button, or dropdown items. */\nimport { computed, ref } from 'vue'\nimport type { PillNavItem, PillNavItemInput, PillNavOption, PillNavOptionInput } from '../../types/components'\nimport { normalizeItemInput } from '../../utils/items'\nimport { useEventListener } from '../../composables/useEventListener'\nimport ActionItemInternal from './ActionItemInternal.vue'\n\ninterface Props {\n items: PillNavItemInput[]\n currentItemId?: string\n}\n\nconst props = defineProps<Props>()\n\nconst emit = defineEmits<{\n select: [item: PillNavItem]\n 'option-select': [option: PillNavOption, item: PillNavItem]\n}>()\n\nconst openItemId = ref<string | null>(null)\nconst itemRefs = ref<Map<string, HTMLElement>>(new Map())\n\nconst normalizedItems = computed<PillNavItem[]>(() => props.items.map(normalizePillNavItem))\n\nfunction handleClick(item: PillNavItem) {\n if (item.disabled) return\n if (item.children?.length) {\n toggleDropdown(item.id)\n return\n }\n if (item.href || item.to) return\n emit('select', item)\n}\n\nfunction handleOptionClick(option: PillNavOption, item: PillNavItem) {\n if (option.disabled) return\n if (!option.href && !option.to) emit('option-select', option, item)\n openItemId.value = null\n}\n\nfunction normalizePillNavItem(item: PillNavItemInput): PillNavItem {\n const normalized = normalizeItemInput<Omit<PillNavItem, 'children'> & { children?: PillNavOptionInput[] }>(item)\n return {\n ...normalized,\n children: normalized.children?.map(normalizeItemInput),\n }\n}\n\nfunction toggleDropdown(itemId: string) {\n openItemId.value = openItemId.value === itemId ? null : itemId\n}\n\nfunction setItemRef(el: HTMLElement | null, itemId: string) {\n if (el) {\n itemRefs.value.set(itemId, el)\n } else {\n itemRefs.value.delete(itemId)\n }\n}\n\nfunction handleClickOutside(event: MouseEvent) {\n const target = event.target as Node\n const clickedInside = Array.from(itemRefs.value.values()).some(el => el.contains(target))\n if (!clickedInside) openItemId.value = null\n}\n\nconst hasActiveChild = (item: PillNavItem) =>\n item.children?.some(child => child.id === props.currentItemId) ?? false\n\nfunction isSvgIcon(icon: PillNavItem['icon']): icon is string | string[] {\n if (!icon) return false\n return Array.isArray(icon) || icon.startsWith('M') || icon.startsWith('m')\n}\n\nuseEventListener(() => document, 'click', handleClickOutside)\n</script>\n\n<template>\n <nav class=\"mint-pill-nav\" aria-label=\"Primary\">\n <div\n v-for=\"item in normalizedItems\"\n :key=\"item.id\"\n :ref=\"(el) => setItemRef(el as HTMLElement | null, item.id)\"\n class=\"mint-pill-nav__item-wrap\"\n >\n <button\n v-if=\"item.children?.length\"\n type=\"button\"\n :class=\"[\n 'mint-pill-nav__item',\n { 'mint-pill-nav__item--active': item.id === currentItemId || hasActiveChild(item) },\n { 'mint-pill-nav__item--disabled': item.disabled },\n ]\"\n :disabled=\"item.disabled\"\n :aria-expanded=\"openItemId === item.id\"\n aria-haspopup=\"menu\"\n @click.stop=\"handleClick(item)\"\n >\n <svg\n v-if=\"isSvgIcon(item.icon)\"\n class=\"mint-pill-nav__icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <template v-if=\"Array.isArray(item.icon)\">\n <path v-for=\"(d, index) in item.icon\" :key=\"index\" :d=\"d\" />\n </template>\n <path v-else :d=\"item.icon\" />\n </svg>\n {{ item.label }}\n <svg\n class=\"mint-pill-nav__chevron\"\n :class=\"{ 'mint-pill-nav__chevron--open': openItemId === item.id }\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <ActionItemInternal\n v-else\n :href=\"item.href\"\n :to=\"item.to\"\n :disabled=\"item.disabled\"\n :class=\"[\n 'mint-pill-nav__item',\n { 'mint-pill-nav__item--active': item.id === currentItemId },\n { 'mint-pill-nav__item--disabled': item.disabled },\n ]\"\n @click=\"handleClick(item)\"\n >\n <svg\n v-if=\"isSvgIcon(item.icon)\"\n class=\"mint-pill-nav__icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <template v-if=\"Array.isArray(item.icon)\">\n <path v-for=\"(d, index) in item.icon\" :key=\"index\" :d=\"d\" />\n </template>\n <path v-else :d=\"item.icon\" />\n </svg>\n {{ item.label }}\n </ActionItemInternal>\n\n <div\n v-if=\"item.children?.length && openItemId === item.id\"\n class=\"mint-pill-nav__dropdown\"\n role=\"menu\"\n >\n <ActionItemInternal\n v-for=\"option in item.children\"\n :key=\"option.id\"\n :href=\"option.href\"\n :to=\"option.to\"\n :disabled=\"option.disabled\"\n :class=\"[\n 'mint-pill-nav__dropdown-item',\n { 'mint-pill-nav__dropdown-item--active': option.id === currentItemId },\n { 'mint-pill-nav__dropdown-item--disabled': option.disabled },\n ]\"\n role=\"menuitem\"\n @click=\"handleOptionClick(option, item)\"\n >\n <span class=\"mint-pill-nav__dropdown-label\">{{ option.label }}</span>\n <span v-if=\"option.description\" class=\"mint-pill-nav__dropdown-description\">\n {{ option.description }}\n </span>\n </ActionItemInternal>\n </div>\n </div>\n </nav>\n</template>\n\n<style>\n@import '../../styles/components/app-pill-nav.css';\n</style>\n","<script setup lang=\"ts\">\n/** Avatar-triggered dropdown with user name/email header, configurable menu items, dividers, and a sign-out action. */\nimport { computed } from 'vue'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport type { AccountMenuItem } from '../types/components'\nimport ActionItemInternal from './internal/ActionItemInternal.vue'\n\ninterface Props {\n userName?: string\n userInitial?: string\n userEmail?: string\n items?: AccountMenuItem[]\n showSignOut?: boolean\n signOutLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n items: () => [] as AccountMenuItem[],\n showSignOut: true,\n signOutLabel: 'Sign out',\n})\n\nconst emit = defineEmits<{\n select: [item: AccountMenuItem]\n 'sign-out': []\n 'menu-open': []\n 'menu-close': []\n}>()\n\nconst { isOpen, rootRef, close, toggle } = useDropdownState({\n onOpen: () => emit('menu-open'),\n onClose: () => emit('menu-close'),\n})\n\nconst initials = computed(() => {\n if (props.userInitial) return props.userInitial.slice(0, 2).toUpperCase()\n if (props.userName) {\n const parts = props.userName.trim().split(/\\s+/).filter(Boolean)\n const first = parts[0]\n if (!first) return 'U'\n const second = parts[1]\n if (second) return (first.charAt(0) + second.charAt(0)).toUpperCase()\n return first.slice(0, 2).toUpperCase()\n }\n return 'U'\n})\n\nfunction closeSilently() {\n isOpen.value = false\n}\n\nfunction handleSelect(item: AccountMenuItem) {\n if (item.divider) return\n emit('select', item)\n close()\n}\n\nfunction handleSignOut() {\n emit('sign-out')\n close()\n}\n</script>\n\n<template>\n <div ref=\"rootRef\" class=\"mint-avatar-menu\">\n <button\n type=\"button\"\n class=\"mint-avatar-menu__trigger\"\n :class=\"{ 'mint-avatar-menu__trigger--open': isOpen }\"\n :aria-expanded=\"isOpen\"\n aria-haspopup=\"menu\"\n :title=\"userName ? `${userName} · Account menu` : 'Account menu'\"\n @click.stop=\"toggle\"\n >\n <span class=\"mint-avatar-menu__avatar\">\n <slot name=\"avatar\" :initials=\"initials\">\n {{ initials }}\n </slot>\n </span>\n <span v-if=\"userName\" class=\"mint-avatar-menu__name\">{{ userName }}</span>\n </button>\n\n <div v-if=\"isOpen\" class=\"mint-avatar-menu__panel\" role=\"menu\">\n <div v-if=\"userName || userEmail\" class=\"mint-avatar-menu__header\">\n <div v-if=\"userName\" class=\"mint-avatar-menu__user-name\">{{ userName }}</div>\n <div v-if=\"userEmail\" class=\"mint-avatar-menu__user-email\">{{ userEmail }}</div>\n </div>\n\n <slot name=\"items\" :close=\"closeSilently\">\n <template v-for=\"item in items\" :key=\"item.id\">\n <div v-if=\"item.divider\" class=\"mint-avatar-menu__divider\" role=\"separator\" />\n <ActionItemInternal\n v-else\n :href=\"item.href\"\n :to=\"item.to\"\n :class=\"['mint-avatar-menu__item', { 'mint-avatar-menu__item--danger': item.danger }]\"\n role=\"menuitem\"\n @click=\"handleSelect(item)\"\n >\n <span v-if=\"item.icon\" class=\"mint-avatar-menu__item-icon\">\n <slot name=\"item-icon\" :item=\"item\" />\n </span>\n <span class=\"mint-avatar-menu__item-label\">{{ item.label }}</span>\n <span v-if=\"item.rightLabel\" class=\"mint-avatar-menu__item-right\">{{ item.rightLabel }}</span>\n </ActionItemInternal>\n </template>\n </slot>\n\n <template v-if=\"showSignOut\">\n <div class=\"mint-avatar-menu__divider\" role=\"separator\" />\n <button\n type=\"button\"\n class=\"mint-avatar-menu__item mint-avatar-menu__item--danger\"\n role=\"menuitem\"\n @click=\"handleSignOut\"\n >\n <span class=\"mint-avatar-menu__item-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4\" />\n <polyline points=\"16 17 21 12 16 7\" />\n <line x1=\"21\" y1=\"12\" x2=\"9\" y2=\"12\" />\n </svg>\n </span>\n <span class=\"mint-avatar-menu__item-label\">{{ signOutLabel }}</span>\n </button>\n </template>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/app-avatar-menu.css';\n</style>\n","<script setup lang=\"ts\">\n/** Avatar-triggered dropdown with user name/email header, configurable menu items, dividers, and a sign-out action. */\nimport { computed } from 'vue'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport type { AccountMenuItem } from '../types/components'\nimport ActionItemInternal from './internal/ActionItemInternal.vue'\n\ninterface Props {\n userName?: string\n userInitial?: string\n userEmail?: string\n items?: AccountMenuItem[]\n showSignOut?: boolean\n signOutLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n items: () => [] as AccountMenuItem[],\n showSignOut: true,\n signOutLabel: 'Sign out',\n})\n\nconst emit = defineEmits<{\n select: [item: AccountMenuItem]\n 'sign-out': []\n 'menu-open': []\n 'menu-close': []\n}>()\n\nconst { isOpen, rootRef, close, toggle } = useDropdownState({\n onOpen: () => emit('menu-open'),\n onClose: () => emit('menu-close'),\n})\n\nconst initials = computed(() => {\n if (props.userInitial) return props.userInitial.slice(0, 2).toUpperCase()\n if (props.userName) {\n const parts = props.userName.trim().split(/\\s+/).filter(Boolean)\n const first = parts[0]\n if (!first) return 'U'\n const second = parts[1]\n if (second) return (first.charAt(0) + second.charAt(0)).toUpperCase()\n return first.slice(0, 2).toUpperCase()\n }\n return 'U'\n})\n\nfunction closeSilently() {\n isOpen.value = false\n}\n\nfunction handleSelect(item: AccountMenuItem) {\n if (item.divider) return\n emit('select', item)\n close()\n}\n\nfunction handleSignOut() {\n emit('sign-out')\n close()\n}\n</script>\n\n<template>\n <div ref=\"rootRef\" class=\"mint-avatar-menu\">\n <button\n type=\"button\"\n class=\"mint-avatar-menu__trigger\"\n :class=\"{ 'mint-avatar-menu__trigger--open': isOpen }\"\n :aria-expanded=\"isOpen\"\n aria-haspopup=\"menu\"\n :title=\"userName ? `${userName} · Account menu` : 'Account menu'\"\n @click.stop=\"toggle\"\n >\n <span class=\"mint-avatar-menu__avatar\">\n <slot name=\"avatar\" :initials=\"initials\">\n {{ initials }}\n </slot>\n </span>\n <span v-if=\"userName\" class=\"mint-avatar-menu__name\">{{ userName }}</span>\n </button>\n\n <div v-if=\"isOpen\" class=\"mint-avatar-menu__panel\" role=\"menu\">\n <div v-if=\"userName || userEmail\" class=\"mint-avatar-menu__header\">\n <div v-if=\"userName\" class=\"mint-avatar-menu__user-name\">{{ userName }}</div>\n <div v-if=\"userEmail\" class=\"mint-avatar-menu__user-email\">{{ userEmail }}</div>\n </div>\n\n <slot name=\"items\" :close=\"closeSilently\">\n <template v-for=\"item in items\" :key=\"item.id\">\n <div v-if=\"item.divider\" class=\"mint-avatar-menu__divider\" role=\"separator\" />\n <ActionItemInternal\n v-else\n :href=\"item.href\"\n :to=\"item.to\"\n :class=\"['mint-avatar-menu__item', { 'mint-avatar-menu__item--danger': item.danger }]\"\n role=\"menuitem\"\n @click=\"handleSelect(item)\"\n >\n <span v-if=\"item.icon\" class=\"mint-avatar-menu__item-icon\">\n <slot name=\"item-icon\" :item=\"item\" />\n </span>\n <span class=\"mint-avatar-menu__item-label\">{{ item.label }}</span>\n <span v-if=\"item.rightLabel\" class=\"mint-avatar-menu__item-right\">{{ item.rightLabel }}</span>\n </ActionItemInternal>\n </template>\n </slot>\n\n <template v-if=\"showSignOut\">\n <div class=\"mint-avatar-menu__divider\" role=\"separator\" />\n <button\n type=\"button\"\n class=\"mint-avatar-menu__item mint-avatar-menu__item--danger\"\n role=\"menuitem\"\n @click=\"handleSignOut\"\n >\n <span class=\"mint-avatar-menu__item-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4\" />\n <polyline points=\"16 17 21 12 16 7\" />\n <line x1=\"21\" y1=\"12\" x2=\"9\" y2=\"12\" />\n </svg>\n </span>\n <span class=\"mint-avatar-menu__item-label\">{{ signOutLabel }}</span>\n </button>\n </template>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/app-avatar-menu.css';\n</style>\n","<script setup lang=\"ts\">\n/** Dropdown menu for switching between installed plugins with color swatches, version badges, and an install link. */\nimport { useDropdownState } from '../composables/useDropdownState'\nimport type { PluginSwitcherPlugin } from '../types/components'\nimport ActionItemInternal from './internal/ActionItemInternal.vue'\n\ninterface Props {\n current: PluginSwitcherPlugin\n plugins?: PluginSwitcherPlugin[]\n installLabel?: string\n installTo?: string\n installHref?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n plugins: () => [] as PluginSwitcherPlugin[],\n installLabel: 'Install plugin…',\n})\n\nconst emit = defineEmits<{\n select: [plugin: PluginSwitcherPlugin]\n 'install-click': []\n}>()\n\nconst { isOpen, rootRef, close, toggle } = useDropdownState()\n\nfunction initialsOf(plugin: PluginSwitcherPlugin) {\n const label = plugin.label || plugin.id\n return label.slice(0, 2).toUpperCase()\n}\n\nfunction handleSelect(plugin: PluginSwitcherPlugin) {\n if (plugin.id === props.current.id) {\n close()\n return\n }\n emit('select', plugin)\n close()\n}\n\nfunction handleInstall() {\n emit('install-click')\n close()\n}\n</script>\n\n<template>\n <div ref=\"rootRef\" class=\"mint-plugin-switcher\">\n <button\n type=\"button\"\n class=\"mint-plugin-switcher__trigger\"\n :class=\"{ 'mint-plugin-switcher__trigger--open': isOpen }\"\n :aria-expanded=\"isOpen\"\n aria-haspopup=\"menu\"\n @click.stop=\"toggle\"\n >\n <span class=\"mint-plugin-switcher__label\">{{ current.label }}</span>\n <span v-if=\"current.version\" class=\"mint-plugin-switcher__version\">v{{ current.version }}</span>\n <span class=\"mint-plugin-switcher__chevron-wrap\" aria-hidden=\"true\">\n <svg\n class=\"mint-plugin-switcher__chevron\"\n :class=\"{ 'mint-plugin-switcher__chevron--open': isOpen }\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </span>\n </button>\n\n <div v-if=\"isOpen\" class=\"mint-plugin-switcher__menu\" role=\"menu\">\n <div class=\"mint-plugin-switcher__menu-title\">Switch plugin</div>\n <ActionItemInternal\n v-for=\"plugin in plugins\"\n :key=\"plugin.id\"\n :href=\"plugin.href\"\n :to=\"plugin.to\"\n :class=\"[\n 'mint-plugin-switcher__item',\n { 'mint-plugin-switcher__item--active': plugin.id === current.id },\n ]\"\n role=\"menuitem\"\n @click=\"handleSelect(plugin)\"\n >\n <span\n class=\"mint-plugin-switcher__item-swatch\"\n :style=\"{ background: plugin.color ?? 'var(--color-primary)' }\"\n >\n {{ initialsOf(plugin) }}\n </span>\n <span class=\"mint-plugin-switcher__item-label\">{{ plugin.label }}</span>\n <span v-if=\"plugin.version\" class=\"mint-plugin-switcher__item-version\">v{{ plugin.version }}</span>\n <svg\n v-if=\"plugin.id === current.id\"\n class=\"mint-plugin-switcher__item-check\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M5 13l4 4L19 7\" />\n </svg>\n </ActionItemInternal>\n\n <template v-if=\"plugins.length && (installHref || installTo || $slots.install)\">\n <div class=\"mint-plugin-switcher__divider\" role=\"separator\" />\n </template>\n <slot name=\"install\">\n <ActionItemInternal\n v-if=\"installHref || installTo\"\n :href=\"installHref\"\n :to=\"installTo\"\n class=\"mint-plugin-switcher__install\"\n role=\"menuitem\"\n @click=\"handleInstall\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n <span>{{ installLabel }}</span>\n </ActionItemInternal>\n </slot>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/app-plugin-switcher.css';\n</style>\n","<script setup lang=\"ts\">\n/** Dropdown menu for switching between installed plugins with color swatches, version badges, and an install link. */\nimport { useDropdownState } from '../composables/useDropdownState'\nimport type { PluginSwitcherPlugin } from '../types/components'\nimport ActionItemInternal from './internal/ActionItemInternal.vue'\n\ninterface Props {\n current: PluginSwitcherPlugin\n plugins?: PluginSwitcherPlugin[]\n installLabel?: string\n installTo?: string\n installHref?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n plugins: () => [] as PluginSwitcherPlugin[],\n installLabel: 'Install plugin…',\n})\n\nconst emit = defineEmits<{\n select: [plugin: PluginSwitcherPlugin]\n 'install-click': []\n}>()\n\nconst { isOpen, rootRef, close, toggle } = useDropdownState()\n\nfunction initialsOf(plugin: PluginSwitcherPlugin) {\n const label = plugin.label || plugin.id\n return label.slice(0, 2).toUpperCase()\n}\n\nfunction handleSelect(plugin: PluginSwitcherPlugin) {\n if (plugin.id === props.current.id) {\n close()\n return\n }\n emit('select', plugin)\n close()\n}\n\nfunction handleInstall() {\n emit('install-click')\n close()\n}\n</script>\n\n<template>\n <div ref=\"rootRef\" class=\"mint-plugin-switcher\">\n <button\n type=\"button\"\n class=\"mint-plugin-switcher__trigger\"\n :class=\"{ 'mint-plugin-switcher__trigger--open': isOpen }\"\n :aria-expanded=\"isOpen\"\n aria-haspopup=\"menu\"\n @click.stop=\"toggle\"\n >\n <span class=\"mint-plugin-switcher__label\">{{ current.label }}</span>\n <span v-if=\"current.version\" class=\"mint-plugin-switcher__version\">v{{ current.version }}</span>\n <span class=\"mint-plugin-switcher__chevron-wrap\" aria-hidden=\"true\">\n <svg\n class=\"mint-plugin-switcher__chevron\"\n :class=\"{ 'mint-plugin-switcher__chevron--open': isOpen }\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </span>\n </button>\n\n <div v-if=\"isOpen\" class=\"mint-plugin-switcher__menu\" role=\"menu\">\n <div class=\"mint-plugin-switcher__menu-title\">Switch plugin</div>\n <ActionItemInternal\n v-for=\"plugin in plugins\"\n :key=\"plugin.id\"\n :href=\"plugin.href\"\n :to=\"plugin.to\"\n :class=\"[\n 'mint-plugin-switcher__item',\n { 'mint-plugin-switcher__item--active': plugin.id === current.id },\n ]\"\n role=\"menuitem\"\n @click=\"handleSelect(plugin)\"\n >\n <span\n class=\"mint-plugin-switcher__item-swatch\"\n :style=\"{ background: plugin.color ?? 'var(--color-primary)' }\"\n >\n {{ initialsOf(plugin) }}\n </span>\n <span class=\"mint-plugin-switcher__item-label\">{{ plugin.label }}</span>\n <span v-if=\"plugin.version\" class=\"mint-plugin-switcher__item-version\">v{{ plugin.version }}</span>\n <svg\n v-if=\"plugin.id === current.id\"\n class=\"mint-plugin-switcher__item-check\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M5 13l4 4L19 7\" />\n </svg>\n </ActionItemInternal>\n\n <template v-if=\"plugins.length && (installHref || installTo || $slots.install)\">\n <div class=\"mint-plugin-switcher__divider\" role=\"separator\" />\n </template>\n <slot name=\"install\">\n <ActionItemInternal\n v-if=\"installHref || installTo\"\n :href=\"installHref\"\n :to=\"installTo\"\n class=\"mint-plugin-switcher__install\"\n role=\"menuitem\"\n @click=\"handleInstall\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n <span>{{ installLabel }}</span>\n </ActionItemInternal>\n </slot>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/app-plugin-switcher.css';\n</style>\n","<script setup lang=\"ts\">\n/** Full application top bar with brand logo, page selector or plugin switcher, centered pill nav, experiment popover, and avatar menu. */\nimport { ref, computed, inject } from 'vue'\nimport type {\n PillNavOption,\n TopBarSettingsConfig,\n TopBarVariant,\n PillNavItem,\n PillNavItemInput,\n PageSelectorItem,\n PageSelectorItemInput,\n PluginSwitcherInfo,\n PluginSwitcherPlugin,\n AccountMenuItem,\n SettingsTab,\n} from '../types/components'\nimport type { PluginNavItem } from '../types/platform'\nimport { normalizeItemInput } from '../utils/items'\nimport ThemeToggle from './ThemeToggle.vue'\nimport SettingsModal from './SettingsModal.vue'\nimport ExperimentPopover from './ExperimentPopover.vue'\nimport ExperimentSelectorModal from './ExperimentSelectorModal.vue'\nimport AppTopBarPageSelectorInternal from './internal/AppTopBarPageSelectorInternal.vue'\nimport AppTopBarPillNavInternal from './internal/AppTopBarPillNavInternal.vue'\nimport AppAvatarMenu from './AppAvatarMenu.vue'\nimport AppPluginSwitcher from './AppPluginSwitcher.vue'\nimport PluginIcon from './PluginIcon.vue'\nimport { usePlatformContext } from '../composables/usePlatformContext'\nimport { APP_EXPERIMENT_KEY } from '../composables/useAppExperiment'\n\ninterface Props {\n /** App or plugin title shown in the left title group when no page selector is present. */\n title?: string\n /** Secondary title copy shown under the title in title-group layouts. */\n subtitle?: string\n /** Show the default MINT logo when the icon/logo slot is not provided. */\n showLogo?: boolean\n /** Top bar visual treatment. */\n variant?: TopBarVariant\n /** Home link used by the brand icon. */\n homePath?: string\n\n /** Preferred route-level page switch entries for plugin and platform pages. Integrated plugins read platform plugin.nav_items metadata automatically when pageSelector is omitted. */\n pageSelector?: PageSelectorItemInput[]\n /** Active id for the preferred page selector. */\n currentPageSelectorId?: string\n /** Optional plugin switcher shown in the left navigation position instead of pageSelector. */\n pluginSwitcher?: PluginSwitcherInfo\n\n /** Preferred centered navigation for local modes inside the current route. */\n pillNav?: PillNavItemInput[]\n /** Active id for the preferred centered pill navigation. */\n currentPillId?: string\n\n /** Account dropdown entries. Takes precedence over the classic profile button. */\n accountMenu?: AccountMenuItem[]\n /** Show the notifications icon button. */\n showNotifications?: boolean\n /** Draw a notification dot on the notifications icon. */\n hasNotificationDot?: boolean\n\n /** Show the theme toggle button. */\n showThemeToggle?: boolean\n /** Show the settings button and modal. */\n showSettings?: boolean\n /** Built-in SettingsModal configuration. */\n settingsConfig?: TopBarSettingsConfig\n /** Show the standalone badge when the plugin is not integrated into the platform. */\n showStandaloneLabel?: boolean\n /** Custom standalone badge label. */\n standaloneLabel?: string\n /** Show the classic admin shortcut. */\n showAdmin?: boolean\n /** Route used by the classic admin shortcut. */\n adminPath?: string\n /** Show the classic profile button when accountMenu is not provided. */\n showProfile?: boolean\n /** Classic profile display name. */\n userName?: string\n /** Explicit classic profile initial. */\n userInitial?: string\n /** Classic profile email, used by avatar/account layouts. */\n userEmail?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showLogo: true,\n variant: 'card',\n homePath: '/',\n showThemeToggle: false,\n showSettings: false,\n showStandaloneLabel: true,\n standaloneLabel: 'Standalone',\n showAdmin: false,\n adminPath: '/admin',\n showProfile: false,\n showNotifications: false,\n hasNotificationDot: false,\n})\n\nconst emit = defineEmits<{\n 'profile-click': []\n 'admin-click': []\n 'page-selector-select': [page: PageSelectorItem]\n 'pill-select': [item: PillNavItem]\n 'pill-option-select': [option: PillNavOption, item: PillNavItem]\n 'plugin-switcher-select': [plugin: PluginSwitcherPlugin]\n 'plugin-switcher-install': []\n 'account-menu-select': [item: AccountMenuItem]\n 'sign-out': []\n 'notifications-click': []\n 'settings-values-change': [data: Record<string, unknown>]\n}>()\n\nconst settingsOpen = ref(false)\nconst { isIntegrated, plugin } = usePlatformContext()\nconst isStandalone = computed(() => !isIntegrated.value)\nconst appExperiment = inject(APP_EXPERIMENT_KEY, null)\n\nconst pluginIcon = computed(() => {\n const currentPlugin = plugin.value\n return currentPlugin?.icon\n ? { icon: currentPlugin.icon, color: currentPlugin.color }\n : null\n})\n\nconst profileInitial = computed(() => {\n if (props.userInitial) return props.userInitial\n if (props.userName) return props.userName.charAt(0).toUpperCase()\n return 'U'\n})\n\nconst hasPlatformPageSelector = computed(() =>\n props.pageSelector === undefined && (plugin.value?.nav_items?.length ?? 0) > 1,\n)\nconst hasPageSelector = computed(() => (props.pageSelector?.length ?? 0) > 1 || hasPlatformPageSelector.value)\nconst hasPluginSwitcher = computed(() => !!props.pluginSwitcher)\nconst hasPillNav = computed(() => !!props.pillNav?.length)\nconst hasAccountMenu = computed(() => !!props.accountMenu?.length)\nconst hasTitleGroup = computed(\n () =>\n !hasPageSelector.value &&\n !hasPluginSwitcher.value &&\n !!props.title &&\n !!props.subtitle\n)\nconst hasTitleOnly = computed(\n () =>\n !hasPageSelector.value &&\n !hasPluginSwitcher.value &&\n !hasTitleGroup.value &&\n !!props.title,\n)\nconst platformPageSelector = computed<PageSelectorItem[]>(() =>\n hasPlatformPageSelector.value\n ? plugin.value?.nav_items?.map(pluginNavItemToPageSelectorItem) ?? []\n : [],\n)\nconst normalizedPageSelector = computed<PageSelectorItem[]>(() =>\n props.pageSelector !== undefined\n ? props.pageSelector.map(normalizeItemInput)\n : platformPageSelector.value,\n)\nconst normalizedPillNav = computed<PillNavItemInput[]>(() => props.pillNav ?? [])\nconst normalizedSettingsTabs = computed<SettingsTab[]>(() =>\n props.settingsConfig?.tabs?.map(normalizeItemInput) ?? [],\n)\nconst effectiveCurrentPageSelectorId = computed(() =>\n props.currentPageSelectorId\n ?? (hasPlatformPageSelector.value ? currentItemIdFromLocation(normalizedPageSelector.value) : undefined)\n ?? (hasPlatformPageSelector.value ? normalizedPageSelector.value[0]?.id : undefined),\n)\n\nfunction normalizeNavPath(path?: string): string {\n const raw = path?.trim() || '/'\n const prefixed = raw.startsWith('/') ? raw : `/${raw}`\n return prefixed.replace(/\\/+$/, '') || '/'\n}\n\nfunction pageIdFromPath(path: string, fallback: string): string {\n const normalized = normalizeNavPath(path)\n if (normalized === '/') return 'dashboard'\n return normalized.replace(/^\\/+/, '').replace(/\\/+/g, '-') || fallback\n}\n\nfunction pluginNavItemToPageSelectorItem(item: PluginNavItem, index: number): PageSelectorItem {\n const path = normalizeNavPath(item.path)\n return {\n id: item.id || pageIdFromPath(path, `page-${index + 1}`),\n label: item.label,\n to: path,\n icon: item.icon || plugin.value?.icon,\n hint: item.description || plugin.value?.name,\n }\n}\n\nfunction currentPagePath(): string | undefined {\n if (typeof window === 'undefined') return undefined\n const pathname = normalizeNavPath(window.location.pathname)\n const routePrefix = normalizeNavPath(plugin.value?.route_prefix)\n\n if (pathname === routePrefix) return '/'\n if (routePrefix !== '/' && pathname.startsWith(`${routePrefix}/`)) {\n return normalizeNavPath(pathname.slice(routePrefix.length))\n }\n return pathname\n}\n\nfunction currentItemIdFromLocation(pages: Array<Pick<PageSelectorItem, 'id' | 'to' | 'href'>>): string | undefined {\n const path = currentPagePath()\n if (!path) return undefined\n return pages.find((page) => normalizeNavPath(page.to || page.href) === path)?.id\n}\n</script>\n\n<template>\n <header\n :class=\"[\n 'mint-topbar',\n `mint-topbar--${props.variant}`,\n ]\"\n >\n <div class=\"mint-topbar__container\">\n <!-- Left: brand -->\n <div class=\"mint-topbar__brand\">\n <a\n v-if=\"homePath && (homePath.startsWith('http') || homePath.startsWith('/'))\"\n :href=\"homePath\"\n class=\"mint-topbar-home-link\"\n >\n <slot name=\"icon\">\n <PluginIcon\n v-if=\"pluginIcon\"\n class=\"mint-topbar__plugin-icon\"\n :icon=\"pluginIcon.icon\"\n :tone=\"pluginIcon.color\"\n size=\"md\"\n variant=\"solid\"\n />\n <slot v-else name=\"logo\">\n <div v-if=\"showLogo\" class=\"mint-topbar__logo\">\n <div class=\"mint-topbar__logo-icon\">\n <span class=\"mint-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </a>\n <router-link\n v-else-if=\"homePath\"\n :to=\"homePath\"\n class=\"mint-topbar-home-link\"\n >\n <slot name=\"icon\">\n <PluginIcon\n v-if=\"pluginIcon\"\n class=\"mint-topbar__plugin-icon\"\n :icon=\"pluginIcon.icon\"\n :tone=\"pluginIcon.color\"\n size=\"md\"\n variant=\"solid\"\n />\n <slot v-else name=\"logo\">\n <div v-if=\"showLogo\" class=\"mint-topbar__logo\">\n <div class=\"mint-topbar__logo-icon\">\n <span class=\"mint-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </router-link>\n <template v-else>\n <slot name=\"icon\">\n <PluginIcon\n v-if=\"pluginIcon\"\n class=\"mint-topbar__plugin-icon\"\n :icon=\"pluginIcon.icon\"\n :tone=\"pluginIcon.color\"\n size=\"md\"\n variant=\"solid\"\n />\n <slot v-else name=\"logo\">\n <div v-if=\"showLogo\" class=\"mint-topbar__logo\">\n <div class=\"mint-topbar__logo-icon\">\n <span class=\"mint-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </template>\n </div>\n\n <!-- Left: page selector or plugin switcher (new) -->\n <AppPluginSwitcher\n v-if=\"hasPluginSwitcher && pluginSwitcher\"\n :current=\"pluginSwitcher.current\"\n :plugins=\"pluginSwitcher.plugins\"\n :install-to=\"pluginSwitcher.installTo\"\n :install-href=\"pluginSwitcher.installHref\"\n @select=\"emit('plugin-switcher-select', $event)\"\n @install-click=\"emit('plugin-switcher-install')\"\n />\n <AppTopBarPageSelectorInternal\n v-else-if=\"hasPageSelector\"\n :pages=\"normalizedPageSelector\"\n :current-page-id=\"effectiveCurrentPageSelectorId\"\n @select=\"emit('page-selector-select', $event)\"\n >\n <template v-if=\"$slots['page-selector-icon']\" #icon=\"slotProps\">\n <slot name=\"page-selector-icon\" v-bind=\"slotProps\" />\n </template>\n <template v-if=\"$slots['page-selector-item-icon']\" #item-icon=\"slotProps\">\n <slot name=\"page-selector-item-icon\" v-bind=\"slotProps\" />\n </template>\n </AppTopBarPageSelectorInternal>\n\n <!-- Left: title -->\n <div v-if=\"hasTitleGroup\" class=\"mint-topbar-title-group\">\n <span class=\"mint-topbar-title\">{{ title }}</span>\n <span class=\"mint-topbar-subtitle\">{{ subtitle }}</span>\n </div>\n\n <span v-else-if=\"hasTitleOnly\" class=\"mint-topbar__title-only\">{{ title }}</span>\n\n <!-- Nav slot (inline, after brand/selector) -->\n <slot name=\"nav\" />\n\n <!-- Center: pill nav (new) -->\n <div v-if=\"hasPillNav || $slots.center\" class=\"mint-topbar__center\">\n <slot name=\"center\">\n <AppTopBarPillNavInternal\n v-if=\"hasPillNav && pillNav\"\n :items=\"normalizedPillNav\"\n :current-item-id=\"currentPillId\"\n @select=\"emit('pill-select', $event)\"\n @option-select=\"(option, item) => emit('pill-option-select', option, item)\"\n />\n </slot>\n </div>\n\n <!-- Right section -->\n <div class=\"mint-topbar__right\">\n <span v-if=\"showStandaloneLabel && isStandalone && !appExperiment\" class=\"mint-topbar__standalone-badge\">\n {{ standaloneLabel }}\n </span>\n\n <ExperimentPopover\n v-if=\"appExperiment && !isStandalone\"\n v-bind=\"appExperiment.popover.value\"\n @select=\"appExperiment.openModal()\"\n @save=\"appExperiment.handleSave()\"\n @detach=\"appExperiment.handleDetach()\"\n />\n\n <span v-if=\"appExperiment && isStandalone\" class=\"mint-topbar__standalone-badge\">\n {{ standaloneLabel }}\n </span>\n\n <slot name=\"actions\" />\n\n <!-- Notifications (new) -->\n <button\n v-if=\"showNotifications\"\n type=\"button\"\n class=\"mint-topbar__icon-btn\"\n aria-label=\"Notifications\"\n @click=\"emit('notifications-click')\"\n >\n <svg class=\"mint-topbar__icon-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9\" />\n <path d=\"M10.3 21a1.94 1.94 0 0 0 3.4 0\" />\n </svg>\n <span v-if=\"hasNotificationDot\" class=\"mint-topbar__icon-btn-dot\" aria-hidden=\"true\" />\n </button>\n\n <!-- Classic: theme toggle -->\n <ThemeToggle v-if=\"showThemeToggle\" size=\"sm\" />\n\n <!-- Classic: settings gear -->\n <button\n v-if=\"showSettings\"\n type=\"button\"\n class=\"mint-topbar__settings-btn\"\n aria-label=\"Open settings\"\n @click=\"settingsOpen = true\"\n >\n <svg class=\"mint-topbar__settings-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915\" /><circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n </button>\n\n <!-- Classic: admin link -->\n <router-link\n v-if=\"showAdmin\"\n :to=\"adminPath\"\n class=\"mint-topbar__admin-btn\"\n aria-label=\"Admin Dashboard\"\n @click=\"emit('admin-click')\"\n >\n <svg class=\"mint-topbar__admin-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z\" /><path d=\"m9 12 2 2 4-4\" />\n </svg>\n </router-link>\n\n <!-- New: avatar menu (takes precedence over classic profile button) -->\n <AppAvatarMenu\n v-if=\"hasAccountMenu || $slots['account-menu-items']\"\n :user-name=\"userName\"\n :user-initial=\"userInitial\"\n :user-email=\"userEmail\"\n :items=\"accountMenu\"\n @select=\"emit('account-menu-select', $event)\"\n @sign-out=\"emit('sign-out')\"\n >\n <template v-if=\"$slots['account-menu-items']\" #items=\"slotProps\">\n <slot name=\"account-menu-items\" v-bind=\"slotProps\" />\n </template>\n <template v-if=\"$slots['account-menu-item-icon']\" #item-icon=\"slotProps\">\n <slot name=\"account-menu-item-icon\" v-bind=\"slotProps\" />\n </template>\n </AppAvatarMenu>\n\n <!-- Classic: profile button (only when avatar menu not provided) -->\n <button\n v-else-if=\"showProfile\"\n type=\"button\"\n class=\"mint-topbar__profile-btn\"\n aria-label=\"Edit profile\"\n @click=\"emit('profile-click')\"\n >\n <div class=\"mint-topbar__profile-avatar\">\n {{ profileInitial }}\n </div>\n <span v-if=\"userName\" class=\"mint-topbar__profile-name\">{{ userName }}</span>\n </button>\n </div>\n </div>\n </header>\n\n <SettingsModal\n v-if=\"showSettings\"\n v-model=\"settingsOpen\"\n :title=\"settingsConfig?.title\"\n :tabs=\"normalizedSettingsTabs\"\n :show-appearance=\"settingsConfig?.showAppearance ?? true\"\n :size=\"settingsConfig?.size\"\n :layout=\"settingsConfig?.layout\"\n :schema=\"settingsConfig?.schema\"\n :model=\"settingsConfig?.model\"\n :controls=\"settingsConfig?.controls\"\n :control-options=\"settingsConfig?.controlOptions\"\n :values=\"settingsConfig?.values\"\n :enhancements=\"settingsConfig?.enhancements\"\n @update:values=\"emit('settings-values-change', $event)\"\n >\n <template v-for=\"tab in normalizedSettingsTabs\" :key=\"tab.id\" #[`tab-${tab.id}`]>\n <slot :name=\"`settings-tab-${tab.id}`\" />\n </template>\n <template #appearance>\n <slot name=\"settings-appearance\" />\n </template>\n </SettingsModal>\n\n <ExperimentSelectorModal\n v-if=\"appExperiment && !isStandalone\"\n v-bind=\"appExperiment.selectorModal.value\"\n @update:model-value=\"$event ? appExperiment.openModal() : appExperiment.closeModal()\"\n @select=\"appExperiment.handleSelect($event)\"\n @deselect=\"appExperiment.handleDetach()\"\n />\n</template>\n\n<style>\n@import '../styles/components/app-top-bar.css';\n</style>\n","<script setup lang=\"ts\">\n/** Full application top bar with brand logo, page selector or plugin switcher, centered pill nav, experiment popover, and avatar menu. */\nimport { ref, computed, inject } from 'vue'\nimport type {\n PillNavOption,\n TopBarSettingsConfig,\n TopBarVariant,\n PillNavItem,\n PillNavItemInput,\n PageSelectorItem,\n PageSelectorItemInput,\n PluginSwitcherInfo,\n PluginSwitcherPlugin,\n AccountMenuItem,\n SettingsTab,\n} from '../types/components'\nimport type { PluginNavItem } from '../types/platform'\nimport { normalizeItemInput } from '../utils/items'\nimport ThemeToggle from './ThemeToggle.vue'\nimport SettingsModal from './SettingsModal.vue'\nimport ExperimentPopover from './ExperimentPopover.vue'\nimport ExperimentSelectorModal from './ExperimentSelectorModal.vue'\nimport AppTopBarPageSelectorInternal from './internal/AppTopBarPageSelectorInternal.vue'\nimport AppTopBarPillNavInternal from './internal/AppTopBarPillNavInternal.vue'\nimport AppAvatarMenu from './AppAvatarMenu.vue'\nimport AppPluginSwitcher from './AppPluginSwitcher.vue'\nimport PluginIcon from './PluginIcon.vue'\nimport { usePlatformContext } from '../composables/usePlatformContext'\nimport { APP_EXPERIMENT_KEY } from '../composables/useAppExperiment'\n\ninterface Props {\n /** App or plugin title shown in the left title group when no page selector is present. */\n title?: string\n /** Secondary title copy shown under the title in title-group layouts. */\n subtitle?: string\n /** Show the default MINT logo when the icon/logo slot is not provided. */\n showLogo?: boolean\n /** Top bar visual treatment. */\n variant?: TopBarVariant\n /** Home link used by the brand icon. */\n homePath?: string\n\n /** Preferred route-level page switch entries for plugin and platform pages. Integrated plugins read platform plugin.nav_items metadata automatically when pageSelector is omitted. */\n pageSelector?: PageSelectorItemInput[]\n /** Active id for the preferred page selector. */\n currentPageSelectorId?: string\n /** Optional plugin switcher shown in the left navigation position instead of pageSelector. */\n pluginSwitcher?: PluginSwitcherInfo\n\n /** Preferred centered navigation for local modes inside the current route. */\n pillNav?: PillNavItemInput[]\n /** Active id for the preferred centered pill navigation. */\n currentPillId?: string\n\n /** Account dropdown entries. Takes precedence over the classic profile button. */\n accountMenu?: AccountMenuItem[]\n /** Show the notifications icon button. */\n showNotifications?: boolean\n /** Draw a notification dot on the notifications icon. */\n hasNotificationDot?: boolean\n\n /** Show the theme toggle button. */\n showThemeToggle?: boolean\n /** Show the settings button and modal. */\n showSettings?: boolean\n /** Built-in SettingsModal configuration. */\n settingsConfig?: TopBarSettingsConfig\n /** Show the standalone badge when the plugin is not integrated into the platform. */\n showStandaloneLabel?: boolean\n /** Custom standalone badge label. */\n standaloneLabel?: string\n /** Show the classic admin shortcut. */\n showAdmin?: boolean\n /** Route used by the classic admin shortcut. */\n adminPath?: string\n /** Show the classic profile button when accountMenu is not provided. */\n showProfile?: boolean\n /** Classic profile display name. */\n userName?: string\n /** Explicit classic profile initial. */\n userInitial?: string\n /** Classic profile email, used by avatar/account layouts. */\n userEmail?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showLogo: true,\n variant: 'card',\n homePath: '/',\n showThemeToggle: false,\n showSettings: false,\n showStandaloneLabel: true,\n standaloneLabel: 'Standalone',\n showAdmin: false,\n adminPath: '/admin',\n showProfile: false,\n showNotifications: false,\n hasNotificationDot: false,\n})\n\nconst emit = defineEmits<{\n 'profile-click': []\n 'admin-click': []\n 'page-selector-select': [page: PageSelectorItem]\n 'pill-select': [item: PillNavItem]\n 'pill-option-select': [option: PillNavOption, item: PillNavItem]\n 'plugin-switcher-select': [plugin: PluginSwitcherPlugin]\n 'plugin-switcher-install': []\n 'account-menu-select': [item: AccountMenuItem]\n 'sign-out': []\n 'notifications-click': []\n 'settings-values-change': [data: Record<string, unknown>]\n}>()\n\nconst settingsOpen = ref(false)\nconst { isIntegrated, plugin } = usePlatformContext()\nconst isStandalone = computed(() => !isIntegrated.value)\nconst appExperiment = inject(APP_EXPERIMENT_KEY, null)\n\nconst pluginIcon = computed(() => {\n const currentPlugin = plugin.value\n return currentPlugin?.icon\n ? { icon: currentPlugin.icon, color: currentPlugin.color }\n : null\n})\n\nconst profileInitial = computed(() => {\n if (props.userInitial) return props.userInitial\n if (props.userName) return props.userName.charAt(0).toUpperCase()\n return 'U'\n})\n\nconst hasPlatformPageSelector = computed(() =>\n props.pageSelector === undefined && (plugin.value?.nav_items?.length ?? 0) > 1,\n)\nconst hasPageSelector = computed(() => (props.pageSelector?.length ?? 0) > 1 || hasPlatformPageSelector.value)\nconst hasPluginSwitcher = computed(() => !!props.pluginSwitcher)\nconst hasPillNav = computed(() => !!props.pillNav?.length)\nconst hasAccountMenu = computed(() => !!props.accountMenu?.length)\nconst hasTitleGroup = computed(\n () =>\n !hasPageSelector.value &&\n !hasPluginSwitcher.value &&\n !!props.title &&\n !!props.subtitle\n)\nconst hasTitleOnly = computed(\n () =>\n !hasPageSelector.value &&\n !hasPluginSwitcher.value &&\n !hasTitleGroup.value &&\n !!props.title,\n)\nconst platformPageSelector = computed<PageSelectorItem[]>(() =>\n hasPlatformPageSelector.value\n ? plugin.value?.nav_items?.map(pluginNavItemToPageSelectorItem) ?? []\n : [],\n)\nconst normalizedPageSelector = computed<PageSelectorItem[]>(() =>\n props.pageSelector !== undefined\n ? props.pageSelector.map(normalizeItemInput)\n : platformPageSelector.value,\n)\nconst normalizedPillNav = computed<PillNavItemInput[]>(() => props.pillNav ?? [])\nconst normalizedSettingsTabs = computed<SettingsTab[]>(() =>\n props.settingsConfig?.tabs?.map(normalizeItemInput) ?? [],\n)\nconst effectiveCurrentPageSelectorId = computed(() =>\n props.currentPageSelectorId\n ?? (hasPlatformPageSelector.value ? currentItemIdFromLocation(normalizedPageSelector.value) : undefined)\n ?? (hasPlatformPageSelector.value ? normalizedPageSelector.value[0]?.id : undefined),\n)\n\nfunction normalizeNavPath(path?: string): string {\n const raw = path?.trim() || '/'\n const prefixed = raw.startsWith('/') ? raw : `/${raw}`\n return prefixed.replace(/\\/+$/, '') || '/'\n}\n\nfunction pageIdFromPath(path: string, fallback: string): string {\n const normalized = normalizeNavPath(path)\n if (normalized === '/') return 'dashboard'\n return normalized.replace(/^\\/+/, '').replace(/\\/+/g, '-') || fallback\n}\n\nfunction pluginNavItemToPageSelectorItem(item: PluginNavItem, index: number): PageSelectorItem {\n const path = normalizeNavPath(item.path)\n return {\n id: item.id || pageIdFromPath(path, `page-${index + 1}`),\n label: item.label,\n to: path,\n icon: item.icon || plugin.value?.icon,\n hint: item.description || plugin.value?.name,\n }\n}\n\nfunction currentPagePath(): string | undefined {\n if (typeof window === 'undefined') return undefined\n const pathname = normalizeNavPath(window.location.pathname)\n const routePrefix = normalizeNavPath(plugin.value?.route_prefix)\n\n if (pathname === routePrefix) return '/'\n if (routePrefix !== '/' && pathname.startsWith(`${routePrefix}/`)) {\n return normalizeNavPath(pathname.slice(routePrefix.length))\n }\n return pathname\n}\n\nfunction currentItemIdFromLocation(pages: Array<Pick<PageSelectorItem, 'id' | 'to' | 'href'>>): string | undefined {\n const path = currentPagePath()\n if (!path) return undefined\n return pages.find((page) => normalizeNavPath(page.to || page.href) === path)?.id\n}\n</script>\n\n<template>\n <header\n :class=\"[\n 'mint-topbar',\n `mint-topbar--${props.variant}`,\n ]\"\n >\n <div class=\"mint-topbar__container\">\n <!-- Left: brand -->\n <div class=\"mint-topbar__brand\">\n <a\n v-if=\"homePath && (homePath.startsWith('http') || homePath.startsWith('/'))\"\n :href=\"homePath\"\n class=\"mint-topbar-home-link\"\n >\n <slot name=\"icon\">\n <PluginIcon\n v-if=\"pluginIcon\"\n class=\"mint-topbar__plugin-icon\"\n :icon=\"pluginIcon.icon\"\n :tone=\"pluginIcon.color\"\n size=\"md\"\n variant=\"solid\"\n />\n <slot v-else name=\"logo\">\n <div v-if=\"showLogo\" class=\"mint-topbar__logo\">\n <div class=\"mint-topbar__logo-icon\">\n <span class=\"mint-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </a>\n <router-link\n v-else-if=\"homePath\"\n :to=\"homePath\"\n class=\"mint-topbar-home-link\"\n >\n <slot name=\"icon\">\n <PluginIcon\n v-if=\"pluginIcon\"\n class=\"mint-topbar__plugin-icon\"\n :icon=\"pluginIcon.icon\"\n :tone=\"pluginIcon.color\"\n size=\"md\"\n variant=\"solid\"\n />\n <slot v-else name=\"logo\">\n <div v-if=\"showLogo\" class=\"mint-topbar__logo\">\n <div class=\"mint-topbar__logo-icon\">\n <span class=\"mint-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </router-link>\n <template v-else>\n <slot name=\"icon\">\n <PluginIcon\n v-if=\"pluginIcon\"\n class=\"mint-topbar__plugin-icon\"\n :icon=\"pluginIcon.icon\"\n :tone=\"pluginIcon.color\"\n size=\"md\"\n variant=\"solid\"\n />\n <slot v-else name=\"logo\">\n <div v-if=\"showLogo\" class=\"mint-topbar__logo\">\n <div class=\"mint-topbar__logo-icon\">\n <span class=\"mint-topbar__logo-text\">M</span>\n </div>\n </div>\n </slot>\n </slot>\n </template>\n </div>\n\n <!-- Left: page selector or plugin switcher (new) -->\n <AppPluginSwitcher\n v-if=\"hasPluginSwitcher && pluginSwitcher\"\n :current=\"pluginSwitcher.current\"\n :plugins=\"pluginSwitcher.plugins\"\n :install-to=\"pluginSwitcher.installTo\"\n :install-href=\"pluginSwitcher.installHref\"\n @select=\"emit('plugin-switcher-select', $event)\"\n @install-click=\"emit('plugin-switcher-install')\"\n />\n <AppTopBarPageSelectorInternal\n v-else-if=\"hasPageSelector\"\n :pages=\"normalizedPageSelector\"\n :current-page-id=\"effectiveCurrentPageSelectorId\"\n @select=\"emit('page-selector-select', $event)\"\n >\n <template v-if=\"$slots['page-selector-icon']\" #icon=\"slotProps\">\n <slot name=\"page-selector-icon\" v-bind=\"slotProps\" />\n </template>\n <template v-if=\"$slots['page-selector-item-icon']\" #item-icon=\"slotProps\">\n <slot name=\"page-selector-item-icon\" v-bind=\"slotProps\" />\n </template>\n </AppTopBarPageSelectorInternal>\n\n <!-- Left: title -->\n <div v-if=\"hasTitleGroup\" class=\"mint-topbar-title-group\">\n <span class=\"mint-topbar-title\">{{ title }}</span>\n <span class=\"mint-topbar-subtitle\">{{ subtitle }}</span>\n </div>\n\n <span v-else-if=\"hasTitleOnly\" class=\"mint-topbar__title-only\">{{ title }}</span>\n\n <!-- Nav slot (inline, after brand/selector) -->\n <slot name=\"nav\" />\n\n <!-- Center: pill nav (new) -->\n <div v-if=\"hasPillNav || $slots.center\" class=\"mint-topbar__center\">\n <slot name=\"center\">\n <AppTopBarPillNavInternal\n v-if=\"hasPillNav && pillNav\"\n :items=\"normalizedPillNav\"\n :current-item-id=\"currentPillId\"\n @select=\"emit('pill-select', $event)\"\n @option-select=\"(option, item) => emit('pill-option-select', option, item)\"\n />\n </slot>\n </div>\n\n <!-- Right section -->\n <div class=\"mint-topbar__right\">\n <span v-if=\"showStandaloneLabel && isStandalone && !appExperiment\" class=\"mint-topbar__standalone-badge\">\n {{ standaloneLabel }}\n </span>\n\n <ExperimentPopover\n v-if=\"appExperiment && !isStandalone\"\n v-bind=\"appExperiment.popover.value\"\n @select=\"appExperiment.openModal()\"\n @save=\"appExperiment.handleSave()\"\n @detach=\"appExperiment.handleDetach()\"\n />\n\n <span v-if=\"appExperiment && isStandalone\" class=\"mint-topbar__standalone-badge\">\n {{ standaloneLabel }}\n </span>\n\n <slot name=\"actions\" />\n\n <!-- Notifications (new) -->\n <button\n v-if=\"showNotifications\"\n type=\"button\"\n class=\"mint-topbar__icon-btn\"\n aria-label=\"Notifications\"\n @click=\"emit('notifications-click')\"\n >\n <svg class=\"mint-topbar__icon-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9\" />\n <path d=\"M10.3 21a1.94 1.94 0 0 0 3.4 0\" />\n </svg>\n <span v-if=\"hasNotificationDot\" class=\"mint-topbar__icon-btn-dot\" aria-hidden=\"true\" />\n </button>\n\n <!-- Classic: theme toggle -->\n <ThemeToggle v-if=\"showThemeToggle\" size=\"sm\" />\n\n <!-- Classic: settings gear -->\n <button\n v-if=\"showSettings\"\n type=\"button\"\n class=\"mint-topbar__settings-btn\"\n aria-label=\"Open settings\"\n @click=\"settingsOpen = true\"\n >\n <svg class=\"mint-topbar__settings-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915\" /><circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n </button>\n\n <!-- Classic: admin link -->\n <router-link\n v-if=\"showAdmin\"\n :to=\"adminPath\"\n class=\"mint-topbar__admin-btn\"\n aria-label=\"Admin Dashboard\"\n @click=\"emit('admin-click')\"\n >\n <svg class=\"mint-topbar__admin-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z\" /><path d=\"m9 12 2 2 4-4\" />\n </svg>\n </router-link>\n\n <!-- New: avatar menu (takes precedence over classic profile button) -->\n <AppAvatarMenu\n v-if=\"hasAccountMenu || $slots['account-menu-items']\"\n :user-name=\"userName\"\n :user-initial=\"userInitial\"\n :user-email=\"userEmail\"\n :items=\"accountMenu\"\n @select=\"emit('account-menu-select', $event)\"\n @sign-out=\"emit('sign-out')\"\n >\n <template v-if=\"$slots['account-menu-items']\" #items=\"slotProps\">\n <slot name=\"account-menu-items\" v-bind=\"slotProps\" />\n </template>\n <template v-if=\"$slots['account-menu-item-icon']\" #item-icon=\"slotProps\">\n <slot name=\"account-menu-item-icon\" v-bind=\"slotProps\" />\n </template>\n </AppAvatarMenu>\n\n <!-- Classic: profile button (only when avatar menu not provided) -->\n <button\n v-else-if=\"showProfile\"\n type=\"button\"\n class=\"mint-topbar__profile-btn\"\n aria-label=\"Edit profile\"\n @click=\"emit('profile-click')\"\n >\n <div class=\"mint-topbar__profile-avatar\">\n {{ profileInitial }}\n </div>\n <span v-if=\"userName\" class=\"mint-topbar__profile-name\">{{ userName }}</span>\n </button>\n </div>\n </div>\n </header>\n\n <SettingsModal\n v-if=\"showSettings\"\n v-model=\"settingsOpen\"\n :title=\"settingsConfig?.title\"\n :tabs=\"normalizedSettingsTabs\"\n :show-appearance=\"settingsConfig?.showAppearance ?? true\"\n :size=\"settingsConfig?.size\"\n :layout=\"settingsConfig?.layout\"\n :schema=\"settingsConfig?.schema\"\n :model=\"settingsConfig?.model\"\n :controls=\"settingsConfig?.controls\"\n :control-options=\"settingsConfig?.controlOptions\"\n :values=\"settingsConfig?.values\"\n :enhancements=\"settingsConfig?.enhancements\"\n @update:values=\"emit('settings-values-change', $event)\"\n >\n <template v-for=\"tab in normalizedSettingsTabs\" :key=\"tab.id\" #[`tab-${tab.id}`]>\n <slot :name=\"`settings-tab-${tab.id}`\" />\n </template>\n <template #appearance>\n <slot name=\"settings-appearance\" />\n </template>\n </SettingsModal>\n\n <ExperimentSelectorModal\n v-if=\"appExperiment && !isStandalone\"\n v-bind=\"appExperiment.selectorModal.value\"\n @update:model-value=\"$event ? appExperiment.openModal() : appExperiment.closeModal()\"\n @select=\"appExperiment.handleSelect($event)\"\n @deselect=\"appExperiment.handleDetach()\"\n />\n</template>\n\n<style>\n@import '../styles/components/app-top-bar.css';\n</style>\n","<script setup lang=\"ts\">\n/** Multi-step wizard with a progress stepper, per-step validity tracking, and Back/Next navigation. */\nimport { ref, computed } from 'vue'\nimport type { WizardStep, WizardStepState } from '../types'\n\nexport interface Props {\n steps: WizardStep[]\n modelValue?: number\n linear?: boolean\n showProgress?: boolean\n showStepNumbers?: boolean\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: 0,\n linear: true,\n showProgress: true,\n showStepNumbers: true,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [index: number]\n 'complete': []\n 'cancel': []\n 'step-change': [change: [from: number, to: number]]\n}>()\n\nconst stepValidity = ref<boolean[]>([])\n\n// Initialize validity for all steps to true\nif (stepValidity.value.length === 0) {\n stepValidity.value = props.steps.map(() => true)\n}\n\nconst stepStates = computed<WizardStepState[]>(() => {\n return props.steps.map((_, index) => {\n if (index === props.modelValue) return 'active'\n if (index < props.modelValue && stepValidity.value[index] !== false) return 'completed'\n if (index > props.modelValue && props.linear) {\n for (let i = props.modelValue; i < index; i++) {\n if (stepValidity.value[i] === false) return 'disabled'\n }\n }\n return 'pending'\n })\n})\n\nfunction setStepValid(index: number, valid: boolean) {\n if (index >= 0 && index < props.steps.length) {\n stepValidity.value[index] = valid\n }\n}\n\nfunction goToStep(index: number) {\n if (index < 0 || index >= props.steps.length) return\n if (stepStates.value[index] === 'disabled') return\n\n if (props.linear && index > props.modelValue) {\n for (let i = props.modelValue; i < index; i++) {\n if (stepValidity.value[i] === false) return\n }\n }\n\n const from = props.modelValue\n emit('step-change', [from, index])\n emit('update:modelValue', index)\n}\n\nfunction goNext() {\n if (stepValidity.value[props.modelValue] === false) return\n if (props.modelValue < props.steps.length - 1) {\n goToStep(props.modelValue + 1)\n }\n}\n\nfunction goBack() {\n if (props.modelValue > 0) {\n goToStep(props.modelValue - 1)\n }\n}\n\nfunction finish() {\n if (stepValidity.value[props.modelValue] === false) return\n if (props.modelValue === props.steps.length - 1) {\n emit('complete')\n }\n}\n\nfunction cancel() {\n emit('cancel')\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n goBack()\n break\n case 'ArrowRight':\n event.preventDefault()\n goNext()\n break\n case 'Escape':\n cancel()\n break\n }\n}\n\ndefineExpose({ setStepValid })\n</script>\n\n<template>\n <div\n :class=\"['mint-wizard', `mint-wizard--${props.size}`]\"\n tabindex=\"0\"\n @keydown=\"handleKeydown\"\n >\n <!-- Progress indicator -->\n <div v-if=\"props.showProgress\" class=\"mint-wizard__progress\">\n <slot name=\"progress\" :steps=\"props.steps\" :current=\"props.modelValue\" :states=\"stepStates\">\n <div class=\"mint-wizard__steps-indicator\">\n <template v-for=\"(step, index) in props.steps\" :key=\"step.id\">\n <!-- Connector line (before each step except first) -->\n <div\n v-if=\"index > 0\"\n :class=\"[\n 'mint-wizard__step-connector',\n stepStates[index - 1] === 'completed' ? 'mint-wizard__step-connector--completed' : '',\n ]\"\n />\n <!-- Step indicator -->\n <div\n :class=\"[\n 'mint-wizard__step-indicator',\n `mint-wizard__step-indicator--${stepStates[index]}`,\n ]\"\n @click=\"goToStep(index)\"\n >\n <div class=\"mint-wizard__step-dot\">\n <template v-if=\"stepStates[index] === 'completed'\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </template>\n <template v-else-if=\"props.showStepNumbers\">\n {{ index + 1 }}\n </template>\n </div>\n <div class=\"mint-wizard__step-label\">{{ step.label }}</div>\n </div>\n </template>\n </div>\n </slot>\n </div>\n\n <!-- Step content -->\n <div class=\"mint-wizard__body\">\n <div\n v-for=\"(step, index) in props.steps\"\n :key=\"step.id\"\n v-show=\"index === props.modelValue\"\n class=\"mint-wizard__step-content\"\n >\n <slot :name=\"`step-${step.id}`\" :step=\"step\" :index=\"index\" />\n </div>\n </div>\n\n <!-- Navigation -->\n <div class=\"mint-wizard__navigation\">\n <slot\n name=\"navigation\"\n :go-back=\"goBack\"\n :go-next=\"goNext\"\n :finish=\"finish\"\n :cancel=\"cancel\"\n :is-first=\"props.modelValue === 0\"\n :is-last=\"props.modelValue === props.steps.length - 1\"\n :can-proceed=\"stepValidity[props.modelValue] !== false\"\n >\n <button\n type=\"button\"\n class=\"mint-wizard__nav-btn mint-wizard__nav-btn--cancel\"\n @click=\"cancel\"\n >\n Cancel\n </button>\n <div style=\"flex: 1\" />\n <button\n type=\"button\"\n class=\"mint-wizard__nav-btn mint-wizard__nav-btn--back\"\n :disabled=\"props.modelValue === 0\"\n @click=\"goBack\"\n >\n Back\n </button>\n <button\n v-if=\"props.modelValue < props.steps.length - 1\"\n type=\"button\"\n class=\"mint-wizard__nav-btn mint-wizard__nav-btn--next\"\n :disabled=\"stepValidity[props.modelValue] === false\"\n @click=\"goNext\"\n >\n Next\n </button>\n <button\n v-else\n type=\"button\"\n class=\"mint-wizard__nav-btn mint-wizard__nav-btn--finish\"\n :disabled=\"stepValidity[props.modelValue] === false\"\n @click=\"finish\"\n >\n Finish\n </button>\n </slot>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/step-wizard.css';\n</style>\n","<script setup lang=\"ts\">\n/** Multi-step wizard with a progress stepper, per-step validity tracking, and Back/Next navigation. */\nimport { ref, computed } from 'vue'\nimport type { WizardStep, WizardStepState } from '../types'\n\nexport interface Props {\n steps: WizardStep[]\n modelValue?: number\n linear?: boolean\n showProgress?: boolean\n showStepNumbers?: boolean\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: 0,\n linear: true,\n showProgress: true,\n showStepNumbers: true,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [index: number]\n 'complete': []\n 'cancel': []\n 'step-change': [change: [from: number, to: number]]\n}>()\n\nconst stepValidity = ref<boolean[]>([])\n\n// Initialize validity for all steps to true\nif (stepValidity.value.length === 0) {\n stepValidity.value = props.steps.map(() => true)\n}\n\nconst stepStates = computed<WizardStepState[]>(() => {\n return props.steps.map((_, index) => {\n if (index === props.modelValue) return 'active'\n if (index < props.modelValue && stepValidity.value[index] !== false) return 'completed'\n if (index > props.modelValue && props.linear) {\n for (let i = props.modelValue; i < index; i++) {\n if (stepValidity.value[i] === false) return 'disabled'\n }\n }\n return 'pending'\n })\n})\n\nfunction setStepValid(index: number, valid: boolean) {\n if (index >= 0 && index < props.steps.length) {\n stepValidity.value[index] = valid\n }\n}\n\nfunction goToStep(index: number) {\n if (index < 0 || index >= props.steps.length) return\n if (stepStates.value[index] === 'disabled') return\n\n if (props.linear && index > props.modelValue) {\n for (let i = props.modelValue; i < index; i++) {\n if (stepValidity.value[i] === false) return\n }\n }\n\n const from = props.modelValue\n emit('step-change', [from, index])\n emit('update:modelValue', index)\n}\n\nfunction goNext() {\n if (stepValidity.value[props.modelValue] === false) return\n if (props.modelValue < props.steps.length - 1) {\n goToStep(props.modelValue + 1)\n }\n}\n\nfunction goBack() {\n if (props.modelValue > 0) {\n goToStep(props.modelValue - 1)\n }\n}\n\nfunction finish() {\n if (stepValidity.value[props.modelValue] === false) return\n if (props.modelValue === props.steps.length - 1) {\n emit('complete')\n }\n}\n\nfunction cancel() {\n emit('cancel')\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n goBack()\n break\n case 'ArrowRight':\n event.preventDefault()\n goNext()\n break\n case 'Escape':\n cancel()\n break\n }\n}\n\ndefineExpose({ setStepValid })\n</script>\n\n<template>\n <div\n :class=\"['mint-wizard', `mint-wizard--${props.size}`]\"\n tabindex=\"0\"\n @keydown=\"handleKeydown\"\n >\n <!-- Progress indicator -->\n <div v-if=\"props.showProgress\" class=\"mint-wizard__progress\">\n <slot name=\"progress\" :steps=\"props.steps\" :current=\"props.modelValue\" :states=\"stepStates\">\n <div class=\"mint-wizard__steps-indicator\">\n <template v-for=\"(step, index) in props.steps\" :key=\"step.id\">\n <!-- Connector line (before each step except first) -->\n <div\n v-if=\"index > 0\"\n :class=\"[\n 'mint-wizard__step-connector',\n stepStates[index - 1] === 'completed' ? 'mint-wizard__step-connector--completed' : '',\n ]\"\n />\n <!-- Step indicator -->\n <div\n :class=\"[\n 'mint-wizard__step-indicator',\n `mint-wizard__step-indicator--${stepStates[index]}`,\n ]\"\n @click=\"goToStep(index)\"\n >\n <div class=\"mint-wizard__step-dot\">\n <template v-if=\"stepStates[index] === 'completed'\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </template>\n <template v-else-if=\"props.showStepNumbers\">\n {{ index + 1 }}\n </template>\n </div>\n <div class=\"mint-wizard__step-label\">{{ step.label }}</div>\n </div>\n </template>\n </div>\n </slot>\n </div>\n\n <!-- Step content -->\n <div class=\"mint-wizard__body\">\n <div\n v-for=\"(step, index) in props.steps\"\n :key=\"step.id\"\n v-show=\"index === props.modelValue\"\n class=\"mint-wizard__step-content\"\n >\n <slot :name=\"`step-${step.id}`\" :step=\"step\" :index=\"index\" />\n </div>\n </div>\n\n <!-- Navigation -->\n <div class=\"mint-wizard__navigation\">\n <slot\n name=\"navigation\"\n :go-back=\"goBack\"\n :go-next=\"goNext\"\n :finish=\"finish\"\n :cancel=\"cancel\"\n :is-first=\"props.modelValue === 0\"\n :is-last=\"props.modelValue === props.steps.length - 1\"\n :can-proceed=\"stepValidity[props.modelValue] !== false\"\n >\n <button\n type=\"button\"\n class=\"mint-wizard__nav-btn mint-wizard__nav-btn--cancel\"\n @click=\"cancel\"\n >\n Cancel\n </button>\n <div style=\"flex: 1\" />\n <button\n type=\"button\"\n class=\"mint-wizard__nav-btn mint-wizard__nav-btn--back\"\n :disabled=\"props.modelValue === 0\"\n @click=\"goBack\"\n >\n Back\n </button>\n <button\n v-if=\"props.modelValue < props.steps.length - 1\"\n type=\"button\"\n class=\"mint-wizard__nav-btn mint-wizard__nav-btn--next\"\n :disabled=\"stepValidity[props.modelValue] === false\"\n @click=\"goNext\"\n >\n Next\n </button>\n <button\n v-else\n type=\"button\"\n class=\"mint-wizard__nav-btn mint-wizard__nav-btn--finish\"\n :disabled=\"stepValidity[props.modelValue] === false\"\n @click=\"finish\"\n >\n Finish\n </button>\n </slot>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/step-wizard.css';\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { FormSectionSchema, UseFormBuilderReturn } from '../../types/form-builder'\nimport CollapsibleCard from '../CollapsibleCard.vue'\nimport FormFieldRendererInternal from './FormFieldRendererInternal.vue'\n\ninterface Props {\n section: FormSectionSchema\n builder: UseFormBuilderReturn<Record<string, unknown>>\n}\n\nconst props = defineProps<Props>()\n\nconst visibleFields = computed(() =>\n props.section.fields.filter((f) => props.builder.isFieldVisible(f.name)),\n)\n\nconst gridStyle = computed(() => ({\n gridTemplateColumns: `repeat(${props.section.columns ?? 1}, 1fr)`,\n}))\n</script>\n\n<template>\n <div v-if=\"visibleFields.length > 0\" class=\"mint-form-section\">\n <slot :name=\"`section:${section.id}`\" :section=\"section\" :form=\"builder.form\">\n <CollapsibleCard\n v-if=\"section.collapsible\"\n :title=\"section.title\"\n :subtitle=\"section.description\"\n :default-open=\"section.defaultOpen ?? true\"\n >\n <div class=\"mint-form-section__grid\" :style=\"gridStyle\">\n <div\n v-for=\"field in visibleFields\"\n :key=\"field.name\"\n :style=\"field.colSpan ? { gridColumn: `span ${field.colSpan}` } : undefined\"\n >\n <slot :name=\"`field:${field.name}`\" :field=\"field\" :form=\"builder.form\" :field-props=\"builder.form.getFieldProps(field.name)\">\n <FormFieldRendererInternal\n :field=\"field\"\n :resolved-props=\"builder.getResolvedFieldProps(field)\"\n :form=\"builder.form\"\n />\n </slot>\n </div>\n </div>\n </CollapsibleCard>\n\n <div v-else class=\"mint-form-section--static\">\n <div v-if=\"section.title || section.description\" class=\"mint-form-section__header\">\n <h3 v-if=\"section.title\" class=\"mint-form-section__title\">{{ section.title }}</h3>\n <p v-if=\"section.description\" class=\"mint-form-section__description\">{{ section.description }}</p>\n </div>\n <div class=\"mint-form-section__grid\" :style=\"gridStyle\">\n <div\n v-for=\"field in visibleFields\"\n :key=\"field.name\"\n :style=\"field.colSpan ? { gridColumn: `span ${field.colSpan}` } : undefined\"\n >\n <slot :name=\"`field:${field.name}`\" :field=\"field\" :form=\"builder.form\" :field-props=\"builder.form.getFieldProps(field.name)\">\n <FormFieldRendererInternal\n :field=\"field\"\n :resolved-props=\"builder.getResolvedFieldProps(field)\"\n :form=\"builder.form\"\n />\n </slot>\n </div>\n </div>\n </div>\n </slot>\n\n <slot :name=\"`section:${section.id}:after`\" :form=\"builder.form\" />\n </div>\n</template>\n\n<style>\n@import '../../styles/components/form-builder.css';\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { FormSectionSchema, UseFormBuilderReturn } from '../../types/form-builder'\nimport CollapsibleCard from '../CollapsibleCard.vue'\nimport FormFieldRendererInternal from './FormFieldRendererInternal.vue'\n\ninterface Props {\n section: FormSectionSchema\n builder: UseFormBuilderReturn<Record<string, unknown>>\n}\n\nconst props = defineProps<Props>()\n\nconst visibleFields = computed(() =>\n props.section.fields.filter((f) => props.builder.isFieldVisible(f.name)),\n)\n\nconst gridStyle = computed(() => ({\n gridTemplateColumns: `repeat(${props.section.columns ?? 1}, 1fr)`,\n}))\n</script>\n\n<template>\n <div v-if=\"visibleFields.length > 0\" class=\"mint-form-section\">\n <slot :name=\"`section:${section.id}`\" :section=\"section\" :form=\"builder.form\">\n <CollapsibleCard\n v-if=\"section.collapsible\"\n :title=\"section.title\"\n :subtitle=\"section.description\"\n :default-open=\"section.defaultOpen ?? true\"\n >\n <div class=\"mint-form-section__grid\" :style=\"gridStyle\">\n <div\n v-for=\"field in visibleFields\"\n :key=\"field.name\"\n :style=\"field.colSpan ? { gridColumn: `span ${field.colSpan}` } : undefined\"\n >\n <slot :name=\"`field:${field.name}`\" :field=\"field\" :form=\"builder.form\" :field-props=\"builder.form.getFieldProps(field.name)\">\n <FormFieldRendererInternal\n :field=\"field\"\n :resolved-props=\"builder.getResolvedFieldProps(field)\"\n :form=\"builder.form\"\n />\n </slot>\n </div>\n </div>\n </CollapsibleCard>\n\n <div v-else class=\"mint-form-section--static\">\n <div v-if=\"section.title || section.description\" class=\"mint-form-section__header\">\n <h3 v-if=\"section.title\" class=\"mint-form-section__title\">{{ section.title }}</h3>\n <p v-if=\"section.description\" class=\"mint-form-section__description\">{{ section.description }}</p>\n </div>\n <div class=\"mint-form-section__grid\" :style=\"gridStyle\">\n <div\n v-for=\"field in visibleFields\"\n :key=\"field.name\"\n :style=\"field.colSpan ? { gridColumn: `span ${field.colSpan}` } : undefined\"\n >\n <slot :name=\"`field:${field.name}`\" :field=\"field\" :form=\"builder.form\" :field-props=\"builder.form.getFieldProps(field.name)\">\n <FormFieldRendererInternal\n :field=\"field\"\n :resolved-props=\"builder.getResolvedFieldProps(field)\"\n :form=\"builder.form\"\n />\n </slot>\n </div>\n </div>\n </div>\n </slot>\n\n <slot :name=\"`section:${section.id}:after`\" :form=\"builder.form\" />\n </div>\n</template>\n\n<style>\n@import '../../styles/components/form-builder.css';\n</style>\n","<script setup lang=\"ts\">\n/**\n * Action bar rendered at the bottom of a FormBuilder.\n *\n * Supports two modes:\n * - Flat form: shows an optional Cancel button and a Submit button.\n * - Wizard (`isWizard=true`): shows Back / Next on intermediate steps and\n * Submit only on the last step. Back is hidden on the first step.\n *\n * The `canProceed` prop gates the primary action (Next or Submit) to prevent\n * advancing when the current step has validation errors.\n */\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n isWizard?: boolean\n isFirst?: boolean\n isLast?: boolean\n canProceed?: boolean\n loading?: boolean\n disabled?: boolean\n submitLabel?: string\n cancelLabel?: string\n showCancel?: boolean\n}\n\nwithDefaults(defineProps<Props>(), {\n isWizard: false,\n isFirst: true,\n isLast: true,\n canProceed: true,\n loading: false,\n disabled: false,\n submitLabel: 'Submit',\n cancelLabel: 'Cancel',\n showCancel: false,\n})\n\nconst emit = defineEmits<{\n submit: []\n cancel: []\n back: []\n next: []\n}>()\n</script>\n\n<template>\n <div class=\"mint-form-actions\">\n <BaseButton\n v-if=\"showCancel\"\n variant=\"ghost\"\n :disabled=\"loading || disabled\"\n @click=\"emit('cancel')\"\n >\n {{ cancelLabel }}\n </BaseButton>\n\n <div style=\"flex: 1\" />\n\n <BaseButton\n v-if=\"isWizard && !isFirst\"\n variant=\"secondary\"\n :disabled=\"loading || disabled\"\n @click=\"emit('back')\"\n >\n Back\n </BaseButton>\n\n <BaseButton\n v-if=\"isWizard && !isLast\"\n variant=\"primary\"\n :disabled=\"!canProceed || loading || disabled\"\n @click=\"emit('next')\"\n >\n Next\n </BaseButton>\n\n <BaseButton\n v-if=\"!isWizard || isLast\"\n variant=\"primary\"\n :loading=\"loading\"\n :disabled=\"!canProceed || loading || disabled\"\n @click=\"emit('submit')\"\n >\n {{ submitLabel }}\n </BaseButton>\n </div>\n</template>\n\n<style>\n@import '../styles/components/form-builder.css';\n</style>\n","<script setup lang=\"ts\">\n/**\n * Action bar rendered at the bottom of a FormBuilder.\n *\n * Supports two modes:\n * - Flat form: shows an optional Cancel button and a Submit button.\n * - Wizard (`isWizard=true`): shows Back / Next on intermediate steps and\n * Submit only on the last step. Back is hidden on the first step.\n *\n * The `canProceed` prop gates the primary action (Next or Submit) to prevent\n * advancing when the current step has validation errors.\n */\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n isWizard?: boolean\n isFirst?: boolean\n isLast?: boolean\n canProceed?: boolean\n loading?: boolean\n disabled?: boolean\n submitLabel?: string\n cancelLabel?: string\n showCancel?: boolean\n}\n\nwithDefaults(defineProps<Props>(), {\n isWizard: false,\n isFirst: true,\n isLast: true,\n canProceed: true,\n loading: false,\n disabled: false,\n submitLabel: 'Submit',\n cancelLabel: 'Cancel',\n showCancel: false,\n})\n\nconst emit = defineEmits<{\n submit: []\n cancel: []\n back: []\n next: []\n}>()\n</script>\n\n<template>\n <div class=\"mint-form-actions\">\n <BaseButton\n v-if=\"showCancel\"\n variant=\"ghost\"\n :disabled=\"loading || disabled\"\n @click=\"emit('cancel')\"\n >\n {{ cancelLabel }}\n </BaseButton>\n\n <div style=\"flex: 1\" />\n\n <BaseButton\n v-if=\"isWizard && !isFirst\"\n variant=\"secondary\"\n :disabled=\"loading || disabled\"\n @click=\"emit('back')\"\n >\n Back\n </BaseButton>\n\n <BaseButton\n v-if=\"isWizard && !isLast\"\n variant=\"primary\"\n :disabled=\"!canProceed || loading || disabled\"\n @click=\"emit('next')\"\n >\n Next\n </BaseButton>\n\n <BaseButton\n v-if=\"!isWizard || isLast\"\n variant=\"primary\"\n :loading=\"loading\"\n :disabled=\"!canProceed || loading || disabled\"\n @click=\"emit('submit')\"\n >\n {{ submitLabel }}\n </BaseButton>\n </div>\n</template>\n\n<style>\n@import '../styles/components/form-builder.css';\n</style>\n","<script setup lang=\"ts\">\n/**\n * Schema-driven form component that renders flat or multi-step wizard forms.\n *\n * Pass a `FormSchema` with either `sections` (flat) or `steps` (wizard), or\n * pass a compact `controls` schema for standard generated forms.\n * Use `v-model` for two-way binding of the form data; the `submit` event\n * carries only the data for currently-visible fields. Supply `enhancements`\n * for dynamic options, custom validators, a submit handler, and field-change\n * callbacks that cannot be expressed in JSON.\n * Compact control schemas can be bound directly:\n * `<FormBuilder :controls=\"controls\" v-model=\"values\" />`.\n * Complete control models can also be bound directly:\n * `<FormBuilder :model=\"workspaceModel\" v-model=\"values\" />`.\n *\n * Exposes `form`, `validate`, `reset`, `updateSchema`, `goNext`, `goBack`,\n * `goToStep`, and `builder` for imperative control via template refs.\n *\n * Slots:\n * - `field:<name>` — override a single field's input component\n * - `section:<id>` — replace an entire section body\n * - `section:<id>:after` — inject content after a section\n * - `actions` — replace the default FormActions bar when `showActions` is true\n */\nimport { ref, computed, watch } from 'vue'\nimport type { FormSchema, FormEnhancements, UseFormBuilderReturn } from '../types/form-builder'\nimport type { WizardStep } from '../types'\nimport {\n controlsToFormSchema,\n defineControlModel,\n getControlDefaults,\n mergeControlWorkspaceOptions,\n type ControlModel,\n type ControlModelBinding,\n type ControlSchema,\n type ControlWorkspaceOptions,\n} from '../composables/useControlSchema'\nimport { useFormBuilder } from '../composables/useFormBuilder'\nimport {\n formSchemaFieldNames,\n pickExistingRecordKeys,\n recordValuesEqualForKeys,\n} from '../utils/formModelSync'\nimport StepWizard from './StepWizard.vue'\nimport FormSectionRenderer from './internal/FormSectionRenderer.vue'\nimport FormActions from './FormActions.vue'\n\ninterface Props {\n /** Full form or wizard schema. Takes precedence when `controls` is also provided. */\n schema?: FormSchema\n /** Model returned by defineControlModel(), or a raw nested ControlModel for one-step form generation. */\n model?: ControlModel | ControlModelBinding\n /** Compact control schema used to generate a flat FormSchema. */\n controls?: ControlSchema\n /** Options passed to compact control schema generation, including shared initialValues. */\n controlOptions?: ControlWorkspaceOptions\n modelValue?: Record<string, unknown>\n enhancements?: FormEnhancements<Record<string, unknown>>\n loading?: boolean\n disabled?: boolean\n size?: 'sm' | 'md' | 'lg'\n readonly?: boolean\n /** Show the default or slotted form actions. */\n showActions?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n model: undefined,\n controlOptions: () => ({}),\n loading: false,\n disabled: false,\n readonly: false,\n showActions: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [data: Record<string, unknown>]\n submit: [data: Record<string, unknown>]\n cancel: []\n}>()\n\nconst resolvedModel = computed<ControlModelBinding | undefined>(() => {\n if (props.model === undefined) return undefined\n return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)\n})\n\nconst resolvedControls = computed<ControlSchema | undefined>(() =>\n props.controls ?? resolvedModel.value?.controls,\n)\n\nconst resolvedControlOptions = computed<ControlWorkspaceOptions>(() =>\n mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, props.controlOptions)\n)\n\nconst resolvedSchema = computed<FormSchema>(() => {\n if (props.schema) return props.schema\n if (resolvedControls.value) return controlsToFormSchema(resolvedControls.value, resolvedControlOptions.value)\n return { sections: [] }\n})\n\nconst resolvedModelValue = computed<Record<string, unknown>>(() => ({\n ...(resolvedControls.value ? getControlDefaults(resolvedControls.value) : {}),\n ...(resolvedControlOptions.value.initialValues ?? {}),\n ...(props.modelValue ?? {}),\n}))\n\nconst builder = useFormBuilder(\n resolvedSchema.value,\n resolvedModelValue.value,\n props.enhancements,\n)\n\n// Sync schema/control changes into the internal builder so generated forms stay coherent.\nwatch(\n resolvedSchema,\n (schema) => {\n const fieldNames = formSchemaFieldNames(schema)\n const schemaValues = pickExistingRecordKeys(resolvedModelValue.value, fieldNames)\n const existingValues = pickExistingRecordKeys(\n builder.form.data as Record<string, unknown>,\n fieldNames,\n )\n const nextValues = props.modelValue === undefined\n ? { ...schemaValues, ...existingValues }\n : schemaValues\n\n builder.updateSchema(schema, nextValues)\n },\n { deep: true },\n)\n\n// Sync external modelValue changes into the internal form without resetting on no-op echo updates.\nwatch(\n () => props.modelValue,\n () => {\n const data = resolvedModelValue.value\n const fieldNames = builderFieldNames()\n if (recordValuesEqualForKeys(data, builder.form.data as Record<string, unknown>, fieldNames)) return\n builder.reset(pickExistingRecordKeys(data, fieldNames))\n },\n { deep: true },\n)\n\n// Sync modelValue changes back to parent\nwatch(\n () => ({ ...builder.form.data }),\n (data) => emit('update:modelValue', data as Record<string, unknown>),\n { deep: true },\n)\n\n// Wizard support\nconst isWizard = computed(() => !!resolvedSchema.value.steps)\nconst wizardRef = ref<InstanceType<typeof StepWizard> | null>(null)\n\nconst wizardSteps = computed<WizardStep[]>(() => {\n if (!resolvedSchema.value.steps) return []\n return resolvedSchema.value.steps.map((step) => ({\n id: step.id,\n label: step.label,\n description: step.description,\n icon: step.icon,\n optional: step.optional,\n }))\n})\n\n// Sync step validation state with StepWizard\nwatch(\n () => builder.isCurrentStepValid.value,\n (valid) => {\n wizardRef.value?.setStepValid(builder.currentStep.value, valid)\n },\n)\n\nfunction handleNext() {\n const success = builder.goNext()\n if (success) {\n wizardRef.value?.setStepValid(builder.currentStep.value - 1, true)\n }\n}\n\nasync function handleSubmit() {\n await builder.submit()\n const formData = builder.form.data as Record<string, unknown>\n const visibleData = Object.fromEntries(\n builder.fields\n .filter((f) => builder.isFieldVisible(f.name))\n .map((f) => [f.name, formData[f.name]]),\n )\n emit('submit', visibleData)\n}\n\nfunction handleCancel() {\n emit('cancel')\n}\n\nfunction builderFieldNames(): string[] {\n return builder.fields.map(field => field.name)\n}\n\nfunction isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {\n return 'controls' in model && 'controlOptions' in model\n}\n\ndefineExpose({\n form: builder.form,\n validate: builder.validate,\n reset: builder.reset,\n updateSchema: builder.updateSchema,\n goNext: builder.goNext,\n goBack: builder.goBack,\n goToStep: builder.goToStep,\n builder,\n})\n</script>\n\n<template>\n <div :class=\"['mint-form-builder', size ? `mint-form-builder--${size}` : '']\">\n <!-- Wizard mode -->\n <template v-if=\"isWizard && resolvedSchema.steps\">\n <StepWizard\n ref=\"wizardRef\"\n :steps=\"wizardSteps\"\n :model-value=\"builder.currentStep.value\"\n @update:model-value=\"builder.goToStep($event)\"\n >\n <template v-for=\"step in resolvedSchema.steps\" :key=\"step.id\" #[`step-${step.id}`]>\n <div class=\"mint-form-builder__step\">\n <template v-for=\"section in step.sections\" :key=\"section.id\">\n <FormSectionRenderer\n v-if=\"builder.isSectionVisible(section.id)\"\n :section=\"section\"\n :builder=\"(builder as UseFormBuilderReturn<Record<string, unknown>>)\"\n >\n <!-- Forward field slots -->\n <template v-for=\"field in section.fields\" :key=\"field.name\" #[`field:${field.name}`]=\"slotProps\">\n <slot :name=\"`field:${field.name}`\" v-bind=\"slotProps\" />\n </template>\n <!-- Forward section slots -->\n <template #[`section:${section.id}`]=\"slotProps\">\n <slot :name=\"`section:${section.id}`\" v-bind=\"slotProps\" />\n </template>\n <template #[`section:${section.id}:after`]=\"slotProps\">\n <slot :name=\"`section:${section.id}:after`\" v-bind=\"slotProps\" />\n </template>\n </FormSectionRenderer>\n </template>\n </div>\n </template>\n\n <template v-if=\"showActions\" #navigation=\"{ isFirst, isLast }\">\n <slot name=\"actions\" :form=\"builder.form\" :builder=\"builder\">\n <FormActions\n is-wizard\n :is-first=\"isFirst\"\n :is-last=\"isLast\"\n :can-proceed=\"builder.isCurrentStepValid.value\"\n :loading=\"loading || builder.form.isSubmitting.value\"\n :disabled=\"disabled\"\n :submit-label=\"resolvedSchema.submitLabel ?? 'Submit'\"\n :cancel-label=\"resolvedSchema.cancelLabel ?? 'Cancel'\"\n :show-cancel=\"resolvedSchema.showCancel ?? false\"\n @next=\"handleNext\"\n @back=\"builder.goBack\"\n @submit=\"handleSubmit\"\n @cancel=\"handleCancel\"\n />\n </slot>\n </template>\n </StepWizard>\n </template>\n\n <!-- Flat mode -->\n <template v-else-if=\"resolvedSchema.sections\">\n <template v-for=\"section in resolvedSchema.sections\" :key=\"section.id\">\n <FormSectionRenderer\n v-if=\"builder.isSectionVisible(section.id)\"\n :section=\"section\"\n :builder=\"(builder as UseFormBuilderReturn<Record<string, unknown>>)\"\n >\n <!-- Forward field slots -->\n <template v-for=\"field in section.fields\" :key=\"field.name\" #[`field:${field.name}`]=\"slotProps\">\n <slot :name=\"`field:${field.name}`\" v-bind=\"slotProps\" />\n </template>\n <!-- Forward section slots -->\n <template #[`section:${section.id}`]=\"slotProps\">\n <slot :name=\"`section:${section.id}`\" v-bind=\"slotProps\" />\n </template>\n <template #[`section:${section.id}:after`]=\"slotProps\">\n <slot :name=\"`section:${section.id}:after`\" v-bind=\"slotProps\" />\n </template>\n </FormSectionRenderer>\n </template>\n\n <slot v-if=\"showActions\" name=\"actions\" :form=\"builder.form\" :builder=\"builder\">\n <FormActions\n :loading=\"loading || builder.form.isSubmitting.value\"\n :disabled=\"disabled\"\n :submit-label=\"resolvedSchema.submitLabel ?? 'Submit'\"\n :cancel-label=\"resolvedSchema.cancelLabel ?? 'Cancel'\"\n :show-cancel=\"resolvedSchema.showCancel ?? false\"\n @submit=\"handleSubmit\"\n @cancel=\"handleCancel\"\n />\n </slot>\n </template>\n </div>\n</template>\n\n<style>\n@import '../styles/components/form-builder.css';\n</style>\n","<script setup lang=\"ts\">\n/**\n * Schema-driven form component that renders flat or multi-step wizard forms.\n *\n * Pass a `FormSchema` with either `sections` (flat) or `steps` (wizard), or\n * pass a compact `controls` schema for standard generated forms.\n * Use `v-model` for two-way binding of the form data; the `submit` event\n * carries only the data for currently-visible fields. Supply `enhancements`\n * for dynamic options, custom validators, a submit handler, and field-change\n * callbacks that cannot be expressed in JSON.\n * Compact control schemas can be bound directly:\n * `<FormBuilder :controls=\"controls\" v-model=\"values\" />`.\n * Complete control models can also be bound directly:\n * `<FormBuilder :model=\"workspaceModel\" v-model=\"values\" />`.\n *\n * Exposes `form`, `validate`, `reset`, `updateSchema`, `goNext`, `goBack`,\n * `goToStep`, and `builder` for imperative control via template refs.\n *\n * Slots:\n * - `field:<name>` — override a single field's input component\n * - `section:<id>` — replace an entire section body\n * - `section:<id>:after` — inject content after a section\n * - `actions` — replace the default FormActions bar when `showActions` is true\n */\nimport { ref, computed, watch } from 'vue'\nimport type { FormSchema, FormEnhancements, UseFormBuilderReturn } from '../types/form-builder'\nimport type { WizardStep } from '../types'\nimport {\n controlsToFormSchema,\n defineControlModel,\n getControlDefaults,\n mergeControlWorkspaceOptions,\n type ControlModel,\n type ControlModelBinding,\n type ControlSchema,\n type ControlWorkspaceOptions,\n} from '../composables/useControlSchema'\nimport { useFormBuilder } from '../composables/useFormBuilder'\nimport {\n formSchemaFieldNames,\n pickExistingRecordKeys,\n recordValuesEqualForKeys,\n} from '../utils/formModelSync'\nimport StepWizard from './StepWizard.vue'\nimport FormSectionRenderer from './internal/FormSectionRenderer.vue'\nimport FormActions from './FormActions.vue'\n\ninterface Props {\n /** Full form or wizard schema. Takes precedence when `controls` is also provided. */\n schema?: FormSchema\n /** Model returned by defineControlModel(), or a raw nested ControlModel for one-step form generation. */\n model?: ControlModel | ControlModelBinding\n /** Compact control schema used to generate a flat FormSchema. */\n controls?: ControlSchema\n /** Options passed to compact control schema generation, including shared initialValues. */\n controlOptions?: ControlWorkspaceOptions\n modelValue?: Record<string, unknown>\n enhancements?: FormEnhancements<Record<string, unknown>>\n loading?: boolean\n disabled?: boolean\n size?: 'sm' | 'md' | 'lg'\n readonly?: boolean\n /** Show the default or slotted form actions. */\n showActions?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n model: undefined,\n controlOptions: () => ({}),\n loading: false,\n disabled: false,\n readonly: false,\n showActions: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [data: Record<string, unknown>]\n submit: [data: Record<string, unknown>]\n cancel: []\n}>()\n\nconst resolvedModel = computed<ControlModelBinding | undefined>(() => {\n if (props.model === undefined) return undefined\n return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)\n})\n\nconst resolvedControls = computed<ControlSchema | undefined>(() =>\n props.controls ?? resolvedModel.value?.controls,\n)\n\nconst resolvedControlOptions = computed<ControlWorkspaceOptions>(() =>\n mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, props.controlOptions)\n)\n\nconst resolvedSchema = computed<FormSchema>(() => {\n if (props.schema) return props.schema\n if (resolvedControls.value) return controlsToFormSchema(resolvedControls.value, resolvedControlOptions.value)\n return { sections: [] }\n})\n\nconst resolvedModelValue = computed<Record<string, unknown>>(() => ({\n ...(resolvedControls.value ? getControlDefaults(resolvedControls.value) : {}),\n ...(resolvedControlOptions.value.initialValues ?? {}),\n ...(props.modelValue ?? {}),\n}))\n\nconst builder = useFormBuilder(\n resolvedSchema.value,\n resolvedModelValue.value,\n props.enhancements,\n)\n\n// Sync schema/control changes into the internal builder so generated forms stay coherent.\nwatch(\n resolvedSchema,\n (schema) => {\n const fieldNames = formSchemaFieldNames(schema)\n const schemaValues = pickExistingRecordKeys(resolvedModelValue.value, fieldNames)\n const existingValues = pickExistingRecordKeys(\n builder.form.data as Record<string, unknown>,\n fieldNames,\n )\n const nextValues = props.modelValue === undefined\n ? { ...schemaValues, ...existingValues }\n : schemaValues\n\n builder.updateSchema(schema, nextValues)\n },\n { deep: true },\n)\n\n// Sync external modelValue changes into the internal form without resetting on no-op echo updates.\nwatch(\n () => props.modelValue,\n () => {\n const data = resolvedModelValue.value\n const fieldNames = builderFieldNames()\n if (recordValuesEqualForKeys(data, builder.form.data as Record<string, unknown>, fieldNames)) return\n builder.reset(pickExistingRecordKeys(data, fieldNames))\n },\n { deep: true },\n)\n\n// Sync modelValue changes back to parent\nwatch(\n () => ({ ...builder.form.data }),\n (data) => emit('update:modelValue', data as Record<string, unknown>),\n { deep: true },\n)\n\n// Wizard support\nconst isWizard = computed(() => !!resolvedSchema.value.steps)\nconst wizardRef = ref<InstanceType<typeof StepWizard> | null>(null)\n\nconst wizardSteps = computed<WizardStep[]>(() => {\n if (!resolvedSchema.value.steps) return []\n return resolvedSchema.value.steps.map((step) => ({\n id: step.id,\n label: step.label,\n description: step.description,\n icon: step.icon,\n optional: step.optional,\n }))\n})\n\n// Sync step validation state with StepWizard\nwatch(\n () => builder.isCurrentStepValid.value,\n (valid) => {\n wizardRef.value?.setStepValid(builder.currentStep.value, valid)\n },\n)\n\nfunction handleNext() {\n const success = builder.goNext()\n if (success) {\n wizardRef.value?.setStepValid(builder.currentStep.value - 1, true)\n }\n}\n\nasync function handleSubmit() {\n await builder.submit()\n const formData = builder.form.data as Record<string, unknown>\n const visibleData = Object.fromEntries(\n builder.fields\n .filter((f) => builder.isFieldVisible(f.name))\n .map((f) => [f.name, formData[f.name]]),\n )\n emit('submit', visibleData)\n}\n\nfunction handleCancel() {\n emit('cancel')\n}\n\nfunction builderFieldNames(): string[] {\n return builder.fields.map(field => field.name)\n}\n\nfunction isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {\n return 'controls' in model && 'controlOptions' in model\n}\n\ndefineExpose({\n form: builder.form,\n validate: builder.validate,\n reset: builder.reset,\n updateSchema: builder.updateSchema,\n goNext: builder.goNext,\n goBack: builder.goBack,\n goToStep: builder.goToStep,\n builder,\n})\n</script>\n\n<template>\n <div :class=\"['mint-form-builder', size ? `mint-form-builder--${size}` : '']\">\n <!-- Wizard mode -->\n <template v-if=\"isWizard && resolvedSchema.steps\">\n <StepWizard\n ref=\"wizardRef\"\n :steps=\"wizardSteps\"\n :model-value=\"builder.currentStep.value\"\n @update:model-value=\"builder.goToStep($event)\"\n >\n <template v-for=\"step in resolvedSchema.steps\" :key=\"step.id\" #[`step-${step.id}`]>\n <div class=\"mint-form-builder__step\">\n <template v-for=\"section in step.sections\" :key=\"section.id\">\n <FormSectionRenderer\n v-if=\"builder.isSectionVisible(section.id)\"\n :section=\"section\"\n :builder=\"(builder as UseFormBuilderReturn<Record<string, unknown>>)\"\n >\n <!-- Forward field slots -->\n <template v-for=\"field in section.fields\" :key=\"field.name\" #[`field:${field.name}`]=\"slotProps\">\n <slot :name=\"`field:${field.name}`\" v-bind=\"slotProps\" />\n </template>\n <!-- Forward section slots -->\n <template #[`section:${section.id}`]=\"slotProps\">\n <slot :name=\"`section:${section.id}`\" v-bind=\"slotProps\" />\n </template>\n <template #[`section:${section.id}:after`]=\"slotProps\">\n <slot :name=\"`section:${section.id}:after`\" v-bind=\"slotProps\" />\n </template>\n </FormSectionRenderer>\n </template>\n </div>\n </template>\n\n <template v-if=\"showActions\" #navigation=\"{ isFirst, isLast }\">\n <slot name=\"actions\" :form=\"builder.form\" :builder=\"builder\">\n <FormActions\n is-wizard\n :is-first=\"isFirst\"\n :is-last=\"isLast\"\n :can-proceed=\"builder.isCurrentStepValid.value\"\n :loading=\"loading || builder.form.isSubmitting.value\"\n :disabled=\"disabled\"\n :submit-label=\"resolvedSchema.submitLabel ?? 'Submit'\"\n :cancel-label=\"resolvedSchema.cancelLabel ?? 'Cancel'\"\n :show-cancel=\"resolvedSchema.showCancel ?? false\"\n @next=\"handleNext\"\n @back=\"builder.goBack\"\n @submit=\"handleSubmit\"\n @cancel=\"handleCancel\"\n />\n </slot>\n </template>\n </StepWizard>\n </template>\n\n <!-- Flat mode -->\n <template v-else-if=\"resolvedSchema.sections\">\n <template v-for=\"section in resolvedSchema.sections\" :key=\"section.id\">\n <FormSectionRenderer\n v-if=\"builder.isSectionVisible(section.id)\"\n :section=\"section\"\n :builder=\"(builder as UseFormBuilderReturn<Record<string, unknown>>)\"\n >\n <!-- Forward field slots -->\n <template v-for=\"field in section.fields\" :key=\"field.name\" #[`field:${field.name}`]=\"slotProps\">\n <slot :name=\"`field:${field.name}`\" v-bind=\"slotProps\" />\n </template>\n <!-- Forward section slots -->\n <template #[`section:${section.id}`]=\"slotProps\">\n <slot :name=\"`section:${section.id}`\" v-bind=\"slotProps\" />\n </template>\n <template #[`section:${section.id}:after`]=\"slotProps\">\n <slot :name=\"`section:${section.id}:after`\" v-bind=\"slotProps\" />\n </template>\n </FormSectionRenderer>\n </template>\n\n <slot v-if=\"showActions\" name=\"actions\" :form=\"builder.form\" :builder=\"builder\">\n <FormActions\n :loading=\"loading || builder.form.isSubmitting.value\"\n :disabled=\"disabled\"\n :submit-label=\"resolvedSchema.submitLabel ?? 'Submit'\"\n :cancel-label=\"resolvedSchema.cancelLabel ?? 'Cancel'\"\n :show-cancel=\"resolvedSchema.showCancel ?? false\"\n @submit=\"handleSubmit\"\n @cancel=\"handleCancel\"\n />\n </slot>\n </template>\n </div>\n</template>\n\n<style>\n@import '../styles/components/form-builder.css';\n</style>\n","<script setup lang=\"ts\">\n/**\n * AppSidebar - Context-sensitive toolkit panel\n *\n * Shows tool sections relevant to the active view. Sections are defined via\n * the `panels` config and rendered as CollapsibleCards. Controls can be\n * provided through named slots or auto-rendered from FormBuilder schemas.\n *\n * When activeView is omitted, the first non-empty panel view is selected.\n * When no view has matching panels, the sidebar hides entirely.\n *\n * @example\n * ```vue\n * <AppSidebar :panels=\"toolPanels\" :active-view=\"activeTab\">\n * <template #section-parameters>\n * <BaseSlider v-model=\"threshold\" label=\"Threshold\" />\n * </template>\n * <template #section-display>\n * <BaseToggle v-model=\"showOutliers\" label=\"Show outliers\" />\n * </template>\n * <template #header>Plugin Tools</template>\n * </AppSidebar>\n *\n * <!-- Direct schema-driven controls -->\n * <AppSidebar :controls=\"controls\" :active-view=\"activeTab\" v-model=\"values\" />\n * ```\n */\nimport { computed, ref, useSlots, type Slots } from 'vue'\nimport type { PillNavItem, SidebarToolSection } from '../types'\nimport type { FormEnhancements, FormSchema } from '../types/form-builder'\nimport {\n controlsToSectionFormSchemas,\n controlsToSidebarPanels,\n defineControlModel,\n getControlDefaults,\n mergeControlWorkspaceOptions,\n type ControlModel,\n type ControlModelBinding,\n type ControlSchema,\n type ControlWorkspaceOptions,\n} from '../composables/useControlSchema'\nimport CollapsibleCard from './CollapsibleCard.vue'\nimport FormBuilder from './FormBuilder.vue'\n\ninterface Props {\n /** Optional chrome title rendered above generated sections. */\n title?: string\n /** Optional secondary chrome copy rendered below title. */\n subtitle?: string\n /** Optional compact badge/count rendered in the chrome header. */\n badge?: string | number\n /** Visual preset for common plugin sidebars. `analysis` preserves the LEAF-style MINT analysis sidebar design language. */\n variant?: 'default' | 'analysis'\n /** Map of view IDs to their tool sections */\n panels?: Record<string, SidebarToolSection[]>\n /** Which view's panels to display. Defaults to the first non-empty panel view. */\n activeView?: string\n /** Floating variant with absolute positioning. Defaults to false for analysis variant. */\n floating?: boolean\n /** Compact layout: smaller headers, tighter spacing, no icon backgrounds */\n dense?: boolean\n /** Width when visible. Defaults to 20rem for analysis variant, otherwise 280px. */\n width?: string\n /** Position sidebar on left or right side */\n side?: 'left' | 'right'\n /** Toggle state map: sectionId → boolean */\n toggleState?: Record<string, boolean>\n /** Optional FormBuilder schemas keyed by section ID. Used when no section slot is provided. */\n forms?: Record<string, FormSchema>\n /** Generated view IDs from useControlSchema(). Consumed for clean v-bind ergonomics. */\n viewIds?: string[]\n /** Generated AppTopBar pillNav-compatible view items from useControlSchema(). Consumed for clean v-bind ergonomics. */\n viewItems?: PillNavItem[]\n /** Default view ID used when activeView is omitted. */\n defaultView?: string\n /** Model returned by defineControlModel(), or a raw nested ControlModel for one-step sidebar/form generation. */\n model?: ControlModel | ControlModelBinding\n /** Compact control schema. When provided, AppSidebar generates panels, section forms, and default values. */\n controls?: ControlSchema\n /** Options passed to compact control schema generation, including shared initialValues. */\n controlOptions?: ControlWorkspaceOptions\n /** DOM id for the scrollable content area. Use with Teleport when route/tab children own sidebar controls. */\n contentId?: string\n /** Render the sidebar shell even when no panel matches the active view. Useful for default-slot or Teleport-driven sidebars. */\n showWhenEmpty?: boolean\n /** Shared values for auto-rendered section forms. Supports default v-model. */\n modelValue?: Record<string, unknown>\n /** Shared values for auto-rendered section forms */\n values?: Record<string, unknown>\n /** Runtime FormBuilder enhancements for auto-rendered section forms */\n formEnhancements?: FormEnhancements<Record<string, unknown>>\n /** Show submit/cancel actions inside auto-rendered section forms */\n showFormActions?: boolean\n /** Loading/saving state for auto-rendered section forms */\n formLoading?: boolean\n /** Disabled state for auto-rendered section forms */\n formDisabled?: boolean\n /** Readonly state for auto-rendered section forms */\n formReadonly?: boolean\n /** Size passed to auto-rendered section forms */\n formSize?: 'sm' | 'md' | 'lg'\n /** Show a built-in collapse/expand button in the sidebar chrome. Defaults to true for analysis variant. */\n collapsible?: boolean\n /** Controlled collapsed state. */\n collapsed?: boolean\n /** Initial collapsed state when collapsed is uncontrolled. */\n defaultCollapsed?: boolean\n /** Width when collapsed. */\n collapsedWidth?: string\n /** Accessible label for the collapse action. */\n collapseButtonLabel?: string\n /** Accessible label for the expand action. */\n expandButtonLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: undefined,\n subtitle: undefined,\n badge: undefined,\n variant: 'default',\n panels: () => ({}),\n activeView: '',\n floating: undefined,\n dense: false,\n width: undefined,\n side: 'left',\n toggleState: () => ({}),\n forms: () => ({}),\n viewIds: () => [],\n viewItems: () => [],\n defaultView: '',\n model: undefined,\n controlOptions: () => ({}),\n contentId: undefined,\n showWhenEmpty: false,\n modelValue: undefined,\n values: () => ({}),\n showFormActions: false,\n formLoading: false,\n formDisabled: false,\n formReadonly: false,\n formSize: 'sm',\n collapsible: undefined,\n collapsed: undefined,\n defaultCollapsed: false,\n collapsedWidth: '3rem',\n collapseButtonLabel: 'Collapse sidebar',\n expandButtonLabel: 'Expand sidebar',\n})\n\nconst emit = defineEmits<{\n 'update:toggle': [sectionId: string, value: boolean]\n 'update:modelValue': [values: Record<string, unknown>]\n 'update:values': [values: Record<string, unknown>]\n 'update:collapsed': [value: boolean]\n 'form-submit': [sectionId: string, values: Record<string, unknown>]\n 'form-cancel': [sectionId: string]\n}>()\n\nconst slots: Slots = useSlots()\nconst internalCollapsed = ref(props.defaultCollapsed)\nconst collapsedModel = computed({\n get: () => props.collapsed ?? internalCollapsed.value,\n set: (value: boolean) => {\n internalCollapsed.value = value\n emit('update:collapsed', value)\n },\n})\n\nconst isAnalysisVariant = computed(() => props.variant === 'analysis')\nconst resolvedFloating = computed(() => props.floating ?? !isAnalysisVariant.value)\nconst resolvedWidth = computed(() => props.width ?? (isAnalysisVariant.value ? '20rem' : '280px'))\nconst resolvedCollapsible = computed(() => props.collapsible ?? isAnalysisVariant.value)\n\nconst resolvedModel = computed<ControlModelBinding | undefined>(() => {\n if (props.model === undefined) return undefined\n return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)\n})\n\nconst resolvedControls = computed<ControlSchema | undefined>(() =>\n props.controls ?? resolvedModel.value?.controls,\n)\n\nconst resolvedControlOptions = computed<ControlWorkspaceOptions>(() =>\n mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, props.controlOptions)\n)\n\nconst generatedPanels = computed<Record<string, SidebarToolSection[]>>(() => {\n if (!resolvedControls.value) return {}\n return controlsToSidebarPanels(resolvedControls.value, resolvedControlOptions.value)\n})\n\nconst generatedForms = computed<Record<string, FormSchema>>(() => {\n if (!resolvedControls.value) return {}\n return controlsToSectionFormSchemas(resolvedControls.value, resolvedControlOptions.value)\n})\n\nconst generatedDefaults = computed<Record<string, unknown>>(() => {\n if (!resolvedControls.value) return {}\n return {\n ...getControlDefaults(resolvedControls.value),\n ...(resolvedControlOptions.value.initialValues ?? {}),\n }\n})\n\nconst resolvedPanels = computed<Record<string, SidebarToolSection[]>>(() =>\n mergeSidebarPanels(generatedPanels.value, props.panels),\n)\n\nconst resolvedForms = computed<Record<string, FormSchema>>(() => ({\n ...generatedForms.value,\n ...props.forms,\n}))\n\nconst resolvedValues = computed<Record<string, unknown>>(() => ({\n ...generatedDefaults.value,\n ...(props.modelValue ?? props.values),\n}))\n\nconst resolvedActiveView = computed(() => {\n if (props.activeView) return props.activeView\n if (props.defaultView) return props.defaultView\n return firstVisibleViewId(resolvedPanels.value)\n})\n\nconst activeSections = computed<SidebarToolSection[]>(() => {\n if (!resolvedActiveView.value || !resolvedPanels.value[resolvedActiveView.value]) return []\n return resolvedPanels.value[resolvedActiveView.value]\n})\n\nconst isVisible = computed<boolean>(() =>\n activeSections.value.length > 0 || props.showWhenEmpty || Boolean(slots.default),\n)\n\nconst sidebarClasses = computed<string[]>(() => [\n 'mint-sidebar',\n `mint-sidebar--${props.side}`,\n `mint-sidebar--${props.variant}`,\n resolvedFloating.value ? 'mint-sidebar--floating' : 'mint-sidebar--static',\n props.dense ? 'mint-sidebar--dense' : '',\n resolvedCollapsible.value ? 'mint-sidebar--collapsible' : '',\n collapsedModel.value ? 'mint-sidebar--collapsed' : '',\n !isVisible.value ? 'mint-sidebar--hidden' : '',\n])\n\nconst sidebarStyle = computed<Record<string, string>>(() => ({\n width: collapsedModel.value ? props.collapsedWidth : resolvedWidth.value,\n}))\n\nfunction handleFormUpdate(values: Record<string, unknown>) {\n const nextValues = { ...resolvedValues.value, ...values }\n emit('update:modelValue', nextValues)\n emit('update:values', nextValues)\n}\n\nfunction handleFormSubmit(sectionId: string, values: Record<string, unknown>) {\n emit('form-submit', sectionId, values)\n}\n\nfunction handleFormCancel(sectionId: string) {\n emit('form-cancel', sectionId)\n}\n\nfunction toggleCollapsed() {\n collapsedModel.value = !collapsedModel.value\n}\n\nfunction expandCollapsed() {\n collapsedModel.value = false\n}\n\nfunction mergeSidebarPanels(\n generated: Record<string, SidebarToolSection[]>,\n explicit: Record<string, SidebarToolSection[]>,\n): Record<string, SidebarToolSection[]> {\n const merged: Record<string, SidebarToolSection[]> = {}\n const viewIds = new Set([...Object.keys(generated), ...Object.keys(explicit)])\n\n for (const viewId of viewIds) {\n const sectionsById = new Map<string, SidebarToolSection>()\n for (const section of generated[viewId] ?? []) {\n sectionsById.set(section.id, section)\n }\n for (const section of explicit[viewId] ?? []) {\n sectionsById.set(section.id, section)\n }\n merged[viewId] = [...sectionsById.values()]\n }\n\n return merged\n}\n\nfunction firstVisibleViewId(panels: Record<string, SidebarToolSection[]>): string {\n for (const [viewId, sections] of Object.entries(panels)) {\n if (sections.length > 0) return viewId\n }\n return ''\n}\n\nfunction isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {\n return 'controls' in model && 'controlOptions' in model\n}\n</script>\n\n<template>\n <aside\n :class=\"sidebarClasses\"\n :style=\"sidebarStyle\"\n >\n <!-- Header slot / built-in chrome -->\n <div\n v-if=\"$slots.header || title || subtitle || badge !== undefined || resolvedCollapsible\"\n class=\"mint-sidebar__header\"\n >\n <slot\n name=\"header\"\n :collapsed=\"collapsedModel\"\n :toggle-collapsed=\"toggleCollapsed\"\n >\n <div v-if=\"!collapsedModel\" class=\"mint-sidebar__heading\">\n <div class=\"mint-sidebar__heading-copy\">\n <h2 v-if=\"title\" class=\"mint-sidebar__title\">{{ title }}</h2>\n <p v-if=\"subtitle\" class=\"mint-sidebar__subtitle\">{{ subtitle }}</p>\n </div>\n <span v-if=\"badge !== undefined\" class=\"mint-sidebar__badge\">{{ badge }}</span>\n </div>\n <button\n v-if=\"resolvedCollapsible\"\n type=\"button\"\n class=\"mint-sidebar__collapse-button\"\n :aria-label=\"collapsedModel ? expandButtonLabel : collapseButtonLabel\"\n :aria-expanded=\"!collapsedModel\"\n @click=\"toggleCollapsed\"\n >\n <svg\n class=\"mint-sidebar__collapse-icon\"\n :class=\"{ 'mint-sidebar__collapse-icon--collapsed': collapsedModel }\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M11 19l-7-7 7-7m8 14l-7-7 7-7\" />\n </svg>\n </button>\n </slot>\n </div>\n\n <!-- Tool sections -->\n <div\n v-if=\"!collapsedModel\"\n :id=\"contentId\"\n class=\"mint-sidebar__sections\"\n >\n <CollapsibleCard\n v-for=\"section in activeSections\"\n :key=\"section.id\"\n :title=\"section.label\"\n :subtitle=\"section.subtitle\"\n :icon=\"section.icon\"\n :icon-color=\"section.iconColor\"\n :icon-bg=\"section.iconBg\"\n :dense=\"dense\"\n :default-open=\"section.defaultOpen !== false\"\n :show-toggle=\"section.showToggle\"\n :toggle-value=\"toggleState[section.id] ?? false\"\n @update:toggle-value=\"emit('update:toggle', section.id, $event)\"\n >\n <slot :name=\"`section-${section.id}`\">\n <FormBuilder\n v-if=\"resolvedForms[section.id]\"\n :model-value=\"resolvedValues\"\n :schema=\"resolvedForms[section.id]\"\n :enhancements=\"formEnhancements\"\n :loading=\"formLoading\"\n :disabled=\"formDisabled\"\n :readonly=\"formReadonly\"\n :size=\"formSize\"\n :show-actions=\"showFormActions\"\n @update:model-value=\"handleFormUpdate\"\n @submit=\"handleFormSubmit(section.id, $event)\"\n @cancel=\"handleFormCancel(section.id)\"\n />\n </slot>\n </CollapsibleCard>\n\n <slot\n :sections=\"activeSections\"\n :active-view=\"resolvedActiveView\"\n :values=\"resolvedValues\"\n />\n </div>\n\n <div v-else-if=\"$slots.collapsed\" class=\"mint-sidebar__collapsed\">\n <slot\n name=\"collapsed\"\n :sections=\"activeSections\"\n :expand=\"expandCollapsed\"\n />\n </div>\n\n <!-- Footer slot -->\n <div v-if=\"!collapsedModel && $slots.footer\" class=\"mint-sidebar__footer\">\n <slot name=\"footer\" />\n </div>\n </aside>\n</template>\n\n<style>\n@import '../styles/components/app-sidebar.css';\n</style>\n","<script setup lang=\"ts\">\n/**\n * AppSidebar - Context-sensitive toolkit panel\n *\n * Shows tool sections relevant to the active view. Sections are defined via\n * the `panels` config and rendered as CollapsibleCards. Controls can be\n * provided through named slots or auto-rendered from FormBuilder schemas.\n *\n * When activeView is omitted, the first non-empty panel view is selected.\n * When no view has matching panels, the sidebar hides entirely.\n *\n * @example\n * ```vue\n * <AppSidebar :panels=\"toolPanels\" :active-view=\"activeTab\">\n * <template #section-parameters>\n * <BaseSlider v-model=\"threshold\" label=\"Threshold\" />\n * </template>\n * <template #section-display>\n * <BaseToggle v-model=\"showOutliers\" label=\"Show outliers\" />\n * </template>\n * <template #header>Plugin Tools</template>\n * </AppSidebar>\n *\n * <!-- Direct schema-driven controls -->\n * <AppSidebar :controls=\"controls\" :active-view=\"activeTab\" v-model=\"values\" />\n * ```\n */\nimport { computed, ref, useSlots, type Slots } from 'vue'\nimport type { PillNavItem, SidebarToolSection } from '../types'\nimport type { FormEnhancements, FormSchema } from '../types/form-builder'\nimport {\n controlsToSectionFormSchemas,\n controlsToSidebarPanels,\n defineControlModel,\n getControlDefaults,\n mergeControlWorkspaceOptions,\n type ControlModel,\n type ControlModelBinding,\n type ControlSchema,\n type ControlWorkspaceOptions,\n} from '../composables/useControlSchema'\nimport CollapsibleCard from './CollapsibleCard.vue'\nimport FormBuilder from './FormBuilder.vue'\n\ninterface Props {\n /** Optional chrome title rendered above generated sections. */\n title?: string\n /** Optional secondary chrome copy rendered below title. */\n subtitle?: string\n /** Optional compact badge/count rendered in the chrome header. */\n badge?: string | number\n /** Visual preset for common plugin sidebars. `analysis` preserves the LEAF-style MINT analysis sidebar design language. */\n variant?: 'default' | 'analysis'\n /** Map of view IDs to their tool sections */\n panels?: Record<string, SidebarToolSection[]>\n /** Which view's panels to display. Defaults to the first non-empty panel view. */\n activeView?: string\n /** Floating variant with absolute positioning. Defaults to false for analysis variant. */\n floating?: boolean\n /** Compact layout: smaller headers, tighter spacing, no icon backgrounds */\n dense?: boolean\n /** Width when visible. Defaults to 20rem for analysis variant, otherwise 280px. */\n width?: string\n /** Position sidebar on left or right side */\n side?: 'left' | 'right'\n /** Toggle state map: sectionId → boolean */\n toggleState?: Record<string, boolean>\n /** Optional FormBuilder schemas keyed by section ID. Used when no section slot is provided. */\n forms?: Record<string, FormSchema>\n /** Generated view IDs from useControlSchema(). Consumed for clean v-bind ergonomics. */\n viewIds?: string[]\n /** Generated AppTopBar pillNav-compatible view items from useControlSchema(). Consumed for clean v-bind ergonomics. */\n viewItems?: PillNavItem[]\n /** Default view ID used when activeView is omitted. */\n defaultView?: string\n /** Model returned by defineControlModel(), or a raw nested ControlModel for one-step sidebar/form generation. */\n model?: ControlModel | ControlModelBinding\n /** Compact control schema. When provided, AppSidebar generates panels, section forms, and default values. */\n controls?: ControlSchema\n /** Options passed to compact control schema generation, including shared initialValues. */\n controlOptions?: ControlWorkspaceOptions\n /** DOM id for the scrollable content area. Use with Teleport when route/tab children own sidebar controls. */\n contentId?: string\n /** Render the sidebar shell even when no panel matches the active view. Useful for default-slot or Teleport-driven sidebars. */\n showWhenEmpty?: boolean\n /** Shared values for auto-rendered section forms. Supports default v-model. */\n modelValue?: Record<string, unknown>\n /** Shared values for auto-rendered section forms */\n values?: Record<string, unknown>\n /** Runtime FormBuilder enhancements for auto-rendered section forms */\n formEnhancements?: FormEnhancements<Record<string, unknown>>\n /** Show submit/cancel actions inside auto-rendered section forms */\n showFormActions?: boolean\n /** Loading/saving state for auto-rendered section forms */\n formLoading?: boolean\n /** Disabled state for auto-rendered section forms */\n formDisabled?: boolean\n /** Readonly state for auto-rendered section forms */\n formReadonly?: boolean\n /** Size passed to auto-rendered section forms */\n formSize?: 'sm' | 'md' | 'lg'\n /** Show a built-in collapse/expand button in the sidebar chrome. Defaults to true for analysis variant. */\n collapsible?: boolean\n /** Controlled collapsed state. */\n collapsed?: boolean\n /** Initial collapsed state when collapsed is uncontrolled. */\n defaultCollapsed?: boolean\n /** Width when collapsed. */\n collapsedWidth?: string\n /** Accessible label for the collapse action. */\n collapseButtonLabel?: string\n /** Accessible label for the expand action. */\n expandButtonLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: undefined,\n subtitle: undefined,\n badge: undefined,\n variant: 'default',\n panels: () => ({}),\n activeView: '',\n floating: undefined,\n dense: false,\n width: undefined,\n side: 'left',\n toggleState: () => ({}),\n forms: () => ({}),\n viewIds: () => [],\n viewItems: () => [],\n defaultView: '',\n model: undefined,\n controlOptions: () => ({}),\n contentId: undefined,\n showWhenEmpty: false,\n modelValue: undefined,\n values: () => ({}),\n showFormActions: false,\n formLoading: false,\n formDisabled: false,\n formReadonly: false,\n formSize: 'sm',\n collapsible: undefined,\n collapsed: undefined,\n defaultCollapsed: false,\n collapsedWidth: '3rem',\n collapseButtonLabel: 'Collapse sidebar',\n expandButtonLabel: 'Expand sidebar',\n})\n\nconst emit = defineEmits<{\n 'update:toggle': [sectionId: string, value: boolean]\n 'update:modelValue': [values: Record<string, unknown>]\n 'update:values': [values: Record<string, unknown>]\n 'update:collapsed': [value: boolean]\n 'form-submit': [sectionId: string, values: Record<string, unknown>]\n 'form-cancel': [sectionId: string]\n}>()\n\nconst slots: Slots = useSlots()\nconst internalCollapsed = ref(props.defaultCollapsed)\nconst collapsedModel = computed({\n get: () => props.collapsed ?? internalCollapsed.value,\n set: (value: boolean) => {\n internalCollapsed.value = value\n emit('update:collapsed', value)\n },\n})\n\nconst isAnalysisVariant = computed(() => props.variant === 'analysis')\nconst resolvedFloating = computed(() => props.floating ?? !isAnalysisVariant.value)\nconst resolvedWidth = computed(() => props.width ?? (isAnalysisVariant.value ? '20rem' : '280px'))\nconst resolvedCollapsible = computed(() => props.collapsible ?? isAnalysisVariant.value)\n\nconst resolvedModel = computed<ControlModelBinding | undefined>(() => {\n if (props.model === undefined) return undefined\n return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)\n})\n\nconst resolvedControls = computed<ControlSchema | undefined>(() =>\n props.controls ?? resolvedModel.value?.controls,\n)\n\nconst resolvedControlOptions = computed<ControlWorkspaceOptions>(() =>\n mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, props.controlOptions)\n)\n\nconst generatedPanels = computed<Record<string, SidebarToolSection[]>>(() => {\n if (!resolvedControls.value) return {}\n return controlsToSidebarPanels(resolvedControls.value, resolvedControlOptions.value)\n})\n\nconst generatedForms = computed<Record<string, FormSchema>>(() => {\n if (!resolvedControls.value) return {}\n return controlsToSectionFormSchemas(resolvedControls.value, resolvedControlOptions.value)\n})\n\nconst generatedDefaults = computed<Record<string, unknown>>(() => {\n if (!resolvedControls.value) return {}\n return {\n ...getControlDefaults(resolvedControls.value),\n ...(resolvedControlOptions.value.initialValues ?? {}),\n }\n})\n\nconst resolvedPanels = computed<Record<string, SidebarToolSection[]>>(() =>\n mergeSidebarPanels(generatedPanels.value, props.panels),\n)\n\nconst resolvedForms = computed<Record<string, FormSchema>>(() => ({\n ...generatedForms.value,\n ...props.forms,\n}))\n\nconst resolvedValues = computed<Record<string, unknown>>(() => ({\n ...generatedDefaults.value,\n ...(props.modelValue ?? props.values),\n}))\n\nconst resolvedActiveView = computed(() => {\n if (props.activeView) return props.activeView\n if (props.defaultView) return props.defaultView\n return firstVisibleViewId(resolvedPanels.value)\n})\n\nconst activeSections = computed<SidebarToolSection[]>(() => {\n if (!resolvedActiveView.value || !resolvedPanels.value[resolvedActiveView.value]) return []\n return resolvedPanels.value[resolvedActiveView.value]\n})\n\nconst isVisible = computed<boolean>(() =>\n activeSections.value.length > 0 || props.showWhenEmpty || Boolean(slots.default),\n)\n\nconst sidebarClasses = computed<string[]>(() => [\n 'mint-sidebar',\n `mint-sidebar--${props.side}`,\n `mint-sidebar--${props.variant}`,\n resolvedFloating.value ? 'mint-sidebar--floating' : 'mint-sidebar--static',\n props.dense ? 'mint-sidebar--dense' : '',\n resolvedCollapsible.value ? 'mint-sidebar--collapsible' : '',\n collapsedModel.value ? 'mint-sidebar--collapsed' : '',\n !isVisible.value ? 'mint-sidebar--hidden' : '',\n])\n\nconst sidebarStyle = computed<Record<string, string>>(() => ({\n width: collapsedModel.value ? props.collapsedWidth : resolvedWidth.value,\n}))\n\nfunction handleFormUpdate(values: Record<string, unknown>) {\n const nextValues = { ...resolvedValues.value, ...values }\n emit('update:modelValue', nextValues)\n emit('update:values', nextValues)\n}\n\nfunction handleFormSubmit(sectionId: string, values: Record<string, unknown>) {\n emit('form-submit', sectionId, values)\n}\n\nfunction handleFormCancel(sectionId: string) {\n emit('form-cancel', sectionId)\n}\n\nfunction toggleCollapsed() {\n collapsedModel.value = !collapsedModel.value\n}\n\nfunction expandCollapsed() {\n collapsedModel.value = false\n}\n\nfunction mergeSidebarPanels(\n generated: Record<string, SidebarToolSection[]>,\n explicit: Record<string, SidebarToolSection[]>,\n): Record<string, SidebarToolSection[]> {\n const merged: Record<string, SidebarToolSection[]> = {}\n const viewIds = new Set([...Object.keys(generated), ...Object.keys(explicit)])\n\n for (const viewId of viewIds) {\n const sectionsById = new Map<string, SidebarToolSection>()\n for (const section of generated[viewId] ?? []) {\n sectionsById.set(section.id, section)\n }\n for (const section of explicit[viewId] ?? []) {\n sectionsById.set(section.id, section)\n }\n merged[viewId] = [...sectionsById.values()]\n }\n\n return merged\n}\n\nfunction firstVisibleViewId(panels: Record<string, SidebarToolSection[]>): string {\n for (const [viewId, sections] of Object.entries(panels)) {\n if (sections.length > 0) return viewId\n }\n return ''\n}\n\nfunction isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {\n return 'controls' in model && 'controlOptions' in model\n}\n</script>\n\n<template>\n <aside\n :class=\"sidebarClasses\"\n :style=\"sidebarStyle\"\n >\n <!-- Header slot / built-in chrome -->\n <div\n v-if=\"$slots.header || title || subtitle || badge !== undefined || resolvedCollapsible\"\n class=\"mint-sidebar__header\"\n >\n <slot\n name=\"header\"\n :collapsed=\"collapsedModel\"\n :toggle-collapsed=\"toggleCollapsed\"\n >\n <div v-if=\"!collapsedModel\" class=\"mint-sidebar__heading\">\n <div class=\"mint-sidebar__heading-copy\">\n <h2 v-if=\"title\" class=\"mint-sidebar__title\">{{ title }}</h2>\n <p v-if=\"subtitle\" class=\"mint-sidebar__subtitle\">{{ subtitle }}</p>\n </div>\n <span v-if=\"badge !== undefined\" class=\"mint-sidebar__badge\">{{ badge }}</span>\n </div>\n <button\n v-if=\"resolvedCollapsible\"\n type=\"button\"\n class=\"mint-sidebar__collapse-button\"\n :aria-label=\"collapsedModel ? expandButtonLabel : collapseButtonLabel\"\n :aria-expanded=\"!collapsedModel\"\n @click=\"toggleCollapsed\"\n >\n <svg\n class=\"mint-sidebar__collapse-icon\"\n :class=\"{ 'mint-sidebar__collapse-icon--collapsed': collapsedModel }\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M11 19l-7-7 7-7m8 14l-7-7 7-7\" />\n </svg>\n </button>\n </slot>\n </div>\n\n <!-- Tool sections -->\n <div\n v-if=\"!collapsedModel\"\n :id=\"contentId\"\n class=\"mint-sidebar__sections\"\n >\n <CollapsibleCard\n v-for=\"section in activeSections\"\n :key=\"section.id\"\n :title=\"section.label\"\n :subtitle=\"section.subtitle\"\n :icon=\"section.icon\"\n :icon-color=\"section.iconColor\"\n :icon-bg=\"section.iconBg\"\n :dense=\"dense\"\n :default-open=\"section.defaultOpen !== false\"\n :show-toggle=\"section.showToggle\"\n :toggle-value=\"toggleState[section.id] ?? false\"\n @update:toggle-value=\"emit('update:toggle', section.id, $event)\"\n >\n <slot :name=\"`section-${section.id}`\">\n <FormBuilder\n v-if=\"resolvedForms[section.id]\"\n :model-value=\"resolvedValues\"\n :schema=\"resolvedForms[section.id]\"\n :enhancements=\"formEnhancements\"\n :loading=\"formLoading\"\n :disabled=\"formDisabled\"\n :readonly=\"formReadonly\"\n :size=\"formSize\"\n :show-actions=\"showFormActions\"\n @update:model-value=\"handleFormUpdate\"\n @submit=\"handleFormSubmit(section.id, $event)\"\n @cancel=\"handleFormCancel(section.id)\"\n />\n </slot>\n </CollapsibleCard>\n\n <slot\n :sections=\"activeSections\"\n :active-view=\"resolvedActiveView\"\n :values=\"resolvedValues\"\n />\n </div>\n\n <div v-else-if=\"$slots.collapsed\" class=\"mint-sidebar__collapsed\">\n <slot\n name=\"collapsed\"\n :sections=\"activeSections\"\n :expand=\"expandCollapsed\"\n />\n </div>\n\n <!-- Footer slot -->\n <div v-if=\"!collapsedModel && $slots.footer\" class=\"mint-sidebar__footer\">\n <slot name=\"footer\" />\n </div>\n </aside>\n</template>\n\n<style>\n@import '../styles/components/app-sidebar.css';\n</style>\n","<script setup lang=\"ts\">\n/**\n * AppLayout - Page layout shell with topbar, sidebar, and main content slots\n *\n * Provides a responsive application layout structure with optional topbar and sidebar.\n * The sidebar slot is a simple pass-through on desktop. When responsiveSidebar is\n * enabled, AppLayout owns the mobile toggle/backdrop shell around the sidebar.\n *\n * @example\n * ```vue\n * <AppLayout sidebar-position=\"right\" floating>\n * <template #topbar>\n * <AppTopBar title=\"My App\" />\n * </template>\n * <template #sidebar>\n * <AppSidebar :panels=\"toolPanels\" :active-view=\"activeTab\">\n * <template #section-params>...</template>\n * </AppSidebar>\n * </template>\n * <main-content />\n * </AppLayout>\n * ```\n */\nimport { computed, ref } from 'vue'\n\ninterface Props {\n /** Position of sidebar (left or right side of screen) */\n sidebarPosition?: 'left' | 'right'\n /** Width of sidebar (use 'auto' to fit content) */\n sidebarWidth?: string\n /** When true, topbar/sidebar/main render as floating cards with gaps */\n floating?: boolean\n /** Convert the sidebar into a mobile overlay with built-in toggle and backdrop below 1024px. */\n responsiveSidebar?: boolean\n /** Controlled mobile sidebar open state. Desktop sidebar remains visible. */\n sidebarOpen?: boolean\n /** Initial mobile sidebar open state when sidebarOpen is uncontrolled. */\n defaultSidebarOpen?: boolean\n /** Accessible label for the mobile sidebar toggle. */\n sidebarToggleLabel?: string\n /** Accessible label used when the mobile sidebar is open. */\n sidebarCloseLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n sidebarPosition: 'left',\n sidebarWidth: 'auto',\n floating: false,\n responsiveSidebar: false,\n sidebarOpen: undefined,\n defaultSidebarOpen: false,\n sidebarToggleLabel: 'Open sidebar',\n sidebarCloseLabel: 'Close sidebar',\n})\n\nconst emit = defineEmits<{\n 'update:sidebarOpen': [value: boolean]\n}>()\n\nconst internalSidebarOpen = ref(props.defaultSidebarOpen)\nconst sidebarOpenModel = computed({\n get: () => props.sidebarOpen ?? internalSidebarOpen.value,\n set: (value: boolean) => {\n internalSidebarOpen.value = value\n emit('update:sidebarOpen', value)\n },\n})\n\nconst layoutClasses = computed(() => [\n 'mint-layout',\n props.sidebarPosition === 'right' ? 'mint-layout--sidebar-right' : '',\n props.floating ? 'mint-layout--floating' : '',\n props.responsiveSidebar ? 'mint-layout--responsive-sidebar' : '',\n sidebarOpenModel.value ? 'mint-layout--sidebar-open' : '',\n])\n\nconst sidebarStyle = computed(() => {\n return props.sidebarWidth !== 'auto' ? { width: props.sidebarWidth } : undefined\n})\n\nfunction toggleSidebar() {\n sidebarOpenModel.value = !sidebarOpenModel.value\n}\n\nfunction closeSidebar() {\n sidebarOpenModel.value = false\n}\n</script>\n\n<template>\n <div :class=\"layoutClasses\">\n <div v-if=\"$slots.topbar\" class=\"mint-layout__topbar\">\n <slot name=\"topbar\" />\n </div>\n\n <div class=\"mint-layout__body\">\n <button\n v-if=\"responsiveSidebar && $slots.sidebar\"\n type=\"button\"\n class=\"mint-layout__sidebar-toggle\"\n :aria-label=\"sidebarOpenModel ? sidebarCloseLabel : sidebarToggleLabel\"\n :aria-expanded=\"sidebarOpenModel\"\n @click=\"toggleSidebar\"\n >\n <svg\n v-if=\"!sidebarOpenModel\"\n class=\"mint-layout__sidebar-toggle-icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M4 6h16M4 12h16M4 18h16\" />\n </svg>\n <svg\n v-else\n class=\"mint-layout__sidebar-toggle-icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M6 18 18 6M6 6l12 12\" />\n </svg>\n </button>\n\n <div\n v-if=\"responsiveSidebar && $slots.sidebar && sidebarOpenModel\"\n class=\"mint-layout__sidebar-backdrop\"\n @click=\"closeSidebar\"\n />\n\n <div\n v-if=\"$slots.sidebar\"\n class=\"mint-layout__sidebar\"\n :style=\"sidebarStyle\"\n >\n <slot name=\"sidebar\" />\n </div>\n\n <main class=\"mint-layout__main\">\n <slot />\n </main>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/app-layout.css';\n</style>\n","<script setup lang=\"ts\">\n/**\n * AppLayout - Page layout shell with topbar, sidebar, and main content slots\n *\n * Provides a responsive application layout structure with optional topbar and sidebar.\n * The sidebar slot is a simple pass-through on desktop. When responsiveSidebar is\n * enabled, AppLayout owns the mobile toggle/backdrop shell around the sidebar.\n *\n * @example\n * ```vue\n * <AppLayout sidebar-position=\"right\" floating>\n * <template #topbar>\n * <AppTopBar title=\"My App\" />\n * </template>\n * <template #sidebar>\n * <AppSidebar :panels=\"toolPanels\" :active-view=\"activeTab\">\n * <template #section-params>...</template>\n * </AppSidebar>\n * </template>\n * <main-content />\n * </AppLayout>\n * ```\n */\nimport { computed, ref } from 'vue'\n\ninterface Props {\n /** Position of sidebar (left or right side of screen) */\n sidebarPosition?: 'left' | 'right'\n /** Width of sidebar (use 'auto' to fit content) */\n sidebarWidth?: string\n /** When true, topbar/sidebar/main render as floating cards with gaps */\n floating?: boolean\n /** Convert the sidebar into a mobile overlay with built-in toggle and backdrop below 1024px. */\n responsiveSidebar?: boolean\n /** Controlled mobile sidebar open state. Desktop sidebar remains visible. */\n sidebarOpen?: boolean\n /** Initial mobile sidebar open state when sidebarOpen is uncontrolled. */\n defaultSidebarOpen?: boolean\n /** Accessible label for the mobile sidebar toggle. */\n sidebarToggleLabel?: string\n /** Accessible label used when the mobile sidebar is open. */\n sidebarCloseLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n sidebarPosition: 'left',\n sidebarWidth: 'auto',\n floating: false,\n responsiveSidebar: false,\n sidebarOpen: undefined,\n defaultSidebarOpen: false,\n sidebarToggleLabel: 'Open sidebar',\n sidebarCloseLabel: 'Close sidebar',\n})\n\nconst emit = defineEmits<{\n 'update:sidebarOpen': [value: boolean]\n}>()\n\nconst internalSidebarOpen = ref(props.defaultSidebarOpen)\nconst sidebarOpenModel = computed({\n get: () => props.sidebarOpen ?? internalSidebarOpen.value,\n set: (value: boolean) => {\n internalSidebarOpen.value = value\n emit('update:sidebarOpen', value)\n },\n})\n\nconst layoutClasses = computed(() => [\n 'mint-layout',\n props.sidebarPosition === 'right' ? 'mint-layout--sidebar-right' : '',\n props.floating ? 'mint-layout--floating' : '',\n props.responsiveSidebar ? 'mint-layout--responsive-sidebar' : '',\n sidebarOpenModel.value ? 'mint-layout--sidebar-open' : '',\n])\n\nconst sidebarStyle = computed(() => {\n return props.sidebarWidth !== 'auto' ? { width: props.sidebarWidth } : undefined\n})\n\nfunction toggleSidebar() {\n sidebarOpenModel.value = !sidebarOpenModel.value\n}\n\nfunction closeSidebar() {\n sidebarOpenModel.value = false\n}\n</script>\n\n<template>\n <div :class=\"layoutClasses\">\n <div v-if=\"$slots.topbar\" class=\"mint-layout__topbar\">\n <slot name=\"topbar\" />\n </div>\n\n <div class=\"mint-layout__body\">\n <button\n v-if=\"responsiveSidebar && $slots.sidebar\"\n type=\"button\"\n class=\"mint-layout__sidebar-toggle\"\n :aria-label=\"sidebarOpenModel ? sidebarCloseLabel : sidebarToggleLabel\"\n :aria-expanded=\"sidebarOpenModel\"\n @click=\"toggleSidebar\"\n >\n <svg\n v-if=\"!sidebarOpenModel\"\n class=\"mint-layout__sidebar-toggle-icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M4 6h16M4 12h16M4 18h16\" />\n </svg>\n <svg\n v-else\n class=\"mint-layout__sidebar-toggle-icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M6 18 18 6M6 6l12 12\" />\n </svg>\n </button>\n\n <div\n v-if=\"responsiveSidebar && $slots.sidebar && sidebarOpenModel\"\n class=\"mint-layout__sidebar-backdrop\"\n @click=\"closeSidebar\"\n />\n\n <div\n v-if=\"$slots.sidebar\"\n class=\"mint-layout__sidebar\"\n :style=\"sidebarStyle\"\n >\n <slot name=\"sidebar\" />\n </div>\n\n <main class=\"mint-layout__main\">\n <slot />\n </main>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/app-layout.css';\n</style>\n","<script setup lang=\"ts\">\n/** Standard plugin workspace shell that combines AppLayout, AppTopBar, and AppSidebar with LEAF-style defaults. */\nimport { computed, reactive, ref, useSlots, watch } from 'vue'\nimport type {\n AccountMenuItem,\n ExperimentSummary,\n PageSelectorItem,\n PageSelectorItemInput,\n PillNavItem,\n PillNavItemInput,\n PluginSwitcherInfo,\n PluginSwitcherPlugin,\n SidebarToolSection,\n TopBarSettingsConfig,\n PillNavOption,\n TopBarVariant,\n} from '../types'\nimport type { FormEnhancements, FormSchema } from '../types/form-builder'\nimport type {\n ControlComponentBinding,\n ControlComponentBindingsById,\n ControlComponentBindingsConfig,\n ControlComponentPropsByIdMap,\n ControlComponentPropsMap,\n ControlModel,\n ControlModelBinding,\n ControlSchema,\n ControlWorkspaceOptions,\n} from '../composables/useControlSchema'\nimport {\n controlValuesToComponentBindings,\n controlValuesToComponentBindingsById,\n controlValuesToComponentProps,\n controlsToViewItems,\n defineControlModel,\n getDefaultControlView,\n getControlDefaults,\n mergeControlWorkspaceOptions,\n} from '../composables/useControlSchema'\nimport {\n useAppExperiment,\n type AppExperimentRecord,\n} from '../composables/useAppExperiment'\nimport AppLayout from './AppLayout.vue'\nimport AppSidebar from './AppSidebar.vue'\nimport AppTopBar from './AppTopBar.vue'\n\ntype SidebarVariant = 'default' | 'analysis'\n\ninterface Props {\n /** App or plugin title shown in the top bar. */\n title?: string\n /** Secondary copy shown under the title. */\n subtitle?: string\n /** Top bar visual treatment. */\n topBarVariant?: TopBarVariant\n /** Home link used by the top bar brand icon. */\n homePath?: string\n /** Show the default MINT logo when no icon/logo slot is provided. */\n showLogo?: boolean\n /** Preferred route-level page selector entries. */\n pageSelector?: PageSelectorItemInput[]\n /** Active page selector id. Defaults to activeView. */\n currentPageSelectorId?: string\n /** Optional plugin switcher shown in the left navigation position. */\n pluginSwitcher?: PluginSwitcherInfo\n /** Preferred centered navigation for local modes inside the current route. */\n pillNav?: PillNavItemInput[]\n /** Active centered pill id. Defaults to activeView. */\n currentPillId?: string\n /** Show the theme toggle button. */\n showThemeToggle?: boolean\n /** Show the settings button and modal. */\n showSettings?: boolean\n /** Built-in settings modal configuration. */\n settingsConfig?: TopBarSettingsConfig\n /** Show the standalone badge when not integrated into the platform. */\n showStandaloneLabel?: boolean\n /** Custom standalone badge label. */\n standaloneLabel?: string\n /** Account dropdown entries. */\n accountMenu?: AccountMenuItem[]\n /** Show the notifications icon button. */\n showNotifications?: boolean\n /** Draw a notification dot on the notifications icon. */\n hasNotificationDot?: boolean\n /** Show the classic admin shortcut. */\n showAdmin?: boolean\n /** Route used by the classic admin shortcut. */\n adminPath?: string\n /** Show the classic profile button when accountMenu is not provided. */\n showProfile?: boolean\n /** Classic profile display name. */\n userName?: string\n /** Explicit classic profile initial. */\n userInitial?: string\n /** Classic profile email. */\n userEmail?: string\n /** Enable the built-in AppTopBar experiment popover and selector flow. */\n experimentShell?: boolean\n /** Current experiment mirrored into the AppTopBar experiment chip when experimentShell is enabled. */\n experiment?: AppExperimentRecord | null\n /** Save handler used by the AppTopBar experiment chip. Return a message to flash success. */\n experimentSave?: () => string | null | Promise<string | null>\n /** Disable the experiment save action. */\n experimentSaveDisabled?: boolean\n /** Tooltip shown when the experiment save action is disabled. */\n experimentSaveDisabledMessage?: string\n\n /** Active workspace view used by top bar navigation and sidebar panels. */\n activeView?: string\n /** Initial active view when activeView is uncontrolled. */\n defaultActiveView?: string\n /** Map of view IDs to sidebar tool sections. */\n panels?: Record<string, SidebarToolSection[]>\n /** Optional FormBuilder schemas keyed by section ID. */\n forms?: Record<string, FormSchema>\n /** Sidebar chrome title. */\n sidebarTitle?: string\n /** Sidebar chrome subtitle. */\n sidebarSubtitle?: string\n /** Optional compact badge/count rendered in the sidebar header. */\n sidebarBadge?: string | number\n /** AppSidebar visual preset. `analysis` matches the LEAF-style MINT analysis sidebar language. */\n sidebarVariant?: SidebarVariant\n /** Sidebar CSS width. Defaults to AppSidebar's variant width. */\n sidebarWidth?: string\n /** Sidebar position in AppLayout. */\n sidebarPosition?: 'left' | 'right'\n /** Convert the sidebar into an SDK-owned mobile overlay below the AppLayout breakpoint. */\n responsiveSidebar?: boolean\n /** Render the sidebar shell even when no panel matches the active view. */\n showSidebarWhenEmpty?: boolean\n /** Disable the built-in sidebar surface entirely. */\n showSidebar?: boolean\n /** DOM id for the AppSidebar scrollable content area. Use with route/tab-owned Teleport sidebars. */\n sidebarContentId?: string\n /** Compact AppSidebar density. */\n dense?: boolean\n /** Show a built-in collapse/expand button in the sidebar chrome. Defaults to AppSidebar's variant behavior. */\n sidebarCollapsible?: boolean\n /** Sidebar width when collapsed. */\n sidebarCollapsedWidth?: string\n /** Accessible label for the sidebar collapse action. */\n sidebarCollapseButtonLabel?: string\n /** Accessible label for the sidebar expand action. */\n sidebarExpandButtonLabel?: string\n /** Floating AppLayout style. */\n floating?: boolean\n /** Optional compact control model for generated sidebar forms. */\n model?: ControlModel | ControlModelBinding\n /** Compact control schema for generated sidebar forms. */\n controls?: ControlSchema\n /** Options passed to compact control schema generation. */\n controlOptions?: ControlWorkspaceOptions\n /** Optional SDK component bindings exposed to the default slot with resolved props. */\n componentBindings?: ControlComponentBindingsConfig\n /** Optional mapping from workspace values to component props exposed to the default slot. */\n componentProps?: ControlComponentPropsMap\n /** Optional named mappings from workspace values to component props exposed to the default slot. */\n componentPropsById?: ControlComponentPropsByIdMap\n /** Shared values for auto-rendered sidebar forms. Supports default v-model. */\n modelValue?: Record<string, unknown>\n /** Shared values for auto-rendered sidebar forms. */\n values?: Record<string, unknown>\n /** Runtime FormBuilder enhancements for auto-rendered sidebar forms. */\n formEnhancements?: FormEnhancements<Record<string, unknown>>\n /** Show submit/cancel actions inside auto-rendered sidebar forms. */\n showFormActions?: boolean\n /** Loading/saving state for auto-rendered sidebar forms. */\n formLoading?: boolean\n /** Disabled state for auto-rendered sidebar forms. */\n formDisabled?: boolean\n /** Readonly state for auto-rendered sidebar forms. */\n formReadonly?: boolean\n /** Size passed to auto-rendered sidebar forms. */\n formSize?: 'sm' | 'md' | 'lg'\n /** Controlled sidebar collapsed state. */\n sidebarCollapsed?: boolean\n /** Initial sidebar collapsed state when uncontrolled. */\n defaultSidebarCollapsed?: boolean\n /** Accessible label for the mobile sidebar toggle. */\n sidebarToggleLabel?: string\n /** Accessible label used when the mobile sidebar is open. */\n sidebarCloseLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: 'Workspace',\n subtitle: undefined,\n topBarVariant: 'card',\n homePath: '/',\n showLogo: true,\n pageSelector: undefined,\n currentPageSelectorId: undefined,\n pluginSwitcher: undefined,\n pillNav: undefined,\n currentPillId: undefined,\n showThemeToggle: false,\n showSettings: false,\n settingsConfig: undefined,\n showStandaloneLabel: true,\n standaloneLabel: 'Standalone',\n accountMenu: undefined,\n showNotifications: false,\n hasNotificationDot: false,\n showAdmin: false,\n adminPath: '/admin',\n showProfile: false,\n userName: undefined,\n userInitial: undefined,\n userEmail: undefined,\n experimentShell: false,\n experiment: undefined,\n experimentSave: undefined,\n experimentSaveDisabled: false,\n experimentSaveDisabledMessage: undefined,\n activeView: undefined,\n defaultActiveView: undefined,\n panels: () => ({}),\n forms: () => ({}),\n sidebarTitle: undefined,\n sidebarSubtitle: undefined,\n sidebarBadge: undefined,\n sidebarVariant: 'analysis',\n sidebarWidth: undefined,\n sidebarPosition: 'left',\n responsiveSidebar: true,\n showSidebarWhenEmpty: false,\n showSidebar: true,\n sidebarContentId: undefined,\n dense: true,\n sidebarCollapsible: undefined,\n sidebarCollapsedWidth: '3rem',\n sidebarCollapseButtonLabel: 'Collapse sidebar',\n sidebarExpandButtonLabel: 'Expand sidebar',\n floating: true,\n model: undefined,\n controls: undefined,\n controlOptions: () => ({}),\n componentBindings: undefined,\n componentProps: undefined,\n componentPropsById: undefined,\n modelValue: undefined,\n values: undefined,\n formEnhancements: undefined,\n showFormActions: false,\n formLoading: false,\n formDisabled: false,\n formReadonly: false,\n formSize: 'sm',\n sidebarCollapsed: undefined,\n defaultSidebarCollapsed: false,\n sidebarToggleLabel: 'Open workspace sidebar',\n sidebarCloseLabel: 'Close workspace sidebar',\n})\n\nconst emit = defineEmits<{\n 'update:activeView': [value: string]\n 'page-selector-select': [page: PageSelectorItem]\n 'pill-select': [item: PillNavItem]\n 'pill-option-select': [option: PillNavOption, item: PillNavItem]\n 'plugin-switcher-select': [plugin: PluginSwitcherPlugin]\n 'plugin-switcher-install': []\n 'account-menu-select': [item: AccountMenuItem]\n 'sign-out': []\n 'notifications-click': []\n 'settings-values-change': [data: Record<string, unknown>]\n 'profile-click': []\n 'admin-click': []\n 'experiment-select': [experiment: ExperimentSummary]\n 'experiment-detach': []\n 'update:modelValue': [values: Record<string, unknown>]\n 'update:values': [values: Record<string, unknown>]\n 'update:sidebarCollapsed': [value: boolean]\n 'update:toggle': [sectionId: string, value: boolean]\n 'form-submit': [sectionId: string, values: Record<string, unknown>]\n 'form-cancel': [sectionId: string]\n}>()\n\ninterface PluginWorkspaceSlotProps {\n activeView: string\n setActiveView: (viewId: string) => void\n values: Record<string, unknown>\n componentBindings: ControlComponentBinding[]\n componentBindingsById: ControlComponentBindingsById\n componentProps: Record<string, unknown>\n componentPropsById: Record<string, Record<string, unknown>>\n}\n\ntype ForwardedSlotProps = Record<string, unknown>\n\ndefineSlots<{\n default?: (props: PluginWorkspaceSlotProps) => unknown\n topbar?: (props: PluginWorkspaceSlotProps) => unknown\n sidebar?: (props: PluginWorkspaceSlotProps) => unknown\n icon?: (props: ForwardedSlotProps) => unknown\n logo?: (props: ForwardedSlotProps) => unknown\n 'page-selector-icon'?: (props: ForwardedSlotProps) => unknown\n 'page-selector-item-icon'?: (props: ForwardedSlotProps) => unknown\n nav?: (props: ForwardedSlotProps) => unknown\n center?: (props: ForwardedSlotProps) => unknown\n actions?: (props: ForwardedSlotProps) => unknown\n 'account-menu-items'?: (props: ForwardedSlotProps) => unknown\n 'account-menu-item-icon'?: (props: ForwardedSlotProps) => unknown\n 'settings-appearance'?: (props: ForwardedSlotProps) => unknown\n header?: (props: ForwardedSlotProps) => unknown\n collapsed?: (props: ForwardedSlotProps) => unknown\n footer?: (props: ForwardedSlotProps) => unknown\n}>()\n\nconst topBarForwardedSlots = new Set([\n 'icon',\n 'logo',\n 'page-selector-icon',\n 'page-selector-item-icon',\n 'nav',\n 'center',\n 'actions',\n 'account-menu-items',\n 'account-menu-item-icon',\n 'settings-appearance',\n])\n\nconst sidebarForwardedSlots = new Set([\n 'header',\n 'collapsed',\n 'footer',\n])\n\nconst slots = useSlots()\nconst internalActiveView = ref(props.defaultActiveView)\n\nif (props.experimentShell) {\n useAppExperiment({\n experiment: computed(() => props.experiment),\n onSelect: experiment => emit('experiment-select', experiment),\n onSave: props.experimentSave,\n onDetach: () => emit('experiment-detach'),\n saveDisabled: computed(() => props.experimentSaveDisabled),\n saveDisabledMessage: computed(() => props.experimentSaveDisabledMessage),\n })\n}\n\nconst topBarSlotNames = computed<string[]>(() =>\n Object.keys(slots).filter(name =>\n topBarForwardedSlots.has(name) || name.startsWith('settings-tab-'),\n ),\n)\n\nconst sidebarSlotNames = computed<string[]>(() =>\n Object.keys(slots).filter(name =>\n sidebarForwardedSlots.has(name) || name.startsWith('section-'),\n ),\n)\n\nconst resolvedModel = computed<ControlModelBinding | undefined>(() => {\n if (props.model === undefined) return undefined\n return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)\n})\n\nconst resolvedControls = computed<ControlSchema | undefined>(() =>\n props.controls ?? resolvedModel.value?.controls,\n)\n\nconst resolvedControlOptions = computed<ControlWorkspaceOptions>(() =>\n mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, props.controlOptions),\n)\n\nconst externalControlValues = computed(() => props.modelValue ?? props.values)\nconst internalControlValues = reactive<Record<string, unknown>>({})\nlet syncingControlValues = false\n\nconst generatedControlDefaults = computed<Record<string, unknown>>(() => {\n if (!resolvedControls.value) return {}\n return {\n ...getControlDefaults(resolvedControls.value),\n ...(resolvedControlOptions.value.initialValues ?? {}),\n }\n})\n\nsyncResolvedControlValues()\n\nwatch(\n [generatedControlDefaults, externalControlValues],\n syncResolvedControlValues,\n { deep: true },\n)\n\nwatch(\n internalControlValues,\n () => {\n if (syncingControlValues) return\n emitControlValues()\n },\n { deep: true, flush: 'sync' },\n)\n\nconst resolvedComponentBindings = computed(() =>\n controlValuesToComponentBindings(\n internalControlValues,\n props.componentBindings ?? resolvedModel.value?.componentBindings,\n ),\n)\n\nconst resolvedComponentBindingsById = computed(() =>\n controlValuesToComponentBindingsById(\n internalControlValues,\n props.componentBindings ?? resolvedModel.value?.componentBindings,\n ),\n)\n\nconst resolvedComponentProps = computed(() => {\n const mapping = props.componentProps ?? resolvedModel.value?.componentProps\n return mapping === undefined ? {} : controlValuesToComponentProps(internalControlValues, mapping)\n})\n\nconst resolvedComponentPropsById = computed(() => {\n const mappings = props.componentPropsById ?? resolvedModel.value?.componentPropsById\n if (mappings === undefined) return {}\n\n return Object.fromEntries(\n Object.entries(mappings).map(([id, mapping]) => [\n id,\n controlValuesToComponentProps(internalControlValues, mapping),\n ]),\n )\n})\n\nconst generatedPillNav = computed<PillNavItem[]>(() => {\n if (!resolvedControls.value) return []\n return controlsToViewItems(resolvedControls.value, resolvedControlOptions.value)\n})\n\nconst generatedDefaultView = computed(() => {\n if (!resolvedControls.value) return ''\n return getDefaultControlView(resolvedControls.value, resolvedControlOptions.value)\n})\n\nconst resolvedPillNav = computed<PillNavItemInput[] | undefined>(() => {\n if (props.pillNav !== undefined) return props.pillNav\n return generatedPillNav.value.length > 0 ? generatedPillNav.value : undefined\n})\n\nconst firstConfiguredViewId = computed(() =>\n props.defaultActiveView\n ?? firstItemId(props.pillNav)\n ?? generatedDefaultView.value\n ?? firstItemId(props.pageSelector)\n ?? Object.keys(props.panels)[0]\n ?? '',\n)\n\nconst resolvedActiveView = computed({\n get: () =>\n props.activeView\n ?? props.currentPillId\n ?? props.currentPageSelectorId\n ?? internalActiveView.value\n ?? firstConfiguredViewId.value,\n set: (value: string) => {\n internalActiveView.value = value\n emit('update:activeView', value)\n },\n})\n\nconst resolvedCurrentPillId = computed(() => props.currentPillId ?? resolvedActiveView.value)\nconst resolvedCurrentPageSelectorId = computed(() => props.currentPageSelectorId ?? resolvedActiveView.value)\n\nconst hasSidebarSurface = computed(() => {\n if (!props.showSidebar) return false\n return (\n Object.keys(props.panels).length > 0\n || Object.keys(props.forms).length > 0\n || props.model !== undefined\n || props.controls !== undefined\n || props.showSidebarWhenEmpty\n || props.sidebarContentId !== undefined\n || props.sidebarTitle !== undefined\n || props.sidebarSubtitle !== undefined\n || props.sidebarBadge !== undefined\n || Boolean(slots.sidebar)\n || sidebarSlotNames.value.length > 0\n )\n})\n\nfunction firstItemId(items?: readonly (string | { id: string })[]): string | undefined {\n const item = items?.[0]\n if (item === undefined) return undefined\n return typeof item === 'string' ? item : item.id\n}\n\nfunction isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {\n return 'controls' in model && 'controlOptions' in model\n}\n\nfunction setActiveView(viewId: string) {\n resolvedActiveView.value = viewId\n}\n\nfunction handlePageSelectorSelect(page: PageSelectorItem) {\n setActiveView(page.id)\n emit('page-selector-select', page)\n}\n\nfunction handlePillSelect(item: PillNavItem) {\n setActiveView(item.id)\n emit('pill-select', item)\n}\n\nfunction handlePillOptionSelect(option: PillNavOption, item: PillNavItem) {\n emit('pill-option-select', option, item)\n}\n\nfunction handleToggle(sectionId: string, value: boolean) {\n emit('update:toggle', sectionId, value)\n}\n\nfunction handleControlValuesUpdate(values: Record<string, unknown>) {\n syncingControlValues = true\n replaceControlValues({\n ...generatedControlDefaults.value,\n ...values,\n })\n syncingControlValues = false\n emitControlValues()\n}\n\nfunction handleFormSubmit(sectionId: string, values: Record<string, unknown>) {\n emit('form-submit', sectionId, values)\n}\n\nfunction handleFormCancel(sectionId: string) {\n emit('form-cancel', sectionId)\n}\n\nfunction syncResolvedControlValues() {\n syncingControlValues = true\n replaceControlValues({\n ...generatedControlDefaults.value,\n ...(externalControlValues.value === undefined ? internalControlValues : externalControlValues.value),\n })\n syncingControlValues = false\n}\n\nfunction replaceControlValues(values: Record<string, unknown>) {\n for (const key of Object.keys(internalControlValues)) {\n if (!(key in values)) {\n delete internalControlValues[key]\n }\n }\n Object.assign(internalControlValues, values)\n}\n\nfunction emitControlValues() {\n const nextValues = { ...internalControlValues }\n emit('update:modelValue', nextValues)\n emit('update:values', nextValues)\n}\n\n</script>\n\n<template>\n <AppLayout\n class=\"mint-plugin-workspace-view\"\n :sidebar-position=\"sidebarPosition\"\n :sidebar-width=\"sidebarWidth\"\n :floating=\"floating\"\n :responsive-sidebar=\"responsiveSidebar\"\n :sidebar-toggle-label=\"sidebarToggleLabel\"\n :sidebar-close-label=\"sidebarCloseLabel\"\n >\n <template #topbar>\n <slot\n name=\"topbar\"\n :active-view=\"resolvedActiveView\"\n :set-active-view=\"setActiveView\"\n :values=\"internalControlValues\"\n :component-bindings=\"resolvedComponentBindings\"\n :component-bindings-by-id=\"resolvedComponentBindingsById\"\n :component-props=\"resolvedComponentProps\"\n :component-props-by-id=\"resolvedComponentPropsById\"\n >\n <AppTopBar\n :title=\"title\"\n :subtitle=\"subtitle\"\n :variant=\"topBarVariant\"\n :home-path=\"homePath\"\n :show-logo=\"showLogo\"\n :page-selector=\"pageSelector\"\n :current-page-selector-id=\"resolvedCurrentPageSelectorId\"\n :plugin-switcher=\"pluginSwitcher\"\n :pill-nav=\"resolvedPillNav\"\n :current-pill-id=\"resolvedCurrentPillId\"\n :show-theme-toggle=\"showThemeToggle\"\n :show-settings=\"showSettings\"\n :settings-config=\"settingsConfig\"\n :show-standalone-label=\"showStandaloneLabel\"\n :standalone-label=\"standaloneLabel\"\n :account-menu=\"accountMenu\"\n :show-notifications=\"showNotifications\"\n :has-notification-dot=\"hasNotificationDot\"\n :show-admin=\"showAdmin\"\n :admin-path=\"adminPath\"\n :show-profile=\"showProfile\"\n :user-name=\"userName\"\n :user-initial=\"userInitial\"\n :user-email=\"userEmail\"\n @page-selector-select=\"handlePageSelectorSelect\"\n @pill-select=\"handlePillSelect\"\n @pill-option-select=\"handlePillOptionSelect\"\n @plugin-switcher-select=\"emit('plugin-switcher-select', $event)\"\n @plugin-switcher-install=\"emit('plugin-switcher-install')\"\n @account-menu-select=\"emit('account-menu-select', $event)\"\n @sign-out=\"emit('sign-out')\"\n @notifications-click=\"emit('notifications-click')\"\n @settings-values-change=\"emit('settings-values-change', $event)\"\n @profile-click=\"emit('profile-click')\"\n @admin-click=\"emit('admin-click')\"\n >\n <template\n v-for=\"slotName in topBarSlotNames\"\n :key=\"slotName\"\n #[slotName]=\"slotProps\"\n >\n <!-- @vue-ignore dynamic SDK slot forwarding for AppTopBar passthrough slots -->\n <slot\n :name=\"slotName\"\n v-bind=\"slotProps ?? {}\"\n />\n </template>\n </AppTopBar>\n </slot>\n </template>\n\n <template v-if=\"hasSidebarSurface\" #sidebar>\n <slot\n name=\"sidebar\"\n :active-view=\"resolvedActiveView\"\n :set-active-view=\"setActiveView\"\n :values=\"internalControlValues\"\n :component-bindings=\"resolvedComponentBindings\"\n :component-bindings-by-id=\"resolvedComponentBindingsById\"\n :component-props=\"resolvedComponentProps\"\n :component-props-by-id=\"resolvedComponentPropsById\"\n >\n <AppSidebar\n :title=\"sidebarTitle\"\n :subtitle=\"sidebarSubtitle\"\n :badge=\"sidebarBadge\"\n :variant=\"sidebarVariant\"\n :panels=\"panels\"\n :active-view=\"resolvedActiveView\"\n :floating=\"false\"\n :dense=\"dense\"\n :width=\"sidebarWidth\"\n :side=\"sidebarPosition\"\n :forms=\"forms\"\n :content-id=\"sidebarContentId\"\n :show-when-empty=\"showSidebarWhenEmpty || sidebarContentId !== undefined\"\n :model=\"model\"\n :controls=\"controls\"\n :control-options=\"controlOptions\"\n :values=\"internalControlValues\"\n :form-enhancements=\"formEnhancements\"\n :show-form-actions=\"showFormActions\"\n :form-loading=\"formLoading\"\n :form-disabled=\"formDisabled\"\n :form-readonly=\"formReadonly\"\n :form-size=\"formSize\"\n :collapsible=\"sidebarCollapsible\"\n :collapsed=\"sidebarCollapsed\"\n :default-collapsed=\"defaultSidebarCollapsed\"\n :collapsed-width=\"sidebarCollapsedWidth\"\n :collapse-button-label=\"sidebarCollapseButtonLabel\"\n :expand-button-label=\"sidebarExpandButtonLabel\"\n @update:model-value=\"handleControlValuesUpdate\"\n @update:collapsed=\"emit('update:sidebarCollapsed', $event)\"\n @update:toggle=\"handleToggle\"\n @form-submit=\"handleFormSubmit\"\n @form-cancel=\"handleFormCancel\"\n >\n <template\n v-for=\"slotName in sidebarSlotNames\"\n :key=\"slotName\"\n #[slotName]=\"slotProps\"\n >\n <!-- @vue-ignore dynamic SDK slot forwarding for AppSidebar section-* passthrough slots -->\n <slot\n :name=\"slotName\"\n v-bind=\"slotProps ?? {}\"\n />\n </template>\n </AppSidebar>\n </slot>\n </template>\n\n <slot\n :active-view=\"resolvedActiveView\"\n :set-active-view=\"setActiveView\"\n :values=\"internalControlValues\"\n :component-bindings=\"resolvedComponentBindings\"\n :component-bindings-by-id=\"resolvedComponentBindingsById\"\n :component-props=\"resolvedComponentProps\"\n :component-props-by-id=\"resolvedComponentPropsById\"\n />\n </AppLayout>\n</template>\n","<script setup lang=\"ts\">\n/** Standard plugin workspace shell that combines AppLayout, AppTopBar, and AppSidebar with LEAF-style defaults. */\nimport { computed, reactive, ref, useSlots, watch } from 'vue'\nimport type {\n AccountMenuItem,\n ExperimentSummary,\n PageSelectorItem,\n PageSelectorItemInput,\n PillNavItem,\n PillNavItemInput,\n PluginSwitcherInfo,\n PluginSwitcherPlugin,\n SidebarToolSection,\n TopBarSettingsConfig,\n PillNavOption,\n TopBarVariant,\n} from '../types'\nimport type { FormEnhancements, FormSchema } from '../types/form-builder'\nimport type {\n ControlComponentBinding,\n ControlComponentBindingsById,\n ControlComponentBindingsConfig,\n ControlComponentPropsByIdMap,\n ControlComponentPropsMap,\n ControlModel,\n ControlModelBinding,\n ControlSchema,\n ControlWorkspaceOptions,\n} from '../composables/useControlSchema'\nimport {\n controlValuesToComponentBindings,\n controlValuesToComponentBindingsById,\n controlValuesToComponentProps,\n controlsToViewItems,\n defineControlModel,\n getDefaultControlView,\n getControlDefaults,\n mergeControlWorkspaceOptions,\n} from '../composables/useControlSchema'\nimport {\n useAppExperiment,\n type AppExperimentRecord,\n} from '../composables/useAppExperiment'\nimport AppLayout from './AppLayout.vue'\nimport AppSidebar from './AppSidebar.vue'\nimport AppTopBar from './AppTopBar.vue'\n\ntype SidebarVariant = 'default' | 'analysis'\n\ninterface Props {\n /** App or plugin title shown in the top bar. */\n title?: string\n /** Secondary copy shown under the title. */\n subtitle?: string\n /** Top bar visual treatment. */\n topBarVariant?: TopBarVariant\n /** Home link used by the top bar brand icon. */\n homePath?: string\n /** Show the default MINT logo when no icon/logo slot is provided. */\n showLogo?: boolean\n /** Preferred route-level page selector entries. */\n pageSelector?: PageSelectorItemInput[]\n /** Active page selector id. Defaults to activeView. */\n currentPageSelectorId?: string\n /** Optional plugin switcher shown in the left navigation position. */\n pluginSwitcher?: PluginSwitcherInfo\n /** Preferred centered navigation for local modes inside the current route. */\n pillNav?: PillNavItemInput[]\n /** Active centered pill id. Defaults to activeView. */\n currentPillId?: string\n /** Show the theme toggle button. */\n showThemeToggle?: boolean\n /** Show the settings button and modal. */\n showSettings?: boolean\n /** Built-in settings modal configuration. */\n settingsConfig?: TopBarSettingsConfig\n /** Show the standalone badge when not integrated into the platform. */\n showStandaloneLabel?: boolean\n /** Custom standalone badge label. */\n standaloneLabel?: string\n /** Account dropdown entries. */\n accountMenu?: AccountMenuItem[]\n /** Show the notifications icon button. */\n showNotifications?: boolean\n /** Draw a notification dot on the notifications icon. */\n hasNotificationDot?: boolean\n /** Show the classic admin shortcut. */\n showAdmin?: boolean\n /** Route used by the classic admin shortcut. */\n adminPath?: string\n /** Show the classic profile button when accountMenu is not provided. */\n showProfile?: boolean\n /** Classic profile display name. */\n userName?: string\n /** Explicit classic profile initial. */\n userInitial?: string\n /** Classic profile email. */\n userEmail?: string\n /** Enable the built-in AppTopBar experiment popover and selector flow. */\n experimentShell?: boolean\n /** Current experiment mirrored into the AppTopBar experiment chip when experimentShell is enabled. */\n experiment?: AppExperimentRecord | null\n /** Save handler used by the AppTopBar experiment chip. Return a message to flash success. */\n experimentSave?: () => string | null | Promise<string | null>\n /** Disable the experiment save action. */\n experimentSaveDisabled?: boolean\n /** Tooltip shown when the experiment save action is disabled. */\n experimentSaveDisabledMessage?: string\n\n /** Active workspace view used by top bar navigation and sidebar panels. */\n activeView?: string\n /** Initial active view when activeView is uncontrolled. */\n defaultActiveView?: string\n /** Map of view IDs to sidebar tool sections. */\n panels?: Record<string, SidebarToolSection[]>\n /** Optional FormBuilder schemas keyed by section ID. */\n forms?: Record<string, FormSchema>\n /** Sidebar chrome title. */\n sidebarTitle?: string\n /** Sidebar chrome subtitle. */\n sidebarSubtitle?: string\n /** Optional compact badge/count rendered in the sidebar header. */\n sidebarBadge?: string | number\n /** AppSidebar visual preset. `analysis` matches the LEAF-style MINT analysis sidebar language. */\n sidebarVariant?: SidebarVariant\n /** Sidebar CSS width. Defaults to AppSidebar's variant width. */\n sidebarWidth?: string\n /** Sidebar position in AppLayout. */\n sidebarPosition?: 'left' | 'right'\n /** Convert the sidebar into an SDK-owned mobile overlay below the AppLayout breakpoint. */\n responsiveSidebar?: boolean\n /** Render the sidebar shell even when no panel matches the active view. */\n showSidebarWhenEmpty?: boolean\n /** Disable the built-in sidebar surface entirely. */\n showSidebar?: boolean\n /** DOM id for the AppSidebar scrollable content area. Use with route/tab-owned Teleport sidebars. */\n sidebarContentId?: string\n /** Compact AppSidebar density. */\n dense?: boolean\n /** Show a built-in collapse/expand button in the sidebar chrome. Defaults to AppSidebar's variant behavior. */\n sidebarCollapsible?: boolean\n /** Sidebar width when collapsed. */\n sidebarCollapsedWidth?: string\n /** Accessible label for the sidebar collapse action. */\n sidebarCollapseButtonLabel?: string\n /** Accessible label for the sidebar expand action. */\n sidebarExpandButtonLabel?: string\n /** Floating AppLayout style. */\n floating?: boolean\n /** Optional compact control model for generated sidebar forms. */\n model?: ControlModel | ControlModelBinding\n /** Compact control schema for generated sidebar forms. */\n controls?: ControlSchema\n /** Options passed to compact control schema generation. */\n controlOptions?: ControlWorkspaceOptions\n /** Optional SDK component bindings exposed to the default slot with resolved props. */\n componentBindings?: ControlComponentBindingsConfig\n /** Optional mapping from workspace values to component props exposed to the default slot. */\n componentProps?: ControlComponentPropsMap\n /** Optional named mappings from workspace values to component props exposed to the default slot. */\n componentPropsById?: ControlComponentPropsByIdMap\n /** Shared values for auto-rendered sidebar forms. Supports default v-model. */\n modelValue?: Record<string, unknown>\n /** Shared values for auto-rendered sidebar forms. */\n values?: Record<string, unknown>\n /** Runtime FormBuilder enhancements for auto-rendered sidebar forms. */\n formEnhancements?: FormEnhancements<Record<string, unknown>>\n /** Show submit/cancel actions inside auto-rendered sidebar forms. */\n showFormActions?: boolean\n /** Loading/saving state for auto-rendered sidebar forms. */\n formLoading?: boolean\n /** Disabled state for auto-rendered sidebar forms. */\n formDisabled?: boolean\n /** Readonly state for auto-rendered sidebar forms. */\n formReadonly?: boolean\n /** Size passed to auto-rendered sidebar forms. */\n formSize?: 'sm' | 'md' | 'lg'\n /** Controlled sidebar collapsed state. */\n sidebarCollapsed?: boolean\n /** Initial sidebar collapsed state when uncontrolled. */\n defaultSidebarCollapsed?: boolean\n /** Accessible label for the mobile sidebar toggle. */\n sidebarToggleLabel?: string\n /** Accessible label used when the mobile sidebar is open. */\n sidebarCloseLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: 'Workspace',\n subtitle: undefined,\n topBarVariant: 'card',\n homePath: '/',\n showLogo: true,\n pageSelector: undefined,\n currentPageSelectorId: undefined,\n pluginSwitcher: undefined,\n pillNav: undefined,\n currentPillId: undefined,\n showThemeToggle: false,\n showSettings: false,\n settingsConfig: undefined,\n showStandaloneLabel: true,\n standaloneLabel: 'Standalone',\n accountMenu: undefined,\n showNotifications: false,\n hasNotificationDot: false,\n showAdmin: false,\n adminPath: '/admin',\n showProfile: false,\n userName: undefined,\n userInitial: undefined,\n userEmail: undefined,\n experimentShell: false,\n experiment: undefined,\n experimentSave: undefined,\n experimentSaveDisabled: false,\n experimentSaveDisabledMessage: undefined,\n activeView: undefined,\n defaultActiveView: undefined,\n panels: () => ({}),\n forms: () => ({}),\n sidebarTitle: undefined,\n sidebarSubtitle: undefined,\n sidebarBadge: undefined,\n sidebarVariant: 'analysis',\n sidebarWidth: undefined,\n sidebarPosition: 'left',\n responsiveSidebar: true,\n showSidebarWhenEmpty: false,\n showSidebar: true,\n sidebarContentId: undefined,\n dense: true,\n sidebarCollapsible: undefined,\n sidebarCollapsedWidth: '3rem',\n sidebarCollapseButtonLabel: 'Collapse sidebar',\n sidebarExpandButtonLabel: 'Expand sidebar',\n floating: true,\n model: undefined,\n controls: undefined,\n controlOptions: () => ({}),\n componentBindings: undefined,\n componentProps: undefined,\n componentPropsById: undefined,\n modelValue: undefined,\n values: undefined,\n formEnhancements: undefined,\n showFormActions: false,\n formLoading: false,\n formDisabled: false,\n formReadonly: false,\n formSize: 'sm',\n sidebarCollapsed: undefined,\n defaultSidebarCollapsed: false,\n sidebarToggleLabel: 'Open workspace sidebar',\n sidebarCloseLabel: 'Close workspace sidebar',\n})\n\nconst emit = defineEmits<{\n 'update:activeView': [value: string]\n 'page-selector-select': [page: PageSelectorItem]\n 'pill-select': [item: PillNavItem]\n 'pill-option-select': [option: PillNavOption, item: PillNavItem]\n 'plugin-switcher-select': [plugin: PluginSwitcherPlugin]\n 'plugin-switcher-install': []\n 'account-menu-select': [item: AccountMenuItem]\n 'sign-out': []\n 'notifications-click': []\n 'settings-values-change': [data: Record<string, unknown>]\n 'profile-click': []\n 'admin-click': []\n 'experiment-select': [experiment: ExperimentSummary]\n 'experiment-detach': []\n 'update:modelValue': [values: Record<string, unknown>]\n 'update:values': [values: Record<string, unknown>]\n 'update:sidebarCollapsed': [value: boolean]\n 'update:toggle': [sectionId: string, value: boolean]\n 'form-submit': [sectionId: string, values: Record<string, unknown>]\n 'form-cancel': [sectionId: string]\n}>()\n\ninterface PluginWorkspaceSlotProps {\n activeView: string\n setActiveView: (viewId: string) => void\n values: Record<string, unknown>\n componentBindings: ControlComponentBinding[]\n componentBindingsById: ControlComponentBindingsById\n componentProps: Record<string, unknown>\n componentPropsById: Record<string, Record<string, unknown>>\n}\n\ntype ForwardedSlotProps = Record<string, unknown>\n\ndefineSlots<{\n default?: (props: PluginWorkspaceSlotProps) => unknown\n topbar?: (props: PluginWorkspaceSlotProps) => unknown\n sidebar?: (props: PluginWorkspaceSlotProps) => unknown\n icon?: (props: ForwardedSlotProps) => unknown\n logo?: (props: ForwardedSlotProps) => unknown\n 'page-selector-icon'?: (props: ForwardedSlotProps) => unknown\n 'page-selector-item-icon'?: (props: ForwardedSlotProps) => unknown\n nav?: (props: ForwardedSlotProps) => unknown\n center?: (props: ForwardedSlotProps) => unknown\n actions?: (props: ForwardedSlotProps) => unknown\n 'account-menu-items'?: (props: ForwardedSlotProps) => unknown\n 'account-menu-item-icon'?: (props: ForwardedSlotProps) => unknown\n 'settings-appearance'?: (props: ForwardedSlotProps) => unknown\n header?: (props: ForwardedSlotProps) => unknown\n collapsed?: (props: ForwardedSlotProps) => unknown\n footer?: (props: ForwardedSlotProps) => unknown\n}>()\n\nconst topBarForwardedSlots = new Set([\n 'icon',\n 'logo',\n 'page-selector-icon',\n 'page-selector-item-icon',\n 'nav',\n 'center',\n 'actions',\n 'account-menu-items',\n 'account-menu-item-icon',\n 'settings-appearance',\n])\n\nconst sidebarForwardedSlots = new Set([\n 'header',\n 'collapsed',\n 'footer',\n])\n\nconst slots = useSlots()\nconst internalActiveView = ref(props.defaultActiveView)\n\nif (props.experimentShell) {\n useAppExperiment({\n experiment: computed(() => props.experiment),\n onSelect: experiment => emit('experiment-select', experiment),\n onSave: props.experimentSave,\n onDetach: () => emit('experiment-detach'),\n saveDisabled: computed(() => props.experimentSaveDisabled),\n saveDisabledMessage: computed(() => props.experimentSaveDisabledMessage),\n })\n}\n\nconst topBarSlotNames = computed<string[]>(() =>\n Object.keys(slots).filter(name =>\n topBarForwardedSlots.has(name) || name.startsWith('settings-tab-'),\n ),\n)\n\nconst sidebarSlotNames = computed<string[]>(() =>\n Object.keys(slots).filter(name =>\n sidebarForwardedSlots.has(name) || name.startsWith('section-'),\n ),\n)\n\nconst resolvedModel = computed<ControlModelBinding | undefined>(() => {\n if (props.model === undefined) return undefined\n return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)\n})\n\nconst resolvedControls = computed<ControlSchema | undefined>(() =>\n props.controls ?? resolvedModel.value?.controls,\n)\n\nconst resolvedControlOptions = computed<ControlWorkspaceOptions>(() =>\n mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, props.controlOptions),\n)\n\nconst externalControlValues = computed(() => props.modelValue ?? props.values)\nconst internalControlValues = reactive<Record<string, unknown>>({})\nlet syncingControlValues = false\n\nconst generatedControlDefaults = computed<Record<string, unknown>>(() => {\n if (!resolvedControls.value) return {}\n return {\n ...getControlDefaults(resolvedControls.value),\n ...(resolvedControlOptions.value.initialValues ?? {}),\n }\n})\n\nsyncResolvedControlValues()\n\nwatch(\n [generatedControlDefaults, externalControlValues],\n syncResolvedControlValues,\n { deep: true },\n)\n\nwatch(\n internalControlValues,\n () => {\n if (syncingControlValues) return\n emitControlValues()\n },\n { deep: true, flush: 'sync' },\n)\n\nconst resolvedComponentBindings = computed(() =>\n controlValuesToComponentBindings(\n internalControlValues,\n props.componentBindings ?? resolvedModel.value?.componentBindings,\n ),\n)\n\nconst resolvedComponentBindingsById = computed(() =>\n controlValuesToComponentBindingsById(\n internalControlValues,\n props.componentBindings ?? resolvedModel.value?.componentBindings,\n ),\n)\n\nconst resolvedComponentProps = computed(() => {\n const mapping = props.componentProps ?? resolvedModel.value?.componentProps\n return mapping === undefined ? {} : controlValuesToComponentProps(internalControlValues, mapping)\n})\n\nconst resolvedComponentPropsById = computed(() => {\n const mappings = props.componentPropsById ?? resolvedModel.value?.componentPropsById\n if (mappings === undefined) return {}\n\n return Object.fromEntries(\n Object.entries(mappings).map(([id, mapping]) => [\n id,\n controlValuesToComponentProps(internalControlValues, mapping),\n ]),\n )\n})\n\nconst generatedPillNav = computed<PillNavItem[]>(() => {\n if (!resolvedControls.value) return []\n return controlsToViewItems(resolvedControls.value, resolvedControlOptions.value)\n})\n\nconst generatedDefaultView = computed(() => {\n if (!resolvedControls.value) return ''\n return getDefaultControlView(resolvedControls.value, resolvedControlOptions.value)\n})\n\nconst resolvedPillNav = computed<PillNavItemInput[] | undefined>(() => {\n if (props.pillNav !== undefined) return props.pillNav\n return generatedPillNav.value.length > 0 ? generatedPillNav.value : undefined\n})\n\nconst firstConfiguredViewId = computed(() =>\n props.defaultActiveView\n ?? firstItemId(props.pillNav)\n ?? generatedDefaultView.value\n ?? firstItemId(props.pageSelector)\n ?? Object.keys(props.panels)[0]\n ?? '',\n)\n\nconst resolvedActiveView = computed({\n get: () =>\n props.activeView\n ?? props.currentPillId\n ?? props.currentPageSelectorId\n ?? internalActiveView.value\n ?? firstConfiguredViewId.value,\n set: (value: string) => {\n internalActiveView.value = value\n emit('update:activeView', value)\n },\n})\n\nconst resolvedCurrentPillId = computed(() => props.currentPillId ?? resolvedActiveView.value)\nconst resolvedCurrentPageSelectorId = computed(() => props.currentPageSelectorId ?? resolvedActiveView.value)\n\nconst hasSidebarSurface = computed(() => {\n if (!props.showSidebar) return false\n return (\n Object.keys(props.panels).length > 0\n || Object.keys(props.forms).length > 0\n || props.model !== undefined\n || props.controls !== undefined\n || props.showSidebarWhenEmpty\n || props.sidebarContentId !== undefined\n || props.sidebarTitle !== undefined\n || props.sidebarSubtitle !== undefined\n || props.sidebarBadge !== undefined\n || Boolean(slots.sidebar)\n || sidebarSlotNames.value.length > 0\n )\n})\n\nfunction firstItemId(items?: readonly (string | { id: string })[]): string | undefined {\n const item = items?.[0]\n if (item === undefined) return undefined\n return typeof item === 'string' ? item : item.id\n}\n\nfunction isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {\n return 'controls' in model && 'controlOptions' in model\n}\n\nfunction setActiveView(viewId: string) {\n resolvedActiveView.value = viewId\n}\n\nfunction handlePageSelectorSelect(page: PageSelectorItem) {\n setActiveView(page.id)\n emit('page-selector-select', page)\n}\n\nfunction handlePillSelect(item: PillNavItem) {\n setActiveView(item.id)\n emit('pill-select', item)\n}\n\nfunction handlePillOptionSelect(option: PillNavOption, item: PillNavItem) {\n emit('pill-option-select', option, item)\n}\n\nfunction handleToggle(sectionId: string, value: boolean) {\n emit('update:toggle', sectionId, value)\n}\n\nfunction handleControlValuesUpdate(values: Record<string, unknown>) {\n syncingControlValues = true\n replaceControlValues({\n ...generatedControlDefaults.value,\n ...values,\n })\n syncingControlValues = false\n emitControlValues()\n}\n\nfunction handleFormSubmit(sectionId: string, values: Record<string, unknown>) {\n emit('form-submit', sectionId, values)\n}\n\nfunction handleFormCancel(sectionId: string) {\n emit('form-cancel', sectionId)\n}\n\nfunction syncResolvedControlValues() {\n syncingControlValues = true\n replaceControlValues({\n ...generatedControlDefaults.value,\n ...(externalControlValues.value === undefined ? internalControlValues : externalControlValues.value),\n })\n syncingControlValues = false\n}\n\nfunction replaceControlValues(values: Record<string, unknown>) {\n for (const key of Object.keys(internalControlValues)) {\n if (!(key in values)) {\n delete internalControlValues[key]\n }\n }\n Object.assign(internalControlValues, values)\n}\n\nfunction emitControlValues() {\n const nextValues = { ...internalControlValues }\n emit('update:modelValue', nextValues)\n emit('update:values', nextValues)\n}\n\n</script>\n\n<template>\n <AppLayout\n class=\"mint-plugin-workspace-view\"\n :sidebar-position=\"sidebarPosition\"\n :sidebar-width=\"sidebarWidth\"\n :floating=\"floating\"\n :responsive-sidebar=\"responsiveSidebar\"\n :sidebar-toggle-label=\"sidebarToggleLabel\"\n :sidebar-close-label=\"sidebarCloseLabel\"\n >\n <template #topbar>\n <slot\n name=\"topbar\"\n :active-view=\"resolvedActiveView\"\n :set-active-view=\"setActiveView\"\n :values=\"internalControlValues\"\n :component-bindings=\"resolvedComponentBindings\"\n :component-bindings-by-id=\"resolvedComponentBindingsById\"\n :component-props=\"resolvedComponentProps\"\n :component-props-by-id=\"resolvedComponentPropsById\"\n >\n <AppTopBar\n :title=\"title\"\n :subtitle=\"subtitle\"\n :variant=\"topBarVariant\"\n :home-path=\"homePath\"\n :show-logo=\"showLogo\"\n :page-selector=\"pageSelector\"\n :current-page-selector-id=\"resolvedCurrentPageSelectorId\"\n :plugin-switcher=\"pluginSwitcher\"\n :pill-nav=\"resolvedPillNav\"\n :current-pill-id=\"resolvedCurrentPillId\"\n :show-theme-toggle=\"showThemeToggle\"\n :show-settings=\"showSettings\"\n :settings-config=\"settingsConfig\"\n :show-standalone-label=\"showStandaloneLabel\"\n :standalone-label=\"standaloneLabel\"\n :account-menu=\"accountMenu\"\n :show-notifications=\"showNotifications\"\n :has-notification-dot=\"hasNotificationDot\"\n :show-admin=\"showAdmin\"\n :admin-path=\"adminPath\"\n :show-profile=\"showProfile\"\n :user-name=\"userName\"\n :user-initial=\"userInitial\"\n :user-email=\"userEmail\"\n @page-selector-select=\"handlePageSelectorSelect\"\n @pill-select=\"handlePillSelect\"\n @pill-option-select=\"handlePillOptionSelect\"\n @plugin-switcher-select=\"emit('plugin-switcher-select', $event)\"\n @plugin-switcher-install=\"emit('plugin-switcher-install')\"\n @account-menu-select=\"emit('account-menu-select', $event)\"\n @sign-out=\"emit('sign-out')\"\n @notifications-click=\"emit('notifications-click')\"\n @settings-values-change=\"emit('settings-values-change', $event)\"\n @profile-click=\"emit('profile-click')\"\n @admin-click=\"emit('admin-click')\"\n >\n <template\n v-for=\"slotName in topBarSlotNames\"\n :key=\"slotName\"\n #[slotName]=\"slotProps\"\n >\n <!-- @vue-ignore dynamic SDK slot forwarding for AppTopBar passthrough slots -->\n <slot\n :name=\"slotName\"\n v-bind=\"slotProps ?? {}\"\n />\n </template>\n </AppTopBar>\n </slot>\n </template>\n\n <template v-if=\"hasSidebarSurface\" #sidebar>\n <slot\n name=\"sidebar\"\n :active-view=\"resolvedActiveView\"\n :set-active-view=\"setActiveView\"\n :values=\"internalControlValues\"\n :component-bindings=\"resolvedComponentBindings\"\n :component-bindings-by-id=\"resolvedComponentBindingsById\"\n :component-props=\"resolvedComponentProps\"\n :component-props-by-id=\"resolvedComponentPropsById\"\n >\n <AppSidebar\n :title=\"sidebarTitle\"\n :subtitle=\"sidebarSubtitle\"\n :badge=\"sidebarBadge\"\n :variant=\"sidebarVariant\"\n :panels=\"panels\"\n :active-view=\"resolvedActiveView\"\n :floating=\"false\"\n :dense=\"dense\"\n :width=\"sidebarWidth\"\n :side=\"sidebarPosition\"\n :forms=\"forms\"\n :content-id=\"sidebarContentId\"\n :show-when-empty=\"showSidebarWhenEmpty || sidebarContentId !== undefined\"\n :model=\"model\"\n :controls=\"controls\"\n :control-options=\"controlOptions\"\n :values=\"internalControlValues\"\n :form-enhancements=\"formEnhancements\"\n :show-form-actions=\"showFormActions\"\n :form-loading=\"formLoading\"\n :form-disabled=\"formDisabled\"\n :form-readonly=\"formReadonly\"\n :form-size=\"formSize\"\n :collapsible=\"sidebarCollapsible\"\n :collapsed=\"sidebarCollapsed\"\n :default-collapsed=\"defaultSidebarCollapsed\"\n :collapsed-width=\"sidebarCollapsedWidth\"\n :collapse-button-label=\"sidebarCollapseButtonLabel\"\n :expand-button-label=\"sidebarExpandButtonLabel\"\n @update:model-value=\"handleControlValuesUpdate\"\n @update:collapsed=\"emit('update:sidebarCollapsed', $event)\"\n @update:toggle=\"handleToggle\"\n @form-submit=\"handleFormSubmit\"\n @form-cancel=\"handleFormCancel\"\n >\n <template\n v-for=\"slotName in sidebarSlotNames\"\n :key=\"slotName\"\n #[slotName]=\"slotProps\"\n >\n <!-- @vue-ignore dynamic SDK slot forwarding for AppSidebar section-* passthrough slots -->\n <slot\n :name=\"slotName\"\n v-bind=\"slotProps ?? {}\"\n />\n </template>\n </AppSidebar>\n </slot>\n </template>\n\n <slot\n :active-view=\"resolvedActiveView\"\n :set-active-view=\"setActiveView\"\n :values=\"internalControlValues\"\n :component-bindings=\"resolvedComponentBindings\"\n :component-bindings-by-id=\"resolvedComponentBindingsById\"\n :component-props=\"resolvedComponentProps\"\n :component-props-by-id=\"resolvedComponentPropsById\"\n />\n </AppLayout>\n</template>\n","<script setup lang=\"ts\">\n/** Tabbed dosing calculator supporting single dilution, serial dilution, and unit conversion, with optional direct assignment to selected plate wells. */\nimport { ref, computed, watch } from 'vue'\nimport ConcentrationInput from './ConcentrationInput.vue'\nimport {\n useConcentrationUnits,\n type ConcentrationValue,\n} from '../composables/useConcentrationUnits'\nimport {\n useDoseCalculator,\n type VolumeValue,\n type VolumeUnit,\n type DilutionResult,\n type SerialDilutionResult,\n type WellConcentration,\n} from '../composables/useDoseCalculator'\n\ntype CalculatorMode = 'dilution' | 'serial' | 'conversion'\n\ninterface Props {\n mode?: CalculatorMode | 'auto'\n molecularWeight?: number\n targetWells?: string[]\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n mode: 'auto',\n disabled: false,\n})\n\nconst emit = defineEmits<{\n 'apply-to-wells': [results: WellConcentration[]]\n 'calculate': [result: DilutionResult | SerialDilutionResult]\n}>()\n\nconst { formatWithUnit } = useConcentrationUnits()\nconst {\n volumeUnits,\n calculateDilution,\n calculateSerialDilution,\n formatVolume,\n generateWellConcentrations,\n} = useDoseCalculator()\n\n// Active mode (for tabbed interface)\nconst activeMode = ref<CalculatorMode>(props.mode === 'auto' ? 'dilution' : props.mode)\n\n// Dilution form state\nconst stockConcentration = ref<ConcentrationValue>({ value: 10, unit: 'mM' })\nconst finalConcentration = ref<ConcentrationValue>({ value: 100, unit: 'µM' })\nconst finalVolume = ref<VolumeValue>({ value: 1, unit: 'mL' })\n\n// Serial dilution form state\nconst serialStartConcentration = ref<ConcentrationValue>({ value: 100, unit: 'µM' })\nconst serialDilutionFactor = ref(3)\nconst serialNumberOfDilutions = ref(8)\nconst serialVolumePerWell = ref<VolumeValue>({ value: 100, unit: 'µL' })\n\n// Results\nconst dilutionResult = ref<DilutionResult | null>(null)\nconst serialResult = ref<SerialDilutionResult | null>(null)\n\n// Computed\nconst showTabs = computed(() => props.mode === 'auto')\n\nconst hasTargetWells = computed(() => {\n return props.targetWells && props.targetWells.length > 0\n})\n\nconst canApplyToWells = computed(() => {\n if (!hasTargetWells.value) return false\n if (activeMode.value === 'serial' && serialResult.value?.valid) return true\n if (activeMode.value === 'dilution' && dilutionResult.value?.valid) return true\n return false\n})\n\n// Calculate dilution result\nfunction recalculateDilution() {\n if (!stockConcentration.value || !finalConcentration.value || !finalVolume.value) {\n dilutionResult.value = null\n return\n }\n\n dilutionResult.value = calculateDilution({\n stockConcentration: stockConcentration.value,\n finalConcentration: finalConcentration.value,\n finalVolume: finalVolume.value,\n })\n\n if (dilutionResult.value.valid) {\n emit('calculate', dilutionResult.value)\n }\n}\n\n// Calculate serial dilution result\nfunction recalculateSerial() {\n if (\n !serialStartConcentration.value ||\n serialDilutionFactor.value <= 1 ||\n serialNumberOfDilutions.value < 1 ||\n !serialVolumePerWell.value\n ) {\n serialResult.value = null\n return\n }\n\n serialResult.value = calculateSerialDilution({\n startingConcentration: serialStartConcentration.value,\n dilutionFactor: serialDilutionFactor.value,\n numberOfDilutions: serialNumberOfDilutions.value,\n volumePerWell: serialVolumePerWell.value,\n })\n\n if (serialResult.value.valid) {\n emit('calculate', serialResult.value)\n }\n}\n\n// Apply to wells\nfunction handleApplyToWells() {\n if (!props.targetWells || props.targetWells.length === 0) return\n\n if (activeMode.value === 'serial' && serialResult.value?.valid) {\n const wellConcentrations = generateWellConcentrations(\n serialResult.value,\n props.targetWells\n )\n emit('apply-to-wells', wellConcentrations)\n }\n\n if (activeMode.value === 'dilution' && dilutionResult.value?.valid) {\n // For simple dilution, apply same concentration to all wells\n const wellConcentrations: WellConcentration[] = props.targetWells.map(wellId => ({\n wellId,\n concentration: finalConcentration.value,\n volume: finalVolume.value,\n }))\n emit('apply-to-wells', wellConcentrations)\n }\n}\n\n// Volume unit change handler\nfunction handleVolumeChange(event: Event, target: 'final' | 'serial') {\n const input = event.target as HTMLInputElement\n const value = parseFloat(input.value)\n if (isNaN(value)) return\n\n if (target === 'final') {\n finalVolume.value = { ...finalVolume.value, value }\n } else {\n serialVolumePerWell.value = { ...serialVolumePerWell.value, value }\n }\n}\n\nfunction handleVolumeUnitChange(event: Event, target: 'final' | 'serial') {\n const select = event.target as HTMLSelectElement\n const unit = select.value as VolumeUnit\n\n if (target === 'final') {\n finalVolume.value = { ...finalVolume.value, unit }\n } else {\n serialVolumePerWell.value = { ...serialVolumePerWell.value, unit }\n }\n}\n\n// Watch for changes and recalculate\nwatch(\n [stockConcentration, finalConcentration, finalVolume],\n () => {\n if (activeMode.value === 'dilution') {\n recalculateDilution()\n }\n },\n { deep: true, immediate: true }\n)\n\nwatch(\n [serialStartConcentration, serialDilutionFactor, serialNumberOfDilutions, serialVolumePerWell],\n () => {\n if (activeMode.value === 'serial') {\n recalculateSerial()\n }\n },\n { deep: true, immediate: true }\n)\n\nwatch(activeMode, (mode) => {\n if (mode === 'dilution') {\n recalculateDilution()\n } else if (mode === 'serial') {\n recalculateSerial()\n }\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-dose-calculator',\n disabled ? 'mint-dose-calculator--disabled' : '',\n ]\"\n >\n <!-- Tabs -->\n <div v-if=\"showTabs\" class=\"mint-dose-calculator__tabs\" role=\"tablist\">\n <button\n type=\"button\"\n :class=\"[\n 'mint-dose-calculator__tab',\n activeMode === 'dilution' ? 'mint-dose-calculator__tab--active' : '',\n ]\"\n role=\"tab\"\n :aria-selected=\"activeMode === 'dilution'\"\n @click=\"activeMode = 'dilution'\"\n >\n Dilution\n </button>\n <button\n type=\"button\"\n :class=\"[\n 'mint-dose-calculator__tab',\n activeMode === 'serial' ? 'mint-dose-calculator__tab--active' : '',\n ]\"\n role=\"tab\"\n :aria-selected=\"activeMode === 'serial'\"\n @click=\"activeMode = 'serial'\"\n >\n Serial\n </button>\n <button\n type=\"button\"\n :class=\"[\n 'mint-dose-calculator__tab',\n activeMode === 'conversion' ? 'mint-dose-calculator__tab--active' : '',\n ]\"\n role=\"tab\"\n :aria-selected=\"activeMode === 'conversion'\"\n @click=\"activeMode = 'conversion'\"\n >\n Conversion\n </button>\n </div>\n\n <!-- Dilution form -->\n <div\n v-if=\"activeMode === 'dilution'\"\n class=\"mint-dose-calculator__form\"\n role=\"tabpanel\"\n >\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Stock Concentration</label>\n <ConcentrationInput\n v-model=\"stockConcentration\"\n :disabled=\"disabled\"\n :show-conversion=\"false\"\n size=\"md\"\n />\n </div>\n\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Final Concentration</label>\n <ConcentrationInput\n v-model=\"finalConcentration\"\n :disabled=\"disabled\"\n :show-conversion=\"false\"\n size=\"md\"\n />\n </div>\n\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Final Volume</label>\n <div class=\"mint-dose-calculator__field-row\">\n <input\n type=\"number\"\n :value=\"finalVolume.value\"\n :disabled=\"disabled\"\n class=\"mint-dose-calculator__input\"\n min=\"0\"\n step=\"any\"\n @input=\"handleVolumeChange($event, 'final')\"\n />\n <select\n :value=\"finalVolume.unit\"\n :disabled=\"disabled\"\n class=\"mint-dose-calculator__select\"\n @change=\"handleVolumeUnitChange($event, 'final')\"\n >\n <option v-for=\"unit in volumeUnits\" :key=\"unit\" :value=\"unit\">\n {{ unit }}\n </option>\n </select>\n </div>\n </div>\n\n <div class=\"mint-dose-calculator__divider\" />\n\n <!-- Dilution Result -->\n <div v-if=\"dilutionResult?.valid\" class=\"mint-dose-calculator__result\">\n <div class=\"mint-dose-calculator__result-title\">Result</div>\n <div class=\"mint-dose-calculator__result-row\">\n <span class=\"mint-dose-calculator__result-label\">Stock volume</span>\n <span class=\"mint-dose-calculator__result-value\">\n {{ formatVolume(dilutionResult.stockVolume) }}\n </span>\n </div>\n <div class=\"mint-dose-calculator__result-row\">\n <span class=\"mint-dose-calculator__result-label\">Diluent volume</span>\n <span class=\"mint-dose-calculator__result-value\">\n {{ formatVolume(dilutionResult.diluentVolume) }}\n </span>\n </div>\n <div class=\"mint-dose-calculator__result-row\">\n <span class=\"mint-dose-calculator__result-label\">Dilution factor</span>\n <span class=\"mint-dose-calculator__result-value\">\n {{ dilutionResult.dilutionFactor.toFixed(1) }}×\n </span>\n </div>\n </div>\n\n <div v-else-if=\"dilutionResult?.error\" class=\"mint-dose-calculator__error\">\n {{ dilutionResult.error }}\n </div>\n </div>\n\n <!-- Serial Dilution form -->\n <div\n v-else-if=\"activeMode === 'serial'\"\n class=\"mint-dose-calculator__form\"\n role=\"tabpanel\"\n >\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Starting Concentration</label>\n <ConcentrationInput\n v-model=\"serialStartConcentration\"\n :disabled=\"disabled\"\n :show-conversion=\"false\"\n size=\"md\"\n />\n </div>\n\n <div class=\"mint-dose-calculator__field-row\">\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Dilution Factor</label>\n <div class=\"mint-dose-calculator__input-wrapper\">\n <input\n v-model.number=\"serialDilutionFactor\"\n type=\"number\"\n :disabled=\"disabled\"\n class=\"mint-dose-calculator__input\"\n min=\"1.1\"\n step=\"0.1\"\n />\n <span class=\"mint-dose-calculator__unit-suffix\">×</span>\n </div>\n </div>\n\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Dilutions</label>\n <input\n v-model.number=\"serialNumberOfDilutions\"\n type=\"number\"\n :disabled=\"disabled\"\n class=\"mint-dose-calculator__input\"\n min=\"1\"\n max=\"24\"\n />\n </div>\n </div>\n\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Volume per Well</label>\n <div class=\"mint-dose-calculator__field-row\">\n <input\n type=\"number\"\n :value=\"serialVolumePerWell.value\"\n :disabled=\"disabled\"\n class=\"mint-dose-calculator__input\"\n min=\"0\"\n step=\"any\"\n @input=\"handleVolumeChange($event, 'serial')\"\n />\n <select\n :value=\"serialVolumePerWell.unit\"\n :disabled=\"disabled\"\n class=\"mint-dose-calculator__select\"\n @change=\"handleVolumeUnitChange($event, 'serial')\"\n >\n <option v-for=\"unit in volumeUnits\" :key=\"unit\" :value=\"unit\">\n {{ unit }}\n </option>\n </select>\n </div>\n </div>\n\n <div class=\"mint-dose-calculator__divider\" />\n\n <!-- Serial Preview -->\n <div v-if=\"serialResult?.valid\" class=\"mint-dose-calculator__preview\">\n <div class=\"mint-dose-calculator__preview-title\">Preview</div>\n <div class=\"mint-dose-calculator__preview-steps\">\n <template v-for=\"(step, index) in serialResult.steps\" :key=\"step.stepNumber\">\n <div class=\"mint-dose-calculator__preview-step\">\n <span class=\"mint-dose-calculator__preview-step-num\">{{ step.stepNumber }}:</span>\n <span class=\"mint-dose-calculator__preview-step-conc\">\n {{ formatWithUnit(step.concentration, 3) }}\n </span>\n </div>\n <span\n v-if=\"index < serialResult.steps.length - 1\"\n class=\"mint-dose-calculator__preview-arrow\"\n >\n →\n </span>\n </template>\n </div>\n </div>\n\n <div v-else-if=\"serialResult?.error\" class=\"mint-dose-calculator__error\">\n {{ serialResult.error }}\n </div>\n </div>\n\n <!-- Conversion form -->\n <div\n v-else-if=\"activeMode === 'conversion'\"\n class=\"mint-dose-calculator__form\"\n role=\"tabpanel\"\n >\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Input Concentration</label>\n <ConcentrationInput\n :disabled=\"disabled\"\n :molecular-weight=\"molecularWeight\"\n :show-conversion=\"true\"\n size=\"md\"\n />\n </div>\n\n <div\n v-if=\"!molecularWeight\"\n class=\"mint-dose-calculator__error\"\n >\n Provide molecular weight for mass ↔ molarity conversion\n </div>\n </div>\n\n <!-- Molecular weight section (shown if needed) -->\n <div\n v-if=\"molecularWeight\"\n class=\"mint-dose-calculator__mw-section\"\n >\n <span class=\"mint-dose-calculator__mw-label\">\n Molecular Weight: {{ molecularWeight }} g/mol\n </span>\n </div>\n\n <!-- Apply to wells button -->\n <div v-if=\"hasTargetWells\" class=\"mint-dose-calculator__form\">\n <button\n type=\"button\"\n class=\"mint-dose-calculator__apply-btn\"\n :disabled=\"!canApplyToWells || disabled\"\n @click=\"handleApplyToWells\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n Apply to {{ targetWells?.length }} selected well{{ targetWells?.length === 1 ? '' : 's' }}\n </button>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/dose-calculator.css';\n</style>\n","<script setup lang=\"ts\">\n/** Tabbed dosing calculator supporting single dilution, serial dilution, and unit conversion, with optional direct assignment to selected plate wells. */\nimport { ref, computed, watch } from 'vue'\nimport ConcentrationInput from './ConcentrationInput.vue'\nimport {\n useConcentrationUnits,\n type ConcentrationValue,\n} from '../composables/useConcentrationUnits'\nimport {\n useDoseCalculator,\n type VolumeValue,\n type VolumeUnit,\n type DilutionResult,\n type SerialDilutionResult,\n type WellConcentration,\n} from '../composables/useDoseCalculator'\n\ntype CalculatorMode = 'dilution' | 'serial' | 'conversion'\n\ninterface Props {\n mode?: CalculatorMode | 'auto'\n molecularWeight?: number\n targetWells?: string[]\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n mode: 'auto',\n disabled: false,\n})\n\nconst emit = defineEmits<{\n 'apply-to-wells': [results: WellConcentration[]]\n 'calculate': [result: DilutionResult | SerialDilutionResult]\n}>()\n\nconst { formatWithUnit } = useConcentrationUnits()\nconst {\n volumeUnits,\n calculateDilution,\n calculateSerialDilution,\n formatVolume,\n generateWellConcentrations,\n} = useDoseCalculator()\n\n// Active mode (for tabbed interface)\nconst activeMode = ref<CalculatorMode>(props.mode === 'auto' ? 'dilution' : props.mode)\n\n// Dilution form state\nconst stockConcentration = ref<ConcentrationValue>({ value: 10, unit: 'mM' })\nconst finalConcentration = ref<ConcentrationValue>({ value: 100, unit: 'µM' })\nconst finalVolume = ref<VolumeValue>({ value: 1, unit: 'mL' })\n\n// Serial dilution form state\nconst serialStartConcentration = ref<ConcentrationValue>({ value: 100, unit: 'µM' })\nconst serialDilutionFactor = ref(3)\nconst serialNumberOfDilutions = ref(8)\nconst serialVolumePerWell = ref<VolumeValue>({ value: 100, unit: 'µL' })\n\n// Results\nconst dilutionResult = ref<DilutionResult | null>(null)\nconst serialResult = ref<SerialDilutionResult | null>(null)\n\n// Computed\nconst showTabs = computed(() => props.mode === 'auto')\n\nconst hasTargetWells = computed(() => {\n return props.targetWells && props.targetWells.length > 0\n})\n\nconst canApplyToWells = computed(() => {\n if (!hasTargetWells.value) return false\n if (activeMode.value === 'serial' && serialResult.value?.valid) return true\n if (activeMode.value === 'dilution' && dilutionResult.value?.valid) return true\n return false\n})\n\n// Calculate dilution result\nfunction recalculateDilution() {\n if (!stockConcentration.value || !finalConcentration.value || !finalVolume.value) {\n dilutionResult.value = null\n return\n }\n\n dilutionResult.value = calculateDilution({\n stockConcentration: stockConcentration.value,\n finalConcentration: finalConcentration.value,\n finalVolume: finalVolume.value,\n })\n\n if (dilutionResult.value.valid) {\n emit('calculate', dilutionResult.value)\n }\n}\n\n// Calculate serial dilution result\nfunction recalculateSerial() {\n if (\n !serialStartConcentration.value ||\n serialDilutionFactor.value <= 1 ||\n serialNumberOfDilutions.value < 1 ||\n !serialVolumePerWell.value\n ) {\n serialResult.value = null\n return\n }\n\n serialResult.value = calculateSerialDilution({\n startingConcentration: serialStartConcentration.value,\n dilutionFactor: serialDilutionFactor.value,\n numberOfDilutions: serialNumberOfDilutions.value,\n volumePerWell: serialVolumePerWell.value,\n })\n\n if (serialResult.value.valid) {\n emit('calculate', serialResult.value)\n }\n}\n\n// Apply to wells\nfunction handleApplyToWells() {\n if (!props.targetWells || props.targetWells.length === 0) return\n\n if (activeMode.value === 'serial' && serialResult.value?.valid) {\n const wellConcentrations = generateWellConcentrations(\n serialResult.value,\n props.targetWells\n )\n emit('apply-to-wells', wellConcentrations)\n }\n\n if (activeMode.value === 'dilution' && dilutionResult.value?.valid) {\n // For simple dilution, apply same concentration to all wells\n const wellConcentrations: WellConcentration[] = props.targetWells.map(wellId => ({\n wellId,\n concentration: finalConcentration.value,\n volume: finalVolume.value,\n }))\n emit('apply-to-wells', wellConcentrations)\n }\n}\n\n// Volume unit change handler\nfunction handleVolumeChange(event: Event, target: 'final' | 'serial') {\n const input = event.target as HTMLInputElement\n const value = parseFloat(input.value)\n if (isNaN(value)) return\n\n if (target === 'final') {\n finalVolume.value = { ...finalVolume.value, value }\n } else {\n serialVolumePerWell.value = { ...serialVolumePerWell.value, value }\n }\n}\n\nfunction handleVolumeUnitChange(event: Event, target: 'final' | 'serial') {\n const select = event.target as HTMLSelectElement\n const unit = select.value as VolumeUnit\n\n if (target === 'final') {\n finalVolume.value = { ...finalVolume.value, unit }\n } else {\n serialVolumePerWell.value = { ...serialVolumePerWell.value, unit }\n }\n}\n\n// Watch for changes and recalculate\nwatch(\n [stockConcentration, finalConcentration, finalVolume],\n () => {\n if (activeMode.value === 'dilution') {\n recalculateDilution()\n }\n },\n { deep: true, immediate: true }\n)\n\nwatch(\n [serialStartConcentration, serialDilutionFactor, serialNumberOfDilutions, serialVolumePerWell],\n () => {\n if (activeMode.value === 'serial') {\n recalculateSerial()\n }\n },\n { deep: true, immediate: true }\n)\n\nwatch(activeMode, (mode) => {\n if (mode === 'dilution') {\n recalculateDilution()\n } else if (mode === 'serial') {\n recalculateSerial()\n }\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-dose-calculator',\n disabled ? 'mint-dose-calculator--disabled' : '',\n ]\"\n >\n <!-- Tabs -->\n <div v-if=\"showTabs\" class=\"mint-dose-calculator__tabs\" role=\"tablist\">\n <button\n type=\"button\"\n :class=\"[\n 'mint-dose-calculator__tab',\n activeMode === 'dilution' ? 'mint-dose-calculator__tab--active' : '',\n ]\"\n role=\"tab\"\n :aria-selected=\"activeMode === 'dilution'\"\n @click=\"activeMode = 'dilution'\"\n >\n Dilution\n </button>\n <button\n type=\"button\"\n :class=\"[\n 'mint-dose-calculator__tab',\n activeMode === 'serial' ? 'mint-dose-calculator__tab--active' : '',\n ]\"\n role=\"tab\"\n :aria-selected=\"activeMode === 'serial'\"\n @click=\"activeMode = 'serial'\"\n >\n Serial\n </button>\n <button\n type=\"button\"\n :class=\"[\n 'mint-dose-calculator__tab',\n activeMode === 'conversion' ? 'mint-dose-calculator__tab--active' : '',\n ]\"\n role=\"tab\"\n :aria-selected=\"activeMode === 'conversion'\"\n @click=\"activeMode = 'conversion'\"\n >\n Conversion\n </button>\n </div>\n\n <!-- Dilution form -->\n <div\n v-if=\"activeMode === 'dilution'\"\n class=\"mint-dose-calculator__form\"\n role=\"tabpanel\"\n >\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Stock Concentration</label>\n <ConcentrationInput\n v-model=\"stockConcentration\"\n :disabled=\"disabled\"\n :show-conversion=\"false\"\n size=\"md\"\n />\n </div>\n\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Final Concentration</label>\n <ConcentrationInput\n v-model=\"finalConcentration\"\n :disabled=\"disabled\"\n :show-conversion=\"false\"\n size=\"md\"\n />\n </div>\n\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Final Volume</label>\n <div class=\"mint-dose-calculator__field-row\">\n <input\n type=\"number\"\n :value=\"finalVolume.value\"\n :disabled=\"disabled\"\n class=\"mint-dose-calculator__input\"\n min=\"0\"\n step=\"any\"\n @input=\"handleVolumeChange($event, 'final')\"\n />\n <select\n :value=\"finalVolume.unit\"\n :disabled=\"disabled\"\n class=\"mint-dose-calculator__select\"\n @change=\"handleVolumeUnitChange($event, 'final')\"\n >\n <option v-for=\"unit in volumeUnits\" :key=\"unit\" :value=\"unit\">\n {{ unit }}\n </option>\n </select>\n </div>\n </div>\n\n <div class=\"mint-dose-calculator__divider\" />\n\n <!-- Dilution Result -->\n <div v-if=\"dilutionResult?.valid\" class=\"mint-dose-calculator__result\">\n <div class=\"mint-dose-calculator__result-title\">Result</div>\n <div class=\"mint-dose-calculator__result-row\">\n <span class=\"mint-dose-calculator__result-label\">Stock volume</span>\n <span class=\"mint-dose-calculator__result-value\">\n {{ formatVolume(dilutionResult.stockVolume) }}\n </span>\n </div>\n <div class=\"mint-dose-calculator__result-row\">\n <span class=\"mint-dose-calculator__result-label\">Diluent volume</span>\n <span class=\"mint-dose-calculator__result-value\">\n {{ formatVolume(dilutionResult.diluentVolume) }}\n </span>\n </div>\n <div class=\"mint-dose-calculator__result-row\">\n <span class=\"mint-dose-calculator__result-label\">Dilution factor</span>\n <span class=\"mint-dose-calculator__result-value\">\n {{ dilutionResult.dilutionFactor.toFixed(1) }}×\n </span>\n </div>\n </div>\n\n <div v-else-if=\"dilutionResult?.error\" class=\"mint-dose-calculator__error\">\n {{ dilutionResult.error }}\n </div>\n </div>\n\n <!-- Serial Dilution form -->\n <div\n v-else-if=\"activeMode === 'serial'\"\n class=\"mint-dose-calculator__form\"\n role=\"tabpanel\"\n >\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Starting Concentration</label>\n <ConcentrationInput\n v-model=\"serialStartConcentration\"\n :disabled=\"disabled\"\n :show-conversion=\"false\"\n size=\"md\"\n />\n </div>\n\n <div class=\"mint-dose-calculator__field-row\">\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Dilution Factor</label>\n <div class=\"mint-dose-calculator__input-wrapper\">\n <input\n v-model.number=\"serialDilutionFactor\"\n type=\"number\"\n :disabled=\"disabled\"\n class=\"mint-dose-calculator__input\"\n min=\"1.1\"\n step=\"0.1\"\n />\n <span class=\"mint-dose-calculator__unit-suffix\">×</span>\n </div>\n </div>\n\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Dilutions</label>\n <input\n v-model.number=\"serialNumberOfDilutions\"\n type=\"number\"\n :disabled=\"disabled\"\n class=\"mint-dose-calculator__input\"\n min=\"1\"\n max=\"24\"\n />\n </div>\n </div>\n\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Volume per Well</label>\n <div class=\"mint-dose-calculator__field-row\">\n <input\n type=\"number\"\n :value=\"serialVolumePerWell.value\"\n :disabled=\"disabled\"\n class=\"mint-dose-calculator__input\"\n min=\"0\"\n step=\"any\"\n @input=\"handleVolumeChange($event, 'serial')\"\n />\n <select\n :value=\"serialVolumePerWell.unit\"\n :disabled=\"disabled\"\n class=\"mint-dose-calculator__select\"\n @change=\"handleVolumeUnitChange($event, 'serial')\"\n >\n <option v-for=\"unit in volumeUnits\" :key=\"unit\" :value=\"unit\">\n {{ unit }}\n </option>\n </select>\n </div>\n </div>\n\n <div class=\"mint-dose-calculator__divider\" />\n\n <!-- Serial Preview -->\n <div v-if=\"serialResult?.valid\" class=\"mint-dose-calculator__preview\">\n <div class=\"mint-dose-calculator__preview-title\">Preview</div>\n <div class=\"mint-dose-calculator__preview-steps\">\n <template v-for=\"(step, index) in serialResult.steps\" :key=\"step.stepNumber\">\n <div class=\"mint-dose-calculator__preview-step\">\n <span class=\"mint-dose-calculator__preview-step-num\">{{ step.stepNumber }}:</span>\n <span class=\"mint-dose-calculator__preview-step-conc\">\n {{ formatWithUnit(step.concentration, 3) }}\n </span>\n </div>\n <span\n v-if=\"index < serialResult.steps.length - 1\"\n class=\"mint-dose-calculator__preview-arrow\"\n >\n →\n </span>\n </template>\n </div>\n </div>\n\n <div v-else-if=\"serialResult?.error\" class=\"mint-dose-calculator__error\">\n {{ serialResult.error }}\n </div>\n </div>\n\n <!-- Conversion form -->\n <div\n v-else-if=\"activeMode === 'conversion'\"\n class=\"mint-dose-calculator__form\"\n role=\"tabpanel\"\n >\n <div class=\"mint-dose-calculator__field\">\n <label class=\"mint-dose-calculator__field-label\">Input Concentration</label>\n <ConcentrationInput\n :disabled=\"disabled\"\n :molecular-weight=\"molecularWeight\"\n :show-conversion=\"true\"\n size=\"md\"\n />\n </div>\n\n <div\n v-if=\"!molecularWeight\"\n class=\"mint-dose-calculator__error\"\n >\n Provide molecular weight for mass ↔ molarity conversion\n </div>\n </div>\n\n <!-- Molecular weight section (shown if needed) -->\n <div\n v-if=\"molecularWeight\"\n class=\"mint-dose-calculator__mw-section\"\n >\n <span class=\"mint-dose-calculator__mw-label\">\n Molecular Weight: {{ molecularWeight }} g/mol\n </span>\n </div>\n\n <!-- Apply to wells button -->\n <div v-if=\"hasTargetWells\" class=\"mint-dose-calculator__form\">\n <button\n type=\"button\"\n class=\"mint-dose-calculator__apply-btn\"\n :disabled=\"!canApplyToWells || disabled\"\n @click=\"handleApplyToWells\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n Apply to {{ targetWells?.length }} selected well{{ targetWells?.length === 1 ? '' : 's' }}\n </button>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/dose-calculator.css';\n</style>\n","<script setup lang=\"ts\">\n/** Horizontal or vertical timeline of ordered protocol steps with status indicators, drag-to-reorder, and step-type iconography. */\nimport { ref, computed } from 'vue'\nimport type { ProtocolStep, ProtocolStepType, ProtocolStepStatus } from '../types'\n\ninterface Props {\n modelValue?: ProtocolStep[]\n orientation?: 'horizontal' | 'vertical'\n showDuration?: boolean\n showTime?: boolean\n editable?: boolean\n collapsible?: boolean\n expandedStepId?: string\n size?: 'sm' | 'md' | 'lg'\n colorByStatus?: boolean\n colorByType?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: () => [],\n orientation: 'vertical',\n showDuration: true,\n showTime: false,\n editable: false,\n collapsible: true,\n expandedStepId: undefined,\n size: 'md',\n colorByStatus: true,\n colorByType: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [steps: ProtocolStep[]]\n 'step-click': [step: ProtocolStep]\n 'step-expand': [stepId: string | undefined]\n 'step-add': [afterStepId?: string]\n 'step-remove': [stepId: string]\n 'step-update': [step: ProtocolStep]\n 'step-reorder': [stepId: string, newOrder: number]\n 'step-status-change': [stepId: string, status: ProtocolStepStatus]\n}>()\n\nconst localExpandedId = ref<string | undefined>(props.expandedStepId)\nconst draggedStepId = ref<string | null>(null)\nconst dragOverStepId = ref<string | null>(null)\n\nconst expandedId = computed({\n get: () => props.expandedStepId ?? localExpandedId.value,\n set: (id) => {\n localExpandedId.value = id\n emit('step-expand', id)\n },\n})\n\nconst sortedSteps = computed(() => {\n if (props.modelValue.every((step, i, arr) => i === 0 || step.order >= arr[i - 1].order)) {\n return props.modelValue\n }\n return [...props.modelValue].sort((a, b) => a.order - b.order)\n})\n\nconst stepTypeIcons: Record<ProtocolStepType, string> = {\n incubation: 'M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z',\n wash: 'M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z',\n addition: 'M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z',\n measurement: 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z',\n transfer: 'M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4',\n centrifuge: 'M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15',\n mix: 'M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z',\n custom: 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z',\n}\n\nconst stepTypeColors: Record<ProtocolStepType, string> = {\n incubation: '#F59E0B',\n wash: '#06B6D4',\n addition: '#10B981',\n measurement: '#8B5CF6',\n transfer: '#3B82F6',\n centrifuge: '#6B7280',\n mix: '#F97316',\n custom: '#EC4899',\n}\n\nconst statusClasses: Record<ProtocolStepStatus, string> = {\n pending: 'mint-timeline__card--pending',\n in_progress: 'mint-timeline__card--in-progress',\n completed: 'mint-timeline__card--completed',\n failed: 'mint-timeline__card--failed',\n skipped: 'mint-timeline__card--skipped',\n}\n\nconst iconStatusClasses: Record<ProtocolStepStatus, string> = {\n pending: 'mint-timeline__icon--pending',\n in_progress: 'mint-timeline__icon--in-progress',\n completed: 'mint-timeline__icon--completed',\n failed: 'mint-timeline__icon--failed',\n skipped: 'mint-timeline__icon--skipped',\n}\n\nfunction formatDuration(minutes: number | undefined): string {\n if (minutes === undefined) return ''\n if (minutes < 60) return `${minutes}m`\n const hours = Math.floor(minutes / 60)\n const mins = minutes % 60\n return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`\n}\n\nfunction getStepColor(step: ProtocolStep): string {\n return props.colorByType && !props.colorByStatus ? stepTypeColors[step.type] : ''\n}\n\nfunction getStatusClass(step: ProtocolStep): string {\n return props.colorByStatus ? statusClasses[step.status] : statusClasses.pending\n}\n\nfunction getIconStatusClass(step: ProtocolStep): string {\n return props.colorByStatus ? iconStatusClasses[step.status] : iconStatusClasses.pending\n}\n\nfunction toggleExpand(step: ProtocolStep) {\n expandedId.value = expandedId.value === step.id ? undefined : step.id\n}\n\nfunction handleStepClick(step: ProtocolStep) {\n emit('step-click', step)\n if (props.collapsible) {\n toggleExpand(step)\n }\n}\n\nfunction handleStatusChange(step: ProtocolStep, status: ProtocolStepStatus) {\n const updatedSteps = props.modelValue.map(s =>\n s.id === step.id ? { ...s, status } : s\n )\n emit('step-status-change', step.id, status)\n emit('update:modelValue', updatedSteps)\n}\n\nfunction handleRemoveStep(stepId: string) {\n const updatedSteps = props.modelValue\n .filter(s => s.id !== stepId)\n .map((s, i) => ({ ...s, order: i }))\n emit('step-remove', stepId)\n emit('update:modelValue', updatedSteps)\n}\n\nfunction handleAddStep(afterStepId?: string) {\n emit('step-add', afterStepId)\n}\n\nfunction handleDragStart(event: DragEvent, step: ProtocolStep) {\n if (!props.editable) return\n draggedStepId.value = step.id\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', step.id)\n }\n}\n\nfunction handleDragOver(event: DragEvent, step: ProtocolStep) {\n if (!props.editable || !draggedStepId.value) return\n event.preventDefault()\n dragOverStepId.value = step.id\n}\n\nfunction handleDragLeave() {\n dragOverStepId.value = null\n}\n\nfunction handleDrop(event: DragEvent, targetStep: ProtocolStep) {\n if (!props.editable || !draggedStepId.value) return\n event.preventDefault()\n\n const draggedStep = props.modelValue.find(s => s.id === draggedStepId.value)\n if (!draggedStep || draggedStep.id === targetStep.id) {\n draggedStepId.value = null\n dragOverStepId.value = null\n return\n }\n\n const steps = [...props.modelValue]\n const draggedIndex = steps.findIndex(s => s.id === draggedStepId.value)\n const targetIndex = steps.findIndex(s => s.id === targetStep.id)\n\n steps.splice(draggedIndex, 1)\n steps.splice(targetIndex, 0, draggedStep)\n\n const reordered = steps.map((s, i) => ({ ...s, order: i }))\n emit('step-reorder', draggedStepId.value, targetIndex)\n emit('update:modelValue', reordered)\n\n draggedStepId.value = null\n dragOverStepId.value = null\n}\n\nfunction handleDragEnd() {\n draggedStepId.value = null\n dragOverStepId.value = null\n}\n\n</script>\n\n<template>\n <div\n :class=\"['mint-timeline', orientation === 'horizontal' ? 'mint-timeline--horizontal' : 'mint-timeline--vertical']\"\n role=\"list\"\n :aria-label=\"`Protocol timeline with ${sortedSteps.length} steps`\"\n >\n <div\n v-for=\"(step, index) in sortedSteps\"\n :key=\"step.id\"\n class=\"mint-timeline__step\"\n role=\"listitem\"\n :draggable=\"editable\"\n @dragstart=\"handleDragStart($event, step)\"\n @dragover=\"handleDragOver($event, step)\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop($event, step)\"\n @dragend=\"handleDragEnd\"\n >\n <div\n v-if=\"index < sortedSteps.length - 1\"\n class=\"mint-timeline__connector\"\n />\n\n <div\n :class=\"[\n 'mint-timeline__card',\n `mint-timeline__card--${size}`,\n getStatusClass(step),\n dragOverStepId === step.id ? 'mint-timeline__card--drag-over' : '',\n draggedStepId === step.id ? 'mint-timeline__card--dragging' : '',\n editable ? 'mint-timeline__card--editable' : '',\n ]\"\n :style=\"colorByType ? { borderColor: getStepColor(step) } : {}\"\n @click=\"handleStepClick(step)\"\n >\n <div :class=\"['mint-timeline__header', `mint-timeline__header--${size}`]\">\n <div\n :class=\"[\n 'mint-timeline__icon',\n `mint-timeline__icon--${size}`,\n getIconStatusClass(step),\n ]\"\n :style=\"colorByType ? { backgroundColor: `${getStepColor(step)}20`, color: getStepColor(step) } : {}\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" :d=\"stepTypeIcons[step.type]\" />\n </svg>\n </div>\n\n <div class=\"mint-timeline__content\">\n <div class=\"mint-timeline__title-row\">\n <span :class=\"['mint-timeline__title', `mint-timeline__title--${size}`]\">\n {{ step.name }}\n </span>\n <span\n v-if=\"showDuration && step.duration\"\n :class=\"['mint-timeline__duration', size === 'sm' ? 'mint-timeline__duration--sm' : '']\"\n >\n {{ formatDuration(step.duration) }}\n </span>\n </div>\n <div :class=\"['mint-timeline__type', `mint-timeline__type--${size}`]\">\n {{ step.type.replace('_', ' ') }}\n </div>\n </div>\n\n <svg\n v-if=\"collapsible && (step.description || step.parameters)\"\n :class=\"[\n 'mint-timeline__expand-icon',\n expandedId === step.id ? 'mint-timeline__expand-icon--expanded' : '',\n ]\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n\n <button\n v-if=\"editable\"\n type=\"button\"\n class=\"mint-timeline__remove-btn\"\n :aria-label=\"`Remove ${step.name}`\"\n @click.stop=\"handleRemoveStep(step.id)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div\n v-if=\"expandedId === step.id && (step.description || step.parameters)\"\n :aria-expanded=\"expandedId === step.id\"\n class=\"mint-timeline__expanded\"\n >\n <p v-if=\"step.description\" :class=\"['mint-timeline__description', `mint-timeline__description--${size}`]\">\n {{ step.description }}\n </p>\n\n <div v-if=\"step.parameters && Object.keys(step.parameters).length > 0\" class=\"mint-timeline__parameters\">\n <div\n v-for=\"(value, key) in step.parameters\"\n :key=\"String(key)\"\n :class=\"['mint-timeline__param', `mint-timeline__param--${size}`]\"\n >\n <span class=\"mint-timeline__param-key\">{{ key }}:</span>\n <span class=\"mint-timeline__param-value\">{{ value }}</span>\n </div>\n </div>\n\n <div v-if=\"editable\" class=\"mint-timeline__status-controls\">\n <button\n v-for=\"status in (['pending', 'in_progress', 'completed', 'failed', 'skipped'] as const)\"\n :key=\"status\"\n type=\"button\"\n :class=\"[\n 'mint-timeline__status-btn',\n `mint-timeline__status-btn--${status}`,\n step.status === status ? 'mint-timeline__status-btn--active' : '',\n ]\"\n @click.stop=\"handleStatusChange(step, status)\"\n >\n {{ status.replace('_', ' ') }}\n </button>\n </div>\n </div>\n </div>\n\n <button\n v-if=\"editable && orientation === 'vertical'\"\n type=\"button\"\n class=\"mint-timeline__add-inline\"\n :aria-label=\"`Add step after ${step.name}`\"\n @click.stop=\"handleAddStep(step.id)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n </button>\n\n <div v-if=\"orientation === 'vertical' && index < sortedSteps.length - 1\" class=\"mint-timeline__spacer--vertical\" />\n <div v-if=\"orientation === 'horizontal' && index < sortedSteps.length - 1\" class=\"mint-timeline__spacer--horizontal\" />\n </div>\n\n <div\n v-if=\"sortedSteps.length === 0\"\n class=\"mint-timeline__empty\"\n >\n <div class=\"mint-timeline__empty-content\">\n <p class=\"mint-timeline__empty-text\">No protocol steps defined</p>\n <button\n v-if=\"editable\"\n type=\"button\"\n class=\"mint-timeline__empty-btn\"\n @click=\"handleAddStep()\"\n >\n Add First Step\n </button>\n </div>\n </div>\n\n <button\n v-if=\"editable && sortedSteps.length > 0\"\n type=\"button\"\n :class=\"['mint-timeline__add-end', `mint-timeline__add-end--${size}`]\"\n @click=\"handleAddStep()\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n <span :class=\"`mint-timeline__add-end-text--${size}`\">Add Step</span>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/experiment-timeline.css';\n</style>\n","<script setup lang=\"ts\">\n/** Horizontal or vertical timeline of ordered protocol steps with status indicators, drag-to-reorder, and step-type iconography. */\nimport { ref, computed } from 'vue'\nimport type { ProtocolStep, ProtocolStepType, ProtocolStepStatus } from '../types'\n\ninterface Props {\n modelValue?: ProtocolStep[]\n orientation?: 'horizontal' | 'vertical'\n showDuration?: boolean\n showTime?: boolean\n editable?: boolean\n collapsible?: boolean\n expandedStepId?: string\n size?: 'sm' | 'md' | 'lg'\n colorByStatus?: boolean\n colorByType?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: () => [],\n orientation: 'vertical',\n showDuration: true,\n showTime: false,\n editable: false,\n collapsible: true,\n expandedStepId: undefined,\n size: 'md',\n colorByStatus: true,\n colorByType: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [steps: ProtocolStep[]]\n 'step-click': [step: ProtocolStep]\n 'step-expand': [stepId: string | undefined]\n 'step-add': [afterStepId?: string]\n 'step-remove': [stepId: string]\n 'step-update': [step: ProtocolStep]\n 'step-reorder': [stepId: string, newOrder: number]\n 'step-status-change': [stepId: string, status: ProtocolStepStatus]\n}>()\n\nconst localExpandedId = ref<string | undefined>(props.expandedStepId)\nconst draggedStepId = ref<string | null>(null)\nconst dragOverStepId = ref<string | null>(null)\n\nconst expandedId = computed({\n get: () => props.expandedStepId ?? localExpandedId.value,\n set: (id) => {\n localExpandedId.value = id\n emit('step-expand', id)\n },\n})\n\nconst sortedSteps = computed(() => {\n if (props.modelValue.every((step, i, arr) => i === 0 || step.order >= arr[i - 1].order)) {\n return props.modelValue\n }\n return [...props.modelValue].sort((a, b) => a.order - b.order)\n})\n\nconst stepTypeIcons: Record<ProtocolStepType, string> = {\n incubation: 'M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z',\n wash: 'M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z',\n addition: 'M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z',\n measurement: 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z',\n transfer: 'M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4',\n centrifuge: 'M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15',\n mix: 'M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z',\n custom: 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z',\n}\n\nconst stepTypeColors: Record<ProtocolStepType, string> = {\n incubation: '#F59E0B',\n wash: '#06B6D4',\n addition: '#10B981',\n measurement: '#8B5CF6',\n transfer: '#3B82F6',\n centrifuge: '#6B7280',\n mix: '#F97316',\n custom: '#EC4899',\n}\n\nconst statusClasses: Record<ProtocolStepStatus, string> = {\n pending: 'mint-timeline__card--pending',\n in_progress: 'mint-timeline__card--in-progress',\n completed: 'mint-timeline__card--completed',\n failed: 'mint-timeline__card--failed',\n skipped: 'mint-timeline__card--skipped',\n}\n\nconst iconStatusClasses: Record<ProtocolStepStatus, string> = {\n pending: 'mint-timeline__icon--pending',\n in_progress: 'mint-timeline__icon--in-progress',\n completed: 'mint-timeline__icon--completed',\n failed: 'mint-timeline__icon--failed',\n skipped: 'mint-timeline__icon--skipped',\n}\n\nfunction formatDuration(minutes: number | undefined): string {\n if (minutes === undefined) return ''\n if (minutes < 60) return `${minutes}m`\n const hours = Math.floor(minutes / 60)\n const mins = minutes % 60\n return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`\n}\n\nfunction getStepColor(step: ProtocolStep): string {\n return props.colorByType && !props.colorByStatus ? stepTypeColors[step.type] : ''\n}\n\nfunction getStatusClass(step: ProtocolStep): string {\n return props.colorByStatus ? statusClasses[step.status] : statusClasses.pending\n}\n\nfunction getIconStatusClass(step: ProtocolStep): string {\n return props.colorByStatus ? iconStatusClasses[step.status] : iconStatusClasses.pending\n}\n\nfunction toggleExpand(step: ProtocolStep) {\n expandedId.value = expandedId.value === step.id ? undefined : step.id\n}\n\nfunction handleStepClick(step: ProtocolStep) {\n emit('step-click', step)\n if (props.collapsible) {\n toggleExpand(step)\n }\n}\n\nfunction handleStatusChange(step: ProtocolStep, status: ProtocolStepStatus) {\n const updatedSteps = props.modelValue.map(s =>\n s.id === step.id ? { ...s, status } : s\n )\n emit('step-status-change', step.id, status)\n emit('update:modelValue', updatedSteps)\n}\n\nfunction handleRemoveStep(stepId: string) {\n const updatedSteps = props.modelValue\n .filter(s => s.id !== stepId)\n .map((s, i) => ({ ...s, order: i }))\n emit('step-remove', stepId)\n emit('update:modelValue', updatedSteps)\n}\n\nfunction handleAddStep(afterStepId?: string) {\n emit('step-add', afterStepId)\n}\n\nfunction handleDragStart(event: DragEvent, step: ProtocolStep) {\n if (!props.editable) return\n draggedStepId.value = step.id\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', step.id)\n }\n}\n\nfunction handleDragOver(event: DragEvent, step: ProtocolStep) {\n if (!props.editable || !draggedStepId.value) return\n event.preventDefault()\n dragOverStepId.value = step.id\n}\n\nfunction handleDragLeave() {\n dragOverStepId.value = null\n}\n\nfunction handleDrop(event: DragEvent, targetStep: ProtocolStep) {\n if (!props.editable || !draggedStepId.value) return\n event.preventDefault()\n\n const draggedStep = props.modelValue.find(s => s.id === draggedStepId.value)\n if (!draggedStep || draggedStep.id === targetStep.id) {\n draggedStepId.value = null\n dragOverStepId.value = null\n return\n }\n\n const steps = [...props.modelValue]\n const draggedIndex = steps.findIndex(s => s.id === draggedStepId.value)\n const targetIndex = steps.findIndex(s => s.id === targetStep.id)\n\n steps.splice(draggedIndex, 1)\n steps.splice(targetIndex, 0, draggedStep)\n\n const reordered = steps.map((s, i) => ({ ...s, order: i }))\n emit('step-reorder', draggedStepId.value, targetIndex)\n emit('update:modelValue', reordered)\n\n draggedStepId.value = null\n dragOverStepId.value = null\n}\n\nfunction handleDragEnd() {\n draggedStepId.value = null\n dragOverStepId.value = null\n}\n\n</script>\n\n<template>\n <div\n :class=\"['mint-timeline', orientation === 'horizontal' ? 'mint-timeline--horizontal' : 'mint-timeline--vertical']\"\n role=\"list\"\n :aria-label=\"`Protocol timeline with ${sortedSteps.length} steps`\"\n >\n <div\n v-for=\"(step, index) in sortedSteps\"\n :key=\"step.id\"\n class=\"mint-timeline__step\"\n role=\"listitem\"\n :draggable=\"editable\"\n @dragstart=\"handleDragStart($event, step)\"\n @dragover=\"handleDragOver($event, step)\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop($event, step)\"\n @dragend=\"handleDragEnd\"\n >\n <div\n v-if=\"index < sortedSteps.length - 1\"\n class=\"mint-timeline__connector\"\n />\n\n <div\n :class=\"[\n 'mint-timeline__card',\n `mint-timeline__card--${size}`,\n getStatusClass(step),\n dragOverStepId === step.id ? 'mint-timeline__card--drag-over' : '',\n draggedStepId === step.id ? 'mint-timeline__card--dragging' : '',\n editable ? 'mint-timeline__card--editable' : '',\n ]\"\n :style=\"colorByType ? { borderColor: getStepColor(step) } : {}\"\n @click=\"handleStepClick(step)\"\n >\n <div :class=\"['mint-timeline__header', `mint-timeline__header--${size}`]\">\n <div\n :class=\"[\n 'mint-timeline__icon',\n `mint-timeline__icon--${size}`,\n getIconStatusClass(step),\n ]\"\n :style=\"colorByType ? { backgroundColor: `${getStepColor(step)}20`, color: getStepColor(step) } : {}\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" :d=\"stepTypeIcons[step.type]\" />\n </svg>\n </div>\n\n <div class=\"mint-timeline__content\">\n <div class=\"mint-timeline__title-row\">\n <span :class=\"['mint-timeline__title', `mint-timeline__title--${size}`]\">\n {{ step.name }}\n </span>\n <span\n v-if=\"showDuration && step.duration\"\n :class=\"['mint-timeline__duration', size === 'sm' ? 'mint-timeline__duration--sm' : '']\"\n >\n {{ formatDuration(step.duration) }}\n </span>\n </div>\n <div :class=\"['mint-timeline__type', `mint-timeline__type--${size}`]\">\n {{ step.type.replace('_', ' ') }}\n </div>\n </div>\n\n <svg\n v-if=\"collapsible && (step.description || step.parameters)\"\n :class=\"[\n 'mint-timeline__expand-icon',\n expandedId === step.id ? 'mint-timeline__expand-icon--expanded' : '',\n ]\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n\n <button\n v-if=\"editable\"\n type=\"button\"\n class=\"mint-timeline__remove-btn\"\n :aria-label=\"`Remove ${step.name}`\"\n @click.stop=\"handleRemoveStep(step.id)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div\n v-if=\"expandedId === step.id && (step.description || step.parameters)\"\n :aria-expanded=\"expandedId === step.id\"\n class=\"mint-timeline__expanded\"\n >\n <p v-if=\"step.description\" :class=\"['mint-timeline__description', `mint-timeline__description--${size}`]\">\n {{ step.description }}\n </p>\n\n <div v-if=\"step.parameters && Object.keys(step.parameters).length > 0\" class=\"mint-timeline__parameters\">\n <div\n v-for=\"(value, key) in step.parameters\"\n :key=\"String(key)\"\n :class=\"['mint-timeline__param', `mint-timeline__param--${size}`]\"\n >\n <span class=\"mint-timeline__param-key\">{{ key }}:</span>\n <span class=\"mint-timeline__param-value\">{{ value }}</span>\n </div>\n </div>\n\n <div v-if=\"editable\" class=\"mint-timeline__status-controls\">\n <button\n v-for=\"status in (['pending', 'in_progress', 'completed', 'failed', 'skipped'] as const)\"\n :key=\"status\"\n type=\"button\"\n :class=\"[\n 'mint-timeline__status-btn',\n `mint-timeline__status-btn--${status}`,\n step.status === status ? 'mint-timeline__status-btn--active' : '',\n ]\"\n @click.stop=\"handleStatusChange(step, status)\"\n >\n {{ status.replace('_', ' ') }}\n </button>\n </div>\n </div>\n </div>\n\n <button\n v-if=\"editable && orientation === 'vertical'\"\n type=\"button\"\n class=\"mint-timeline__add-inline\"\n :aria-label=\"`Add step after ${step.name}`\"\n @click.stop=\"handleAddStep(step.id)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n </button>\n\n <div v-if=\"orientation === 'vertical' && index < sortedSteps.length - 1\" class=\"mint-timeline__spacer--vertical\" />\n <div v-if=\"orientation === 'horizontal' && index < sortedSteps.length - 1\" class=\"mint-timeline__spacer--horizontal\" />\n </div>\n\n <div\n v-if=\"sortedSteps.length === 0\"\n class=\"mint-timeline__empty\"\n >\n <div class=\"mint-timeline__empty-content\">\n <p class=\"mint-timeline__empty-text\">No protocol steps defined</p>\n <button\n v-if=\"editable\"\n type=\"button\"\n class=\"mint-timeline__empty-btn\"\n @click=\"handleAddStep()\"\n >\n Add First Step\n </button>\n </div>\n </div>\n\n <button\n v-if=\"editable && sortedSteps.length > 0\"\n type=\"button\"\n :class=\"['mint-timeline__add-end', `mint-timeline__add-end--${size}`]\"\n @click=\"handleAddStep()\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n <span :class=\"`mint-timeline__add-end-text--${size}`\">Add Step</span>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/experiment-timeline.css';\n</style>\n","<script setup lang=\"ts\">\nimport { ref, watch, computed } from 'vue'\nimport type { Well, WellEditData, WellEditField } from '../../types'\nimport { useEventListener } from '../../composables/useEventListener'\n\ninterface Props {\n wellId: string\n wellData?: Partial<Well>\n editFields?: WellEditField[]\n defaultInjectionVolume?: number\n position: { x: number; y: number }\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n wellData: () => ({}),\n editFields: () => ['label', 'sampleType', 'injectionVolume', 'injectionCount', 'customMethod'],\n defaultInjectionVolume: 5,\n})\n\nconst emit = defineEmits<{\n save: [data: WellEditData]\n clear: []\n close: []\n}>()\n\nconst showAdvanced = ref(false)\n\nconst formData = ref({\n label: '',\n sampleType: 'sample',\n injectionVolume: props.defaultInjectionVolume,\n injectionCount: 1,\n customMethod: '',\n})\n\nwatch(\n () => [props.wellId, props.wellData] as const,\n () => {\n const meta = props.wellData?.metadata as Record<string, unknown> | undefined\n formData.value = {\n label: (meta?.label as string) || '',\n sampleType: props.wellData?.sampleType || 'sample',\n injectionVolume: (meta?.injectionVolume as number) || props.defaultInjectionVolume,\n injectionCount: (meta?.injectionCount as number) || 1,\n customMethod: (meta?.customMethod as string) || '',\n }\n showAdvanced.value = (formData.value.injectionCount > 1) || !!formData.value.customMethod\n },\n { immediate: true },\n)\n\nconst hasField = (field: WellEditField) => props.editFields.includes(field)\n\nconst popupStyle = computed(() => {\n const x = props.position.x\n const y = props.position.y\n const style: Record<string, string> = {\n left: `${x}px`,\n top: `${y}px`,\n }\n return style\n})\n\nfunction save() {\n emit('save', {\n wellId: props.wellId,\n label: formData.value.label.trim(),\n sampleType: formData.value.sampleType,\n injectionVolume: formData.value.injectionVolume,\n injectionCount: formData.value.injectionCount || 1,\n customMethod: formData.value.customMethod?.trim() || '',\n })\n}\n\nfunction clear() {\n emit('clear')\n}\n\nfunction close() {\n emit('close')\n}\n\nfunction handleKeydown(e: KeyboardEvent) {\n if (e.key === 'Escape') close()\n}\n\nuseEventListener(() => document, 'keydown', handleKeydown)\n\nconst sampleTypeButtons = [\n { type: 'sample', label: 'S', tooltip: 'Sample' },\n { type: 'blank', label: 'B', tooltip: 'Blank' },\n { type: 'qc', label: 'Q', tooltip: 'QC' },\n]\n</script>\n\n<template>\n <Teleport to=\"body\">\n <!-- Backdrop -->\n <div class=\"mint-well-edit-popup__backdrop\" @click=\"close\" />\n\n <!-- Popup -->\n <div\n class=\"mint-well-edit-popup\"\n :style=\"popupStyle\"\n @click.stop\n >\n <!-- Header -->\n <div class=\"mint-well-edit-popup__header\">\n <div class=\"mint-well-edit-popup__header-left\">\n <span class=\"mint-well-edit-popup__well-id\">{{ wellId }}</span>\n <span class=\"mint-well-edit-popup__title\">Edit Well</span>\n </div>\n <button class=\"mint-well-edit-popup__close\" @click=\"close\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"mint-well-edit-popup__body\">\n <!-- Sample Name -->\n <div v-if=\"hasField('label')\">\n <label class=\"mint-well-edit-popup__label\">Sample Name</label>\n <input\n v-model=\"formData.label\"\n type=\"text\"\n placeholder=\"e.g., Sample_001\"\n class=\"mint-well-edit-popup__input\"\n @keydown.enter=\"save\"\n autofocus\n />\n </div>\n\n <!-- Type + Volume row -->\n <div class=\"mint-well-edit-popup__row\">\n <div v-if=\"hasField('sampleType')\">\n <label class=\"mint-well-edit-popup__label\">Type</label>\n <div class=\"mint-well-edit-popup__type-grid\">\n <button\n v-for=\"btn in sampleTypeButtons\"\n :key=\"btn.type\"\n :title=\"btn.tooltip\"\n class=\"mint-well-edit-popup__type-btn\"\n :class=\"{\n [`mint-well-edit-popup__type-btn--${btn.type}`]: formData.sampleType === btn.type,\n }\"\n @click=\"formData.sampleType = btn.type\"\n >\n {{ btn.label }}\n </button>\n </div>\n </div>\n <div v-if=\"hasField('injectionVolume')\">\n <label class=\"mint-well-edit-popup__label\">Volume (uL)</label>\n <input\n v-model.number=\"formData.injectionVolume\"\n type=\"number\"\n min=\"0.1\"\n max=\"100\"\n step=\"0.5\"\n class=\"mint-well-edit-popup__input\"\n />\n </div>\n </div>\n\n <!-- Advanced toggle -->\n <label\n v-if=\"hasField('injectionCount') || hasField('customMethod')\"\n class=\"mint-well-edit-popup__advanced-toggle\"\n >\n <input type=\"checkbox\" v-model=\"showAdvanced\" />\n <span>Advanced options</span>\n </label>\n\n <!-- Advanced fields -->\n <template v-if=\"showAdvanced\">\n <!-- Injection count -->\n <div v-if=\"hasField('injectionCount')\">\n <label class=\"mint-well-edit-popup__label\">Repeat Injections</label>\n <div class=\"mint-well-edit-popup__count-grid\">\n <button\n v-for=\"n in [1, 2, 3, 4, 5]\"\n :key=\"n\"\n class=\"mint-well-edit-popup__count-btn\"\n :class=\"{ 'mint-well-edit-popup__count-btn--active': formData.injectionCount === n }\"\n @click=\"formData.injectionCount = n\"\n >\n {{ n }}×\n </button>\n <input\n v-model.number=\"formData.injectionCount\"\n type=\"number\"\n min=\"1\"\n max=\"10\"\n class=\"mint-well-edit-popup__count-input\"\n />\n </div>\n </div>\n\n <!-- Custom method -->\n <div v-if=\"hasField('customMethod')\">\n <label class=\"mint-well-edit-popup__label\">Custom Method Path</label>\n <input\n v-model=\"formData.customMethod\"\n type=\"text\"\n placeholder=\"Leave empty for default\"\n class=\"mint-well-edit-popup__input mint-well-edit-popup__input--mono\"\n />\n </div>\n </template>\n </div>\n\n <!-- Footer -->\n <div class=\"mint-well-edit-popup__footer\">\n <button class=\"mint-well-edit-popup__btn mint-well-edit-popup__btn--clear\" @click=\"clear\">\n Clear Well\n </button>\n <button class=\"mint-well-edit-popup__btn mint-well-edit-popup__btn--save\" @click=\"save\">\n Save\n </button>\n </div>\n </div>\n </Teleport>\n</template>\n\n<style>\n@import '../../styles/components/well-edit-popup.css';\n</style>\n","<script setup lang=\"ts\">\nimport { ref, watch, computed } from 'vue'\nimport type { Well, WellEditData, WellEditField } from '../../types'\nimport { useEventListener } from '../../composables/useEventListener'\n\ninterface Props {\n wellId: string\n wellData?: Partial<Well>\n editFields?: WellEditField[]\n defaultInjectionVolume?: number\n position: { x: number; y: number }\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n wellData: () => ({}),\n editFields: () => ['label', 'sampleType', 'injectionVolume', 'injectionCount', 'customMethod'],\n defaultInjectionVolume: 5,\n})\n\nconst emit = defineEmits<{\n save: [data: WellEditData]\n clear: []\n close: []\n}>()\n\nconst showAdvanced = ref(false)\n\nconst formData = ref({\n label: '',\n sampleType: 'sample',\n injectionVolume: props.defaultInjectionVolume,\n injectionCount: 1,\n customMethod: '',\n})\n\nwatch(\n () => [props.wellId, props.wellData] as const,\n () => {\n const meta = props.wellData?.metadata as Record<string, unknown> | undefined\n formData.value = {\n label: (meta?.label as string) || '',\n sampleType: props.wellData?.sampleType || 'sample',\n injectionVolume: (meta?.injectionVolume as number) || props.defaultInjectionVolume,\n injectionCount: (meta?.injectionCount as number) || 1,\n customMethod: (meta?.customMethod as string) || '',\n }\n showAdvanced.value = (formData.value.injectionCount > 1) || !!formData.value.customMethod\n },\n { immediate: true },\n)\n\nconst hasField = (field: WellEditField) => props.editFields.includes(field)\n\nconst popupStyle = computed(() => {\n const x = props.position.x\n const y = props.position.y\n const style: Record<string, string> = {\n left: `${x}px`,\n top: `${y}px`,\n }\n return style\n})\n\nfunction save() {\n emit('save', {\n wellId: props.wellId,\n label: formData.value.label.trim(),\n sampleType: formData.value.sampleType,\n injectionVolume: formData.value.injectionVolume,\n injectionCount: formData.value.injectionCount || 1,\n customMethod: formData.value.customMethod?.trim() || '',\n })\n}\n\nfunction clear() {\n emit('clear')\n}\n\nfunction close() {\n emit('close')\n}\n\nfunction handleKeydown(e: KeyboardEvent) {\n if (e.key === 'Escape') close()\n}\n\nuseEventListener(() => document, 'keydown', handleKeydown)\n\nconst sampleTypeButtons = [\n { type: 'sample', label: 'S', tooltip: 'Sample' },\n { type: 'blank', label: 'B', tooltip: 'Blank' },\n { type: 'qc', label: 'Q', tooltip: 'QC' },\n]\n</script>\n\n<template>\n <Teleport to=\"body\">\n <!-- Backdrop -->\n <div class=\"mint-well-edit-popup__backdrop\" @click=\"close\" />\n\n <!-- Popup -->\n <div\n class=\"mint-well-edit-popup\"\n :style=\"popupStyle\"\n @click.stop\n >\n <!-- Header -->\n <div class=\"mint-well-edit-popup__header\">\n <div class=\"mint-well-edit-popup__header-left\">\n <span class=\"mint-well-edit-popup__well-id\">{{ wellId }}</span>\n <span class=\"mint-well-edit-popup__title\">Edit Well</span>\n </div>\n <button class=\"mint-well-edit-popup__close\" @click=\"close\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"mint-well-edit-popup__body\">\n <!-- Sample Name -->\n <div v-if=\"hasField('label')\">\n <label class=\"mint-well-edit-popup__label\">Sample Name</label>\n <input\n v-model=\"formData.label\"\n type=\"text\"\n placeholder=\"e.g., Sample_001\"\n class=\"mint-well-edit-popup__input\"\n @keydown.enter=\"save\"\n autofocus\n />\n </div>\n\n <!-- Type + Volume row -->\n <div class=\"mint-well-edit-popup__row\">\n <div v-if=\"hasField('sampleType')\">\n <label class=\"mint-well-edit-popup__label\">Type</label>\n <div class=\"mint-well-edit-popup__type-grid\">\n <button\n v-for=\"btn in sampleTypeButtons\"\n :key=\"btn.type\"\n :title=\"btn.tooltip\"\n class=\"mint-well-edit-popup__type-btn\"\n :class=\"{\n [`mint-well-edit-popup__type-btn--${btn.type}`]: formData.sampleType === btn.type,\n }\"\n @click=\"formData.sampleType = btn.type\"\n >\n {{ btn.label }}\n </button>\n </div>\n </div>\n <div v-if=\"hasField('injectionVolume')\">\n <label class=\"mint-well-edit-popup__label\">Volume (uL)</label>\n <input\n v-model.number=\"formData.injectionVolume\"\n type=\"number\"\n min=\"0.1\"\n max=\"100\"\n step=\"0.5\"\n class=\"mint-well-edit-popup__input\"\n />\n </div>\n </div>\n\n <!-- Advanced toggle -->\n <label\n v-if=\"hasField('injectionCount') || hasField('customMethod')\"\n class=\"mint-well-edit-popup__advanced-toggle\"\n >\n <input type=\"checkbox\" v-model=\"showAdvanced\" />\n <span>Advanced options</span>\n </label>\n\n <!-- Advanced fields -->\n <template v-if=\"showAdvanced\">\n <!-- Injection count -->\n <div v-if=\"hasField('injectionCount')\">\n <label class=\"mint-well-edit-popup__label\">Repeat Injections</label>\n <div class=\"mint-well-edit-popup__count-grid\">\n <button\n v-for=\"n in [1, 2, 3, 4, 5]\"\n :key=\"n\"\n class=\"mint-well-edit-popup__count-btn\"\n :class=\"{ 'mint-well-edit-popup__count-btn--active': formData.injectionCount === n }\"\n @click=\"formData.injectionCount = n\"\n >\n {{ n }}×\n </button>\n <input\n v-model.number=\"formData.injectionCount\"\n type=\"number\"\n min=\"1\"\n max=\"10\"\n class=\"mint-well-edit-popup__count-input\"\n />\n </div>\n </div>\n\n <!-- Custom method -->\n <div v-if=\"hasField('customMethod')\">\n <label class=\"mint-well-edit-popup__label\">Custom Method Path</label>\n <input\n v-model=\"formData.customMethod\"\n type=\"text\"\n placeholder=\"Leave empty for default\"\n class=\"mint-well-edit-popup__input mint-well-edit-popup__input--mono\"\n />\n </div>\n </template>\n </div>\n\n <!-- Footer -->\n <div class=\"mint-well-edit-popup__footer\">\n <button class=\"mint-well-edit-popup__btn mint-well-edit-popup__btn--clear\" @click=\"clear\">\n Clear Well\n </button>\n <button class=\"mint-well-edit-popup__btn mint-well-edit-popup__btn--save\" @click=\"save\">\n Save\n </button>\n </div>\n </div>\n </Teleport>\n</template>\n\n<style>\n@import '../../styles/components/well-edit-popup.css';\n</style>\n","<script setup lang=\"ts\">\n/** Interactive 96/384-well plate grid with drag-to-select, heatmap overlays, per-well editing, and sample-color mapping. */\nimport { ref, computed } from 'vue'\nimport type { WellPlateFormat, WellPlateSelectionMode, WellPlateSize, Well, HeatmapConfig, WellShape, WellEditField, WellEditData, WellLegendItem, ColumnCondition, RowCondition } from '../types'\nimport { useEventListener } from '../composables/useEventListener'\nimport WellEditPopupInternal from './internal/WellEditPopupInternal.vue'\n\ninterface Props {\n modelValue?: string[]\n format?: WellPlateFormat\n wells?: Record<string, Partial<Well>>\n selectionMode?: WellPlateSelectionMode\n showLabels?: boolean\n showWellIds?: boolean\n showSampleTypeIndicator?: boolean\n heatmap?: HeatmapConfig\n sampleColors?: Record<string, string>\n zoom?: number\n disabled?: boolean\n readonly?: boolean\n size?: WellPlateSize\n wellShape?: WellShape\n showWellLabels?: boolean\n showBadges?: boolean\n editable?: boolean\n editFields?: WellEditField[]\n defaultInjectionVolume?: number\n showLegend?: boolean\n legendItems?: WellLegendItem[]\n columnConditions?: ColumnCondition[]\n rowConditions?: RowCondition[]\n}\n\n// Drag state for moving wells\nconst dragSourceWell = ref<string | null>(null)\nconst dragTargetWell = ref<string | null>(null)\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: () => [],\n format: 96,\n wells: () => ({}),\n selectionMode: 'multiple',\n showLabels: true,\n showWellIds: false,\n showSampleTypeIndicator: false,\n heatmap: () => ({ enabled: false }),\n sampleColors: () => ({}),\n zoom: 1,\n disabled: false,\n readonly: false,\n size: 'md',\n wellShape: 'rounded',\n showWellLabels: false,\n showBadges: false,\n editable: false,\n editFields: () => ['label', 'sampleType', 'injectionVolume', 'injectionCount', 'customMethod'],\n defaultInjectionVolume: 5,\n showLegend: false,\n legendItems: undefined,\n columnConditions: () => [],\n rowConditions: () => [],\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [wellIds: string[]]\n 'well-click': [wellId: string, event: MouseEvent]\n 'well-hover': [wellId: string | null, event?: MouseEvent]\n 'selection-change': [wellIds: string[]]\n 'context-menu': [wellId: string, event: MouseEvent]\n 'well-move': [sourceWellId: string, targetWellId: string]\n 'well-edit': [wellId: string, data: WellEditData]\n 'well-clear': [wellId: string]\n}>()\n\nconst plateRef = ref<HTMLElement | null>(null)\nconst tableRef = ref<HTMLElement | null>(null)\nconst isDragging = ref(false)\nconst dragStart = ref<{ row: number; col: number } | null>(null)\nconst dragEnd = ref<{ row: number; col: number } | null>(null)\nconst hoveredWell = ref<string | null>(null)\n\n// Edit popup state\nconst editingWellId = ref<string | null>(null)\nconst editPopupPosition = ref({ x: 0, y: 0 })\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\nconst plateConfig = computed(() => PLATE_CONFIGS[props.format])\n\nconst rowLabels = computed(() =>\n Array.from({ length: plateConfig.value.rows }, (_, i) => String.fromCharCode(65 + i))\n)\n\nconst colLabels = computed(() =>\n Array.from({ length: plateConfig.value.cols }, (_, i) => i + 1)\n)\n\nconst wellGrid = computed(() => {\n const grid: Well[][] = []\n for (let row = 0; row < plateConfig.value.rows; row++) {\n const rowWells: Well[] = []\n for (let col = 0; col < plateConfig.value.cols; col++) {\n const id = `${rowLabels.value[row]}${col + 1}`\n const wellData = props.wells[id] || {}\n rowWells.push({\n id,\n row,\n col,\n state: wellData.state || 'empty',\n sampleType: wellData.sampleType,\n value: wellData.value,\n metadata: wellData.metadata,\n })\n }\n grid.push(rowWells)\n }\n return grid\n})\n\nconst selectedWellSet = computed(() => new Set(props.modelValue))\n\nconst dragSelectedWells = computed(() => {\n if (!isDragging.value || !dragStart.value || !dragEnd.value) return new Set<string>()\n\n const minRow = Math.min(dragStart.value.row, dragEnd.value.row)\n const maxRow = Math.max(dragStart.value.row, dragEnd.value.row)\n const minCol = Math.min(dragStart.value.col, dragEnd.value.col)\n const maxCol = Math.max(dragStart.value.col, dragEnd.value.col)\n\n const wells = new Set<string>()\n for (let row = minRow; row <= maxRow; row++) {\n for (let col = minCol; col <= maxCol; col++) {\n const id = `${rowLabels.value[row]}${col + 1}`\n wells.add(id)\n }\n }\n return wells\n})\n\n// Size presets with pixel values for reliable sizing\nconst sizeConfig = computed(() => {\n const sizes = {\n sm: { cellWidth: '40px', cellHeight: '40px', headerWidth: '32px', headerHeight: '32px', fontSize: '0.625rem', gap: '2px' },\n md: { cellWidth: '56px', cellHeight: '32px', headerWidth: '32px', headerHeight: '24px', fontSize: '0.75rem', gap: '3px' },\n lg: { cellWidth: '80px', cellHeight: '40px', headerWidth: '40px', headerHeight: '32px', fontSize: '0.875rem', gap: '4px' },\n xl: { cellWidth: '96px', cellHeight: '48px', headerWidth: '48px', headerHeight: '40px', fontSize: '1rem', gap: '4px' },\n fill: { cellWidth: '100%', cellHeight: '32px', headerWidth: '32px', headerHeight: '24px', fontSize: '0.75rem', gap: '2px' },\n }\n return sizes[props.size]\n})\n\nconst isFillMode = computed(() => props.size === 'fill')\n\n// Default legend items\nconst defaultLegendItems: WellLegendItem[] = [\n { type: 'sample', label: 'Sample', color: '#10b981' },\n { type: 'blank', label: 'Blank', color: '#f97316' },\n { type: 'qc', label: 'QC', color: '#8b5cf6' },\n]\n\nconst activeLegendItems = computed(() => props.legendItems ?? defaultLegendItems)\n\n// --- Condition header helpers ---\n\nconst hasColumnConditions = computed(() => props.columnConditions.length > 0)\nconst hasRowConditions = computed(() => props.rowConditions.length > 0)\n\n// Map column index (1-based) → ColumnCondition\nconst colConditionMap = computed(() => {\n const map = new Map<number, { condition: ColumnCondition; indexInGroup: number }>()\n for (const cond of props.columnConditions) {\n cond.cols.forEach((col, i) => {\n map.set(col, { condition: cond, indexInGroup: i })\n })\n }\n return map\n})\n\n// Map row letter → RowCondition\nconst rowConditionMap = computed(() => {\n const map = new Map<string, { condition: RowCondition; indexInGroup: number }>()\n for (const cond of props.rowConditions) {\n cond.rows.forEach((row, i) => {\n map.set(row, { condition: cond, indexInGroup: i })\n })\n }\n return map\n})\n\ntype ColSpan = { condition: ColumnCondition; colspan: number } | { gap: true; colspan: number }\ntype RowSpan = { condition: RowCondition; rowspan: number; startRow: number } | { gap: true; rowspan: number; startRow: number }\n\n// Build column condition header spans for the label row (drug name + unit)\nconst colConditionSpans = computed<ColSpan[]>(() => {\n const spans: ColSpan[] = []\n const cols = colLabels.value\n let i = 0\n while (i < cols.length) {\n const entry = colConditionMap.value.get(cols[i])\n if (entry?.indexInGroup === 0) {\n spans.push({ condition: entry.condition, colspan: entry.condition.cols.length })\n i += entry.condition.cols.length\n } else {\n let gapCount = 0\n while (i + gapCount < cols.length && !colConditionMap.value.has(cols[i + gapCount])) {\n gapCount++\n }\n spans.push({ gap: true, colspan: gapCount || 1 })\n i += gapCount || 1\n }\n }\n return spans\n})\n\n// Build row condition spans for the label column (drug name rotated)\nconst rowConditionSpans = computed<RowSpan[]>(() => {\n const spans: RowSpan[] = []\n const rows = rowLabels.value\n let i = 0\n while (i < rows.length) {\n const entry = rowConditionMap.value.get(rows[i])\n if (entry?.indexInGroup === 0) {\n spans.push({ condition: entry.condition, rowspan: entry.condition.rows.length, startRow: i })\n i += entry.condition.rows.length\n } else {\n let gapCount = 0\n while (i + gapCount < rows.length && !rowConditionMap.value.has(rows[i + gapCount])) {\n gapCount++\n }\n spans.push({ gap: true, rowspan: gapCount || 1, startRow: i })\n i += gapCount || 1\n }\n }\n return spans\n})\n\n// Map row index → span info (only contains entries for first row of each span)\nconst rowConditionSpanByRow = computed(() => {\n const map = new Map<number, RowSpan>()\n for (const span of rowConditionSpans.value) {\n map.set(span.startRow, span)\n }\n return map\n})\n\n// Compute gradient style for a condition cell: background opacity scales with concentration,\n// text color adapts based on effective luminance against white background\nfunction conditionGradientStyle(color: string, conc: number, concentrations: number[]): Record<string, string> {\n const min = Math.min(...concentrations)\n const max = Math.max(...concentrations)\n const t = max <= min ? 1 : (conc - min) / (max - min)\n const opacity = 0.25 + t * 0.50\n const r = parseInt(color.slice(1, 3), 16)\n const g = parseInt(color.slice(3, 5), 16)\n const b = parseInt(color.slice(5, 7), 16)\n // Blend with white to get effective RGB, then compute relative luminance\n const er = 255 * (1 - opacity) + r * opacity\n const eg = 255 * (1 - opacity) + g * opacity\n const eb = 255 * (1 - opacity) + b * opacity\n const luminance = (0.299 * er + 0.587 * eg + 0.114 * eb) / 255\n return {\n backgroundColor: `rgba(${r}, ${g}, ${b}, ${opacity})`,\n color: luminance > 0.55 ? '#1e293b' : '#ffffff',\n }\n}\n\n// Format concentration value (drop trailing zeros)\nfunction formatConc(value: number): string {\n if (value >= 1000) return `${value / 1000}k`\n if (value < 0.01) return value.toExponential(0)\n return String(value)\n}\n\n// Sample type colors (matching MSExpDesigner)\nconst defaultSampleTypeColors: Record<string, { bg: string; border: string }> = {\n sample: { bg: 'rgba(16, 185, 129, 0.15)', border: 'rgba(16, 185, 129, 0.4)' },\n control: { bg: 'rgba(59, 130, 246, 0.15)', border: 'rgba(59, 130, 246, 0.4)' },\n blank: { bg: 'rgba(249, 115, 22, 0.15)', border: 'rgba(249, 115, 22, 0.4)' },\n qc: { bg: 'rgba(139, 92, 246, 0.15)', border: 'rgba(139, 92, 246, 0.4)' },\n}\n\nconst heatmapColors: Record<string, string[]> = {\n viridis: ['#440154', '#482878', '#3e4989', '#31688e', '#26828e', '#1f9e89', '#35b779', '#6ece58', '#b5de2b', '#fde725'],\n plasma: ['#0d0887', '#46039f', '#7201a8', '#9c179e', '#bd3786', '#d8576b', '#ed7953', '#fb9f3a', '#fdca26', '#f0f921'],\n turbo: ['#30123b', '#4145ab', '#4675ed', '#39a2fc', '#1bcfd4', '#24e79e', '#71f05f', '#c1f034', '#f1c83c', '#f99538', '#e45a31', '#ba2512', '#7a0403'],\n}\n\nfunction getHeatmapColor(value: number | undefined): string | null {\n if (!props.heatmap?.enabled || value === undefined) return null\n\n const min = props.heatmap.min ?? 0\n const max = props.heatmap.max ?? 1\n const normalized = Math.max(0, Math.min(1, (value - min) / (max - min)))\n\n const colors = props.heatmap.colorScale === 'custom' && props.heatmap.customColors?.length\n ? props.heatmap.customColors\n : heatmapColors[props.heatmap.colorScale || 'viridis']\n\n const index = Math.min(Math.floor(normalized * (colors.length - 1)), colors.length - 1)\n return colors[index]\n}\n\nfunction getWellClasses(well: Well): string[] {\n const isWellSelected = isSelected(well.id)\n const isDragOver = dragSelectedWells.value.has(well.id)\n const isHovered = hoveredWell.value === well.id\n const isDisabled = props.disabled || well.state === 'disabled'\n const isDragSource = dragSourceWell.value === well.id\n const isDragTarget = dragTargetWell.value === well.id\n\n const classes = [\n 'mint-well-plate__well',\n props.wellShape === 'circle' ? 'mint-well-plate__well--circle' : 'mint-well-plate__well--rounded',\n ]\n\n if (props.selectionMode === 'drag' && well.sampleType) {\n classes.push('mint-well-plate__well--draggable')\n }\n\n if (isDragSource) classes.push('mint-well-plate__well--drag-source')\n else if (isDragTarget) classes.push('mint-well-plate__well--drag-target')\n else if (isWellSelected) classes.push('mint-well-plate__well--selected')\n else if (isDragOver) classes.push('mint-well-plate__well--drag-over')\n else if (isHovered && !props.readonly) classes.push('mint-well-plate__well--hovered')\n\n if (isDisabled) classes.push('mint-well-plate__well--disabled')\n if (well.state === 'filled') classes.push('mint-well-plate__well--filled')\n\n return classes\n}\n\nfunction getWellStyle(well: Well): Record<string, string> {\n const heatmapColor = getHeatmapColor(well.value)\n if (heatmapColor) {\n return { backgroundColor: heatmapColor, border: '1px solid transparent' }\n }\n\n if (well.sampleType && props.sampleColors[well.sampleType]) {\n const color = props.sampleColors[well.sampleType]\n return {\n backgroundColor: `${color}26`,\n border: `1px solid ${color}66`,\n }\n }\n\n if (well.sampleType && defaultSampleTypeColors[well.sampleType]) {\n const colors = defaultSampleTypeColors[well.sampleType]\n return {\n backgroundColor: colors.bg,\n border: `1px solid ${colors.border}`,\n }\n }\n\n const borderStyle = well.state === 'filled' ? 'solid' : 'dashed'\n return {\n backgroundColor: 'var(--bg-tertiary)',\n border: `1px ${borderStyle} var(--border-color)`,\n }\n}\n\nfunction getSampleTypeIndicator(well: Well): string | null {\n if (!props.showSampleTypeIndicator || !well.sampleType) return null\n const typeMap: Record<string, string> = {\n sample: 'S',\n control: 'C',\n blank: 'B',\n qc: 'Q',\n }\n return typeMap[well.sampleType] || well.sampleType.charAt(0).toUpperCase()\n}\n\nfunction getWellLabel(well: Well): string | undefined {\n if (!props.showWellLabels) return undefined\n return well.metadata?.label as string | undefined\n}\n\nfunction getWellBadge(well: Well): { text: string; color: string } | null {\n if (!props.showBadges || !well.metadata) return null\n const count = well.metadata.injectionCount as number | undefined\n if (count && count > 1) {\n return { text: String(count), color: '#6366f1' }\n }\n if (well.metadata.customMethod) {\n return { text: '+', color: '#ec4899' }\n }\n return null\n}\n\nfunction isSelected(wellId: string): boolean {\n return selectedWellSet.value.has(wellId) || dragSelectedWells.value.has(wellId)\n}\n\nfunction handleWellClick(well: Well, event: MouseEvent) {\n if (props.disabled || props.readonly) return\n\n emit('well-click', well.id, event)\n\n // When editable, open popup instead of modifying selection\n if (props.editable) {\n openEditPopup(well.id, event)\n return\n }\n\n if (props.selectionMode === 'none') return\n\n const isCurrentlySelected = selectedWellSet.value.has(well.id)\n const isMultiSelect = event.shiftKey || event.metaKey || event.ctrlKey\n\n let newSelection: string[]\n if (props.selectionMode === 'single') {\n newSelection = isCurrentlySelected ? [] : [well.id]\n } else if (isMultiSelect) {\n newSelection = isCurrentlySelected\n ? props.modelValue.filter(id => id !== well.id)\n : [...props.modelValue, well.id]\n } else {\n newSelection = isCurrentlySelected && props.modelValue.length === 1 ? [] : [well.id]\n }\n\n emit('update:modelValue', newSelection)\n emit('selection-change', newSelection)\n}\n\nfunction openEditPopup(wellId: string, event: MouseEvent) {\n const target = event.currentTarget as HTMLElement\n const rect = target.getBoundingClientRect()\n editPopupPosition.value = {\n x: rect.right + 8,\n y: rect.top,\n }\n editingWellId.value = wellId\n}\n\nfunction handleEditSave(data: WellEditData) {\n emit('well-edit', data.wellId, data)\n editingWellId.value = null\n}\n\nfunction handleEditClear() {\n if (editingWellId.value) {\n emit('well-clear', editingWellId.value)\n }\n editingWellId.value = null\n}\n\nfunction handleEditClose() {\n editingWellId.value = null\n}\n\nfunction handleWellMouseDown(well: Well, event: MouseEvent) {\n if (props.disabled || props.readonly || props.selectionMode !== 'rectangle') return\n if (props.editable) return\n if (event.button !== 0) return\n\n isDragging.value = true\n dragStart.value = { row: well.row, col: well.col }\n dragEnd.value = { row: well.row, col: well.col }\n}\n\nfunction handleWellMouseEnter(well: Well, event: MouseEvent) {\n hoveredWell.value = well.id\n emit('well-hover', well.id, event)\n\n if (isDragging.value && props.selectionMode === 'rectangle') {\n dragEnd.value = { row: well.row, col: well.col }\n }\n}\n\nfunction handleWellMouseLeave() {\n hoveredWell.value = null\n emit('well-hover', null)\n}\n\nfunction handleMouseUp() {\n if (!isDragging.value || props.selectionMode !== 'rectangle') return\n\n const newSelection = Array.from(dragSelectedWells.value)\n isDragging.value = false\n dragStart.value = null\n dragEnd.value = null\n\n if (newSelection.length > 0) {\n emit('update:modelValue', newSelection)\n emit('selection-change', newSelection)\n }\n}\n\nfunction handleContextMenu(well: Well, event: MouseEvent) {\n event.preventDefault()\n emit('context-menu', well.id, event)\n}\n\n// Drag handlers for moving well contents\nfunction handleDragStart(well: Well, event: DragEvent) {\n if (props.disabled || props.readonly || props.selectionMode !== 'drag') return\n if (!well.sampleType) return\n\n dragSourceWell.value = well.id\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', well.id)\n }\n}\n\nfunction handleDragOver(well: Well, event: DragEvent) {\n if (props.selectionMode !== 'drag' || !dragSourceWell.value) return\n event.preventDefault()\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move'\n }\n dragTargetWell.value = well.id\n}\n\nfunction handleDragLeave() {\n dragTargetWell.value = null\n}\n\nfunction handleDrop(well: Well, event: DragEvent) {\n event.preventDefault()\n if (props.selectionMode !== 'drag' || !dragSourceWell.value) return\n\n const sourceId = dragSourceWell.value\n const targetId = well.id\n\n if (sourceId !== targetId) {\n emit('well-move', sourceId, targetId)\n }\n\n dragSourceWell.value = null\n dragTargetWell.value = null\n}\n\nfunction handleDragEnd() {\n dragSourceWell.value = null\n dragTargetWell.value = null\n}\n\nfunction handleKeyDown(event: KeyboardEvent) {\n if (props.disabled || props.readonly) return\n\n if (event.key === 'Escape') {\n if (editingWellId.value) {\n editingWellId.value = null\n return\n }\n emit('update:modelValue', [])\n emit('selection-change', [])\n }\n\n if ((event.key === 'a' || event.key === 'A') && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n const allWells = wellGrid.value.flat().map(w => w.id)\n emit('update:modelValue', allWells)\n emit('selection-change', allWells)\n }\n}\n\nuseEventListener(() => document, 'mouseup', handleMouseUp)\nuseEventListener(() => document, 'keydown', handleKeyDown)\n\nconst containerStyle = computed(() =>\n isFillMode.value ? {} : {\n transform: `scale(${props.zoom})`,\n transformOrigin: 'top left',\n }\n)\n\nconst tableStyle = computed(() => ({\n borderCollapse: 'separate' as const,\n borderSpacing: sizeConfig.value.gap,\n ...(isFillMode.value ? { width: '100%', tableLayout: 'fixed' as const } : {}),\n}))\n</script>\n\n<template>\n <div\n ref=\"plateRef\"\n :class=\"['mint-well-plate', isFillMode ? 'mint-well-plate--fill' : 'mint-well-plate--inline']\"\n :style=\"containerStyle\"\n >\n <div class=\"mint-well-plate__scroll\">\n <div class=\"mint-well-plate__content\">\n <table\n ref=\"tableRef\"\n class=\"mint-well-plate__table\"\n role=\"grid\"\n :aria-label=\"`${props.format}-well plate`\"\n :style=\"tableStyle\"\n >\n <!-- Column condition label row (drug name + unit) -->\n <thead v-if=\"hasColumnConditions\">\n <tr>\n <!-- Spacer for row header column -->\n <th :style=\"{ width: sizeConfig.headerWidth }\"></th>\n <!-- Spacer for row condition column -->\n <th v-if=\"hasRowConditions\" :style=\"{ width: sizeConfig.headerWidth }\"></th>\n <template v-for=\"(span, idx) in colConditionSpans\" :key=\"'clabel-' + idx\">\n <th\n v-if=\"'condition' in span\"\n :colspan=\"span.colspan\"\n class=\"mint-well-plate__condition-label\"\n :style=\"{\n height: sizeConfig.headerHeight,\n fontSize: sizeConfig.fontSize,\n color: span.condition.color,\n }\"\n >\n {{ span.condition.label }}<template v-if=\"span.condition.unit\"> ({{ span.condition.unit }})</template>\n </th>\n <th v-else :colspan=\"span.colspan\"></th>\n </template>\n </tr>\n </thead>\n <!-- Column headers (with concentration overlay when conditions present) -->\n <thead v-if=\"props.showLabels\">\n <tr>\n <th :style=\"{ width: sizeConfig.headerWidth, height: sizeConfig.headerHeight }\"></th>\n <!-- Spacer for row condition column in column header row -->\n <th v-if=\"hasRowConditions\" :style=\"{ width: sizeConfig.headerWidth, height: sizeConfig.headerHeight }\"></th>\n <th\n v-for=\"col in colLabels\"\n :key=\"col\"\n class=\"mint-well-plate__col-header\"\n :style=\"{ height: sizeConfig.headerHeight, fontSize: sizeConfig.fontSize }\"\n >\n <template v-for=\"entry in [colConditionMap.get(col)]\" :key=\"col\">\n <span\n v-if=\"entry\"\n class=\"mint-well-plate__condition-cell\"\n :style=\"conditionGradientStyle(\n entry.condition.color,\n entry.condition.concentrations[entry.indexInGroup],\n entry.condition.concentrations,\n )\"\n >\n {{ formatConc(entry.condition.concentrations[entry.indexInGroup]) }}\n </span>\n <template v-else>{{ col }}</template>\n </template>\n </th>\n </tr>\n </thead>\n <tbody>\n <tr v-for=\"(row, rowIndex) in wellGrid\" :key=\"rowIndex\" role=\"row\">\n <!-- Row condition label cell (drug name, spans multiple rows) -->\n <template v-if=\"hasRowConditions && rowConditionSpanByRow.has(rowIndex)\">\n <td\n v-for=\"span in [rowConditionSpanByRow.get(rowIndex)!]\"\n :key=\"rowIndex\"\n :rowspan=\"span.rowspan\"\n :class=\"[\n 'mint-well-plate__row-condition-label',\n 'gap' in span ? 'mint-well-plate__row-condition-label--empty' : '',\n ]\"\n :style=\"{\n width: sizeConfig.headerWidth,\n minWidth: sizeConfig.headerWidth,\n fontSize: sizeConfig.fontSize,\n ...('condition' in span ? { color: span.condition.color } : {}),\n }\"\n >\n <div v-if=\"'condition' in span\" class=\"mint-well-plate__row-condition-wrap\">\n <span class=\"mint-well-plate__row-condition-text\">{{ span.condition.label }}</span>\n </div>\n </td>\n </template>\n <!-- Row header (with concentration overlay when conditions present) -->\n <td\n v-if=\"props.showLabels\"\n class=\"mint-well-plate__row-header\"\n :style=\"{\n width: sizeConfig.headerWidth,\n height: sizeConfig.cellHeight,\n minWidth: sizeConfig.headerWidth,\n minHeight: sizeConfig.cellHeight,\n fontSize: sizeConfig.fontSize,\n }\"\n >\n <template v-for=\"entry in [rowConditionMap.get(rowLabels[rowIndex])]\" :key=\"rowIndex\">\n <span\n v-if=\"entry\"\n class=\"mint-well-plate__condition-cell\"\n :style=\"conditionGradientStyle(\n entry.condition.color,\n entry.condition.concentrations[entry.indexInGroup],\n entry.condition.concentrations,\n )\"\n >\n {{ formatConc(entry.condition.concentrations[entry.indexInGroup]) }}\n </span>\n <template v-else>{{ rowLabels[rowIndex] }}</template>\n </template>\n </td>\n\n <!-- Wells -->\n <td v-for=\"well in row\" :key=\"well.id\" class=\"mint-well-plate__cell\">\n <div\n role=\"gridcell\"\n tabindex=\"0\"\n :aria-label=\"`Well ${well.id}${well.sampleType ? `, Sample type: ${well.sampleType}` : ''}${well.value !== undefined ? `, Value: ${well.value}` : ''}`\"\n :aria-selected=\"isSelected(well.id)\"\n :aria-disabled=\"props.disabled || well.state === 'disabled'\"\n :draggable=\"props.selectionMode === 'drag' && !!well.sampleType\"\n :class=\"getWellClasses(well)\"\n :style=\"{\n width: isFillMode ? '100%' : sizeConfig.cellWidth,\n height: sizeConfig.cellHeight,\n minWidth: isFillMode ? '0' : sizeConfig.cellWidth,\n minHeight: sizeConfig.cellHeight,\n boxSizing: 'border-box',\n fontSize: sizeConfig.fontSize,\n ...getWellStyle(well),\n }\"\n :title=\"`${well.id}${well.sampleType ? `: ${well.sampleType}` : ''}${getWellLabel(well) ? ` - ${getWellLabel(well)}` : ''}`\"\n @click=\"handleWellClick(well, $event)\"\n @mousedown=\"handleWellMouseDown(well, $event)\"\n @mouseenter=\"handleWellMouseEnter(well, $event)\"\n @mouseleave=\"handleWellMouseLeave\"\n @contextmenu=\"handleContextMenu(well, $event)\"\n @dragstart=\"handleDragStart(well, $event)\"\n @dragover.prevent=\"handleDragOver(well, $event)\"\n @dragleave=\"handleDragLeave\"\n @drop.prevent=\"handleDrop(well, $event)\"\n @dragend=\"handleDragEnd\"\n @keydown.enter=\"handleWellClick(well, $event as unknown as MouseEvent)\"\n @keydown.space.prevent=\"handleWellClick(well, $event as unknown as MouseEvent)\"\n >\n <!-- Well label text (from metadata.label) -->\n <span\n v-if=\"getWellLabel(well)\"\n class=\"mint-well-plate__label\"\n >\n {{ getWellLabel(well) }}\n </span>\n <!-- Sample type indicator (S/B/Q/C) -->\n <span\n v-else-if=\"getSampleTypeIndicator(well)\"\n class=\"mint-well-plate__indicator\"\n >\n {{ getSampleTypeIndicator(well) }}\n </span>\n <!-- Well ID -->\n <span\n v-else-if=\"props.showWellIds\"\n class=\"mint-well-plate__well-id\"\n >\n {{ well.id }}\n </span>\n\n <!-- Badge (injection count or custom method) -->\n <span\n v-if=\"getWellBadge(well)\"\n class=\"mint-well-plate__badge\"\n :style=\"{ backgroundColor: getWellBadge(well)?.color }\"\n :title=\"getWellBadge(well)?.text === '+' ? 'Custom method' : `${getWellBadge(well)?.text ?? ''}x injections`\"\n >\n {{ getWellBadge(well)?.text }}\n </span>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- Heatmap legend (inside content wrapper to match table width) -->\n <div\n v-if=\"props.heatmap?.enabled && props.heatmap.showLegend\"\n class=\"mint-well-plate__legend\"\n >\n <span class=\"mint-well-plate__legend-label\">{{ props.heatmap.min ?? 0 }}</span>\n <div class=\"mint-well-plate__legend-bar\">\n <div\n v-for=\"(color, index) in (props.heatmap.colorScale === 'custom' && props.heatmap.customColors?.length ? props.heatmap.customColors : heatmapColors[props.heatmap.colorScale || 'viridis'])\"\n :key=\"index\"\n class=\"mint-well-plate__legend-segment\"\n :style=\"{ backgroundColor: color }\"\n />\n </div>\n <span class=\"mint-well-plate__legend-label\">{{ props.heatmap.max ?? 1 }}</span>\n </div>\n\n <!-- Sample type legend bar -->\n <div v-if=\"props.showLegend\" class=\"mint-well-plate__sample-legend\">\n <div\n v-for=\"item in activeLegendItems\"\n :key=\"item.type\"\n class=\"mint-well-plate__sample-legend-item\"\n >\n <span\n class=\"mint-well-plate__sample-legend-dot\"\n :style=\"{ backgroundColor: `${item.color}26`, border: `1px solid ${item.color}66` }\"\n />\n <span class=\"mint-well-plate__sample-legend-text\">{{ item.label }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Edit popup -->\n <WellEditPopupInternal\n v-if=\"editable && editingWellId\"\n :well-id=\"editingWellId\"\n :well-data=\"wells[editingWellId]\"\n :edit-fields=\"editFields\"\n :default-injection-volume=\"defaultInjectionVolume\"\n :position=\"editPopupPosition\"\n @save=\"handleEditSave\"\n @clear=\"handleEditClear\"\n @close=\"handleEditClose\"\n />\n </div>\n</template>\n\n<style>\n@import '../styles/components/well-plate.css';\n</style>\n","<script setup lang=\"ts\">\n/** Interactive 96/384-well plate grid with drag-to-select, heatmap overlays, per-well editing, and sample-color mapping. */\nimport { ref, computed } from 'vue'\nimport type { WellPlateFormat, WellPlateSelectionMode, WellPlateSize, Well, HeatmapConfig, WellShape, WellEditField, WellEditData, WellLegendItem, ColumnCondition, RowCondition } from '../types'\nimport { useEventListener } from '../composables/useEventListener'\nimport WellEditPopupInternal from './internal/WellEditPopupInternal.vue'\n\ninterface Props {\n modelValue?: string[]\n format?: WellPlateFormat\n wells?: Record<string, Partial<Well>>\n selectionMode?: WellPlateSelectionMode\n showLabels?: boolean\n showWellIds?: boolean\n showSampleTypeIndicator?: boolean\n heatmap?: HeatmapConfig\n sampleColors?: Record<string, string>\n zoom?: number\n disabled?: boolean\n readonly?: boolean\n size?: WellPlateSize\n wellShape?: WellShape\n showWellLabels?: boolean\n showBadges?: boolean\n editable?: boolean\n editFields?: WellEditField[]\n defaultInjectionVolume?: number\n showLegend?: boolean\n legendItems?: WellLegendItem[]\n columnConditions?: ColumnCondition[]\n rowConditions?: RowCondition[]\n}\n\n// Drag state for moving wells\nconst dragSourceWell = ref<string | null>(null)\nconst dragTargetWell = ref<string | null>(null)\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: () => [],\n format: 96,\n wells: () => ({}),\n selectionMode: 'multiple',\n showLabels: true,\n showWellIds: false,\n showSampleTypeIndicator: false,\n heatmap: () => ({ enabled: false }),\n sampleColors: () => ({}),\n zoom: 1,\n disabled: false,\n readonly: false,\n size: 'md',\n wellShape: 'rounded',\n showWellLabels: false,\n showBadges: false,\n editable: false,\n editFields: () => ['label', 'sampleType', 'injectionVolume', 'injectionCount', 'customMethod'],\n defaultInjectionVolume: 5,\n showLegend: false,\n legendItems: undefined,\n columnConditions: () => [],\n rowConditions: () => [],\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [wellIds: string[]]\n 'well-click': [wellId: string, event: MouseEvent]\n 'well-hover': [wellId: string | null, event?: MouseEvent]\n 'selection-change': [wellIds: string[]]\n 'context-menu': [wellId: string, event: MouseEvent]\n 'well-move': [sourceWellId: string, targetWellId: string]\n 'well-edit': [wellId: string, data: WellEditData]\n 'well-clear': [wellId: string]\n}>()\n\nconst plateRef = ref<HTMLElement | null>(null)\nconst tableRef = ref<HTMLElement | null>(null)\nconst isDragging = ref(false)\nconst dragStart = ref<{ row: number; col: number } | null>(null)\nconst dragEnd = ref<{ row: number; col: number } | null>(null)\nconst hoveredWell = ref<string | null>(null)\n\n// Edit popup state\nconst editingWellId = ref<string | null>(null)\nconst editPopupPosition = ref({ x: 0, y: 0 })\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\nconst plateConfig = computed(() => PLATE_CONFIGS[props.format])\n\nconst rowLabels = computed(() =>\n Array.from({ length: plateConfig.value.rows }, (_, i) => String.fromCharCode(65 + i))\n)\n\nconst colLabels = computed(() =>\n Array.from({ length: plateConfig.value.cols }, (_, i) => i + 1)\n)\n\nconst wellGrid = computed(() => {\n const grid: Well[][] = []\n for (let row = 0; row < plateConfig.value.rows; row++) {\n const rowWells: Well[] = []\n for (let col = 0; col < plateConfig.value.cols; col++) {\n const id = `${rowLabels.value[row]}${col + 1}`\n const wellData = props.wells[id] || {}\n rowWells.push({\n id,\n row,\n col,\n state: wellData.state || 'empty',\n sampleType: wellData.sampleType,\n value: wellData.value,\n metadata: wellData.metadata,\n })\n }\n grid.push(rowWells)\n }\n return grid\n})\n\nconst selectedWellSet = computed(() => new Set(props.modelValue))\n\nconst dragSelectedWells = computed(() => {\n if (!isDragging.value || !dragStart.value || !dragEnd.value) return new Set<string>()\n\n const minRow = Math.min(dragStart.value.row, dragEnd.value.row)\n const maxRow = Math.max(dragStart.value.row, dragEnd.value.row)\n const minCol = Math.min(dragStart.value.col, dragEnd.value.col)\n const maxCol = Math.max(dragStart.value.col, dragEnd.value.col)\n\n const wells = new Set<string>()\n for (let row = minRow; row <= maxRow; row++) {\n for (let col = minCol; col <= maxCol; col++) {\n const id = `${rowLabels.value[row]}${col + 1}`\n wells.add(id)\n }\n }\n return wells\n})\n\n// Size presets with pixel values for reliable sizing\nconst sizeConfig = computed(() => {\n const sizes = {\n sm: { cellWidth: '40px', cellHeight: '40px', headerWidth: '32px', headerHeight: '32px', fontSize: '0.625rem', gap: '2px' },\n md: { cellWidth: '56px', cellHeight: '32px', headerWidth: '32px', headerHeight: '24px', fontSize: '0.75rem', gap: '3px' },\n lg: { cellWidth: '80px', cellHeight: '40px', headerWidth: '40px', headerHeight: '32px', fontSize: '0.875rem', gap: '4px' },\n xl: { cellWidth: '96px', cellHeight: '48px', headerWidth: '48px', headerHeight: '40px', fontSize: '1rem', gap: '4px' },\n fill: { cellWidth: '100%', cellHeight: '32px', headerWidth: '32px', headerHeight: '24px', fontSize: '0.75rem', gap: '2px' },\n }\n return sizes[props.size]\n})\n\nconst isFillMode = computed(() => props.size === 'fill')\n\n// Default legend items\nconst defaultLegendItems: WellLegendItem[] = [\n { type: 'sample', label: 'Sample', color: '#10b981' },\n { type: 'blank', label: 'Blank', color: '#f97316' },\n { type: 'qc', label: 'QC', color: '#8b5cf6' },\n]\n\nconst activeLegendItems = computed(() => props.legendItems ?? defaultLegendItems)\n\n// --- Condition header helpers ---\n\nconst hasColumnConditions = computed(() => props.columnConditions.length > 0)\nconst hasRowConditions = computed(() => props.rowConditions.length > 0)\n\n// Map column index (1-based) → ColumnCondition\nconst colConditionMap = computed(() => {\n const map = new Map<number, { condition: ColumnCondition; indexInGroup: number }>()\n for (const cond of props.columnConditions) {\n cond.cols.forEach((col, i) => {\n map.set(col, { condition: cond, indexInGroup: i })\n })\n }\n return map\n})\n\n// Map row letter → RowCondition\nconst rowConditionMap = computed(() => {\n const map = new Map<string, { condition: RowCondition; indexInGroup: number }>()\n for (const cond of props.rowConditions) {\n cond.rows.forEach((row, i) => {\n map.set(row, { condition: cond, indexInGroup: i })\n })\n }\n return map\n})\n\ntype ColSpan = { condition: ColumnCondition; colspan: number } | { gap: true; colspan: number }\ntype RowSpan = { condition: RowCondition; rowspan: number; startRow: number } | { gap: true; rowspan: number; startRow: number }\n\n// Build column condition header spans for the label row (drug name + unit)\nconst colConditionSpans = computed<ColSpan[]>(() => {\n const spans: ColSpan[] = []\n const cols = colLabels.value\n let i = 0\n while (i < cols.length) {\n const entry = colConditionMap.value.get(cols[i])\n if (entry?.indexInGroup === 0) {\n spans.push({ condition: entry.condition, colspan: entry.condition.cols.length })\n i += entry.condition.cols.length\n } else {\n let gapCount = 0\n while (i + gapCount < cols.length && !colConditionMap.value.has(cols[i + gapCount])) {\n gapCount++\n }\n spans.push({ gap: true, colspan: gapCount || 1 })\n i += gapCount || 1\n }\n }\n return spans\n})\n\n// Build row condition spans for the label column (drug name rotated)\nconst rowConditionSpans = computed<RowSpan[]>(() => {\n const spans: RowSpan[] = []\n const rows = rowLabels.value\n let i = 0\n while (i < rows.length) {\n const entry = rowConditionMap.value.get(rows[i])\n if (entry?.indexInGroup === 0) {\n spans.push({ condition: entry.condition, rowspan: entry.condition.rows.length, startRow: i })\n i += entry.condition.rows.length\n } else {\n let gapCount = 0\n while (i + gapCount < rows.length && !rowConditionMap.value.has(rows[i + gapCount])) {\n gapCount++\n }\n spans.push({ gap: true, rowspan: gapCount || 1, startRow: i })\n i += gapCount || 1\n }\n }\n return spans\n})\n\n// Map row index → span info (only contains entries for first row of each span)\nconst rowConditionSpanByRow = computed(() => {\n const map = new Map<number, RowSpan>()\n for (const span of rowConditionSpans.value) {\n map.set(span.startRow, span)\n }\n return map\n})\n\n// Compute gradient style for a condition cell: background opacity scales with concentration,\n// text color adapts based on effective luminance against white background\nfunction conditionGradientStyle(color: string, conc: number, concentrations: number[]): Record<string, string> {\n const min = Math.min(...concentrations)\n const max = Math.max(...concentrations)\n const t = max <= min ? 1 : (conc - min) / (max - min)\n const opacity = 0.25 + t * 0.50\n const r = parseInt(color.slice(1, 3), 16)\n const g = parseInt(color.slice(3, 5), 16)\n const b = parseInt(color.slice(5, 7), 16)\n // Blend with white to get effective RGB, then compute relative luminance\n const er = 255 * (1 - opacity) + r * opacity\n const eg = 255 * (1 - opacity) + g * opacity\n const eb = 255 * (1 - opacity) + b * opacity\n const luminance = (0.299 * er + 0.587 * eg + 0.114 * eb) / 255\n return {\n backgroundColor: `rgba(${r}, ${g}, ${b}, ${opacity})`,\n color: luminance > 0.55 ? '#1e293b' : '#ffffff',\n }\n}\n\n// Format concentration value (drop trailing zeros)\nfunction formatConc(value: number): string {\n if (value >= 1000) return `${value / 1000}k`\n if (value < 0.01) return value.toExponential(0)\n return String(value)\n}\n\n// Sample type colors (matching MSExpDesigner)\nconst defaultSampleTypeColors: Record<string, { bg: string; border: string }> = {\n sample: { bg: 'rgba(16, 185, 129, 0.15)', border: 'rgba(16, 185, 129, 0.4)' },\n control: { bg: 'rgba(59, 130, 246, 0.15)', border: 'rgba(59, 130, 246, 0.4)' },\n blank: { bg: 'rgba(249, 115, 22, 0.15)', border: 'rgba(249, 115, 22, 0.4)' },\n qc: { bg: 'rgba(139, 92, 246, 0.15)', border: 'rgba(139, 92, 246, 0.4)' },\n}\n\nconst heatmapColors: Record<string, string[]> = {\n viridis: ['#440154', '#482878', '#3e4989', '#31688e', '#26828e', '#1f9e89', '#35b779', '#6ece58', '#b5de2b', '#fde725'],\n plasma: ['#0d0887', '#46039f', '#7201a8', '#9c179e', '#bd3786', '#d8576b', '#ed7953', '#fb9f3a', '#fdca26', '#f0f921'],\n turbo: ['#30123b', '#4145ab', '#4675ed', '#39a2fc', '#1bcfd4', '#24e79e', '#71f05f', '#c1f034', '#f1c83c', '#f99538', '#e45a31', '#ba2512', '#7a0403'],\n}\n\nfunction getHeatmapColor(value: number | undefined): string | null {\n if (!props.heatmap?.enabled || value === undefined) return null\n\n const min = props.heatmap.min ?? 0\n const max = props.heatmap.max ?? 1\n const normalized = Math.max(0, Math.min(1, (value - min) / (max - min)))\n\n const colors = props.heatmap.colorScale === 'custom' && props.heatmap.customColors?.length\n ? props.heatmap.customColors\n : heatmapColors[props.heatmap.colorScale || 'viridis']\n\n const index = Math.min(Math.floor(normalized * (colors.length - 1)), colors.length - 1)\n return colors[index]\n}\n\nfunction getWellClasses(well: Well): string[] {\n const isWellSelected = isSelected(well.id)\n const isDragOver = dragSelectedWells.value.has(well.id)\n const isHovered = hoveredWell.value === well.id\n const isDisabled = props.disabled || well.state === 'disabled'\n const isDragSource = dragSourceWell.value === well.id\n const isDragTarget = dragTargetWell.value === well.id\n\n const classes = [\n 'mint-well-plate__well',\n props.wellShape === 'circle' ? 'mint-well-plate__well--circle' : 'mint-well-plate__well--rounded',\n ]\n\n if (props.selectionMode === 'drag' && well.sampleType) {\n classes.push('mint-well-plate__well--draggable')\n }\n\n if (isDragSource) classes.push('mint-well-plate__well--drag-source')\n else if (isDragTarget) classes.push('mint-well-plate__well--drag-target')\n else if (isWellSelected) classes.push('mint-well-plate__well--selected')\n else if (isDragOver) classes.push('mint-well-plate__well--drag-over')\n else if (isHovered && !props.readonly) classes.push('mint-well-plate__well--hovered')\n\n if (isDisabled) classes.push('mint-well-plate__well--disabled')\n if (well.state === 'filled') classes.push('mint-well-plate__well--filled')\n\n return classes\n}\n\nfunction getWellStyle(well: Well): Record<string, string> {\n const heatmapColor = getHeatmapColor(well.value)\n if (heatmapColor) {\n return { backgroundColor: heatmapColor, border: '1px solid transparent' }\n }\n\n if (well.sampleType && props.sampleColors[well.sampleType]) {\n const color = props.sampleColors[well.sampleType]\n return {\n backgroundColor: `${color}26`,\n border: `1px solid ${color}66`,\n }\n }\n\n if (well.sampleType && defaultSampleTypeColors[well.sampleType]) {\n const colors = defaultSampleTypeColors[well.sampleType]\n return {\n backgroundColor: colors.bg,\n border: `1px solid ${colors.border}`,\n }\n }\n\n const borderStyle = well.state === 'filled' ? 'solid' : 'dashed'\n return {\n backgroundColor: 'var(--bg-tertiary)',\n border: `1px ${borderStyle} var(--border-color)`,\n }\n}\n\nfunction getSampleTypeIndicator(well: Well): string | null {\n if (!props.showSampleTypeIndicator || !well.sampleType) return null\n const typeMap: Record<string, string> = {\n sample: 'S',\n control: 'C',\n blank: 'B',\n qc: 'Q',\n }\n return typeMap[well.sampleType] || well.sampleType.charAt(0).toUpperCase()\n}\n\nfunction getWellLabel(well: Well): string | undefined {\n if (!props.showWellLabels) return undefined\n return well.metadata?.label as string | undefined\n}\n\nfunction getWellBadge(well: Well): { text: string; color: string } | null {\n if (!props.showBadges || !well.metadata) return null\n const count = well.metadata.injectionCount as number | undefined\n if (count && count > 1) {\n return { text: String(count), color: '#6366f1' }\n }\n if (well.metadata.customMethod) {\n return { text: '+', color: '#ec4899' }\n }\n return null\n}\n\nfunction isSelected(wellId: string): boolean {\n return selectedWellSet.value.has(wellId) || dragSelectedWells.value.has(wellId)\n}\n\nfunction handleWellClick(well: Well, event: MouseEvent) {\n if (props.disabled || props.readonly) return\n\n emit('well-click', well.id, event)\n\n // When editable, open popup instead of modifying selection\n if (props.editable) {\n openEditPopup(well.id, event)\n return\n }\n\n if (props.selectionMode === 'none') return\n\n const isCurrentlySelected = selectedWellSet.value.has(well.id)\n const isMultiSelect = event.shiftKey || event.metaKey || event.ctrlKey\n\n let newSelection: string[]\n if (props.selectionMode === 'single') {\n newSelection = isCurrentlySelected ? [] : [well.id]\n } else if (isMultiSelect) {\n newSelection = isCurrentlySelected\n ? props.modelValue.filter(id => id !== well.id)\n : [...props.modelValue, well.id]\n } else {\n newSelection = isCurrentlySelected && props.modelValue.length === 1 ? [] : [well.id]\n }\n\n emit('update:modelValue', newSelection)\n emit('selection-change', newSelection)\n}\n\nfunction openEditPopup(wellId: string, event: MouseEvent) {\n const target = event.currentTarget as HTMLElement\n const rect = target.getBoundingClientRect()\n editPopupPosition.value = {\n x: rect.right + 8,\n y: rect.top,\n }\n editingWellId.value = wellId\n}\n\nfunction handleEditSave(data: WellEditData) {\n emit('well-edit', data.wellId, data)\n editingWellId.value = null\n}\n\nfunction handleEditClear() {\n if (editingWellId.value) {\n emit('well-clear', editingWellId.value)\n }\n editingWellId.value = null\n}\n\nfunction handleEditClose() {\n editingWellId.value = null\n}\n\nfunction handleWellMouseDown(well: Well, event: MouseEvent) {\n if (props.disabled || props.readonly || props.selectionMode !== 'rectangle') return\n if (props.editable) return\n if (event.button !== 0) return\n\n isDragging.value = true\n dragStart.value = { row: well.row, col: well.col }\n dragEnd.value = { row: well.row, col: well.col }\n}\n\nfunction handleWellMouseEnter(well: Well, event: MouseEvent) {\n hoveredWell.value = well.id\n emit('well-hover', well.id, event)\n\n if (isDragging.value && props.selectionMode === 'rectangle') {\n dragEnd.value = { row: well.row, col: well.col }\n }\n}\n\nfunction handleWellMouseLeave() {\n hoveredWell.value = null\n emit('well-hover', null)\n}\n\nfunction handleMouseUp() {\n if (!isDragging.value || props.selectionMode !== 'rectangle') return\n\n const newSelection = Array.from(dragSelectedWells.value)\n isDragging.value = false\n dragStart.value = null\n dragEnd.value = null\n\n if (newSelection.length > 0) {\n emit('update:modelValue', newSelection)\n emit('selection-change', newSelection)\n }\n}\n\nfunction handleContextMenu(well: Well, event: MouseEvent) {\n event.preventDefault()\n emit('context-menu', well.id, event)\n}\n\n// Drag handlers for moving well contents\nfunction handleDragStart(well: Well, event: DragEvent) {\n if (props.disabled || props.readonly || props.selectionMode !== 'drag') return\n if (!well.sampleType) return\n\n dragSourceWell.value = well.id\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', well.id)\n }\n}\n\nfunction handleDragOver(well: Well, event: DragEvent) {\n if (props.selectionMode !== 'drag' || !dragSourceWell.value) return\n event.preventDefault()\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move'\n }\n dragTargetWell.value = well.id\n}\n\nfunction handleDragLeave() {\n dragTargetWell.value = null\n}\n\nfunction handleDrop(well: Well, event: DragEvent) {\n event.preventDefault()\n if (props.selectionMode !== 'drag' || !dragSourceWell.value) return\n\n const sourceId = dragSourceWell.value\n const targetId = well.id\n\n if (sourceId !== targetId) {\n emit('well-move', sourceId, targetId)\n }\n\n dragSourceWell.value = null\n dragTargetWell.value = null\n}\n\nfunction handleDragEnd() {\n dragSourceWell.value = null\n dragTargetWell.value = null\n}\n\nfunction handleKeyDown(event: KeyboardEvent) {\n if (props.disabled || props.readonly) return\n\n if (event.key === 'Escape') {\n if (editingWellId.value) {\n editingWellId.value = null\n return\n }\n emit('update:modelValue', [])\n emit('selection-change', [])\n }\n\n if ((event.key === 'a' || event.key === 'A') && (event.metaKey || event.ctrlKey)) {\n event.preventDefault()\n const allWells = wellGrid.value.flat().map(w => w.id)\n emit('update:modelValue', allWells)\n emit('selection-change', allWells)\n }\n}\n\nuseEventListener(() => document, 'mouseup', handleMouseUp)\nuseEventListener(() => document, 'keydown', handleKeyDown)\n\nconst containerStyle = computed(() =>\n isFillMode.value ? {} : {\n transform: `scale(${props.zoom})`,\n transformOrigin: 'top left',\n }\n)\n\nconst tableStyle = computed(() => ({\n borderCollapse: 'separate' as const,\n borderSpacing: sizeConfig.value.gap,\n ...(isFillMode.value ? { width: '100%', tableLayout: 'fixed' as const } : {}),\n}))\n</script>\n\n<template>\n <div\n ref=\"plateRef\"\n :class=\"['mint-well-plate', isFillMode ? 'mint-well-plate--fill' : 'mint-well-plate--inline']\"\n :style=\"containerStyle\"\n >\n <div class=\"mint-well-plate__scroll\">\n <div class=\"mint-well-plate__content\">\n <table\n ref=\"tableRef\"\n class=\"mint-well-plate__table\"\n role=\"grid\"\n :aria-label=\"`${props.format}-well plate`\"\n :style=\"tableStyle\"\n >\n <!-- Column condition label row (drug name + unit) -->\n <thead v-if=\"hasColumnConditions\">\n <tr>\n <!-- Spacer for row header column -->\n <th :style=\"{ width: sizeConfig.headerWidth }\"></th>\n <!-- Spacer for row condition column -->\n <th v-if=\"hasRowConditions\" :style=\"{ width: sizeConfig.headerWidth }\"></th>\n <template v-for=\"(span, idx) in colConditionSpans\" :key=\"'clabel-' + idx\">\n <th\n v-if=\"'condition' in span\"\n :colspan=\"span.colspan\"\n class=\"mint-well-plate__condition-label\"\n :style=\"{\n height: sizeConfig.headerHeight,\n fontSize: sizeConfig.fontSize,\n color: span.condition.color,\n }\"\n >\n {{ span.condition.label }}<template v-if=\"span.condition.unit\"> ({{ span.condition.unit }})</template>\n </th>\n <th v-else :colspan=\"span.colspan\"></th>\n </template>\n </tr>\n </thead>\n <!-- Column headers (with concentration overlay when conditions present) -->\n <thead v-if=\"props.showLabels\">\n <tr>\n <th :style=\"{ width: sizeConfig.headerWidth, height: sizeConfig.headerHeight }\"></th>\n <!-- Spacer for row condition column in column header row -->\n <th v-if=\"hasRowConditions\" :style=\"{ width: sizeConfig.headerWidth, height: sizeConfig.headerHeight }\"></th>\n <th\n v-for=\"col in colLabels\"\n :key=\"col\"\n class=\"mint-well-plate__col-header\"\n :style=\"{ height: sizeConfig.headerHeight, fontSize: sizeConfig.fontSize }\"\n >\n <template v-for=\"entry in [colConditionMap.get(col)]\" :key=\"col\">\n <span\n v-if=\"entry\"\n class=\"mint-well-plate__condition-cell\"\n :style=\"conditionGradientStyle(\n entry.condition.color,\n entry.condition.concentrations[entry.indexInGroup],\n entry.condition.concentrations,\n )\"\n >\n {{ formatConc(entry.condition.concentrations[entry.indexInGroup]) }}\n </span>\n <template v-else>{{ col }}</template>\n </template>\n </th>\n </tr>\n </thead>\n <tbody>\n <tr v-for=\"(row, rowIndex) in wellGrid\" :key=\"rowIndex\" role=\"row\">\n <!-- Row condition label cell (drug name, spans multiple rows) -->\n <template v-if=\"hasRowConditions && rowConditionSpanByRow.has(rowIndex)\">\n <td\n v-for=\"span in [rowConditionSpanByRow.get(rowIndex)!]\"\n :key=\"rowIndex\"\n :rowspan=\"span.rowspan\"\n :class=\"[\n 'mint-well-plate__row-condition-label',\n 'gap' in span ? 'mint-well-plate__row-condition-label--empty' : '',\n ]\"\n :style=\"{\n width: sizeConfig.headerWidth,\n minWidth: sizeConfig.headerWidth,\n fontSize: sizeConfig.fontSize,\n ...('condition' in span ? { color: span.condition.color } : {}),\n }\"\n >\n <div v-if=\"'condition' in span\" class=\"mint-well-plate__row-condition-wrap\">\n <span class=\"mint-well-plate__row-condition-text\">{{ span.condition.label }}</span>\n </div>\n </td>\n </template>\n <!-- Row header (with concentration overlay when conditions present) -->\n <td\n v-if=\"props.showLabels\"\n class=\"mint-well-plate__row-header\"\n :style=\"{\n width: sizeConfig.headerWidth,\n height: sizeConfig.cellHeight,\n minWidth: sizeConfig.headerWidth,\n minHeight: sizeConfig.cellHeight,\n fontSize: sizeConfig.fontSize,\n }\"\n >\n <template v-for=\"entry in [rowConditionMap.get(rowLabels[rowIndex])]\" :key=\"rowIndex\">\n <span\n v-if=\"entry\"\n class=\"mint-well-plate__condition-cell\"\n :style=\"conditionGradientStyle(\n entry.condition.color,\n entry.condition.concentrations[entry.indexInGroup],\n entry.condition.concentrations,\n )\"\n >\n {{ formatConc(entry.condition.concentrations[entry.indexInGroup]) }}\n </span>\n <template v-else>{{ rowLabels[rowIndex] }}</template>\n </template>\n </td>\n\n <!-- Wells -->\n <td v-for=\"well in row\" :key=\"well.id\" class=\"mint-well-plate__cell\">\n <div\n role=\"gridcell\"\n tabindex=\"0\"\n :aria-label=\"`Well ${well.id}${well.sampleType ? `, Sample type: ${well.sampleType}` : ''}${well.value !== undefined ? `, Value: ${well.value}` : ''}`\"\n :aria-selected=\"isSelected(well.id)\"\n :aria-disabled=\"props.disabled || well.state === 'disabled'\"\n :draggable=\"props.selectionMode === 'drag' && !!well.sampleType\"\n :class=\"getWellClasses(well)\"\n :style=\"{\n width: isFillMode ? '100%' : sizeConfig.cellWidth,\n height: sizeConfig.cellHeight,\n minWidth: isFillMode ? '0' : sizeConfig.cellWidth,\n minHeight: sizeConfig.cellHeight,\n boxSizing: 'border-box',\n fontSize: sizeConfig.fontSize,\n ...getWellStyle(well),\n }\"\n :title=\"`${well.id}${well.sampleType ? `: ${well.sampleType}` : ''}${getWellLabel(well) ? ` - ${getWellLabel(well)}` : ''}`\"\n @click=\"handleWellClick(well, $event)\"\n @mousedown=\"handleWellMouseDown(well, $event)\"\n @mouseenter=\"handleWellMouseEnter(well, $event)\"\n @mouseleave=\"handleWellMouseLeave\"\n @contextmenu=\"handleContextMenu(well, $event)\"\n @dragstart=\"handleDragStart(well, $event)\"\n @dragover.prevent=\"handleDragOver(well, $event)\"\n @dragleave=\"handleDragLeave\"\n @drop.prevent=\"handleDrop(well, $event)\"\n @dragend=\"handleDragEnd\"\n @keydown.enter=\"handleWellClick(well, $event as unknown as MouseEvent)\"\n @keydown.space.prevent=\"handleWellClick(well, $event as unknown as MouseEvent)\"\n >\n <!-- Well label text (from metadata.label) -->\n <span\n v-if=\"getWellLabel(well)\"\n class=\"mint-well-plate__label\"\n >\n {{ getWellLabel(well) }}\n </span>\n <!-- Sample type indicator (S/B/Q/C) -->\n <span\n v-else-if=\"getSampleTypeIndicator(well)\"\n class=\"mint-well-plate__indicator\"\n >\n {{ getSampleTypeIndicator(well) }}\n </span>\n <!-- Well ID -->\n <span\n v-else-if=\"props.showWellIds\"\n class=\"mint-well-plate__well-id\"\n >\n {{ well.id }}\n </span>\n\n <!-- Badge (injection count or custom method) -->\n <span\n v-if=\"getWellBadge(well)\"\n class=\"mint-well-plate__badge\"\n :style=\"{ backgroundColor: getWellBadge(well)?.color }\"\n :title=\"getWellBadge(well)?.text === '+' ? 'Custom method' : `${getWellBadge(well)?.text ?? ''}x injections`\"\n >\n {{ getWellBadge(well)?.text }}\n </span>\n </div>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- Heatmap legend (inside content wrapper to match table width) -->\n <div\n v-if=\"props.heatmap?.enabled && props.heatmap.showLegend\"\n class=\"mint-well-plate__legend\"\n >\n <span class=\"mint-well-plate__legend-label\">{{ props.heatmap.min ?? 0 }}</span>\n <div class=\"mint-well-plate__legend-bar\">\n <div\n v-for=\"(color, index) in (props.heatmap.colorScale === 'custom' && props.heatmap.customColors?.length ? props.heatmap.customColors : heatmapColors[props.heatmap.colorScale || 'viridis'])\"\n :key=\"index\"\n class=\"mint-well-plate__legend-segment\"\n :style=\"{ backgroundColor: color }\"\n />\n </div>\n <span class=\"mint-well-plate__legend-label\">{{ props.heatmap.max ?? 1 }}</span>\n </div>\n\n <!-- Sample type legend bar -->\n <div v-if=\"props.showLegend\" class=\"mint-well-plate__sample-legend\">\n <div\n v-for=\"item in activeLegendItems\"\n :key=\"item.type\"\n class=\"mint-well-plate__sample-legend-item\"\n >\n <span\n class=\"mint-well-plate__sample-legend-dot\"\n :style=\"{ backgroundColor: `${item.color}26`, border: `1px solid ${item.color}66` }\"\n />\n <span class=\"mint-well-plate__sample-legend-text\">{{ item.label }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Edit popup -->\n <WellEditPopupInternal\n v-if=\"editable && editingWellId\"\n :well-id=\"editingWellId\"\n :well-data=\"wells[editingWellId]\"\n :edit-fields=\"editFields\"\n :default-injection-volume=\"defaultInjectionVolume\"\n :position=\"editPopupPosition\"\n @save=\"handleEditSave\"\n @clear=\"handleEditClear\"\n @close=\"handleEditClose\"\n />\n </div>\n</template>\n\n<style>\n@import '../styles/components/well-plate.css';\n</style>\n","<script setup lang=\"ts\">\n/** Clickable color-swatch legend for plate sample types, supporting selection, count display, add/remove, and custom color assignment. */\nimport type { SampleType } from '../types'\n\ninterface Props {\n modelValue?: string\n samples: SampleType[]\n showCounts?: boolean\n editable?: boolean\n colorPalette?: string[]\n size?: 'sm' | 'md' | 'lg'\n orientation?: 'vertical' | 'horizontal'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: undefined,\n samples: () => [],\n showCounts: true,\n editable: false,\n colorPalette: () => [\n '#3B82F6', '#10B981', '#EF4444', '#F59E0B', '#8B5CF6',\n '#F97316', '#06B6D4', '#14B8A6', '#6B7280',\n ],\n size: 'md',\n orientation: 'vertical',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [sampleId: string | undefined]\n 'sample-click': [sample: SampleType]\n 'sample-add': []\n 'sample-remove': [sampleId: string]\n 'color-change': [sampleId: string, color: string]\n}>()\n\n\nfunction getSampleColor(sample: SampleType, index: number): string {\n return sample.color || props.colorPalette[index % props.colorPalette.length]\n}\n\nfunction handleSampleClick(sample: SampleType) {\n const newValue = props.modelValue === sample.id ? undefined : sample.id\n emit('sample-click', sample)\n emit('update:modelValue', newValue)\n}\n\nfunction handleRemove(event: Event, sampleId: string) {\n event.stopPropagation()\n emit('sample-remove', sampleId)\n}\n\nfunction handleAdd() {\n emit('sample-add')\n}\n</script>\n\n<template>\n <div :class=\"['mint-sample-legend', `mint-sample-legend--${orientation}`]\" role=\"list\" aria-label=\"Sample types\">\n <button\n v-for=\"(sample, index) in samples\"\n :key=\"sample.id\"\n type=\"button\"\n role=\"listitem\"\n :aria-pressed=\"modelValue === sample.id\"\n :class=\"[\n 'mint-sample-legend__item',\n `mint-sample-legend__item--${size}`,\n modelValue === sample.id ? 'mint-sample-legend__item--selected' : '',\n ]\"\n @click=\"handleSampleClick(sample)\"\n >\n <span\n :class=\"['mint-sample-legend__swatch', `mint-sample-legend__swatch--${size}`]\"\n :style=\"{ backgroundColor: getSampleColor(sample, index) }\"\n :aria-hidden=\"true\"\n />\n\n <span :class=\"['mint-sample-legend__name', `mint-sample-legend__name--${size}`]\">\n {{ sample.name }}\n </span>\n\n <span\n v-if=\"showCounts && sample.count !== undefined\"\n :class=\"['mint-sample-legend__count', `mint-sample-legend__count--${size}`]\"\n >\n {{ sample.count }}\n </span>\n\n <button\n v-if=\"editable\"\n type=\"button\"\n class=\"mint-sample-legend__remove\"\n :aria-label=\"`Remove ${sample.name}`\"\n @click=\"handleRemove($event, sample.id)\"\n >\n <svg class=\"mint-sample-legend__remove-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </button>\n\n <button\n v-if=\"editable\"\n type=\"button\"\n :class=\"['mint-sample-legend__add', `mint-sample-legend__add--${size}`]\"\n aria-label=\"Add sample type\"\n @click=\"handleAdd\"\n >\n <svg :class=\"`mint-sample-legend__add-icon--${size}`\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n <span :class=\"`mint-sample-legend__add-text--${size}`\">Add</span>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/sample-legend.css';\n</style>\n","<script setup lang=\"ts\">\n/** Clickable color-swatch legend for plate sample types, supporting selection, count display, add/remove, and custom color assignment. */\nimport type { SampleType } from '../types'\n\ninterface Props {\n modelValue?: string\n samples: SampleType[]\n showCounts?: boolean\n editable?: boolean\n colorPalette?: string[]\n size?: 'sm' | 'md' | 'lg'\n orientation?: 'vertical' | 'horizontal'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: undefined,\n samples: () => [],\n showCounts: true,\n editable: false,\n colorPalette: () => [\n '#3B82F6', '#10B981', '#EF4444', '#F59E0B', '#8B5CF6',\n '#F97316', '#06B6D4', '#14B8A6', '#6B7280',\n ],\n size: 'md',\n orientation: 'vertical',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [sampleId: string | undefined]\n 'sample-click': [sample: SampleType]\n 'sample-add': []\n 'sample-remove': [sampleId: string]\n 'color-change': [sampleId: string, color: string]\n}>()\n\n\nfunction getSampleColor(sample: SampleType, index: number): string {\n return sample.color || props.colorPalette[index % props.colorPalette.length]\n}\n\nfunction handleSampleClick(sample: SampleType) {\n const newValue = props.modelValue === sample.id ? undefined : sample.id\n emit('sample-click', sample)\n emit('update:modelValue', newValue)\n}\n\nfunction handleRemove(event: Event, sampleId: string) {\n event.stopPropagation()\n emit('sample-remove', sampleId)\n}\n\nfunction handleAdd() {\n emit('sample-add')\n}\n</script>\n\n<template>\n <div :class=\"['mint-sample-legend', `mint-sample-legend--${orientation}`]\" role=\"list\" aria-label=\"Sample types\">\n <button\n v-for=\"(sample, index) in samples\"\n :key=\"sample.id\"\n type=\"button\"\n role=\"listitem\"\n :aria-pressed=\"modelValue === sample.id\"\n :class=\"[\n 'mint-sample-legend__item',\n `mint-sample-legend__item--${size}`,\n modelValue === sample.id ? 'mint-sample-legend__item--selected' : '',\n ]\"\n @click=\"handleSampleClick(sample)\"\n >\n <span\n :class=\"['mint-sample-legend__swatch', `mint-sample-legend__swatch--${size}`]\"\n :style=\"{ backgroundColor: getSampleColor(sample, index) }\"\n :aria-hidden=\"true\"\n />\n\n <span :class=\"['mint-sample-legend__name', `mint-sample-legend__name--${size}`]\">\n {{ sample.name }}\n </span>\n\n <span\n v-if=\"showCounts && sample.count !== undefined\"\n :class=\"['mint-sample-legend__count', `mint-sample-legend__count--${size}`]\"\n >\n {{ sample.count }}\n </span>\n\n <button\n v-if=\"editable\"\n type=\"button\"\n class=\"mint-sample-legend__remove\"\n :aria-label=\"`Remove ${sample.name}`\"\n @click=\"handleRemove($event, sample.id)\"\n >\n <svg class=\"mint-sample-legend__remove-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </button>\n\n <button\n v-if=\"editable\"\n type=\"button\"\n :class=\"['mint-sample-legend__add', `mint-sample-legend__add--${size}`]\"\n aria-label=\"Add sample type\"\n @click=\"handleAdd\"\n >\n <svg :class=\"`mint-sample-legend__add-icon--${size}`\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n <span :class=\"`mint-sample-legend__add-text--${size}`\">Add</span>\n </button>\n </div>\n</template>\n\n<style>\n@import '../styles/components/sample-legend.css';\n</style>\n","<script setup lang=\"ts\">\n/** Full plate-map design environment for 96/384-well plates with sample assignment, slot-color coding, undo/redo, and CSV/JSON import-export. */\nimport { ref, computed, watch } from 'vue'\nimport type { PlateMapEditorState, WellPlateFormat, SampleType, Well } from '../types'\nimport { useWellPlateEditor } from '../composables/useWellPlateEditor'\nimport { useEventListener } from '../composables/useEventListener'\nimport WellPlate from './WellPlate.vue'\nimport SampleLegend from './SampleLegend.vue'\n\n// Slot colors matching MSExpDesigner\ntype SlotPosition = 'R' | 'G' | 'B' | 'Y'\nconst SLOT_COLORS: Record<SlotPosition, string> = {\n R: '#ef4444', // red\n G: '#22c55e', // green\n B: '#3b82f6', // blue\n Y: '#eab308', // yellow\n}\nconst SLOT_ORDER: SlotPosition[] = ['R', 'G', 'B', 'Y']\n\ninterface Props {\n modelValue?: PlateMapEditorState\n format?: WellPlateFormat\n maxPlates?: number\n samples?: SampleType[]\n showToolbar?: boolean\n showSidebar?: boolean\n allowAddPlates?: boolean\n allowAddSamples?: boolean\n size?: 'sm' | 'md' | 'lg' | 'xl' | 'fill'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: undefined,\n format: 96,\n maxPlates: 10,\n samples: () => [],\n showToolbar: true,\n showSidebar: true,\n allowAddPlates: true,\n allowAddSamples: true,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [state: PlateMapEditorState]\n 'plate-add': [plate: { id: string; name: string }]\n 'plate-remove': [plateId: string]\n 'sample-assign': [wellIds: string[], sampleId: string | undefined]\n 'wells-clear': [wellIds: string[]]\n 'undo': []\n 'redo': []\n 'export': [data: string, format: 'json' | 'csv']\n 'import': [success: boolean]\n}>()\n\nconst editor = useWellPlateEditor(props.modelValue, {\n defaultFormat: props.format,\n})\n\nconst newSampleName = ref('')\nconst showImportModal = ref(false)\nconst importText = ref('')\nconst importFormat = ref<'json' | 'csv'>('json')\n\n// Track slot assignment for each plate\nconst plateSlots = ref<Map<string, SlotPosition>>(new Map())\n\n// Assign slots to plates on creation\nfunction getPlateSlot(plateId: string, plateIndex: number): SlotPosition {\n if (!plateSlots.value.has(plateId)) {\n plateSlots.value.set(plateId, SLOT_ORDER[plateIndex % SLOT_ORDER.length])\n }\n return plateSlots.value.get(plateId)!\n}\n\nconst sampleColors = computed(() => {\n const colors: Record<string, string> = {}\n for (const sample of editor.samples.value) {\n if (sample.color) {\n colors[sample.id] = sample.color\n }\n }\n return colors\n})\n\nconst wellsData = computed(() => {\n const plate = editor.activePlate.value\n if (!plate) return {}\n\n const wells: Record<string, Partial<Well>> = {}\n for (const [wellId, well] of Object.entries(plate.wells)) {\n wells[wellId] = {\n state: well.state,\n sampleType: well.sampleType,\n value: well.value,\n }\n }\n return wells\n})\n\n// Count samples in a plate\nfunction getPlateWellCount(plateId: string): number {\n const plate = editor.plates.value.find(p => p.id === plateId)\n if (!plate) return 0\n return Object.values(plate.wells).filter(w => w.sampleType).length\n}\n\nwatch(\n () => editor.state.value,\n (newState) => emit('update:modelValue', { ...newState }),\n { deep: true }\n)\n\nwatch(\n () => props.modelValue,\n (newValue) => {\n if (newValue) editor.loadState(newValue)\n }\n)\n\nfunction handleSelectionChange(wellIds: string[]) {\n editor.setSelectedWells(wellIds)\n}\n\nfunction handleSampleClick(sample: SampleType) {\n const newSampleId = editor.activeSampleId.value === sample.id ? undefined : sample.id\n editor.setActiveSample(newSampleId)\n}\n\nfunction handleAssignSample() {\n const wells = editor.selectedWells.value\n if (wells.length === 0) return\n\n editor.assignSample(wells, editor.activeSampleId.value)\n emit('sample-assign', wells, editor.activeSampleId.value)\n}\n\nfunction handleClearWells() {\n const wells = editor.selectedWells.value\n if (wells.length === 0) return\n\n editor.clearWells(wells)\n emit('wells-clear', wells)\n}\n\nfunction handleAddSample() {\n if (!newSampleName.value.trim()) return\n editor.addSample(newSampleName.value.trim())\n newSampleName.value = ''\n}\n\nfunction handleRemoveSample(sampleId: string) {\n editor.removeSample(sampleId)\n}\n\nfunction handleAddPlate() {\n if (editor.plates.value.length >= props.maxPlates) return\n const plate = editor.addPlate()\n emit('plate-add', { id: plate.id, name: plate.name })\n}\n\nfunction handleRemovePlate(plateId: string) {\n editor.removePlate(plateId)\n plateSlots.value.delete(plateId)\n emit('plate-remove', plateId)\n}\n\nfunction handleUndo() {\n editor.undo()\n emit('undo')\n}\n\nfunction handleRedo() {\n editor.redo()\n emit('redo')\n}\n\nfunction handleExport(format: 'json' | 'csv') {\n const data = editor.exportData(format)\n emit('export', data, format)\n\n const blob = new Blob([data], { type: format === 'json' ? 'application/json' : 'text/csv' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = `plate-map.${format}`\n a.click()\n URL.revokeObjectURL(url)\n}\n\nfunction handleImport() {\n const success = editor.importData(importText.value, importFormat.value)\n emit('import', success)\n if (success) {\n showImportModal.value = false\n importText.value = ''\n }\n}\n\nfunction handleKeyDown(event: KeyboardEvent) {\n const isUndo = (event.metaKey || event.ctrlKey) && event.key === 'z'\n if (isUndo) {\n event.preventDefault()\n event.shiftKey ? handleRedo() : handleUndo()\n return\n }\n\n const isDelete = event.key === 'Delete' || event.key === 'Backspace'\n if (isDelete && editor.selectedWells.value.length > 0) {\n event.preventDefault()\n handleClearWells()\n return\n }\n\n const num = parseInt(event.key)\n const isValidSampleKey = num >= 1 && num <= 9 && editor.samples.value.length >= num\n if (isValidSampleKey) {\n editor.setActiveSample(editor.samples.value[num - 1].id)\n if (editor.selectedWells.value.length > 0) {\n handleAssignSample()\n }\n }\n}\n\nuseEventListener(() => document, 'keydown', handleKeyDown)\n</script>\n\n<template>\n <div :class=\"['mint-plate-editor', { 'mint-plate-editor--with-sidebar': showSidebar }]\">\n <!-- Main plate area -->\n <div class=\"mint-plate-editor__main\">\n <!-- Toolbar -->\n <div v-if=\"showToolbar\" class=\"mint-plate-editor__toolbar\">\n <!-- Plate tabs -->\n <div class=\"mint-plate-editor__tabs\">\n <button\n v-for=\"(plate, index) in editor.plates.value\"\n :key=\"plate.id\"\n type=\"button\"\n :class=\"['mint-plate-editor__tab', { 'mint-plate-editor__tab--active': plate.id === editor.activePlate.value?.id }]\"\n @click=\"editor.setActivePlate(plate.id)\"\n >\n <span\n class=\"mint-plate-editor__tab-slot\"\n :style=\"{ backgroundColor: SLOT_COLORS[getPlateSlot(plate.id, index)] }\"\n />\n <span class=\"mint-plate-editor__tab-name\">{{ plate.name }}</span>\n <span\n v-if=\"getPlateWellCount(plate.id) > 0\"\n class=\"mint-plate-editor__tab-count\"\n >\n {{ getPlateWellCount(plate.id) }}\n </span>\n <button\n v-if=\"editor.plates.value.length > 1\"\n type=\"button\"\n class=\"mint-plate-editor__tab-remove\"\n :aria-label=\"`Remove ${plate.name}`\"\n @click.stop=\"handleRemovePlate(plate.id)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </button>\n\n <button\n v-if=\"allowAddPlates && editor.plates.value.length < maxPlates\"\n type=\"button\"\n class=\"mint-plate-editor__add-plate\"\n aria-label=\"Add plate\"\n @click=\"handleAddPlate\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n </svg>\n <span>Add</span>\n </button>\n </div>\n\n <div class=\"mint-plate-editor__spacer\" />\n\n <!-- Actions -->\n <div class=\"mint-plate-editor__actions\">\n <button\n type=\"button\"\n :disabled=\"!editor.canUndo.value\"\n class=\"mint-plate-editor__action-btn\"\n title=\"Undo (Ctrl+Z)\"\n @click=\"handleUndo\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9 14 4 9l5-5\" /><path d=\"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11\" />\n </svg>\n </button>\n\n <button\n type=\"button\"\n :disabled=\"!editor.canRedo.value\"\n class=\"mint-plate-editor__action-btn\"\n title=\"Redo (Ctrl+Shift+Z)\"\n @click=\"handleRedo\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m15 14 5-5-5-5\" /><path d=\"M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13\" />\n </svg>\n </button>\n\n <div class=\"mint-plate-editor__divider\" />\n\n <button\n type=\"button\"\n class=\"mint-plate-editor__action-btn\"\n title=\"Import\"\n @click=\"showImportModal = true\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 3v12\" /><path d=\"m17 8-5-5-5 5\" /><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n </svg>\n </button>\n\n <button\n type=\"button\"\n class=\"mint-plate-editor__action-btn\"\n title=\"Export JSON\"\n @click=\"handleExport('json')\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 15V3\" /><path d=\"m7 10 5 5 5-5\" /><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Well plate -->\n <WellPlate\n v-if=\"editor.activePlate.value\"\n :model-value=\"editor.selectedWells.value\"\n :format=\"editor.activePlate.value.format\"\n :wells=\"wellsData\"\n :sample-colors=\"sampleColors\"\n :size=\"size\"\n selection-mode=\"rectangle\"\n show-sample-type-indicator\n @update:model-value=\"handleSelectionChange\"\n />\n\n <!-- Selection info bar -->\n <div\n v-if=\"editor.selectedWells.value.length > 0\"\n class=\"mint-plate-editor__selection-bar\"\n >\n <span class=\"mint-plate-editor__selection-count\">\n <strong>{{ editor.selectedWells.value.length }}</strong> wells selected\n </span>\n <div class=\"mint-plate-editor__spacer\" />\n <button\n v-if=\"editor.activeSampleId.value\"\n type=\"button\"\n class=\"mint-plate-editor__assign-btn\"\n @click=\"handleAssignSample\"\n >\n Assign {{ editor.samples.value.find(s => s.id === editor.activeSampleId.value)?.name }}\n </button>\n <button\n type=\"button\"\n class=\"mint-plate-editor__clear-btn\"\n @click=\"handleClearWells\"\n >\n Clear\n </button>\n </div>\n\n <!-- Legend -->\n <div class=\"mint-plate-editor__legend\">\n <div class=\"mint-plate-editor__legend-items\">\n <div class=\"mint-plate-editor__legend-item\">\n <div class=\"mint-plate-editor__legend-swatch\" style=\"background-color: rgba(16, 185, 129, 0.15); border: 1px solid rgba(16, 185, 129, 0.4)\" />\n <span class=\"mint-plate-editor__legend-label\">Sample</span>\n </div>\n <div class=\"mint-plate-editor__legend-item\">\n <div class=\"mint-plate-editor__legend-swatch\" style=\"background-color: rgba(59, 130, 246, 0.15); border: 1px solid rgba(59, 130, 246, 0.4)\" />\n <span class=\"mint-plate-editor__legend-label\">Control</span>\n </div>\n <div class=\"mint-plate-editor__legend-item\">\n <div class=\"mint-plate-editor__legend-swatch\" style=\"background-color: rgba(249, 115, 22, 0.15); border: 1px solid rgba(249, 115, 22, 0.4)\" />\n <span class=\"mint-plate-editor__legend-label\">Blank</span>\n </div>\n <div class=\"mint-plate-editor__legend-item\">\n <div class=\"mint-plate-editor__legend-swatch\" style=\"background-color: rgba(139, 92, 246, 0.15); border: 1px solid rgba(139, 92, 246, 0.4)\" />\n <span class=\"mint-plate-editor__legend-label\">QC</span>\n </div>\n </div>\n <span class=\"mint-plate-editor__legend-hint\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72\" /><path d=\"m14 7 3 3\" /><path d=\"M5 6v4\" /><path d=\"M19 14v4\" /><path d=\"M10 2v2\" /><path d=\"M7 8H3\" /><path d=\"M21 16h-4\" /><path d=\"M11 3H9\" />\n </svg>\n Drag to select\n </span>\n </div>\n </div>\n\n <!-- Sidebar -->\n <div v-if=\"showSidebar\" class=\"mint-plate-editor__sidebar\">\n <div class=\"mint-plate-editor__sidebar-panel\">\n <h3 class=\"mint-plate-editor__sidebar-title\">Sample Types</h3>\n\n <SampleLegend\n :model-value=\"editor.activeSampleId.value\"\n :samples=\"editor.samples.value\"\n :editable=\"allowAddSamples\"\n :size=\"size === 'lg' ? 'md' : 'sm'\"\n @update:model-value=\"editor.setActiveSample($event)\"\n @sample-click=\"handleSampleClick\"\n @sample-remove=\"handleRemoveSample\"\n />\n\n <!-- Add sample -->\n <div v-if=\"allowAddSamples\" class=\"mint-plate-editor__add-sample\">\n <div class=\"mint-plate-editor__add-sample-form\">\n <input\n v-model=\"newSampleName\"\n type=\"text\"\n placeholder=\"New sample...\"\n class=\"mint-plate-editor__add-sample-input\"\n @keyup.enter=\"handleAddSample\"\n />\n <button\n type=\"button\"\n :disabled=\"!newSampleName.trim()\"\n class=\"mint-plate-editor__add-sample-btn\"\n @click=\"handleAddSample\"\n >\n Add\n </button>\n </div>\n </div>\n\n <!-- Keyboard shortcuts -->\n <div class=\"mint-plate-editor__shortcuts\">\n <h4 class=\"mint-plate-editor__shortcuts-title\">Shortcuts</h4>\n <div class=\"mint-plate-editor__shortcuts-list\">\n <div><kbd class=\"mint-plate-editor__shortcut-key\">1-9</kbd> Quick assign</div>\n <div><kbd class=\"mint-plate-editor__shortcut-key\">Del</kbd> Clear wells</div>\n <div><kbd class=\"mint-plate-editor__shortcut-key\">Ctrl+Z</kbd> Undo</div>\n <div><kbd class=\"mint-plate-editor__shortcut-key\">Ctrl+A</kbd> Select all</div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Import modal -->\n <Teleport to=\"body\">\n <div\n v-if=\"showImportModal\"\n class=\"mint-plate-editor__modal-overlay\"\n @click.self=\"showImportModal = false\"\n >\n <div class=\"mint-plate-editor__modal\">\n <h3 class=\"mint-plate-editor__modal-title\">Import Plate Map</h3>\n\n <div class=\"mint-plate-editor__modal-field\">\n <label class=\"mint-plate-editor__modal-label\">Format</label>\n <select\n v-model=\"importFormat\"\n class=\"mint-plate-editor__modal-select\"\n >\n <option value=\"json\">JSON</option>\n <option value=\"csv\">CSV</option>\n </select>\n </div>\n\n <div class=\"mint-plate-editor__modal-field\">\n <label class=\"mint-plate-editor__modal-label\">Data</label>\n <textarea\n v-model=\"importText\"\n rows=\"8\"\n class=\"mint-plate-editor__modal-textarea\"\n placeholder=\"Paste your data here...\"\n />\n </div>\n\n <div class=\"mint-plate-editor__modal-actions\">\n <button\n type=\"button\"\n class=\"mint-plate-editor__modal-cancel\"\n @click=\"showImportModal = false\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n :disabled=\"!importText.trim()\"\n class=\"mint-plate-editor__modal-submit\"\n @click=\"handleImport\"\n >\n Import\n </button>\n </div>\n </div>\n </div>\n </Teleport>\n </div>\n</template>\n\n<style>\n@import '../styles/components/plate-map-editor.css';\n</style>\n","<script setup lang=\"ts\">\n/** Full plate-map design environment for 96/384-well plates with sample assignment, slot-color coding, undo/redo, and CSV/JSON import-export. */\nimport { ref, computed, watch } from 'vue'\nimport type { PlateMapEditorState, WellPlateFormat, SampleType, Well } from '../types'\nimport { useWellPlateEditor } from '../composables/useWellPlateEditor'\nimport { useEventListener } from '../composables/useEventListener'\nimport WellPlate from './WellPlate.vue'\nimport SampleLegend from './SampleLegend.vue'\n\n// Slot colors matching MSExpDesigner\ntype SlotPosition = 'R' | 'G' | 'B' | 'Y'\nconst SLOT_COLORS: Record<SlotPosition, string> = {\n R: '#ef4444', // red\n G: '#22c55e', // green\n B: '#3b82f6', // blue\n Y: '#eab308', // yellow\n}\nconst SLOT_ORDER: SlotPosition[] = ['R', 'G', 'B', 'Y']\n\ninterface Props {\n modelValue?: PlateMapEditorState\n format?: WellPlateFormat\n maxPlates?: number\n samples?: SampleType[]\n showToolbar?: boolean\n showSidebar?: boolean\n allowAddPlates?: boolean\n allowAddSamples?: boolean\n size?: 'sm' | 'md' | 'lg' | 'xl' | 'fill'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: undefined,\n format: 96,\n maxPlates: 10,\n samples: () => [],\n showToolbar: true,\n showSidebar: true,\n allowAddPlates: true,\n allowAddSamples: true,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [state: PlateMapEditorState]\n 'plate-add': [plate: { id: string; name: string }]\n 'plate-remove': [plateId: string]\n 'sample-assign': [wellIds: string[], sampleId: string | undefined]\n 'wells-clear': [wellIds: string[]]\n 'undo': []\n 'redo': []\n 'export': [data: string, format: 'json' | 'csv']\n 'import': [success: boolean]\n}>()\n\nconst editor = useWellPlateEditor(props.modelValue, {\n defaultFormat: props.format,\n})\n\nconst newSampleName = ref('')\nconst showImportModal = ref(false)\nconst importText = ref('')\nconst importFormat = ref<'json' | 'csv'>('json')\n\n// Track slot assignment for each plate\nconst plateSlots = ref<Map<string, SlotPosition>>(new Map())\n\n// Assign slots to plates on creation\nfunction getPlateSlot(plateId: string, plateIndex: number): SlotPosition {\n if (!plateSlots.value.has(plateId)) {\n plateSlots.value.set(plateId, SLOT_ORDER[plateIndex % SLOT_ORDER.length])\n }\n return plateSlots.value.get(plateId)!\n}\n\nconst sampleColors = computed(() => {\n const colors: Record<string, string> = {}\n for (const sample of editor.samples.value) {\n if (sample.color) {\n colors[sample.id] = sample.color\n }\n }\n return colors\n})\n\nconst wellsData = computed(() => {\n const plate = editor.activePlate.value\n if (!plate) return {}\n\n const wells: Record<string, Partial<Well>> = {}\n for (const [wellId, well] of Object.entries(plate.wells)) {\n wells[wellId] = {\n state: well.state,\n sampleType: well.sampleType,\n value: well.value,\n }\n }\n return wells\n})\n\n// Count samples in a plate\nfunction getPlateWellCount(plateId: string): number {\n const plate = editor.plates.value.find(p => p.id === plateId)\n if (!plate) return 0\n return Object.values(plate.wells).filter(w => w.sampleType).length\n}\n\nwatch(\n () => editor.state.value,\n (newState) => emit('update:modelValue', { ...newState }),\n { deep: true }\n)\n\nwatch(\n () => props.modelValue,\n (newValue) => {\n if (newValue) editor.loadState(newValue)\n }\n)\n\nfunction handleSelectionChange(wellIds: string[]) {\n editor.setSelectedWells(wellIds)\n}\n\nfunction handleSampleClick(sample: SampleType) {\n const newSampleId = editor.activeSampleId.value === sample.id ? undefined : sample.id\n editor.setActiveSample(newSampleId)\n}\n\nfunction handleAssignSample() {\n const wells = editor.selectedWells.value\n if (wells.length === 0) return\n\n editor.assignSample(wells, editor.activeSampleId.value)\n emit('sample-assign', wells, editor.activeSampleId.value)\n}\n\nfunction handleClearWells() {\n const wells = editor.selectedWells.value\n if (wells.length === 0) return\n\n editor.clearWells(wells)\n emit('wells-clear', wells)\n}\n\nfunction handleAddSample() {\n if (!newSampleName.value.trim()) return\n editor.addSample(newSampleName.value.trim())\n newSampleName.value = ''\n}\n\nfunction handleRemoveSample(sampleId: string) {\n editor.removeSample(sampleId)\n}\n\nfunction handleAddPlate() {\n if (editor.plates.value.length >= props.maxPlates) return\n const plate = editor.addPlate()\n emit('plate-add', { id: plate.id, name: plate.name })\n}\n\nfunction handleRemovePlate(plateId: string) {\n editor.removePlate(plateId)\n plateSlots.value.delete(plateId)\n emit('plate-remove', plateId)\n}\n\nfunction handleUndo() {\n editor.undo()\n emit('undo')\n}\n\nfunction handleRedo() {\n editor.redo()\n emit('redo')\n}\n\nfunction handleExport(format: 'json' | 'csv') {\n const data = editor.exportData(format)\n emit('export', data, format)\n\n const blob = new Blob([data], { type: format === 'json' ? 'application/json' : 'text/csv' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = `plate-map.${format}`\n a.click()\n URL.revokeObjectURL(url)\n}\n\nfunction handleImport() {\n const success = editor.importData(importText.value, importFormat.value)\n emit('import', success)\n if (success) {\n showImportModal.value = false\n importText.value = ''\n }\n}\n\nfunction handleKeyDown(event: KeyboardEvent) {\n const isUndo = (event.metaKey || event.ctrlKey) && event.key === 'z'\n if (isUndo) {\n event.preventDefault()\n event.shiftKey ? handleRedo() : handleUndo()\n return\n }\n\n const isDelete = event.key === 'Delete' || event.key === 'Backspace'\n if (isDelete && editor.selectedWells.value.length > 0) {\n event.preventDefault()\n handleClearWells()\n return\n }\n\n const num = parseInt(event.key)\n const isValidSampleKey = num >= 1 && num <= 9 && editor.samples.value.length >= num\n if (isValidSampleKey) {\n editor.setActiveSample(editor.samples.value[num - 1].id)\n if (editor.selectedWells.value.length > 0) {\n handleAssignSample()\n }\n }\n}\n\nuseEventListener(() => document, 'keydown', handleKeyDown)\n</script>\n\n<template>\n <div :class=\"['mint-plate-editor', { 'mint-plate-editor--with-sidebar': showSidebar }]\">\n <!-- Main plate area -->\n <div class=\"mint-plate-editor__main\">\n <!-- Toolbar -->\n <div v-if=\"showToolbar\" class=\"mint-plate-editor__toolbar\">\n <!-- Plate tabs -->\n <div class=\"mint-plate-editor__tabs\">\n <button\n v-for=\"(plate, index) in editor.plates.value\"\n :key=\"plate.id\"\n type=\"button\"\n :class=\"['mint-plate-editor__tab', { 'mint-plate-editor__tab--active': plate.id === editor.activePlate.value?.id }]\"\n @click=\"editor.setActivePlate(plate.id)\"\n >\n <span\n class=\"mint-plate-editor__tab-slot\"\n :style=\"{ backgroundColor: SLOT_COLORS[getPlateSlot(plate.id, index)] }\"\n />\n <span class=\"mint-plate-editor__tab-name\">{{ plate.name }}</span>\n <span\n v-if=\"getPlateWellCount(plate.id) > 0\"\n class=\"mint-plate-editor__tab-count\"\n >\n {{ getPlateWellCount(plate.id) }}\n </span>\n <button\n v-if=\"editor.plates.value.length > 1\"\n type=\"button\"\n class=\"mint-plate-editor__tab-remove\"\n :aria-label=\"`Remove ${plate.name}`\"\n @click.stop=\"handleRemovePlate(plate.id)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </button>\n\n <button\n v-if=\"allowAddPlates && editor.plates.value.length < maxPlates\"\n type=\"button\"\n class=\"mint-plate-editor__add-plate\"\n aria-label=\"Add plate\"\n @click=\"handleAddPlate\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n </svg>\n <span>Add</span>\n </button>\n </div>\n\n <div class=\"mint-plate-editor__spacer\" />\n\n <!-- Actions -->\n <div class=\"mint-plate-editor__actions\">\n <button\n type=\"button\"\n :disabled=\"!editor.canUndo.value\"\n class=\"mint-plate-editor__action-btn\"\n title=\"Undo (Ctrl+Z)\"\n @click=\"handleUndo\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9 14 4 9l5-5\" /><path d=\"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11\" />\n </svg>\n </button>\n\n <button\n type=\"button\"\n :disabled=\"!editor.canRedo.value\"\n class=\"mint-plate-editor__action-btn\"\n title=\"Redo (Ctrl+Shift+Z)\"\n @click=\"handleRedo\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m15 14 5-5-5-5\" /><path d=\"M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13\" />\n </svg>\n </button>\n\n <div class=\"mint-plate-editor__divider\" />\n\n <button\n type=\"button\"\n class=\"mint-plate-editor__action-btn\"\n title=\"Import\"\n @click=\"showImportModal = true\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 3v12\" /><path d=\"m17 8-5-5-5 5\" /><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n </svg>\n </button>\n\n <button\n type=\"button\"\n class=\"mint-plate-editor__action-btn\"\n title=\"Export JSON\"\n @click=\"handleExport('json')\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 15V3\" /><path d=\"m7 10 5 5 5-5\" /><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Well plate -->\n <WellPlate\n v-if=\"editor.activePlate.value\"\n :model-value=\"editor.selectedWells.value\"\n :format=\"editor.activePlate.value.format\"\n :wells=\"wellsData\"\n :sample-colors=\"sampleColors\"\n :size=\"size\"\n selection-mode=\"rectangle\"\n show-sample-type-indicator\n @update:model-value=\"handleSelectionChange\"\n />\n\n <!-- Selection info bar -->\n <div\n v-if=\"editor.selectedWells.value.length > 0\"\n class=\"mint-plate-editor__selection-bar\"\n >\n <span class=\"mint-plate-editor__selection-count\">\n <strong>{{ editor.selectedWells.value.length }}</strong> wells selected\n </span>\n <div class=\"mint-plate-editor__spacer\" />\n <button\n v-if=\"editor.activeSampleId.value\"\n type=\"button\"\n class=\"mint-plate-editor__assign-btn\"\n @click=\"handleAssignSample\"\n >\n Assign {{ editor.samples.value.find(s => s.id === editor.activeSampleId.value)?.name }}\n </button>\n <button\n type=\"button\"\n class=\"mint-plate-editor__clear-btn\"\n @click=\"handleClearWells\"\n >\n Clear\n </button>\n </div>\n\n <!-- Legend -->\n <div class=\"mint-plate-editor__legend\">\n <div class=\"mint-plate-editor__legend-items\">\n <div class=\"mint-plate-editor__legend-item\">\n <div class=\"mint-plate-editor__legend-swatch\" style=\"background-color: rgba(16, 185, 129, 0.15); border: 1px solid rgba(16, 185, 129, 0.4)\" />\n <span class=\"mint-plate-editor__legend-label\">Sample</span>\n </div>\n <div class=\"mint-plate-editor__legend-item\">\n <div class=\"mint-plate-editor__legend-swatch\" style=\"background-color: rgba(59, 130, 246, 0.15); border: 1px solid rgba(59, 130, 246, 0.4)\" />\n <span class=\"mint-plate-editor__legend-label\">Control</span>\n </div>\n <div class=\"mint-plate-editor__legend-item\">\n <div class=\"mint-plate-editor__legend-swatch\" style=\"background-color: rgba(249, 115, 22, 0.15); border: 1px solid rgba(249, 115, 22, 0.4)\" />\n <span class=\"mint-plate-editor__legend-label\">Blank</span>\n </div>\n <div class=\"mint-plate-editor__legend-item\">\n <div class=\"mint-plate-editor__legend-swatch\" style=\"background-color: rgba(139, 92, 246, 0.15); border: 1px solid rgba(139, 92, 246, 0.4)\" />\n <span class=\"mint-plate-editor__legend-label\">QC</span>\n </div>\n </div>\n <span class=\"mint-plate-editor__legend-hint\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72\" /><path d=\"m14 7 3 3\" /><path d=\"M5 6v4\" /><path d=\"M19 14v4\" /><path d=\"M10 2v2\" /><path d=\"M7 8H3\" /><path d=\"M21 16h-4\" /><path d=\"M11 3H9\" />\n </svg>\n Drag to select\n </span>\n </div>\n </div>\n\n <!-- Sidebar -->\n <div v-if=\"showSidebar\" class=\"mint-plate-editor__sidebar\">\n <div class=\"mint-plate-editor__sidebar-panel\">\n <h3 class=\"mint-plate-editor__sidebar-title\">Sample Types</h3>\n\n <SampleLegend\n :model-value=\"editor.activeSampleId.value\"\n :samples=\"editor.samples.value\"\n :editable=\"allowAddSamples\"\n :size=\"size === 'lg' ? 'md' : 'sm'\"\n @update:model-value=\"editor.setActiveSample($event)\"\n @sample-click=\"handleSampleClick\"\n @sample-remove=\"handleRemoveSample\"\n />\n\n <!-- Add sample -->\n <div v-if=\"allowAddSamples\" class=\"mint-plate-editor__add-sample\">\n <div class=\"mint-plate-editor__add-sample-form\">\n <input\n v-model=\"newSampleName\"\n type=\"text\"\n placeholder=\"New sample...\"\n class=\"mint-plate-editor__add-sample-input\"\n @keyup.enter=\"handleAddSample\"\n />\n <button\n type=\"button\"\n :disabled=\"!newSampleName.trim()\"\n class=\"mint-plate-editor__add-sample-btn\"\n @click=\"handleAddSample\"\n >\n Add\n </button>\n </div>\n </div>\n\n <!-- Keyboard shortcuts -->\n <div class=\"mint-plate-editor__shortcuts\">\n <h4 class=\"mint-plate-editor__shortcuts-title\">Shortcuts</h4>\n <div class=\"mint-plate-editor__shortcuts-list\">\n <div><kbd class=\"mint-plate-editor__shortcut-key\">1-9</kbd> Quick assign</div>\n <div><kbd class=\"mint-plate-editor__shortcut-key\">Del</kbd> Clear wells</div>\n <div><kbd class=\"mint-plate-editor__shortcut-key\">Ctrl+Z</kbd> Undo</div>\n <div><kbd class=\"mint-plate-editor__shortcut-key\">Ctrl+A</kbd> Select all</div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Import modal -->\n <Teleport to=\"body\">\n <div\n v-if=\"showImportModal\"\n class=\"mint-plate-editor__modal-overlay\"\n @click.self=\"showImportModal = false\"\n >\n <div class=\"mint-plate-editor__modal\">\n <h3 class=\"mint-plate-editor__modal-title\">Import Plate Map</h3>\n\n <div class=\"mint-plate-editor__modal-field\">\n <label class=\"mint-plate-editor__modal-label\">Format</label>\n <select\n v-model=\"importFormat\"\n class=\"mint-plate-editor__modal-select\"\n >\n <option value=\"json\">JSON</option>\n <option value=\"csv\">CSV</option>\n </select>\n </div>\n\n <div class=\"mint-plate-editor__modal-field\">\n <label class=\"mint-plate-editor__modal-label\">Data</label>\n <textarea\n v-model=\"importText\"\n rows=\"8\"\n class=\"mint-plate-editor__modal-textarea\"\n placeholder=\"Paste your data here...\"\n />\n </div>\n\n <div class=\"mint-plate-editor__modal-actions\">\n <button\n type=\"button\"\n class=\"mint-plate-editor__modal-cancel\"\n @click=\"showImportModal = false\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n :disabled=\"!importText.trim()\"\n class=\"mint-plate-editor__modal-submit\"\n @click=\"handleImport\"\n >\n Import\n </button>\n </div>\n </div>\n </div>\n </Teleport>\n </div>\n</template>\n\n<style>\n@import '../styles/components/plate-map-editor.css';\n</style>\n","<script setup lang=\"ts\">\n/** Sortable, searchable table of experiment reagents showing lot number, expiry, storage conditions, and stock level with low-stock warnings. */\nimport { ref, computed } from 'vue'\nimport type { ReagentColumn, Reagent } from '../types'\nimport { useSortedItems } from '../composables/useSortedItems'\nimport { useTextSearch } from '../composables/useTextSearch'\n\ninterface Props {\n modelValue?: Reagent[]\n readonly?: boolean\n showStockLevel?: boolean\n lowStockThreshold?: number\n columns?: ReagentColumn[]\n sortable?: boolean\n searchable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: () => [],\n readonly: false,\n showStockLevel: true,\n lowStockThreshold: 10,\n columns: () => ['name', 'lot', 'expiry', 'storage', 'stock'],\n sortable: true,\n searchable: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [reagents: Reagent[]]\n 'add': []\n 'edit': [reagent: Reagent]\n 'remove': [reagentId: string]\n 'reorder': [reagentId: string, fromIndex: number, toIndex: number]\n}>()\n\n// State\nconst searchQuery = ref('')\nconst sortColumn = ref<ReagentColumn | null>(null)\nconst sortDirection = ref<'asc' | 'desc'>('asc')\nconst draggedId = ref<string | null>(null)\nconst dragOverId = ref<string | null>(null)\n\n// Column configuration\nconst columnLabels: Record<ReagentColumn, string> = {\n name: 'Name',\n catalog: 'Catalog #',\n lot: 'Lot #',\n expiry: 'Expiry',\n storage: 'Storage',\n location: 'Location',\n stock: 'Stock',\n supplier: 'Supplier',\n}\n\nconst reagentSearch = useTextSearch({\n items: () => props.modelValue || [],\n query: searchQuery,\n enabled: () => props.searchable,\n getText: (reagent) => [\n reagent.name,\n reagent.catalogNumber,\n reagent.lotNumber,\n reagent.supplier,\n ],\n})\n\nconst reagentSort = computed(() => {\n if (!sortColumn.value) return null\n return {\n key: sortColumn.value,\n direction: sortDirection.value,\n }\n})\n\nconst sortedReagents = useSortedItems<Reagent, ReagentColumn>({\n items: reagentSearch.filteredItems,\n sort: reagentSort,\n caseSensitive: false,\n getValue: (reagent, column) => getColumnValue(reagent, column),\n})\nconst filteredReagents = sortedReagents.sortedItems\n\nfunction getColumnValue(reagent: Reagent, column: ReagentColumn): unknown {\n switch (column) {\n case 'name':\n return reagent.name\n case 'catalog':\n return reagent.catalogNumber\n case 'lot':\n return reagent.lotNumber\n case 'expiry':\n return reagent.expiryDate ? new Date(reagent.expiryDate).getTime() : null\n case 'storage':\n return reagent.storageCondition\n case 'location':\n return reagent.location\n case 'stock':\n return reagent.stockLevel\n case 'supplier':\n return reagent.supplier\n default:\n return null\n }\n}\n\n// Helpers\nfunction isExpired(reagent: Reagent): boolean {\n if (!reagent.expiryDate) return false\n const expiry = new Date(reagent.expiryDate)\n return expiry < new Date()\n}\n\nfunction isExpiringSoon(reagent: Reagent, daysThreshold = 30): boolean {\n if (!reagent.expiryDate) return false\n const expiry = new Date(reagent.expiryDate)\n const now = new Date()\n const daysUntilExpiry = (expiry.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)\n return daysUntilExpiry > 0 && daysUntilExpiry <= daysThreshold\n}\n\nfunction isLowStock(reagent: Reagent): boolean {\n if (reagent.stockLevel === undefined) return false\n return reagent.stockLevel <= props.lowStockThreshold\n}\n\nfunction formatExpiryDate(date: Date | string | undefined): string {\n if (!date) return '-'\n const d = new Date(date)\n return d.toLocaleDateString('en-US', { year: 'numeric', month: 'short' })\n}\n\nfunction getStockLevel(reagent: Reagent): number {\n return reagent.stockLevel ?? 0\n}\n\nfunction getStockFillClass(level: number): string {\n if (level >= 50) return 'mint-reagent-list__stock-fill--good'\n if (level >= 20) return 'mint-reagent-list__stock-fill--warning'\n return 'mint-reagent-list__stock-fill--low'\n}\n\nfunction getRowClass(reagent: Reagent): string[] {\n const classes: string[] = ['mint-reagent-list__row']\n if (isExpired(reagent)) classes.push('mint-reagent-list__row--expired')\n else if (isExpiringSoon(reagent)) classes.push('mint-reagent-list__row--expiring-soon')\n if (isLowStock(reagent)) classes.push('mint-reagent-list__row--low-stock')\n if (draggedId.value === reagent.id) classes.push('mint-reagent-list__row--dragging')\n if (dragOverId.value === reagent.id) classes.push('mint-reagent-list__row--drag-over')\n return classes\n}\n\n// Actions\nfunction handleSort(column: ReagentColumn) {\n if (!props.sortable) return\n if (sortColumn.value === column) {\n sortDirection.value = sortDirection.value === 'asc' ? 'desc' : 'asc'\n } else {\n sortColumn.value = column\n sortDirection.value = 'asc'\n }\n}\n\nfunction handleEdit(reagent: Reagent) {\n emit('edit', reagent)\n}\n\nfunction handleRemove(reagentId: string) {\n emit('remove', reagentId)\n const updated = (props.modelValue || []).filter((r) => r.id !== reagentId)\n emit('update:modelValue', updated)\n}\n\nfunction handleAdd() {\n emit('add')\n}\n\n// Drag and drop\nfunction handleDragStart(event: DragEvent, reagent: Reagent) {\n if (props.readonly || !props.sortable) return\n draggedId.value = reagent.id\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', reagent.id)\n }\n}\n\nfunction handleDragOver(event: DragEvent, reagent: Reagent) {\n if (props.readonly || !props.sortable || !draggedId.value) return\n event.preventDefault()\n dragOverId.value = reagent.id\n}\n\nfunction handleDragLeave() {\n dragOverId.value = null\n}\n\nfunction handleDrop(event: DragEvent, targetReagent: Reagent) {\n if (props.readonly || !props.sortable || !draggedId.value) return\n event.preventDefault()\n\n const reagents = [...(props.modelValue || [])]\n const fromIndex = reagents.findIndex((r) => r.id === draggedId.value)\n const toIndex = reagents.findIndex((r) => r.id === targetReagent.id)\n\n if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) {\n draggedId.value = null\n dragOverId.value = null\n return\n }\n\n const [removed] = reagents.splice(fromIndex, 1)\n reagents.splice(toIndex, 0, removed)\n\n emit('reorder', draggedId.value, fromIndex, toIndex)\n emit('update:modelValue', reagents)\n\n draggedId.value = null\n dragOverId.value = null\n}\n\nfunction handleDragEnd() {\n draggedId.value = null\n dragOverId.value = null\n}\n</script>\n\n<template>\n <div class=\"mint-reagent-list\">\n <!-- Header -->\n <div class=\"mint-reagent-list__header\">\n <!-- Search -->\n <div v-if=\"searchable\" class=\"mint-reagent-list__search\">\n <svg class=\"mint-reagent-list__search-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" />\n </svg>\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n class=\"mint-reagent-list__search-input\"\n placeholder=\"Search reagents...\"\n />\n </div>\n\n <!-- Add button -->\n <button\n v-if=\"!readonly\"\n type=\"button\"\n class=\"mint-reagent-list__add-btn\"\n @click=\"handleAdd\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n Add\n </button>\n </div>\n\n <!-- Table -->\n <table v-if=\"filteredReagents.length > 0\" class=\"mint-reagent-list__table\">\n <thead>\n <tr>\n <th v-if=\"sortable && !readonly\" style=\"width: 24px\" />\n <th\n v-for=\"col in columns\"\n :key=\"col\"\n :class=\"[sortable ? 'mint-reagent-list__col--sortable' : '']\"\n @click=\"handleSort(col)\"\n >\n {{ columnLabels[col] }}\n <svg\n v-if=\"sortable\"\n :class=\"[\n 'mint-reagent-list__sort-icon',\n sortColumn === col ? 'mint-reagent-list__sort-icon--active' : '',\n ]\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n v-if=\"sortColumn === col && sortDirection === 'desc'\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M19 9l-7 7-7-7\"\n />\n <path\n v-else\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M5 15l7-7 7 7\"\n />\n </svg>\n </th>\n <th v-if=\"!readonly\" style=\"width: 80px\" />\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"reagent in filteredReagents\"\n :key=\"reagent.id\"\n :class=\"getRowClass(reagent)\"\n :draggable=\"sortable && !readonly\"\n @dragstart=\"handleDragStart($event, reagent)\"\n @dragover=\"handleDragOver($event, reagent)\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop($event, reagent)\"\n @dragend=\"handleDragEnd\"\n >\n <!-- Drag handle -->\n <td v-if=\"sortable && !readonly\">\n <svg class=\"mint-reagent-list__drag-handle\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8h16M4 16h16\" />\n </svg>\n </td>\n\n <!-- Name -->\n <td v-if=\"columns.includes('name')\" class=\"mint-reagent-list__cell mint-reagent-list__cell--name\">\n <a\n v-if=\"reagent.url\"\n :href=\"reagent.url\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"mint-reagent-list__link\"\n >\n {{ reagent.name }}\n </a>\n <span v-else>{{ reagent.name }}</span>\n </td>\n\n <!-- Catalog number -->\n <td v-if=\"columns.includes('catalog')\" class=\"mint-reagent-list__cell mint-reagent-list__cell--muted\">\n {{ reagent.catalogNumber || '-' }}\n </td>\n\n <!-- Lot number -->\n <td v-if=\"columns.includes('lot')\" class=\"mint-reagent-list__cell mint-reagent-list__cell--muted\">\n {{ reagent.lotNumber || '-' }}\n </td>\n\n <!-- Expiry -->\n <td v-if=\"columns.includes('expiry')\" class=\"mint-reagent-list__cell\">\n <span v-if=\"isExpired(reagent)\" class=\"mint-reagent-list__expired-text\">\n {{ formatExpiryDate(reagent.expiryDate) }}\n </span>\n <span v-else-if=\"isExpiringSoon(reagent)\" class=\"mint-reagent-list__expiring-text\">\n {{ formatExpiryDate(reagent.expiryDate) }}\n </span>\n <span v-else>\n {{ formatExpiryDate(reagent.expiryDate) }}\n </span>\n\n <!-- Expiry badge -->\n <span v-if=\"isExpired(reagent)\" class=\"mint-reagent-list__badge mint-reagent-list__badge--error\">\n <svg class=\"mint-reagent-list__badge-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n Expired\n </span>\n <span v-else-if=\"isExpiringSoon(reagent)\" class=\"mint-reagent-list__badge mint-reagent-list__badge--warning\">\n <svg class=\"mint-reagent-list__badge-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\n </svg>\n Soon\n </span>\n </td>\n\n <!-- Storage -->\n <td v-if=\"columns.includes('storage')\" class=\"mint-reagent-list__cell\">\n <span\n v-if=\"reagent.storageCondition\"\n :class=\"['mint-reagent-list__storage', `mint-reagent-list__storage--${reagent.storageCondition}`]\"\n >\n {{ reagent.storageCondition }}\n </span>\n <span v-else class=\"mint-reagent-list__cell--muted\">-</span>\n </td>\n\n <!-- Location -->\n <td v-if=\"columns.includes('location')\" class=\"mint-reagent-list__cell mint-reagent-list__cell--muted\">\n {{ reagent.location || '-' }}\n </td>\n\n <!-- Stock -->\n <td v-if=\"columns.includes('stock') && showStockLevel\" class=\"mint-reagent-list__cell\">\n <div v-if=\"reagent.stockLevel !== undefined\" class=\"mint-reagent-list__stock\">\n <div class=\"mint-reagent-list__stock-bar\">\n <div\n :class=\"['mint-reagent-list__stock-fill', getStockFillClass(getStockLevel(reagent))]\"\n :style=\"{ width: `${Math.min(100, getStockLevel(reagent))}%` }\"\n />\n </div>\n <span class=\"mint-reagent-list__stock-text\">\n {{ getStockLevel(reagent) }}%\n </span>\n </div>\n <span v-else class=\"mint-reagent-list__cell--muted\">-</span>\n </td>\n\n <!-- Supplier -->\n <td v-if=\"columns.includes('supplier')\" class=\"mint-reagent-list__cell mint-reagent-list__cell--muted\">\n {{ reagent.supplier || '-' }}\n </td>\n\n <!-- Actions -->\n <td v-if=\"!readonly\" class=\"mint-reagent-list__cell mint-reagent-list__cell--actions\">\n <button\n type=\"button\"\n class=\"mint-reagent-list__action-btn\"\n aria-label=\"Edit\"\n @click=\"handleEdit(reagent)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"mint-reagent-list__action-btn mint-reagent-list__action-btn--danger\"\n aria-label=\"Remove\"\n @click=\"handleRemove(reagent.id)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\" />\n </svg>\n </button>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- Empty state -->\n <div v-else class=\"mint-reagent-list__empty\">\n <svg class=\"mint-reagent-list__empty-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\" />\n </svg>\n <p class=\"mint-reagent-list__empty-text\">\n {{ searchQuery ? 'No reagents found' : 'No reagents added yet' }}\n </p>\n <button\n v-if=\"!readonly && !searchQuery\"\n type=\"button\"\n class=\"mint-reagent-list__empty-btn\"\n @click=\"handleAdd\"\n >\n Add Reagent\n </button>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/reagent-list.css';\n</style>\n","<script setup lang=\"ts\">\n/** Sortable, searchable table of experiment reagents showing lot number, expiry, storage conditions, and stock level with low-stock warnings. */\nimport { ref, computed } from 'vue'\nimport type { ReagentColumn, Reagent } from '../types'\nimport { useSortedItems } from '../composables/useSortedItems'\nimport { useTextSearch } from '../composables/useTextSearch'\n\ninterface Props {\n modelValue?: Reagent[]\n readonly?: boolean\n showStockLevel?: boolean\n lowStockThreshold?: number\n columns?: ReagentColumn[]\n sortable?: boolean\n searchable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: () => [],\n readonly: false,\n showStockLevel: true,\n lowStockThreshold: 10,\n columns: () => ['name', 'lot', 'expiry', 'storage', 'stock'],\n sortable: true,\n searchable: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [reagents: Reagent[]]\n 'add': []\n 'edit': [reagent: Reagent]\n 'remove': [reagentId: string]\n 'reorder': [reagentId: string, fromIndex: number, toIndex: number]\n}>()\n\n// State\nconst searchQuery = ref('')\nconst sortColumn = ref<ReagentColumn | null>(null)\nconst sortDirection = ref<'asc' | 'desc'>('asc')\nconst draggedId = ref<string | null>(null)\nconst dragOverId = ref<string | null>(null)\n\n// Column configuration\nconst columnLabels: Record<ReagentColumn, string> = {\n name: 'Name',\n catalog: 'Catalog #',\n lot: 'Lot #',\n expiry: 'Expiry',\n storage: 'Storage',\n location: 'Location',\n stock: 'Stock',\n supplier: 'Supplier',\n}\n\nconst reagentSearch = useTextSearch({\n items: () => props.modelValue || [],\n query: searchQuery,\n enabled: () => props.searchable,\n getText: (reagent) => [\n reagent.name,\n reagent.catalogNumber,\n reagent.lotNumber,\n reagent.supplier,\n ],\n})\n\nconst reagentSort = computed(() => {\n if (!sortColumn.value) return null\n return {\n key: sortColumn.value,\n direction: sortDirection.value,\n }\n})\n\nconst sortedReagents = useSortedItems<Reagent, ReagentColumn>({\n items: reagentSearch.filteredItems,\n sort: reagentSort,\n caseSensitive: false,\n getValue: (reagent, column) => getColumnValue(reagent, column),\n})\nconst filteredReagents = sortedReagents.sortedItems\n\nfunction getColumnValue(reagent: Reagent, column: ReagentColumn): unknown {\n switch (column) {\n case 'name':\n return reagent.name\n case 'catalog':\n return reagent.catalogNumber\n case 'lot':\n return reagent.lotNumber\n case 'expiry':\n return reagent.expiryDate ? new Date(reagent.expiryDate).getTime() : null\n case 'storage':\n return reagent.storageCondition\n case 'location':\n return reagent.location\n case 'stock':\n return reagent.stockLevel\n case 'supplier':\n return reagent.supplier\n default:\n return null\n }\n}\n\n// Helpers\nfunction isExpired(reagent: Reagent): boolean {\n if (!reagent.expiryDate) return false\n const expiry = new Date(reagent.expiryDate)\n return expiry < new Date()\n}\n\nfunction isExpiringSoon(reagent: Reagent, daysThreshold = 30): boolean {\n if (!reagent.expiryDate) return false\n const expiry = new Date(reagent.expiryDate)\n const now = new Date()\n const daysUntilExpiry = (expiry.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)\n return daysUntilExpiry > 0 && daysUntilExpiry <= daysThreshold\n}\n\nfunction isLowStock(reagent: Reagent): boolean {\n if (reagent.stockLevel === undefined) return false\n return reagent.stockLevel <= props.lowStockThreshold\n}\n\nfunction formatExpiryDate(date: Date | string | undefined): string {\n if (!date) return '-'\n const d = new Date(date)\n return d.toLocaleDateString('en-US', { year: 'numeric', month: 'short' })\n}\n\nfunction getStockLevel(reagent: Reagent): number {\n return reagent.stockLevel ?? 0\n}\n\nfunction getStockFillClass(level: number): string {\n if (level >= 50) return 'mint-reagent-list__stock-fill--good'\n if (level >= 20) return 'mint-reagent-list__stock-fill--warning'\n return 'mint-reagent-list__stock-fill--low'\n}\n\nfunction getRowClass(reagent: Reagent): string[] {\n const classes: string[] = ['mint-reagent-list__row']\n if (isExpired(reagent)) classes.push('mint-reagent-list__row--expired')\n else if (isExpiringSoon(reagent)) classes.push('mint-reagent-list__row--expiring-soon')\n if (isLowStock(reagent)) classes.push('mint-reagent-list__row--low-stock')\n if (draggedId.value === reagent.id) classes.push('mint-reagent-list__row--dragging')\n if (dragOverId.value === reagent.id) classes.push('mint-reagent-list__row--drag-over')\n return classes\n}\n\n// Actions\nfunction handleSort(column: ReagentColumn) {\n if (!props.sortable) return\n if (sortColumn.value === column) {\n sortDirection.value = sortDirection.value === 'asc' ? 'desc' : 'asc'\n } else {\n sortColumn.value = column\n sortDirection.value = 'asc'\n }\n}\n\nfunction handleEdit(reagent: Reagent) {\n emit('edit', reagent)\n}\n\nfunction handleRemove(reagentId: string) {\n emit('remove', reagentId)\n const updated = (props.modelValue || []).filter((r) => r.id !== reagentId)\n emit('update:modelValue', updated)\n}\n\nfunction handleAdd() {\n emit('add')\n}\n\n// Drag and drop\nfunction handleDragStart(event: DragEvent, reagent: Reagent) {\n if (props.readonly || !props.sortable) return\n draggedId.value = reagent.id\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', reagent.id)\n }\n}\n\nfunction handleDragOver(event: DragEvent, reagent: Reagent) {\n if (props.readonly || !props.sortable || !draggedId.value) return\n event.preventDefault()\n dragOverId.value = reagent.id\n}\n\nfunction handleDragLeave() {\n dragOverId.value = null\n}\n\nfunction handleDrop(event: DragEvent, targetReagent: Reagent) {\n if (props.readonly || !props.sortable || !draggedId.value) return\n event.preventDefault()\n\n const reagents = [...(props.modelValue || [])]\n const fromIndex = reagents.findIndex((r) => r.id === draggedId.value)\n const toIndex = reagents.findIndex((r) => r.id === targetReagent.id)\n\n if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) {\n draggedId.value = null\n dragOverId.value = null\n return\n }\n\n const [removed] = reagents.splice(fromIndex, 1)\n reagents.splice(toIndex, 0, removed)\n\n emit('reorder', draggedId.value, fromIndex, toIndex)\n emit('update:modelValue', reagents)\n\n draggedId.value = null\n dragOverId.value = null\n}\n\nfunction handleDragEnd() {\n draggedId.value = null\n dragOverId.value = null\n}\n</script>\n\n<template>\n <div class=\"mint-reagent-list\">\n <!-- Header -->\n <div class=\"mint-reagent-list__header\">\n <!-- Search -->\n <div v-if=\"searchable\" class=\"mint-reagent-list__search\">\n <svg class=\"mint-reagent-list__search-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" />\n </svg>\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n class=\"mint-reagent-list__search-input\"\n placeholder=\"Search reagents...\"\n />\n </div>\n\n <!-- Add button -->\n <button\n v-if=\"!readonly\"\n type=\"button\"\n class=\"mint-reagent-list__add-btn\"\n @click=\"handleAdd\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n Add\n </button>\n </div>\n\n <!-- Table -->\n <table v-if=\"filteredReagents.length > 0\" class=\"mint-reagent-list__table\">\n <thead>\n <tr>\n <th v-if=\"sortable && !readonly\" style=\"width: 24px\" />\n <th\n v-for=\"col in columns\"\n :key=\"col\"\n :class=\"[sortable ? 'mint-reagent-list__col--sortable' : '']\"\n @click=\"handleSort(col)\"\n >\n {{ columnLabels[col] }}\n <svg\n v-if=\"sortable\"\n :class=\"[\n 'mint-reagent-list__sort-icon',\n sortColumn === col ? 'mint-reagent-list__sort-icon--active' : '',\n ]\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n v-if=\"sortColumn === col && sortDirection === 'desc'\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M19 9l-7 7-7-7\"\n />\n <path\n v-else\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M5 15l7-7 7 7\"\n />\n </svg>\n </th>\n <th v-if=\"!readonly\" style=\"width: 80px\" />\n </tr>\n </thead>\n <tbody>\n <tr\n v-for=\"reagent in filteredReagents\"\n :key=\"reagent.id\"\n :class=\"getRowClass(reagent)\"\n :draggable=\"sortable && !readonly\"\n @dragstart=\"handleDragStart($event, reagent)\"\n @dragover=\"handleDragOver($event, reagent)\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop($event, reagent)\"\n @dragend=\"handleDragEnd\"\n >\n <!-- Drag handle -->\n <td v-if=\"sortable && !readonly\">\n <svg class=\"mint-reagent-list__drag-handle\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8h16M4 16h16\" />\n </svg>\n </td>\n\n <!-- Name -->\n <td v-if=\"columns.includes('name')\" class=\"mint-reagent-list__cell mint-reagent-list__cell--name\">\n <a\n v-if=\"reagent.url\"\n :href=\"reagent.url\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"mint-reagent-list__link\"\n >\n {{ reagent.name }}\n </a>\n <span v-else>{{ reagent.name }}</span>\n </td>\n\n <!-- Catalog number -->\n <td v-if=\"columns.includes('catalog')\" class=\"mint-reagent-list__cell mint-reagent-list__cell--muted\">\n {{ reagent.catalogNumber || '-' }}\n </td>\n\n <!-- Lot number -->\n <td v-if=\"columns.includes('lot')\" class=\"mint-reagent-list__cell mint-reagent-list__cell--muted\">\n {{ reagent.lotNumber || '-' }}\n </td>\n\n <!-- Expiry -->\n <td v-if=\"columns.includes('expiry')\" class=\"mint-reagent-list__cell\">\n <span v-if=\"isExpired(reagent)\" class=\"mint-reagent-list__expired-text\">\n {{ formatExpiryDate(reagent.expiryDate) }}\n </span>\n <span v-else-if=\"isExpiringSoon(reagent)\" class=\"mint-reagent-list__expiring-text\">\n {{ formatExpiryDate(reagent.expiryDate) }}\n </span>\n <span v-else>\n {{ formatExpiryDate(reagent.expiryDate) }}\n </span>\n\n <!-- Expiry badge -->\n <span v-if=\"isExpired(reagent)\" class=\"mint-reagent-list__badge mint-reagent-list__badge--error\">\n <svg class=\"mint-reagent-list__badge-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n Expired\n </span>\n <span v-else-if=\"isExpiringSoon(reagent)\" class=\"mint-reagent-list__badge mint-reagent-list__badge--warning\">\n <svg class=\"mint-reagent-list__badge-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\" />\n </svg>\n Soon\n </span>\n </td>\n\n <!-- Storage -->\n <td v-if=\"columns.includes('storage')\" class=\"mint-reagent-list__cell\">\n <span\n v-if=\"reagent.storageCondition\"\n :class=\"['mint-reagent-list__storage', `mint-reagent-list__storage--${reagent.storageCondition}`]\"\n >\n {{ reagent.storageCondition }}\n </span>\n <span v-else class=\"mint-reagent-list__cell--muted\">-</span>\n </td>\n\n <!-- Location -->\n <td v-if=\"columns.includes('location')\" class=\"mint-reagent-list__cell mint-reagent-list__cell--muted\">\n {{ reagent.location || '-' }}\n </td>\n\n <!-- Stock -->\n <td v-if=\"columns.includes('stock') && showStockLevel\" class=\"mint-reagent-list__cell\">\n <div v-if=\"reagent.stockLevel !== undefined\" class=\"mint-reagent-list__stock\">\n <div class=\"mint-reagent-list__stock-bar\">\n <div\n :class=\"['mint-reagent-list__stock-fill', getStockFillClass(getStockLevel(reagent))]\"\n :style=\"{ width: `${Math.min(100, getStockLevel(reagent))}%` }\"\n />\n </div>\n <span class=\"mint-reagent-list__stock-text\">\n {{ getStockLevel(reagent) }}%\n </span>\n </div>\n <span v-else class=\"mint-reagent-list__cell--muted\">-</span>\n </td>\n\n <!-- Supplier -->\n <td v-if=\"columns.includes('supplier')\" class=\"mint-reagent-list__cell mint-reagent-list__cell--muted\">\n {{ reagent.supplier || '-' }}\n </td>\n\n <!-- Actions -->\n <td v-if=\"!readonly\" class=\"mint-reagent-list__cell mint-reagent-list__cell--actions\">\n <button\n type=\"button\"\n class=\"mint-reagent-list__action-btn\"\n aria-label=\"Edit\"\n @click=\"handleEdit(reagent)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"mint-reagent-list__action-btn mint-reagent-list__action-btn--danger\"\n aria-label=\"Remove\"\n @click=\"handleRemove(reagent.id)\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\" />\n </svg>\n </button>\n </td>\n </tr>\n </tbody>\n </table>\n\n <!-- Empty state -->\n <div v-else class=\"mint-reagent-list__empty\">\n <svg class=\"mint-reagent-list__empty-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\" />\n </svg>\n <p class=\"mint-reagent-list__empty-text\">\n {{ searchQuery ? 'No reagents found' : 'No reagents added yet' }}\n </p>\n <button\n v-if=\"!readonly && !searchQuery\"\n type=\"button\"\n class=\"mint-reagent-list__empty-btn\"\n @click=\"handleAdd\"\n >\n Add Reagent\n </button>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/reagent-list.css';\n</style>\n","<script setup lang=\"ts\">\n/** Animated SVG spinner with size and color variants, and a visually hidden status label for screen readers. */\ninterface Props {\n size?: 'xs' | 'sm' | 'md' | 'lg'\n variant?: 'primary' | 'cta' | 'muted'\n label?: string\n}\n\nwithDefaults(defineProps<Props>(), {\n size: 'md',\n variant: 'primary',\n label: 'Loading',\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-spinner',\n `mint-spinner--${size}`,\n `mint-spinner--${variant}`,\n ]\"\n role=\"status\"\n >\n <svg\n class=\"mint-spinner__svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n style=\"opacity: 0.25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n />\n <path\n style=\"opacity: 0.75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n <span class=\"mint-spinner__label\">{{ label }}</span>\n </div>\n</template>\n\n<style>\n@import '../styles/components/loading-spinner.css';\n</style>\n","<script setup lang=\"ts\">\n/** Animated SVG spinner with size and color variants, and a visually hidden status label for screen readers. */\ninterface Props {\n size?: 'xs' | 'sm' | 'md' | 'lg'\n variant?: 'primary' | 'cta' | 'muted'\n label?: string\n}\n\nwithDefaults(defineProps<Props>(), {\n size: 'md',\n variant: 'primary',\n label: 'Loading',\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-spinner',\n `mint-spinner--${size}`,\n `mint-spinner--${variant}`,\n ]\"\n role=\"status\"\n >\n <svg\n class=\"mint-spinner__svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n style=\"opacity: 0.25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n />\n <path\n style=\"opacity: 0.75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n <span class=\"mint-spinner__label\">{{ label }}</span>\n </div>\n</template>\n\n<style>\n@import '../styles/components/loading-spinner.css';\n</style>\n","<script setup lang=\"ts\">\n/** Wizard modal that auto-groups samples by parsing naming patterns or an uploaded CSV file. */\nimport { ref, computed, watch, nextTick } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseInput from './BaseInput.vue'\nimport StepWizard from './StepWizard.vue'\nimport LoadingSpinner from './LoadingSpinner.vue'\nimport AlertBox from './AlertBox.vue'\nimport { useAutoGroup, parseCSV } from '../composables/useAutoGroup'\nimport { useApi } from '../composables/useApi'\nimport type { WizardStep } from '../types/components'\nimport type { AutoGroupResult } from '../types/auto-group'\n\nexport interface Props {\n modelValue: boolean\n samples?: string[]\n experimentId?: number\n /** Pre-fetched design data — bypasses API fetch when provided */\n designData?: Record<string, unknown>\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n samples: () => [],\n experimentId: undefined,\n designData: undefined,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n 'apply': [result: AutoGroupResult]\n}>()\n\nconst autoGroup = useAutoGroup()\nconst api = useApi()\nconst wizardRef = ref<InstanceType<typeof StepWizard> | null>(null)\nconst currentStep = ref(0)\nconst isDragOver = ref(false)\nconst csvFileName = ref('')\nconst experimentLoading = ref(false)\nconst experimentError = ref<string | null>(null)\nconst csvError = ref<string | null>(null)\n\n// Pre-fill from props\nwatch(() => props.modelValue, (open) => {\n if (open) {\n autoGroup.reset()\n currentStep.value = 0\n csvFileName.value = ''\n experimentError.value = null\n csvError.value = null\n if (props.experimentId || props.designData) {\n fetchExperimentData()\n } else if (props.samples.length > 0) {\n autoGroup.rawText.value = props.samples.join('\\n')\n }\n }\n}, { immediate: true })\n\n// Dynamic steps: skip outlier step when no outliers\nconst allSteps: WizardStep[] = [\n { id: 'input', label: 'Input' },\n { id: 'outliers', label: 'Outliers' },\n { id: 'fields', label: 'Fields' },\n { id: 'preview', label: 'Preview' },\n]\n\nconst dynamicSteps = computed<WizardStep[]>(() => {\n if (autoGroup.hasOutliers.value) {\n return allSteps\n }\n return allSteps.filter(s => s.id !== 'outliers')\n})\n\n// Map current step index to step ID\nconst currentStepId = computed(() => {\n const step = dynamicSteps.value[currentStep.value]\n return step?.id ?? 'input'\n})\n\n// Clamp step when dynamic steps change\nwatch(() => dynamicSteps.value.length, (newLen) => {\n if (currentStep.value >= newLen) {\n currentStep.value = newLen - 1\n }\n})\n\n// Step validity\nconst inputValid = computed(() => autoGroup.samples.value.length > 0)\nconst fieldsValid = computed(() => autoGroup.enabledFields.value.size > 0)\n\nwatch([inputValid, fieldsValid, dynamicSteps], () => {\n if (!wizardRef.value) return\n const steps = dynamicSteps.value\n for (let i = 0; i < steps.length; i++) {\n const id = steps[i].id\n if (id === 'input') wizardRef.value.setStepValid(i, inputValid.value)\n else if (id === 'outliers') wizardRef.value.setStepValid(i, true)\n else if (id === 'fields') wizardRef.value.setStepValid(i, fieldsValid.value)\n else if (id === 'preview') wizardRef.value.setStepValid(i, true)\n }\n}, { immediate: true })\n\n// Navigation\nfunction handleNext() {\n if (currentStepId.value === 'input') {\n autoGroup.parseInput()\n }\n // StepWizard handles the actual navigation via v-model\n}\n\nfunction handleApply() {\n emit('apply', autoGroup.result.value)\n emit('update:modelValue', false)\n}\n\nfunction handleCancel() {\n emit('update:modelValue', false)\n}\n\n// Experiment data fetching\nasync function fetchExperimentData() {\n if (!props.experimentId && !props.designData) return\n\n autoGroup.inputMode.value = 'experiment'\n experimentLoading.value = true\n experimentError.value = null\n\n try {\n let pluginData: Record<string, unknown> | undefined\n\n if (props.designData) {\n pluginData = props.designData\n } else {\n const result = await api.get<Record<string, unknown>>(\n `/experiments/${props.experimentId}/data`,\n )\n pluginData = result.data as Record<string, unknown> | undefined\n }\n\n if (!pluginData || typeof pluginData !== 'object') {\n experimentError.value = 'No design data found for this experiment'\n return\n }\n\n const success = autoGroup.loadExperimentData(pluginData)\n if (!success) {\n experimentError.value = 'No sample metadata found in this experiment'\n return\n }\n\n // Auto-advance to Fields step\n await nextTick()\n const fieldsIdx = dynamicSteps.value.findIndex(s => s.id === 'fields')\n if (fieldsIdx >= 0) {\n currentStep.value = fieldsIdx\n }\n } catch (e) {\n experimentError.value = e instanceof Error ? e.message : 'Failed to fetch experiment data'\n } finally {\n experimentLoading.value = false\n }\n}\n\n// CSV file handling\nfunction handleFileDrop(event: DragEvent) {\n event.preventDefault()\n isDragOver.value = false\n const files = event.dataTransfer?.files\n if (files && files.length > 0) {\n processFile(files[0])\n }\n}\n\nfunction handleFileInput(event: Event) {\n const target = event.target as HTMLInputElement\n if (target.files && target.files.length > 0) {\n processFile(target.files[0])\n }\n}\n\nasync function processFile(file: File) {\n if (!file.name.endsWith('.csv') && !file.name.endsWith('.tsv')) return\n\n csvError.value = null\n try {\n const text = await file.text()\n const parsed = parseCSV(text)\n autoGroup.inputMode.value = 'csv'\n autoGroup.csvData.value = parsed\n csvFileName.value = file.name\n } catch (e) {\n csvError.value = e instanceof Error ? e.message : 'Failed to parse file'\n }\n}\n\nfunction clearCsvFile() {\n autoGroup.csvData.value = null\n autoGroup.inputMode.value = 'paste'\n csvFileName.value = ''\n}\n\n// Experiment metadata field names (for display in experiment mode)\nconst experimentFieldNames = computed(() =>\n autoGroup.csvData.value?.columns.filter(c => c !== 'sample_name') ?? [],\n)\n\n// Last step check\nconst isLastStep = computed(() => currentStep.value === dynamicSteps.value.length - 1)\nconst isFirstStep = computed(() => currentStep.value === 0)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n title=\"Smart Group\"\n size=\"lg\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @close=\"handleCancel\"\n >\n <div class=\"mint-auto-group\">\n <StepWizard\n ref=\"wizardRef\"\n v-model=\"currentStep\"\n :steps=\"dynamicSteps\"\n :linear=\"true\"\n size=\"sm\"\n >\n <!-- Step: Input -->\n <template #step-input>\n <div class=\"mint-auto-group__input-step\">\n <!-- Mode toggle -->\n <div class=\"mint-auto-group__mode-toggle\">\n <BaseButton\n v-if=\"experimentId || designData\"\n :variant=\"autoGroup.inputMode.value === 'experiment' ? 'primary' : 'secondary'\"\n size=\"sm\"\n :disabled=\"experimentLoading\"\n @click=\"fetchExperimentData\"\n >\n Experiment\n </BaseButton>\n <BaseButton\n :variant=\"autoGroup.inputMode.value === 'paste' ? 'primary' : 'secondary'\"\n size=\"sm\"\n @click=\"autoGroup.inputMode.value = 'paste'\"\n >\n Paste\n </BaseButton>\n <BaseButton\n :variant=\"autoGroup.inputMode.value === 'csv' ? 'primary' : 'secondary'\"\n size=\"sm\"\n @click=\"autoGroup.inputMode.value = 'csv'\"\n >\n CSV\n </BaseButton>\n </div>\n\n <!-- Experiment mode -->\n <div v-if=\"autoGroup.inputMode.value === 'experiment'\" class=\"mint-auto-group__experiment\">\n <div v-if=\"experimentLoading\" class=\"mint-auto-group__experiment-loading\">\n <LoadingSpinner size=\"sm\" />\n <span>Loading experiment metadata…</span>\n </div>\n <div v-else-if=\"experimentError\" class=\"mint-auto-group__experiment-error\">\n <p>{{ experimentError }}</p>\n <p>\n Switch to <button type=\"button\" class=\"mint-auto-group__link-btn\" @click=\"autoGroup.inputMode.value = 'paste'\">Paste</button>\n or <button type=\"button\" class=\"mint-auto-group__link-btn\" @click=\"autoGroup.inputMode.value = 'csv'\">CSV</button> mode instead.\n </p>\n </div>\n <div v-else-if=\"autoGroup.csvData.value\" class=\"mint-auto-group__experiment-loaded\">\n <div class=\"mint-auto-group__experiment-summary\">\n <span class=\"mint-auto-group__experiment-stat\">\n <strong>{{ autoGroup.csvData.value.rows.length }}</strong> samples\n </span>\n <span class=\"mint-auto-group__experiment-stat\">\n <strong>{{ experimentFieldNames.length }}</strong> metadata fields\n </span>\n </div>\n <div class=\"mint-auto-group__experiment-fields\">\n <span\n v-for=\"col in experimentFieldNames\"\n :key=\"col\"\n class=\"mint-auto-group__field-value\"\n >{{ col }}</span>\n </div>\n </div>\n </div>\n\n <!-- Paste mode -->\n <div v-if=\"autoGroup.inputMode.value === 'paste'\" class=\"mint-auto-group__paste\">\n <textarea\n v-model=\"autoGroup.rawText.value\"\n class=\"mint-auto-group__textarea\"\n rows=\"12\"\n placeholder=\"Paste sample names, one per line...\"\n />\n <div v-if=\"autoGroup.samples.value.length > 0\" class=\"mint-auto-group__sample-count\">\n {{ autoGroup.samples.value.length }} samples\n </div>\n </div>\n\n <!-- CSV mode -->\n <div v-if=\"autoGroup.inputMode.value === 'csv'\" class=\"mint-auto-group__csv\">\n <div v-if=\"!autoGroup.csvData.value\"\n :class=\"[\n 'mint-auto-group__dropzone',\n isDragOver ? 'mint-auto-group__dropzone--dragover' : '',\n ]\"\n @drop=\"handleFileDrop\"\n @dragover.prevent=\"isDragOver = true\"\n @dragleave=\"isDragOver = false\"\n >\n <input\n type=\"file\"\n accept=\".csv,.tsv\"\n class=\"mint-auto-group__file-input\"\n @change=\"handleFileInput\"\n />\n <svg class=\"mint-auto-group__upload-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\" d=\"M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n <p class=\"mint-auto-group__upload-text\">\n <span class=\"mint-auto-group__upload-highlight\">Click to upload</span>\n or drag and drop a CSV file\n </p>\n <p class=\"mint-auto-group__upload-hint\">\n CSV or TSV with a header row. First column or column named\n \"Sample\" / \"File Name\" is used as sample identifier.\n Other columns become grouping fields.\n </p>\n </div>\n\n <div v-if=\"csvError && !autoGroup.csvData.value\" class=\"mint-auto-group__csv-error\">\n {{ csvError }}\n </div>\n\n <div v-if=\"autoGroup.csvData.value\" class=\"mint-auto-group__file-info\">\n <svg class=\"mint-auto-group__file-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n <span class=\"mint-auto-group__file-name\">{{ csvFileName }}</span>\n <span class=\"mint-auto-group__file-rows\">{{ autoGroup.csvData.value.rows.length }} rows</span>\n <button type=\"button\" class=\"mint-auto-group__file-clear\" @click=\"clearCsvFile\">\n Change file\n </button>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Step: Outliers -->\n <template #step-outliers>\n <div class=\"mint-auto-group__outlier-step\">\n <div class=\"mint-auto-group__outlier-banner\">\n {{ autoGroup.outliers.value.length }} of {{ autoGroup.samples.value.length }}\n samples have irregular structure\n (fewer than {{ autoGroup.dominantFieldCount.value }} fields, delimiter\n <code>{{ autoGroup.delimiter.value }}</code>)\n </div>\n\n <div class=\"mint-auto-group__outlier-batch\">\n <BaseButton size=\"sm\" variant=\"secondary\" @click=\"autoGroup.setAllOutlierActions('include')\">\n Include All\n </BaseButton>\n <BaseButton size=\"sm\" variant=\"secondary\" @click=\"autoGroup.setAllOutlierActions('exclude')\">\n Exclude All\n </BaseButton>\n </div>\n\n <div class=\"mint-auto-group__outlier-list\">\n <div\n v-for=\"outlier in autoGroup.outliers.value\"\n :key=\"outlier.index\"\n class=\"mint-auto-group__outlier-item\"\n >\n <div class=\"mint-auto-group__outlier-info\">\n <code class=\"mint-auto-group__outlier-name\">{{ outlier.sample }}</code>\n <span class=\"mint-auto-group__outlier-badge\">{{ outlier.fieldCount }} fields</span>\n </div>\n <div class=\"mint-auto-group__outlier-actions\">\n <button\n type=\"button\"\n :class=\"['mint-auto-group__action-btn', outlier.action === 'include' ? 'mint-auto-group__action-btn--active' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'include')\"\n >Include</button>\n <button\n type=\"button\"\n :class=\"['mint-auto-group__action-btn', outlier.action === 'exclude' ? 'mint-auto-group__action-btn--active mint-auto-group__action-btn--exclude' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'exclude')\"\n >Exclude</button>\n <button\n type=\"button\"\n :class=\"['mint-auto-group__action-btn', outlier.action === 'qc' ? 'mint-auto-group__action-btn--active mint-auto-group__action-btn--qc' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'qc')\"\n >QC</button>\n </div>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Step: Fields -->\n <template #step-fields>\n <div class=\"mint-auto-group__field-step\">\n <div class=\"mint-auto-group__field-grid\">\n <div\n v-for=\"col in autoGroup.effectiveColumns.value\"\n :key=\"col.index\"\n :class=\"[\n 'mint-auto-group__field-card',\n autoGroup.enabledFields.value.has(col.index) ? 'mint-auto-group__field-card--enabled' : '',\n ]\"\n >\n <div class=\"mint-auto-group__field-header\">\n <label class=\"mint-auto-group__field-toggle\">\n <input\n type=\"checkbox\"\n :checked=\"autoGroup.enabledFields.value.has(col.index)\"\n @change=\"autoGroup.toggleField(col.index)\"\n />\n <span class=\"mint-auto-group__field-toggle-label\">Group by</span>\n </label>\n <span class=\"mint-auto-group__field-cardinality\">{{ col.cardinality }} unique</span>\n </div>\n\n <BaseInput\n :model-value=\"col.name\"\n placeholder=\"Field name...\"\n class=\"mint-auto-group__field-name-input\"\n @update:model-value=\"autoGroup.renameField(col.index, String($event ?? ''))\"\n />\n\n <div class=\"mint-auto-group__field-values\">\n <span\n v-for=\"val in col.uniqueValues.slice(0, 5)\"\n :key=\"val\"\n class=\"mint-auto-group__field-value\"\n >{{ val }}</span>\n <span\n v-if=\"col.uniqueValues.length > 5\"\n class=\"mint-auto-group__field-more\"\n >+{{ col.uniqueValues.length - 5 }} more</span>\n </div>\n </div>\n </div>\n\n <div v-if=\"autoGroup.enabledFields.value.size > 0\" class=\"mint-auto-group__field-summary\">\n Grouping by:\n <strong>{{\n autoGroup.effectiveColumns.value\n .filter(c => autoGroup.enabledFields.value.has(c.index))\n .map(c => c.name)\n .join(' / ')\n }}</strong>\n </div>\n\n <div v-if=\"autoGroup.enabledFields.value.size === 0\" class=\"mint-auto-group__field-warning\">\n Select at least one field for grouping\n </div>\n </div>\n </template>\n\n <!-- Step: Preview -->\n <template #step-preview>\n <div class=\"mint-auto-group__preview-step\">\n <div class=\"mint-auto-group__preview-summary\">\n <span class=\"mint-auto-group__preview-stat\">\n <strong>{{ autoGroup.groups.value.length }}</strong> groups\n </span>\n <span class=\"mint-auto-group__preview-stat\">\n <strong>{{ autoGroup.groups.value.reduce((sum, g) => sum + g.samples.length, 0) }}</strong> samples\n </span>\n <span v-if=\"autoGroup.excludedSamples.value.length > 0\" class=\"mint-auto-group__preview-stat mint-auto-group__preview-stat--excluded\">\n <strong>{{ autoGroup.excludedSamples.value.length }}</strong> excluded\n </span>\n </div>\n\n <AlertBox\n v-if=\"autoGroup.allSingletons.value\"\n type=\"warning\"\n title=\"Each group has only one sample\"\n >\n Your enabled fields produce a unique key for every row, so no aggregation is happening. Go back to <em>Fields</em> and disable any column whose values are unique per sample.\n </AlertBox>\n\n <div class=\"mint-auto-group__preview-groups\">\n <details\n v-for=\"group in autoGroup.groups.value\"\n :key=\"group.name\"\n class=\"mint-auto-group__preview-group\"\n >\n <summary class=\"mint-auto-group__preview-group-header\">\n <span\n class=\"mint-auto-group__preview-dot\"\n :style=\"{ backgroundColor: group.color }\"\n />\n <span class=\"mint-auto-group__preview-group-name\">{{ group.name }}</span>\n <span\n class=\"mint-auto-group__preview-group-count\"\n :style=\"{ backgroundColor: group.color + '20', color: group.color }\"\n >{{ group.samples.length }}</span>\n </summary>\n <div class=\"mint-auto-group__preview-samples\">\n <span\n v-for=\"sample in group.samples\"\n :key=\"sample\"\n class=\"mint-auto-group__preview-sample\"\n >{{ sample }}</span>\n </div>\n </details>\n </div>\n\n <div v-if=\"autoGroup.excludedSamples.value.length > 0\" class=\"mint-auto-group__preview-excluded\">\n <div class=\"mint-auto-group__preview-excluded-title\">Excluded Samples</div>\n <div class=\"mint-auto-group__preview-excluded-list\">\n <span\n v-for=\"sample in autoGroup.excludedSamples.value\"\n :key=\"sample\"\n class=\"mint-auto-group__preview-sample mint-auto-group__preview-sample--excluded\"\n >{{ sample }}</span>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Custom navigation -->\n <template #navigation=\"{ goBack, goNext, canProceed }\">\n <div class=\"mint-auto-group__nav\">\n <BaseButton variant=\"secondary\" @click=\"handleCancel\">\n Cancel\n </BaseButton>\n <div class=\"mint-auto-group__nav-spacer\" />\n <BaseButton\n v-if=\"!isFirstStep\"\n variant=\"secondary\"\n @click=\"goBack\"\n >\n Back\n </BaseButton>\n <BaseButton\n v-if=\"!isLastStep\"\n variant=\"primary\"\n :disabled=\"!canProceed\"\n @click=\"() => { handleNext(); goNext() }\"\n >\n Next\n </BaseButton>\n <BaseButton\n v-if=\"isLastStep\"\n variant=\"primary\"\n @click=\"handleApply\"\n >\n Apply\n </BaseButton>\n </div>\n </template>\n </StepWizard>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/auto-group-modal.css';\n</style>\n","<script setup lang=\"ts\">\n/** Wizard modal that auto-groups samples by parsing naming patterns or an uploaded CSV file. */\nimport { ref, computed, watch, nextTick } from 'vue'\nimport BaseModal from './BaseModal.vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseInput from './BaseInput.vue'\nimport StepWizard from './StepWizard.vue'\nimport LoadingSpinner from './LoadingSpinner.vue'\nimport AlertBox from './AlertBox.vue'\nimport { useAutoGroup, parseCSV } from '../composables/useAutoGroup'\nimport { useApi } from '../composables/useApi'\nimport type { WizardStep } from '../types/components'\nimport type { AutoGroupResult } from '../types/auto-group'\n\nexport interface Props {\n modelValue: boolean\n samples?: string[]\n experimentId?: number\n /** Pre-fetched design data — bypasses API fetch when provided */\n designData?: Record<string, unknown>\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n samples: () => [],\n experimentId: undefined,\n designData: undefined,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n 'apply': [result: AutoGroupResult]\n}>()\n\nconst autoGroup = useAutoGroup()\nconst api = useApi()\nconst wizardRef = ref<InstanceType<typeof StepWizard> | null>(null)\nconst currentStep = ref(0)\nconst isDragOver = ref(false)\nconst csvFileName = ref('')\nconst experimentLoading = ref(false)\nconst experimentError = ref<string | null>(null)\nconst csvError = ref<string | null>(null)\n\n// Pre-fill from props\nwatch(() => props.modelValue, (open) => {\n if (open) {\n autoGroup.reset()\n currentStep.value = 0\n csvFileName.value = ''\n experimentError.value = null\n csvError.value = null\n if (props.experimentId || props.designData) {\n fetchExperimentData()\n } else if (props.samples.length > 0) {\n autoGroup.rawText.value = props.samples.join('\\n')\n }\n }\n}, { immediate: true })\n\n// Dynamic steps: skip outlier step when no outliers\nconst allSteps: WizardStep[] = [\n { id: 'input', label: 'Input' },\n { id: 'outliers', label: 'Outliers' },\n { id: 'fields', label: 'Fields' },\n { id: 'preview', label: 'Preview' },\n]\n\nconst dynamicSteps = computed<WizardStep[]>(() => {\n if (autoGroup.hasOutliers.value) {\n return allSteps\n }\n return allSteps.filter(s => s.id !== 'outliers')\n})\n\n// Map current step index to step ID\nconst currentStepId = computed(() => {\n const step = dynamicSteps.value[currentStep.value]\n return step?.id ?? 'input'\n})\n\n// Clamp step when dynamic steps change\nwatch(() => dynamicSteps.value.length, (newLen) => {\n if (currentStep.value >= newLen) {\n currentStep.value = newLen - 1\n }\n})\n\n// Step validity\nconst inputValid = computed(() => autoGroup.samples.value.length > 0)\nconst fieldsValid = computed(() => autoGroup.enabledFields.value.size > 0)\n\nwatch([inputValid, fieldsValid, dynamicSteps], () => {\n if (!wizardRef.value) return\n const steps = dynamicSteps.value\n for (let i = 0; i < steps.length; i++) {\n const id = steps[i].id\n if (id === 'input') wizardRef.value.setStepValid(i, inputValid.value)\n else if (id === 'outliers') wizardRef.value.setStepValid(i, true)\n else if (id === 'fields') wizardRef.value.setStepValid(i, fieldsValid.value)\n else if (id === 'preview') wizardRef.value.setStepValid(i, true)\n }\n}, { immediate: true })\n\n// Navigation\nfunction handleNext() {\n if (currentStepId.value === 'input') {\n autoGroup.parseInput()\n }\n // StepWizard handles the actual navigation via v-model\n}\n\nfunction handleApply() {\n emit('apply', autoGroup.result.value)\n emit('update:modelValue', false)\n}\n\nfunction handleCancel() {\n emit('update:modelValue', false)\n}\n\n// Experiment data fetching\nasync function fetchExperimentData() {\n if (!props.experimentId && !props.designData) return\n\n autoGroup.inputMode.value = 'experiment'\n experimentLoading.value = true\n experimentError.value = null\n\n try {\n let pluginData: Record<string, unknown> | undefined\n\n if (props.designData) {\n pluginData = props.designData\n } else {\n const result = await api.get<Record<string, unknown>>(\n `/experiments/${props.experimentId}/data`,\n )\n pluginData = result.data as Record<string, unknown> | undefined\n }\n\n if (!pluginData || typeof pluginData !== 'object') {\n experimentError.value = 'No design data found for this experiment'\n return\n }\n\n const success = autoGroup.loadExperimentData(pluginData)\n if (!success) {\n experimentError.value = 'No sample metadata found in this experiment'\n return\n }\n\n // Auto-advance to Fields step\n await nextTick()\n const fieldsIdx = dynamicSteps.value.findIndex(s => s.id === 'fields')\n if (fieldsIdx >= 0) {\n currentStep.value = fieldsIdx\n }\n } catch (e) {\n experimentError.value = e instanceof Error ? e.message : 'Failed to fetch experiment data'\n } finally {\n experimentLoading.value = false\n }\n}\n\n// CSV file handling\nfunction handleFileDrop(event: DragEvent) {\n event.preventDefault()\n isDragOver.value = false\n const files = event.dataTransfer?.files\n if (files && files.length > 0) {\n processFile(files[0])\n }\n}\n\nfunction handleFileInput(event: Event) {\n const target = event.target as HTMLInputElement\n if (target.files && target.files.length > 0) {\n processFile(target.files[0])\n }\n}\n\nasync function processFile(file: File) {\n if (!file.name.endsWith('.csv') && !file.name.endsWith('.tsv')) return\n\n csvError.value = null\n try {\n const text = await file.text()\n const parsed = parseCSV(text)\n autoGroup.inputMode.value = 'csv'\n autoGroup.csvData.value = parsed\n csvFileName.value = file.name\n } catch (e) {\n csvError.value = e instanceof Error ? e.message : 'Failed to parse file'\n }\n}\n\nfunction clearCsvFile() {\n autoGroup.csvData.value = null\n autoGroup.inputMode.value = 'paste'\n csvFileName.value = ''\n}\n\n// Experiment metadata field names (for display in experiment mode)\nconst experimentFieldNames = computed(() =>\n autoGroup.csvData.value?.columns.filter(c => c !== 'sample_name') ?? [],\n)\n\n// Last step check\nconst isLastStep = computed(() => currentStep.value === dynamicSteps.value.length - 1)\nconst isFirstStep = computed(() => currentStep.value === 0)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n title=\"Smart Group\"\n size=\"lg\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @close=\"handleCancel\"\n >\n <div class=\"mint-auto-group\">\n <StepWizard\n ref=\"wizardRef\"\n v-model=\"currentStep\"\n :steps=\"dynamicSteps\"\n :linear=\"true\"\n size=\"sm\"\n >\n <!-- Step: Input -->\n <template #step-input>\n <div class=\"mint-auto-group__input-step\">\n <!-- Mode toggle -->\n <div class=\"mint-auto-group__mode-toggle\">\n <BaseButton\n v-if=\"experimentId || designData\"\n :variant=\"autoGroup.inputMode.value === 'experiment' ? 'primary' : 'secondary'\"\n size=\"sm\"\n :disabled=\"experimentLoading\"\n @click=\"fetchExperimentData\"\n >\n Experiment\n </BaseButton>\n <BaseButton\n :variant=\"autoGroup.inputMode.value === 'paste' ? 'primary' : 'secondary'\"\n size=\"sm\"\n @click=\"autoGroup.inputMode.value = 'paste'\"\n >\n Paste\n </BaseButton>\n <BaseButton\n :variant=\"autoGroup.inputMode.value === 'csv' ? 'primary' : 'secondary'\"\n size=\"sm\"\n @click=\"autoGroup.inputMode.value = 'csv'\"\n >\n CSV\n </BaseButton>\n </div>\n\n <!-- Experiment mode -->\n <div v-if=\"autoGroup.inputMode.value === 'experiment'\" class=\"mint-auto-group__experiment\">\n <div v-if=\"experimentLoading\" class=\"mint-auto-group__experiment-loading\">\n <LoadingSpinner size=\"sm\" />\n <span>Loading experiment metadata…</span>\n </div>\n <div v-else-if=\"experimentError\" class=\"mint-auto-group__experiment-error\">\n <p>{{ experimentError }}</p>\n <p>\n Switch to <button type=\"button\" class=\"mint-auto-group__link-btn\" @click=\"autoGroup.inputMode.value = 'paste'\">Paste</button>\n or <button type=\"button\" class=\"mint-auto-group__link-btn\" @click=\"autoGroup.inputMode.value = 'csv'\">CSV</button> mode instead.\n </p>\n </div>\n <div v-else-if=\"autoGroup.csvData.value\" class=\"mint-auto-group__experiment-loaded\">\n <div class=\"mint-auto-group__experiment-summary\">\n <span class=\"mint-auto-group__experiment-stat\">\n <strong>{{ autoGroup.csvData.value.rows.length }}</strong> samples\n </span>\n <span class=\"mint-auto-group__experiment-stat\">\n <strong>{{ experimentFieldNames.length }}</strong> metadata fields\n </span>\n </div>\n <div class=\"mint-auto-group__experiment-fields\">\n <span\n v-for=\"col in experimentFieldNames\"\n :key=\"col\"\n class=\"mint-auto-group__field-value\"\n >{{ col }}</span>\n </div>\n </div>\n </div>\n\n <!-- Paste mode -->\n <div v-if=\"autoGroup.inputMode.value === 'paste'\" class=\"mint-auto-group__paste\">\n <textarea\n v-model=\"autoGroup.rawText.value\"\n class=\"mint-auto-group__textarea\"\n rows=\"12\"\n placeholder=\"Paste sample names, one per line...\"\n />\n <div v-if=\"autoGroup.samples.value.length > 0\" class=\"mint-auto-group__sample-count\">\n {{ autoGroup.samples.value.length }} samples\n </div>\n </div>\n\n <!-- CSV mode -->\n <div v-if=\"autoGroup.inputMode.value === 'csv'\" class=\"mint-auto-group__csv\">\n <div v-if=\"!autoGroup.csvData.value\"\n :class=\"[\n 'mint-auto-group__dropzone',\n isDragOver ? 'mint-auto-group__dropzone--dragover' : '',\n ]\"\n @drop=\"handleFileDrop\"\n @dragover.prevent=\"isDragOver = true\"\n @dragleave=\"isDragOver = false\"\n >\n <input\n type=\"file\"\n accept=\".csv,.tsv\"\n class=\"mint-auto-group__file-input\"\n @change=\"handleFileInput\"\n />\n <svg class=\"mint-auto-group__upload-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\" d=\"M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n <p class=\"mint-auto-group__upload-text\">\n <span class=\"mint-auto-group__upload-highlight\">Click to upload</span>\n or drag and drop a CSV file\n </p>\n <p class=\"mint-auto-group__upload-hint\">\n CSV or TSV with a header row. First column or column named\n \"Sample\" / \"File Name\" is used as sample identifier.\n Other columns become grouping fields.\n </p>\n </div>\n\n <div v-if=\"csvError && !autoGroup.csvData.value\" class=\"mint-auto-group__csv-error\">\n {{ csvError }}\n </div>\n\n <div v-if=\"autoGroup.csvData.value\" class=\"mint-auto-group__file-info\">\n <svg class=\"mint-auto-group__file-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n <span class=\"mint-auto-group__file-name\">{{ csvFileName }}</span>\n <span class=\"mint-auto-group__file-rows\">{{ autoGroup.csvData.value.rows.length }} rows</span>\n <button type=\"button\" class=\"mint-auto-group__file-clear\" @click=\"clearCsvFile\">\n Change file\n </button>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Step: Outliers -->\n <template #step-outliers>\n <div class=\"mint-auto-group__outlier-step\">\n <div class=\"mint-auto-group__outlier-banner\">\n {{ autoGroup.outliers.value.length }} of {{ autoGroup.samples.value.length }}\n samples have irregular structure\n (fewer than {{ autoGroup.dominantFieldCount.value }} fields, delimiter\n <code>{{ autoGroup.delimiter.value }}</code>)\n </div>\n\n <div class=\"mint-auto-group__outlier-batch\">\n <BaseButton size=\"sm\" variant=\"secondary\" @click=\"autoGroup.setAllOutlierActions('include')\">\n Include All\n </BaseButton>\n <BaseButton size=\"sm\" variant=\"secondary\" @click=\"autoGroup.setAllOutlierActions('exclude')\">\n Exclude All\n </BaseButton>\n </div>\n\n <div class=\"mint-auto-group__outlier-list\">\n <div\n v-for=\"outlier in autoGroup.outliers.value\"\n :key=\"outlier.index\"\n class=\"mint-auto-group__outlier-item\"\n >\n <div class=\"mint-auto-group__outlier-info\">\n <code class=\"mint-auto-group__outlier-name\">{{ outlier.sample }}</code>\n <span class=\"mint-auto-group__outlier-badge\">{{ outlier.fieldCount }} fields</span>\n </div>\n <div class=\"mint-auto-group__outlier-actions\">\n <button\n type=\"button\"\n :class=\"['mint-auto-group__action-btn', outlier.action === 'include' ? 'mint-auto-group__action-btn--active' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'include')\"\n >Include</button>\n <button\n type=\"button\"\n :class=\"['mint-auto-group__action-btn', outlier.action === 'exclude' ? 'mint-auto-group__action-btn--active mint-auto-group__action-btn--exclude' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'exclude')\"\n >Exclude</button>\n <button\n type=\"button\"\n :class=\"['mint-auto-group__action-btn', outlier.action === 'qc' ? 'mint-auto-group__action-btn--active mint-auto-group__action-btn--qc' : '']\"\n @click=\"autoGroup.setOutlierAction(outlier.index, 'qc')\"\n >QC</button>\n </div>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Step: Fields -->\n <template #step-fields>\n <div class=\"mint-auto-group__field-step\">\n <div class=\"mint-auto-group__field-grid\">\n <div\n v-for=\"col in autoGroup.effectiveColumns.value\"\n :key=\"col.index\"\n :class=\"[\n 'mint-auto-group__field-card',\n autoGroup.enabledFields.value.has(col.index) ? 'mint-auto-group__field-card--enabled' : '',\n ]\"\n >\n <div class=\"mint-auto-group__field-header\">\n <label class=\"mint-auto-group__field-toggle\">\n <input\n type=\"checkbox\"\n :checked=\"autoGroup.enabledFields.value.has(col.index)\"\n @change=\"autoGroup.toggleField(col.index)\"\n />\n <span class=\"mint-auto-group__field-toggle-label\">Group by</span>\n </label>\n <span class=\"mint-auto-group__field-cardinality\">{{ col.cardinality }} unique</span>\n </div>\n\n <BaseInput\n :model-value=\"col.name\"\n placeholder=\"Field name...\"\n class=\"mint-auto-group__field-name-input\"\n @update:model-value=\"autoGroup.renameField(col.index, String($event ?? ''))\"\n />\n\n <div class=\"mint-auto-group__field-values\">\n <span\n v-for=\"val in col.uniqueValues.slice(0, 5)\"\n :key=\"val\"\n class=\"mint-auto-group__field-value\"\n >{{ val }}</span>\n <span\n v-if=\"col.uniqueValues.length > 5\"\n class=\"mint-auto-group__field-more\"\n >+{{ col.uniqueValues.length - 5 }} more</span>\n </div>\n </div>\n </div>\n\n <div v-if=\"autoGroup.enabledFields.value.size > 0\" class=\"mint-auto-group__field-summary\">\n Grouping by:\n <strong>{{\n autoGroup.effectiveColumns.value\n .filter(c => autoGroup.enabledFields.value.has(c.index))\n .map(c => c.name)\n .join(' / ')\n }}</strong>\n </div>\n\n <div v-if=\"autoGroup.enabledFields.value.size === 0\" class=\"mint-auto-group__field-warning\">\n Select at least one field for grouping\n </div>\n </div>\n </template>\n\n <!-- Step: Preview -->\n <template #step-preview>\n <div class=\"mint-auto-group__preview-step\">\n <div class=\"mint-auto-group__preview-summary\">\n <span class=\"mint-auto-group__preview-stat\">\n <strong>{{ autoGroup.groups.value.length }}</strong> groups\n </span>\n <span class=\"mint-auto-group__preview-stat\">\n <strong>{{ autoGroup.groups.value.reduce((sum, g) => sum + g.samples.length, 0) }}</strong> samples\n </span>\n <span v-if=\"autoGroup.excludedSamples.value.length > 0\" class=\"mint-auto-group__preview-stat mint-auto-group__preview-stat--excluded\">\n <strong>{{ autoGroup.excludedSamples.value.length }}</strong> excluded\n </span>\n </div>\n\n <AlertBox\n v-if=\"autoGroup.allSingletons.value\"\n type=\"warning\"\n title=\"Each group has only one sample\"\n >\n Your enabled fields produce a unique key for every row, so no aggregation is happening. Go back to <em>Fields</em> and disable any column whose values are unique per sample.\n </AlertBox>\n\n <div class=\"mint-auto-group__preview-groups\">\n <details\n v-for=\"group in autoGroup.groups.value\"\n :key=\"group.name\"\n class=\"mint-auto-group__preview-group\"\n >\n <summary class=\"mint-auto-group__preview-group-header\">\n <span\n class=\"mint-auto-group__preview-dot\"\n :style=\"{ backgroundColor: group.color }\"\n />\n <span class=\"mint-auto-group__preview-group-name\">{{ group.name }}</span>\n <span\n class=\"mint-auto-group__preview-group-count\"\n :style=\"{ backgroundColor: group.color + '20', color: group.color }\"\n >{{ group.samples.length }}</span>\n </summary>\n <div class=\"mint-auto-group__preview-samples\">\n <span\n v-for=\"sample in group.samples\"\n :key=\"sample\"\n class=\"mint-auto-group__preview-sample\"\n >{{ sample }}</span>\n </div>\n </details>\n </div>\n\n <div v-if=\"autoGroup.excludedSamples.value.length > 0\" class=\"mint-auto-group__preview-excluded\">\n <div class=\"mint-auto-group__preview-excluded-title\">Excluded Samples</div>\n <div class=\"mint-auto-group__preview-excluded-list\">\n <span\n v-for=\"sample in autoGroup.excludedSamples.value\"\n :key=\"sample\"\n class=\"mint-auto-group__preview-sample mint-auto-group__preview-sample--excluded\"\n >{{ sample }}</span>\n </div>\n </div>\n </div>\n </template>\n\n <!-- Custom navigation -->\n <template #navigation=\"{ goBack, goNext, canProceed }\">\n <div class=\"mint-auto-group__nav\">\n <BaseButton variant=\"secondary\" @click=\"handleCancel\">\n Cancel\n </BaseButton>\n <div class=\"mint-auto-group__nav-spacer\" />\n <BaseButton\n v-if=\"!isFirstStep\"\n variant=\"secondary\"\n @click=\"goBack\"\n >\n Back\n </BaseButton>\n <BaseButton\n v-if=\"!isLastStep\"\n variant=\"primary\"\n :disabled=\"!canProceed\"\n @click=\"() => { handleNext(); goNext() }\"\n >\n Next\n </BaseButton>\n <BaseButton\n v-if=\"isLastStep\"\n variant=\"primary\"\n @click=\"handleApply\"\n >\n Apply\n </BaseButton>\n </div>\n </template>\n </StepWizard>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/auto-group-modal.css';\n</style>\n","<script setup lang=\"ts\">\n/** Interactive sample list with drag-and-drop group assignment, color labeling, and smart-group support. */\nimport { ref, computed } from 'vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseInput from './BaseInput.vue'\nimport AutoGroupModal from './AutoGroupModal.vue'\nimport type { SampleGroup } from '../types'\nimport type { AutoGroupResult } from '../types/auto-group'\nimport { DEFAULT_COLORS } from '../composables/useAutoGroup'\nimport { useTextSearch } from '../composables/useTextSearch'\nimport { useListSelection } from '../composables/useListSelection'\nimport { useSampleGroups, type SampleMajorGroup } from '../composables/useSampleGroups'\nimport { useExpansionSet } from '../composables/useExpansionSet'\n\ninterface Props {\n samples: string[]\n modelValue: string[]\n groups?: SampleGroup[]\n enableGrouping?: boolean\n enableSmartGroup?: boolean\n experimentId?: number\n designData?: Record<string, unknown>\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n groups: () => [],\n enableGrouping: true,\n enableSmartGroup: true,\n experimentId: undefined,\n designData: undefined,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [samples: string[]]\n 'update:groups': [groups: SampleGroup[]]\n smartGroup: [result: AutoGroupResult]\n}>()\n\n// UI State\ntype ColorEdit =\n | { kind: 'single'; name: string }\n | { kind: 'family'; names: string[] }\n\nconst showSmartGroupModal = ref(false)\nconst newGroupName = ref('')\nconst editingColor = ref<ColorEdit | null>(null)\nconst colorPickerInput = ref<HTMLInputElement | null>(null)\nconst searchQuery = ref('')\nconst groupExpansion = useExpansionSet()\n\n// Sample Drag State\nconst draggingSample = ref<string | null>(null)\nconst dragSourceGroup = ref<string | null>(null)\nconst dragOverGroup = ref<string | null>(null)\n\n// Group-Reorder Drag State\ntype GroupDragKind = 'major' | 'sub' | 'flat'\nconst draggingGroup = ref<string | null>(null)\nconst draggingGroupKind = ref<GroupDragKind | null>(null)\nconst reorderTarget = ref<string | null>(null)\nconst reorderPosition = ref<'before' | 'after' | null>(null)\n\n// Computed: groups from props\nconst internalGroups = computed({\n get: () => props.groups,\n set: (value) => emit('update:groups', value),\n})\n\nconst sampleGroups = useSampleGroups({\n samples: () => props.samples,\n groups: internalGroups,\n})\nconst hierarchicalGroups = sampleGroups.hierarchicalGroups\nconst showHierarchy = sampleGroups.showHierarchy\n\nconst groupingEnabled = computed(() => internalGroups.value.length > 0)\nconst ungroupedSamples = sampleGroups.ungroupedSamples\n\nconst sampleSearch = useTextSearch({\n items: () => props.samples,\n query: searchQuery,\n getText: sample => sample,\n})\nconst filteredSamples = sampleSearch.filteredItems\n\nconst sampleSelection = useListSelection({\n selected: () => props.modelValue,\n items: () => props.samples,\n})\nconst isAllSelected = sampleSelection.isAllSelected\n\n// Toggle functions\nfunction toggleSelectAll() {\n emit('update:modelValue', sampleSelection.toggleAll())\n}\n\nfunction toggleSample(sample: string) {\n emit('update:modelValue', sampleSelection.toggleValue(sample))\n}\n\nfunction toggleSamplesSelection(samples: string[]) {\n emit('update:modelValue', sampleSelection.toggleValues(samples))\n}\n\nfunction toggleGroupSamples(groupName: string) {\n const group = sampleGroups.findGroup(groupName)\n if (!group) return\n toggleSamplesSelection(group.samples)\n}\n\nfunction toggleMajorGroupSamples(majorGroup: SampleMajorGroup) {\n toggleSamplesSelection(majorGroup.allSamples)\n}\n\n// Selection state checks\nfunction isFullySelected(samples: string[]): boolean {\n return sampleSelection.isFullySelected(samples)\n}\n\nfunction isPartiallySelected(samples: string[]): boolean {\n return sampleSelection.isPartiallySelected(samples)\n}\n\nfunction isGroupFullySelected(groupName: string): boolean {\n const group = sampleGroups.findGroup(groupName)\n return group ? isFullySelected(group.samples) : false\n}\n\nfunction isGroupPartiallySelected(groupName: string): boolean {\n const group = sampleGroups.findGroup(groupName)\n return group ? isPartiallySelected(group.samples) : false\n}\n\nfunction isMajorGroupFullySelected(majorGroup: SampleMajorGroup): boolean {\n return isFullySelected(majorGroup.allSamples)\n}\n\nfunction isMajorGroupPartiallySelected(majorGroup: SampleMajorGroup): boolean {\n return isPartiallySelected(majorGroup.allSamples)\n}\n\n// Expand/collapse\nfunction toggleGroupExpanded(groupName: string) {\n groupExpansion.toggle(groupName)\n}\n\nfunction isGroupExpanded(groupName: string): boolean {\n return groupExpansion.isExpanded(groupName)\n}\n\nfunction expandAllGroups() {\n const expanded: string[] = []\n for (const major of hierarchicalGroups.value) {\n expanded.push(`major:${major.name}`)\n for (const sub of major.subGroups) {\n expanded.push(sub.name)\n }\n }\n expanded.push('__ungrouped__')\n groupExpansion.setExpanded(expanded)\n}\n\nfunction collapseAllGroups() {\n groupExpansion.collapseAll()\n}\n\n// Smart group\nfunction handleSmartGroupApply(result: AutoGroupResult) {\n emit('smartGroup', result)\n emit('update:groups', result.groups)\n}\n\n// Group management\nfunction clearGroups() {\n internalGroups.value = []\n}\n\nfunction deleteMajorGroup(majorGroup: SampleMajorGroup) {\n internalGroups.value = internalGroups.value.filter(\n g => !majorGroup.subGroups.some(sg => sg.name === g.name)\n )\n}\n\nfunction deleteGroup(groupName: string) {\n internalGroups.value = internalGroups.value.filter(g => g.name !== groupName)\n}\n\nfunction removeSampleFromGroup(sample: string, groupName: string) {\n internalGroups.value = internalGroups.value.map(g =>\n g.name === groupName\n ? { ...g, samples: g.samples.filter((s: string) => s !== sample) }\n : g\n )\n}\n\n// Drag and Drop handlers\nfunction handleDragStart(sample: string, sourceGroup: string | null, event: DragEvent) {\n draggingSample.value = sample\n dragSourceGroup.value = sourceGroup\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', sample)\n }\n}\n\nfunction resetDragState() {\n draggingSample.value = null\n dragSourceGroup.value = null\n dragOverGroup.value = null\n}\n\nfunction handleDragEnd() {\n resetDragState()\n}\n\nfunction handleDragOver(groupName: string, event: DragEvent) {\n // Only accept this drop target when a sample is being dragged; group drags are handled separately.\n if (!draggingSample.value) return\n event.preventDefault()\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move'\n }\n dragOverGroup.value = groupName\n}\n\nfunction handleDragLeave() {\n dragOverGroup.value = null\n}\n\nfunction handleDrop(targetGroupName: string, event: DragEvent) {\n if (!draggingSample.value) return\n event.preventDefault()\n\n const sample = draggingSample.value\n const sourceGroup = dragSourceGroup.value\n\n if (sourceGroup === targetGroupName) {\n resetDragState()\n return\n }\n\n internalGroups.value = internalGroups.value.map(g => {\n if (sourceGroup && g.name === sourceGroup) {\n return { ...g, samples: g.samples.filter((s: string) => s !== sample) }\n }\n if (g.name === targetGroupName && !g.samples.includes(sample)) {\n return { ...g, samples: [...g.samples, sample] }\n }\n return g\n })\n\n resetDragState()\n}\n\n// Group reorder helpers\nfunction detectSeparator(groups: SampleGroup[]): string {\n return groups.some(g => g.name.includes('/')) ? '/' : '_'\n}\n\nfunction getMajorPrefix(groupName: string, separator: string): string {\n const parts = groupName.split(separator)\n return parts.length > 1 ? parts[0] : groupName\n}\n\nfunction handleGroupDragStart(name: string, kind: GroupDragKind, event: DragEvent) {\n draggingGroup.value = name\n draggingGroupKind.value = kind\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', `mint-group:${name}`)\n }\n}\n\nfunction handleGroupDragEnd() {\n draggingGroup.value = null\n draggingGroupKind.value = null\n reorderTarget.value = null\n reorderPosition.value = null\n}\n\nfunction handleGroupDragOver(name: string, kind: GroupDragKind, event: DragEvent) {\n if (!draggingGroup.value || draggingGroupKind.value !== kind) return\n if (draggingGroup.value === name) return\n\n // Sub-group reorder is restricted to siblings under the same major prefix —\n // crossing majors would silently rename the group, which is too magical.\n if (kind === 'sub') {\n const sep = detectSeparator(internalGroups.value)\n if (getMajorPrefix(draggingGroup.value, sep) !== getMajorPrefix(name, sep)) return\n }\n\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect()\n reorderTarget.value = name\n reorderPosition.value = event.clientY < rect.top + rect.height / 2 ? 'before' : 'after'\n}\n\nfunction handleGroupDragLeave(event: DragEvent) {\n const cur = event.currentTarget as HTMLElement\n const rel = event.relatedTarget as Node | null\n if (rel && cur.contains(rel)) return\n reorderTarget.value = null\n reorderPosition.value = null\n}\n\nfunction handleGroupDrop(name: string, kind: GroupDragKind, event: DragEvent) {\n if (!draggingGroup.value || draggingGroupKind.value !== kind) {\n handleGroupDragEnd()\n return\n }\n const position = reorderPosition.value\n if (!position || draggingGroup.value === name) {\n handleGroupDragEnd()\n return\n }\n\n event.preventDefault()\n\n if (kind === 'major') {\n moveMajorGroup(draggingGroup.value, name, position)\n } else {\n moveGroup(draggingGroup.value, name, position)\n }\n\n handleGroupDragEnd()\n}\n\nfunction moveGroup(source: string, target: string, position: 'before' | 'after') {\n if (source === target) return\n const groups = [...internalGroups.value]\n const srcIdx = groups.findIndex(g => g.name === source)\n if (srcIdx === -1) return\n const [item] = groups.splice(srcIdx, 1)\n const targetIdx = groups.findIndex(g => g.name === target)\n if (targetIdx === -1) return\n groups.splice(position === 'before' ? targetIdx : targetIdx + 1, 0, item)\n internalGroups.value = groups\n}\n\nfunction moveMajorGroup(source: string, target: string, position: 'before' | 'after') {\n if (source === target) return\n const groups = [...internalGroups.value]\n const sep = detectSeparator(groups)\n\n const isSource = (g: SampleGroup) => getMajorPrefix(g.name, sep) === source\n const isTarget = (g: SampleGroup) => getMajorPrefix(g.name, sep) === target\n\n const sourceGroups = groups.filter(isSource)\n if (sourceGroups.length === 0) return\n const remaining = groups.filter(g => !isSource(g))\n\n let firstTarget = -1\n let lastTarget = -1\n remaining.forEach((g, i) => {\n if (isTarget(g)) {\n if (firstTarget === -1) firstTarget = i\n lastTarget = i\n }\n })\n if (firstTarget === -1) return\n\n const insertIdx = position === 'before' ? firstTarget : lastTarget + 1\n remaining.splice(insertIdx, 0, ...sourceGroups)\n internalGroups.value = remaining\n}\n\n// Color picker\nfunction openColorPicker(groupName: string, event: Event) {\n event.stopPropagation()\n editingColor.value = { kind: 'single', name: groupName }\n colorPickerInput.value?.click()\n}\n\nfunction openMajorGroupColorPicker(majorGroup: SampleMajorGroup, event: Event) {\n event.stopPropagation()\n editingColor.value = { kind: 'family', names: majorGroup.subGroups.map(sg => sg.name) }\n colorPickerInput.value?.click()\n}\n\nfunction handleColorChange(event: Event) {\n const edit = editingColor.value\n if (!edit) return\n\n const newColor = (event.target as HTMLInputElement).value\n // For a family edit, every sub-group gets the new seed; the hierarchical\n // computed re-derives shades from it.\n const targets = edit.kind === 'family' ? new Set(edit.names) : new Set([edit.name])\n internalGroups.value = internalGroups.value.map(g =>\n targets.has(g.name) ? { ...g, color: newColor } : g,\n )\n editingColor.value = null\n}\n\nfunction getGroupColor(groupName: string): string {\n return sampleGroups.getGroupColor(groupName)\n}\n\nconst colorPickerSeed = computed(() => {\n const edit = editingColor.value\n if (!edit) return '#3B82F6'\n return getGroupColor(edit.kind === 'family' ? edit.names[0] : edit.name)\n})\n\n// New group\nfunction addNewGroup() {\n if (!newGroupName.value.trim()) return\n\n const usedColors = new Set(internalGroups.value.map(g => g.color))\n const availableColor = DEFAULT_COLORS.find(c => !usedColors.has(c)) || DEFAULT_COLORS[0]\n\n const newGroup: SampleGroup = {\n name: newGroupName.value.trim(),\n color: availableColor,\n samples: [],\n }\n\n internalGroups.value = [...internalGroups.value, newGroup]\n newGroupName.value = ''\n}\n\n</script>\n\n<template>\n <div class=\"mint-sample-selector\">\n <!-- Select All Row -->\n <label class=\"mint-sample-selector__select-all\">\n <input\n type=\"checkbox\"\n :checked=\"isAllSelected\"\n @change=\"toggleSelectAll\"\n class=\"mint-sample-selector__checkbox\"\n />\n <span class=\"mint-sample-selector__select-all-label\">Select All</span>\n <span class=\"mint-sample-selector__select-all-count\">{{ samples.length }} samples</span>\n </label>\n\n <!-- Action Buttons Row -->\n <div v-if=\"enableGrouping\" class=\"mint-sample-selector__actions\">\n <div class=\"mint-sample-selector__actions-row\">\n <!-- Smart Group Button -->\n <BaseButton\n v-if=\"enableSmartGroup\"\n :variant=\"groupingEnabled ? 'primary' : 'secondary'\"\n size=\"sm\"\n :disabled=\"samples.length === 0\"\n class=\"mint-sample-selector__action-btn\"\n @click=\"showSmartGroupModal = true\"\n >\n <svg class=\"mint-sample-selector__action-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13 10V3L4 14h7v7l9-11h-7z\" />\n </svg>\n <span>Smart Group</span>\n </BaseButton>\n\n <!-- Reset Button -->\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n :disabled=\"internalGroups.length === 0\"\n class=\"mint-sample-selector__action-btn mint-sample-selector__action-btn--reset\"\n @click=\"clearGroups\"\n title=\"Clear all groups\"\n >\n <svg class=\"mint-sample-selector__action-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\" />\n </svg>\n </BaseButton>\n </div>\n </div>\n\n <!-- Grouped View -->\n <div v-if=\"groupingEnabled\" class=\"mint-sample-selector__grouped\">\n <!-- Groups Header -->\n <div class=\"mint-sample-selector__groups-header\">\n <span class=\"mint-sample-selector__groups-title\">Groups ({{ internalGroups.length }})</span>\n <div class=\"mint-sample-selector__groups-controls\">\n <button type=\"button\" class=\"mint-sample-selector__expand-btn\" @click=\"expandAllGroups\" title=\"Expand all\">\n <svg class=\"mint-sample-selector__expand-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n <button type=\"button\" class=\"mint-sample-selector__expand-btn\" @click=\"collapseAllGroups\" title=\"Collapse all\">\n <svg class=\"mint-sample-selector__expand-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Hierarchical Tree -->\n <div class=\"mint-sample-selector__tree\">\n <!-- Major Groups (when hierarchy is meaningful) -->\n <template v-if=\"showHierarchy\">\n <div\n v-for=\"majorGroup in hierarchicalGroups\"\n :key=\"majorGroup.name\"\n class=\"mint-sample-selector__major-group\"\n >\n <!-- Major Group Header -->\n <div\n :class=\"[\n 'mint-sample-selector__major-header',\n draggingGroup === majorGroup.name && draggingGroupKind === 'major' ? 'mint-sample-selector__header--dragging' : '',\n reorderTarget === majorGroup.name && draggingGroupKind === 'major' && reorderPosition === 'before' ? 'mint-sample-selector__header--drag-over-before' : '',\n reorderTarget === majorGroup.name && draggingGroupKind === 'major' && reorderPosition === 'after' ? 'mint-sample-selector__header--drag-over-after' : '',\n ]\"\n draggable=\"true\"\n @click=\"toggleGroupExpanded(`major:${majorGroup.name}`)\"\n @dragstart=\"handleGroupDragStart(majorGroup.name, 'major', $event)\"\n @dragend=\"handleGroupDragEnd\"\n @dragover=\"handleGroupDragOver(majorGroup.name, 'major', $event)\"\n @dragleave=\"handleGroupDragLeave($event)\"\n @drop=\"handleGroupDrop(majorGroup.name, 'major', $event)\"\n >\n <svg\n :class=\"[\n 'mint-sample-selector__chevron',\n isGroupExpanded(`major:${majorGroup.name}`) ? 'mint-sample-selector__chevron--open' : '',\n ]\"\n fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n\n <input\n type=\"checkbox\"\n :checked=\"isMajorGroupFullySelected(majorGroup)\"\n :indeterminate=\"isMajorGroupPartiallySelected(majorGroup)\"\n class=\"mint-sample-selector__checkbox\"\n :style=\"{ accentColor: majorGroup.color }\"\n @click.stop\n @change=\"toggleMajorGroupSamples(majorGroup)\"\n />\n\n <button\n type=\"button\"\n class=\"mint-sample-selector__color-dot mint-sample-selector__color-dot--large mint-sample-selector__color-dot--clickable\"\n :style=\"{ backgroundColor: majorGroup.color }\"\n @click.stop=\"openMajorGroupColorPicker(majorGroup, $event)\"\n title=\"Click to change color family\"\n />\n\n <span class=\"mint-sample-selector__major-name\">{{ majorGroup.name }}</span>\n\n <span\n class=\"mint-sample-selector__count-badge\"\n :style=\"{ backgroundColor: majorGroup.color + '20', color: majorGroup.color }\"\n >\n {{ majorGroup.allSamples.length }}\n </span>\n\n <button\n type=\"button\"\n class=\"mint-sample-selector__delete-btn mint-sample-selector__delete-btn--hidden\"\n @click.stop=\"deleteMajorGroup(majorGroup)\"\n title=\"Remove major group\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <!-- Sub Groups (collapsible) -->\n <Transition name=\"mint-collapse\">\n <div\n v-if=\"isGroupExpanded(`major:${majorGroup.name}`)\"\n class=\"mint-sample-selector__sub-groups\"\n :style=\"{ borderColor: majorGroup.color + '30' }\"\n >\n <div\n v-for=\"subGroup in majorGroup.subGroups\"\n :key=\"subGroup.name\"\n :class=\"[\n 'mint-sample-selector__sub-group',\n dragOverGroup === subGroup.name ? 'mint-sample-selector__sub-group--drag-over' : '',\n ]\"\n @dragover=\"handleDragOver(subGroup.name, $event)\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop(subGroup.name, $event)\"\n >\n <!-- Sub Group Header -->\n <div\n :class=\"[\n 'mint-sample-selector__sub-header',\n draggingGroup === subGroup.name && draggingGroupKind === 'sub' ? 'mint-sample-selector__header--dragging' : '',\n reorderTarget === subGroup.name && draggingGroupKind === 'sub' && reorderPosition === 'before' ? 'mint-sample-selector__header--drag-over-before' : '',\n reorderTarget === subGroup.name && draggingGroupKind === 'sub' && reorderPosition === 'after' ? 'mint-sample-selector__header--drag-over-after' : '',\n ]\"\n draggable=\"true\"\n @click=\"toggleGroupExpanded(subGroup.name)\"\n @dragstart=\"handleGroupDragStart(subGroup.name, 'sub', $event)\"\n @dragend=\"handleGroupDragEnd\"\n @dragover=\"handleGroupDragOver(subGroup.name, 'sub', $event)\"\n @dragleave=\"handleGroupDragLeave($event)\"\n @drop=\"handleGroupDrop(subGroup.name, 'sub', $event)\"\n >\n <svg\n :class=\"[\n 'mint-sample-selector__chevron mint-sample-selector__chevron--small',\n isGroupExpanded(subGroup.name) ? 'mint-sample-selector__chevron--open' : '',\n ]\"\n fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n\n <input\n type=\"checkbox\"\n :checked=\"isGroupFullySelected(subGroup.name)\"\n :indeterminate=\"isGroupPartiallySelected(subGroup.name)\"\n class=\"mint-sample-selector__checkbox mint-sample-selector__checkbox--small\"\n :style=\"{ accentColor: subGroup.displayColor }\"\n @click.stop\n @change=\"toggleGroupSamples(subGroup.name)\"\n />\n\n <div\n class=\"mint-sample-selector__color-dot\"\n :style=\"{ backgroundColor: subGroup.displayColor }\"\n aria-hidden=\"true\"\n />\n\n <span class=\"mint-sample-selector__sub-name\">{{ subGroup.name }}</span>\n\n <span\n class=\"mint-sample-selector__count-badge mint-sample-selector__count-badge--small\"\n :style=\"{ backgroundColor: subGroup.displayBg, color: subGroup.displayColor }\"\n >\n {{ subGroup.samples.length }}\n </span>\n\n <button\n type=\"button\"\n class=\"mint-sample-selector__delete-btn mint-sample-selector__delete-btn--hidden\"\n @click.stop=\"deleteGroup(subGroup.name)\"\n title=\"Remove sub group\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <!-- Samples (collapsible) -->\n <Transition name=\"mint-collapse\">\n <div\n v-if=\"isGroupExpanded(subGroup.name)\"\n class=\"mint-sample-selector__samples\"\n :style=\"{ borderColor: subGroup.displayBorder }\"\n >\n <div\n v-for=\"sample in subGroup.samples\"\n :key=\"sample\"\n :class=\"[\n 'mint-sample-selector__sample',\n draggingSample === sample ? 'mint-sample-selector__sample--dragging' : '',\n ]\"\n draggable=\"true\"\n @dragstart=\"handleDragStart(sample, subGroup.name, $event)\"\n @dragend=\"handleDragEnd\"\n >\n <svg class=\"mint-sample-selector__drag-handle\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8h16M4 16h16\" />\n </svg>\n <input\n type=\"checkbox\"\n :checked=\"modelValue.includes(sample)\"\n class=\"mint-sample-selector__checkbox mint-sample-selector__checkbox--tiny\"\n :style=\"{ accentColor: subGroup.displayColor }\"\n @change=\"toggleSample(sample)\"\n />\n <span class=\"mint-sample-selector__sample-name\">{{ sample }}</span>\n <button\n type=\"button\"\n class=\"mint-sample-selector__remove-btn\"\n @click=\"removeSampleFromGroup(sample, subGroup.name)\"\n title=\"Remove from group\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M20 12H4\" />\n </svg>\n </button>\n </div>\n </div>\n </Transition>\n </div>\n </div>\n </Transition>\n </div>\n </template>\n\n <!-- Flat Groups (when no meaningful hierarchy - e.g., level 1) -->\n <template v-else>\n <div\n v-for=\"group in internalGroups\"\n :key=\"group.name\"\n :class=\"[\n 'mint-sample-selector__sub-group',\n dragOverGroup === group.name ? 'mint-sample-selector__sub-group--drag-over' : '',\n ]\"\n @dragover=\"handleDragOver(group.name, $event)\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop(group.name, $event)\"\n >\n <!-- Group Header -->\n <div\n :class=\"[\n 'mint-sample-selector__major-header',\n draggingGroup === group.name && draggingGroupKind === 'flat' ? 'mint-sample-selector__header--dragging' : '',\n reorderTarget === group.name && draggingGroupKind === 'flat' && reorderPosition === 'before' ? 'mint-sample-selector__header--drag-over-before' : '',\n reorderTarget === group.name && draggingGroupKind === 'flat' && reorderPosition === 'after' ? 'mint-sample-selector__header--drag-over-after' : '',\n ]\"\n draggable=\"true\"\n @click=\"toggleGroupExpanded(group.name)\"\n @dragstart=\"handleGroupDragStart(group.name, 'flat', $event)\"\n @dragend=\"handleGroupDragEnd\"\n @dragover=\"handleGroupDragOver(group.name, 'flat', $event)\"\n @dragleave=\"handleGroupDragLeave($event)\"\n @drop=\"handleGroupDrop(group.name, 'flat', $event)\"\n >\n <svg\n :class=\"[\n 'mint-sample-selector__chevron',\n isGroupExpanded(group.name) ? 'mint-sample-selector__chevron--open' : '',\n ]\"\n fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n\n <input\n type=\"checkbox\"\n :checked=\"isGroupFullySelected(group.name)\"\n :indeterminate=\"isGroupPartiallySelected(group.name)\"\n class=\"mint-sample-selector__checkbox\"\n :style=\"{ accentColor: group.color }\"\n @click.stop\n @change=\"toggleGroupSamples(group.name)\"\n />\n\n <button\n type=\"button\"\n class=\"mint-sample-selector__color-dot mint-sample-selector__color-dot--large mint-sample-selector__color-dot--clickable\"\n :style=\"{ backgroundColor: group.color }\"\n @click.stop=\"openColorPicker(group.name, $event)\"\n title=\"Click to change color\"\n />\n\n <span class=\"mint-sample-selector__major-name\">{{ group.name }}</span>\n\n <span\n class=\"mint-sample-selector__count-badge\"\n :style=\"{ backgroundColor: group.color + '20', color: group.color }\"\n >\n {{ group.samples.length }}\n </span>\n\n <button\n type=\"button\"\n class=\"mint-sample-selector__delete-btn mint-sample-selector__delete-btn--hidden\"\n @click.stop=\"deleteGroup(group.name)\"\n title=\"Remove group\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <!-- Samples (collapsible) -->\n <Transition name=\"mint-collapse\">\n <div\n v-if=\"isGroupExpanded(group.name)\"\n class=\"mint-sample-selector__samples\"\n :style=\"{ borderColor: group.color + '40' }\"\n >\n <div\n v-for=\"sample in group.samples\"\n :key=\"sample\"\n :class=\"[\n 'mint-sample-selector__sample',\n draggingSample === sample ? 'mint-sample-selector__sample--dragging' : '',\n ]\"\n draggable=\"true\"\n @dragstart=\"handleDragStart(sample, group.name, $event)\"\n @dragend=\"handleDragEnd\"\n >\n <svg class=\"mint-sample-selector__drag-handle\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8h16M4 16h16\" />\n </svg>\n <input\n type=\"checkbox\"\n :checked=\"modelValue.includes(sample)\"\n class=\"mint-sample-selector__checkbox mint-sample-selector__checkbox--tiny\"\n :style=\"{ accentColor: group.color }\"\n @change=\"toggleSample(sample)\"\n />\n <span class=\"mint-sample-selector__sample-name\">{{ sample }}</span>\n <button\n type=\"button\"\n class=\"mint-sample-selector__remove-btn\"\n @click=\"removeSampleFromGroup(sample, group.name)\"\n title=\"Remove from group\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M20 12H4\" />\n </svg>\n </button>\n </div>\n </div>\n </Transition>\n </div>\n </template>\n\n <!-- Empty state -->\n <div v-if=\"internalGroups.length === 0\" class=\"mint-sample-selector__empty\">\n Click Smart Group to auto-group samples\n </div>\n </div>\n\n <!-- Ungrouped Samples Section -->\n <div v-if=\"ungroupedSamples.length > 0\" class=\"mint-sample-selector__ungrouped\">\n <div\n class=\"mint-sample-selector__ungrouped-header\"\n @click=\"toggleGroupExpanded('__ungrouped__')\"\n >\n <svg\n :class=\"[\n 'mint-sample-selector__chevron',\n isGroupExpanded('__ungrouped__') ? 'mint-sample-selector__chevron--open' : '',\n ]\"\n fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n <span class=\"mint-sample-selector__ungrouped-label\">Ungrouped</span>\n <span class=\"mint-sample-selector__ungrouped-count\">{{ ungroupedSamples.length }}</span>\n </div>\n\n <Transition name=\"mint-collapse\">\n <div\n v-if=\"isGroupExpanded('__ungrouped__')\"\n class=\"mint-sample-selector__ungrouped-list\"\n >\n <div\n v-for=\"sample in ungroupedSamples\"\n :key=\"sample\"\n :class=\"[\n 'mint-sample-selector__sample',\n draggingSample === sample ? 'mint-sample-selector__sample--dragging' : '',\n ]\"\n draggable=\"true\"\n @dragstart=\"handleDragStart(sample, null, $event)\"\n @dragend=\"handleDragEnd\"\n >\n <svg class=\"mint-sample-selector__drag-handle\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8h16M4 16h16\" />\n </svg>\n <input\n type=\"checkbox\"\n :checked=\"modelValue.includes(sample)\"\n class=\"mint-sample-selector__checkbox mint-sample-selector__checkbox--small\"\n @change=\"toggleSample(sample)\"\n />\n <span class=\"mint-sample-selector__sample-name\">{{ sample }}</span>\n </div>\n </div>\n </Transition>\n </div>\n\n <!-- New Group Input -->\n <div class=\"mint-sample-selector__new-group\">\n <BaseInput\n v-model=\"newGroupName\"\n placeholder=\"New group name...\"\n class=\"mint-sample-selector__new-group-input\"\n @keyup.enter=\"addNewGroup\"\n />\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n :disabled=\"!newGroupName.trim()\"\n class=\"mint-sample-selector__new-group-btn\"\n @click=\"addNewGroup\"\n >\n Add\n </BaseButton>\n </div>\n\n <!-- Hidden color picker input -->\n <input\n ref=\"colorPickerInput\"\n type=\"color\"\n class=\"mint-sample-selector__color-input\"\n :value=\"colorPickerSeed\"\n @change=\"handleColorChange\"\n />\n </div>\n\n <!-- Flat View (when no groups) -->\n <div v-if=\"!groupingEnabled\" class=\"mint-sample-selector__flat\">\n <!-- Search -->\n <div class=\"mint-sample-selector__search\">\n <svg class=\"mint-sample-selector__search-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" />\n </svg>\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n placeholder=\"Search samples...\"\n class=\"mint-sample-selector__search-input\"\n />\n </div>\n\n <!-- Flat samples list -->\n <div class=\"mint-sample-selector__flat-list\">\n <div\n v-for=\"sample in filteredSamples\"\n :key=\"sample\"\n class=\"mint-sample-selector__flat-item\"\n >\n <input\n type=\"checkbox\"\n :checked=\"modelValue.includes(sample)\"\n class=\"mint-sample-selector__checkbox\"\n @change=\"toggleSample(sample)\"\n />\n <span class=\"mint-sample-selector__flat-name\">{{ sample }}</span>\n </div>\n\n <div v-if=\"filteredSamples.length === 0 && searchQuery.trim()\" class=\"mint-sample-selector__empty\">\n No samples match \"{{ searchQuery }}\"\n </div>\n </div>\n </div>\n\n <!-- Smart Grouping Modal -->\n <AutoGroupModal\n v-model=\"showSmartGroupModal\"\n :samples=\"samples\"\n :experiment-id=\"experimentId\"\n :design-data=\"designData\"\n @apply=\"handleSmartGroupApply\"\n />\n </div>\n</template>\n\n<style>\n@import '../styles/components/sample-selector.css';\n</style>\n","<script setup lang=\"ts\">\n/** Interactive sample list with drag-and-drop group assignment, color labeling, and smart-group support. */\nimport { ref, computed } from 'vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseInput from './BaseInput.vue'\nimport AutoGroupModal from './AutoGroupModal.vue'\nimport type { SampleGroup } from '../types'\nimport type { AutoGroupResult } from '../types/auto-group'\nimport { DEFAULT_COLORS } from '../composables/useAutoGroup'\nimport { useTextSearch } from '../composables/useTextSearch'\nimport { useListSelection } from '../composables/useListSelection'\nimport { useSampleGroups, type SampleMajorGroup } from '../composables/useSampleGroups'\nimport { useExpansionSet } from '../composables/useExpansionSet'\n\ninterface Props {\n samples: string[]\n modelValue: string[]\n groups?: SampleGroup[]\n enableGrouping?: boolean\n enableSmartGroup?: boolean\n experimentId?: number\n designData?: Record<string, unknown>\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n groups: () => [],\n enableGrouping: true,\n enableSmartGroup: true,\n experimentId: undefined,\n designData: undefined,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [samples: string[]]\n 'update:groups': [groups: SampleGroup[]]\n smartGroup: [result: AutoGroupResult]\n}>()\n\n// UI State\ntype ColorEdit =\n | { kind: 'single'; name: string }\n | { kind: 'family'; names: string[] }\n\nconst showSmartGroupModal = ref(false)\nconst newGroupName = ref('')\nconst editingColor = ref<ColorEdit | null>(null)\nconst colorPickerInput = ref<HTMLInputElement | null>(null)\nconst searchQuery = ref('')\nconst groupExpansion = useExpansionSet()\n\n// Sample Drag State\nconst draggingSample = ref<string | null>(null)\nconst dragSourceGroup = ref<string | null>(null)\nconst dragOverGroup = ref<string | null>(null)\n\n// Group-Reorder Drag State\ntype GroupDragKind = 'major' | 'sub' | 'flat'\nconst draggingGroup = ref<string | null>(null)\nconst draggingGroupKind = ref<GroupDragKind | null>(null)\nconst reorderTarget = ref<string | null>(null)\nconst reorderPosition = ref<'before' | 'after' | null>(null)\n\n// Computed: groups from props\nconst internalGroups = computed({\n get: () => props.groups,\n set: (value) => emit('update:groups', value),\n})\n\nconst sampleGroups = useSampleGroups({\n samples: () => props.samples,\n groups: internalGroups,\n})\nconst hierarchicalGroups = sampleGroups.hierarchicalGroups\nconst showHierarchy = sampleGroups.showHierarchy\n\nconst groupingEnabled = computed(() => internalGroups.value.length > 0)\nconst ungroupedSamples = sampleGroups.ungroupedSamples\n\nconst sampleSearch = useTextSearch({\n items: () => props.samples,\n query: searchQuery,\n getText: sample => sample,\n})\nconst filteredSamples = sampleSearch.filteredItems\n\nconst sampleSelection = useListSelection({\n selected: () => props.modelValue,\n items: () => props.samples,\n})\nconst isAllSelected = sampleSelection.isAllSelected\n\n// Toggle functions\nfunction toggleSelectAll() {\n emit('update:modelValue', sampleSelection.toggleAll())\n}\n\nfunction toggleSample(sample: string) {\n emit('update:modelValue', sampleSelection.toggleValue(sample))\n}\n\nfunction toggleSamplesSelection(samples: string[]) {\n emit('update:modelValue', sampleSelection.toggleValues(samples))\n}\n\nfunction toggleGroupSamples(groupName: string) {\n const group = sampleGroups.findGroup(groupName)\n if (!group) return\n toggleSamplesSelection(group.samples)\n}\n\nfunction toggleMajorGroupSamples(majorGroup: SampleMajorGroup) {\n toggleSamplesSelection(majorGroup.allSamples)\n}\n\n// Selection state checks\nfunction isFullySelected(samples: string[]): boolean {\n return sampleSelection.isFullySelected(samples)\n}\n\nfunction isPartiallySelected(samples: string[]): boolean {\n return sampleSelection.isPartiallySelected(samples)\n}\n\nfunction isGroupFullySelected(groupName: string): boolean {\n const group = sampleGroups.findGroup(groupName)\n return group ? isFullySelected(group.samples) : false\n}\n\nfunction isGroupPartiallySelected(groupName: string): boolean {\n const group = sampleGroups.findGroup(groupName)\n return group ? isPartiallySelected(group.samples) : false\n}\n\nfunction isMajorGroupFullySelected(majorGroup: SampleMajorGroup): boolean {\n return isFullySelected(majorGroup.allSamples)\n}\n\nfunction isMajorGroupPartiallySelected(majorGroup: SampleMajorGroup): boolean {\n return isPartiallySelected(majorGroup.allSamples)\n}\n\n// Expand/collapse\nfunction toggleGroupExpanded(groupName: string) {\n groupExpansion.toggle(groupName)\n}\n\nfunction isGroupExpanded(groupName: string): boolean {\n return groupExpansion.isExpanded(groupName)\n}\n\nfunction expandAllGroups() {\n const expanded: string[] = []\n for (const major of hierarchicalGroups.value) {\n expanded.push(`major:${major.name}`)\n for (const sub of major.subGroups) {\n expanded.push(sub.name)\n }\n }\n expanded.push('__ungrouped__')\n groupExpansion.setExpanded(expanded)\n}\n\nfunction collapseAllGroups() {\n groupExpansion.collapseAll()\n}\n\n// Smart group\nfunction handleSmartGroupApply(result: AutoGroupResult) {\n emit('smartGroup', result)\n emit('update:groups', result.groups)\n}\n\n// Group management\nfunction clearGroups() {\n internalGroups.value = []\n}\n\nfunction deleteMajorGroup(majorGroup: SampleMajorGroup) {\n internalGroups.value = internalGroups.value.filter(\n g => !majorGroup.subGroups.some(sg => sg.name === g.name)\n )\n}\n\nfunction deleteGroup(groupName: string) {\n internalGroups.value = internalGroups.value.filter(g => g.name !== groupName)\n}\n\nfunction removeSampleFromGroup(sample: string, groupName: string) {\n internalGroups.value = internalGroups.value.map(g =>\n g.name === groupName\n ? { ...g, samples: g.samples.filter((s: string) => s !== sample) }\n : g\n )\n}\n\n// Drag and Drop handlers\nfunction handleDragStart(sample: string, sourceGroup: string | null, event: DragEvent) {\n draggingSample.value = sample\n dragSourceGroup.value = sourceGroup\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', sample)\n }\n}\n\nfunction resetDragState() {\n draggingSample.value = null\n dragSourceGroup.value = null\n dragOverGroup.value = null\n}\n\nfunction handleDragEnd() {\n resetDragState()\n}\n\nfunction handleDragOver(groupName: string, event: DragEvent) {\n // Only accept this drop target when a sample is being dragged; group drags are handled separately.\n if (!draggingSample.value) return\n event.preventDefault()\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move'\n }\n dragOverGroup.value = groupName\n}\n\nfunction handleDragLeave() {\n dragOverGroup.value = null\n}\n\nfunction handleDrop(targetGroupName: string, event: DragEvent) {\n if (!draggingSample.value) return\n event.preventDefault()\n\n const sample = draggingSample.value\n const sourceGroup = dragSourceGroup.value\n\n if (sourceGroup === targetGroupName) {\n resetDragState()\n return\n }\n\n internalGroups.value = internalGroups.value.map(g => {\n if (sourceGroup && g.name === sourceGroup) {\n return { ...g, samples: g.samples.filter((s: string) => s !== sample) }\n }\n if (g.name === targetGroupName && !g.samples.includes(sample)) {\n return { ...g, samples: [...g.samples, sample] }\n }\n return g\n })\n\n resetDragState()\n}\n\n// Group reorder helpers\nfunction detectSeparator(groups: SampleGroup[]): string {\n return groups.some(g => g.name.includes('/')) ? '/' : '_'\n}\n\nfunction getMajorPrefix(groupName: string, separator: string): string {\n const parts = groupName.split(separator)\n return parts.length > 1 ? parts[0] : groupName\n}\n\nfunction handleGroupDragStart(name: string, kind: GroupDragKind, event: DragEvent) {\n draggingGroup.value = name\n draggingGroupKind.value = kind\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', `mint-group:${name}`)\n }\n}\n\nfunction handleGroupDragEnd() {\n draggingGroup.value = null\n draggingGroupKind.value = null\n reorderTarget.value = null\n reorderPosition.value = null\n}\n\nfunction handleGroupDragOver(name: string, kind: GroupDragKind, event: DragEvent) {\n if (!draggingGroup.value || draggingGroupKind.value !== kind) return\n if (draggingGroup.value === name) return\n\n // Sub-group reorder is restricted to siblings under the same major prefix —\n // crossing majors would silently rename the group, which is too magical.\n if (kind === 'sub') {\n const sep = detectSeparator(internalGroups.value)\n if (getMajorPrefix(draggingGroup.value, sep) !== getMajorPrefix(name, sep)) return\n }\n\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect()\n reorderTarget.value = name\n reorderPosition.value = event.clientY < rect.top + rect.height / 2 ? 'before' : 'after'\n}\n\nfunction handleGroupDragLeave(event: DragEvent) {\n const cur = event.currentTarget as HTMLElement\n const rel = event.relatedTarget as Node | null\n if (rel && cur.contains(rel)) return\n reorderTarget.value = null\n reorderPosition.value = null\n}\n\nfunction handleGroupDrop(name: string, kind: GroupDragKind, event: DragEvent) {\n if (!draggingGroup.value || draggingGroupKind.value !== kind) {\n handleGroupDragEnd()\n return\n }\n const position = reorderPosition.value\n if (!position || draggingGroup.value === name) {\n handleGroupDragEnd()\n return\n }\n\n event.preventDefault()\n\n if (kind === 'major') {\n moveMajorGroup(draggingGroup.value, name, position)\n } else {\n moveGroup(draggingGroup.value, name, position)\n }\n\n handleGroupDragEnd()\n}\n\nfunction moveGroup(source: string, target: string, position: 'before' | 'after') {\n if (source === target) return\n const groups = [...internalGroups.value]\n const srcIdx = groups.findIndex(g => g.name === source)\n if (srcIdx === -1) return\n const [item] = groups.splice(srcIdx, 1)\n const targetIdx = groups.findIndex(g => g.name === target)\n if (targetIdx === -1) return\n groups.splice(position === 'before' ? targetIdx : targetIdx + 1, 0, item)\n internalGroups.value = groups\n}\n\nfunction moveMajorGroup(source: string, target: string, position: 'before' | 'after') {\n if (source === target) return\n const groups = [...internalGroups.value]\n const sep = detectSeparator(groups)\n\n const isSource = (g: SampleGroup) => getMajorPrefix(g.name, sep) === source\n const isTarget = (g: SampleGroup) => getMajorPrefix(g.name, sep) === target\n\n const sourceGroups = groups.filter(isSource)\n if (sourceGroups.length === 0) return\n const remaining = groups.filter(g => !isSource(g))\n\n let firstTarget = -1\n let lastTarget = -1\n remaining.forEach((g, i) => {\n if (isTarget(g)) {\n if (firstTarget === -1) firstTarget = i\n lastTarget = i\n }\n })\n if (firstTarget === -1) return\n\n const insertIdx = position === 'before' ? firstTarget : lastTarget + 1\n remaining.splice(insertIdx, 0, ...sourceGroups)\n internalGroups.value = remaining\n}\n\n// Color picker\nfunction openColorPicker(groupName: string, event: Event) {\n event.stopPropagation()\n editingColor.value = { kind: 'single', name: groupName }\n colorPickerInput.value?.click()\n}\n\nfunction openMajorGroupColorPicker(majorGroup: SampleMajorGroup, event: Event) {\n event.stopPropagation()\n editingColor.value = { kind: 'family', names: majorGroup.subGroups.map(sg => sg.name) }\n colorPickerInput.value?.click()\n}\n\nfunction handleColorChange(event: Event) {\n const edit = editingColor.value\n if (!edit) return\n\n const newColor = (event.target as HTMLInputElement).value\n // For a family edit, every sub-group gets the new seed; the hierarchical\n // computed re-derives shades from it.\n const targets = edit.kind === 'family' ? new Set(edit.names) : new Set([edit.name])\n internalGroups.value = internalGroups.value.map(g =>\n targets.has(g.name) ? { ...g, color: newColor } : g,\n )\n editingColor.value = null\n}\n\nfunction getGroupColor(groupName: string): string {\n return sampleGroups.getGroupColor(groupName)\n}\n\nconst colorPickerSeed = computed(() => {\n const edit = editingColor.value\n if (!edit) return '#3B82F6'\n return getGroupColor(edit.kind === 'family' ? edit.names[0] : edit.name)\n})\n\n// New group\nfunction addNewGroup() {\n if (!newGroupName.value.trim()) return\n\n const usedColors = new Set(internalGroups.value.map(g => g.color))\n const availableColor = DEFAULT_COLORS.find(c => !usedColors.has(c)) || DEFAULT_COLORS[0]\n\n const newGroup: SampleGroup = {\n name: newGroupName.value.trim(),\n color: availableColor,\n samples: [],\n }\n\n internalGroups.value = [...internalGroups.value, newGroup]\n newGroupName.value = ''\n}\n\n</script>\n\n<template>\n <div class=\"mint-sample-selector\">\n <!-- Select All Row -->\n <label class=\"mint-sample-selector__select-all\">\n <input\n type=\"checkbox\"\n :checked=\"isAllSelected\"\n @change=\"toggleSelectAll\"\n class=\"mint-sample-selector__checkbox\"\n />\n <span class=\"mint-sample-selector__select-all-label\">Select All</span>\n <span class=\"mint-sample-selector__select-all-count\">{{ samples.length }} samples</span>\n </label>\n\n <!-- Action Buttons Row -->\n <div v-if=\"enableGrouping\" class=\"mint-sample-selector__actions\">\n <div class=\"mint-sample-selector__actions-row\">\n <!-- Smart Group Button -->\n <BaseButton\n v-if=\"enableSmartGroup\"\n :variant=\"groupingEnabled ? 'primary' : 'secondary'\"\n size=\"sm\"\n :disabled=\"samples.length === 0\"\n class=\"mint-sample-selector__action-btn\"\n @click=\"showSmartGroupModal = true\"\n >\n <svg class=\"mint-sample-selector__action-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M13 10V3L4 14h7v7l9-11h-7z\" />\n </svg>\n <span>Smart Group</span>\n </BaseButton>\n\n <!-- Reset Button -->\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n :disabled=\"internalGroups.length === 0\"\n class=\"mint-sample-selector__action-btn mint-sample-selector__action-btn--reset\"\n @click=\"clearGroups\"\n title=\"Clear all groups\"\n >\n <svg class=\"mint-sample-selector__action-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\" />\n </svg>\n </BaseButton>\n </div>\n </div>\n\n <!-- Grouped View -->\n <div v-if=\"groupingEnabled\" class=\"mint-sample-selector__grouped\">\n <!-- Groups Header -->\n <div class=\"mint-sample-selector__groups-header\">\n <span class=\"mint-sample-selector__groups-title\">Groups ({{ internalGroups.length }})</span>\n <div class=\"mint-sample-selector__groups-controls\">\n <button type=\"button\" class=\"mint-sample-selector__expand-btn\" @click=\"expandAllGroups\" title=\"Expand all\">\n <svg class=\"mint-sample-selector__expand-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n <button type=\"button\" class=\"mint-sample-selector__expand-btn\" @click=\"collapseAllGroups\" title=\"Collapse all\">\n <svg class=\"mint-sample-selector__expand-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Hierarchical Tree -->\n <div class=\"mint-sample-selector__tree\">\n <!-- Major Groups (when hierarchy is meaningful) -->\n <template v-if=\"showHierarchy\">\n <div\n v-for=\"majorGroup in hierarchicalGroups\"\n :key=\"majorGroup.name\"\n class=\"mint-sample-selector__major-group\"\n >\n <!-- Major Group Header -->\n <div\n :class=\"[\n 'mint-sample-selector__major-header',\n draggingGroup === majorGroup.name && draggingGroupKind === 'major' ? 'mint-sample-selector__header--dragging' : '',\n reorderTarget === majorGroup.name && draggingGroupKind === 'major' && reorderPosition === 'before' ? 'mint-sample-selector__header--drag-over-before' : '',\n reorderTarget === majorGroup.name && draggingGroupKind === 'major' && reorderPosition === 'after' ? 'mint-sample-selector__header--drag-over-after' : '',\n ]\"\n draggable=\"true\"\n @click=\"toggleGroupExpanded(`major:${majorGroup.name}`)\"\n @dragstart=\"handleGroupDragStart(majorGroup.name, 'major', $event)\"\n @dragend=\"handleGroupDragEnd\"\n @dragover=\"handleGroupDragOver(majorGroup.name, 'major', $event)\"\n @dragleave=\"handleGroupDragLeave($event)\"\n @drop=\"handleGroupDrop(majorGroup.name, 'major', $event)\"\n >\n <svg\n :class=\"[\n 'mint-sample-selector__chevron',\n isGroupExpanded(`major:${majorGroup.name}`) ? 'mint-sample-selector__chevron--open' : '',\n ]\"\n fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n\n <input\n type=\"checkbox\"\n :checked=\"isMajorGroupFullySelected(majorGroup)\"\n :indeterminate=\"isMajorGroupPartiallySelected(majorGroup)\"\n class=\"mint-sample-selector__checkbox\"\n :style=\"{ accentColor: majorGroup.color }\"\n @click.stop\n @change=\"toggleMajorGroupSamples(majorGroup)\"\n />\n\n <button\n type=\"button\"\n class=\"mint-sample-selector__color-dot mint-sample-selector__color-dot--large mint-sample-selector__color-dot--clickable\"\n :style=\"{ backgroundColor: majorGroup.color }\"\n @click.stop=\"openMajorGroupColorPicker(majorGroup, $event)\"\n title=\"Click to change color family\"\n />\n\n <span class=\"mint-sample-selector__major-name\">{{ majorGroup.name }}</span>\n\n <span\n class=\"mint-sample-selector__count-badge\"\n :style=\"{ backgroundColor: majorGroup.color + '20', color: majorGroup.color }\"\n >\n {{ majorGroup.allSamples.length }}\n </span>\n\n <button\n type=\"button\"\n class=\"mint-sample-selector__delete-btn mint-sample-selector__delete-btn--hidden\"\n @click.stop=\"deleteMajorGroup(majorGroup)\"\n title=\"Remove major group\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <!-- Sub Groups (collapsible) -->\n <Transition name=\"mint-collapse\">\n <div\n v-if=\"isGroupExpanded(`major:${majorGroup.name}`)\"\n class=\"mint-sample-selector__sub-groups\"\n :style=\"{ borderColor: majorGroup.color + '30' }\"\n >\n <div\n v-for=\"subGroup in majorGroup.subGroups\"\n :key=\"subGroup.name\"\n :class=\"[\n 'mint-sample-selector__sub-group',\n dragOverGroup === subGroup.name ? 'mint-sample-selector__sub-group--drag-over' : '',\n ]\"\n @dragover=\"handleDragOver(subGroup.name, $event)\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop(subGroup.name, $event)\"\n >\n <!-- Sub Group Header -->\n <div\n :class=\"[\n 'mint-sample-selector__sub-header',\n draggingGroup === subGroup.name && draggingGroupKind === 'sub' ? 'mint-sample-selector__header--dragging' : '',\n reorderTarget === subGroup.name && draggingGroupKind === 'sub' && reorderPosition === 'before' ? 'mint-sample-selector__header--drag-over-before' : '',\n reorderTarget === subGroup.name && draggingGroupKind === 'sub' && reorderPosition === 'after' ? 'mint-sample-selector__header--drag-over-after' : '',\n ]\"\n draggable=\"true\"\n @click=\"toggleGroupExpanded(subGroup.name)\"\n @dragstart=\"handleGroupDragStart(subGroup.name, 'sub', $event)\"\n @dragend=\"handleGroupDragEnd\"\n @dragover=\"handleGroupDragOver(subGroup.name, 'sub', $event)\"\n @dragleave=\"handleGroupDragLeave($event)\"\n @drop=\"handleGroupDrop(subGroup.name, 'sub', $event)\"\n >\n <svg\n :class=\"[\n 'mint-sample-selector__chevron mint-sample-selector__chevron--small',\n isGroupExpanded(subGroup.name) ? 'mint-sample-selector__chevron--open' : '',\n ]\"\n fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n\n <input\n type=\"checkbox\"\n :checked=\"isGroupFullySelected(subGroup.name)\"\n :indeterminate=\"isGroupPartiallySelected(subGroup.name)\"\n class=\"mint-sample-selector__checkbox mint-sample-selector__checkbox--small\"\n :style=\"{ accentColor: subGroup.displayColor }\"\n @click.stop\n @change=\"toggleGroupSamples(subGroup.name)\"\n />\n\n <div\n class=\"mint-sample-selector__color-dot\"\n :style=\"{ backgroundColor: subGroup.displayColor }\"\n aria-hidden=\"true\"\n />\n\n <span class=\"mint-sample-selector__sub-name\">{{ subGroup.name }}</span>\n\n <span\n class=\"mint-sample-selector__count-badge mint-sample-selector__count-badge--small\"\n :style=\"{ backgroundColor: subGroup.displayBg, color: subGroup.displayColor }\"\n >\n {{ subGroup.samples.length }}\n </span>\n\n <button\n type=\"button\"\n class=\"mint-sample-selector__delete-btn mint-sample-selector__delete-btn--hidden\"\n @click.stop=\"deleteGroup(subGroup.name)\"\n title=\"Remove sub group\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <!-- Samples (collapsible) -->\n <Transition name=\"mint-collapse\">\n <div\n v-if=\"isGroupExpanded(subGroup.name)\"\n class=\"mint-sample-selector__samples\"\n :style=\"{ borderColor: subGroup.displayBorder }\"\n >\n <div\n v-for=\"sample in subGroup.samples\"\n :key=\"sample\"\n :class=\"[\n 'mint-sample-selector__sample',\n draggingSample === sample ? 'mint-sample-selector__sample--dragging' : '',\n ]\"\n draggable=\"true\"\n @dragstart=\"handleDragStart(sample, subGroup.name, $event)\"\n @dragend=\"handleDragEnd\"\n >\n <svg class=\"mint-sample-selector__drag-handle\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8h16M4 16h16\" />\n </svg>\n <input\n type=\"checkbox\"\n :checked=\"modelValue.includes(sample)\"\n class=\"mint-sample-selector__checkbox mint-sample-selector__checkbox--tiny\"\n :style=\"{ accentColor: subGroup.displayColor }\"\n @change=\"toggleSample(sample)\"\n />\n <span class=\"mint-sample-selector__sample-name\">{{ sample }}</span>\n <button\n type=\"button\"\n class=\"mint-sample-selector__remove-btn\"\n @click=\"removeSampleFromGroup(sample, subGroup.name)\"\n title=\"Remove from group\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M20 12H4\" />\n </svg>\n </button>\n </div>\n </div>\n </Transition>\n </div>\n </div>\n </Transition>\n </div>\n </template>\n\n <!-- Flat Groups (when no meaningful hierarchy - e.g., level 1) -->\n <template v-else>\n <div\n v-for=\"group in internalGroups\"\n :key=\"group.name\"\n :class=\"[\n 'mint-sample-selector__sub-group',\n dragOverGroup === group.name ? 'mint-sample-selector__sub-group--drag-over' : '',\n ]\"\n @dragover=\"handleDragOver(group.name, $event)\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop(group.name, $event)\"\n >\n <!-- Group Header -->\n <div\n :class=\"[\n 'mint-sample-selector__major-header',\n draggingGroup === group.name && draggingGroupKind === 'flat' ? 'mint-sample-selector__header--dragging' : '',\n reorderTarget === group.name && draggingGroupKind === 'flat' && reorderPosition === 'before' ? 'mint-sample-selector__header--drag-over-before' : '',\n reorderTarget === group.name && draggingGroupKind === 'flat' && reorderPosition === 'after' ? 'mint-sample-selector__header--drag-over-after' : '',\n ]\"\n draggable=\"true\"\n @click=\"toggleGroupExpanded(group.name)\"\n @dragstart=\"handleGroupDragStart(group.name, 'flat', $event)\"\n @dragend=\"handleGroupDragEnd\"\n @dragover=\"handleGroupDragOver(group.name, 'flat', $event)\"\n @dragleave=\"handleGroupDragLeave($event)\"\n @drop=\"handleGroupDrop(group.name, 'flat', $event)\"\n >\n <svg\n :class=\"[\n 'mint-sample-selector__chevron',\n isGroupExpanded(group.name) ? 'mint-sample-selector__chevron--open' : '',\n ]\"\n fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n\n <input\n type=\"checkbox\"\n :checked=\"isGroupFullySelected(group.name)\"\n :indeterminate=\"isGroupPartiallySelected(group.name)\"\n class=\"mint-sample-selector__checkbox\"\n :style=\"{ accentColor: group.color }\"\n @click.stop\n @change=\"toggleGroupSamples(group.name)\"\n />\n\n <button\n type=\"button\"\n class=\"mint-sample-selector__color-dot mint-sample-selector__color-dot--large mint-sample-selector__color-dot--clickable\"\n :style=\"{ backgroundColor: group.color }\"\n @click.stop=\"openColorPicker(group.name, $event)\"\n title=\"Click to change color\"\n />\n\n <span class=\"mint-sample-selector__major-name\">{{ group.name }}</span>\n\n <span\n class=\"mint-sample-selector__count-badge\"\n :style=\"{ backgroundColor: group.color + '20', color: group.color }\"\n >\n {{ group.samples.length }}\n </span>\n\n <button\n type=\"button\"\n class=\"mint-sample-selector__delete-btn mint-sample-selector__delete-btn--hidden\"\n @click.stop=\"deleteGroup(group.name)\"\n title=\"Remove group\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <!-- Samples (collapsible) -->\n <Transition name=\"mint-collapse\">\n <div\n v-if=\"isGroupExpanded(group.name)\"\n class=\"mint-sample-selector__samples\"\n :style=\"{ borderColor: group.color + '40' }\"\n >\n <div\n v-for=\"sample in group.samples\"\n :key=\"sample\"\n :class=\"[\n 'mint-sample-selector__sample',\n draggingSample === sample ? 'mint-sample-selector__sample--dragging' : '',\n ]\"\n draggable=\"true\"\n @dragstart=\"handleDragStart(sample, group.name, $event)\"\n @dragend=\"handleDragEnd\"\n >\n <svg class=\"mint-sample-selector__drag-handle\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8h16M4 16h16\" />\n </svg>\n <input\n type=\"checkbox\"\n :checked=\"modelValue.includes(sample)\"\n class=\"mint-sample-selector__checkbox mint-sample-selector__checkbox--tiny\"\n :style=\"{ accentColor: group.color }\"\n @change=\"toggleSample(sample)\"\n />\n <span class=\"mint-sample-selector__sample-name\">{{ sample }}</span>\n <button\n type=\"button\"\n class=\"mint-sample-selector__remove-btn\"\n @click=\"removeSampleFromGroup(sample, group.name)\"\n title=\"Remove from group\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M20 12H4\" />\n </svg>\n </button>\n </div>\n </div>\n </Transition>\n </div>\n </template>\n\n <!-- Empty state -->\n <div v-if=\"internalGroups.length === 0\" class=\"mint-sample-selector__empty\">\n Click Smart Group to auto-group samples\n </div>\n </div>\n\n <!-- Ungrouped Samples Section -->\n <div v-if=\"ungroupedSamples.length > 0\" class=\"mint-sample-selector__ungrouped\">\n <div\n class=\"mint-sample-selector__ungrouped-header\"\n @click=\"toggleGroupExpanded('__ungrouped__')\"\n >\n <svg\n :class=\"[\n 'mint-sample-selector__chevron',\n isGroupExpanded('__ungrouped__') ? 'mint-sample-selector__chevron--open' : '',\n ]\"\n fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n <span class=\"mint-sample-selector__ungrouped-label\">Ungrouped</span>\n <span class=\"mint-sample-selector__ungrouped-count\">{{ ungroupedSamples.length }}</span>\n </div>\n\n <Transition name=\"mint-collapse\">\n <div\n v-if=\"isGroupExpanded('__ungrouped__')\"\n class=\"mint-sample-selector__ungrouped-list\"\n >\n <div\n v-for=\"sample in ungroupedSamples\"\n :key=\"sample\"\n :class=\"[\n 'mint-sample-selector__sample',\n draggingSample === sample ? 'mint-sample-selector__sample--dragging' : '',\n ]\"\n draggable=\"true\"\n @dragstart=\"handleDragStart(sample, null, $event)\"\n @dragend=\"handleDragEnd\"\n >\n <svg class=\"mint-sample-selector__drag-handle\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8h16M4 16h16\" />\n </svg>\n <input\n type=\"checkbox\"\n :checked=\"modelValue.includes(sample)\"\n class=\"mint-sample-selector__checkbox mint-sample-selector__checkbox--small\"\n @change=\"toggleSample(sample)\"\n />\n <span class=\"mint-sample-selector__sample-name\">{{ sample }}</span>\n </div>\n </div>\n </Transition>\n </div>\n\n <!-- New Group Input -->\n <div class=\"mint-sample-selector__new-group\">\n <BaseInput\n v-model=\"newGroupName\"\n placeholder=\"New group name...\"\n class=\"mint-sample-selector__new-group-input\"\n @keyup.enter=\"addNewGroup\"\n />\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n :disabled=\"!newGroupName.trim()\"\n class=\"mint-sample-selector__new-group-btn\"\n @click=\"addNewGroup\"\n >\n Add\n </BaseButton>\n </div>\n\n <!-- Hidden color picker input -->\n <input\n ref=\"colorPickerInput\"\n type=\"color\"\n class=\"mint-sample-selector__color-input\"\n :value=\"colorPickerSeed\"\n @change=\"handleColorChange\"\n />\n </div>\n\n <!-- Flat View (when no groups) -->\n <div v-if=\"!groupingEnabled\" class=\"mint-sample-selector__flat\">\n <!-- Search -->\n <div class=\"mint-sample-selector__search\">\n <svg class=\"mint-sample-selector__search-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" />\n </svg>\n <input\n v-model=\"searchQuery\"\n type=\"text\"\n placeholder=\"Search samples...\"\n class=\"mint-sample-selector__search-input\"\n />\n </div>\n\n <!-- Flat samples list -->\n <div class=\"mint-sample-selector__flat-list\">\n <div\n v-for=\"sample in filteredSamples\"\n :key=\"sample\"\n class=\"mint-sample-selector__flat-item\"\n >\n <input\n type=\"checkbox\"\n :checked=\"modelValue.includes(sample)\"\n class=\"mint-sample-selector__checkbox\"\n @change=\"toggleSample(sample)\"\n />\n <span class=\"mint-sample-selector__flat-name\">{{ sample }}</span>\n </div>\n\n <div v-if=\"filteredSamples.length === 0 && searchQuery.trim()\" class=\"mint-sample-selector__empty\">\n No samples match \"{{ searchQuery }}\"\n </div>\n </div>\n </div>\n\n <!-- Smart Grouping Modal -->\n <AutoGroupModal\n v-model=\"showSmartGroupModal\"\n :samples=\"samples\"\n :experiment-id=\"experimentId\"\n :design-data=\"designData\"\n @apply=\"handleSmartGroupApply\"\n />\n </div>\n</template>\n\n<style>\n@import '../styles/components/sample-selector.css';\n</style>\n","<script setup lang=\"ts\">\n/** Drag-and-drop week/day calendar for scheduling lab events in configurable time slots. */\nimport { ref, computed, watch, onMounted, onUnmounted } from 'vue'\nimport type {\n ScheduleView,\n ScheduleEvent,\n ScheduleBlockedSlot,\n ScheduleEventStatus,\n} from '../types/components'\nimport { formatTime } from '../composables/useTimeUtils'\nimport { useScheduleDrag } from '../composables/useScheduleDrag'\n\ninterface Props {\n modelValue?: Date | string\n view?: ScheduleView\n events?: ScheduleEvent[]\n dayStartHour?: number\n dayEndHour?: number\n slotDuration?: number\n showNowIndicator?: boolean\n readonly?: boolean\n blockedSlots?: ScheduleBlockedSlot[]\n weekStartsOn?: 0 | 1\n showViewToggle?: boolean\n showNavigation?: boolean\n statusColors?: Record<string, string>\n locale?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n view: 'week',\n events: () => [],\n dayStartHour: 6,\n dayEndHour: 22,\n slotDuration: 30,\n showNowIndicator: true,\n readonly: false,\n blockedSlots: () => [],\n weekStartsOn: 1,\n showViewToggle: true,\n showNavigation: true,\n locale: 'en-US',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: Date]\n 'update:view': [value: ScheduleView]\n 'event-click': [event: ScheduleEvent]\n 'event-create': [context: { start: Date; end: Date }]\n 'event-update': [context: { event: ScheduleEvent; newStart: Date; newEnd: Date }]\n 'slot-click': [context: { date: Date; hour: number; minute: number }]\n 'navigate': [context: { direction: string; date: Date }]\n}>()\n\nconst SLOT_HEIGHT = 40\nconst currentView = ref<ScheduleView>(props.view)\nconst currentDate = ref<Date>(parseModelDate(props.modelValue))\nconst nowTimer = ref<ReturnType<typeof setInterval>>()\nconst now = ref(new Date())\nconst bodyRef = ref<HTMLDivElement>()\n\nwatch(() => props.view, (v) => { currentView.value = v })\nwatch(() => props.modelValue, (v) => { if (v) currentDate.value = parseModelDate(v) })\n\nconst slotHeightRef = computed(() => SLOT_HEIGHT)\nconst slotDurationRef = computed(() => props.slotDuration)\nconst dayStartRef = computed(() => props.dayStartHour)\nconst dayEndRef = computed(() => props.dayEndHour)\nconst readonlyRef = computed(() => props.readonly)\n\nconst { isDragging, ghost, startCreate, startMove, startResize } = useScheduleDrag({\n slotDuration: slotDurationRef,\n dayStartHour: dayStartRef,\n dayEndHour: dayEndRef,\n readonly: readonlyRef,\n slotHeight: slotHeightRef,\n onCreateComplete(start, end) {\n emit('event-create', { start, end })\n },\n onMoveComplete(event, newStart, newEnd) {\n emit('event-update', { event, newStart, newEnd })\n },\n onResizeComplete(event, newStart, newEnd) {\n emit('event-update', { event, newStart, newEnd })\n },\n})\n\nconst totalSlots = computed(() => {\n return ((props.dayEndHour - props.dayStartHour) * 60) / props.slotDuration\n})\n\nconst timeLabels = computed(() => {\n const labels: string[] = []\n for (let i = 0; i <= totalSlots.value; i++) {\n const totalMinutes = props.dayStartHour * 60 + i * props.slotDuration\n const hour = Math.floor(totalMinutes / 60)\n const minute = totalMinutes % 60\n labels.push(formatTime(hour, minute, '24h'))\n }\n return labels\n})\n\nconst weekDays = computed(() => {\n const days: Date[] = []\n const start = getWeekStart(currentDate.value)\n for (let i = 0; i < 7; i++) {\n const d = new Date(start)\n d.setDate(d.getDate() + i)\n days.push(d)\n }\n return days\n})\n\nconst visibleDays = computed(() => {\n if (currentView.value === 'day') return [currentDate.value]\n if (currentView.value === 'week') return weekDays.value\n return []\n})\n\nconst headerTitle = computed(() => {\n if (currentView.value === 'day') {\n return currentDate.value.toLocaleDateString(props.locale, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n })\n }\n if (currentView.value === 'week') {\n const days = weekDays.value\n const first = days[0]\n const last = days[6]\n const sameMonth = first.getMonth() === last.getMonth()\n if (sameMonth) {\n return `${first.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })} - ${last.getDate()}, ${last.getFullYear()}`\n }\n return `${first.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })} - ${last.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })}, ${last.getFullYear()}`\n }\n return currentDate.value.toLocaleDateString(props.locale, { year: 'numeric', month: 'long' })\n})\n\n// Month view computations\nconst monthDays = computed(() => {\n const year = currentDate.value.getFullYear()\n const month = currentDate.value.getMonth()\n const firstDay = new Date(year, month, 1)\n const lastDay = new Date(year, month + 1, 0)\n\n const days: { date: Date; isCurrentMonth: boolean }[] = []\n\n // Adjust for weekStartsOn\n let startPadding = firstDay.getDay() - props.weekStartsOn\n if (startPadding < 0) startPadding += 7\n\n for (let i = startPadding - 1; i >= 0; i--) {\n const date = new Date(year, month, -i)\n days.push({ date, isCurrentMonth: false })\n }\n\n for (let i = 1; i <= lastDay.getDate(); i++) {\n days.push({ date: new Date(year, month, i), isCurrentMonth: true })\n }\n\n const remaining = 42 - days.length\n for (let i = 1; i <= remaining; i++) {\n days.push({ date: new Date(year, month + 1, i), isCurrentMonth: false })\n }\n\n return days\n})\n\nconst monthWeekdayLabels = computed(() => {\n const labels: string[] = []\n const base = new Date(2024, 0, props.weekStartsOn === 1 ? 1 : 7) // A known Monday or Sunday\n for (let i = 0; i < 7; i++) {\n const d = new Date(base)\n d.setDate(d.getDate() + i)\n labels.push(d.toLocaleDateString(props.locale, { weekday: 'short' }))\n }\n return labels\n})\n\nfunction parseModelDate(value?: Date | string): Date {\n if (!value) return new Date()\n if (value instanceof Date) return value\n const d = new Date(value)\n return isNaN(d.getTime()) ? new Date() : d\n}\n\nfunction getWeekStart(date: Date): Date {\n const d = new Date(date)\n const day = d.getDay()\n const diff = (day - props.weekStartsOn + 7) % 7\n d.setDate(d.getDate() - diff)\n return d\n}\n\nfunction isToday(date: Date): boolean {\n return date.toDateString() === now.value.toDateString()\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return a.toDateString() === b.toDateString()\n}\n\nfunction navigate(direction: 'prev' | 'next' | 'today') {\n const d = new Date(currentDate.value)\n if (direction === 'today') {\n currentDate.value = new Date()\n } else {\n const delta = direction === 'prev' ? -1 : 1\n if (currentView.value === 'day') d.setDate(d.getDate() + delta)\n else if (currentView.value === 'week') d.setDate(d.getDate() + delta * 7)\n else d.setMonth(d.getMonth() + delta)\n currentDate.value = d\n }\n emit('update:modelValue', currentDate.value)\n emit('navigate', { direction, date: currentDate.value })\n}\n\nfunction setView(view: ScheduleView) {\n currentView.value = view\n emit('update:view', view)\n}\n\nfunction getEventStyle(event: ScheduleEvent, dayDate: Date) {\n const eventStart = new Date(event.start)\n const eventEnd = new Date(event.end)\n if (!isSameDay(eventStart, dayDate) && !isSameDay(eventEnd, dayDate)) return null\n\n const dayStartMin = props.dayStartHour * 60\n const startMin = Math.max(eventStart.getHours() * 60 + eventStart.getMinutes(), dayStartMin) - dayStartMin\n const endMin = Math.min(eventEnd.getHours() * 60 + eventEnd.getMinutes(), props.dayEndHour * 60) - dayStartMin\n\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n return {\n top: `${startMin * pixelsPerMin}px`,\n height: `${Math.max((endMin - startMin) * pixelsPerMin, SLOT_HEIGHT / 2)}px`,\n }\n}\n\nfunction getEventsForDay(day: Date): ScheduleEvent[] {\n return props.events.filter((e) => {\n const start = new Date(e.start)\n return isSameDay(start, day)\n })\n}\n\nfunction getEventsForDate(date: Date): ScheduleEvent[] {\n return props.events.filter((e) => {\n const start = new Date(e.start)\n return isSameDay(start, date)\n })\n}\n\nfunction getBlockedForDay(day: Date): ScheduleBlockedSlot[] {\n return props.blockedSlots.filter((b) => {\n const start = new Date(b.start)\n return isSameDay(start, day)\n })\n}\n\nfunction getBlockedStyle(slot: ScheduleBlockedSlot) {\n const start = new Date(slot.start)\n const end = new Date(slot.end)\n const dayStartMin = props.dayStartHour * 60\n const startMin = Math.max(start.getHours() * 60 + start.getMinutes(), dayStartMin) - dayStartMin\n const endMin = Math.min(end.getHours() * 60 + end.getMinutes(), props.dayEndHour * 60) - dayStartMin\n\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n return {\n top: `${startMin * pixelsPerMin}px`,\n height: `${(endMin - startMin) * pixelsPerMin}px`,\n }\n}\n\nfunction getNowIndicatorStyle(): Record<string, string> | null {\n if (!props.showNowIndicator) return null\n const nowMin = now.value.getHours() * 60 + now.value.getMinutes()\n const dayStartMin = props.dayStartHour * 60\n const dayEndMin = props.dayEndHour * 60\n if (nowMin < dayStartMin || nowMin > dayEndMin) return null\n\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n return { top: `${(nowMin - dayStartMin) * pixelsPerMin}px` }\n}\n\nfunction getStatusClass(status?: ScheduleEventStatus): string {\n if (!status) return 'mint-schedule__event--confirmed'\n return `mint-schedule__event--${status}`\n}\n\nfunction getMonthStatusClass(status?: ScheduleEventStatus): string {\n if (!status) return 'mint-schedule__month-event--confirmed'\n return `mint-schedule__month-event--${status}`\n}\n\nfunction onEventClick(event: ScheduleEvent, e: MouseEvent) {\n e.stopPropagation()\n emit('event-click', event)\n}\n\nfunction onSlotClick(day: Date, slotIndex: number) {\n if (props.readonly || isDragging.value) return\n const totalMinutes = props.dayStartHour * 60 + slotIndex * props.slotDuration\n const hour = Math.floor(totalMinutes / 60)\n const minute = totalMinutes % 60\n const date = new Date(day)\n date.setHours(hour, minute, 0, 0)\n emit('slot-click', { date, hour, minute })\n}\n\nfunction onSlotPointerDown(day: Date, slotIndex: number, e: PointerEvent) {\n if (props.readonly) return\n const date = new Date(day)\n const totalMinutes = props.dayStartHour * 60 + slotIndex * props.slotDuration\n date.setHours(Math.floor(totalMinutes / 60), totalMinutes % 60, 0, 0)\n const dayIndex = visibleDays.value.findIndex((d) => isSameDay(d, day))\n startCreate(date, e.clientY, dayIndex)\n}\n\nfunction onEventPointerDown(event: ScheduleEvent, dayIndex: number, e: PointerEvent) {\n e.stopPropagation()\n startMove(event, e.clientY, dayIndex)\n}\n\nfunction onResizePointerDown(event: ScheduleEvent, edge: 'top' | 'bottom', dayIndex: number, e: PointerEvent) {\n e.stopPropagation()\n startResize(event, edge, e.clientY, dayIndex)\n}\n\nfunction onMonthDayClick(date: Date) {\n currentDate.value = date\n emit('update:modelValue', date)\n setView('day')\n}\n\nfunction formatEventTime(event: ScheduleEvent): string {\n const start = new Date(event.start)\n const end = new Date(event.end)\n return `${formatTime(start.getHours(), start.getMinutes())} - ${formatTime(end.getHours(), end.getMinutes())}`\n}\n\nonMounted(() => {\n nowTimer.value = setInterval(() => { now.value = new Date() }, 60000)\n\n // Scroll to current time or 8am\n if (bodyRef.value) {\n const targetHour = now.value.getHours() >= props.dayStartHour && now.value.getHours() <= props.dayEndHour\n ? now.value.getHours()\n : 8\n const offsetMin = (targetHour - props.dayStartHour) * 60\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n bodyRef.value.scrollTop = Math.max(0, offsetMin * pixelsPerMin - 100)\n }\n})\n\nonUnmounted(() => {\n if (nowTimer.value) clearInterval(nowTimer.value)\n})\n</script>\n\n<template>\n <div class=\"mint-schedule\" :style=\"({ '--slot-height': `${SLOT_HEIGHT}px` } as Record<string, string>)\">\n <!-- Header -->\n <div v-if=\"showNavigation || showViewToggle\" class=\"mint-schedule__header\">\n <div v-if=\"showNavigation\" class=\"mint-schedule__nav\">\n <button\n type=\"button\"\n class=\"mint-schedule__nav-btn\"\n aria-label=\"Previous\"\n @click=\"navigate('prev')\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"mint-schedule__nav-btn\"\n aria-label=\"Next\"\n @click=\"navigate('next')\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"mint-schedule__today-btn\"\n @click=\"navigate('today')\"\n >\n Today\n </button>\n </div>\n\n <span class=\"mint-schedule__title\">{{ headerTitle }}</span>\n\n <div v-if=\"showViewToggle\" class=\"mint-schedule__view-toggle\">\n <button\n v-for=\"v in (['day', 'week', 'month'] as ScheduleView[])\"\n :key=\"v\"\n type=\"button\"\n :class=\"['mint-schedule__view-btn', currentView === v ? 'mint-schedule__view-btn--active' : '']\"\n @click=\"setView(v)\"\n >\n {{ v.charAt(0).toUpperCase() + v.slice(1) }}\n </button>\n </div>\n </div>\n\n <!-- Day / Week View -->\n <template v-if=\"currentView === 'day' || currentView === 'week'\">\n <!-- Day headers -->\n <div v-if=\"currentView === 'week'\" class=\"mint-schedule__day-headers\">\n <div class=\"mint-schedule__day-header-gutter\" />\n <div\n v-for=\"(day, i) in visibleDays\"\n :key=\"i\"\n :class=\"['mint-schedule__day-header', isToday(day) ? 'mint-schedule__day-header--today' : '']\"\n >\n <slot name=\"day-header\" :date=\"day\" :isToday=\"isToday(day)\">\n <span class=\"mint-schedule__day-header-name\">\n {{ day.toLocaleDateString(locale, { weekday: 'short' }) }}\n </span>\n <span class=\"mint-schedule__day-header-number\">\n {{ day.getDate() }}\n </span>\n </slot>\n </div>\n </div>\n\n <!-- Time grid body -->\n <div ref=\"bodyRef\" class=\"mint-schedule__body\">\n <!-- Time gutter -->\n <div class=\"mint-schedule__time-gutter\">\n <div\n v-for=\"(label, i) in timeLabels\"\n :key=\"i\"\n class=\"mint-schedule__time-label\"\n >\n <slot name=\"time-label\" :label=\"label\" :index=\"i\">\n {{ i < timeLabels.length - 1 ? label : '' }}\n </slot>\n </div>\n </div>\n\n <!-- Day columns -->\n <div class=\"mint-schedule__day-columns\">\n <div\n v-for=\"(day, dayIdx) in visibleDays\"\n :key=\"dayIdx\"\n :class=\"['mint-schedule__day-column', isToday(day) ? 'mint-schedule__day-column--today' : '']\"\n >\n <!-- Slot grid -->\n <div\n v-for=\"slotIdx in totalSlots\"\n :key=\"slotIdx\"\n :class=\"['mint-schedule__slot', !readonly ? 'mint-schedule__slot--interactive' : '']\"\n @click=\"onSlotClick(day, slotIdx - 1)\"\n @pointerdown=\"onSlotPointerDown(day, slotIdx - 1, $event)\"\n />\n\n <!-- Events -->\n <div\n v-for=\"event in getEventsForDay(day)\"\n :key=\"event.id\"\n :class=\"['mint-schedule__event', getStatusClass(event.status)]\"\n :style=\"getEventStyle(event, day) || undefined\"\n @click=\"onEventClick(event, $event)\"\n @pointerdown=\"onEventPointerDown(event, dayIdx, $event)\"\n >\n <slot name=\"event\" :event=\"event\">\n <div\n v-if=\"!readonly && event.resizable !== false\"\n class=\"mint-schedule__event-resize-handle mint-schedule__event-resize-handle--top\"\n @pointerdown=\"onResizePointerDown(event, 'top', dayIdx, $event)\"\n />\n <div class=\"mint-schedule__event-title\">{{ event.title }}</div>\n <div class=\"mint-schedule__event-time\">{{ formatEventTime(event) }}</div>\n <div\n v-if=\"!readonly && event.resizable !== false\"\n class=\"mint-schedule__event-resize-handle mint-schedule__event-resize-handle--bottom\"\n @pointerdown=\"onResizePointerDown(event, 'bottom', dayIdx, $event)\"\n />\n </slot>\n </div>\n\n <!-- Blocked slots -->\n <div\n v-for=\"(blocked, bIdx) in getBlockedForDay(day)\"\n :key=\"`b-${bIdx}`\"\n class=\"mint-schedule__blocked\"\n :style=\"getBlockedStyle(blocked)\"\n >\n <span v-if=\"blocked.label\" class=\"mint-schedule__blocked-label\">\n {{ blocked.label }}\n </span>\n </div>\n\n <!-- Now indicator -->\n <div\n v-if=\"showNowIndicator && isToday(day) && getNowIndicatorStyle()\"\n class=\"mint-schedule__now-indicator\"\n :style=\"getNowIndicatorStyle()!\"\n >\n <div class=\"mint-schedule__now-dot\" />\n </div>\n\n <!-- Drag ghost -->\n <div\n v-if=\"isDragging && ghost && ghost.dayIndex === dayIdx\"\n class=\"mint-schedule__ghost\"\n :style=\"ghost.style\"\n />\n </div>\n </div>\n </div>\n </template>\n\n <!-- Month View -->\n <template v-if=\"currentView === 'month'\">\n <div class=\"mint-schedule__month-grid\">\n <!-- Weekday headers -->\n <div\n v-for=\"label in monthWeekdayLabels\"\n :key=\"label\"\n class=\"mint-schedule__month-weekday\"\n >\n {{ label }}\n </div>\n\n <!-- Day cells -->\n <div\n v-for=\"(day, i) in monthDays\"\n :key=\"i\"\n :class=\"[\n 'mint-schedule__month-cell',\n isToday(day.date) ? 'mint-schedule__month-cell--today' : '',\n !day.isCurrentMonth ? 'mint-schedule__month-cell--outside' : '',\n ]\"\n @click=\"onMonthDayClick(day.date)\"\n >\n <slot name=\"month-day\" :date=\"day.date\" :isToday=\"isToday(day.date)\" :events=\"getEventsForDate(day.date)\">\n <div class=\"mint-schedule__month-date\">{{ day.date.getDate() }}</div>\n <div\n v-for=\"event in getEventsForDate(day.date).slice(0, 3)\"\n :key=\"event.id\"\n :class=\"['mint-schedule__month-event', getMonthStatusClass(event.status)]\"\n @click.stop=\"onEventClick(event, $event)\"\n >\n {{ event.title }}\n </div>\n <div\n v-if=\"getEventsForDate(day.date).length > 3\"\n class=\"mint-schedule__month-more\"\n >\n +{{ getEventsForDate(day.date).length - 3 }} more\n </div>\n </slot>\n </div>\n </div>\n </template>\n </div>\n</template>\n\n<style>\n@import '../styles/components/schedule-calendar.css';\n</style>\n","<script setup lang=\"ts\">\n/** Drag-and-drop week/day calendar for scheduling lab events in configurable time slots. */\nimport { ref, computed, watch, onMounted, onUnmounted } from 'vue'\nimport type {\n ScheduleView,\n ScheduleEvent,\n ScheduleBlockedSlot,\n ScheduleEventStatus,\n} from '../types/components'\nimport { formatTime } from '../composables/useTimeUtils'\nimport { useScheduleDrag } from '../composables/useScheduleDrag'\n\ninterface Props {\n modelValue?: Date | string\n view?: ScheduleView\n events?: ScheduleEvent[]\n dayStartHour?: number\n dayEndHour?: number\n slotDuration?: number\n showNowIndicator?: boolean\n readonly?: boolean\n blockedSlots?: ScheduleBlockedSlot[]\n weekStartsOn?: 0 | 1\n showViewToggle?: boolean\n showNavigation?: boolean\n statusColors?: Record<string, string>\n locale?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n view: 'week',\n events: () => [],\n dayStartHour: 6,\n dayEndHour: 22,\n slotDuration: 30,\n showNowIndicator: true,\n readonly: false,\n blockedSlots: () => [],\n weekStartsOn: 1,\n showViewToggle: true,\n showNavigation: true,\n locale: 'en-US',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: Date]\n 'update:view': [value: ScheduleView]\n 'event-click': [event: ScheduleEvent]\n 'event-create': [context: { start: Date; end: Date }]\n 'event-update': [context: { event: ScheduleEvent; newStart: Date; newEnd: Date }]\n 'slot-click': [context: { date: Date; hour: number; minute: number }]\n 'navigate': [context: { direction: string; date: Date }]\n}>()\n\nconst SLOT_HEIGHT = 40\nconst currentView = ref<ScheduleView>(props.view)\nconst currentDate = ref<Date>(parseModelDate(props.modelValue))\nconst nowTimer = ref<ReturnType<typeof setInterval>>()\nconst now = ref(new Date())\nconst bodyRef = ref<HTMLDivElement>()\n\nwatch(() => props.view, (v) => { currentView.value = v })\nwatch(() => props.modelValue, (v) => { if (v) currentDate.value = parseModelDate(v) })\n\nconst slotHeightRef = computed(() => SLOT_HEIGHT)\nconst slotDurationRef = computed(() => props.slotDuration)\nconst dayStartRef = computed(() => props.dayStartHour)\nconst dayEndRef = computed(() => props.dayEndHour)\nconst readonlyRef = computed(() => props.readonly)\n\nconst { isDragging, ghost, startCreate, startMove, startResize } = useScheduleDrag({\n slotDuration: slotDurationRef,\n dayStartHour: dayStartRef,\n dayEndHour: dayEndRef,\n readonly: readonlyRef,\n slotHeight: slotHeightRef,\n onCreateComplete(start, end) {\n emit('event-create', { start, end })\n },\n onMoveComplete(event, newStart, newEnd) {\n emit('event-update', { event, newStart, newEnd })\n },\n onResizeComplete(event, newStart, newEnd) {\n emit('event-update', { event, newStart, newEnd })\n },\n})\n\nconst totalSlots = computed(() => {\n return ((props.dayEndHour - props.dayStartHour) * 60) / props.slotDuration\n})\n\nconst timeLabels = computed(() => {\n const labels: string[] = []\n for (let i = 0; i <= totalSlots.value; i++) {\n const totalMinutes = props.dayStartHour * 60 + i * props.slotDuration\n const hour = Math.floor(totalMinutes / 60)\n const minute = totalMinutes % 60\n labels.push(formatTime(hour, minute, '24h'))\n }\n return labels\n})\n\nconst weekDays = computed(() => {\n const days: Date[] = []\n const start = getWeekStart(currentDate.value)\n for (let i = 0; i < 7; i++) {\n const d = new Date(start)\n d.setDate(d.getDate() + i)\n days.push(d)\n }\n return days\n})\n\nconst visibleDays = computed(() => {\n if (currentView.value === 'day') return [currentDate.value]\n if (currentView.value === 'week') return weekDays.value\n return []\n})\n\nconst headerTitle = computed(() => {\n if (currentView.value === 'day') {\n return currentDate.value.toLocaleDateString(props.locale, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n })\n }\n if (currentView.value === 'week') {\n const days = weekDays.value\n const first = days[0]\n const last = days[6]\n const sameMonth = first.getMonth() === last.getMonth()\n if (sameMonth) {\n return `${first.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })} - ${last.getDate()}, ${last.getFullYear()}`\n }\n return `${first.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })} - ${last.toLocaleDateString(props.locale, { month: 'short', day: 'numeric' })}, ${last.getFullYear()}`\n }\n return currentDate.value.toLocaleDateString(props.locale, { year: 'numeric', month: 'long' })\n})\n\n// Month view computations\nconst monthDays = computed(() => {\n const year = currentDate.value.getFullYear()\n const month = currentDate.value.getMonth()\n const firstDay = new Date(year, month, 1)\n const lastDay = new Date(year, month + 1, 0)\n\n const days: { date: Date; isCurrentMonth: boolean }[] = []\n\n // Adjust for weekStartsOn\n let startPadding = firstDay.getDay() - props.weekStartsOn\n if (startPadding < 0) startPadding += 7\n\n for (let i = startPadding - 1; i >= 0; i--) {\n const date = new Date(year, month, -i)\n days.push({ date, isCurrentMonth: false })\n }\n\n for (let i = 1; i <= lastDay.getDate(); i++) {\n days.push({ date: new Date(year, month, i), isCurrentMonth: true })\n }\n\n const remaining = 42 - days.length\n for (let i = 1; i <= remaining; i++) {\n days.push({ date: new Date(year, month + 1, i), isCurrentMonth: false })\n }\n\n return days\n})\n\nconst monthWeekdayLabels = computed(() => {\n const labels: string[] = []\n const base = new Date(2024, 0, props.weekStartsOn === 1 ? 1 : 7) // A known Monday or Sunday\n for (let i = 0; i < 7; i++) {\n const d = new Date(base)\n d.setDate(d.getDate() + i)\n labels.push(d.toLocaleDateString(props.locale, { weekday: 'short' }))\n }\n return labels\n})\n\nfunction parseModelDate(value?: Date | string): Date {\n if (!value) return new Date()\n if (value instanceof Date) return value\n const d = new Date(value)\n return isNaN(d.getTime()) ? new Date() : d\n}\n\nfunction getWeekStart(date: Date): Date {\n const d = new Date(date)\n const day = d.getDay()\n const diff = (day - props.weekStartsOn + 7) % 7\n d.setDate(d.getDate() - diff)\n return d\n}\n\nfunction isToday(date: Date): boolean {\n return date.toDateString() === now.value.toDateString()\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return a.toDateString() === b.toDateString()\n}\n\nfunction navigate(direction: 'prev' | 'next' | 'today') {\n const d = new Date(currentDate.value)\n if (direction === 'today') {\n currentDate.value = new Date()\n } else {\n const delta = direction === 'prev' ? -1 : 1\n if (currentView.value === 'day') d.setDate(d.getDate() + delta)\n else if (currentView.value === 'week') d.setDate(d.getDate() + delta * 7)\n else d.setMonth(d.getMonth() + delta)\n currentDate.value = d\n }\n emit('update:modelValue', currentDate.value)\n emit('navigate', { direction, date: currentDate.value })\n}\n\nfunction setView(view: ScheduleView) {\n currentView.value = view\n emit('update:view', view)\n}\n\nfunction getEventStyle(event: ScheduleEvent, dayDate: Date) {\n const eventStart = new Date(event.start)\n const eventEnd = new Date(event.end)\n if (!isSameDay(eventStart, dayDate) && !isSameDay(eventEnd, dayDate)) return null\n\n const dayStartMin = props.dayStartHour * 60\n const startMin = Math.max(eventStart.getHours() * 60 + eventStart.getMinutes(), dayStartMin) - dayStartMin\n const endMin = Math.min(eventEnd.getHours() * 60 + eventEnd.getMinutes(), props.dayEndHour * 60) - dayStartMin\n\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n return {\n top: `${startMin * pixelsPerMin}px`,\n height: `${Math.max((endMin - startMin) * pixelsPerMin, SLOT_HEIGHT / 2)}px`,\n }\n}\n\nfunction getEventsForDay(day: Date): ScheduleEvent[] {\n return props.events.filter((e) => {\n const start = new Date(e.start)\n return isSameDay(start, day)\n })\n}\n\nfunction getEventsForDate(date: Date): ScheduleEvent[] {\n return props.events.filter((e) => {\n const start = new Date(e.start)\n return isSameDay(start, date)\n })\n}\n\nfunction getBlockedForDay(day: Date): ScheduleBlockedSlot[] {\n return props.blockedSlots.filter((b) => {\n const start = new Date(b.start)\n return isSameDay(start, day)\n })\n}\n\nfunction getBlockedStyle(slot: ScheduleBlockedSlot) {\n const start = new Date(slot.start)\n const end = new Date(slot.end)\n const dayStartMin = props.dayStartHour * 60\n const startMin = Math.max(start.getHours() * 60 + start.getMinutes(), dayStartMin) - dayStartMin\n const endMin = Math.min(end.getHours() * 60 + end.getMinutes(), props.dayEndHour * 60) - dayStartMin\n\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n return {\n top: `${startMin * pixelsPerMin}px`,\n height: `${(endMin - startMin) * pixelsPerMin}px`,\n }\n}\n\nfunction getNowIndicatorStyle(): Record<string, string> | null {\n if (!props.showNowIndicator) return null\n const nowMin = now.value.getHours() * 60 + now.value.getMinutes()\n const dayStartMin = props.dayStartHour * 60\n const dayEndMin = props.dayEndHour * 60\n if (nowMin < dayStartMin || nowMin > dayEndMin) return null\n\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n return { top: `${(nowMin - dayStartMin) * pixelsPerMin}px` }\n}\n\nfunction getStatusClass(status?: ScheduleEventStatus): string {\n if (!status) return 'mint-schedule__event--confirmed'\n return `mint-schedule__event--${status}`\n}\n\nfunction getMonthStatusClass(status?: ScheduleEventStatus): string {\n if (!status) return 'mint-schedule__month-event--confirmed'\n return `mint-schedule__month-event--${status}`\n}\n\nfunction onEventClick(event: ScheduleEvent, e: MouseEvent) {\n e.stopPropagation()\n emit('event-click', event)\n}\n\nfunction onSlotClick(day: Date, slotIndex: number) {\n if (props.readonly || isDragging.value) return\n const totalMinutes = props.dayStartHour * 60 + slotIndex * props.slotDuration\n const hour = Math.floor(totalMinutes / 60)\n const minute = totalMinutes % 60\n const date = new Date(day)\n date.setHours(hour, minute, 0, 0)\n emit('slot-click', { date, hour, minute })\n}\n\nfunction onSlotPointerDown(day: Date, slotIndex: number, e: PointerEvent) {\n if (props.readonly) return\n const date = new Date(day)\n const totalMinutes = props.dayStartHour * 60 + slotIndex * props.slotDuration\n date.setHours(Math.floor(totalMinutes / 60), totalMinutes % 60, 0, 0)\n const dayIndex = visibleDays.value.findIndex((d) => isSameDay(d, day))\n startCreate(date, e.clientY, dayIndex)\n}\n\nfunction onEventPointerDown(event: ScheduleEvent, dayIndex: number, e: PointerEvent) {\n e.stopPropagation()\n startMove(event, e.clientY, dayIndex)\n}\n\nfunction onResizePointerDown(event: ScheduleEvent, edge: 'top' | 'bottom', dayIndex: number, e: PointerEvent) {\n e.stopPropagation()\n startResize(event, edge, e.clientY, dayIndex)\n}\n\nfunction onMonthDayClick(date: Date) {\n currentDate.value = date\n emit('update:modelValue', date)\n setView('day')\n}\n\nfunction formatEventTime(event: ScheduleEvent): string {\n const start = new Date(event.start)\n const end = new Date(event.end)\n return `${formatTime(start.getHours(), start.getMinutes())} - ${formatTime(end.getHours(), end.getMinutes())}`\n}\n\nonMounted(() => {\n nowTimer.value = setInterval(() => { now.value = new Date() }, 60000)\n\n // Scroll to current time or 8am\n if (bodyRef.value) {\n const targetHour = now.value.getHours() >= props.dayStartHour && now.value.getHours() <= props.dayEndHour\n ? now.value.getHours()\n : 8\n const offsetMin = (targetHour - props.dayStartHour) * 60\n const pixelsPerMin = SLOT_HEIGHT / props.slotDuration\n bodyRef.value.scrollTop = Math.max(0, offsetMin * pixelsPerMin - 100)\n }\n})\n\nonUnmounted(() => {\n if (nowTimer.value) clearInterval(nowTimer.value)\n})\n</script>\n\n<template>\n <div class=\"mint-schedule\" :style=\"({ '--slot-height': `${SLOT_HEIGHT}px` } as Record<string, string>)\">\n <!-- Header -->\n <div v-if=\"showNavigation || showViewToggle\" class=\"mint-schedule__header\">\n <div v-if=\"showNavigation\" class=\"mint-schedule__nav\">\n <button\n type=\"button\"\n class=\"mint-schedule__nav-btn\"\n aria-label=\"Previous\"\n @click=\"navigate('prev')\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"mint-schedule__nav-btn\"\n aria-label=\"Next\"\n @click=\"navigate('next')\"\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"mint-schedule__today-btn\"\n @click=\"navigate('today')\"\n >\n Today\n </button>\n </div>\n\n <span class=\"mint-schedule__title\">{{ headerTitle }}</span>\n\n <div v-if=\"showViewToggle\" class=\"mint-schedule__view-toggle\">\n <button\n v-for=\"v in (['day', 'week', 'month'] as ScheduleView[])\"\n :key=\"v\"\n type=\"button\"\n :class=\"['mint-schedule__view-btn', currentView === v ? 'mint-schedule__view-btn--active' : '']\"\n @click=\"setView(v)\"\n >\n {{ v.charAt(0).toUpperCase() + v.slice(1) }}\n </button>\n </div>\n </div>\n\n <!-- Day / Week View -->\n <template v-if=\"currentView === 'day' || currentView === 'week'\">\n <!-- Day headers -->\n <div v-if=\"currentView === 'week'\" class=\"mint-schedule__day-headers\">\n <div class=\"mint-schedule__day-header-gutter\" />\n <div\n v-for=\"(day, i) in visibleDays\"\n :key=\"i\"\n :class=\"['mint-schedule__day-header', isToday(day) ? 'mint-schedule__day-header--today' : '']\"\n >\n <slot name=\"day-header\" :date=\"day\" :isToday=\"isToday(day)\">\n <span class=\"mint-schedule__day-header-name\">\n {{ day.toLocaleDateString(locale, { weekday: 'short' }) }}\n </span>\n <span class=\"mint-schedule__day-header-number\">\n {{ day.getDate() }}\n </span>\n </slot>\n </div>\n </div>\n\n <!-- Time grid body -->\n <div ref=\"bodyRef\" class=\"mint-schedule__body\">\n <!-- Time gutter -->\n <div class=\"mint-schedule__time-gutter\">\n <div\n v-for=\"(label, i) in timeLabels\"\n :key=\"i\"\n class=\"mint-schedule__time-label\"\n >\n <slot name=\"time-label\" :label=\"label\" :index=\"i\">\n {{ i < timeLabels.length - 1 ? label : '' }}\n </slot>\n </div>\n </div>\n\n <!-- Day columns -->\n <div class=\"mint-schedule__day-columns\">\n <div\n v-for=\"(day, dayIdx) in visibleDays\"\n :key=\"dayIdx\"\n :class=\"['mint-schedule__day-column', isToday(day) ? 'mint-schedule__day-column--today' : '']\"\n >\n <!-- Slot grid -->\n <div\n v-for=\"slotIdx in totalSlots\"\n :key=\"slotIdx\"\n :class=\"['mint-schedule__slot', !readonly ? 'mint-schedule__slot--interactive' : '']\"\n @click=\"onSlotClick(day, slotIdx - 1)\"\n @pointerdown=\"onSlotPointerDown(day, slotIdx - 1, $event)\"\n />\n\n <!-- Events -->\n <div\n v-for=\"event in getEventsForDay(day)\"\n :key=\"event.id\"\n :class=\"['mint-schedule__event', getStatusClass(event.status)]\"\n :style=\"getEventStyle(event, day) || undefined\"\n @click=\"onEventClick(event, $event)\"\n @pointerdown=\"onEventPointerDown(event, dayIdx, $event)\"\n >\n <slot name=\"event\" :event=\"event\">\n <div\n v-if=\"!readonly && event.resizable !== false\"\n class=\"mint-schedule__event-resize-handle mint-schedule__event-resize-handle--top\"\n @pointerdown=\"onResizePointerDown(event, 'top', dayIdx, $event)\"\n />\n <div class=\"mint-schedule__event-title\">{{ event.title }}</div>\n <div class=\"mint-schedule__event-time\">{{ formatEventTime(event) }}</div>\n <div\n v-if=\"!readonly && event.resizable !== false\"\n class=\"mint-schedule__event-resize-handle mint-schedule__event-resize-handle--bottom\"\n @pointerdown=\"onResizePointerDown(event, 'bottom', dayIdx, $event)\"\n />\n </slot>\n </div>\n\n <!-- Blocked slots -->\n <div\n v-for=\"(blocked, bIdx) in getBlockedForDay(day)\"\n :key=\"`b-${bIdx}`\"\n class=\"mint-schedule__blocked\"\n :style=\"getBlockedStyle(blocked)\"\n >\n <span v-if=\"blocked.label\" class=\"mint-schedule__blocked-label\">\n {{ blocked.label }}\n </span>\n </div>\n\n <!-- Now indicator -->\n <div\n v-if=\"showNowIndicator && isToday(day) && getNowIndicatorStyle()\"\n class=\"mint-schedule__now-indicator\"\n :style=\"getNowIndicatorStyle()!\"\n >\n <div class=\"mint-schedule__now-dot\" />\n </div>\n\n <!-- Drag ghost -->\n <div\n v-if=\"isDragging && ghost && ghost.dayIndex === dayIdx\"\n class=\"mint-schedule__ghost\"\n :style=\"ghost.style\"\n />\n </div>\n </div>\n </div>\n </template>\n\n <!-- Month View -->\n <template v-if=\"currentView === 'month'\">\n <div class=\"mint-schedule__month-grid\">\n <!-- Weekday headers -->\n <div\n v-for=\"label in monthWeekdayLabels\"\n :key=\"label\"\n class=\"mint-schedule__month-weekday\"\n >\n {{ label }}\n </div>\n\n <!-- Day cells -->\n <div\n v-for=\"(day, i) in monthDays\"\n :key=\"i\"\n :class=\"[\n 'mint-schedule__month-cell',\n isToday(day.date) ? 'mint-schedule__month-cell--today' : '',\n !day.isCurrentMonth ? 'mint-schedule__month-cell--outside' : '',\n ]\"\n @click=\"onMonthDayClick(day.date)\"\n >\n <slot name=\"month-day\" :date=\"day.date\" :isToday=\"isToday(day.date)\" :events=\"getEventsForDate(day.date)\">\n <div class=\"mint-schedule__month-date\">{{ day.date.getDate() }}</div>\n <div\n v-for=\"event in getEventsForDate(day.date).slice(0, 3)\"\n :key=\"event.id\"\n :class=\"['mint-schedule__month-event', getMonthStatusClass(event.status)]\"\n @click.stop=\"onEventClick(event, $event)\"\n >\n {{ event.title }}\n </div>\n <div\n v-if=\"getEventsForDate(day.date).length > 3\"\n class=\"mint-schedule__month-more\"\n >\n +{{ getEventsForDate(day.date).length - 3 }} more\n </div>\n </slot>\n </div>\n </div>\n </template>\n </div>\n</template>\n\n<style>\n@import '../styles/components/schedule-calendar.css';\n</style>\n","<script setup lang=\"ts\">\n/** Render generated SDK component bindings such as WellPlate, DoseCalculator, DataFrame, and PlateMapEditor. */\nimport { computed, type Component } from 'vue'\nimport DataFrame from './DataFrame.vue'\nimport DoseCalculator from './DoseCalculator.vue'\nimport ExperimentTimeline from './ExperimentTimeline.vue'\nimport PlateMapEditor from './PlateMapEditor.vue'\nimport ReagentList from './ReagentList.vue'\nimport SampleSelector from './SampleSelector.vue'\nimport ScheduleCalendar from './ScheduleCalendar.vue'\nimport WellPlate from './WellPlate.vue'\n\nexport interface ComponentBindingRendererBinding {\n id?: string\n component: string\n props?: Record<string, unknown> | readonly string[]\n propsObject?: Record<string, unknown>\n description?: string\n template_id?: string\n}\n\ntype ComponentBindingRendererLayout = 'grid' | 'stack'\n\ninterface Props {\n /** Single generated SDK component binding to render. */\n binding?: ComponentBindingRendererBinding\n /** Generated SDK component bindings to render. */\n bindings?: ComponentBindingRendererBinding[]\n /** Optional allow-list of component names, for example ['WellPlate', 'DataFrame']. */\n include?: string[]\n /** Optional deny-list of component names. */\n exclude?: string[]\n /** Compact child component sizing. */\n dense?: boolean\n /** Prefer preview-safe props for editable components. */\n readonly?: boolean\n /** Show component/template labels above each rendered component. */\n showHeaders?: boolean\n /** Show binding descriptions in each header. */\n showDescriptions?: boolean\n /** Grid or vertical stack layout. */\n layout?: ComponentBindingRendererLayout\n /** Message shown when no binding can be rendered. */\n emptyText?: string\n}\n\ntype RenderableBinding = ComponentBindingRendererBinding & {\n id: string\n componentImpl: Component\n normalizedProps: Record<string, unknown>\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n binding: undefined,\n bindings: () => [],\n include: () => [],\n exclude: () => [],\n dense: false,\n readonly: true,\n showHeaders: true,\n showDescriptions: true,\n layout: 'grid',\n emptyText: 'No renderable SDK component bindings.',\n})\n\nconst componentRegistry: Record<string, Component> = {\n DataFrame,\n DoseCalculator,\n ExperimentTimeline,\n PlateMapEditor,\n ReagentList,\n SampleSelector,\n ScheduleCalendar,\n WellPlate,\n}\n\nconst inputBindings = computed<ComponentBindingRendererBinding[]>(() => [\n ...(props.binding ? [props.binding] : []),\n ...props.bindings,\n])\n\nconst renderableBindings = computed<RenderableBinding[]>(() =>\n inputBindings.value\n .filter(binding => isIncluded(binding.component))\n .map((binding, index) => {\n const componentImpl = componentRegistry[binding.component]\n if (!componentImpl) return null\n return {\n ...binding,\n id: binding.id ?? `${binding.component}-${index + 1}`,\n componentImpl,\n normalizedProps: normalizeProps(binding),\n }\n })\n .filter((binding): binding is RenderableBinding => binding !== null)\n)\n\nconst rendererClasses = computed(() => [\n 'mint-component-binding-renderer',\n `mint-component-binding-renderer--${props.layout}`,\n props.dense ? 'mint-component-binding-renderer--dense' : '',\n])\n\nfunction isIncluded(componentName: string): boolean {\n if (props.include.length > 0 && !props.include.includes(componentName)) return false\n return !props.exclude.includes(componentName)\n}\n\nfunction baseProps(binding: ComponentBindingRendererBinding): Record<string, unknown> {\n if (binding.propsObject) return { ...binding.propsObject }\n if (isRecord(binding.props)) return { ...binding.props }\n return {}\n}\n\nfunction normalizeProps(binding: ComponentBindingRendererBinding): Record<string, unknown> {\n const base = baseProps(binding)\n\n switch (binding.component) {\n case 'WellPlate':\n return {\n ...base,\n readonly: props.readonly || Boolean(base.readonly),\n size: props.dense ? 'md' : (base.size ?? 'fill'),\n }\n case 'PlateMapEditor':\n return {\n ...base,\n size: props.dense ? 'md' : (base.size ?? 'fill'),\n showToolbar: props.readonly ? false : (base.showToolbar ?? true),\n showSidebar: props.readonly ? false : (base.showSidebar ?? true),\n allowAddPlates: props.readonly ? false : (base.allowAddPlates ?? true),\n allowAddSamples: props.readonly ? false : (base.allowAddSamples ?? true),\n }\n case 'DataFrame':\n return {\n ...base,\n size: props.dense ? 'sm' : (base.size ?? 'md'),\n maxHeight: base.maxHeight ?? (props.dense ? '280px' : undefined),\n }\n case 'ExperimentTimeline':\n return {\n ...base,\n editable: props.readonly ? false : base.editable,\n size: props.dense ? 'sm' : (base.size ?? 'md'),\n }\n case 'ReagentList':\n return {\n ...base,\n readonly: props.readonly || Boolean(base.readonly),\n }\n case 'SampleSelector':\n return {\n ...base,\n enableGrouping: props.readonly ? false : (base.enableGrouping ?? true),\n enableSmartGroup: props.readonly ? false : (base.enableSmartGroup ?? true),\n }\n case 'ScheduleCalendar':\n return {\n ...base,\n readonly: props.readonly || Boolean(base.readonly),\n showNavigation: props.dense ? false : (base.showNavigation ?? true),\n showViewToggle: props.dense ? false : (base.showViewToggle ?? true),\n }\n default:\n return base\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n</script>\n\n<template>\n <div :class=\"rendererClasses\">\n <section\n v-for=\"renderableBinding in renderableBindings\"\n :key=\"renderableBinding.id\"\n class=\"mint-component-binding-renderer__item\"\n :data-component-binding-id=\"renderableBinding.id\"\n :data-template-id=\"renderableBinding.template_id\"\n :data-template-component=\"renderableBinding.component\"\n >\n <header\n v-if=\"showHeaders\"\n class=\"mint-component-binding-renderer__header\"\n >\n <div class=\"mint-component-binding-renderer__title-group\">\n <p class=\"mint-component-binding-renderer__component\">\n {{ renderableBinding.component }}\n </p>\n <p\n v-if=\"renderableBinding.template_id || renderableBinding.id\"\n class=\"mint-component-binding-renderer__source\"\n >\n {{ renderableBinding.template_id ?? renderableBinding.id }}\n </p>\n </div>\n <p\n v-if=\"showDescriptions && renderableBinding.description\"\n class=\"mint-component-binding-renderer__description\"\n >\n {{ renderableBinding.description }}\n </p>\n </header>\n\n <div class=\"mint-component-binding-renderer__body\">\n <component\n :is=\"renderableBinding.componentImpl\"\n v-bind=\"renderableBinding.normalizedProps\"\n />\n </div>\n </section>\n\n <p\n v-if=\"renderableBindings.length === 0\"\n class=\"mint-component-binding-renderer__empty\"\n >\n {{ emptyText }}\n </p>\n </div>\n</template>\n\n<style scoped>\n.mint-component-binding-renderer {\n display: grid;\n gap: 1rem;\n min-width: 0;\n}\n\n.mint-component-binding-renderer--grid {\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 28rem), 1fr));\n}\n\n.mint-component-binding-renderer--stack {\n grid-template-columns: minmax(0, 1fr);\n}\n\n.mint-component-binding-renderer--dense {\n gap: 0.75rem;\n}\n\n.mint-component-binding-renderer__item {\n min-width: 0;\n overflow: hidden;\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 0.5rem;\n background: var(--bg-card, #ffffff);\n box-shadow: var(--shadow-sm, 0 1px 2px rgb(15 23 42 / 0.06));\n}\n\n.mint-component-binding-renderer__header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 1rem;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--border-color, #e5e7eb);\n background: var(--bg-secondary, #f8fafc);\n}\n\n.mint-component-binding-renderer__title-group {\n min-width: 0;\n}\n\n.mint-component-binding-renderer__component {\n margin: 0;\n color: var(--text-primary, #0f172a);\n font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace);\n font-size: 0.8125rem;\n font-weight: 600;\n}\n\n.mint-component-binding-renderer__source,\n.mint-component-binding-renderer__description,\n.mint-component-binding-renderer__empty {\n margin: 0;\n color: var(--text-secondary, #475569);\n font-size: 0.75rem;\n line-height: 1.4;\n}\n\n.mint-component-binding-renderer__description {\n max-width: 24rem;\n text-align: right;\n}\n\n.mint-component-binding-renderer__body {\n min-width: 0;\n padding: 1rem;\n}\n\n.mint-component-binding-renderer--dense .mint-component-binding-renderer__header,\n.mint-component-binding-renderer--dense .mint-component-binding-renderer__body {\n padding: 0.75rem;\n}\n\n.mint-component-binding-renderer__empty {\n padding: 1rem;\n border: 1px dashed var(--border-color, #e5e7eb);\n border-radius: 0.5rem;\n background: var(--bg-secondary, #f8fafc);\n text-align: center;\n}\n\n@media (max-width: 720px) {\n .mint-component-binding-renderer__header {\n display: block;\n }\n\n .mint-component-binding-renderer__description {\n max-width: none;\n margin-top: 0.25rem;\n text-align: left;\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/** Render generated SDK component bindings such as WellPlate, DoseCalculator, DataFrame, and PlateMapEditor. */\nimport { computed, type Component } from 'vue'\nimport DataFrame from './DataFrame.vue'\nimport DoseCalculator from './DoseCalculator.vue'\nimport ExperimentTimeline from './ExperimentTimeline.vue'\nimport PlateMapEditor from './PlateMapEditor.vue'\nimport ReagentList from './ReagentList.vue'\nimport SampleSelector from './SampleSelector.vue'\nimport ScheduleCalendar from './ScheduleCalendar.vue'\nimport WellPlate from './WellPlate.vue'\n\nexport interface ComponentBindingRendererBinding {\n id?: string\n component: string\n props?: Record<string, unknown> | readonly string[]\n propsObject?: Record<string, unknown>\n description?: string\n template_id?: string\n}\n\ntype ComponentBindingRendererLayout = 'grid' | 'stack'\n\ninterface Props {\n /** Single generated SDK component binding to render. */\n binding?: ComponentBindingRendererBinding\n /** Generated SDK component bindings to render. */\n bindings?: ComponentBindingRendererBinding[]\n /** Optional allow-list of component names, for example ['WellPlate', 'DataFrame']. */\n include?: string[]\n /** Optional deny-list of component names. */\n exclude?: string[]\n /** Compact child component sizing. */\n dense?: boolean\n /** Prefer preview-safe props for editable components. */\n readonly?: boolean\n /** Show component/template labels above each rendered component. */\n showHeaders?: boolean\n /** Show binding descriptions in each header. */\n showDescriptions?: boolean\n /** Grid or vertical stack layout. */\n layout?: ComponentBindingRendererLayout\n /** Message shown when no binding can be rendered. */\n emptyText?: string\n}\n\ntype RenderableBinding = ComponentBindingRendererBinding & {\n id: string\n componentImpl: Component\n normalizedProps: Record<string, unknown>\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n binding: undefined,\n bindings: () => [],\n include: () => [],\n exclude: () => [],\n dense: false,\n readonly: true,\n showHeaders: true,\n showDescriptions: true,\n layout: 'grid',\n emptyText: 'No renderable SDK component bindings.',\n})\n\nconst componentRegistry: Record<string, Component> = {\n DataFrame,\n DoseCalculator,\n ExperimentTimeline,\n PlateMapEditor,\n ReagentList,\n SampleSelector,\n ScheduleCalendar,\n WellPlate,\n}\n\nconst inputBindings = computed<ComponentBindingRendererBinding[]>(() => [\n ...(props.binding ? [props.binding] : []),\n ...props.bindings,\n])\n\nconst renderableBindings = computed<RenderableBinding[]>(() =>\n inputBindings.value\n .filter(binding => isIncluded(binding.component))\n .map((binding, index) => {\n const componentImpl = componentRegistry[binding.component]\n if (!componentImpl) return null\n return {\n ...binding,\n id: binding.id ?? `${binding.component}-${index + 1}`,\n componentImpl,\n normalizedProps: normalizeProps(binding),\n }\n })\n .filter((binding): binding is RenderableBinding => binding !== null)\n)\n\nconst rendererClasses = computed(() => [\n 'mint-component-binding-renderer',\n `mint-component-binding-renderer--${props.layout}`,\n props.dense ? 'mint-component-binding-renderer--dense' : '',\n])\n\nfunction isIncluded(componentName: string): boolean {\n if (props.include.length > 0 && !props.include.includes(componentName)) return false\n return !props.exclude.includes(componentName)\n}\n\nfunction baseProps(binding: ComponentBindingRendererBinding): Record<string, unknown> {\n if (binding.propsObject) return { ...binding.propsObject }\n if (isRecord(binding.props)) return { ...binding.props }\n return {}\n}\n\nfunction normalizeProps(binding: ComponentBindingRendererBinding): Record<string, unknown> {\n const base = baseProps(binding)\n\n switch (binding.component) {\n case 'WellPlate':\n return {\n ...base,\n readonly: props.readonly || Boolean(base.readonly),\n size: props.dense ? 'md' : (base.size ?? 'fill'),\n }\n case 'PlateMapEditor':\n return {\n ...base,\n size: props.dense ? 'md' : (base.size ?? 'fill'),\n showToolbar: props.readonly ? false : (base.showToolbar ?? true),\n showSidebar: props.readonly ? false : (base.showSidebar ?? true),\n allowAddPlates: props.readonly ? false : (base.allowAddPlates ?? true),\n allowAddSamples: props.readonly ? false : (base.allowAddSamples ?? true),\n }\n case 'DataFrame':\n return {\n ...base,\n size: props.dense ? 'sm' : (base.size ?? 'md'),\n maxHeight: base.maxHeight ?? (props.dense ? '280px' : undefined),\n }\n case 'ExperimentTimeline':\n return {\n ...base,\n editable: props.readonly ? false : base.editable,\n size: props.dense ? 'sm' : (base.size ?? 'md'),\n }\n case 'ReagentList':\n return {\n ...base,\n readonly: props.readonly || Boolean(base.readonly),\n }\n case 'SampleSelector':\n return {\n ...base,\n enableGrouping: props.readonly ? false : (base.enableGrouping ?? true),\n enableSmartGroup: props.readonly ? false : (base.enableSmartGroup ?? true),\n }\n case 'ScheduleCalendar':\n return {\n ...base,\n readonly: props.readonly || Boolean(base.readonly),\n showNavigation: props.dense ? false : (base.showNavigation ?? true),\n showViewToggle: props.dense ? false : (base.showViewToggle ?? true),\n }\n default:\n return base\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n</script>\n\n<template>\n <div :class=\"rendererClasses\">\n <section\n v-for=\"renderableBinding in renderableBindings\"\n :key=\"renderableBinding.id\"\n class=\"mint-component-binding-renderer__item\"\n :data-component-binding-id=\"renderableBinding.id\"\n :data-template-id=\"renderableBinding.template_id\"\n :data-template-component=\"renderableBinding.component\"\n >\n <header\n v-if=\"showHeaders\"\n class=\"mint-component-binding-renderer__header\"\n >\n <div class=\"mint-component-binding-renderer__title-group\">\n <p class=\"mint-component-binding-renderer__component\">\n {{ renderableBinding.component }}\n </p>\n <p\n v-if=\"renderableBinding.template_id || renderableBinding.id\"\n class=\"mint-component-binding-renderer__source\"\n >\n {{ renderableBinding.template_id ?? renderableBinding.id }}\n </p>\n </div>\n <p\n v-if=\"showDescriptions && renderableBinding.description\"\n class=\"mint-component-binding-renderer__description\"\n >\n {{ renderableBinding.description }}\n </p>\n </header>\n\n <div class=\"mint-component-binding-renderer__body\">\n <component\n :is=\"renderableBinding.componentImpl\"\n v-bind=\"renderableBinding.normalizedProps\"\n />\n </div>\n </section>\n\n <p\n v-if=\"renderableBindings.length === 0\"\n class=\"mint-component-binding-renderer__empty\"\n >\n {{ emptyText }}\n </p>\n </div>\n</template>\n\n<style scoped>\n.mint-component-binding-renderer {\n display: grid;\n gap: 1rem;\n min-width: 0;\n}\n\n.mint-component-binding-renderer--grid {\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 28rem), 1fr));\n}\n\n.mint-component-binding-renderer--stack {\n grid-template-columns: minmax(0, 1fr);\n}\n\n.mint-component-binding-renderer--dense {\n gap: 0.75rem;\n}\n\n.mint-component-binding-renderer__item {\n min-width: 0;\n overflow: hidden;\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 0.5rem;\n background: var(--bg-card, #ffffff);\n box-shadow: var(--shadow-sm, 0 1px 2px rgb(15 23 42 / 0.06));\n}\n\n.mint-component-binding-renderer__header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 1rem;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--border-color, #e5e7eb);\n background: var(--bg-secondary, #f8fafc);\n}\n\n.mint-component-binding-renderer__title-group {\n min-width: 0;\n}\n\n.mint-component-binding-renderer__component {\n margin: 0;\n color: var(--text-primary, #0f172a);\n font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace);\n font-size: 0.8125rem;\n font-weight: 600;\n}\n\n.mint-component-binding-renderer__source,\n.mint-component-binding-renderer__description,\n.mint-component-binding-renderer__empty {\n margin: 0;\n color: var(--text-secondary, #475569);\n font-size: 0.75rem;\n line-height: 1.4;\n}\n\n.mint-component-binding-renderer__description {\n max-width: 24rem;\n text-align: right;\n}\n\n.mint-component-binding-renderer__body {\n min-width: 0;\n padding: 1rem;\n}\n\n.mint-component-binding-renderer--dense .mint-component-binding-renderer__header,\n.mint-component-binding-renderer--dense .mint-component-binding-renderer__body {\n padding: 0.75rem;\n}\n\n.mint-component-binding-renderer__empty {\n padding: 1rem;\n border: 1px dashed var(--border-color, #e5e7eb);\n border-radius: 0.5rem;\n background: var(--bg-secondary, #f8fafc);\n text-align: center;\n}\n\n@media (max-width: 720px) {\n .mint-component-binding-renderer__header {\n display: block;\n }\n\n .mint-component-binding-renderer__description {\n max-width: none;\n margin-top: 0.25rem;\n text-align: left;\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/** Complete control workspace component that turns a simple controls data model into AppTopBar, AppSidebar, and FormBuilder forms. */\nimport { computed, effectScope, onScopeDispose, shallowRef, toRaw, watch, type EffectScope } from 'vue'\nimport type { TopBarVariant } from '../types'\nimport type { FormEnhancements } from '../types/form-builder'\nimport type {\n ControlComponentBindingsConfig,\n ControlComponentPropsByIdMap,\n ControlComponentPropsMap,\n ControlModel,\n ControlModelBinding,\n ControlSchema,\n ControlWorkspaceOptions,\n UseControlWorkspaceReturn,\n} from '../composables/useControlSchema'\nimport { defineControlModel, mergeControlWorkspaceOptions, useControlWorkspace } from '../composables/useControlSchema'\nimport AppLayout from './AppLayout.vue'\nimport AppSidebar from './AppSidebar.vue'\nimport AppTopBar from './AppTopBar.vue'\nimport FormBuilder from './FormBuilder.vue'\n\ntype ControlWorkspaceSidebarVariant = 'default' | 'analysis'\ntype ResolvedControlWorkspace = UseControlWorkspaceReturn<ControlSchema>\n\ninterface ControlWorkspaceDefaultSlotProps {\n workspace: ResolvedControlWorkspace\n bindings: ResolvedControlWorkspace['bindings']\n values: ResolvedControlWorkspace['values']\n componentBindings: ReturnType<ResolvedControlWorkspace['getComponentBindings']>\n componentBindingsById: ReturnType<ResolvedControlWorkspace['getComponentBindingsById']>\n componentProps: ReturnType<ResolvedControlWorkspace['getComponentProps']>\n componentPropsById: ReturnType<ResolvedControlWorkspace['getComponentPropsById']>\n}\n\ninterface ControlWorkspaceTopbarSlotProps {\n workspace: ResolvedControlWorkspace\n bindings: ResolvedControlWorkspace['bindings']\n topBar: ResolvedControlWorkspace['bindings']['topBar']['value']\n}\n\ninterface ControlWorkspaceSidebarSlotProps {\n workspace: ResolvedControlWorkspace\n bindings: ResolvedControlWorkspace['bindings']\n sidebar: ResolvedControlWorkspace['sidebar']\n}\n\ninterface Props {\n /** Model returned by defineControlModel()/defineDoseDesignControlModel(), or a raw nested ControlModel. */\n model?: ControlModel | ControlModelBinding\n /** Workspace returned by useControlWorkspace(). Use for full manual control. */\n workspace?: UseControlWorkspaceReturn<ControlSchema>\n /** Compact controls schema. When provided without workspace, the view creates the workspace internally. */\n controls?: ControlSchema\n /** Options passed to the internally generated useControlWorkspace() call. */\n controlOptions?: ControlWorkspaceOptions\n /** Initial values for the internally generated workspace. Merged over controlOptions.initialValues. */\n initialValues?: Record<string, unknown>\n /** External values for the internally generated workspace. Supports default v-model. */\n modelValue?: Record<string, unknown>\n /** External values for the internally generated workspace. Supports v-model:values. */\n values?: Record<string, unknown>\n /** AppTopBar title. */\n title?: string\n /** AppTopBar subtitle. */\n subtitle?: string\n /** AppTopBar visual variant. */\n topBarVariant?: TopBarVariant\n /** AppSidebar/AppLayout sidebar width. */\n sidebarWidth?: string\n /** AppSidebar visual preset. `analysis` matches the LEAF-style MINT analysis sidebar design language. */\n sidebarVariant?: ControlWorkspaceSidebarVariant\n /** Convert the sidebar into an SDK-owned mobile overlay below the AppLayout breakpoint. */\n responsiveSidebar?: boolean\n /** Sidebar position in AppLayout. */\n sidebarPosition?: 'left' | 'right'\n /** Optional AppSidebar chrome title for LEAF-style plugin workbenches. */\n sidebarTitle?: string\n /** Optional AppSidebar chrome subtitle for active experiment/run context. */\n sidebarSubtitle?: string\n /** Optional compact badge/count rendered in the AppSidebar chrome header. */\n sidebarBadge?: string | number\n /** Floating AppLayout style. */\n floating?: boolean\n /** Compact AppSidebar density. */\n dense?: boolean\n /** Whether AppTopBar should show generated settings. */\n showSettings?: boolean\n /** Render FormBuilder actions in the default content. */\n showFormActions?: boolean\n /** Runtime FormBuilder enhancements passed to generated forms. */\n formEnhancements?: FormEnhancements<Record<string, unknown>>\n /** Optional SDK component bindings exposed to the default slot with resolved props. */\n componentBindings?: ControlComponentBindingsConfig\n /** Optional mapping from workspace values to component props exposed to the default slot. */\n componentProps?: ControlComponentPropsMap\n /** Optional named mappings from workspace values to component props exposed to the default slot. */\n componentPropsById?: ControlComponentPropsByIdMap\n /** Loading/saving state passed to generated forms. */\n formLoading?: boolean\n /** Disabled state passed to generated forms. */\n formDisabled?: boolean\n /** Readonly state passed to generated forms. */\n formReadonly?: boolean\n /** FormBuilder size in the default content. */\n formSize?: 'sm' | 'md' | 'lg'\n}\n\nconst emit = defineEmits<{\n /** Emitted when generated workspace values change through FormBuilder, AppSidebar, or AppTopBar settings. */\n 'update:modelValue': [values: Record<string, unknown>]\n /** Emitted when generated workspace values change through FormBuilder, AppSidebar, or AppTopBar settings. */\n 'update:values': [values: Record<string, unknown>]\n /** Forwarded from the default FormBuilder when showFormActions is enabled. */\n submit: [values: Record<string, unknown>]\n /** Forwarded from the default FormBuilder when showFormActions is enabled. */\n cancel: []\n}>()\n\ndefineSlots<{\n default?: (props: ControlWorkspaceDefaultSlotProps) => unknown\n topbar?: (props: ControlWorkspaceTopbarSlotProps) => unknown\n sidebar?: (props: ControlWorkspaceSidebarSlotProps) => unknown\n}>()\n\nconst props = withDefaults(defineProps<Props>(), {\n model: undefined,\n workspace: undefined,\n controls: undefined,\n controlOptions: () => ({}),\n initialValues: undefined,\n modelValue: undefined,\n values: undefined,\n title: 'Workspace',\n subtitle: undefined,\n topBarVariant: 'card',\n sidebarWidth: '320px',\n sidebarVariant: 'analysis',\n responsiveSidebar: true,\n sidebarPosition: 'left',\n sidebarTitle: undefined,\n sidebarSubtitle: undefined,\n sidebarBadge: undefined,\n floating: false,\n dense: true,\n showSettings: true,\n showFormActions: false,\n formEnhancements: undefined,\n componentBindings: undefined,\n componentProps: undefined,\n componentPropsById: undefined,\n formLoading: false,\n formDisabled: false,\n formReadonly: false,\n formSize: 'md',\n})\n\nconst externalValues = computed(() => props.modelValue ?? props.values)\nconst resolvedModel = computed<ControlModelBinding | undefined>(() => {\n if (props.model === undefined) return undefined\n return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)\n})\nconst resolvedControls = computed<ControlSchema>(() => props.controls ?? resolvedModel.value?.controls ?? {})\nconst baseControlOptions = computed<ControlWorkspaceOptions>(() =>\n mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, props.controlOptions)\n)\n\nconst resolvedControlOptions = computed<ControlWorkspaceOptions>(() => {\n if (props.initialValues === undefined && externalValues.value === undefined) return baseControlOptions.value\n\n return {\n ...baseControlOptions.value,\n initialValues: {\n ...(baseControlOptions.value.initialValues ?? {}),\n ...(props.initialValues ?? {}),\n ...(externalValues.value ?? {}),\n },\n }\n})\n\nlet generatedWorkspaceScope: EffectScope | undefined\n\nfunction createGeneratedWorkspace(seedValues: Record<string, unknown> = {}) {\n generatedWorkspaceScope?.stop()\n generatedWorkspaceScope = effectScope()\n return generatedWorkspaceScope.run(() =>\n useControlWorkspace(resolvedControls.value, {\n ...resolvedControlOptions.value,\n initialValues: {\n ...seedValues,\n ...(resolvedControlOptions.value.initialValues ?? {}),\n },\n })\n )!\n}\n\nconst generatedWorkspace = shallowRef<UseControlWorkspaceReturn<ControlSchema>>(createGeneratedWorkspace())\nconst resolvedWorkspace = computed<UseControlWorkspaceReturn<ControlSchema>>(() => props.workspace ?? generatedWorkspace.value)\nconst resolvedComponentBindings = computed(() =>\n resolvedWorkspace.value.getComponentBindings(props.componentBindings ?? resolvedModel.value?.componentBindings)\n)\nconst resolvedComponentBindingsById = computed(() =>\n resolvedWorkspace.value.getComponentBindingsById(props.componentBindings ?? resolvedModel.value?.componentBindings)\n)\nconst resolvedComponentProps = computed(() =>\n resolvedWorkspace.value.getComponentProps(props.componentProps ?? resolvedModel.value?.componentProps)\n)\nconst resolvedComponentPropsById = computed(() =>\n resolvedWorkspace.value.getComponentPropsById(props.componentPropsById ?? resolvedModel.value?.componentPropsById)\n)\nconst resolvedBindings = computed<ResolvedControlWorkspace['bindings']>(() => ({\n ...resolvedWorkspace.value.bindings,\n componentBindings: resolvedComponentBindings,\n componentBindingsById: resolvedComponentBindingsById,\n componentProps: resolvedComponentProps,\n componentPropsById: resolvedComponentPropsById,\n}))\nconst resolvedTopBarSlot = computed(() => resolvedWorkspace.value.bindings.topBar.value)\n\nwatch(\n [() => props.model, () => props.controls, () => props.controlOptions, () => props.initialValues],\n () => {\n const previousWorkspace = generatedWorkspace.value\n const previousActiveView = previousWorkspace.activeView.value\n generatedWorkspace.value = createGeneratedWorkspace({ ...previousWorkspace.values })\n generatedWorkspace.value.setActiveView(previousActiveView)\n },\n { deep: true },\n)\n\nonScopeDispose(() => generatedWorkspaceScope?.stop())\n\nwatch(\n externalValues,\n (values) => {\n if (values === undefined || props.workspace !== undefined) return\n if (recordsEqual(generatedWorkspace.value.values, values)) return\n generatedWorkspace.value.setValues(values)\n },\n { deep: true },\n)\n\nwatch(\n () => generatedWorkspace.value.values,\n (values) => {\n if (props.workspace !== undefined) return\n const nextValues = { ...values }\n if (externalValues.value !== undefined && recordsEqual(externalValues.value, nextValues)) return\n emit('update:modelValue', nextValues)\n emit('update:values', nextValues)\n },\n { deep: true },\n)\n\nfunction recordsEqual(left: Record<string, unknown>, right: Record<string, unknown>): boolean {\n const leftKeys = Object.keys(left)\n const rightKeys = Object.keys(right)\n if (leftKeys.length !== rightKeys.length) return false\n return leftKeys.every(key => valuesEqual(left[key], right[key]))\n}\n\nfunction valuesEqual(left: unknown, right: unknown): boolean {\n const leftRaw = toRaw(left)\n const rightRaw = toRaw(right)\n\n if (Object.is(leftRaw, rightRaw)) return true\n\n if (Array.isArray(leftRaw) || Array.isArray(rightRaw)) {\n if (!Array.isArray(leftRaw) || !Array.isArray(rightRaw)) return false\n if (leftRaw.length !== rightRaw.length) return false\n return leftRaw.every((item, index) => valuesEqual(item, rightRaw[index]))\n }\n\n if (isPlainRecord(leftRaw) || isPlainRecord(rightRaw)) {\n if (!isPlainRecord(leftRaw) || !isPlainRecord(rightRaw)) return false\n return recordsEqual(leftRaw, rightRaw)\n }\n\n return false\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {\n return 'controlOptions' in model\n}\n</script>\n\n<template>\n <AppLayout\n class=\"mint-control-workspace-view\"\n :sidebar-position=\"sidebarPosition\"\n :sidebar-width=\"sidebarWidth\"\n :responsive-sidebar=\"responsiveSidebar\"\n :floating=\"floating\"\n >\n <template #topbar>\n <slot\n name=\"topbar\"\n :workspace=\"resolvedWorkspace\"\n :bindings=\"resolvedBindings\"\n :top-bar=\"resolvedTopBarSlot\"\n >\n <AppTopBar\n :title=\"title\"\n :subtitle=\"subtitle\"\n :variant=\"topBarVariant\"\n :pill-nav=\"resolvedWorkspace.pillNav.items\"\n :current-pill-id=\"resolvedWorkspace.pillNav.currentItemId\"\n :show-settings=\"showSettings\"\n :settings-config=\"resolvedWorkspace.topBarSettings.settingsConfig\"\n @pill-select=\"resolvedWorkspace.pillNav.onSelect\"\n @settings-values-change=\"resolvedWorkspace.topBarSettings.onSettingsValuesChange\"\n />\n </slot>\n </template>\n\n <template #sidebar>\n <slot\n name=\"sidebar\"\n :workspace=\"resolvedWorkspace\"\n :bindings=\"resolvedBindings\"\n :sidebar=\"resolvedWorkspace.sidebar\"\n >\n <AppSidebar\n v-bind=\"resolvedWorkspace.sidebar\"\n :title=\"sidebarTitle\"\n :subtitle=\"sidebarSubtitle\"\n :badge=\"sidebarBadge\"\n :variant=\"sidebarVariant\"\n :floating=\"false\"\n :dense=\"dense\"\n :width=\"sidebarWidth\"\n :form-enhancements=\"formEnhancements\"\n :form-loading=\"formLoading\"\n :form-disabled=\"formDisabled\"\n :form-readonly=\"formReadonly\"\n />\n </slot>\n </template>\n\n <main class=\"mint-control-workspace-view__content\">\n <slot\n :workspace=\"resolvedWorkspace\"\n :bindings=\"resolvedBindings\"\n :values=\"resolvedWorkspace.values\"\n :component-bindings=\"resolvedComponentBindings\"\n :component-bindings-by-id=\"resolvedComponentBindingsById\"\n :component-props=\"resolvedComponentProps\"\n :component-props-by-id=\"resolvedComponentPropsById\"\n >\n <FormBuilder\n v-bind=\"resolvedWorkspace.form\"\n :enhancements=\"formEnhancements\"\n :loading=\"formLoading\"\n :disabled=\"formDisabled\"\n :readonly=\"formReadonly\"\n :show-actions=\"showFormActions\"\n :size=\"formSize\"\n @submit=\"emit('submit', $event)\"\n @cancel=\"emit('cancel')\"\n />\n </slot>\n </main>\n </AppLayout>\n</template>\n\n<style scoped>\n.mint-control-workspace-view {\n min-width: 0;\n}\n\n.mint-control-workspace-view__content {\n min-width: 0;\n padding: 1rem;\n}\n</style>\n","<script setup lang=\"ts\">\n/** Complete control workspace component that turns a simple controls data model into AppTopBar, AppSidebar, and FormBuilder forms. */\nimport { computed, effectScope, onScopeDispose, shallowRef, toRaw, watch, type EffectScope } from 'vue'\nimport type { TopBarVariant } from '../types'\nimport type { FormEnhancements } from '../types/form-builder'\nimport type {\n ControlComponentBindingsConfig,\n ControlComponentPropsByIdMap,\n ControlComponentPropsMap,\n ControlModel,\n ControlModelBinding,\n ControlSchema,\n ControlWorkspaceOptions,\n UseControlWorkspaceReturn,\n} from '../composables/useControlSchema'\nimport { defineControlModel, mergeControlWorkspaceOptions, useControlWorkspace } from '../composables/useControlSchema'\nimport AppLayout from './AppLayout.vue'\nimport AppSidebar from './AppSidebar.vue'\nimport AppTopBar from './AppTopBar.vue'\nimport FormBuilder from './FormBuilder.vue'\n\ntype ControlWorkspaceSidebarVariant = 'default' | 'analysis'\ntype ResolvedControlWorkspace = UseControlWorkspaceReturn<ControlSchema>\n\ninterface ControlWorkspaceDefaultSlotProps {\n workspace: ResolvedControlWorkspace\n bindings: ResolvedControlWorkspace['bindings']\n values: ResolvedControlWorkspace['values']\n componentBindings: ReturnType<ResolvedControlWorkspace['getComponentBindings']>\n componentBindingsById: ReturnType<ResolvedControlWorkspace['getComponentBindingsById']>\n componentProps: ReturnType<ResolvedControlWorkspace['getComponentProps']>\n componentPropsById: ReturnType<ResolvedControlWorkspace['getComponentPropsById']>\n}\n\ninterface ControlWorkspaceTopbarSlotProps {\n workspace: ResolvedControlWorkspace\n bindings: ResolvedControlWorkspace['bindings']\n topBar: ResolvedControlWorkspace['bindings']['topBar']['value']\n}\n\ninterface ControlWorkspaceSidebarSlotProps {\n workspace: ResolvedControlWorkspace\n bindings: ResolvedControlWorkspace['bindings']\n sidebar: ResolvedControlWorkspace['sidebar']\n}\n\ninterface Props {\n /** Model returned by defineControlModel()/defineDoseDesignControlModel(), or a raw nested ControlModel. */\n model?: ControlModel | ControlModelBinding\n /** Workspace returned by useControlWorkspace(). Use for full manual control. */\n workspace?: UseControlWorkspaceReturn<ControlSchema>\n /** Compact controls schema. When provided without workspace, the view creates the workspace internally. */\n controls?: ControlSchema\n /** Options passed to the internally generated useControlWorkspace() call. */\n controlOptions?: ControlWorkspaceOptions\n /** Initial values for the internally generated workspace. Merged over controlOptions.initialValues. */\n initialValues?: Record<string, unknown>\n /** External values for the internally generated workspace. Supports default v-model. */\n modelValue?: Record<string, unknown>\n /** External values for the internally generated workspace. Supports v-model:values. */\n values?: Record<string, unknown>\n /** AppTopBar title. */\n title?: string\n /** AppTopBar subtitle. */\n subtitle?: string\n /** AppTopBar visual variant. */\n topBarVariant?: TopBarVariant\n /** AppSidebar/AppLayout sidebar width. */\n sidebarWidth?: string\n /** AppSidebar visual preset. `analysis` matches the LEAF-style MINT analysis sidebar design language. */\n sidebarVariant?: ControlWorkspaceSidebarVariant\n /** Convert the sidebar into an SDK-owned mobile overlay below the AppLayout breakpoint. */\n responsiveSidebar?: boolean\n /** Sidebar position in AppLayout. */\n sidebarPosition?: 'left' | 'right'\n /** Optional AppSidebar chrome title for LEAF-style plugin workbenches. */\n sidebarTitle?: string\n /** Optional AppSidebar chrome subtitle for active experiment/run context. */\n sidebarSubtitle?: string\n /** Optional compact badge/count rendered in the AppSidebar chrome header. */\n sidebarBadge?: string | number\n /** Floating AppLayout style. */\n floating?: boolean\n /** Compact AppSidebar density. */\n dense?: boolean\n /** Whether AppTopBar should show generated settings. */\n showSettings?: boolean\n /** Render FormBuilder actions in the default content. */\n showFormActions?: boolean\n /** Runtime FormBuilder enhancements passed to generated forms. */\n formEnhancements?: FormEnhancements<Record<string, unknown>>\n /** Optional SDK component bindings exposed to the default slot with resolved props. */\n componentBindings?: ControlComponentBindingsConfig\n /** Optional mapping from workspace values to component props exposed to the default slot. */\n componentProps?: ControlComponentPropsMap\n /** Optional named mappings from workspace values to component props exposed to the default slot. */\n componentPropsById?: ControlComponentPropsByIdMap\n /** Loading/saving state passed to generated forms. */\n formLoading?: boolean\n /** Disabled state passed to generated forms. */\n formDisabled?: boolean\n /** Readonly state passed to generated forms. */\n formReadonly?: boolean\n /** FormBuilder size in the default content. */\n formSize?: 'sm' | 'md' | 'lg'\n}\n\nconst emit = defineEmits<{\n /** Emitted when generated workspace values change through FormBuilder, AppSidebar, or AppTopBar settings. */\n 'update:modelValue': [values: Record<string, unknown>]\n /** Emitted when generated workspace values change through FormBuilder, AppSidebar, or AppTopBar settings. */\n 'update:values': [values: Record<string, unknown>]\n /** Forwarded from the default FormBuilder when showFormActions is enabled. */\n submit: [values: Record<string, unknown>]\n /** Forwarded from the default FormBuilder when showFormActions is enabled. */\n cancel: []\n}>()\n\ndefineSlots<{\n default?: (props: ControlWorkspaceDefaultSlotProps) => unknown\n topbar?: (props: ControlWorkspaceTopbarSlotProps) => unknown\n sidebar?: (props: ControlWorkspaceSidebarSlotProps) => unknown\n}>()\n\nconst props = withDefaults(defineProps<Props>(), {\n model: undefined,\n workspace: undefined,\n controls: undefined,\n controlOptions: () => ({}),\n initialValues: undefined,\n modelValue: undefined,\n values: undefined,\n title: 'Workspace',\n subtitle: undefined,\n topBarVariant: 'card',\n sidebarWidth: '320px',\n sidebarVariant: 'analysis',\n responsiveSidebar: true,\n sidebarPosition: 'left',\n sidebarTitle: undefined,\n sidebarSubtitle: undefined,\n sidebarBadge: undefined,\n floating: false,\n dense: true,\n showSettings: true,\n showFormActions: false,\n formEnhancements: undefined,\n componentBindings: undefined,\n componentProps: undefined,\n componentPropsById: undefined,\n formLoading: false,\n formDisabled: false,\n formReadonly: false,\n formSize: 'md',\n})\n\nconst externalValues = computed(() => props.modelValue ?? props.values)\nconst resolvedModel = computed<ControlModelBinding | undefined>(() => {\n if (props.model === undefined) return undefined\n return isControlModelBinding(props.model) ? props.model : defineControlModel(props.model)\n})\nconst resolvedControls = computed<ControlSchema>(() => props.controls ?? resolvedModel.value?.controls ?? {})\nconst baseControlOptions = computed<ControlWorkspaceOptions>(() =>\n mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, props.controlOptions)\n)\n\nconst resolvedControlOptions = computed<ControlWorkspaceOptions>(() => {\n if (props.initialValues === undefined && externalValues.value === undefined) return baseControlOptions.value\n\n return {\n ...baseControlOptions.value,\n initialValues: {\n ...(baseControlOptions.value.initialValues ?? {}),\n ...(props.initialValues ?? {}),\n ...(externalValues.value ?? {}),\n },\n }\n})\n\nlet generatedWorkspaceScope: EffectScope | undefined\n\nfunction createGeneratedWorkspace(seedValues: Record<string, unknown> = {}) {\n generatedWorkspaceScope?.stop()\n generatedWorkspaceScope = effectScope()\n return generatedWorkspaceScope.run(() =>\n useControlWorkspace(resolvedControls.value, {\n ...resolvedControlOptions.value,\n initialValues: {\n ...seedValues,\n ...(resolvedControlOptions.value.initialValues ?? {}),\n },\n })\n )!\n}\n\nconst generatedWorkspace = shallowRef<UseControlWorkspaceReturn<ControlSchema>>(createGeneratedWorkspace())\nconst resolvedWorkspace = computed<UseControlWorkspaceReturn<ControlSchema>>(() => props.workspace ?? generatedWorkspace.value)\nconst resolvedComponentBindings = computed(() =>\n resolvedWorkspace.value.getComponentBindings(props.componentBindings ?? resolvedModel.value?.componentBindings)\n)\nconst resolvedComponentBindingsById = computed(() =>\n resolvedWorkspace.value.getComponentBindingsById(props.componentBindings ?? resolvedModel.value?.componentBindings)\n)\nconst resolvedComponentProps = computed(() =>\n resolvedWorkspace.value.getComponentProps(props.componentProps ?? resolvedModel.value?.componentProps)\n)\nconst resolvedComponentPropsById = computed(() =>\n resolvedWorkspace.value.getComponentPropsById(props.componentPropsById ?? resolvedModel.value?.componentPropsById)\n)\nconst resolvedBindings = computed<ResolvedControlWorkspace['bindings']>(() => ({\n ...resolvedWorkspace.value.bindings,\n componentBindings: resolvedComponentBindings,\n componentBindingsById: resolvedComponentBindingsById,\n componentProps: resolvedComponentProps,\n componentPropsById: resolvedComponentPropsById,\n}))\nconst resolvedTopBarSlot = computed(() => resolvedWorkspace.value.bindings.topBar.value)\n\nwatch(\n [() => props.model, () => props.controls, () => props.controlOptions, () => props.initialValues],\n () => {\n const previousWorkspace = generatedWorkspace.value\n const previousActiveView = previousWorkspace.activeView.value\n generatedWorkspace.value = createGeneratedWorkspace({ ...previousWorkspace.values })\n generatedWorkspace.value.setActiveView(previousActiveView)\n },\n { deep: true },\n)\n\nonScopeDispose(() => generatedWorkspaceScope?.stop())\n\nwatch(\n externalValues,\n (values) => {\n if (values === undefined || props.workspace !== undefined) return\n if (recordsEqual(generatedWorkspace.value.values, values)) return\n generatedWorkspace.value.setValues(values)\n },\n { deep: true },\n)\n\nwatch(\n () => generatedWorkspace.value.values,\n (values) => {\n if (props.workspace !== undefined) return\n const nextValues = { ...values }\n if (externalValues.value !== undefined && recordsEqual(externalValues.value, nextValues)) return\n emit('update:modelValue', nextValues)\n emit('update:values', nextValues)\n },\n { deep: true },\n)\n\nfunction recordsEqual(left: Record<string, unknown>, right: Record<string, unknown>): boolean {\n const leftKeys = Object.keys(left)\n const rightKeys = Object.keys(right)\n if (leftKeys.length !== rightKeys.length) return false\n return leftKeys.every(key => valuesEqual(left[key], right[key]))\n}\n\nfunction valuesEqual(left: unknown, right: unknown): boolean {\n const leftRaw = toRaw(left)\n const rightRaw = toRaw(right)\n\n if (Object.is(leftRaw, rightRaw)) return true\n\n if (Array.isArray(leftRaw) || Array.isArray(rightRaw)) {\n if (!Array.isArray(leftRaw) || !Array.isArray(rightRaw)) return false\n if (leftRaw.length !== rightRaw.length) return false\n return leftRaw.every((item, index) => valuesEqual(item, rightRaw[index]))\n }\n\n if (isPlainRecord(leftRaw) || isPlainRecord(rightRaw)) {\n if (!isPlainRecord(leftRaw) || !isPlainRecord(rightRaw)) return false\n return recordsEqual(leftRaw, rightRaw)\n }\n\n return false\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isControlModelBinding(model: ControlModel | ControlModelBinding): model is ControlModelBinding {\n return 'controlOptions' in model\n}\n</script>\n\n<template>\n <AppLayout\n class=\"mint-control-workspace-view\"\n :sidebar-position=\"sidebarPosition\"\n :sidebar-width=\"sidebarWidth\"\n :responsive-sidebar=\"responsiveSidebar\"\n :floating=\"floating\"\n >\n <template #topbar>\n <slot\n name=\"topbar\"\n :workspace=\"resolvedWorkspace\"\n :bindings=\"resolvedBindings\"\n :top-bar=\"resolvedTopBarSlot\"\n >\n <AppTopBar\n :title=\"title\"\n :subtitle=\"subtitle\"\n :variant=\"topBarVariant\"\n :pill-nav=\"resolvedWorkspace.pillNav.items\"\n :current-pill-id=\"resolvedWorkspace.pillNav.currentItemId\"\n :show-settings=\"showSettings\"\n :settings-config=\"resolvedWorkspace.topBarSettings.settingsConfig\"\n @pill-select=\"resolvedWorkspace.pillNav.onSelect\"\n @settings-values-change=\"resolvedWorkspace.topBarSettings.onSettingsValuesChange\"\n />\n </slot>\n </template>\n\n <template #sidebar>\n <slot\n name=\"sidebar\"\n :workspace=\"resolvedWorkspace\"\n :bindings=\"resolvedBindings\"\n :sidebar=\"resolvedWorkspace.sidebar\"\n >\n <AppSidebar\n v-bind=\"resolvedWorkspace.sidebar\"\n :title=\"sidebarTitle\"\n :subtitle=\"sidebarSubtitle\"\n :badge=\"sidebarBadge\"\n :variant=\"sidebarVariant\"\n :floating=\"false\"\n :dense=\"dense\"\n :width=\"sidebarWidth\"\n :form-enhancements=\"formEnhancements\"\n :form-loading=\"formLoading\"\n :form-disabled=\"formDisabled\"\n :form-readonly=\"formReadonly\"\n />\n </slot>\n </template>\n\n <main class=\"mint-control-workspace-view__content\">\n <slot\n :workspace=\"resolvedWorkspace\"\n :bindings=\"resolvedBindings\"\n :values=\"resolvedWorkspace.values\"\n :component-bindings=\"resolvedComponentBindings\"\n :component-bindings-by-id=\"resolvedComponentBindingsById\"\n :component-props=\"resolvedComponentProps\"\n :component-props-by-id=\"resolvedComponentPropsById\"\n >\n <FormBuilder\n v-bind=\"resolvedWorkspace.form\"\n :enhancements=\"formEnhancements\"\n :loading=\"formLoading\"\n :disabled=\"formDisabled\"\n :readonly=\"formReadonly\"\n :show-actions=\"showFormActions\"\n :size=\"formSize\"\n @submit=\"emit('submit', $event)\"\n @cancel=\"emit('cancel')\"\n />\n </slot>\n </main>\n </AppLayout>\n</template>\n\n<style scoped>\n.mint-control-workspace-view {\n min-width: 0;\n}\n\n.mint-control-workspace-view__content {\n min-width: 0;\n padding: 1rem;\n}\n</style>\n","<script setup lang=\"ts\">\n/** Complete ControlWorkspaceView page shell for WellPlate + DoseCalculator dose design. */\nimport { computed } from 'vue'\nimport type { TopBarVariant } from '../types'\nimport type { FormEnhancements } from '../types/form-builder'\nimport type {\n ControlModel,\n ControlModelBinding,\n ControlSchema,\n ControlWorkspaceOptions,\n DoseDesignControlModelOptions,\n UseControlWorkspaceReturn,\n} from '../composables/useControlSchema'\nimport { defineDoseDesignControlModel } from '../composables/useControlSchema'\nimport ControlWorkspaceView from './ControlWorkspaceView.vue'\nimport DoseCalculator from './DoseCalculator.vue'\nimport WellPlate from './WellPlate.vue'\n\ntype DoseDesignSidebarVariant = 'default' | 'analysis'\ntype ComponentProps = Record<string, unknown>\ntype ComponentPropsById = Record<string, ComponentProps>\ntype ComponentBinding = { id: string, component: string, props: ComponentProps }\ntype ComponentBindingsById = Record<string, ComponentBinding>\ntype ResolvedControlWorkspace = UseControlWorkspaceReturn<ControlSchema>\n\ninterface DoseDesignWorkspaceSlotProps {\n workspace: ResolvedControlWorkspace\n bindings: ResolvedControlWorkspace['bindings']\n values: ResolvedControlWorkspace['values']\n componentBindings: ComponentBinding[]\n componentBindingsById: ComponentBindingsById\n componentPropsById: ComponentPropsById\n wellPlateProps: ComponentProps\n doseCalculatorProps: ComponentProps\n}\n\ninterface Props {\n /** Model returned by defineDoseDesignControlModel(), or a custom compatible ControlWorkspace model. */\n model?: ControlModel | ControlModelBinding\n /** Workspace returned by useControlWorkspace(). Use for full manual control. */\n workspace?: UseControlWorkspaceReturn<ControlSchema>\n /** Options used when this view creates the default dose-design model internally. */\n doseDesignOptions?: DoseDesignControlModelOptions\n /** Options passed to the internally generated ControlWorkspaceView workspace. */\n controlOptions?: ControlWorkspaceOptions\n /** Initial values for the internally generated workspace. */\n initialValues?: Record<string, unknown>\n /** External values for the internally generated workspace. Supports default v-model. */\n modelValue?: Record<string, unknown>\n /** External values for the internally generated workspace. Supports v-model:values. */\n values?: Record<string, unknown>\n /** Named component props id for the generated WellPlate binding. */\n plateId?: string\n /** Named component props id for the generated DoseCalculator binding. */\n doseId?: string\n /** Extra props merged into the generated WellPlate binding. */\n wellPlateProps?: ComponentProps\n /** Extra props merged into the generated DoseCalculator binding. */\n doseCalculatorProps?: ComponentProps\n /** AppTopBar title. */\n title?: string\n /** AppTopBar subtitle. */\n subtitle?: string\n /** AppTopBar visual variant. */\n topBarVariant?: TopBarVariant\n /** AppSidebar/AppLayout sidebar width. */\n sidebarWidth?: string\n /** AppSidebar visual preset. `analysis` matches the LEAF-style MINT analysis sidebar design language. */\n sidebarVariant?: DoseDesignSidebarVariant\n /** Convert the sidebar into an SDK-owned mobile overlay below the AppLayout breakpoint. */\n responsiveSidebar?: boolean\n /** Sidebar position in AppLayout. */\n sidebarPosition?: 'left' | 'right'\n /** Optional AppSidebar chrome title for LEAF-style plugin workbenches. */\n sidebarTitle?: string\n /** Optional AppSidebar chrome subtitle for active experiment/run context. */\n sidebarSubtitle?: string\n /** Optional compact badge/count rendered in the AppSidebar chrome header. */\n sidebarBadge?: string | number\n /** Floating AppLayout style. */\n floating?: boolean\n /** Compact AppSidebar density. */\n dense?: boolean\n /** Whether AppTopBar should show generated settings. */\n showSettings?: boolean\n /** Render FormBuilder actions in the default generated forms. */\n showFormActions?: boolean\n /** Runtime FormBuilder enhancements passed to generated forms. */\n formEnhancements?: FormEnhancements<Record<string, unknown>>\n /** Loading/saving state passed to generated forms. */\n formLoading?: boolean\n /** Disabled state passed to generated forms. */\n formDisabled?: boolean\n /** Readonly state passed to generated forms. */\n formReadonly?: boolean\n /** FormBuilder size in generated forms. */\n formSize?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n model: undefined,\n workspace: undefined,\n doseDesignOptions: () => ({}),\n controlOptions: () => ({}),\n initialValues: undefined,\n modelValue: undefined,\n values: undefined,\n plateId: undefined,\n doseId: undefined,\n wellPlateProps: () => ({}),\n doseCalculatorProps: () => ({}),\n title: 'Dose Design Workspace',\n subtitle: undefined,\n topBarVariant: 'card',\n sidebarWidth: '320px',\n sidebarVariant: 'analysis',\n responsiveSidebar: true,\n sidebarPosition: 'left',\n sidebarTitle: undefined,\n sidebarSubtitle: undefined,\n sidebarBadge: undefined,\n floating: false,\n dense: true,\n showSettings: true,\n showFormActions: false,\n formEnhancements: undefined,\n formLoading: false,\n formDisabled: false,\n formReadonly: false,\n formSize: 'md',\n})\n\nconst emit = defineEmits<{\n /** Emitted when generated workspace values change through WellPlate, FormBuilder, AppSidebar, or AppTopBar settings. */\n 'update:modelValue': [values: Record<string, unknown>]\n /** Emitted when generated workspace values change through WellPlate, FormBuilder, AppSidebar, or AppTopBar settings. */\n 'update:values': [values: Record<string, unknown>]\n /** Forwarded from the internal ControlWorkspaceView when form actions are enabled. */\n submit: [values: Record<string, unknown>]\n /** Forwarded from the internal ControlWorkspaceView when form actions are enabled. */\n cancel: []\n}>()\n\ndefineSlots<{\n default?: (props: DoseDesignWorkspaceSlotProps) => unknown\n plate?: (props: DoseDesignWorkspaceSlotProps) => unknown\n dose?: (props: DoseDesignWorkspaceSlotProps) => unknown\n}>()\n\nconst resolvedModel = computed<ControlModel | ControlModelBinding>(() =>\n props.model ?? defineDoseDesignControlModel(props.doseDesignOptions)\n)\n\nfunction resolvedPlateId(): string {\n return props.plateId ?? props.doseDesignOptions.componentProps?.plateId ?? 'plate'\n}\n\nfunction resolvedDoseId(): string {\n return props.doseId ?? props.doseDesignOptions.componentProps?.doseId ?? 'dose'\n}\n\nfunction wellPlateBinding(componentPropsById: ComponentPropsById): ComponentProps {\n return {\n size: 'fill',\n selectionMode: 'multiple',\n ...(componentPropsById[resolvedPlateId()] ?? {}),\n ...props.wellPlateProps,\n }\n}\n\nfunction doseCalculatorBinding(componentPropsById: ComponentPropsById): ComponentProps {\n return {\n ...(componentPropsById[resolvedDoseId()] ?? {}),\n ...props.doseCalculatorProps,\n }\n}\n\nfunction slotProps(slotProps: {\n workspace: ResolvedControlWorkspace\n bindings: ResolvedControlWorkspace['bindings']\n values: ResolvedControlWorkspace['values']\n componentBindings: ComponentBinding[]\n componentBindingsById: ComponentBindingsById\n componentPropsById: ComponentPropsById\n}): DoseDesignWorkspaceSlotProps {\n return {\n ...slotProps,\n wellPlateProps: wellPlateBinding(slotProps.componentPropsById),\n doseCalculatorProps: doseCalculatorBinding(slotProps.componentPropsById),\n }\n}\n</script>\n\n<template>\n <ControlWorkspaceView\n :model=\"resolvedModel\"\n :workspace=\"workspace\"\n :control-options=\"controlOptions\"\n :initial-values=\"initialValues\"\n :model-value=\"modelValue\"\n :values=\"values\"\n :title=\"title\"\n :subtitle=\"subtitle\"\n :top-bar-variant=\"topBarVariant\"\n :sidebar-width=\"sidebarWidth\"\n :sidebar-variant=\"sidebarVariant\"\n :responsive-sidebar=\"responsiveSidebar\"\n :sidebar-position=\"sidebarPosition\"\n :sidebar-title=\"sidebarTitle\"\n :sidebar-subtitle=\"sidebarSubtitle\"\n :sidebar-badge=\"sidebarBadge\"\n :floating=\"floating\"\n :dense=\"dense\"\n :show-settings=\"showSettings\"\n :show-form-actions=\"showFormActions\"\n :form-enhancements=\"formEnhancements\"\n :form-loading=\"formLoading\"\n :form-disabled=\"formDisabled\"\n :form-readonly=\"formReadonly\"\n :form-size=\"formSize\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @update:values=\"emit('update:values', $event)\"\n @submit=\"emit('submit', $event)\"\n @cancel=\"emit('cancel')\"\n >\n <template #default=\"workspaceSlotProps\">\n <slot v-bind=\"slotProps(workspaceSlotProps)\">\n <div class=\"mint-dose-design-workspace\">\n <slot name=\"plate\" v-bind=\"slotProps(workspaceSlotProps)\">\n <WellPlate v-bind=\"wellPlateBinding(workspaceSlotProps.componentPropsById)\" />\n </slot>\n <slot name=\"dose\" v-bind=\"slotProps(workspaceSlotProps)\">\n <DoseCalculator v-bind=\"doseCalculatorBinding(workspaceSlotProps.componentPropsById)\" />\n </slot>\n </div>\n </slot>\n </template>\n </ControlWorkspaceView>\n</template>\n\n<style scoped>\n.mint-dose-design-workspace {\n display: grid;\n grid-template-columns: minmax(360px, 1fr) minmax(300px, 420px);\n gap: 1rem;\n align-items: start;\n min-width: 0;\n}\n\n@media (max-width: 900px) {\n .mint-dose-design-workspace {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/** Complete ControlWorkspaceView page shell for WellPlate + DoseCalculator dose design. */\nimport { computed } from 'vue'\nimport type { TopBarVariant } from '../types'\nimport type { FormEnhancements } from '../types/form-builder'\nimport type {\n ControlModel,\n ControlModelBinding,\n ControlSchema,\n ControlWorkspaceOptions,\n DoseDesignControlModelOptions,\n UseControlWorkspaceReturn,\n} from '../composables/useControlSchema'\nimport { defineDoseDesignControlModel } from '../composables/useControlSchema'\nimport ControlWorkspaceView from './ControlWorkspaceView.vue'\nimport DoseCalculator from './DoseCalculator.vue'\nimport WellPlate from './WellPlate.vue'\n\ntype DoseDesignSidebarVariant = 'default' | 'analysis'\ntype ComponentProps = Record<string, unknown>\ntype ComponentPropsById = Record<string, ComponentProps>\ntype ComponentBinding = { id: string, component: string, props: ComponentProps }\ntype ComponentBindingsById = Record<string, ComponentBinding>\ntype ResolvedControlWorkspace = UseControlWorkspaceReturn<ControlSchema>\n\ninterface DoseDesignWorkspaceSlotProps {\n workspace: ResolvedControlWorkspace\n bindings: ResolvedControlWorkspace['bindings']\n values: ResolvedControlWorkspace['values']\n componentBindings: ComponentBinding[]\n componentBindingsById: ComponentBindingsById\n componentPropsById: ComponentPropsById\n wellPlateProps: ComponentProps\n doseCalculatorProps: ComponentProps\n}\n\ninterface Props {\n /** Model returned by defineDoseDesignControlModel(), or a custom compatible ControlWorkspace model. */\n model?: ControlModel | ControlModelBinding\n /** Workspace returned by useControlWorkspace(). Use for full manual control. */\n workspace?: UseControlWorkspaceReturn<ControlSchema>\n /** Options used when this view creates the default dose-design model internally. */\n doseDesignOptions?: DoseDesignControlModelOptions\n /** Options passed to the internally generated ControlWorkspaceView workspace. */\n controlOptions?: ControlWorkspaceOptions\n /** Initial values for the internally generated workspace. */\n initialValues?: Record<string, unknown>\n /** External values for the internally generated workspace. Supports default v-model. */\n modelValue?: Record<string, unknown>\n /** External values for the internally generated workspace. Supports v-model:values. */\n values?: Record<string, unknown>\n /** Named component props id for the generated WellPlate binding. */\n plateId?: string\n /** Named component props id for the generated DoseCalculator binding. */\n doseId?: string\n /** Extra props merged into the generated WellPlate binding. */\n wellPlateProps?: ComponentProps\n /** Extra props merged into the generated DoseCalculator binding. */\n doseCalculatorProps?: ComponentProps\n /** AppTopBar title. */\n title?: string\n /** AppTopBar subtitle. */\n subtitle?: string\n /** AppTopBar visual variant. */\n topBarVariant?: TopBarVariant\n /** AppSidebar/AppLayout sidebar width. */\n sidebarWidth?: string\n /** AppSidebar visual preset. `analysis` matches the LEAF-style MINT analysis sidebar design language. */\n sidebarVariant?: DoseDesignSidebarVariant\n /** Convert the sidebar into an SDK-owned mobile overlay below the AppLayout breakpoint. */\n responsiveSidebar?: boolean\n /** Sidebar position in AppLayout. */\n sidebarPosition?: 'left' | 'right'\n /** Optional AppSidebar chrome title for LEAF-style plugin workbenches. */\n sidebarTitle?: string\n /** Optional AppSidebar chrome subtitle for active experiment/run context. */\n sidebarSubtitle?: string\n /** Optional compact badge/count rendered in the AppSidebar chrome header. */\n sidebarBadge?: string | number\n /** Floating AppLayout style. */\n floating?: boolean\n /** Compact AppSidebar density. */\n dense?: boolean\n /** Whether AppTopBar should show generated settings. */\n showSettings?: boolean\n /** Render FormBuilder actions in the default generated forms. */\n showFormActions?: boolean\n /** Runtime FormBuilder enhancements passed to generated forms. */\n formEnhancements?: FormEnhancements<Record<string, unknown>>\n /** Loading/saving state passed to generated forms. */\n formLoading?: boolean\n /** Disabled state passed to generated forms. */\n formDisabled?: boolean\n /** Readonly state passed to generated forms. */\n formReadonly?: boolean\n /** FormBuilder size in generated forms. */\n formSize?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n model: undefined,\n workspace: undefined,\n doseDesignOptions: () => ({}),\n controlOptions: () => ({}),\n initialValues: undefined,\n modelValue: undefined,\n values: undefined,\n plateId: undefined,\n doseId: undefined,\n wellPlateProps: () => ({}),\n doseCalculatorProps: () => ({}),\n title: 'Dose Design Workspace',\n subtitle: undefined,\n topBarVariant: 'card',\n sidebarWidth: '320px',\n sidebarVariant: 'analysis',\n responsiveSidebar: true,\n sidebarPosition: 'left',\n sidebarTitle: undefined,\n sidebarSubtitle: undefined,\n sidebarBadge: undefined,\n floating: false,\n dense: true,\n showSettings: true,\n showFormActions: false,\n formEnhancements: undefined,\n formLoading: false,\n formDisabled: false,\n formReadonly: false,\n formSize: 'md',\n})\n\nconst emit = defineEmits<{\n /** Emitted when generated workspace values change through WellPlate, FormBuilder, AppSidebar, or AppTopBar settings. */\n 'update:modelValue': [values: Record<string, unknown>]\n /** Emitted when generated workspace values change through WellPlate, FormBuilder, AppSidebar, or AppTopBar settings. */\n 'update:values': [values: Record<string, unknown>]\n /** Forwarded from the internal ControlWorkspaceView when form actions are enabled. */\n submit: [values: Record<string, unknown>]\n /** Forwarded from the internal ControlWorkspaceView when form actions are enabled. */\n cancel: []\n}>()\n\ndefineSlots<{\n default?: (props: DoseDesignWorkspaceSlotProps) => unknown\n plate?: (props: DoseDesignWorkspaceSlotProps) => unknown\n dose?: (props: DoseDesignWorkspaceSlotProps) => unknown\n}>()\n\nconst resolvedModel = computed<ControlModel | ControlModelBinding>(() =>\n props.model ?? defineDoseDesignControlModel(props.doseDesignOptions)\n)\n\nfunction resolvedPlateId(): string {\n return props.plateId ?? props.doseDesignOptions.componentProps?.plateId ?? 'plate'\n}\n\nfunction resolvedDoseId(): string {\n return props.doseId ?? props.doseDesignOptions.componentProps?.doseId ?? 'dose'\n}\n\nfunction wellPlateBinding(componentPropsById: ComponentPropsById): ComponentProps {\n return {\n size: 'fill',\n selectionMode: 'multiple',\n ...(componentPropsById[resolvedPlateId()] ?? {}),\n ...props.wellPlateProps,\n }\n}\n\nfunction doseCalculatorBinding(componentPropsById: ComponentPropsById): ComponentProps {\n return {\n ...(componentPropsById[resolvedDoseId()] ?? {}),\n ...props.doseCalculatorProps,\n }\n}\n\nfunction slotProps(slotProps: {\n workspace: ResolvedControlWorkspace\n bindings: ResolvedControlWorkspace['bindings']\n values: ResolvedControlWorkspace['values']\n componentBindings: ComponentBinding[]\n componentBindingsById: ComponentBindingsById\n componentPropsById: ComponentPropsById\n}): DoseDesignWorkspaceSlotProps {\n return {\n ...slotProps,\n wellPlateProps: wellPlateBinding(slotProps.componentPropsById),\n doseCalculatorProps: doseCalculatorBinding(slotProps.componentPropsById),\n }\n}\n</script>\n\n<template>\n <ControlWorkspaceView\n :model=\"resolvedModel\"\n :workspace=\"workspace\"\n :control-options=\"controlOptions\"\n :initial-values=\"initialValues\"\n :model-value=\"modelValue\"\n :values=\"values\"\n :title=\"title\"\n :subtitle=\"subtitle\"\n :top-bar-variant=\"topBarVariant\"\n :sidebar-width=\"sidebarWidth\"\n :sidebar-variant=\"sidebarVariant\"\n :responsive-sidebar=\"responsiveSidebar\"\n :sidebar-position=\"sidebarPosition\"\n :sidebar-title=\"sidebarTitle\"\n :sidebar-subtitle=\"sidebarSubtitle\"\n :sidebar-badge=\"sidebarBadge\"\n :floating=\"floating\"\n :dense=\"dense\"\n :show-settings=\"showSettings\"\n :show-form-actions=\"showFormActions\"\n :form-enhancements=\"formEnhancements\"\n :form-loading=\"formLoading\"\n :form-disabled=\"formDisabled\"\n :form-readonly=\"formReadonly\"\n :form-size=\"formSize\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @update:values=\"emit('update:values', $event)\"\n @submit=\"emit('submit', $event)\"\n @cancel=\"emit('cancel')\"\n >\n <template #default=\"workspaceSlotProps\">\n <slot v-bind=\"slotProps(workspaceSlotProps)\">\n <div class=\"mint-dose-design-workspace\">\n <slot name=\"plate\" v-bind=\"slotProps(workspaceSlotProps)\">\n <WellPlate v-bind=\"wellPlateBinding(workspaceSlotProps.componentPropsById)\" />\n </slot>\n <slot name=\"dose\" v-bind=\"slotProps(workspaceSlotProps)\">\n <DoseCalculator v-bind=\"doseCalculatorBinding(workspaceSlotProps.componentPropsById)\" />\n </slot>\n </div>\n </slot>\n </template>\n </ControlWorkspaceView>\n</template>\n\n<style scoped>\n.mint-dose-design-workspace {\n display: grid;\n grid-template-columns: minmax(360px, 1fr) minmax(300px, 420px);\n gap: 1rem;\n align-items: start;\n min-width: 0;\n}\n\n@media (max-width: 900px) {\n .mint-dose-design-workspace {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/** Content card with optional scroll lock, or a transparent flex layout shell with configurable direction and gap. */\nimport { computed } from 'vue'\n\ninterface Props {\n /** Makes the container scrollable (overflow-y: auto). Ignored when direction is set. */\n scrollable?: boolean\n /** Renders as a transparent flex container instead of a card. */\n direction?: 'row' | 'column'\n /** Flex gap between children. Only applies when direction is set. */\n gap?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n scrollable: false,\n gap: '1rem',\n})\n\nconst isLayout = computed(() => !!props.direction)\n</script>\n\n<template>\n <div\n class=\"mint-container\"\n :class=\"\n isLayout\n ? ['mint-container--layout', direction === 'row' ? 'mint-container--row' : 'mint-container--column']\n : ['mint-container--card', scrollable ? 'mint-container--scrollable' : '']\n \"\n :style=\"isLayout ? { gap } : undefined\"\n >\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Content card with optional scroll lock, or a transparent flex layout shell with configurable direction and gap. */\nimport { computed } from 'vue'\n\ninterface Props {\n /** Makes the container scrollable (overflow-y: auto). Ignored when direction is set. */\n scrollable?: boolean\n /** Renders as a transparent flex container instead of a card. */\n direction?: 'row' | 'column'\n /** Flex gap between children. Only applies when direction is set. */\n gap?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n scrollable: false,\n gap: '1rem',\n})\n\nconst isLayout = computed(() => !!props.direction)\n</script>\n\n<template>\n <div\n class=\"mint-container\"\n :class=\"\n isLayout\n ? ['mint-container--layout', direction === 'row' ? 'mint-container--row' : 'mint-container--column']\n : ['mint-container--card', scrollable ? 'mint-container--scrollable' : '']\n \"\n :style=\"isLayout ? { gap } : undefined\"\n >\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Horizontal or vertical rule with optional inline text label at center or left. */\ninterface Props {\n vertical?: boolean\n label?: string\n align?: 'center' | 'left'\n spacing?: 'sm' | 'md' | 'lg'\n}\n\nwithDefaults(defineProps<Props>(), {\n vertical: false,\n label: undefined,\n align: 'center',\n spacing: 'md',\n})\n</script>\n\n<template>\n <div\n v-if=\"vertical\"\n :class=\"[\n 'mint-divider',\n 'mint-divider--vertical',\n `mint-divider--${spacing}`,\n ]\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n />\n <div\n v-else\n :class=\"[\n 'mint-divider',\n `mint-divider--${spacing}`,\n label ? `mint-divider--labeled mint-divider--${align}` : '',\n ]\"\n role=\"separator\"\n aria-orientation=\"horizontal\"\n >\n <template v-if=\"label\">\n <span v-if=\"align === 'center'\" class=\"mint-divider__line\" />\n <span class=\"mint-divider__label\">{{ label }}</span>\n <span class=\"mint-divider__line\" />\n </template>\n </div>\n</template>\n\n<style>\n@import '../styles/components/divider.css';\n</style>\n","<script setup lang=\"ts\">\n/** Horizontal or vertical rule with optional inline text label at center or left. */\ninterface Props {\n vertical?: boolean\n label?: string\n align?: 'center' | 'left'\n spacing?: 'sm' | 'md' | 'lg'\n}\n\nwithDefaults(defineProps<Props>(), {\n vertical: false,\n label: undefined,\n align: 'center',\n spacing: 'md',\n})\n</script>\n\n<template>\n <div\n v-if=\"vertical\"\n :class=\"[\n 'mint-divider',\n 'mint-divider--vertical',\n `mint-divider--${spacing}`,\n ]\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n />\n <div\n v-else\n :class=\"[\n 'mint-divider',\n `mint-divider--${spacing}`,\n label ? `mint-divider--labeled mint-divider--${align}` : '',\n ]\"\n role=\"separator\"\n aria-orientation=\"horizontal\"\n >\n <template v-if=\"label\">\n <span v-if=\"align === 'center'\" class=\"mint-divider__line\" />\n <span class=\"mint-divider__label\">{{ label }}</span>\n <span class=\"mint-divider__line\" />\n </template>\n </div>\n</template>\n\n<style>\n@import '../styles/components/divider.css';\n</style>\n","<script setup lang=\"ts\">\n/** Colored dot with optional label and pulse animation for live-status indication. */\nimport { computed } from 'vue'\n\ninterface Props {\n status?: 'success' | 'warning' | 'error' | 'info' | 'muted'\n label?: string\n pulse?: boolean\n color?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n status: 'muted',\n label: undefined,\n pulse: false,\n color: undefined,\n})\n\nconst dotStyle = computed(() =>\n props.color ? { backgroundColor: props.color, color: props.color } : {}\n)\n</script>\n\n<template>\n <span class=\"mint-status\">\n <span\n :class=\"[\n 'mint-status__dot',\n { [`mint-status__dot--${status}`]: !color },\n { 'mint-status__dot--pulse': pulse },\n ]\"\n :style=\"dotStyle\"\n />\n <span v-if=\"label\" class=\"mint-status__label\">{{ label }}</span>\n </span>\n</template>\n\n<style>\n@import '../styles/components/status-indicator.css';\n</style>\n","<script setup lang=\"ts\">\n/** Colored dot with optional label and pulse animation for live-status indication. */\nimport { computed } from 'vue'\n\ninterface Props {\n status?: 'success' | 'warning' | 'error' | 'info' | 'muted'\n label?: string\n pulse?: boolean\n color?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n status: 'muted',\n label: undefined,\n pulse: false,\n color: undefined,\n})\n\nconst dotStyle = computed(() =>\n props.color ? { backgroundColor: props.color, color: props.color } : {}\n)\n</script>\n\n<template>\n <span class=\"mint-status\">\n <span\n :class=\"[\n 'mint-status__dot',\n { [`mint-status__dot--${status}`]: !color },\n { 'mint-status__dot--pulse': pulse },\n ]\"\n :style=\"dotStyle\"\n />\n <span v-if=\"label\" class=\"mint-status__label\">{{ label }}</span>\n </span>\n</template>\n\n<style>\n@import '../styles/components/status-indicator.css';\n</style>\n","<script setup lang=\"ts\">\n/** Displays progress as a filled rail or discrete labeled segments, with indeterminate mode. */\nimport { computed } from 'vue'\n\ninterface Props {\n value?: number\n variant?: 'rail' | 'segmented'\n color?: 'primary' | 'success' | 'warning' | 'error' | 'info'\n size?: 'sm' | 'md' | 'lg'\n label?: string\n showValue?: boolean\n indeterminate?: boolean\n steps?: string[]\n currentStep?: number\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n value: 0,\n variant: 'rail',\n color: 'primary',\n size: 'md',\n label: undefined,\n showValue: false,\n indeterminate: false,\n steps: () => [],\n currentStep: 0,\n})\n\nconst clampedValue = computed(() => Math.min(100, Math.max(0, props.value)))\n\nconst barStyle = computed(() => {\n if (props.indeterminate) return undefined\n return { width: `${clampedValue.value}%` }\n})\n\nconst segmentCount = computed(() => props.steps.length || 0)\n</script>\n\n<template>\n <div class=\"mint-progress\" :class=\"`mint-progress--${variant}`\">\n <div\n v-if=\"label || showValue\"\n class=\"mint-progress__header\"\n >\n <span v-if=\"label\" class=\"mint-progress__label\">{{ label }}</span>\n <span v-if=\"showValue && !indeterminate && variant === 'rail'\" class=\"mint-progress__value\">{{ clampedValue }}%</span>\n <span v-if=\"showValue && variant === 'segmented' && segmentCount\" class=\"mint-progress__value\">{{ currentStep }} / {{ segmentCount }}</span>\n </div>\n\n <!-- Rail variant: single filled track -->\n <div\n v-if=\"variant === 'rail'\"\n :class=\"[\n 'mint-progress__track',\n `mint-progress__track--${size}`,\n ]\"\n role=\"progressbar\"\n :aria-valuenow=\"indeterminate ? undefined : clampedValue\"\n :aria-valuemin=\"indeterminate ? undefined : 0\"\n :aria-valuemax=\"indeterminate ? undefined : 100\"\n >\n <div\n :class=\"[\n 'mint-progress__bar',\n `mint-progress__bar--${color}`,\n indeterminate ? 'mint-progress__bar--indeterminate' : '',\n ]\"\n :style=\"barStyle\"\n />\n </div>\n\n <!-- Segmented variant: discrete step segments -->\n <div\n v-else\n :class=\"['mint-progress__segments', `mint-progress__segments--${size}`]\"\n role=\"progressbar\"\n :aria-valuenow=\"currentStep\"\n :aria-valuemin=\"0\"\n :aria-valuemax=\"segmentCount\"\n >\n <span\n v-for=\"(_step, i) in steps\"\n :key=\"i\"\n :class=\"[\n 'mint-progress__segment',\n i < currentStep ? `mint-progress__segment--done mint-progress__segment--${color}` : '',\n i === currentStep ? `mint-progress__segment--active mint-progress__segment--${color}` : '',\n ]\"\n />\n </div>\n\n <!-- Segmented step labels -->\n <div\n v-if=\"variant === 'segmented' && steps.length\"\n class=\"mint-progress__step-labels\"\n >\n <span\n v-for=\"(step, i) in steps\"\n :key=\"i\"\n :class=\"[\n 'mint-progress__step-label',\n i === currentStep ? 'mint-progress__step-label--active' : '',\n i < currentStep ? 'mint-progress__step-label--done' : '',\n ]\"\n >\n {{ step }}\n </span>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/progress-bar.css';\n</style>\n","<script setup lang=\"ts\">\n/** Displays progress as a filled rail or discrete labeled segments, with indeterminate mode. */\nimport { computed } from 'vue'\n\ninterface Props {\n value?: number\n variant?: 'rail' | 'segmented'\n color?: 'primary' | 'success' | 'warning' | 'error' | 'info'\n size?: 'sm' | 'md' | 'lg'\n label?: string\n showValue?: boolean\n indeterminate?: boolean\n steps?: string[]\n currentStep?: number\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n value: 0,\n variant: 'rail',\n color: 'primary',\n size: 'md',\n label: undefined,\n showValue: false,\n indeterminate: false,\n steps: () => [],\n currentStep: 0,\n})\n\nconst clampedValue = computed(() => Math.min(100, Math.max(0, props.value)))\n\nconst barStyle = computed(() => {\n if (props.indeterminate) return undefined\n return { width: `${clampedValue.value}%` }\n})\n\nconst segmentCount = computed(() => props.steps.length || 0)\n</script>\n\n<template>\n <div class=\"mint-progress\" :class=\"`mint-progress--${variant}`\">\n <div\n v-if=\"label || showValue\"\n class=\"mint-progress__header\"\n >\n <span v-if=\"label\" class=\"mint-progress__label\">{{ label }}</span>\n <span v-if=\"showValue && !indeterminate && variant === 'rail'\" class=\"mint-progress__value\">{{ clampedValue }}%</span>\n <span v-if=\"showValue && variant === 'segmented' && segmentCount\" class=\"mint-progress__value\">{{ currentStep }} / {{ segmentCount }}</span>\n </div>\n\n <!-- Rail variant: single filled track -->\n <div\n v-if=\"variant === 'rail'\"\n :class=\"[\n 'mint-progress__track',\n `mint-progress__track--${size}`,\n ]\"\n role=\"progressbar\"\n :aria-valuenow=\"indeterminate ? undefined : clampedValue\"\n :aria-valuemin=\"indeterminate ? undefined : 0\"\n :aria-valuemax=\"indeterminate ? undefined : 100\"\n >\n <div\n :class=\"[\n 'mint-progress__bar',\n `mint-progress__bar--${color}`,\n indeterminate ? 'mint-progress__bar--indeterminate' : '',\n ]\"\n :style=\"barStyle\"\n />\n </div>\n\n <!-- Segmented variant: discrete step segments -->\n <div\n v-else\n :class=\"['mint-progress__segments', `mint-progress__segments--${size}`]\"\n role=\"progressbar\"\n :aria-valuenow=\"currentStep\"\n :aria-valuemin=\"0\"\n :aria-valuemax=\"segmentCount\"\n >\n <span\n v-for=\"(_step, i) in steps\"\n :key=\"i\"\n :class=\"[\n 'mint-progress__segment',\n i < currentStep ? `mint-progress__segment--done mint-progress__segment--${color}` : '',\n i === currentStep ? `mint-progress__segment--active mint-progress__segment--${color}` : '',\n ]\"\n />\n </div>\n\n <!-- Segmented step labels -->\n <div\n v-if=\"variant === 'segmented' && steps.length\"\n class=\"mint-progress__step-labels\"\n >\n <span\n v-for=\"(step, i) in steps\"\n :key=\"i\"\n :class=\"[\n 'mint-progress__step-label',\n i === currentStep ? 'mint-progress__step-label--active' : '',\n i < currentStep ? 'mint-progress__step-label--done' : '',\n ]\"\n >\n {{ step }}\n </span>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/progress-bar.css';\n</style>\n","<script setup lang=\"ts\">\n/** Displays a user avatar as an image, a single derived initial, or a fallback icon, with a deterministic background color and optional status dot. */\nimport { computed } from 'vue'\n\ninterface Props {\n name?: string\n initials?: string\n src?: string\n alt?: string\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n color?: string\n status?: 'online' | 'away' | 'busy' | 'offline'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n alt: '',\n size: 'md',\n})\n\nconst PALETTE = ['#3B82F6', '#8B5CF6', '#EC4899', '#F97316', '#10B981', '#F59E0B', '#06B6D4', '#6366F1']\n\n// Default: first letter of `name`, uppercased — matches the platform's\n// canonical `getAvatarInitial(user)` helper. Consumers wanting two letters\n// (e.g. role badges like \"PI\", \"QC\") pass `:initials=\"'XX'\"` explicitly.\nconst computedInitials = computed(() =>\n props.initials || props.name?.charAt(0).toUpperCase() || ''\n)\n\nconst backgroundColor = computed(() => {\n if (props.color) return props.color\n if (!props.name) return undefined\n\n let hash = 0\n for (let i = 0; i < props.name.length; i++) {\n hash = props.name.charCodeAt(i) + ((hash << 5) - hash)\n }\n return PALETTE[Math.abs(hash) % PALETTE.length]\n})\n</script>\n\n<template>\n <span\n :class=\"['mint-avatar', `mint-avatar--${size}`]\"\n role=\"img\"\n :aria-label=\"alt || name || 'User avatar'\"\n >\n <span\n class=\"mint-avatar__face\"\n :style=\"backgroundColor ? { backgroundColor } : undefined\"\n >\n <img\n v-if=\"src\"\n :src=\"src\"\n :alt=\"alt || name || ''\"\n class=\"mint-avatar__image\"\n >\n <span v-else-if=\"computedInitials\" class=\"mint-avatar__initials\">\n {{ computedInitials }}\n </span>\n <svg\n v-else\n class=\"mint-avatar__fallback\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path d=\"M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z\" />\n </svg>\n </span>\n <span\n v-if=\"status\"\n :class=\"['mint-avatar__status', `mint-avatar__status--${status}`]\"\n aria-hidden=\"true\"\n />\n </span>\n</template>\n\n<style>\n@import '../styles/components/avatar.css';\n</style>\n","<script setup lang=\"ts\">\n/** Displays a user avatar as an image, a single derived initial, or a fallback icon, with a deterministic background color and optional status dot. */\nimport { computed } from 'vue'\n\ninterface Props {\n name?: string\n initials?: string\n src?: string\n alt?: string\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'\n color?: string\n status?: 'online' | 'away' | 'busy' | 'offline'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n alt: '',\n size: 'md',\n})\n\nconst PALETTE = ['#3B82F6', '#8B5CF6', '#EC4899', '#F97316', '#10B981', '#F59E0B', '#06B6D4', '#6366F1']\n\n// Default: first letter of `name`, uppercased — matches the platform's\n// canonical `getAvatarInitial(user)` helper. Consumers wanting two letters\n// (e.g. role badges like \"PI\", \"QC\") pass `:initials=\"'XX'\"` explicitly.\nconst computedInitials = computed(() =>\n props.initials || props.name?.charAt(0).toUpperCase() || ''\n)\n\nconst backgroundColor = computed(() => {\n if (props.color) return props.color\n if (!props.name) return undefined\n\n let hash = 0\n for (let i = 0; i < props.name.length; i++) {\n hash = props.name.charCodeAt(i) + ((hash << 5) - hash)\n }\n return PALETTE[Math.abs(hash) % PALETTE.length]\n})\n</script>\n\n<template>\n <span\n :class=\"['mint-avatar', `mint-avatar--${size}`]\"\n role=\"img\"\n :aria-label=\"alt || name || 'User avatar'\"\n >\n <span\n class=\"mint-avatar__face\"\n :style=\"backgroundColor ? { backgroundColor } : undefined\"\n >\n <img\n v-if=\"src\"\n :src=\"src\"\n :alt=\"alt || name || ''\"\n class=\"mint-avatar__image\"\n >\n <span v-else-if=\"computedInitials\" class=\"mint-avatar__initials\">\n {{ computedInitials }}\n </span>\n <svg\n v-else\n class=\"mint-avatar__fallback\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path d=\"M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z\" />\n </svg>\n </span>\n <span\n v-if=\"status\"\n :class=\"['mint-avatar__status', `mint-avatar__status--${status}`]\"\n aria-hidden=\"true\"\n />\n </span>\n</template>\n\n<style>\n@import '../styles/components/avatar.css';\n</style>\n","<script setup lang=\"ts\">\n/** Renders a breadcrumb trail where ancestors are links or emit-based buttons and the last item is the current page. */\nimport { computed } from 'vue'\nimport type { BreadcrumbItem, BreadcrumbItemInput } from '../types'\nimport { normalizeLabelItemInput } from '../utils/items'\n\ninterface Props {\n items: BreadcrumbItemInput[]\n separator?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n separator: '/',\n})\n\nconst emit = defineEmits<{\n navigate: [item: BreadcrumbItem]\n}>()\n\nconst normalizedItems = computed<BreadcrumbItem[]>(() =>\n props.items.map(normalizeLabelItemInput)\n)\n\nfunction handleClick(item: BreadcrumbItem) {\n if (!item.href) {\n emit('navigate', item)\n }\n}\n</script>\n\n<template>\n <nav class=\"mint-breadcrumb\" aria-label=\"Breadcrumb\">\n <ol class=\"mint-breadcrumb__list\">\n <li\n v-for=\"(item, index) in normalizedItems\"\n :key=\"index\"\n class=\"mint-breadcrumb__item\"\n >\n <slot name=\"item\" :item=\"item\" :index=\"index\" :is-last=\"index === normalizedItems.length - 1\">\n <a\n v-if=\"item.href && index !== normalizedItems.length - 1\"\n :href=\"item.href\"\n class=\"mint-breadcrumb__link\"\n >\n {{ item.label }}\n </a>\n <button\n v-else-if=\"index !== normalizedItems.length - 1\"\n class=\"mint-breadcrumb__link\"\n @click=\"handleClick(item)\"\n >\n {{ item.label }}\n </button>\n <span\n v-else\n class=\"mint-breadcrumb__current\"\n aria-current=\"page\"\n >\n {{ item.label }}\n </span>\n </slot>\n <span\n v-if=\"index !== normalizedItems.length - 1\"\n class=\"mint-breadcrumb__separator\"\n aria-hidden=\"true\"\n >\n <slot name=\"separator\">\n <svg v-if=\"separator === '/'\" class=\"mint-breadcrumb__chevron\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n <template v-else>{{ separator }}</template>\n </slot>\n </span>\n </li>\n </ol>\n </nav>\n</template>\n\n<style>\n@import '../styles/components/breadcrumb.css';\n</style>\n","<script setup lang=\"ts\">\n/** Renders a breadcrumb trail where ancestors are links or emit-based buttons and the last item is the current page. */\nimport { computed } from 'vue'\nimport type { BreadcrumbItem, BreadcrumbItemInput } from '../types'\nimport { normalizeLabelItemInput } from '../utils/items'\n\ninterface Props {\n items: BreadcrumbItemInput[]\n separator?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n separator: '/',\n})\n\nconst emit = defineEmits<{\n navigate: [item: BreadcrumbItem]\n}>()\n\nconst normalizedItems = computed<BreadcrumbItem[]>(() =>\n props.items.map(normalizeLabelItemInput)\n)\n\nfunction handleClick(item: BreadcrumbItem) {\n if (!item.href) {\n emit('navigate', item)\n }\n}\n</script>\n\n<template>\n <nav class=\"mint-breadcrumb\" aria-label=\"Breadcrumb\">\n <ol class=\"mint-breadcrumb__list\">\n <li\n v-for=\"(item, index) in normalizedItems\"\n :key=\"index\"\n class=\"mint-breadcrumb__item\"\n >\n <slot name=\"item\" :item=\"item\" :index=\"index\" :is-last=\"index === normalizedItems.length - 1\">\n <a\n v-if=\"item.href && index !== normalizedItems.length - 1\"\n :href=\"item.href\"\n class=\"mint-breadcrumb__link\"\n >\n {{ item.label }}\n </a>\n <button\n v-else-if=\"index !== normalizedItems.length - 1\"\n class=\"mint-breadcrumb__link\"\n @click=\"handleClick(item)\"\n >\n {{ item.label }}\n </button>\n <span\n v-else\n class=\"mint-breadcrumb__current\"\n aria-current=\"page\"\n >\n {{ item.label }}\n </span>\n </slot>\n <span\n v-if=\"index !== normalizedItems.length - 1\"\n class=\"mint-breadcrumb__separator\"\n aria-hidden=\"true\"\n >\n <slot name=\"separator\">\n <svg v-if=\"separator === '/'\" class=\"mint-breadcrumb__chevron\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n <template v-else>{{ separator }}</template>\n </slot>\n </span>\n </li>\n </ol>\n </nav>\n</template>\n\n<style>\n@import '../styles/components/breadcrumb.css';\n</style>\n","/**\n * Custom positioning reference element.\n * @see https://floating-ui.com/docs/virtual-elements\n */\n\nconst sides = ['top', 'right', 'bottom', 'left'];\nconst alignments = ['start', 'end'];\nconst placements = /*#__PURE__*/sides.reduce((acc, side) => acc.concat(side, side + \"-\" + alignments[0], side + \"-\" + alignments[1]), []);\nconst min = Math.min;\nconst max = Math.max;\nconst round = Math.round;\nconst floor = Math.floor;\nconst createCoords = v => ({\n x: v,\n y: v\n});\nconst oppositeSideMap = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nfunction clamp(start, value, end) {\n return max(start, min(value, end));\n}\nfunction evaluate(value, param) {\n return typeof value === 'function' ? value(param) : value;\n}\nfunction getSide(placement) {\n return placement.split('-')[0];\n}\nfunction getAlignment(placement) {\n return placement.split('-')[1];\n}\nfunction getOppositeAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}\nfunction getAxisLength(axis) {\n return axis === 'y' ? 'height' : 'width';\n}\nfunction getSideAxis(placement) {\n const firstChar = placement[0];\n return firstChar === 't' || firstChar === 'b' ? 'y' : 'x';\n}\nfunction getAlignmentAxis(placement) {\n return getOppositeAxis(getSideAxis(placement));\n}\nfunction getAlignmentSides(placement, rects, rtl) {\n if (rtl === void 0) {\n rtl = false;\n }\n const alignment = getAlignment(placement);\n const alignmentAxis = getAlignmentAxis(placement);\n const length = getAxisLength(alignmentAxis);\n let mainAlignmentSide = alignmentAxis === 'x' ? alignment === (rtl ? 'end' : 'start') ? 'right' : 'left' : alignment === 'start' ? 'bottom' : 'top';\n if (rects.reference[length] > rects.floating[length]) {\n mainAlignmentSide = getOppositePlacement(mainAlignmentSide);\n }\n return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];\n}\nfunction getExpandedPlacements(placement) {\n const oppositePlacement = getOppositePlacement(placement);\n return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];\n}\nfunction getOppositeAlignmentPlacement(placement) {\n return placement.includes('start') ? placement.replace('start', 'end') : placement.replace('end', 'start');\n}\nconst lrPlacement = ['left', 'right'];\nconst rlPlacement = ['right', 'left'];\nconst tbPlacement = ['top', 'bottom'];\nconst btPlacement = ['bottom', 'top'];\nfunction getSideList(side, isStart, rtl) {\n switch (side) {\n case 'top':\n case 'bottom':\n if (rtl) return isStart ? rlPlacement : lrPlacement;\n return isStart ? lrPlacement : rlPlacement;\n case 'left':\n case 'right':\n return isStart ? tbPlacement : btPlacement;\n default:\n return [];\n }\n}\nfunction getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {\n const alignment = getAlignment(placement);\n let list = getSideList(getSide(placement), direction === 'start', rtl);\n if (alignment) {\n list = list.map(side => side + \"-\" + alignment);\n if (flipAlignment) {\n list = list.concat(list.map(getOppositeAlignmentPlacement));\n }\n }\n return list;\n}\nfunction getOppositePlacement(placement) {\n const side = getSide(placement);\n return oppositeSideMap[side] + placement.slice(side.length);\n}\nfunction expandPaddingObject(padding) {\n return {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n ...padding\n };\n}\nfunction getPaddingObject(padding) {\n return typeof padding !== 'number' ? expandPaddingObject(padding) : {\n top: padding,\n right: padding,\n bottom: padding,\n left: padding\n };\n}\nfunction rectToClientRect(rect) {\n const {\n x,\n y,\n width,\n height\n } = rect;\n return {\n width,\n height,\n top: y,\n left: x,\n right: x + width,\n bottom: y + height,\n x,\n y\n };\n}\n\nexport { alignments, clamp, createCoords, evaluate, expandPaddingObject, floor, getAlignment, getAlignmentAxis, getAlignmentSides, getAxisLength, getExpandedPlacements, getOppositeAlignmentPlacement, getOppositeAxis, getOppositeAxisPlacements, getOppositePlacement, getPaddingObject, getSide, getSideAxis, max, min, placements, rectToClientRect, round, sides };\n","import { getSideAxis, getAlignmentAxis, getAxisLength, getSide, getAlignment, evaluate, getPaddingObject, rectToClientRect, min, clamp, placements, getAlignmentSides, getOppositeAlignmentPlacement, getOppositePlacement, getExpandedPlacements, getOppositeAxisPlacements, sides, max, getOppositeAxis } from '@floating-ui/utils';\nexport { rectToClientRect } from '@floating-ui/utils';\n\nfunction computeCoordsFromPlacement(_ref, placement, rtl) {\n let {\n reference,\n floating\n } = _ref;\n const sideAxis = getSideAxis(placement);\n const alignmentAxis = getAlignmentAxis(placement);\n const alignLength = getAxisLength(alignmentAxis);\n const side = getSide(placement);\n const isVertical = sideAxis === 'y';\n const commonX = reference.x + reference.width / 2 - floating.width / 2;\n const commonY = reference.y + reference.height / 2 - floating.height / 2;\n const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2;\n let coords;\n switch (side) {\n case 'top':\n coords = {\n x: commonX,\n y: reference.y - floating.height\n };\n break;\n case 'bottom':\n coords = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n case 'right':\n coords = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n case 'left':\n coords = {\n x: reference.x - floating.width,\n y: commonY\n };\n break;\n default:\n coords = {\n x: reference.x,\n y: reference.y\n };\n }\n switch (getAlignment(placement)) {\n case 'start':\n coords[alignmentAxis] -= commonAlign * (rtl && isVertical ? -1 : 1);\n break;\n case 'end':\n coords[alignmentAxis] += commonAlign * (rtl && isVertical ? -1 : 1);\n break;\n }\n return coords;\n}\n\n/**\n * Resolves with an object of overflow side offsets that determine how much the\n * element is overflowing a given clipping boundary on each side.\n * - positive = overflowing the boundary by that number of pixels\n * - negative = how many pixels left before it will overflow\n * - 0 = lies flush with the boundary\n * @see https://floating-ui.com/docs/detectOverflow\n */\nasync function detectOverflow(state, options) {\n var _await$platform$isEle;\n if (options === void 0) {\n options = {};\n }\n const {\n x,\n y,\n platform,\n rects,\n elements,\n strategy\n } = state;\n const {\n boundary = 'clippingAncestors',\n rootBoundary = 'viewport',\n elementContext = 'floating',\n altBoundary = false,\n padding = 0\n } = evaluate(options, state);\n const paddingObject = getPaddingObject(padding);\n const altContext = elementContext === 'floating' ? 'reference' : 'floating';\n const element = elements[altBoundary ? altContext : elementContext];\n const clippingClientRect = rectToClientRect(await platform.getClippingRect({\n element: ((_await$platform$isEle = await (platform.isElement == null ? void 0 : platform.isElement(element))) != null ? _await$platform$isEle : true) ? element : element.contextElement || (await (platform.getDocumentElement == null ? void 0 : platform.getDocumentElement(elements.floating))),\n boundary,\n rootBoundary,\n strategy\n }));\n const rect = elementContext === 'floating' ? {\n x,\n y,\n width: rects.floating.width,\n height: rects.floating.height\n } : rects.reference;\n const offsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(elements.floating));\n const offsetScale = (await (platform.isElement == null ? void 0 : platform.isElement(offsetParent))) ? (await (platform.getScale == null ? void 0 : platform.getScale(offsetParent))) || {\n x: 1,\n y: 1\n } : {\n x: 1,\n y: 1\n };\n const elementClientRect = rectToClientRect(platform.convertOffsetParentRelativeRectToViewportRelativeRect ? await platform.convertOffsetParentRelativeRectToViewportRelativeRect({\n elements,\n rect,\n offsetParent,\n strategy\n }) : rect);\n return {\n top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y,\n bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y,\n left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x,\n right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x\n };\n}\n\n// Maximum number of resets that can occur before bailing to avoid infinite reset loops.\nconst MAX_RESET_COUNT = 50;\n\n/**\n * Computes the `x` and `y` coordinates that will place the floating element\n * next to a given reference element.\n *\n * This export does not have any `platform` interface logic. You will need to\n * write one for the platform you are using Floating UI with.\n */\nconst computePosition = async (reference, floating, config) => {\n const {\n placement = 'bottom',\n strategy = 'absolute',\n middleware = [],\n platform\n } = config;\n const platformWithDetectOverflow = platform.detectOverflow ? platform : {\n ...platform,\n detectOverflow\n };\n const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(floating));\n let rects = await platform.getElementRects({\n reference,\n floating,\n strategy\n });\n let {\n x,\n y\n } = computeCoordsFromPlacement(rects, placement, rtl);\n let statefulPlacement = placement;\n let resetCount = 0;\n const middlewareData = {};\n for (let i = 0; i < middleware.length; i++) {\n const currentMiddleware = middleware[i];\n if (!currentMiddleware) {\n continue;\n }\n const {\n name,\n fn\n } = currentMiddleware;\n const {\n x: nextX,\n y: nextY,\n data,\n reset\n } = await fn({\n x,\n y,\n initialPlacement: placement,\n placement: statefulPlacement,\n strategy,\n middlewareData,\n rects,\n platform: platformWithDetectOverflow,\n elements: {\n reference,\n floating\n }\n });\n x = nextX != null ? nextX : x;\n y = nextY != null ? nextY : y;\n middlewareData[name] = {\n ...middlewareData[name],\n ...data\n };\n if (reset && resetCount < MAX_RESET_COUNT) {\n resetCount++;\n if (typeof reset === 'object') {\n if (reset.placement) {\n statefulPlacement = reset.placement;\n }\n if (reset.rects) {\n rects = reset.rects === true ? await platform.getElementRects({\n reference,\n floating,\n strategy\n }) : reset.rects;\n }\n ({\n x,\n y\n } = computeCoordsFromPlacement(rects, statefulPlacement, rtl));\n }\n i = -1;\n }\n }\n return {\n x,\n y,\n placement: statefulPlacement,\n strategy,\n middlewareData\n };\n};\n\n/**\n * Provides data to position an inner element of the floating element so that it\n * appears centered to the reference element.\n * @see https://floating-ui.com/docs/arrow\n */\nconst arrow = options => ({\n name: 'arrow',\n options,\n async fn(state) {\n const {\n x,\n y,\n placement,\n rects,\n platform,\n elements,\n middlewareData\n } = state;\n // Since `element` is required, we don't Partial<> the type.\n const {\n element,\n padding = 0\n } = evaluate(options, state) || {};\n if (element == null) {\n return {};\n }\n const paddingObject = getPaddingObject(padding);\n const coords = {\n x,\n y\n };\n const axis = getAlignmentAxis(placement);\n const length = getAxisLength(axis);\n const arrowDimensions = await platform.getDimensions(element);\n const isYAxis = axis === 'y';\n const minProp = isYAxis ? 'top' : 'left';\n const maxProp = isYAxis ? 'bottom' : 'right';\n const clientProp = isYAxis ? 'clientHeight' : 'clientWidth';\n const endDiff = rects.reference[length] + rects.reference[axis] - coords[axis] - rects.floating[length];\n const startDiff = coords[axis] - rects.reference[axis];\n const arrowOffsetParent = await (platform.getOffsetParent == null ? void 0 : platform.getOffsetParent(element));\n let clientSize = arrowOffsetParent ? arrowOffsetParent[clientProp] : 0;\n\n // DOM platform can return `window` as the `offsetParent`.\n if (!clientSize || !(await (platform.isElement == null ? void 0 : platform.isElement(arrowOffsetParent)))) {\n clientSize = elements.floating[clientProp] || rects.floating[length];\n }\n const centerToReference = endDiff / 2 - startDiff / 2;\n\n // If the padding is large enough that it causes the arrow to no longer be\n // centered, modify the padding so that it is centered.\n const largestPossiblePadding = clientSize / 2 - arrowDimensions[length] / 2 - 1;\n const minPadding = min(paddingObject[minProp], largestPossiblePadding);\n const maxPadding = min(paddingObject[maxProp], largestPossiblePadding);\n\n // Make sure the arrow doesn't overflow the floating element if the center\n // point is outside the floating element's bounds.\n const min$1 = minPadding;\n const max = clientSize - arrowDimensions[length] - maxPadding;\n const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;\n const offset = clamp(min$1, center, max);\n\n // If the reference is small enough that the arrow's padding causes it to\n // to point to nothing for an aligned placement, adjust the offset of the\n // floating element itself. To ensure `shift()` continues to take action,\n // a single reset is performed when this is true.\n const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset && rects.reference[length] / 2 - (center < min$1 ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;\n const alignmentOffset = shouldAddOffset ? center < min$1 ? center - min$1 : center - max : 0;\n return {\n [axis]: coords[axis] + alignmentOffset,\n data: {\n [axis]: offset,\n centerOffset: center - offset - alignmentOffset,\n ...(shouldAddOffset && {\n alignmentOffset\n })\n },\n reset: shouldAddOffset\n };\n }\n});\n\nfunction getPlacementList(alignment, autoAlignment, allowedPlacements) {\n const allowedPlacementsSortedByAlignment = alignment ? [...allowedPlacements.filter(placement => getAlignment(placement) === alignment), ...allowedPlacements.filter(placement => getAlignment(placement) !== alignment)] : allowedPlacements.filter(placement => getSide(placement) === placement);\n return allowedPlacementsSortedByAlignment.filter(placement => {\n if (alignment) {\n return getAlignment(placement) === alignment || (autoAlignment ? getOppositeAlignmentPlacement(placement) !== placement : false);\n }\n return true;\n });\n}\n/**\n * Optimizes the visibility of the floating element by choosing the placement\n * that has the most space available automatically, without needing to specify a\n * preferred placement. Alternative to `flip`.\n * @see https://floating-ui.com/docs/autoPlacement\n */\nconst autoPlacement = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'autoPlacement',\n options,\n async fn(state) {\n var _middlewareData$autoP, _middlewareData$autoP2, _placementsThatFitOnE;\n const {\n rects,\n middlewareData,\n placement,\n platform,\n elements\n } = state;\n const {\n crossAxis = false,\n alignment,\n allowedPlacements = placements,\n autoAlignment = true,\n ...detectOverflowOptions\n } = evaluate(options, state);\n const placements$1 = alignment !== undefined || allowedPlacements === placements ? getPlacementList(alignment || null, autoAlignment, allowedPlacements) : allowedPlacements;\n const overflow = await platform.detectOverflow(state, detectOverflowOptions);\n const currentIndex = ((_middlewareData$autoP = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP.index) || 0;\n const currentPlacement = placements$1[currentIndex];\n if (currentPlacement == null) {\n return {};\n }\n const alignmentSides = getAlignmentSides(currentPlacement, rects, await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating)));\n\n // Make `computeCoords` start from the right place.\n if (placement !== currentPlacement) {\n return {\n reset: {\n placement: placements$1[0]\n }\n };\n }\n const currentOverflows = [overflow[getSide(currentPlacement)], overflow[alignmentSides[0]], overflow[alignmentSides[1]]];\n const allOverflows = [...(((_middlewareData$autoP2 = middlewareData.autoPlacement) == null ? void 0 : _middlewareData$autoP2.overflows) || []), {\n placement: currentPlacement,\n overflows: currentOverflows\n }];\n const nextPlacement = placements$1[currentIndex + 1];\n\n // There are more placements to check.\n if (nextPlacement) {\n return {\n data: {\n index: currentIndex + 1,\n overflows: allOverflows\n },\n reset: {\n placement: nextPlacement\n }\n };\n }\n const placementsSortedByMostSpace = allOverflows.map(d => {\n const alignment = getAlignment(d.placement);\n return [d.placement, alignment && crossAxis ?\n // Check along the mainAxis and main crossAxis side.\n d.overflows.slice(0, 2).reduce((acc, v) => acc + v, 0) :\n // Check only the mainAxis.\n d.overflows[0], d.overflows];\n }).sort((a, b) => a[1] - b[1]);\n const placementsThatFitOnEachSide = placementsSortedByMostSpace.filter(d => d[2].slice(0,\n // Aligned placements should not check their opposite crossAxis\n // side.\n getAlignment(d[0]) ? 2 : 3).every(v => v <= 0));\n const resetPlacement = ((_placementsThatFitOnE = placementsThatFitOnEachSide[0]) == null ? void 0 : _placementsThatFitOnE[0]) || placementsSortedByMostSpace[0][0];\n if (resetPlacement !== placement) {\n return {\n data: {\n index: currentIndex + 1,\n overflows: allOverflows\n },\n reset: {\n placement: resetPlacement\n }\n };\n }\n return {};\n }\n };\n};\n\n/**\n * Optimizes the visibility of the floating element by flipping the `placement`\n * in order to keep it in view when the preferred placement(s) will overflow the\n * clipping boundary. Alternative to `autoPlacement`.\n * @see https://floating-ui.com/docs/flip\n */\nconst flip = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'flip',\n options,\n async fn(state) {\n var _middlewareData$arrow, _middlewareData$flip;\n const {\n placement,\n middlewareData,\n rects,\n initialPlacement,\n platform,\n elements\n } = state;\n const {\n mainAxis: checkMainAxis = true,\n crossAxis: checkCrossAxis = true,\n fallbackPlacements: specifiedFallbackPlacements,\n fallbackStrategy = 'bestFit',\n fallbackAxisSideDirection = 'none',\n flipAlignment = true,\n ...detectOverflowOptions\n } = evaluate(options, state);\n\n // If a reset by the arrow was caused due to an alignment offset being\n // added, we should skip any logic now since `flip()` has already done its\n // work.\n // https://github.com/floating-ui/floating-ui/issues/2549#issuecomment-1719601643\n if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {\n return {};\n }\n const side = getSide(placement);\n const initialSideAxis = getSideAxis(initialPlacement);\n const isBasePlacement = getSide(initialPlacement) === initialPlacement;\n const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));\n const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement));\n const hasFallbackAxisSideDirection = fallbackAxisSideDirection !== 'none';\n if (!specifiedFallbackPlacements && hasFallbackAxisSideDirection) {\n fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl));\n }\n const placements = [initialPlacement, ...fallbackPlacements];\n const overflow = await platform.detectOverflow(state, detectOverflowOptions);\n const overflows = [];\n let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || [];\n if (checkMainAxis) {\n overflows.push(overflow[side]);\n }\n if (checkCrossAxis) {\n const sides = getAlignmentSides(placement, rects, rtl);\n overflows.push(overflow[sides[0]], overflow[sides[1]]);\n }\n overflowsData = [...overflowsData, {\n placement,\n overflows\n }];\n\n // One or more sides is overflowing.\n if (!overflows.every(side => side <= 0)) {\n var _middlewareData$flip2, _overflowsData$filter;\n const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1;\n const nextPlacement = placements[nextIndex];\n if (nextPlacement) {\n const ignoreCrossAxisOverflow = checkCrossAxis === 'alignment' ? initialSideAxis !== getSideAxis(nextPlacement) : false;\n if (!ignoreCrossAxisOverflow ||\n // We leave the current main axis only if every placement on that axis\n // overflows the main axis.\n overflowsData.every(d => getSideAxis(d.placement) === initialSideAxis ? d.overflows[0] > 0 : true)) {\n // Try next placement and re-run the lifecycle.\n return {\n data: {\n index: nextIndex,\n overflows: overflowsData\n },\n reset: {\n placement: nextPlacement\n }\n };\n }\n }\n\n // First, find the candidates that fit on the mainAxis side of overflow,\n // then find the placement that fits the best on the main crossAxis side.\n let resetPlacement = (_overflowsData$filter = overflowsData.filter(d => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement;\n\n // Otherwise fallback.\n if (!resetPlacement) {\n switch (fallbackStrategy) {\n case 'bestFit':\n {\n var _overflowsData$filter2;\n const placement = (_overflowsData$filter2 = overflowsData.filter(d => {\n if (hasFallbackAxisSideDirection) {\n const currentSideAxis = getSideAxis(d.placement);\n return currentSideAxis === initialSideAxis ||\n // Create a bias to the `y` side axis due to horizontal\n // reading directions favoring greater width.\n currentSideAxis === 'y';\n }\n return true;\n }).map(d => [d.placement, d.overflows.filter(overflow => overflow > 0).reduce((acc, overflow) => acc + overflow, 0)]).sort((a, b) => a[1] - b[1])[0]) == null ? void 0 : _overflowsData$filter2[0];\n if (placement) {\n resetPlacement = placement;\n }\n break;\n }\n case 'initialPlacement':\n resetPlacement = initialPlacement;\n break;\n }\n }\n if (placement !== resetPlacement) {\n return {\n reset: {\n placement: resetPlacement\n }\n };\n }\n }\n return {};\n }\n };\n};\n\nfunction getSideOffsets(overflow, rect) {\n return {\n top: overflow.top - rect.height,\n right: overflow.right - rect.width,\n bottom: overflow.bottom - rect.height,\n left: overflow.left - rect.width\n };\n}\nfunction isAnySideFullyClipped(overflow) {\n return sides.some(side => overflow[side] >= 0);\n}\n/**\n * Provides data to hide the floating element in applicable situations, such as\n * when it is not in the same clipping context as the reference element.\n * @see https://floating-ui.com/docs/hide\n */\nconst hide = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'hide',\n options,\n async fn(state) {\n const {\n rects,\n platform\n } = state;\n const {\n strategy = 'referenceHidden',\n ...detectOverflowOptions\n } = evaluate(options, state);\n switch (strategy) {\n case 'referenceHidden':\n {\n const overflow = await platform.detectOverflow(state, {\n ...detectOverflowOptions,\n elementContext: 'reference'\n });\n const offsets = getSideOffsets(overflow, rects.reference);\n return {\n data: {\n referenceHiddenOffsets: offsets,\n referenceHidden: isAnySideFullyClipped(offsets)\n }\n };\n }\n case 'escaped':\n {\n const overflow = await platform.detectOverflow(state, {\n ...detectOverflowOptions,\n altBoundary: true\n });\n const offsets = getSideOffsets(overflow, rects.floating);\n return {\n data: {\n escapedOffsets: offsets,\n escaped: isAnySideFullyClipped(offsets)\n }\n };\n }\n default:\n {\n return {};\n }\n }\n }\n };\n};\n\nfunction getBoundingRect(rects) {\n const minX = min(...rects.map(rect => rect.left));\n const minY = min(...rects.map(rect => rect.top));\n const maxX = max(...rects.map(rect => rect.right));\n const maxY = max(...rects.map(rect => rect.bottom));\n return {\n x: minX,\n y: minY,\n width: maxX - minX,\n height: maxY - minY\n };\n}\nfunction getRectsByLine(rects) {\n const sortedRects = rects.slice().sort((a, b) => a.y - b.y);\n const groups = [];\n let prevRect = null;\n for (let i = 0; i < sortedRects.length; i++) {\n const rect = sortedRects[i];\n if (!prevRect || rect.y - prevRect.y > prevRect.height / 2) {\n groups.push([rect]);\n } else {\n groups[groups.length - 1].push(rect);\n }\n prevRect = rect;\n }\n return groups.map(rect => rectToClientRect(getBoundingRect(rect)));\n}\n/**\n * Provides improved positioning for inline reference elements that can span\n * over multiple lines, such as hyperlinks or range selections.\n * @see https://floating-ui.com/docs/inline\n */\nconst inline = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'inline',\n options,\n async fn(state) {\n const {\n placement,\n elements,\n rects,\n platform,\n strategy\n } = state;\n // A MouseEvent's client{X,Y} coords can be up to 2 pixels off a\n // ClientRect's bounds, despite the event listener being triggered. A\n // padding of 2 seems to handle this issue.\n const {\n padding = 2,\n x,\n y\n } = evaluate(options, state);\n const nativeClientRects = Array.from((await (platform.getClientRects == null ? void 0 : platform.getClientRects(elements.reference))) || []);\n const clientRects = getRectsByLine(nativeClientRects);\n const fallback = rectToClientRect(getBoundingRect(nativeClientRects));\n const paddingObject = getPaddingObject(padding);\n function getBoundingClientRect() {\n // There are two rects and they are disjoined.\n if (clientRects.length === 2 && clientRects[0].left > clientRects[1].right && x != null && y != null) {\n // Find the first rect in which the point is fully inside.\n return clientRects.find(rect => x > rect.left - paddingObject.left && x < rect.right + paddingObject.right && y > rect.top - paddingObject.top && y < rect.bottom + paddingObject.bottom) || fallback;\n }\n\n // There are 2 or more connected rects.\n if (clientRects.length >= 2) {\n if (getSideAxis(placement) === 'y') {\n const firstRect = clientRects[0];\n const lastRect = clientRects[clientRects.length - 1];\n const isTop = getSide(placement) === 'top';\n const top = firstRect.top;\n const bottom = lastRect.bottom;\n const left = isTop ? firstRect.left : lastRect.left;\n const right = isTop ? firstRect.right : lastRect.right;\n const width = right - left;\n const height = bottom - top;\n return {\n top,\n bottom,\n left,\n right,\n width,\n height,\n x: left,\n y: top\n };\n }\n const isLeftSide = getSide(placement) === 'left';\n const maxRight = max(...clientRects.map(rect => rect.right));\n const minLeft = min(...clientRects.map(rect => rect.left));\n const measureRects = clientRects.filter(rect => isLeftSide ? rect.left === minLeft : rect.right === maxRight);\n const top = measureRects[0].top;\n const bottom = measureRects[measureRects.length - 1].bottom;\n const left = minLeft;\n const right = maxRight;\n const width = right - left;\n const height = bottom - top;\n return {\n top,\n bottom,\n left,\n right,\n width,\n height,\n x: left,\n y: top\n };\n }\n return fallback;\n }\n const resetRects = await platform.getElementRects({\n reference: {\n getBoundingClientRect\n },\n floating: elements.floating,\n strategy\n });\n if (rects.reference.x !== resetRects.reference.x || rects.reference.y !== resetRects.reference.y || rects.reference.width !== resetRects.reference.width || rects.reference.height !== resetRects.reference.height) {\n return {\n reset: {\n rects: resetRects\n }\n };\n }\n return {};\n }\n };\n};\n\nconst originSides = /*#__PURE__*/new Set(['left', 'top']);\n\n// For type backwards-compatibility, the `OffsetOptions` type was also\n// Derivable.\n\nasync function convertValueToCoords(state, options) {\n const {\n placement,\n platform,\n elements\n } = state;\n const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));\n const side = getSide(placement);\n const alignment = getAlignment(placement);\n const isVertical = getSideAxis(placement) === 'y';\n const mainAxisMulti = originSides.has(side) ? -1 : 1;\n const crossAxisMulti = rtl && isVertical ? -1 : 1;\n const rawValue = evaluate(options, state);\n\n // eslint-disable-next-line prefer-const\n let {\n mainAxis,\n crossAxis,\n alignmentAxis\n } = typeof rawValue === 'number' ? {\n mainAxis: rawValue,\n crossAxis: 0,\n alignmentAxis: null\n } : {\n mainAxis: rawValue.mainAxis || 0,\n crossAxis: rawValue.crossAxis || 0,\n alignmentAxis: rawValue.alignmentAxis\n };\n if (alignment && typeof alignmentAxis === 'number') {\n crossAxis = alignment === 'end' ? alignmentAxis * -1 : alignmentAxis;\n }\n return isVertical ? {\n x: crossAxis * crossAxisMulti,\n y: mainAxis * mainAxisMulti\n } : {\n x: mainAxis * mainAxisMulti,\n y: crossAxis * crossAxisMulti\n };\n}\n\n/**\n * Modifies the placement by translating the floating element along the\n * specified axes.\n * A number (shorthand for `mainAxis` or distance), or an axes configuration\n * object may be passed.\n * @see https://floating-ui.com/docs/offset\n */\nconst offset = function (options) {\n if (options === void 0) {\n options = 0;\n }\n return {\n name: 'offset',\n options,\n async fn(state) {\n var _middlewareData$offse, _middlewareData$arrow;\n const {\n x,\n y,\n placement,\n middlewareData\n } = state;\n const diffCoords = await convertValueToCoords(state, options);\n\n // If the placement is the same and the arrow caused an alignment offset\n // then we don't need to change the positioning coordinates.\n if (placement === ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse.placement) && (_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {\n return {};\n }\n return {\n x: x + diffCoords.x,\n y: y + diffCoords.y,\n data: {\n ...diffCoords,\n placement\n }\n };\n }\n };\n};\n\n/**\n * Optimizes the visibility of the floating element by shifting it in order to\n * keep it in view when it will overflow the clipping boundary.\n * @see https://floating-ui.com/docs/shift\n */\nconst shift = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'shift',\n options,\n async fn(state) {\n const {\n x,\n y,\n placement,\n platform\n } = state;\n const {\n mainAxis: checkMainAxis = true,\n crossAxis: checkCrossAxis = false,\n limiter = {\n fn: _ref => {\n let {\n x,\n y\n } = _ref;\n return {\n x,\n y\n };\n }\n },\n ...detectOverflowOptions\n } = evaluate(options, state);\n const coords = {\n x,\n y\n };\n const overflow = await platform.detectOverflow(state, detectOverflowOptions);\n const crossAxis = getSideAxis(getSide(placement));\n const mainAxis = getOppositeAxis(crossAxis);\n let mainAxisCoord = coords[mainAxis];\n let crossAxisCoord = coords[crossAxis];\n if (checkMainAxis) {\n const minSide = mainAxis === 'y' ? 'top' : 'left';\n const maxSide = mainAxis === 'y' ? 'bottom' : 'right';\n const min = mainAxisCoord + overflow[minSide];\n const max = mainAxisCoord - overflow[maxSide];\n mainAxisCoord = clamp(min, mainAxisCoord, max);\n }\n if (checkCrossAxis) {\n const minSide = crossAxis === 'y' ? 'top' : 'left';\n const maxSide = crossAxis === 'y' ? 'bottom' : 'right';\n const min = crossAxisCoord + overflow[minSide];\n const max = crossAxisCoord - overflow[maxSide];\n crossAxisCoord = clamp(min, crossAxisCoord, max);\n }\n const limitedCoords = limiter.fn({\n ...state,\n [mainAxis]: mainAxisCoord,\n [crossAxis]: crossAxisCoord\n });\n return {\n ...limitedCoords,\n data: {\n x: limitedCoords.x - x,\n y: limitedCoords.y - y,\n enabled: {\n [mainAxis]: checkMainAxis,\n [crossAxis]: checkCrossAxis\n }\n }\n };\n }\n };\n};\n/**\n * Built-in `limiter` that will stop `shift()` at a certain point.\n */\nconst limitShift = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n options,\n fn(state) {\n const {\n x,\n y,\n placement,\n rects,\n middlewareData\n } = state;\n const {\n offset = 0,\n mainAxis: checkMainAxis = true,\n crossAxis: checkCrossAxis = true\n } = evaluate(options, state);\n const coords = {\n x,\n y\n };\n const crossAxis = getSideAxis(placement);\n const mainAxis = getOppositeAxis(crossAxis);\n let mainAxisCoord = coords[mainAxis];\n let crossAxisCoord = coords[crossAxis];\n const rawOffset = evaluate(offset, state);\n const computedOffset = typeof rawOffset === 'number' ? {\n mainAxis: rawOffset,\n crossAxis: 0\n } : {\n mainAxis: 0,\n crossAxis: 0,\n ...rawOffset\n };\n if (checkMainAxis) {\n const len = mainAxis === 'y' ? 'height' : 'width';\n const limitMin = rects.reference[mainAxis] - rects.floating[len] + computedOffset.mainAxis;\n const limitMax = rects.reference[mainAxis] + rects.reference[len] - computedOffset.mainAxis;\n if (mainAxisCoord < limitMin) {\n mainAxisCoord = limitMin;\n } else if (mainAxisCoord > limitMax) {\n mainAxisCoord = limitMax;\n }\n }\n if (checkCrossAxis) {\n var _middlewareData$offse, _middlewareData$offse2;\n const len = mainAxis === 'y' ? 'width' : 'height';\n const isOriginSide = originSides.has(getSide(placement));\n const limitMin = rects.reference[crossAxis] - rects.floating[len] + (isOriginSide ? ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse[crossAxis]) || 0 : 0) + (isOriginSide ? 0 : computedOffset.crossAxis);\n const limitMax = rects.reference[crossAxis] + rects.reference[len] + (isOriginSide ? 0 : ((_middlewareData$offse2 = middlewareData.offset) == null ? void 0 : _middlewareData$offse2[crossAxis]) || 0) - (isOriginSide ? computedOffset.crossAxis : 0);\n if (crossAxisCoord < limitMin) {\n crossAxisCoord = limitMin;\n } else if (crossAxisCoord > limitMax) {\n crossAxisCoord = limitMax;\n }\n }\n return {\n [mainAxis]: mainAxisCoord,\n [crossAxis]: crossAxisCoord\n };\n }\n };\n};\n\n/**\n * Provides data that allows you to change the size of the floating element —\n * for instance, prevent it from overflowing the clipping boundary or match the\n * width of the reference element.\n * @see https://floating-ui.com/docs/size\n */\nconst size = function (options) {\n if (options === void 0) {\n options = {};\n }\n return {\n name: 'size',\n options,\n async fn(state) {\n var _state$middlewareData, _state$middlewareData2;\n const {\n placement,\n rects,\n platform,\n elements\n } = state;\n const {\n apply = () => {},\n ...detectOverflowOptions\n } = evaluate(options, state);\n const overflow = await platform.detectOverflow(state, detectOverflowOptions);\n const side = getSide(placement);\n const alignment = getAlignment(placement);\n const isYAxis = getSideAxis(placement) === 'y';\n const {\n width,\n height\n } = rects.floating;\n let heightSide;\n let widthSide;\n if (side === 'top' || side === 'bottom') {\n heightSide = side;\n widthSide = alignment === ((await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating))) ? 'start' : 'end') ? 'left' : 'right';\n } else {\n widthSide = side;\n heightSide = alignment === 'end' ? 'top' : 'bottom';\n }\n const maximumClippingHeight = height - overflow.top - overflow.bottom;\n const maximumClippingWidth = width - overflow.left - overflow.right;\n const overflowAvailableHeight = min(height - overflow[heightSide], maximumClippingHeight);\n const overflowAvailableWidth = min(width - overflow[widthSide], maximumClippingWidth);\n const noShift = !state.middlewareData.shift;\n let availableHeight = overflowAvailableHeight;\n let availableWidth = overflowAvailableWidth;\n if ((_state$middlewareData = state.middlewareData.shift) != null && _state$middlewareData.enabled.x) {\n availableWidth = maximumClippingWidth;\n }\n if ((_state$middlewareData2 = state.middlewareData.shift) != null && _state$middlewareData2.enabled.y) {\n availableHeight = maximumClippingHeight;\n }\n if (noShift && !alignment) {\n const xMin = max(overflow.left, 0);\n const xMax = max(overflow.right, 0);\n const yMin = max(overflow.top, 0);\n const yMax = max(overflow.bottom, 0);\n if (isYAxis) {\n availableWidth = width - 2 * (xMin !== 0 || xMax !== 0 ? xMin + xMax : max(overflow.left, overflow.right));\n } else {\n availableHeight = height - 2 * (yMin !== 0 || yMax !== 0 ? yMin + yMax : max(overflow.top, overflow.bottom));\n }\n }\n await apply({\n ...state,\n availableWidth,\n availableHeight\n });\n const nextDimensions = await platform.getDimensions(elements.floating);\n if (width !== nextDimensions.width || height !== nextDimensions.height) {\n return {\n reset: {\n rects: true\n }\n };\n }\n return {};\n }\n };\n};\n\nexport { arrow, autoPlacement, computePosition, detectOverflow, flip, hide, inline, limitShift, offset, shift, size };\n","function hasWindow() {\n return typeof window !== 'undefined';\n}\nfunction getNodeName(node) {\n if (isNode(node)) {\n return (node.nodeName || '').toLowerCase();\n }\n // Mocked nodes in testing environments may not be instances of Node. By\n // returning `#document` an infinite loop won't occur.\n // https://github.com/floating-ui/floating-ui/issues/2317\n return '#document';\n}\nfunction getWindow(node) {\n var _node$ownerDocument;\n return (node == null || (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;\n}\nfunction getDocumentElement(node) {\n var _ref;\n return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement;\n}\nfunction isNode(value) {\n if (!hasWindow()) {\n return false;\n }\n return value instanceof Node || value instanceof getWindow(value).Node;\n}\nfunction isElement(value) {\n if (!hasWindow()) {\n return false;\n }\n return value instanceof Element || value instanceof getWindow(value).Element;\n}\nfunction isHTMLElement(value) {\n if (!hasWindow()) {\n return false;\n }\n return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement;\n}\nfunction isShadowRoot(value) {\n if (!hasWindow() || typeof ShadowRoot === 'undefined') {\n return false;\n }\n return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot;\n}\nfunction isOverflowElement(element) {\n const {\n overflow,\n overflowX,\n overflowY,\n display\n } = getComputedStyle(element);\n return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && display !== 'inline' && display !== 'contents';\n}\nfunction isTableElement(element) {\n return /^(table|td|th)$/.test(getNodeName(element));\n}\nfunction isTopLayer(element) {\n try {\n if (element.matches(':popover-open')) {\n return true;\n }\n } catch (_e) {\n // no-op\n }\n try {\n return element.matches(':modal');\n } catch (_e) {\n return false;\n }\n}\nconst willChangeRe = /transform|translate|scale|rotate|perspective|filter/;\nconst containRe = /paint|layout|strict|content/;\nconst isNotNone = value => !!value && value !== 'none';\nlet isWebKitValue;\nfunction isContainingBlock(elementOrCss) {\n const css = isElement(elementOrCss) ? getComputedStyle(elementOrCss) : elementOrCss;\n\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n // https://drafts.csswg.org/css-transforms-2/#individual-transforms\n return isNotNone(css.transform) || isNotNone(css.translate) || isNotNone(css.scale) || isNotNone(css.rotate) || isNotNone(css.perspective) || !isWebKit() && (isNotNone(css.backdropFilter) || isNotNone(css.filter)) || willChangeRe.test(css.willChange || '') || containRe.test(css.contain || '');\n}\nfunction getContainingBlock(element) {\n let currentNode = getParentNode(element);\n while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {\n if (isContainingBlock(currentNode)) {\n return currentNode;\n } else if (isTopLayer(currentNode)) {\n return null;\n }\n currentNode = getParentNode(currentNode);\n }\n return null;\n}\nfunction isWebKit() {\n if (isWebKitValue == null) {\n isWebKitValue = typeof CSS !== 'undefined' && CSS.supports && CSS.supports('-webkit-backdrop-filter', 'none');\n }\n return isWebKitValue;\n}\nfunction isLastTraversableNode(node) {\n return /^(html|body|#document)$/.test(getNodeName(node));\n}\nfunction getComputedStyle(element) {\n return getWindow(element).getComputedStyle(element);\n}\nfunction getNodeScroll(element) {\n if (isElement(element)) {\n return {\n scrollLeft: element.scrollLeft,\n scrollTop: element.scrollTop\n };\n }\n return {\n scrollLeft: element.scrollX,\n scrollTop: element.scrollY\n };\n}\nfunction getParentNode(node) {\n if (getNodeName(node) === 'html') {\n return node;\n }\n const result =\n // Step into the shadow DOM of the parent of a slotted node.\n node.assignedSlot ||\n // DOM Element detected.\n node.parentNode ||\n // ShadowRoot detected.\n isShadowRoot(node) && node.host ||\n // Fallback.\n getDocumentElement(node);\n return isShadowRoot(result) ? result.host : result;\n}\nfunction getNearestOverflowAncestor(node) {\n const parentNode = getParentNode(node);\n if (isLastTraversableNode(parentNode)) {\n return node.ownerDocument ? node.ownerDocument.body : node.body;\n }\n if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {\n return parentNode;\n }\n return getNearestOverflowAncestor(parentNode);\n}\nfunction getOverflowAncestors(node, list, traverseIframes) {\n var _node$ownerDocument2;\n if (list === void 0) {\n list = [];\n }\n if (traverseIframes === void 0) {\n traverseIframes = true;\n }\n const scrollableAncestor = getNearestOverflowAncestor(node);\n const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body);\n const win = getWindow(scrollableAncestor);\n if (isBody) {\n const frameElement = getFrameElement(win);\n return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], frameElement && traverseIframes ? getOverflowAncestors(frameElement) : []);\n } else {\n return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes));\n }\n}\nfunction getFrameElement(win) {\n return win.parent && Object.getPrototypeOf(win.parent) ? win.frameElement : null;\n}\n\nexport { getComputedStyle, getContainingBlock, getDocumentElement, getFrameElement, getNearestOverflowAncestor, getNodeName, getNodeScroll, getOverflowAncestors, getParentNode, getWindow, isContainingBlock, isElement, isHTMLElement, isLastTraversableNode, isNode, isOverflowElement, isShadowRoot, isTableElement, isTopLayer, isWebKit };\n","import { rectToClientRect, arrow as arrow$1, autoPlacement as autoPlacement$1, detectOverflow as detectOverflow$1, flip as flip$1, hide as hide$1, inline as inline$1, limitShift as limitShift$1, offset as offset$1, shift as shift$1, size as size$1, computePosition as computePosition$1 } from '@floating-ui/core';\nimport { round, createCoords, max, min, floor } from '@floating-ui/utils';\nimport { getComputedStyle as getComputedStyle$1, isHTMLElement, isElement, getWindow, isWebKit, getFrameElement, getNodeScroll, getDocumentElement, isTopLayer, getNodeName, isOverflowElement, getOverflowAncestors, getParentNode, isLastTraversableNode, isContainingBlock, isTableElement, getContainingBlock } from '@floating-ui/utils/dom';\nexport { getOverflowAncestors } from '@floating-ui/utils/dom';\n\nfunction getCssDimensions(element) {\n const css = getComputedStyle$1(element);\n // In testing environments, the `width` and `height` properties are empty\n // strings for SVG elements, returning NaN. Fallback to `0` in this case.\n let width = parseFloat(css.width) || 0;\n let height = parseFloat(css.height) || 0;\n const hasOffset = isHTMLElement(element);\n const offsetWidth = hasOffset ? element.offsetWidth : width;\n const offsetHeight = hasOffset ? element.offsetHeight : height;\n const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;\n if (shouldFallback) {\n width = offsetWidth;\n height = offsetHeight;\n }\n return {\n width,\n height,\n $: shouldFallback\n };\n}\n\nfunction unwrapElement(element) {\n return !isElement(element) ? element.contextElement : element;\n}\n\nfunction getScale(element) {\n const domElement = unwrapElement(element);\n if (!isHTMLElement(domElement)) {\n return createCoords(1);\n }\n const rect = domElement.getBoundingClientRect();\n const {\n width,\n height,\n $\n } = getCssDimensions(domElement);\n let x = ($ ? round(rect.width) : rect.width) / width;\n let y = ($ ? round(rect.height) : rect.height) / height;\n\n // 0, NaN, or Infinity should always fallback to 1.\n\n if (!x || !Number.isFinite(x)) {\n x = 1;\n }\n if (!y || !Number.isFinite(y)) {\n y = 1;\n }\n return {\n x,\n y\n };\n}\n\nconst noOffsets = /*#__PURE__*/createCoords(0);\nfunction getVisualOffsets(element) {\n const win = getWindow(element);\n if (!isWebKit() || !win.visualViewport) {\n return noOffsets;\n }\n return {\n x: win.visualViewport.offsetLeft,\n y: win.visualViewport.offsetTop\n };\n}\nfunction shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n if (!floatingOffsetParent || isFixed && floatingOffsetParent !== getWindow(element)) {\n return false;\n }\n return isFixed;\n}\n\nfunction getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n if (isFixedStrategy === void 0) {\n isFixedStrategy = false;\n }\n const clientRect = element.getBoundingClientRect();\n const domElement = unwrapElement(element);\n let scale = createCoords(1);\n if (includeScale) {\n if (offsetParent) {\n if (isElement(offsetParent)) {\n scale = getScale(offsetParent);\n }\n } else {\n scale = getScale(element);\n }\n }\n const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0);\n let x = (clientRect.left + visualOffsets.x) / scale.x;\n let y = (clientRect.top + visualOffsets.y) / scale.y;\n let width = clientRect.width / scale.x;\n let height = clientRect.height / scale.y;\n if (domElement) {\n const win = getWindow(domElement);\n const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;\n let currentWin = win;\n let currentIFrame = getFrameElement(currentWin);\n while (currentIFrame && offsetParent && offsetWin !== currentWin) {\n const iframeScale = getScale(currentIFrame);\n const iframeRect = currentIFrame.getBoundingClientRect();\n const css = getComputedStyle$1(currentIFrame);\n const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;\n const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;\n x *= iframeScale.x;\n y *= iframeScale.y;\n width *= iframeScale.x;\n height *= iframeScale.y;\n x += left;\n y += top;\n currentWin = getWindow(currentIFrame);\n currentIFrame = getFrameElement(currentWin);\n }\n }\n return rectToClientRect({\n width,\n height,\n x,\n y\n });\n}\n\n// If <html> has a CSS width greater than the viewport, then this will be\n// incorrect for RTL.\nfunction getWindowScrollBarX(element, rect) {\n const leftScroll = getNodeScroll(element).scrollLeft;\n if (!rect) {\n return getBoundingClientRect(getDocumentElement(element)).left + leftScroll;\n }\n return rect.left + leftScroll;\n}\n\nfunction getHTMLOffset(documentElement, scroll) {\n const htmlRect = documentElement.getBoundingClientRect();\n const x = htmlRect.left + scroll.scrollLeft - getWindowScrollBarX(documentElement, htmlRect);\n const y = htmlRect.top + scroll.scrollTop;\n return {\n x,\n y\n };\n}\n\nfunction convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {\n let {\n elements,\n rect,\n offsetParent,\n strategy\n } = _ref;\n const isFixed = strategy === 'fixed';\n const documentElement = getDocumentElement(offsetParent);\n const topLayer = elements ? isTopLayer(elements.floating) : false;\n if (offsetParent === documentElement || topLayer && isFixed) {\n return rect;\n }\n let scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n let scale = createCoords(1);\n const offsets = createCoords(0);\n const isOffsetParentAnElement = isHTMLElement(offsetParent);\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n if (isOffsetParentAnElement) {\n const offsetRect = getBoundingClientRect(offsetParent);\n scale = getScale(offsetParent);\n offsets.x = offsetRect.x + offsetParent.clientLeft;\n offsets.y = offsetRect.y + offsetParent.clientTop;\n }\n }\n const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);\n return {\n width: rect.width * scale.x,\n height: rect.height * scale.y,\n x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x + htmlOffset.x,\n y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y + htmlOffset.y\n };\n}\n\nfunction getClientRects(element) {\n return Array.from(element.getClientRects());\n}\n\n// Gets the entire size of the scrollable document area, even extending outside\n// of the `<html>` and `<body>` rect bounds if horizontally scrollable.\nfunction getDocumentRect(element) {\n const html = getDocumentElement(element);\n const scroll = getNodeScroll(element);\n const body = element.ownerDocument.body;\n const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);\n const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);\n let x = -scroll.scrollLeft + getWindowScrollBarX(element);\n const y = -scroll.scrollTop;\n if (getComputedStyle$1(body).direction === 'rtl') {\n x += max(html.clientWidth, body.clientWidth) - width;\n }\n return {\n width,\n height,\n x,\n y\n };\n}\n\n// Safety check: ensure the scrollbar space is reasonable in case this\n// calculation is affected by unusual styles.\n// Most scrollbars leave 15-18px of space.\nconst SCROLLBAR_MAX = 25;\nfunction getViewportRect(element, strategy) {\n const win = getWindow(element);\n const html = getDocumentElement(element);\n const visualViewport = win.visualViewport;\n let width = html.clientWidth;\n let height = html.clientHeight;\n let x = 0;\n let y = 0;\n if (visualViewport) {\n width = visualViewport.width;\n height = visualViewport.height;\n const visualViewportBased = isWebKit();\n if (!visualViewportBased || visualViewportBased && strategy === 'fixed') {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n const windowScrollbarX = getWindowScrollBarX(html);\n // <html> `overflow: hidden` + `scrollbar-gutter: stable` reduces the\n // visual width of the <html> but this is not considered in the size\n // of `html.clientWidth`.\n if (windowScrollbarX <= 0) {\n const doc = html.ownerDocument;\n const body = doc.body;\n const bodyStyles = getComputedStyle(body);\n const bodyMarginInline = doc.compatMode === 'CSS1Compat' ? parseFloat(bodyStyles.marginLeft) + parseFloat(bodyStyles.marginRight) || 0 : 0;\n const clippingStableScrollbarWidth = Math.abs(html.clientWidth - body.clientWidth - bodyMarginInline);\n if (clippingStableScrollbarWidth <= SCROLLBAR_MAX) {\n width -= clippingStableScrollbarWidth;\n }\n } else if (windowScrollbarX <= SCROLLBAR_MAX) {\n // If the <body> scrollbar is on the left, the width needs to be extended\n // by the scrollbar amount so there isn't extra space on the right.\n width += windowScrollbarX;\n }\n return {\n width,\n height,\n x,\n y\n };\n}\n\n// Returns the inner client rect, subtracting scrollbars if present.\nfunction getInnerBoundingClientRect(element, strategy) {\n const clientRect = getBoundingClientRect(element, true, strategy === 'fixed');\n const top = clientRect.top + element.clientTop;\n const left = clientRect.left + element.clientLeft;\n const scale = isHTMLElement(element) ? getScale(element) : createCoords(1);\n const width = element.clientWidth * scale.x;\n const height = element.clientHeight * scale.y;\n const x = left * scale.x;\n const y = top * scale.y;\n return {\n width,\n height,\n x,\n y\n };\n}\nfunction getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {\n let rect;\n if (clippingAncestor === 'viewport') {\n rect = getViewportRect(element, strategy);\n } else if (clippingAncestor === 'document') {\n rect = getDocumentRect(getDocumentElement(element));\n } else if (isElement(clippingAncestor)) {\n rect = getInnerBoundingClientRect(clippingAncestor, strategy);\n } else {\n const visualOffsets = getVisualOffsets(element);\n rect = {\n x: clippingAncestor.x - visualOffsets.x,\n y: clippingAncestor.y - visualOffsets.y,\n width: clippingAncestor.width,\n height: clippingAncestor.height\n };\n }\n return rectToClientRect(rect);\n}\nfunction hasFixedPositionAncestor(element, stopNode) {\n const parentNode = getParentNode(element);\n if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) {\n return false;\n }\n return getComputedStyle$1(parentNode).position === 'fixed' || hasFixedPositionAncestor(parentNode, stopNode);\n}\n\n// A \"clipping ancestor\" is an `overflow` element with the characteristic of\n// clipping (or hiding) child elements. This returns all clipping ancestors\n// of the given element up the tree.\nfunction getClippingElementAncestors(element, cache) {\n const cachedResult = cache.get(element);\n if (cachedResult) {\n return cachedResult;\n }\n let result = getOverflowAncestors(element, [], false).filter(el => isElement(el) && getNodeName(el) !== 'body');\n let currentContainingBlockComputedStyle = null;\n const elementIsFixed = getComputedStyle$1(element).position === 'fixed';\n let currentNode = elementIsFixed ? getParentNode(element) : element;\n\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {\n const computedStyle = getComputedStyle$1(currentNode);\n const currentNodeIsContaining = isContainingBlock(currentNode);\n if (!currentNodeIsContaining && computedStyle.position === 'fixed') {\n currentContainingBlockComputedStyle = null;\n }\n const shouldDropCurrentNode = elementIsFixed ? !currentNodeIsContaining && !currentContainingBlockComputedStyle : !currentNodeIsContaining && computedStyle.position === 'static' && !!currentContainingBlockComputedStyle && (currentContainingBlockComputedStyle.position === 'absolute' || currentContainingBlockComputedStyle.position === 'fixed') || isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode);\n if (shouldDropCurrentNode) {\n // Drop non-containing blocks.\n result = result.filter(ancestor => ancestor !== currentNode);\n } else {\n // Record last containing block for next iteration.\n currentContainingBlockComputedStyle = computedStyle;\n }\n currentNode = getParentNode(currentNode);\n }\n cache.set(element, result);\n return result;\n}\n\n// Gets the maximum area that the element is visible in due to any number of\n// clipping ancestors.\nfunction getClippingRect(_ref) {\n let {\n element,\n boundary,\n rootBoundary,\n strategy\n } = _ref;\n const elementClippingAncestors = boundary === 'clippingAncestors' ? isTopLayer(element) ? [] : getClippingElementAncestors(element, this._c) : [].concat(boundary);\n const clippingAncestors = [...elementClippingAncestors, rootBoundary];\n const firstRect = getClientRectFromClippingAncestor(element, clippingAncestors[0], strategy);\n let top = firstRect.top;\n let right = firstRect.right;\n let bottom = firstRect.bottom;\n let left = firstRect.left;\n for (let i = 1; i < clippingAncestors.length; i++) {\n const rect = getClientRectFromClippingAncestor(element, clippingAncestors[i], strategy);\n top = max(rect.top, top);\n right = min(rect.right, right);\n bottom = min(rect.bottom, bottom);\n left = max(rect.left, left);\n }\n return {\n width: right - left,\n height: bottom - top,\n x: left,\n y: top\n };\n}\n\nfunction getDimensions(element) {\n const {\n width,\n height\n } = getCssDimensions(element);\n return {\n width,\n height\n };\n}\n\nfunction getRectRelativeToOffsetParent(element, offsetParent, strategy) {\n const isOffsetParentAnElement = isHTMLElement(offsetParent);\n const documentElement = getDocumentElement(offsetParent);\n const isFixed = strategy === 'fixed';\n const rect = getBoundingClientRect(element, true, isFixed, offsetParent);\n let scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n const offsets = createCoords(0);\n\n // If the <body> scrollbar appears on the left (e.g. RTL systems). Use\n // Firefox with layout.scrollbar.side = 3 in about:config to test this.\n function setLeftRTLScrollbarOffset() {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || isOverflowElement(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n if (isOffsetParentAnElement) {\n const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent);\n offsets.x = offsetRect.x + offsetParent.clientLeft;\n offsets.y = offsetRect.y + offsetParent.clientTop;\n } else if (documentElement) {\n setLeftRTLScrollbarOffset();\n }\n }\n if (isFixed && !isOffsetParentAnElement && documentElement) {\n setLeftRTLScrollbarOffset();\n }\n const htmlOffset = documentElement && !isOffsetParentAnElement && !isFixed ? getHTMLOffset(documentElement, scroll) : createCoords(0);\n const x = rect.left + scroll.scrollLeft - offsets.x - htmlOffset.x;\n const y = rect.top + scroll.scrollTop - offsets.y - htmlOffset.y;\n return {\n x,\n y,\n width: rect.width,\n height: rect.height\n };\n}\n\nfunction isStaticPositioned(element) {\n return getComputedStyle$1(element).position === 'static';\n}\n\nfunction getTrueOffsetParent(element, polyfill) {\n if (!isHTMLElement(element) || getComputedStyle$1(element).position === 'fixed') {\n return null;\n }\n if (polyfill) {\n return polyfill(element);\n }\n let rawOffsetParent = element.offsetParent;\n\n // Firefox returns the <html> element as the offsetParent if it's non-static,\n // while Chrome and Safari return the <body> element. The <body> element must\n // be used to perform the correct calculations even if the <html> element is\n // non-static.\n if (getDocumentElement(element) === rawOffsetParent) {\n rawOffsetParent = rawOffsetParent.ownerDocument.body;\n }\n return rawOffsetParent;\n}\n\n// Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\nfunction getOffsetParent(element, polyfill) {\n const win = getWindow(element);\n if (isTopLayer(element)) {\n return win;\n }\n if (!isHTMLElement(element)) {\n let svgOffsetParent = getParentNode(element);\n while (svgOffsetParent && !isLastTraversableNode(svgOffsetParent)) {\n if (isElement(svgOffsetParent) && !isStaticPositioned(svgOffsetParent)) {\n return svgOffsetParent;\n }\n svgOffsetParent = getParentNode(svgOffsetParent);\n }\n return win;\n }\n let offsetParent = getTrueOffsetParent(element, polyfill);\n while (offsetParent && isTableElement(offsetParent) && isStaticPositioned(offsetParent)) {\n offsetParent = getTrueOffsetParent(offsetParent, polyfill);\n }\n if (offsetParent && isLastTraversableNode(offsetParent) && isStaticPositioned(offsetParent) && !isContainingBlock(offsetParent)) {\n return win;\n }\n return offsetParent || getContainingBlock(element) || win;\n}\n\nconst getElementRects = async function (data) {\n const getOffsetParentFn = this.getOffsetParent || getOffsetParent;\n const getDimensionsFn = this.getDimensions;\n const floatingDimensions = await getDimensionsFn(data.floating);\n return {\n reference: getRectRelativeToOffsetParent(data.reference, await getOffsetParentFn(data.floating), data.strategy),\n floating: {\n x: 0,\n y: 0,\n width: floatingDimensions.width,\n height: floatingDimensions.height\n }\n };\n};\n\nfunction isRTL(element) {\n return getComputedStyle$1(element).direction === 'rtl';\n}\n\nconst platform = {\n convertOffsetParentRelativeRectToViewportRelativeRect,\n getDocumentElement,\n getClippingRect,\n getOffsetParent,\n getElementRects,\n getClientRects,\n getDimensions,\n getScale,\n isElement,\n isRTL\n};\n\nfunction rectsAreEqual(a, b) {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n\n// https://samthor.au/2021/observing-dom/\nfunction observeMove(element, onMove) {\n let io = null;\n let timeoutId;\n const root = getDocumentElement(element);\n function cleanup() {\n var _io;\n clearTimeout(timeoutId);\n (_io = io) == null || _io.disconnect();\n io = null;\n }\n function refresh(skip, threshold) {\n if (skip === void 0) {\n skip = false;\n }\n if (threshold === void 0) {\n threshold = 1;\n }\n cleanup();\n const elementRectForRootMargin = element.getBoundingClientRect();\n const {\n left,\n top,\n width,\n height\n } = elementRectForRootMargin;\n if (!skip) {\n onMove();\n }\n if (!width || !height) {\n return;\n }\n const insetTop = floor(top);\n const insetRight = floor(root.clientWidth - (left + width));\n const insetBottom = floor(root.clientHeight - (top + height));\n const insetLeft = floor(left);\n const rootMargin = -insetTop + \"px \" + -insetRight + \"px \" + -insetBottom + \"px \" + -insetLeft + \"px\";\n const options = {\n rootMargin,\n threshold: max(0, min(1, threshold)) || 1\n };\n let isFirstUpdate = true;\n function handleObserve(entries) {\n const ratio = entries[0].intersectionRatio;\n if (ratio !== threshold) {\n if (!isFirstUpdate) {\n return refresh();\n }\n if (!ratio) {\n // If the reference is clipped, the ratio is 0. Throttle the refresh\n // to prevent an infinite loop of updates.\n timeoutId = setTimeout(() => {\n refresh(false, 1e-7);\n }, 1000);\n } else {\n refresh(false, ratio);\n }\n }\n if (ratio === 1 && !rectsAreEqual(elementRectForRootMargin, element.getBoundingClientRect())) {\n // It's possible that even though the ratio is reported as 1, the\n // element is not actually fully within the IntersectionObserver's root\n // area anymore. This can happen under performance constraints. This may\n // be a bug in the browser's IntersectionObserver implementation. To\n // work around this, we compare the element's bounding rect now with\n // what it was at the time we created the IntersectionObserver. If they\n // are not equal then the element moved, so we refresh.\n refresh();\n }\n isFirstUpdate = false;\n }\n\n // Older browsers don't support a `document` as the root and will throw an\n // error.\n try {\n io = new IntersectionObserver(handleObserve, {\n ...options,\n // Handle <iframe>s\n root: root.ownerDocument\n });\n } catch (_e) {\n io = new IntersectionObserver(handleObserve, options);\n }\n io.observe(element);\n }\n refresh(true);\n return cleanup;\n}\n\n/**\n * Automatically updates the position of the floating element when necessary.\n * Should only be called when the floating element is mounted on the DOM or\n * visible on the screen.\n * @returns cleanup function that should be invoked when the floating element is\n * removed from the DOM or hidden from the screen.\n * @see https://floating-ui.com/docs/autoUpdate\n */\nfunction autoUpdate(reference, floating, update, options) {\n if (options === void 0) {\n options = {};\n }\n const {\n ancestorScroll = true,\n ancestorResize = true,\n elementResize = typeof ResizeObserver === 'function',\n layoutShift = typeof IntersectionObserver === 'function',\n animationFrame = false\n } = options;\n const referenceEl = unwrapElement(reference);\n const ancestors = ancestorScroll || ancestorResize ? [...(referenceEl ? getOverflowAncestors(referenceEl) : []), ...(floating ? getOverflowAncestors(floating) : [])] : [];\n ancestors.forEach(ancestor => {\n ancestorScroll && ancestor.addEventListener('scroll', update, {\n passive: true\n });\n ancestorResize && ancestor.addEventListener('resize', update);\n });\n const cleanupIo = referenceEl && layoutShift ? observeMove(referenceEl, update) : null;\n let reobserveFrame = -1;\n let resizeObserver = null;\n if (elementResize) {\n resizeObserver = new ResizeObserver(_ref => {\n let [firstEntry] = _ref;\n if (firstEntry && firstEntry.target === referenceEl && resizeObserver && floating) {\n // Prevent update loops when using the `size` middleware.\n // https://github.com/floating-ui/floating-ui/issues/1740\n resizeObserver.unobserve(floating);\n cancelAnimationFrame(reobserveFrame);\n reobserveFrame = requestAnimationFrame(() => {\n var _resizeObserver;\n (_resizeObserver = resizeObserver) == null || _resizeObserver.observe(floating);\n });\n }\n update();\n });\n if (referenceEl && !animationFrame) {\n resizeObserver.observe(referenceEl);\n }\n if (floating) {\n resizeObserver.observe(floating);\n }\n }\n let frameId;\n let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;\n if (animationFrame) {\n frameLoop();\n }\n function frameLoop() {\n const nextRefRect = getBoundingClientRect(reference);\n if (prevRefRect && !rectsAreEqual(prevRefRect, nextRefRect)) {\n update();\n }\n prevRefRect = nextRefRect;\n frameId = requestAnimationFrame(frameLoop);\n }\n update();\n return () => {\n var _resizeObserver2;\n ancestors.forEach(ancestor => {\n ancestorScroll && ancestor.removeEventListener('scroll', update);\n ancestorResize && ancestor.removeEventListener('resize', update);\n });\n cleanupIo == null || cleanupIo();\n (_resizeObserver2 = resizeObserver) == null || _resizeObserver2.disconnect();\n resizeObserver = null;\n if (animationFrame) {\n cancelAnimationFrame(frameId);\n }\n };\n}\n\n/**\n * Resolves with an object of overflow side offsets that determine how much the\n * element is overflowing a given clipping boundary on each side.\n * - positive = overflowing the boundary by that number of pixels\n * - negative = how many pixels left before it will overflow\n * - 0 = lies flush with the boundary\n * @see https://floating-ui.com/docs/detectOverflow\n */\nconst detectOverflow = detectOverflow$1;\n\n/**\n * Modifies the placement by translating the floating element along the\n * specified axes.\n * A number (shorthand for `mainAxis` or distance), or an axes configuration\n * object may be passed.\n * @see https://floating-ui.com/docs/offset\n */\nconst offset = offset$1;\n\n/**\n * Optimizes the visibility of the floating element by choosing the placement\n * that has the most space available automatically, without needing to specify a\n * preferred placement. Alternative to `flip`.\n * @see https://floating-ui.com/docs/autoPlacement\n */\nconst autoPlacement = autoPlacement$1;\n\n/**\n * Optimizes the visibility of the floating element by shifting it in order to\n * keep it in view when it will overflow the clipping boundary.\n * @see https://floating-ui.com/docs/shift\n */\nconst shift = shift$1;\n\n/**\n * Optimizes the visibility of the floating element by flipping the `placement`\n * in order to keep it in view when the preferred placement(s) will overflow the\n * clipping boundary. Alternative to `autoPlacement`.\n * @see https://floating-ui.com/docs/flip\n */\nconst flip = flip$1;\n\n/**\n * Provides data that allows you to change the size of the floating element —\n * for instance, prevent it from overflowing the clipping boundary or match the\n * width of the reference element.\n * @see https://floating-ui.com/docs/size\n */\nconst size = size$1;\n\n/**\n * Provides data to hide the floating element in applicable situations, such as\n * when it is not in the same clipping context as the reference element.\n * @see https://floating-ui.com/docs/hide\n */\nconst hide = hide$1;\n\n/**\n * Provides data to position an inner element of the floating element so that it\n * appears centered to the reference element.\n * @see https://floating-ui.com/docs/arrow\n */\nconst arrow = arrow$1;\n\n/**\n * Provides improved positioning for inline reference elements that can span\n * over multiple lines, such as hyperlinks or range selections.\n * @see https://floating-ui.com/docs/inline\n */\nconst inline = inline$1;\n\n/**\n * Built-in `limiter` that will stop `shift()` at a certain point.\n */\nconst limitShift = limitShift$1;\n\n/**\n * Computes the `x` and `y` coordinates that will place the floating element\n * next to a given reference element.\n */\nconst computePosition = (reference, floating, options) => {\n // This caches the expensive `getClippingElementAncestors` function so that\n // multiple lifecycle resets re-use the same result. It only lives for a\n // single call. If other functions become expensive, we can add them as well.\n const cache = new Map();\n const mergedOptions = {\n platform,\n ...options\n };\n const platformWithCache = {\n ...mergedOptions.platform,\n _c: cache\n };\n return computePosition$1(reference, floating, {\n ...mergedOptions,\n platform: platformWithCache\n });\n};\n\nexport { arrow, autoPlacement, autoUpdate, computePosition, detectOverflow, flip, hide, inline, limitShift, offset, platform, shift, size };\n","<script setup lang=\"ts\">\n/** Floating tooltip using @floating-ui with auto-flip, delay, optional keyboard shortcut badge, and body Teleport. */\nimport { ref, onBeforeUnmount, nextTick } from 'vue'\nimport { computePosition, flip, offset, shift, type Placement } from '@floating-ui/dom'\nimport { useEventListener } from '../composables/useEventListener'\n\ninterface Props {\n text: string\n position?: 'top' | 'bottom' | 'left' | 'right'\n delay?: number\n shortcut?: string\n maxWidth?: string | number\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n position: 'top',\n delay: 200,\n shortcut: undefined,\n maxWidth: undefined,\n})\n\nconst tooltipId = `mint-tooltip-${Math.random().toString(36).slice(2, 9)}`\nconst visible = ref(false)\nconst actualPlacement = ref<Placement>(props.position)\nconst triggerRef = ref<HTMLElement | null>(null)\nconst contentRef = ref<HTMLElement | null>(null)\nconst coords = ref({ top: '0px', left: '0px' })\nlet timeoutId: ReturnType<typeof setTimeout> | undefined\n\nasync function updatePosition() {\n if (!triggerRef.value || !contentRef.value) return\n\n const { x, y, placement } = await computePosition(triggerRef.value, contentRef.value, {\n placement: props.position,\n middleware: [\n offset(6),\n flip(),\n shift({ padding: 8 }),\n ],\n })\n\n coords.value = { top: `${y}px`, left: `${x}px` }\n actualPlacement.value = placement\n}\n\nuseEventListener(() => window, 'scroll', updatePosition, {\n passive: true,\n capture: true,\n enabled: visible,\n})\nuseEventListener(() => window, 'resize', updatePosition, { enabled: visible })\n\nasync function show() {\n timeoutId = setTimeout(async () => {\n visible.value = true\n await nextTick()\n await updatePosition()\n }, props.delay)\n}\n\nfunction hide() {\n clearTimeout(timeoutId)\n timeoutId = undefined\n visible.value = false\n}\n\nonBeforeUnmount(() => {\n clearTimeout(timeoutId)\n})\n\nfunction resolvedMaxWidth(value: string | number | undefined): string | undefined {\n if (value === undefined) return undefined\n return typeof value === 'number' ? `${value}px` : value\n}\n</script>\n\n<template>\n <span\n ref=\"triggerRef\"\n class=\"mint-tooltip\"\n :aria-describedby=\"visible ? tooltipId : undefined\"\n @mouseenter=\"show\"\n @mouseleave=\"hide\"\n @focusin=\"show\"\n @focusout=\"hide\"\n >\n <slot />\n <Teleport to=\"body\">\n <span\n v-if=\"visible\"\n :id=\"tooltipId\"\n ref=\"contentRef\"\n :class=\"[\n 'mint-tooltip__content',\n `mint-tooltip__content--${actualPlacement}`,\n maxWidth ? 'mint-tooltip__content--multiline' : '',\n ]\"\n :style=\"{\n top: coords.top,\n left: coords.left,\n maxWidth: resolvedMaxWidth(maxWidth),\n }\"\n role=\"tooltip\"\n >\n <span class=\"mint-tooltip__body\">{{ text }}</span>\n <kbd v-if=\"shortcut\" class=\"mint-tooltip__shortcut\">{{ shortcut }}</kbd>\n </span>\n </Teleport>\n </span>\n</template>\n\n<style>\n@import '../styles/components/tooltip.css';\n</style>\n","<script setup lang=\"ts\">\n/** Floating tooltip using @floating-ui with auto-flip, delay, optional keyboard shortcut badge, and body Teleport. */\nimport { ref, onBeforeUnmount, nextTick } from 'vue'\nimport { computePosition, flip, offset, shift, type Placement } from '@floating-ui/dom'\nimport { useEventListener } from '../composables/useEventListener'\n\ninterface Props {\n text: string\n position?: 'top' | 'bottom' | 'left' | 'right'\n delay?: number\n shortcut?: string\n maxWidth?: string | number\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n position: 'top',\n delay: 200,\n shortcut: undefined,\n maxWidth: undefined,\n})\n\nconst tooltipId = `mint-tooltip-${Math.random().toString(36).slice(2, 9)}`\nconst visible = ref(false)\nconst actualPlacement = ref<Placement>(props.position)\nconst triggerRef = ref<HTMLElement | null>(null)\nconst contentRef = ref<HTMLElement | null>(null)\nconst coords = ref({ top: '0px', left: '0px' })\nlet timeoutId: ReturnType<typeof setTimeout> | undefined\n\nasync function updatePosition() {\n if (!triggerRef.value || !contentRef.value) return\n\n const { x, y, placement } = await computePosition(triggerRef.value, contentRef.value, {\n placement: props.position,\n middleware: [\n offset(6),\n flip(),\n shift({ padding: 8 }),\n ],\n })\n\n coords.value = { top: `${y}px`, left: `${x}px` }\n actualPlacement.value = placement\n}\n\nuseEventListener(() => window, 'scroll', updatePosition, {\n passive: true,\n capture: true,\n enabled: visible,\n})\nuseEventListener(() => window, 'resize', updatePosition, { enabled: visible })\n\nasync function show() {\n timeoutId = setTimeout(async () => {\n visible.value = true\n await nextTick()\n await updatePosition()\n }, props.delay)\n}\n\nfunction hide() {\n clearTimeout(timeoutId)\n timeoutId = undefined\n visible.value = false\n}\n\nonBeforeUnmount(() => {\n clearTimeout(timeoutId)\n})\n\nfunction resolvedMaxWidth(value: string | number | undefined): string | undefined {\n if (value === undefined) return undefined\n return typeof value === 'number' ? `${value}px` : value\n}\n</script>\n\n<template>\n <span\n ref=\"triggerRef\"\n class=\"mint-tooltip\"\n :aria-describedby=\"visible ? tooltipId : undefined\"\n @mouseenter=\"show\"\n @mouseleave=\"hide\"\n @focusin=\"show\"\n @focusout=\"hide\"\n >\n <slot />\n <Teleport to=\"body\">\n <span\n v-if=\"visible\"\n :id=\"tooltipId\"\n ref=\"contentRef\"\n :class=\"[\n 'mint-tooltip__content',\n `mint-tooltip__content--${actualPlacement}`,\n maxWidth ? 'mint-tooltip__content--multiline' : '',\n ]\"\n :style=\"{\n top: coords.top,\n left: coords.left,\n maxWidth: resolvedMaxWidth(maxWidth),\n }\"\n role=\"tooltip\"\n >\n <span class=\"mint-tooltip__body\">{{ text }}</span>\n <kbd v-if=\"shortcut\" class=\"mint-tooltip__shortcut\">{{ shortcut }}</kbd>\n </span>\n </Teleport>\n </span>\n</template>\n\n<style>\n@import '../styles/components/tooltip.css';\n</style>\n","<script setup lang=\"ts\">\n/** Wraps a chart with header, toolbar slot, loading overlay, empty state, and legend slot. */\nimport LoadingSpinner from './LoadingSpinner.vue'\nimport EmptyState from './EmptyState.vue'\n\ninterface Props {\n title?: string\n description?: string\n loading?: boolean\n empty?: boolean\n emptyMessage?: string\n}\n\nwithDefaults(defineProps<Props>(), {\n loading: false,\n empty: false,\n emptyMessage: 'No data available',\n})\n</script>\n\n<template>\n <div class=\"mint-chart\">\n <div v-if=\"title || description || $slots.toolbar\" class=\"mint-chart__header\">\n <div v-if=\"title || description\" class=\"mint-chart__header-text\">\n <h3 v-if=\"title\" class=\"mint-chart__title\">{{ title }}</h3>\n <p v-if=\"description\" class=\"mint-chart__description\">{{ description }}</p>\n </div>\n <div v-if=\"$slots.toolbar\" class=\"mint-chart__toolbar\">\n <slot name=\"toolbar\" />\n </div>\n </div>\n\n <div class=\"mint-chart__body\">\n <!-- Loading overlay -->\n <div v-if=\"loading\" class=\"mint-chart__loading-overlay\">\n <LoadingSpinner size=\"lg\" />\n </div>\n\n <!-- Empty state -->\n <template v-else-if=\"empty\">\n <slot name=\"empty\">\n <EmptyState\n :title=\"emptyMessage\"\n size=\"sm\"\n icon-path=\"M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z\"\n />\n </slot>\n </template>\n\n <!-- Chart content -->\n <template v-else>\n <slot />\n </template>\n </div>\n\n <div v-if=\"$slots.legend\" class=\"mint-chart__legend\">\n <slot name=\"legend\" />\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/chart-container.css';\n</style>\n","<script setup lang=\"ts\">\n/** Wraps a chart with header, toolbar slot, loading overlay, empty state, and legend slot. */\nimport LoadingSpinner from './LoadingSpinner.vue'\nimport EmptyState from './EmptyState.vue'\n\ninterface Props {\n title?: string\n description?: string\n loading?: boolean\n empty?: boolean\n emptyMessage?: string\n}\n\nwithDefaults(defineProps<Props>(), {\n loading: false,\n empty: false,\n emptyMessage: 'No data available',\n})\n</script>\n\n<template>\n <div class=\"mint-chart\">\n <div v-if=\"title || description || $slots.toolbar\" class=\"mint-chart__header\">\n <div v-if=\"title || description\" class=\"mint-chart__header-text\">\n <h3 v-if=\"title\" class=\"mint-chart__title\">{{ title }}</h3>\n <p v-if=\"description\" class=\"mint-chart__description\">{{ description }}</p>\n </div>\n <div v-if=\"$slots.toolbar\" class=\"mint-chart__toolbar\">\n <slot name=\"toolbar\" />\n </div>\n </div>\n\n <div class=\"mint-chart__body\">\n <!-- Loading overlay -->\n <div v-if=\"loading\" class=\"mint-chart__loading-overlay\">\n <LoadingSpinner size=\"lg\" />\n </div>\n\n <!-- Empty state -->\n <template v-else-if=\"empty\">\n <slot name=\"empty\">\n <EmptyState\n :title=\"emptyMessage\"\n size=\"sm\"\n icon-path=\"M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z\"\n />\n </slot>\n </template>\n\n <!-- Chart content -->\n <template v-else>\n <slot />\n </template>\n </div>\n\n <div v-if=\"$slots.legend\" class=\"mint-chart__legend\">\n <slot name=\"legend\" />\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/chart-container.css';\n</style>\n","<script setup lang=\"ts\">\n/** Render biology template envelopes with matching SDK components such as WellPlate, DoseCalculator, and DataFrame. */\nimport { computed } from 'vue'\nimport {\n toBioTemplateComponentBindings,\n} from '../templates/componentBindings'\nimport type {\n BioTemplateEnvelope,\n TemplateCollectionEnvelope,\n} from '../templates/types'\nimport ComponentBindingRenderer from './ComponentBindingRenderer.vue'\n\ninterface Props {\n /** Template envelope or template collection to render. */\n target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope\n /** Optional allow-list of component names, for example ['WellPlate', 'DataFrame']. */\n include?: string[]\n /** Optional deny-list of component names. */\n exclude?: string[]\n /** Compact child component sizing. */\n dense?: boolean\n /** Prefer preview-safe props for editable components. */\n readonly?: boolean\n /** Show component/template labels above each rendered component. */\n showHeaders?: boolean\n /** Show binding descriptions in each header. */\n showDescriptions?: boolean\n /** Message shown when the target has no renderable bindings. */\n emptyText?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n include: () => [],\n exclude: () => [],\n dense: false,\n readonly: true,\n showHeaders: true,\n showDescriptions: true,\n emptyText: 'No renderable biology template components.',\n})\n\nconst componentBindings = computed(() => toBioTemplateComponentBindings(props.target))\n</script>\n\n<template>\n <ComponentBindingRenderer\n class=\"mint-bio-template-renderer\"\n :bindings=\"componentBindings\"\n :include=\"include\"\n :exclude=\"exclude\"\n :dense=\"dense\"\n :readonly=\"readonly\"\n :show-headers=\"showHeaders\"\n :show-descriptions=\"showDescriptions\"\n :empty-text=\"emptyText\"\n />\n</template>\n","<script setup lang=\"ts\">\n/** Render biology template envelopes with matching SDK components such as WellPlate, DoseCalculator, and DataFrame. */\nimport { computed } from 'vue'\nimport {\n toBioTemplateComponentBindings,\n} from '../templates/componentBindings'\nimport type {\n BioTemplateEnvelope,\n TemplateCollectionEnvelope,\n} from '../templates/types'\nimport ComponentBindingRenderer from './ComponentBindingRenderer.vue'\n\ninterface Props {\n /** Template envelope or template collection to render. */\n target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope\n /** Optional allow-list of component names, for example ['WellPlate', 'DataFrame']. */\n include?: string[]\n /** Optional deny-list of component names. */\n exclude?: string[]\n /** Compact child component sizing. */\n dense?: boolean\n /** Prefer preview-safe props for editable components. */\n readonly?: boolean\n /** Show component/template labels above each rendered component. */\n showHeaders?: boolean\n /** Show binding descriptions in each header. */\n showDescriptions?: boolean\n /** Message shown when the target has no renderable bindings. */\n emptyText?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n include: () => [],\n exclude: () => [],\n dense: false,\n readonly: true,\n showHeaders: true,\n showDescriptions: true,\n emptyText: 'No renderable biology template components.',\n})\n\nconst componentBindings = computed(() => toBioTemplateComponentBindings(props.target))\n</script>\n\n<template>\n <ComponentBindingRenderer\n class=\"mint-bio-template-renderer\"\n :bindings=\"componentBindings\"\n :include=\"include\"\n :exclude=\"exclude\"\n :dense=\"dense\"\n :readonly=\"readonly\"\n :show-headers=\"showHeaders\"\n :show-descriptions=\"showDescriptions\"\n :empty-text=\"emptyText\"\n />\n</template>\n","<script setup lang=\"ts\">\n/** Current-experiment template workspace with load/save/reset controls and WellPlate/DataFrame/DoseCalculator previews. */\nimport { computed } from 'vue'\nimport {\n extractTemplateCollection,\n getBioTemplateComponentProps,\n toBioTemplateComponentBindingsById,\n toBioTemplateComponentProps,\n toBioTemplateComponentPropsByComponent,\n toBioTemplateComponentPropsById,\n type BioTemplateEnvelope,\n type BioTemplateComponentPropsLookupOptions,\n type TemplateCollectionEnvelope,\n} from '../templates'\nimport AlertBox from './AlertBox.vue'\nimport BaseButton from './BaseButton.vue'\nimport BioTemplateRenderer from './BioTemplateRenderer.vue'\n\ntype BioTemplateWorkspaceTarget = BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope\ntype WorkspaceKind = 'template' | 'collection'\n\ninterface TemplateWorkspaceStatus {\n loading?: boolean\n saving?: boolean\n error?: string | null\n hasExperiment?: boolean\n currentExperimentId?: number\n lastLoadedAt?: Date | null\n lastSavedAt?: Date | null\n}\n\ninterface TemplateWorkspaceActions {\n load?: () => unknown\n reset?: () => unknown\n save?: () => unknown\n}\n\ninterface Props {\n /** Template envelope or template collection shown in BioTemplateRenderer. */\n target: BioTemplateWorkspaceTarget\n /** Human-readable workspace label. */\n label: string\n /** Grouped request and current-experiment state for generated plugin pages. */\n status?: TemplateWorkspaceStatus\n /** Grouped load/reset/save handlers for generated plugin pages. */\n actions?: TemplateWorkspaceActions\n /** Copy shown in the status banner. */\n message?: string\n /** Whether the load/save request is active. */\n loading?: boolean\n /** Current request error. */\n error?: string | null\n /** Whether a current experiment id is available. */\n hasExperiment?: boolean\n /** Current experiment id displayed in the footer. */\n currentExperimentId?: number\n /** Last successful load timestamp. */\n lastLoadedAt?: Date | null\n /** Last successful save timestamp. */\n lastSavedAt?: Date | null\n /** Template vs collection copy defaults. */\n kind?: WorkspaceKind\n /** Compact preview layout. */\n dense?: boolean\n /** Render preview components in read-only mode. */\n readonly?: boolean\n /** Show load/save timestamps. */\n showStatus?: boolean\n /** Show template id/version cards for collection targets. */\n showTemplateSummary?: boolean\n loadLabel?: string\n resetLabel?: string\n saveLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n status: undefined,\n actions: undefined,\n message: undefined,\n loading: false,\n error: null,\n hasExperiment: false,\n currentExperimentId: undefined,\n lastLoadedAt: null,\n lastSavedAt: null,\n kind: 'template',\n dense: true,\n readonly: true,\n showStatus: true,\n showTemplateSummary: false,\n loadLabel: 'Load',\n resetLabel: 'Reset defaults',\n saveLabel: 'Save',\n})\n\nconst emit = defineEmits<{\n load: []\n reset: []\n save: []\n}>()\n\nconst statusMessage = computed(() => {\n if (props.message) return props.message\n if (props.kind === 'collection') {\n return `${props.label} saves the full template collection to the current experiment.`\n }\n return `${props.label} template data saves to experiment design_data.`\n})\n\nconst resolvedLoading = computed(() => {\n if (props.status?.loading !== undefined || props.status?.saving !== undefined) {\n return Boolean(props.status.loading) || Boolean(props.status.saving)\n }\n return props.loading\n})\nconst resolvedError = computed(() =>\n props.status?.error !== undefined ? props.status.error : props.error,\n)\nconst resolvedHasExperiment = computed(() =>\n props.status?.hasExperiment ?? props.hasExperiment,\n)\nconst resolvedCurrentExperimentId = computed(() =>\n props.status?.currentExperimentId ?? props.currentExperimentId,\n)\nconst resolvedLastLoadedAt = computed(() =>\n props.status?.lastLoadedAt !== undefined ? props.status.lastLoadedAt : props.lastLoadedAt,\n)\nconst resolvedLastSavedAt = computed(() =>\n props.status?.lastSavedAt !== undefined ? props.status.lastSavedAt : props.lastSavedAt,\n)\n\nconst templateSummaries = computed(() =>\n Object.values(extractTemplateCollection(props.target)).map(template => ({\n id: String(template.template_id),\n version: String(template.template_version ?? 'unknown'),\n }))\n)\nconst componentProps = computed(() => toBioTemplateComponentProps(props.target))\nconst componentBindingsById = computed(() => toBioTemplateComponentBindingsById(props.target))\nconst componentPropsById = computed(() => toBioTemplateComponentPropsById(props.target))\nconst componentPropsByComponent = computed(() => toBioTemplateComponentPropsByComponent(props.target))\nconst renderer = computed(() => ({ target: props.target }))\n\nfunction getComponentProps(component: string, options?: BioTemplateComponentPropsLookupOptions) {\n return getBioTemplateComponentProps(props.target, component, options)\n}\n\ninterface BioTemplateExperimentWorkspaceBindings {\n renderer: { target: BioTemplateWorkspaceTarget }\n componentProps: ReturnType<typeof toBioTemplateComponentProps>\n componentBindingsById: ReturnType<typeof toBioTemplateComponentBindingsById>\n componentPropsById: ReturnType<typeof toBioTemplateComponentPropsById>\n componentPropsByComponent: ReturnType<typeof toBioTemplateComponentPropsByComponent>\n getComponentProps: typeof getComponentProps\n}\n\nconst bindings = computed<BioTemplateExperimentWorkspaceBindings>(() => ({\n renderer: renderer.value,\n componentProps: componentProps.value,\n componentBindingsById: componentBindingsById.value,\n componentPropsById: componentPropsById.value,\n componentPropsByComponent: componentPropsByComponent.value,\n getComponentProps,\n}))\n\ninterface BioTemplateExperimentWorkspaceSlotProps {\n target: BioTemplateWorkspaceTarget\n bindings: BioTemplateExperimentWorkspaceBindings\n componentProps: ReturnType<typeof toBioTemplateComponentProps>\n componentBindingsById: ReturnType<typeof toBioTemplateComponentBindingsById>\n componentPropsById: ReturnType<typeof toBioTemplateComponentPropsById>\n componentPropsByComponent: ReturnType<typeof toBioTemplateComponentPropsByComponent>\n getComponentProps: typeof getComponentProps\n}\n\ndefineSlots<{\n default?: (props: BioTemplateExperimentWorkspaceSlotProps) => unknown\n}>()\n\nfunction handleLoad() {\n if (props.actions?.load) {\n return props.actions.load()\n }\n emit('load')\n}\n\nfunction handleReset() {\n if (props.actions?.reset) {\n return props.actions.reset()\n }\n emit('reset')\n}\n\nfunction handleSave() {\n if (props.actions?.save) {\n return props.actions.save()\n }\n emit('save')\n}\n</script>\n\n<template>\n <div class=\"mint-bio-template-experiment-workspace\">\n <div class=\"mint-bio-template-experiment-workspace__toolbar\">\n <AlertBox type=\"info\" class=\"mint-bio-template-experiment-workspace__status\">\n {{ statusMessage }}\n </AlertBox>\n\n <div class=\"mint-bio-template-experiment-workspace__actions\">\n <BaseButton\n variant=\"secondary\"\n :loading=\"resolvedLoading\"\n :disabled=\"!resolvedHasExperiment\"\n @click=\"handleLoad\"\n >\n {{ loadLabel }}\n </BaseButton>\n <BaseButton variant=\"secondary\" :loading=\"resolvedLoading\" @click=\"handleReset\">\n {{ resetLabel }}\n </BaseButton>\n <BaseButton\n variant=\"primary\"\n :loading=\"resolvedLoading\"\n :disabled=\"!resolvedHasExperiment\"\n @click=\"handleSave\"\n >\n {{ saveLabel }}\n </BaseButton>\n </div>\n </div>\n\n <AlertBox v-if=\"resolvedError\" type=\"error\">\n {{ resolvedError }}\n </AlertBox>\n\n <p v-if=\"showStatus\" class=\"mint-bio-template-experiment-workspace__meta\">\n loaded: {{ resolvedLastLoadedAt?.toLocaleString() ?? 'not loaded' }} -\n saved: {{ resolvedLastSavedAt?.toLocaleString() ?? 'not saved' }}\n </p>\n\n <div\n v-if=\"showTemplateSummary\"\n class=\"mint-bio-template-experiment-workspace__templates\"\n >\n <div\n v-for=\"template in templateSummaries\"\n :key=\"template.id\"\n class=\"mint-bio-template-experiment-workspace__template\"\n >\n <p class=\"mint-bio-template-experiment-workspace__template-id\">\n {{ template.id }}\n </p>\n <p class=\"mint-bio-template-experiment-workspace__template-version\">\n v{{ template.version }}\n </p>\n </div>\n </div>\n\n <slot\n :target=\"target\"\n :bindings=\"bindings\"\n :component-props=\"componentProps\"\n :component-bindings-by-id=\"componentBindingsById\"\n :component-props-by-id=\"componentPropsById\"\n :component-props-by-component=\"componentPropsByComponent\"\n :get-component-props=\"getComponentProps\"\n >\n <BioTemplateRenderer\n :target=\"target\"\n :dense=\"dense\"\n :readonly=\"readonly\"\n />\n </slot>\n\n <p class=\"mint-bio-template-experiment-workspace__meta\">\n experiment: {{ resolvedCurrentExperimentId ?? 'not selected' }}\n </p>\n </div>\n</template>\n\n<style scoped>\n.mint-bio-template-experiment-workspace {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n min-width: 0;\n}\n\n.mint-bio-template-experiment-workspace__toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n}\n\n.mint-bio-template-experiment-workspace__status {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.mint-bio-template-experiment-workspace__actions {\n display: flex;\n flex: 0 0 auto;\n gap: 0.5rem;\n}\n\n.mint-bio-template-experiment-workspace__meta,\n.mint-bio-template-experiment-workspace__template-version {\n margin: 0;\n color: var(--text-secondary, #64748b);\n font-family: var(--font-mono, 'Fira Code', monospace);\n font-size: 0.75rem;\n}\n\n.mint-bio-template-experiment-workspace__templates {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));\n gap: 0.75rem;\n}\n\n.mint-bio-template-experiment-workspace__template {\n min-width: 0;\n padding: 1rem;\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 0.5rem;\n background: var(--bg-primary, #ffffff);\n}\n\n.mint-bio-template-experiment-workspace__template-id {\n margin: 0;\n color: var(--text-primary, #0f172a);\n font-family: var(--font-mono, 'Fira Code', monospace);\n font-size: 0.875rem;\n}\n\n@media (max-width: 48rem) {\n .mint-bio-template-experiment-workspace__toolbar,\n .mint-bio-template-experiment-workspace__actions {\n align-items: stretch;\n flex-direction: column;\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/** Current-experiment template workspace with load/save/reset controls and WellPlate/DataFrame/DoseCalculator previews. */\nimport { computed } from 'vue'\nimport {\n extractTemplateCollection,\n getBioTemplateComponentProps,\n toBioTemplateComponentBindingsById,\n toBioTemplateComponentProps,\n toBioTemplateComponentPropsByComponent,\n toBioTemplateComponentPropsById,\n type BioTemplateEnvelope,\n type BioTemplateComponentPropsLookupOptions,\n type TemplateCollectionEnvelope,\n} from '../templates'\nimport AlertBox from './AlertBox.vue'\nimport BaseButton from './BaseButton.vue'\nimport BioTemplateRenderer from './BioTemplateRenderer.vue'\n\ntype BioTemplateWorkspaceTarget = BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope\ntype WorkspaceKind = 'template' | 'collection'\n\ninterface TemplateWorkspaceStatus {\n loading?: boolean\n saving?: boolean\n error?: string | null\n hasExperiment?: boolean\n currentExperimentId?: number\n lastLoadedAt?: Date | null\n lastSavedAt?: Date | null\n}\n\ninterface TemplateWorkspaceActions {\n load?: () => unknown\n reset?: () => unknown\n save?: () => unknown\n}\n\ninterface Props {\n /** Template envelope or template collection shown in BioTemplateRenderer. */\n target: BioTemplateWorkspaceTarget\n /** Human-readable workspace label. */\n label: string\n /** Grouped request and current-experiment state for generated plugin pages. */\n status?: TemplateWorkspaceStatus\n /** Grouped load/reset/save handlers for generated plugin pages. */\n actions?: TemplateWorkspaceActions\n /** Copy shown in the status banner. */\n message?: string\n /** Whether the load/save request is active. */\n loading?: boolean\n /** Current request error. */\n error?: string | null\n /** Whether a current experiment id is available. */\n hasExperiment?: boolean\n /** Current experiment id displayed in the footer. */\n currentExperimentId?: number\n /** Last successful load timestamp. */\n lastLoadedAt?: Date | null\n /** Last successful save timestamp. */\n lastSavedAt?: Date | null\n /** Template vs collection copy defaults. */\n kind?: WorkspaceKind\n /** Compact preview layout. */\n dense?: boolean\n /** Render preview components in read-only mode. */\n readonly?: boolean\n /** Show load/save timestamps. */\n showStatus?: boolean\n /** Show template id/version cards for collection targets. */\n showTemplateSummary?: boolean\n loadLabel?: string\n resetLabel?: string\n saveLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n status: undefined,\n actions: undefined,\n message: undefined,\n loading: false,\n error: null,\n hasExperiment: false,\n currentExperimentId: undefined,\n lastLoadedAt: null,\n lastSavedAt: null,\n kind: 'template',\n dense: true,\n readonly: true,\n showStatus: true,\n showTemplateSummary: false,\n loadLabel: 'Load',\n resetLabel: 'Reset defaults',\n saveLabel: 'Save',\n})\n\nconst emit = defineEmits<{\n load: []\n reset: []\n save: []\n}>()\n\nconst statusMessage = computed(() => {\n if (props.message) return props.message\n if (props.kind === 'collection') {\n return `${props.label} saves the full template collection to the current experiment.`\n }\n return `${props.label} template data saves to experiment design_data.`\n})\n\nconst resolvedLoading = computed(() => {\n if (props.status?.loading !== undefined || props.status?.saving !== undefined) {\n return Boolean(props.status.loading) || Boolean(props.status.saving)\n }\n return props.loading\n})\nconst resolvedError = computed(() =>\n props.status?.error !== undefined ? props.status.error : props.error,\n)\nconst resolvedHasExperiment = computed(() =>\n props.status?.hasExperiment ?? props.hasExperiment,\n)\nconst resolvedCurrentExperimentId = computed(() =>\n props.status?.currentExperimentId ?? props.currentExperimentId,\n)\nconst resolvedLastLoadedAt = computed(() =>\n props.status?.lastLoadedAt !== undefined ? props.status.lastLoadedAt : props.lastLoadedAt,\n)\nconst resolvedLastSavedAt = computed(() =>\n props.status?.lastSavedAt !== undefined ? props.status.lastSavedAt : props.lastSavedAt,\n)\n\nconst templateSummaries = computed(() =>\n Object.values(extractTemplateCollection(props.target)).map(template => ({\n id: String(template.template_id),\n version: String(template.template_version ?? 'unknown'),\n }))\n)\nconst componentProps = computed(() => toBioTemplateComponentProps(props.target))\nconst componentBindingsById = computed(() => toBioTemplateComponentBindingsById(props.target))\nconst componentPropsById = computed(() => toBioTemplateComponentPropsById(props.target))\nconst componentPropsByComponent = computed(() => toBioTemplateComponentPropsByComponent(props.target))\nconst renderer = computed(() => ({ target: props.target }))\n\nfunction getComponentProps(component: string, options?: BioTemplateComponentPropsLookupOptions) {\n return getBioTemplateComponentProps(props.target, component, options)\n}\n\ninterface BioTemplateExperimentWorkspaceBindings {\n renderer: { target: BioTemplateWorkspaceTarget }\n componentProps: ReturnType<typeof toBioTemplateComponentProps>\n componentBindingsById: ReturnType<typeof toBioTemplateComponentBindingsById>\n componentPropsById: ReturnType<typeof toBioTemplateComponentPropsById>\n componentPropsByComponent: ReturnType<typeof toBioTemplateComponentPropsByComponent>\n getComponentProps: typeof getComponentProps\n}\n\nconst bindings = computed<BioTemplateExperimentWorkspaceBindings>(() => ({\n renderer: renderer.value,\n componentProps: componentProps.value,\n componentBindingsById: componentBindingsById.value,\n componentPropsById: componentPropsById.value,\n componentPropsByComponent: componentPropsByComponent.value,\n getComponentProps,\n}))\n\ninterface BioTemplateExperimentWorkspaceSlotProps {\n target: BioTemplateWorkspaceTarget\n bindings: BioTemplateExperimentWorkspaceBindings\n componentProps: ReturnType<typeof toBioTemplateComponentProps>\n componentBindingsById: ReturnType<typeof toBioTemplateComponentBindingsById>\n componentPropsById: ReturnType<typeof toBioTemplateComponentPropsById>\n componentPropsByComponent: ReturnType<typeof toBioTemplateComponentPropsByComponent>\n getComponentProps: typeof getComponentProps\n}\n\ndefineSlots<{\n default?: (props: BioTemplateExperimentWorkspaceSlotProps) => unknown\n}>()\n\nfunction handleLoad() {\n if (props.actions?.load) {\n return props.actions.load()\n }\n emit('load')\n}\n\nfunction handleReset() {\n if (props.actions?.reset) {\n return props.actions.reset()\n }\n emit('reset')\n}\n\nfunction handleSave() {\n if (props.actions?.save) {\n return props.actions.save()\n }\n emit('save')\n}\n</script>\n\n<template>\n <div class=\"mint-bio-template-experiment-workspace\">\n <div class=\"mint-bio-template-experiment-workspace__toolbar\">\n <AlertBox type=\"info\" class=\"mint-bio-template-experiment-workspace__status\">\n {{ statusMessage }}\n </AlertBox>\n\n <div class=\"mint-bio-template-experiment-workspace__actions\">\n <BaseButton\n variant=\"secondary\"\n :loading=\"resolvedLoading\"\n :disabled=\"!resolvedHasExperiment\"\n @click=\"handleLoad\"\n >\n {{ loadLabel }}\n </BaseButton>\n <BaseButton variant=\"secondary\" :loading=\"resolvedLoading\" @click=\"handleReset\">\n {{ resetLabel }}\n </BaseButton>\n <BaseButton\n variant=\"primary\"\n :loading=\"resolvedLoading\"\n :disabled=\"!resolvedHasExperiment\"\n @click=\"handleSave\"\n >\n {{ saveLabel }}\n </BaseButton>\n </div>\n </div>\n\n <AlertBox v-if=\"resolvedError\" type=\"error\">\n {{ resolvedError }}\n </AlertBox>\n\n <p v-if=\"showStatus\" class=\"mint-bio-template-experiment-workspace__meta\">\n loaded: {{ resolvedLastLoadedAt?.toLocaleString() ?? 'not loaded' }} -\n saved: {{ resolvedLastSavedAt?.toLocaleString() ?? 'not saved' }}\n </p>\n\n <div\n v-if=\"showTemplateSummary\"\n class=\"mint-bio-template-experiment-workspace__templates\"\n >\n <div\n v-for=\"template in templateSummaries\"\n :key=\"template.id\"\n class=\"mint-bio-template-experiment-workspace__template\"\n >\n <p class=\"mint-bio-template-experiment-workspace__template-id\">\n {{ template.id }}\n </p>\n <p class=\"mint-bio-template-experiment-workspace__template-version\">\n v{{ template.version }}\n </p>\n </div>\n </div>\n\n <slot\n :target=\"target\"\n :bindings=\"bindings\"\n :component-props=\"componentProps\"\n :component-bindings-by-id=\"componentBindingsById\"\n :component-props-by-id=\"componentPropsById\"\n :component-props-by-component=\"componentPropsByComponent\"\n :get-component-props=\"getComponentProps\"\n >\n <BioTemplateRenderer\n :target=\"target\"\n :dense=\"dense\"\n :readonly=\"readonly\"\n />\n </slot>\n\n <p class=\"mint-bio-template-experiment-workspace__meta\">\n experiment: {{ resolvedCurrentExperimentId ?? 'not selected' }}\n </p>\n </div>\n</template>\n\n<style scoped>\n.mint-bio-template-experiment-workspace {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n min-width: 0;\n}\n\n.mint-bio-template-experiment-workspace__toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n}\n\n.mint-bio-template-experiment-workspace__status {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.mint-bio-template-experiment-workspace__actions {\n display: flex;\n flex: 0 0 auto;\n gap: 0.5rem;\n}\n\n.mint-bio-template-experiment-workspace__meta,\n.mint-bio-template-experiment-workspace__template-version {\n margin: 0;\n color: var(--text-secondary, #64748b);\n font-family: var(--font-mono, 'Fira Code', monospace);\n font-size: 0.75rem;\n}\n\n.mint-bio-template-experiment-workspace__templates {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));\n gap: 0.75rem;\n}\n\n.mint-bio-template-experiment-workspace__template {\n min-width: 0;\n padding: 1rem;\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 0.5rem;\n background: var(--bg-primary, #ffffff);\n}\n\n.mint-bio-template-experiment-workspace__template-id {\n margin: 0;\n color: var(--text-primary, #0f172a);\n font-family: var(--font-mono, 'Fira Code', monospace);\n font-size: 0.875rem;\n}\n\n@media (max-width: 48rem) {\n .mint-bio-template-experiment-workspace__toolbar,\n .mint-bio-template-experiment-workspace__actions {\n align-items: stretch;\n flex-direction: column;\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/** Complete current-experiment workspace for a curated biology template pack with save/load controls and component-prop slots. */\nimport { computed, effectScope, onScopeDispose, shallowRef, unref, useSlots, watch, type EffectScope } from 'vue'\nimport type { BioTemplateEnvelope, TemplateCollectionEnvelope, TemplatePackId } from '../templates'\nimport type {\n UseBioTemplatePackWorkspaceOptions,\n UseBioTemplatePackWorkspaceReturn,\n} from '../composables/useBioTemplatePackWorkspace'\nimport { useBioTemplatePackWorkspace } from '../composables/useBioTemplatePackWorkspace'\nimport BioTemplateExperimentWorkspaceView from './BioTemplateExperimentWorkspaceView.vue'\n\ninterface BioTemplatePackWorkspaceSlotProps {\n workspace: UseBioTemplatePackWorkspaceReturn\n bindings: UseBioTemplatePackWorkspaceReturn['bindings']['value']\n pack: UseBioTemplatePackWorkspaceReturn['pack']\n target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope\n componentBindingsById: UseBioTemplatePackWorkspaceReturn['componentBindingsById']['value']\n componentProps: UseBioTemplatePackWorkspaceReturn['componentProps']['value']\n componentPropsById: UseBioTemplatePackWorkspaceReturn['componentPropsById']['value']\n componentPropsByComponent: UseBioTemplatePackWorkspaceReturn['componentPropsByComponent']['value']\n getComponentProps: UseBioTemplatePackWorkspaceReturn['getComponentProps']\n form: UseBioTemplatePackWorkspaceReturn['form']['value']\n sidebar: UseBioTemplatePackWorkspaceReturn['sidebar']['value']\n topBar: UseBioTemplatePackWorkspaceReturn['topBar']['value']\n pillNav: UseBioTemplatePackWorkspaceReturn['pillNav']['value']\n topBarSettings: UseBioTemplatePackWorkspaceReturn['topBarSettings']['value']\n}\n\ninterface PackWorkspaceStatus {\n loading?: boolean\n saving?: boolean\n error?: string | null\n hasExperiment?: boolean\n currentExperimentId?: number\n lastLoadedAt?: Date | null\n lastSavedAt?: Date | null\n}\n\ninterface PackWorkspaceActions {\n load?: () => unknown\n reset?: () => unknown\n save?: () => unknown\n}\n\ninterface Props {\n /** Workspace returned by useBioTemplatePackWorkspace(). Use for full manual control. */\n workspace?: UseBioTemplatePackWorkspaceReturn\n /** Built-in template pack id or alias used when workspace is not provided. */\n pack?: TemplatePackId | string\n /** Options passed to the internally generated useBioTemplatePackWorkspace() call. */\n workspaceOptions?: UseBioTemplatePackWorkspaceOptions\n /** External request state when plugin endpoints handle pack persistence. */\n status?: PackWorkspaceStatus\n /** External load/reset/save handlers when plugin endpoints handle pack persistence. */\n actions?: PackWorkspaceActions\n /** Human-readable workspace label. Defaults to the pack catalog label. */\n label?: string\n /** Copy shown in the status banner. */\n message?: string\n /** Compact preview layout. */\n dense?: boolean\n /** Render preview components in read-only mode. */\n readonly?: boolean\n /** Show load/save timestamps. */\n showStatus?: boolean\n /** Show template id/version cards for the generated collection. */\n showTemplateSummary?: boolean\n loadLabel?: string\n resetLabel?: string\n saveLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n workspace: undefined,\n pack: 'cell-culture-screen',\n workspaceOptions: () => ({}),\n status: undefined,\n actions: undefined,\n label: undefined,\n message: undefined,\n dense: true,\n readonly: true,\n showStatus: true,\n showTemplateSummary: true,\n loadLabel: 'Load',\n resetLabel: 'Reset defaults',\n saveLabel: 'Save',\n})\n\ndefineSlots<{\n default?: (props: BioTemplatePackWorkspaceSlotProps) => unknown\n}>()\n\nconst slots = useSlots()\nconst hasDefaultSlot = computed(() => Boolean(slots.default))\n\nlet generatedWorkspaceScope: EffectScope | undefined\n\nfunction createGeneratedWorkspace(): UseBioTemplatePackWorkspaceReturn {\n generatedWorkspaceScope?.stop()\n generatedWorkspaceScope = effectScope()\n return generatedWorkspaceScope.run(() => useBioTemplatePackWorkspace(props.pack, props.workspaceOptions))!\n}\n\nconst generatedWorkspace = shallowRef<UseBioTemplatePackWorkspaceReturn>(createGeneratedWorkspace())\nconst resolvedWorkspace = computed(() => props.workspace ?? generatedWorkspace.value)\nconst target = computed(() => unref(resolvedWorkspace.value.collection))\nconst label = computed(() => props.label ?? resolvedWorkspace.value.pack.label)\nconst form = computed(() => unref(resolvedWorkspace.value.form))\nconst sidebar = computed(() => unref(resolvedWorkspace.value.sidebar))\nconst topBar = computed(() => unref(resolvedWorkspace.value.topBar))\nconst pillNav = computed(() => unref(resolvedWorkspace.value.pillNav))\nconst topBarSettings = computed(() => unref(resolvedWorkspace.value.topBarSettings))\nconst bindings = computed(() => unref(resolvedWorkspace.value.bindings))\nconst resolvedStatus = computed<PackWorkspaceStatus>(() => ({\n loading: props.status?.loading ?? unref(resolvedWorkspace.value.loading),\n saving: props.status?.saving ?? unref(resolvedWorkspace.value.saving),\n error: props.status?.error !== undefined\n ? props.status.error\n : unref(resolvedWorkspace.value.error),\n hasExperiment: props.status?.hasExperiment ?? unref(resolvedWorkspace.value.hasCurrentExperiment),\n currentExperimentId: props.status?.currentExperimentId ?? unref(resolvedWorkspace.value.currentExperimentId),\n lastLoadedAt: props.status?.lastLoadedAt !== undefined\n ? props.status.lastLoadedAt\n : unref(resolvedWorkspace.value.lastLoadedAt),\n lastSavedAt: props.status?.lastSavedAt !== undefined\n ? props.status.lastSavedAt\n : unref(resolvedWorkspace.value.lastSavedAt),\n}))\nconst resolvedActions = computed<PackWorkspaceActions>(() => ({\n load: props.actions?.load ?? (() => resolvedWorkspace.value.loadCurrent()),\n reset: props.actions?.reset ?? (() => resolvedWorkspace.value.resetToDefaultCollection()),\n save: props.actions?.save ?? (() => resolvedWorkspace.value.saveCurrent()),\n}))\n\nwatch(\n [() => props.pack, () => props.workspaceOptions],\n () => {\n generatedWorkspace.value = createGeneratedWorkspace()\n },\n { deep: true },\n)\n\nonScopeDispose(() => generatedWorkspaceScope?.stop())\n</script>\n\n<template>\n <BioTemplateExperimentWorkspaceView\n :target=\"target\"\n kind=\"collection\"\n :label=\"label\"\n :message=\"message\"\n :status=\"resolvedStatus\"\n :actions=\"resolvedActions\"\n :dense=\"dense\"\n :readonly=\"readonly\"\n :show-status=\"showStatus\"\n :show-template-summary=\"showTemplateSummary\"\n :load-label=\"loadLabel\"\n :reset-label=\"resetLabel\"\n :save-label=\"saveLabel\"\n >\n <template v-if=\"hasDefaultSlot\" #default=\"slotProps\">\n <slot\n v-bind=\"slotProps\"\n :workspace=\"resolvedWorkspace\"\n :bindings=\"bindings\"\n :pack=\"resolvedWorkspace.pack\"\n :form=\"form\"\n :sidebar=\"sidebar\"\n :top-bar=\"topBar\"\n :pill-nav=\"pillNav\"\n :top-bar-settings=\"topBarSettings\"\n />\n </template>\n </BioTemplateExperimentWorkspaceView>\n</template>\n","<script setup lang=\"ts\">\n/** Complete current-experiment workspace for a curated biology template pack with save/load controls and component-prop slots. */\nimport { computed, effectScope, onScopeDispose, shallowRef, unref, useSlots, watch, type EffectScope } from 'vue'\nimport type { BioTemplateEnvelope, TemplateCollectionEnvelope, TemplatePackId } from '../templates'\nimport type {\n UseBioTemplatePackWorkspaceOptions,\n UseBioTemplatePackWorkspaceReturn,\n} from '../composables/useBioTemplatePackWorkspace'\nimport { useBioTemplatePackWorkspace } from '../composables/useBioTemplatePackWorkspace'\nimport BioTemplateExperimentWorkspaceView from './BioTemplateExperimentWorkspaceView.vue'\n\ninterface BioTemplatePackWorkspaceSlotProps {\n workspace: UseBioTemplatePackWorkspaceReturn\n bindings: UseBioTemplatePackWorkspaceReturn['bindings']['value']\n pack: UseBioTemplatePackWorkspaceReturn['pack']\n target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope\n componentBindingsById: UseBioTemplatePackWorkspaceReturn['componentBindingsById']['value']\n componentProps: UseBioTemplatePackWorkspaceReturn['componentProps']['value']\n componentPropsById: UseBioTemplatePackWorkspaceReturn['componentPropsById']['value']\n componentPropsByComponent: UseBioTemplatePackWorkspaceReturn['componentPropsByComponent']['value']\n getComponentProps: UseBioTemplatePackWorkspaceReturn['getComponentProps']\n form: UseBioTemplatePackWorkspaceReturn['form']['value']\n sidebar: UseBioTemplatePackWorkspaceReturn['sidebar']['value']\n topBar: UseBioTemplatePackWorkspaceReturn['topBar']['value']\n pillNav: UseBioTemplatePackWorkspaceReturn['pillNav']['value']\n topBarSettings: UseBioTemplatePackWorkspaceReturn['topBarSettings']['value']\n}\n\ninterface PackWorkspaceStatus {\n loading?: boolean\n saving?: boolean\n error?: string | null\n hasExperiment?: boolean\n currentExperimentId?: number\n lastLoadedAt?: Date | null\n lastSavedAt?: Date | null\n}\n\ninterface PackWorkspaceActions {\n load?: () => unknown\n reset?: () => unknown\n save?: () => unknown\n}\n\ninterface Props {\n /** Workspace returned by useBioTemplatePackWorkspace(). Use for full manual control. */\n workspace?: UseBioTemplatePackWorkspaceReturn\n /** Built-in template pack id or alias used when workspace is not provided. */\n pack?: TemplatePackId | string\n /** Options passed to the internally generated useBioTemplatePackWorkspace() call. */\n workspaceOptions?: UseBioTemplatePackWorkspaceOptions\n /** External request state when plugin endpoints handle pack persistence. */\n status?: PackWorkspaceStatus\n /** External load/reset/save handlers when plugin endpoints handle pack persistence. */\n actions?: PackWorkspaceActions\n /** Human-readable workspace label. Defaults to the pack catalog label. */\n label?: string\n /** Copy shown in the status banner. */\n message?: string\n /** Compact preview layout. */\n dense?: boolean\n /** Render preview components in read-only mode. */\n readonly?: boolean\n /** Show load/save timestamps. */\n showStatus?: boolean\n /** Show template id/version cards for the generated collection. */\n showTemplateSummary?: boolean\n loadLabel?: string\n resetLabel?: string\n saveLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n workspace: undefined,\n pack: 'cell-culture-screen',\n workspaceOptions: () => ({}),\n status: undefined,\n actions: undefined,\n label: undefined,\n message: undefined,\n dense: true,\n readonly: true,\n showStatus: true,\n showTemplateSummary: true,\n loadLabel: 'Load',\n resetLabel: 'Reset defaults',\n saveLabel: 'Save',\n})\n\ndefineSlots<{\n default?: (props: BioTemplatePackWorkspaceSlotProps) => unknown\n}>()\n\nconst slots = useSlots()\nconst hasDefaultSlot = computed(() => Boolean(slots.default))\n\nlet generatedWorkspaceScope: EffectScope | undefined\n\nfunction createGeneratedWorkspace(): UseBioTemplatePackWorkspaceReturn {\n generatedWorkspaceScope?.stop()\n generatedWorkspaceScope = effectScope()\n return generatedWorkspaceScope.run(() => useBioTemplatePackWorkspace(props.pack, props.workspaceOptions))!\n}\n\nconst generatedWorkspace = shallowRef<UseBioTemplatePackWorkspaceReturn>(createGeneratedWorkspace())\nconst resolvedWorkspace = computed(() => props.workspace ?? generatedWorkspace.value)\nconst target = computed(() => unref(resolvedWorkspace.value.collection))\nconst label = computed(() => props.label ?? resolvedWorkspace.value.pack.label)\nconst form = computed(() => unref(resolvedWorkspace.value.form))\nconst sidebar = computed(() => unref(resolvedWorkspace.value.sidebar))\nconst topBar = computed(() => unref(resolvedWorkspace.value.topBar))\nconst pillNav = computed(() => unref(resolvedWorkspace.value.pillNav))\nconst topBarSettings = computed(() => unref(resolvedWorkspace.value.topBarSettings))\nconst bindings = computed(() => unref(resolvedWorkspace.value.bindings))\nconst resolvedStatus = computed<PackWorkspaceStatus>(() => ({\n loading: props.status?.loading ?? unref(resolvedWorkspace.value.loading),\n saving: props.status?.saving ?? unref(resolvedWorkspace.value.saving),\n error: props.status?.error !== undefined\n ? props.status.error\n : unref(resolvedWorkspace.value.error),\n hasExperiment: props.status?.hasExperiment ?? unref(resolvedWorkspace.value.hasCurrentExperiment),\n currentExperimentId: props.status?.currentExperimentId ?? unref(resolvedWorkspace.value.currentExperimentId),\n lastLoadedAt: props.status?.lastLoadedAt !== undefined\n ? props.status.lastLoadedAt\n : unref(resolvedWorkspace.value.lastLoadedAt),\n lastSavedAt: props.status?.lastSavedAt !== undefined\n ? props.status.lastSavedAt\n : unref(resolvedWorkspace.value.lastSavedAt),\n}))\nconst resolvedActions = computed<PackWorkspaceActions>(() => ({\n load: props.actions?.load ?? (() => resolvedWorkspace.value.loadCurrent()),\n reset: props.actions?.reset ?? (() => resolvedWorkspace.value.resetToDefaultCollection()),\n save: props.actions?.save ?? (() => resolvedWorkspace.value.saveCurrent()),\n}))\n\nwatch(\n [() => props.pack, () => props.workspaceOptions],\n () => {\n generatedWorkspace.value = createGeneratedWorkspace()\n },\n { deep: true },\n)\n\nonScopeDispose(() => generatedWorkspaceScope?.stop())\n</script>\n\n<template>\n <BioTemplateExperimentWorkspaceView\n :target=\"target\"\n kind=\"collection\"\n :label=\"label\"\n :message=\"message\"\n :status=\"resolvedStatus\"\n :actions=\"resolvedActions\"\n :dense=\"dense\"\n :readonly=\"readonly\"\n :show-status=\"showStatus\"\n :show-template-summary=\"showTemplateSummary\"\n :load-label=\"loadLabel\"\n :reset-label=\"resetLabel\"\n :save-label=\"saveLabel\"\n >\n <template v-if=\"hasDefaultSlot\" #default=\"slotProps\">\n <slot\n v-bind=\"slotProps\"\n :workspace=\"resolvedWorkspace\"\n :bindings=\"bindings\"\n :pack=\"resolvedWorkspace.pack\"\n :form=\"form\"\n :sidebar=\"sidebar\"\n :top-bar=\"topBar\"\n :pill-nav=\"pillNav\"\n :top-bar-settings=\"topBarSettings\"\n />\n </template>\n </BioTemplateExperimentWorkspaceView>\n</template>\n","<script setup lang=\"ts\">\n/** Complete editable biology template preset workspace for WellPlate/DoseCalculator, LC-MS, qPCR, generated controls, preview components, and current-experiment save wiring. */\nimport { computed, effectScope, onScopeDispose, shallowRef, toRaw, unref, watch, type EffectScope } from 'vue'\nimport type { BioTemplateControlValues, TemplatePresetId } from '../templates'\nimport type {\n UseBioTemplatePresetWorkspaceOptions,\n UseBioTemplatePresetWorkspaceReturn,\n} from '../composables/useBioTemplatePresetWorkspace'\nimport { useBioTemplatePresetWorkspace } from '../composables/useBioTemplatePresetWorkspace'\nimport AlertBox from './AlertBox.vue'\nimport AppSidebar from './AppSidebar.vue'\nimport BaseButton from './BaseButton.vue'\nimport BioTemplateRenderer from './BioTemplateRenderer.vue'\nimport AppTopBarPillNavInternal from './internal/AppTopBarPillNavInternal.vue'\n\ntype BioTemplatePresetSidebarVariant = 'default' | 'analysis'\n\ninterface BioTemplatePresetWorkspaceSlotProps {\n workspace: UseBioTemplatePresetWorkspaceReturn\n bindings: UseBioTemplatePresetWorkspaceReturn['bindings']\n collection: UseBioTemplatePresetWorkspaceReturn['collection']['value']\n renderer: UseBioTemplatePresetWorkspaceReturn['renderer']['value']\n componentBindingsById: UseBioTemplatePresetWorkspaceReturn['componentBindingsById']['value']\n componentProps: UseBioTemplatePresetWorkspaceReturn['componentProps']['value']\n componentPropsById: UseBioTemplatePresetWorkspaceReturn['componentPropsById']['value']\n componentPropsByComponent: UseBioTemplatePresetWorkspaceReturn['componentPropsByComponent']['value']\n getComponentProps: UseBioTemplatePresetWorkspaceReturn['getComponentProps']\n}\n\ninterface Props {\n /** Workspace returned by useBioTemplatePresetWorkspace(). Use for full manual control. */\n workspace?: UseBioTemplatePresetWorkspaceReturn\n /** Built-in preset id used to create the workspace when workspace is not provided. */\n preset?: TemplatePresetId\n /** Options passed to the internally generated useBioTemplatePresetWorkspace() call. */\n workspaceOptions?: UseBioTemplatePresetWorkspaceOptions\n /** Initial control values for the internally generated preset workspace. */\n initialValues?: BioTemplateControlValues\n /** External control values for the internally generated preset workspace. Supports default v-model. */\n modelValue?: BioTemplateControlValues\n /** External control values for the internally generated preset workspace. Supports v-model:values. */\n values?: BioTemplateControlValues\n /** Label shown in the status banner. Defaults to the humanized preset id. */\n label?: string\n /** Sidebar CSS width. */\n sidebarWidth?: string\n /** AppSidebar visual preset. `analysis` matches the LEAF-style MINT analysis sidebar design language. */\n sidebarVariant?: BioTemplatePresetSidebarVariant\n /** Compact sidebar and preview layout. */\n dense?: boolean\n /** Render preview components in read-only mode. */\n readonly?: boolean\n /** Show save timestamp. */\n showStatus?: boolean\n /** Show cards for templates included in the preset collection. */\n showTemplateSummary?: boolean\n /** Show component binding count. */\n showComponentSummary?: boolean\n saveLabel?: string\n resetLabel?: string\n}\n\nconst emit = defineEmits<{\n /** Emitted when generated preset controls change through the embedded sidebar or reset action. */\n 'update:modelValue': [values: BioTemplateControlValues]\n /** Emitted when generated preset controls change through the embedded sidebar or reset action. */\n 'update:values': [values: BioTemplateControlValues]\n}>()\n\ndefineSlots<{\n default?: (props: BioTemplatePresetWorkspaceSlotProps) => unknown\n}>()\n\nconst props = withDefaults(defineProps<Props>(), {\n workspace: undefined,\n preset: 'wellplate-screen',\n workspaceOptions: () => ({}),\n initialValues: undefined,\n modelValue: undefined,\n values: undefined,\n sidebarWidth: '320px',\n sidebarVariant: 'analysis',\n dense: true,\n readonly: true,\n showStatus: true,\n showTemplateSummary: true,\n showComponentSummary: false,\n saveLabel: 'Save',\n resetLabel: 'Reset defaults',\n})\n\nconst externalValues = computed(() => props.modelValue ?? props.values)\nconst resolvedWorkspaceOptions = computed<UseBioTemplatePresetWorkspaceOptions>(() => {\n if (props.initialValues === undefined && externalValues.value === undefined) return props.workspaceOptions\n\n return {\n ...props.workspaceOptions,\n initialValues: {\n ...(props.workspaceOptions.initialValues ?? {}),\n ...(props.initialValues ?? {}),\n ...(externalValues.value ?? {}),\n },\n }\n})\n\nlet generatedWorkspaceScope: EffectScope | undefined\n\nfunction createGeneratedWorkspace(): UseBioTemplatePresetWorkspaceReturn {\n generatedWorkspaceScope?.stop()\n generatedWorkspaceScope = effectScope()\n return generatedWorkspaceScope.run(() => useBioTemplatePresetWorkspace(props.preset, resolvedWorkspaceOptions.value))!\n}\n\nconst generatedWorkspace = shallowRef<UseBioTemplatePresetWorkspaceReturn>(createGeneratedWorkspace())\nconst resolvedWorkspace = computed(() => props.workspace ?? generatedWorkspace.value)\n\nwatch(\n [() => props.preset, () => props.workspaceOptions, () => props.initialValues],\n () => {\n generatedWorkspace.value = createGeneratedWorkspace()\n },\n { deep: true },\n)\n\nonScopeDispose(() => generatedWorkspaceScope?.stop())\n\nwatch(\n externalValues,\n (values) => {\n if (values === undefined || props.workspace !== undefined) return\n if (recordsEqual(toPlainRecord(generatedWorkspace.value.controlValues.value), values)) return\n generatedWorkspace.value.setControlValues(values)\n },\n { deep: true },\n)\n\nwatch(\n () => generatedWorkspace.value.controlValues.value,\n (values) => {\n if (props.workspace !== undefined) return\n const nextValues = toPlainRecord(values)\n if (externalValues.value !== undefined && recordsEqual(nextValues, externalValues.value)) return\n emit('update:modelValue', nextValues)\n emit('update:values', nextValues)\n },\n { deep: true },\n)\n\nconst presetId = computed(() => resolvedWorkspace.value.presetId)\nconst label = computed(() => props.label ?? unref(resolvedWorkspace.value.presetLabel))\nconst collection = computed(() => unref(resolvedWorkspace.value.collection))\nconst templateIds = computed(() => unref(resolvedWorkspace.value.templateIds))\nconst currentExperimentId = computed(() => unref(resolvedWorkspace.value.currentExperimentId))\nconst hasCurrentExperiment = computed(() => unref(resolvedWorkspace.value.hasCurrentExperiment))\nconst isSaving = computed(() => unref(resolvedWorkspace.value.saving))\nconst error = computed(() => unref(resolvedWorkspace.value.error))\nconst lastSavedAt = computed(() => unref(resolvedWorkspace.value.lastSavedAt))\nconst renderer = computed(() => unref(resolvedWorkspace.value.renderer))\nconst componentBindingsById = computed(() => unref(resolvedWorkspace.value.componentBindingsById))\nconst componentProps = computed(() => unref(resolvedWorkspace.value.componentProps))\nconst componentPropsById = computed(() => unref(resolvedWorkspace.value.componentPropsById))\nconst componentPropsByComponent = computed(() => unref(resolvedWorkspace.value.componentPropsByComponent))\nconst componentPropsCount = computed(() => unref(resolvedWorkspace.value.componentProps).length)\nconst bindings = computed(() => resolvedWorkspace.value.bindings)\n\nfunction toPlainRecord(value: Record<string, unknown>): Record<string, unknown> {\n return toPlain(value) as Record<string, unknown>\n}\n\nfunction toPlain(value: unknown): unknown {\n const raw = toRaw(value)\n if (Array.isArray(raw)) return raw.map(item => toPlain(item))\n if (raw && typeof raw === 'object') {\n return Object.fromEntries(\n Object.entries(raw as Record<string, unknown>).map(([key, item]) => [key, toPlain(item)])\n )\n }\n return raw\n}\n\nfunction recordsEqual(left: Record<string, unknown>, right: Record<string, unknown>): boolean {\n const leftKeys = Object.keys(left)\n const rightKeys = Object.keys(right)\n if (leftKeys.length !== rightKeys.length) return false\n return leftKeys.every(key => valuesEqual(left[key], right[key]))\n}\n\nfunction valuesEqual(left: unknown, right: unknown): boolean {\n const leftRaw = toRaw(left)\n const rightRaw = toRaw(right)\n\n if (Object.is(leftRaw, rightRaw)) return true\n\n if (Array.isArray(leftRaw) || Array.isArray(rightRaw)) {\n if (!Array.isArray(leftRaw) || !Array.isArray(rightRaw)) return false\n if (leftRaw.length !== rightRaw.length) return false\n return leftRaw.every((item, index) => valuesEqual(item, rightRaw[index]))\n }\n\n if (isPlainRecord(leftRaw) || isPlainRecord(rightRaw)) {\n if (!isPlainRecord(leftRaw) || !isPlainRecord(rightRaw)) return false\n return recordsEqual(leftRaw, rightRaw)\n }\n\n return false\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n</script>\n\n<template>\n <div class=\"mint-bio-template-preset-workspace\">\n <div class=\"mint-bio-template-preset-workspace__toolbar\">\n <AlertBox type=\"info\" class=\"mint-bio-template-preset-workspace__status\">\n {{ label }} preset saves to the current experiment design_data.\n </AlertBox>\n\n <div class=\"mint-bio-template-preset-workspace__actions\">\n <BaseButton\n variant=\"secondary\"\n :disabled=\"isSaving\"\n @click=\"resolvedWorkspace.resetToDefaultCollection()\"\n >\n {{ resetLabel }}\n </BaseButton>\n <BaseButton\n variant=\"primary\"\n :loading=\"isSaving\"\n :disabled=\"!hasCurrentExperiment\"\n @click=\"resolvedWorkspace.saveCurrent()\"\n >\n {{ saveLabel }}\n </BaseButton>\n </div>\n </div>\n\n <AlertBox v-if=\"error\" type=\"error\">\n {{ error }}\n </AlertBox>\n\n <p v-if=\"showStatus\" class=\"mint-bio-template-preset-workspace__meta\">\n saved: {{ lastSavedAt?.toLocaleString() ?? 'not saved' }}\n </p>\n\n <div class=\"mint-bio-template-preset-workspace__main\">\n <AppSidebar\n v-bind=\"resolvedWorkspace.sidebar\"\n :variant=\"sidebarVariant\"\n :floating=\"false\"\n :width=\"sidebarWidth\"\n :dense=\"dense\"\n >\n <template #header>\n <p class=\"mint-bio-template-preset-workspace__sidebar-title\">\n Controls\n </p>\n <AppTopBarPillNavInternal\n v-if=\"resolvedWorkspace.pillNav.items.length > 1\"\n class=\"mint-bio-template-preset-workspace__view-nav\"\n :items=\"resolvedWorkspace.pillNav.items\"\n :current-item-id=\"resolvedWorkspace.pillNav.currentItemId\"\n @select=\"resolvedWorkspace.pillNav.onSelect\"\n />\n <p class=\"mint-bio-template-preset-workspace__sidebar-subtitle\">\n {{ presetId }}\n </p>\n </template>\n </AppSidebar>\n\n <slot\n :workspace=\"resolvedWorkspace\"\n :bindings=\"bindings\"\n :collection=\"collection\"\n :renderer=\"renderer\"\n :component-bindings-by-id=\"componentBindingsById\"\n :component-props=\"componentProps\"\n :component-props-by-id=\"componentPropsById\"\n :component-props-by-component=\"componentPropsByComponent\"\n :get-component-props=\"resolvedWorkspace.getComponentProps\"\n >\n <BioTemplateRenderer\n v-bind=\"renderer\"\n :dense=\"dense\"\n :readonly=\"readonly\"\n />\n </slot>\n </div>\n\n <div\n v-if=\"showTemplateSummary\"\n class=\"mint-bio-template-preset-workspace__templates\"\n >\n <div\n v-for=\"templateId in templateIds\"\n :key=\"templateId\"\n class=\"mint-bio-template-preset-workspace__template\"\n >\n <p class=\"mint-bio-template-preset-workspace__template-id\">\n {{ templateId }}\n </p>\n <p class=\"mint-bio-template-preset-workspace__template-meta\">\n {{ presetId }} - Experiment {{ currentExperimentId ?? 'not selected' }}\n </p>\n </div>\n </div>\n\n <p v-if=\"showComponentSummary\" class=\"mint-bio-template-preset-workspace__meta\">\n component prop bindings: {{ componentPropsCount }}\n </p>\n </div>\n</template>\n\n<style scoped>\n.mint-bio-template-preset-workspace {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n min-width: 0;\n}\n\n.mint-bio-template-preset-workspace__toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n}\n\n.mint-bio-template-preset-workspace__status {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.mint-bio-template-preset-workspace__actions {\n display: flex;\n flex: 0 0 auto;\n gap: 0.5rem;\n}\n\n.mint-bio-template-preset-workspace__meta,\n.mint-bio-template-preset-workspace__sidebar-subtitle,\n.mint-bio-template-preset-workspace__template-meta {\n margin: 0;\n color: var(--text-secondary, #64748b);\n font-family: var(--font-mono, 'Fira Code', monospace);\n font-size: 0.75rem;\n}\n\n.mint-bio-template-preset-workspace__main {\n display: grid;\n grid-template-columns: minmax(16rem, max-content) minmax(0, 1fr);\n gap: 1rem;\n align-items: start;\n min-width: 0;\n}\n\n.mint-bio-template-preset-workspace__sidebar-title {\n margin: 0;\n color: var(--text-primary, #0f172a);\n font-size: 0.875rem;\n font-weight: 600;\n}\n\n.mint-bio-template-preset-workspace__view-nav {\n margin-top: 0.5rem;\n}\n\n.mint-bio-template-preset-workspace__templates {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));\n gap: 0.75rem;\n}\n\n.mint-bio-template-preset-workspace__template {\n min-width: 0;\n padding: 1rem;\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 0.5rem;\n background: var(--bg-primary, #ffffff);\n}\n\n.mint-bio-template-preset-workspace__template-id {\n margin: 0;\n color: var(--text-primary, #0f172a);\n font-family: var(--font-mono, 'Fira Code', monospace);\n font-size: 0.875rem;\n}\n\n@media (max-width: 48rem) {\n .mint-bio-template-preset-workspace__toolbar,\n .mint-bio-template-preset-workspace__actions {\n align-items: stretch;\n flex-direction: column;\n }\n\n .mint-bio-template-preset-workspace__main {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/** Complete editable biology template preset workspace for WellPlate/DoseCalculator, LC-MS, qPCR, generated controls, preview components, and current-experiment save wiring. */\nimport { computed, effectScope, onScopeDispose, shallowRef, toRaw, unref, watch, type EffectScope } from 'vue'\nimport type { BioTemplateControlValues, TemplatePresetId } from '../templates'\nimport type {\n UseBioTemplatePresetWorkspaceOptions,\n UseBioTemplatePresetWorkspaceReturn,\n} from '../composables/useBioTemplatePresetWorkspace'\nimport { useBioTemplatePresetWorkspace } from '../composables/useBioTemplatePresetWorkspace'\nimport AlertBox from './AlertBox.vue'\nimport AppSidebar from './AppSidebar.vue'\nimport BaseButton from './BaseButton.vue'\nimport BioTemplateRenderer from './BioTemplateRenderer.vue'\nimport AppTopBarPillNavInternal from './internal/AppTopBarPillNavInternal.vue'\n\ntype BioTemplatePresetSidebarVariant = 'default' | 'analysis'\n\ninterface BioTemplatePresetWorkspaceSlotProps {\n workspace: UseBioTemplatePresetWorkspaceReturn\n bindings: UseBioTemplatePresetWorkspaceReturn['bindings']\n collection: UseBioTemplatePresetWorkspaceReturn['collection']['value']\n renderer: UseBioTemplatePresetWorkspaceReturn['renderer']['value']\n componentBindingsById: UseBioTemplatePresetWorkspaceReturn['componentBindingsById']['value']\n componentProps: UseBioTemplatePresetWorkspaceReturn['componentProps']['value']\n componentPropsById: UseBioTemplatePresetWorkspaceReturn['componentPropsById']['value']\n componentPropsByComponent: UseBioTemplatePresetWorkspaceReturn['componentPropsByComponent']['value']\n getComponentProps: UseBioTemplatePresetWorkspaceReturn['getComponentProps']\n}\n\ninterface Props {\n /** Workspace returned by useBioTemplatePresetWorkspace(). Use for full manual control. */\n workspace?: UseBioTemplatePresetWorkspaceReturn\n /** Built-in preset id used to create the workspace when workspace is not provided. */\n preset?: TemplatePresetId\n /** Options passed to the internally generated useBioTemplatePresetWorkspace() call. */\n workspaceOptions?: UseBioTemplatePresetWorkspaceOptions\n /** Initial control values for the internally generated preset workspace. */\n initialValues?: BioTemplateControlValues\n /** External control values for the internally generated preset workspace. Supports default v-model. */\n modelValue?: BioTemplateControlValues\n /** External control values for the internally generated preset workspace. Supports v-model:values. */\n values?: BioTemplateControlValues\n /** Label shown in the status banner. Defaults to the humanized preset id. */\n label?: string\n /** Sidebar CSS width. */\n sidebarWidth?: string\n /** AppSidebar visual preset. `analysis` matches the LEAF-style MINT analysis sidebar design language. */\n sidebarVariant?: BioTemplatePresetSidebarVariant\n /** Compact sidebar and preview layout. */\n dense?: boolean\n /** Render preview components in read-only mode. */\n readonly?: boolean\n /** Show save timestamp. */\n showStatus?: boolean\n /** Show cards for templates included in the preset collection. */\n showTemplateSummary?: boolean\n /** Show component binding count. */\n showComponentSummary?: boolean\n saveLabel?: string\n resetLabel?: string\n}\n\nconst emit = defineEmits<{\n /** Emitted when generated preset controls change through the embedded sidebar or reset action. */\n 'update:modelValue': [values: BioTemplateControlValues]\n /** Emitted when generated preset controls change through the embedded sidebar or reset action. */\n 'update:values': [values: BioTemplateControlValues]\n}>()\n\ndefineSlots<{\n default?: (props: BioTemplatePresetWorkspaceSlotProps) => unknown\n}>()\n\nconst props = withDefaults(defineProps<Props>(), {\n workspace: undefined,\n preset: 'wellplate-screen',\n workspaceOptions: () => ({}),\n initialValues: undefined,\n modelValue: undefined,\n values: undefined,\n sidebarWidth: '320px',\n sidebarVariant: 'analysis',\n dense: true,\n readonly: true,\n showStatus: true,\n showTemplateSummary: true,\n showComponentSummary: false,\n saveLabel: 'Save',\n resetLabel: 'Reset defaults',\n})\n\nconst externalValues = computed(() => props.modelValue ?? props.values)\nconst resolvedWorkspaceOptions = computed<UseBioTemplatePresetWorkspaceOptions>(() => {\n if (props.initialValues === undefined && externalValues.value === undefined) return props.workspaceOptions\n\n return {\n ...props.workspaceOptions,\n initialValues: {\n ...(props.workspaceOptions.initialValues ?? {}),\n ...(props.initialValues ?? {}),\n ...(externalValues.value ?? {}),\n },\n }\n})\n\nlet generatedWorkspaceScope: EffectScope | undefined\n\nfunction createGeneratedWorkspace(): UseBioTemplatePresetWorkspaceReturn {\n generatedWorkspaceScope?.stop()\n generatedWorkspaceScope = effectScope()\n return generatedWorkspaceScope.run(() => useBioTemplatePresetWorkspace(props.preset, resolvedWorkspaceOptions.value))!\n}\n\nconst generatedWorkspace = shallowRef<UseBioTemplatePresetWorkspaceReturn>(createGeneratedWorkspace())\nconst resolvedWorkspace = computed(() => props.workspace ?? generatedWorkspace.value)\n\nwatch(\n [() => props.preset, () => props.workspaceOptions, () => props.initialValues],\n () => {\n generatedWorkspace.value = createGeneratedWorkspace()\n },\n { deep: true },\n)\n\nonScopeDispose(() => generatedWorkspaceScope?.stop())\n\nwatch(\n externalValues,\n (values) => {\n if (values === undefined || props.workspace !== undefined) return\n if (recordsEqual(toPlainRecord(generatedWorkspace.value.controlValues.value), values)) return\n generatedWorkspace.value.setControlValues(values)\n },\n { deep: true },\n)\n\nwatch(\n () => generatedWorkspace.value.controlValues.value,\n (values) => {\n if (props.workspace !== undefined) return\n const nextValues = toPlainRecord(values)\n if (externalValues.value !== undefined && recordsEqual(nextValues, externalValues.value)) return\n emit('update:modelValue', nextValues)\n emit('update:values', nextValues)\n },\n { deep: true },\n)\n\nconst presetId = computed(() => resolvedWorkspace.value.presetId)\nconst label = computed(() => props.label ?? unref(resolvedWorkspace.value.presetLabel))\nconst collection = computed(() => unref(resolvedWorkspace.value.collection))\nconst templateIds = computed(() => unref(resolvedWorkspace.value.templateIds))\nconst currentExperimentId = computed(() => unref(resolvedWorkspace.value.currentExperimentId))\nconst hasCurrentExperiment = computed(() => unref(resolvedWorkspace.value.hasCurrentExperiment))\nconst isSaving = computed(() => unref(resolvedWorkspace.value.saving))\nconst error = computed(() => unref(resolvedWorkspace.value.error))\nconst lastSavedAt = computed(() => unref(resolvedWorkspace.value.lastSavedAt))\nconst renderer = computed(() => unref(resolvedWorkspace.value.renderer))\nconst componentBindingsById = computed(() => unref(resolvedWorkspace.value.componentBindingsById))\nconst componentProps = computed(() => unref(resolvedWorkspace.value.componentProps))\nconst componentPropsById = computed(() => unref(resolvedWorkspace.value.componentPropsById))\nconst componentPropsByComponent = computed(() => unref(resolvedWorkspace.value.componentPropsByComponent))\nconst componentPropsCount = computed(() => unref(resolvedWorkspace.value.componentProps).length)\nconst bindings = computed(() => resolvedWorkspace.value.bindings)\n\nfunction toPlainRecord(value: Record<string, unknown>): Record<string, unknown> {\n return toPlain(value) as Record<string, unknown>\n}\n\nfunction toPlain(value: unknown): unknown {\n const raw = toRaw(value)\n if (Array.isArray(raw)) return raw.map(item => toPlain(item))\n if (raw && typeof raw === 'object') {\n return Object.fromEntries(\n Object.entries(raw as Record<string, unknown>).map(([key, item]) => [key, toPlain(item)])\n )\n }\n return raw\n}\n\nfunction recordsEqual(left: Record<string, unknown>, right: Record<string, unknown>): boolean {\n const leftKeys = Object.keys(left)\n const rightKeys = Object.keys(right)\n if (leftKeys.length !== rightKeys.length) return false\n return leftKeys.every(key => valuesEqual(left[key], right[key]))\n}\n\nfunction valuesEqual(left: unknown, right: unknown): boolean {\n const leftRaw = toRaw(left)\n const rightRaw = toRaw(right)\n\n if (Object.is(leftRaw, rightRaw)) return true\n\n if (Array.isArray(leftRaw) || Array.isArray(rightRaw)) {\n if (!Array.isArray(leftRaw) || !Array.isArray(rightRaw)) return false\n if (leftRaw.length !== rightRaw.length) return false\n return leftRaw.every((item, index) => valuesEqual(item, rightRaw[index]))\n }\n\n if (isPlainRecord(leftRaw) || isPlainRecord(rightRaw)) {\n if (!isPlainRecord(leftRaw) || !isPlainRecord(rightRaw)) return false\n return recordsEqual(leftRaw, rightRaw)\n }\n\n return false\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n</script>\n\n<template>\n <div class=\"mint-bio-template-preset-workspace\">\n <div class=\"mint-bio-template-preset-workspace__toolbar\">\n <AlertBox type=\"info\" class=\"mint-bio-template-preset-workspace__status\">\n {{ label }} preset saves to the current experiment design_data.\n </AlertBox>\n\n <div class=\"mint-bio-template-preset-workspace__actions\">\n <BaseButton\n variant=\"secondary\"\n :disabled=\"isSaving\"\n @click=\"resolvedWorkspace.resetToDefaultCollection()\"\n >\n {{ resetLabel }}\n </BaseButton>\n <BaseButton\n variant=\"primary\"\n :loading=\"isSaving\"\n :disabled=\"!hasCurrentExperiment\"\n @click=\"resolvedWorkspace.saveCurrent()\"\n >\n {{ saveLabel }}\n </BaseButton>\n </div>\n </div>\n\n <AlertBox v-if=\"error\" type=\"error\">\n {{ error }}\n </AlertBox>\n\n <p v-if=\"showStatus\" class=\"mint-bio-template-preset-workspace__meta\">\n saved: {{ lastSavedAt?.toLocaleString() ?? 'not saved' }}\n </p>\n\n <div class=\"mint-bio-template-preset-workspace__main\">\n <AppSidebar\n v-bind=\"resolvedWorkspace.sidebar\"\n :variant=\"sidebarVariant\"\n :floating=\"false\"\n :width=\"sidebarWidth\"\n :dense=\"dense\"\n >\n <template #header>\n <p class=\"mint-bio-template-preset-workspace__sidebar-title\">\n Controls\n </p>\n <AppTopBarPillNavInternal\n v-if=\"resolvedWorkspace.pillNav.items.length > 1\"\n class=\"mint-bio-template-preset-workspace__view-nav\"\n :items=\"resolvedWorkspace.pillNav.items\"\n :current-item-id=\"resolvedWorkspace.pillNav.currentItemId\"\n @select=\"resolvedWorkspace.pillNav.onSelect\"\n />\n <p class=\"mint-bio-template-preset-workspace__sidebar-subtitle\">\n {{ presetId }}\n </p>\n </template>\n </AppSidebar>\n\n <slot\n :workspace=\"resolvedWorkspace\"\n :bindings=\"bindings\"\n :collection=\"collection\"\n :renderer=\"renderer\"\n :component-bindings-by-id=\"componentBindingsById\"\n :component-props=\"componentProps\"\n :component-props-by-id=\"componentPropsById\"\n :component-props-by-component=\"componentPropsByComponent\"\n :get-component-props=\"resolvedWorkspace.getComponentProps\"\n >\n <BioTemplateRenderer\n v-bind=\"renderer\"\n :dense=\"dense\"\n :readonly=\"readonly\"\n />\n </slot>\n </div>\n\n <div\n v-if=\"showTemplateSummary\"\n class=\"mint-bio-template-preset-workspace__templates\"\n >\n <div\n v-for=\"templateId in templateIds\"\n :key=\"templateId\"\n class=\"mint-bio-template-preset-workspace__template\"\n >\n <p class=\"mint-bio-template-preset-workspace__template-id\">\n {{ templateId }}\n </p>\n <p class=\"mint-bio-template-preset-workspace__template-meta\">\n {{ presetId }} - Experiment {{ currentExperimentId ?? 'not selected' }}\n </p>\n </div>\n </div>\n\n <p v-if=\"showComponentSummary\" class=\"mint-bio-template-preset-workspace__meta\">\n component prop bindings: {{ componentPropsCount }}\n </p>\n </div>\n</template>\n\n<style scoped>\n.mint-bio-template-preset-workspace {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n min-width: 0;\n}\n\n.mint-bio-template-preset-workspace__toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem;\n}\n\n.mint-bio-template-preset-workspace__status {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.mint-bio-template-preset-workspace__actions {\n display: flex;\n flex: 0 0 auto;\n gap: 0.5rem;\n}\n\n.mint-bio-template-preset-workspace__meta,\n.mint-bio-template-preset-workspace__sidebar-subtitle,\n.mint-bio-template-preset-workspace__template-meta {\n margin: 0;\n color: var(--text-secondary, #64748b);\n font-family: var(--font-mono, 'Fira Code', monospace);\n font-size: 0.75rem;\n}\n\n.mint-bio-template-preset-workspace__main {\n display: grid;\n grid-template-columns: minmax(16rem, max-content) minmax(0, 1fr);\n gap: 1rem;\n align-items: start;\n min-width: 0;\n}\n\n.mint-bio-template-preset-workspace__sidebar-title {\n margin: 0;\n color: var(--text-primary, #0f172a);\n font-size: 0.875rem;\n font-weight: 600;\n}\n\n.mint-bio-template-preset-workspace__view-nav {\n margin-top: 0.5rem;\n}\n\n.mint-bio-template-preset-workspace__templates {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));\n gap: 0.75rem;\n}\n\n.mint-bio-template-preset-workspace__template {\n min-width: 0;\n padding: 1rem;\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 0.5rem;\n background: var(--bg-primary, #ffffff);\n}\n\n.mint-bio-template-preset-workspace__template-id {\n margin: 0;\n color: var(--text-primary, #0f172a);\n font-family: var(--font-mono, 'Fira Code', monospace);\n font-size: 0.875rem;\n}\n\n@media (max-width: 48rem) {\n .mint-bio-template-preset-workspace__toolbar,\n .mint-bio-template-preset-workspace__actions {\n align-items: stretch;\n flex-direction: column;\n }\n\n .mint-bio-template-preset-workspace__main {\n grid-template-columns: minmax(0, 1fr);\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/** Multi-rack editor for managing collections of well-plate racks with add/remove/reorder controls and per-well editing. */\nimport { ref, computed, watch } from 'vue'\nimport type { Rack, SlotPosition, WellPlateFormat, WellPlateSize, WellEditData, Well } from '../types'\nimport { useRackEditor } from '../composables/useRackEditor'\nimport WellPlate from './WellPlate.vue'\n\ninterface Props {\n modelValue?: Rack[]\n activeRackId?: string\n maxRacks?: number\n minRacks?: number\n allowReorder?: boolean\n editable?: boolean\n readonly?: boolean\n wellPlateSize?: WellPlateSize\n showLegend?: boolean\n showBadges?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: undefined,\n activeRackId: undefined,\n maxRacks: 10,\n minRacks: 1,\n allowReorder: true,\n editable: true,\n readonly: false,\n wellPlateSize: 'md',\n showLegend: true,\n showBadges: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [racks: Rack[]]\n 'update:activeRackId': [rackId: string]\n 'rack-add': [rack: Rack]\n 'rack-remove': [rackId: string]\n 'rack-reorder': [rackIds: string[]]\n 'well-edit': [rackId: string, wellId: string, data: WellEditData]\n}>()\n\nconst editor = useRackEditor(props.modelValue, {\n maxRacks: props.maxRacks,\n minRacks: props.minRacks,\n})\n\n// Sync external modelValue -> internal racks\nwatch(\n () => props.modelValue,\n (newRacks) => {\n if (newRacks && JSON.stringify(newRacks) !== JSON.stringify(editor.racks.value)) {\n editor.racks.value = newRacks.map(r => ({ ...r }))\n }\n },\n)\n\n// Sync external activeRackId -> internal\nwatch(\n () => props.activeRackId,\n (id) => {\n if (id && id !== editor.activeRackId.value) {\n editor.setActiveRack(id)\n }\n },\n)\n\n// Sync internal racks -> external modelValue\nwatch(\n editor.racks,\n (newRacks) => {\n emit('update:modelValue', newRacks)\n },\n { deep: true },\n)\n\n// Sync internal activeRackId -> external\nwatch(\n editor.activeRackId,\n (id) => {\n emit('update:activeRackId', id)\n },\n)\n\nconst SLOT_COLORS: Record<SlotPosition, string> = {\n R: '#ef4444',\n G: '#22c55e',\n B: '#3b82f6',\n Y: '#eab308',\n}\n\nconst SLOT_OPTIONS: SlotPosition[] = ['R', 'G', 'B', 'Y']\n\nconst FORMAT_OPTIONS: { value: WellPlateFormat; label: string }[] = [\n { value: 54, label: '54' },\n { value: 96, label: '96' },\n]\n\n// Drag state for rack reordering\nconst draggingRackId = ref<string | null>(null)\nconst dragOverRackId = ref<string | null>(null)\n\nfunction handleAddRack() {\n const rack = editor.addRack()\n emit('rack-add', rack)\n}\n\nfunction handleRemoveRack(rackId: string) {\n editor.removeRack(rackId)\n emit('rack-remove', rackId)\n}\n\nfunction handleTabClick(rackId: string) {\n editor.setActiveRack(rackId)\n}\n\n// Rack drag handlers\nfunction handleRackDragStart(event: DragEvent, rackId: string) {\n if (!props.allowReorder) return\n draggingRackId.value = rackId\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', rackId)\n }\n}\n\nfunction handleRackDragOver(event: DragEvent, rackId: string) {\n event.preventDefault()\n if (draggingRackId.value && draggingRackId.value !== rackId) {\n dragOverRackId.value = rackId\n }\n}\n\nfunction handleRackDragLeave() {\n dragOverRackId.value = null\n}\n\nfunction handleRackDrop(event: DragEvent, targetRackId: string) {\n event.preventDefault()\n if (!draggingRackId.value || draggingRackId.value === targetRackId) {\n clearDragState()\n return\n }\n\n const fromIndex = editor.racks.value.findIndex(r => r.id === draggingRackId.value)\n const toIndex = editor.racks.value.findIndex(r => r.id === targetRackId)\n\n if (fromIndex !== -1 && toIndex !== -1) {\n editor.reorderRacks(fromIndex, toIndex)\n emit('rack-reorder', editor.racks.value.map(r => r.id))\n }\n\n clearDragState()\n}\n\nfunction handleRackDragEnd() {\n clearDragState()\n}\n\nfunction clearDragState() {\n draggingRackId.value = null\n dragOverRackId.value = null\n}\n\n// Toolbar actions\nfunction handleFormatChange(format: WellPlateFormat) {\n if (!editor.activeRack.value) return\n editor.updateRack(editor.activeRack.value.id, { format })\n}\n\nfunction handleSlotChange(slot: SlotPosition) {\n if (!editor.activeRack.value) return\n editor.updateRack(editor.activeRack.value.id, { slot })\n}\n\nfunction handleClearAll() {\n if (!editor.activeRack.value) return\n editor.clearAllWells(editor.activeRack.value.id)\n}\n\nfunction handleFillSeries() {\n if (!editor.activeRack.value) return\n editor.fillSeries(editor.activeRack.value.id)\n}\n\n// Well edit from WellPlate\nfunction handleWellEdit(wellId: string, data: WellEditData) {\n if (!editor.activeRack.value) return\n const rackId = editor.activeRack.value.id\n\n const wellData: Partial<Well> = {\n id: wellId,\n state: data.label ? 'filled' : 'empty',\n sampleType: data.sampleType || undefined,\n metadata: {\n label: data.label,\n injectionVolume: data.injectionVolume,\n injectionCount: data.injectionCount,\n customMethod: data.customMethod || null,\n },\n }\n\n if (data.label) {\n editor.setWellData(rackId, wellId, wellData)\n } else {\n editor.clearWell(rackId, wellId)\n }\n\n emit('well-edit', rackId, wellId, data)\n}\n\nfunction handleWellClear(wellId: string) {\n if (!editor.activeRack.value) return\n editor.clearWell(editor.activeRack.value.id, wellId)\n}\n\n// Computed well count per rack\nfunction getWellCount(rack: Rack): number {\n return Object.keys(rack.wells).length\n}\n\nconst activeRackWells = computed(() => editor.activeRack.value?.wells ?? {})\n</script>\n\n<template>\n <div :class=\"['mint-rack-editor', { 'mint-rack-editor--readonly': readonly }]\">\n <!-- Rack tabs -->\n <div class=\"mint-rack-editor__tabs\">\n <div class=\"mint-rack-editor__tabs-inner\">\n <button\n v-for=\"rack in editor.racks.value\"\n :key=\"rack.id\"\n :draggable=\"allowReorder && !readonly\"\n :class=\"[\n 'mint-rack-editor__tab',\n {\n 'mint-rack-editor__tab--active': editor.activeRackId.value === rack.id,\n 'mint-rack-editor__tab--dragging': draggingRackId === rack.id,\n 'mint-rack-editor__tab--drag-over': dragOverRackId === rack.id,\n },\n ]\"\n @click=\"handleTabClick(rack.id)\"\n @dragstart=\"handleRackDragStart($event, rack.id)\"\n @dragover=\"handleRackDragOver($event, rack.id)\"\n @dragleave=\"handleRackDragLeave\"\n @drop=\"handleRackDrop($event, rack.id)\"\n @dragend=\"handleRackDragEnd\"\n >\n <span\n class=\"mint-rack-editor__slot-dot\"\n :style=\"{ backgroundColor: SLOT_COLORS[rack.slot] }\"\n />\n {{ rack.name }}\n <span\n v-if=\"getWellCount(rack) > 0\"\n :class=\"[\n 'mint-rack-editor__count',\n editor.activeRackId.value === rack.id ? 'mint-rack-editor__count--active' : 'mint-rack-editor__count--inactive',\n ]\"\n >\n {{ getWellCount(rack) }}\n </span>\n <!-- Remove button -->\n <button\n v-if=\"editor.racks.value.length > minRacks && !readonly\"\n class=\"mint-rack-editor__tab-remove\"\n @click.stop=\"handleRemoveRack(rack.id)\"\n >\n ×\n </button>\n </button>\n </div>\n\n <!-- Add rack button -->\n <button\n v-if=\"editor.racks.value.length < maxRacks && !readonly\"\n class=\"mint-rack-editor__add-btn\"\n @click=\"handleAddRack\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n </svg>\n Add Rack\n </button>\n </div>\n\n <!-- Toolbar for active rack -->\n <div v-if=\"editor.activeRack.value && !readonly\" class=\"mint-rack-editor__toolbar\">\n <div class=\"mint-rack-editor__toolbar-group\">\n <!-- Format selector -->\n <div class=\"mint-rack-editor__toolbar-section\">\n <span class=\"mint-rack-editor__toolbar-label\">Plate</span>\n <div class=\"mint-rack-editor__format-btns\">\n <button\n v-for=\"opt in FORMAT_OPTIONS\"\n :key=\"opt.value\"\n :class=\"[\n 'mint-rack-editor__format-btn',\n editor.activeRack.value.format === opt.value ? 'mint-rack-editor__format-btn--active' : 'mint-rack-editor__format-btn--inactive',\n ]\"\n @click=\"handleFormatChange(opt.value)\"\n >\n {{ opt.label }}\n </button>\n </div>\n </div>\n\n <div class=\"mint-rack-editor__toolbar-divider\" />\n\n <!-- Slot selector -->\n <div class=\"mint-rack-editor__toolbar-section\">\n <span class=\"mint-rack-editor__toolbar-label\">Slot</span>\n <div class=\"mint-rack-editor__slot-btns\">\n <button\n v-for=\"s in SLOT_OPTIONS\"\n :key=\"s\"\n class=\"mint-rack-editor__slot-btn\"\n :style=\"{\n backgroundColor: editor.activeRack.value.slot === s ? SLOT_COLORS[s] : 'var(--bg-secondary)',\n color: editor.activeRack.value.slot === s ? 'white' : SLOT_COLORS[s],\n borderColor: editor.activeRack.value.slot === s ? 'transparent' : `${SLOT_COLORS[s]}40`,\n boxShadow: editor.activeRack.value.slot === s ? '0 2px 4px rgba(0,0,0,0.15)' : 'none',\n }\"\n @click=\"handleSlotChange(s)\"\n >\n {{ s }}\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"mint-rack-editor__toolbar-spacer\" />\n\n <!-- Actions -->\n <button class=\"mint-rack-editor__action-btn\" @click=\"handleClearAll\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10 11v6\" /><path d=\"M14 11v6\" /><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\" /><path d=\"M3 6h18\" /><path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" />\n </svg>\n Clear\n </button>\n <button class=\"mint-rack-editor__action-btn\" @click=\"handleFillSeries\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" /><path d=\"M3 9h18\" /><path d=\"M3 15h18\" /><path d=\"M9 3v18\" /><path d=\"M15 3v18\" />\n </svg>\n Fill Series\n </button>\n </div>\n\n <!-- Plate view -->\n <div v-if=\"editor.activeRack.value\" class=\"mint-rack-editor__plate\">\n <WellPlate\n :format=\"editor.activeRack.value.format\"\n :wells=\"activeRackWells\"\n :editable=\"editable && !readonly\"\n :show-well-labels=\"true\"\n :show-badges=\"showBadges\"\n :show-legend=\"showLegend\"\n :default-injection-volume=\"editor.activeRack.value.injectionVolume\"\n :readonly=\"readonly\"\n :size=\"wellPlateSize\"\n :show-sample-type-indicator=\"true\"\n @well-edit=\"handleWellEdit\"\n @well-clear=\"handleWellClear\"\n />\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/rack-editor.css';\n</style>\n","<script setup lang=\"ts\">\n/** Multi-rack editor for managing collections of well-plate racks with add/remove/reorder controls and per-well editing. */\nimport { ref, computed, watch } from 'vue'\nimport type { Rack, SlotPosition, WellPlateFormat, WellPlateSize, WellEditData, Well } from '../types'\nimport { useRackEditor } from '../composables/useRackEditor'\nimport WellPlate from './WellPlate.vue'\n\ninterface Props {\n modelValue?: Rack[]\n activeRackId?: string\n maxRacks?: number\n minRacks?: number\n allowReorder?: boolean\n editable?: boolean\n readonly?: boolean\n wellPlateSize?: WellPlateSize\n showLegend?: boolean\n showBadges?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: undefined,\n activeRackId: undefined,\n maxRacks: 10,\n minRacks: 1,\n allowReorder: true,\n editable: true,\n readonly: false,\n wellPlateSize: 'md',\n showLegend: true,\n showBadges: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [racks: Rack[]]\n 'update:activeRackId': [rackId: string]\n 'rack-add': [rack: Rack]\n 'rack-remove': [rackId: string]\n 'rack-reorder': [rackIds: string[]]\n 'well-edit': [rackId: string, wellId: string, data: WellEditData]\n}>()\n\nconst editor = useRackEditor(props.modelValue, {\n maxRacks: props.maxRacks,\n minRacks: props.minRacks,\n})\n\n// Sync external modelValue -> internal racks\nwatch(\n () => props.modelValue,\n (newRacks) => {\n if (newRacks && JSON.stringify(newRacks) !== JSON.stringify(editor.racks.value)) {\n editor.racks.value = newRacks.map(r => ({ ...r }))\n }\n },\n)\n\n// Sync external activeRackId -> internal\nwatch(\n () => props.activeRackId,\n (id) => {\n if (id && id !== editor.activeRackId.value) {\n editor.setActiveRack(id)\n }\n },\n)\n\n// Sync internal racks -> external modelValue\nwatch(\n editor.racks,\n (newRacks) => {\n emit('update:modelValue', newRacks)\n },\n { deep: true },\n)\n\n// Sync internal activeRackId -> external\nwatch(\n editor.activeRackId,\n (id) => {\n emit('update:activeRackId', id)\n },\n)\n\nconst SLOT_COLORS: Record<SlotPosition, string> = {\n R: '#ef4444',\n G: '#22c55e',\n B: '#3b82f6',\n Y: '#eab308',\n}\n\nconst SLOT_OPTIONS: SlotPosition[] = ['R', 'G', 'B', 'Y']\n\nconst FORMAT_OPTIONS: { value: WellPlateFormat; label: string }[] = [\n { value: 54, label: '54' },\n { value: 96, label: '96' },\n]\n\n// Drag state for rack reordering\nconst draggingRackId = ref<string | null>(null)\nconst dragOverRackId = ref<string | null>(null)\n\nfunction handleAddRack() {\n const rack = editor.addRack()\n emit('rack-add', rack)\n}\n\nfunction handleRemoveRack(rackId: string) {\n editor.removeRack(rackId)\n emit('rack-remove', rackId)\n}\n\nfunction handleTabClick(rackId: string) {\n editor.setActiveRack(rackId)\n}\n\n// Rack drag handlers\nfunction handleRackDragStart(event: DragEvent, rackId: string) {\n if (!props.allowReorder) return\n draggingRackId.value = rackId\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', rackId)\n }\n}\n\nfunction handleRackDragOver(event: DragEvent, rackId: string) {\n event.preventDefault()\n if (draggingRackId.value && draggingRackId.value !== rackId) {\n dragOverRackId.value = rackId\n }\n}\n\nfunction handleRackDragLeave() {\n dragOverRackId.value = null\n}\n\nfunction handleRackDrop(event: DragEvent, targetRackId: string) {\n event.preventDefault()\n if (!draggingRackId.value || draggingRackId.value === targetRackId) {\n clearDragState()\n return\n }\n\n const fromIndex = editor.racks.value.findIndex(r => r.id === draggingRackId.value)\n const toIndex = editor.racks.value.findIndex(r => r.id === targetRackId)\n\n if (fromIndex !== -1 && toIndex !== -1) {\n editor.reorderRacks(fromIndex, toIndex)\n emit('rack-reorder', editor.racks.value.map(r => r.id))\n }\n\n clearDragState()\n}\n\nfunction handleRackDragEnd() {\n clearDragState()\n}\n\nfunction clearDragState() {\n draggingRackId.value = null\n dragOverRackId.value = null\n}\n\n// Toolbar actions\nfunction handleFormatChange(format: WellPlateFormat) {\n if (!editor.activeRack.value) return\n editor.updateRack(editor.activeRack.value.id, { format })\n}\n\nfunction handleSlotChange(slot: SlotPosition) {\n if (!editor.activeRack.value) return\n editor.updateRack(editor.activeRack.value.id, { slot })\n}\n\nfunction handleClearAll() {\n if (!editor.activeRack.value) return\n editor.clearAllWells(editor.activeRack.value.id)\n}\n\nfunction handleFillSeries() {\n if (!editor.activeRack.value) return\n editor.fillSeries(editor.activeRack.value.id)\n}\n\n// Well edit from WellPlate\nfunction handleWellEdit(wellId: string, data: WellEditData) {\n if (!editor.activeRack.value) return\n const rackId = editor.activeRack.value.id\n\n const wellData: Partial<Well> = {\n id: wellId,\n state: data.label ? 'filled' : 'empty',\n sampleType: data.sampleType || undefined,\n metadata: {\n label: data.label,\n injectionVolume: data.injectionVolume,\n injectionCount: data.injectionCount,\n customMethod: data.customMethod || null,\n },\n }\n\n if (data.label) {\n editor.setWellData(rackId, wellId, wellData)\n } else {\n editor.clearWell(rackId, wellId)\n }\n\n emit('well-edit', rackId, wellId, data)\n}\n\nfunction handleWellClear(wellId: string) {\n if (!editor.activeRack.value) return\n editor.clearWell(editor.activeRack.value.id, wellId)\n}\n\n// Computed well count per rack\nfunction getWellCount(rack: Rack): number {\n return Object.keys(rack.wells).length\n}\n\nconst activeRackWells = computed(() => editor.activeRack.value?.wells ?? {})\n</script>\n\n<template>\n <div :class=\"['mint-rack-editor', { 'mint-rack-editor--readonly': readonly }]\">\n <!-- Rack tabs -->\n <div class=\"mint-rack-editor__tabs\">\n <div class=\"mint-rack-editor__tabs-inner\">\n <button\n v-for=\"rack in editor.racks.value\"\n :key=\"rack.id\"\n :draggable=\"allowReorder && !readonly\"\n :class=\"[\n 'mint-rack-editor__tab',\n {\n 'mint-rack-editor__tab--active': editor.activeRackId.value === rack.id,\n 'mint-rack-editor__tab--dragging': draggingRackId === rack.id,\n 'mint-rack-editor__tab--drag-over': dragOverRackId === rack.id,\n },\n ]\"\n @click=\"handleTabClick(rack.id)\"\n @dragstart=\"handleRackDragStart($event, rack.id)\"\n @dragover=\"handleRackDragOver($event, rack.id)\"\n @dragleave=\"handleRackDragLeave\"\n @drop=\"handleRackDrop($event, rack.id)\"\n @dragend=\"handleRackDragEnd\"\n >\n <span\n class=\"mint-rack-editor__slot-dot\"\n :style=\"{ backgroundColor: SLOT_COLORS[rack.slot] }\"\n />\n {{ rack.name }}\n <span\n v-if=\"getWellCount(rack) > 0\"\n :class=\"[\n 'mint-rack-editor__count',\n editor.activeRackId.value === rack.id ? 'mint-rack-editor__count--active' : 'mint-rack-editor__count--inactive',\n ]\"\n >\n {{ getWellCount(rack) }}\n </span>\n <!-- Remove button -->\n <button\n v-if=\"editor.racks.value.length > minRacks && !readonly\"\n class=\"mint-rack-editor__tab-remove\"\n @click.stop=\"handleRemoveRack(rack.id)\"\n >\n ×\n </button>\n </button>\n </div>\n\n <!-- Add rack button -->\n <button\n v-if=\"editor.racks.value.length < maxRacks && !readonly\"\n class=\"mint-rack-editor__add-btn\"\n @click=\"handleAddRack\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n </svg>\n Add Rack\n </button>\n </div>\n\n <!-- Toolbar for active rack -->\n <div v-if=\"editor.activeRack.value && !readonly\" class=\"mint-rack-editor__toolbar\">\n <div class=\"mint-rack-editor__toolbar-group\">\n <!-- Format selector -->\n <div class=\"mint-rack-editor__toolbar-section\">\n <span class=\"mint-rack-editor__toolbar-label\">Plate</span>\n <div class=\"mint-rack-editor__format-btns\">\n <button\n v-for=\"opt in FORMAT_OPTIONS\"\n :key=\"opt.value\"\n :class=\"[\n 'mint-rack-editor__format-btn',\n editor.activeRack.value.format === opt.value ? 'mint-rack-editor__format-btn--active' : 'mint-rack-editor__format-btn--inactive',\n ]\"\n @click=\"handleFormatChange(opt.value)\"\n >\n {{ opt.label }}\n </button>\n </div>\n </div>\n\n <div class=\"mint-rack-editor__toolbar-divider\" />\n\n <!-- Slot selector -->\n <div class=\"mint-rack-editor__toolbar-section\">\n <span class=\"mint-rack-editor__toolbar-label\">Slot</span>\n <div class=\"mint-rack-editor__slot-btns\">\n <button\n v-for=\"s in SLOT_OPTIONS\"\n :key=\"s\"\n class=\"mint-rack-editor__slot-btn\"\n :style=\"{\n backgroundColor: editor.activeRack.value.slot === s ? SLOT_COLORS[s] : 'var(--bg-secondary)',\n color: editor.activeRack.value.slot === s ? 'white' : SLOT_COLORS[s],\n borderColor: editor.activeRack.value.slot === s ? 'transparent' : `${SLOT_COLORS[s]}40`,\n boxShadow: editor.activeRack.value.slot === s ? '0 2px 4px rgba(0,0,0,0.15)' : 'none',\n }\"\n @click=\"handleSlotChange(s)\"\n >\n {{ s }}\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"mint-rack-editor__toolbar-spacer\" />\n\n <!-- Actions -->\n <button class=\"mint-rack-editor__action-btn\" @click=\"handleClearAll\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10 11v6\" /><path d=\"M14 11v6\" /><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\" /><path d=\"M3 6h18\" /><path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" />\n </svg>\n Clear\n </button>\n <button class=\"mint-rack-editor__action-btn\" @click=\"handleFillSeries\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" /><path d=\"M3 9h18\" /><path d=\"M3 15h18\" /><path d=\"M9 3v18\" /><path d=\"M15 3v18\" />\n </svg>\n Fill Series\n </button>\n </div>\n\n <!-- Plate view -->\n <div v-if=\"editor.activeRack.value\" class=\"mint-rack-editor__plate\">\n <WellPlate\n :format=\"editor.activeRack.value.format\"\n :wells=\"activeRackWells\"\n :editable=\"editable && !readonly\"\n :show-well-labels=\"true\"\n :show-badges=\"showBadges\"\n :show-legend=\"showLegend\"\n :default-injection-volume=\"editor.activeRack.value.injectionVolume\"\n :readonly=\"readonly\"\n :size=\"wellPlateSize\"\n :show-sample-type-indicator=\"true\"\n @well-edit=\"handleWellEdit\"\n @well-clear=\"handleWellClear\"\n />\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/rack-editor.css';\n</style>\n","<script setup lang=\"ts\">\n/** Drag-and-drop two-zone assigner for splitting sample groups into control and treatment arms. */\nimport { ref } from 'vue'\nimport type { GroupItem } from '../types'\nimport { useGroupAssignment, type GroupAssignmentState, type GroupAssignmentZone } from '../composables/useGroupAssignment'\n\ninterface Props {\n groups: GroupItem[]\n group1: string[]\n group2: string[]\n label1?: string\n label2?: string\n color1?: string\n color2?: string\n minPerGroup?: number\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n label1: 'Control',\n label2: 'Treatment',\n color1: '#3B82F6',\n color2: '#F43F5E',\n minPerGroup: 1,\n})\n\nconst emit = defineEmits<{\n 'update:group1': [names: string[]]\n 'update:group2': [names: string[]]\n}>()\n\nconst dragOverZone = ref<'zone1' | 'zone2' | null>(null)\nconst draggingGroup = ref<string | null>(null)\n\nconst assignment = useGroupAssignment({\n groups: () => props.groups,\n group1: () => props.group1,\n group2: () => props.group2,\n label1: () => props.label1,\n label2: () => props.label2,\n minPerGroup: () => props.minPerGroup,\n})\nconst { unassignedGroups, zone1Groups, zone2Groups, zone1Count, zone2Count, validationMessage } = assignment\n\nfunction handleDragStart(event: DragEvent, groupName: string) {\n const transfer = event.dataTransfer\n if (!transfer) return\n\n draggingGroup.value = groupName\n transfer.setData('groupName', groupName)\n transfer.effectAllowed = 'move'\n}\n\nfunction handleDragEnd() {\n draggingGroup.value = null\n dragOverZone.value = null\n}\n\nfunction handleDragOver(event: DragEvent, zone: GroupAssignmentZone) {\n event.preventDefault()\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move'\n }\n dragOverZone.value = zone\n}\n\nfunction handleDragLeave() {\n dragOverZone.value = null\n}\n\nfunction handleDrop(event: DragEvent, zone: GroupAssignmentZone) {\n event.preventDefault()\n dragOverZone.value = null\n\n const groupName = event.dataTransfer?.getData('groupName')\n if (!groupName) return\n\n assignToZone(groupName, zone)\n}\n\nfunction assignToZone(groupName: string, zone: GroupAssignmentZone) {\n emitAssignment(assignment.assignToZone(groupName, zone))\n}\n\nfunction removeFromZone(groupName: string, zone: GroupAssignmentZone) {\n const state = assignment.removeFromZone(groupName, zone)\n if (zone === 'zone1') {\n emit('update:group1', state.group1)\n } else {\n emit('update:group2', state.group2)\n }\n}\n\nfunction clearAll() {\n emitAssignment(assignment.clearAll())\n}\n\nfunction emitAssignment(state: GroupAssignmentState) {\n emit('update:group1', state.group1)\n emit('update:group2', state.group2)\n}\n</script>\n\n<template>\n <div class=\"mint-group-assigner\">\n <!-- Drop zones -->\n <div class=\"mint-group-assigner__zones\">\n <!-- Zone 1 (Control) -->\n <div\n :class=\"[\n 'mint-group-assigner__zone',\n 'mint-group-assigner__zone--1',\n dragOverZone === 'zone1' ? 'mint-group-assigner__zone--dragover' : '',\n ]\"\n :style=\"{ '--zone-color': color1 }\"\n @dragover=\"handleDragOver($event, 'zone1')\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop($event, 'zone1')\"\n >\n <div class=\"mint-group-assigner__zone-header\">\n <span class=\"mint-group-assigner__zone-label\">{{ label1 }}</span>\n <span class=\"mint-group-assigner__zone-count\">{{ zone1Count }} samples</span>\n </div>\n\n <div class=\"mint-group-assigner__zone-content\">\n <div\n v-for=\"group in zone1Groups\"\n :key=\"group.name\"\n class=\"mint-group-assigner__pill\"\n draggable=\"true\"\n @dragstart=\"handleDragStart($event, group.name)\"\n @dragend=\"handleDragEnd\"\n >\n <span\n class=\"mint-group-assigner__pill-color\"\n :style=\"{ backgroundColor: group.color }\"\n />\n <span class=\"mint-group-assigner__pill-name\">{{ group.name }}</span>\n <span class=\"mint-group-assigner__pill-count\">{{ group.count }}</span>\n <button\n type=\"button\"\n class=\"mint-group-assigner__pill-remove\"\n @click=\"removeFromZone(group.name, 'zone1')\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div v-if=\"zone1Groups.length === 0\" class=\"mint-group-assigner__zone-empty\">\n Drag groups here\n </div>\n </div>\n </div>\n\n <!-- Zone 2 (Treatment) -->\n <div\n :class=\"[\n 'mint-group-assigner__zone',\n 'mint-group-assigner__zone--2',\n dragOverZone === 'zone2' ? 'mint-group-assigner__zone--dragover' : '',\n ]\"\n :style=\"{ '--zone-color': color2 }\"\n @dragover=\"handleDragOver($event, 'zone2')\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop($event, 'zone2')\"\n >\n <div class=\"mint-group-assigner__zone-header\">\n <span class=\"mint-group-assigner__zone-label\">{{ label2 }}</span>\n <span class=\"mint-group-assigner__zone-count\">{{ zone2Count }} samples</span>\n </div>\n\n <div class=\"mint-group-assigner__zone-content\">\n <div\n v-for=\"group in zone2Groups\"\n :key=\"group.name\"\n class=\"mint-group-assigner__pill\"\n draggable=\"true\"\n @dragstart=\"handleDragStart($event, group.name)\"\n @dragend=\"handleDragEnd\"\n >\n <span\n class=\"mint-group-assigner__pill-color\"\n :style=\"{ backgroundColor: group.color }\"\n />\n <span class=\"mint-group-assigner__pill-name\">{{ group.name }}</span>\n <span class=\"mint-group-assigner__pill-count\">{{ group.count }}</span>\n <button\n type=\"button\"\n class=\"mint-group-assigner__pill-remove\"\n @click=\"removeFromZone(group.name, 'zone2')\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div v-if=\"zone2Groups.length === 0\" class=\"mint-group-assigner__zone-empty\">\n Drag groups here\n </div>\n </div>\n </div>\n </div>\n\n <!-- Unassigned groups -->\n <div v-if=\"unassignedGroups.length > 0\" class=\"mint-group-assigner__unassigned\">\n <div class=\"mint-group-assigner__unassigned-header\">\n <span class=\"mint-group-assigner__unassigned-title\">Available Groups</span>\n <button\n v-if=\"group1.length > 0 || group2.length > 0\"\n type=\"button\"\n class=\"mint-group-assigner__clear-btn\"\n @click=\"clearAll\"\n >\n Clear all\n </button>\n </div>\n\n <div class=\"mint-group-assigner__unassigned-list\">\n <div\n v-for=\"group in unassignedGroups\"\n :key=\"group.name\"\n :class=\"[\n 'mint-group-assigner__pill',\n 'mint-group-assigner__pill--unassigned',\n draggingGroup === group.name ? 'mint-group-assigner__pill--dragging' : '',\n ]\"\n draggable=\"true\"\n @dragstart=\"handleDragStart($event, group.name)\"\n @dragend=\"handleDragEnd\"\n >\n <span\n class=\"mint-group-assigner__pill-color\"\n :style=\"{ backgroundColor: group.color }\"\n />\n <span class=\"mint-group-assigner__pill-name\">{{ group.name }}</span>\n <span class=\"mint-group-assigner__pill-count\">{{ group.count }}</span>\n </div>\n </div>\n </div>\n\n <!-- Validation message -->\n <div v-if=\"validationMessage\" class=\"mint-group-assigner__validation\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\" />\n <line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\" />\n </svg>\n {{ validationMessage }}\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/group-assigner.css';\n</style>\n","<script setup lang=\"ts\">\n/** Drag-and-drop two-zone assigner for splitting sample groups into control and treatment arms. */\nimport { ref } from 'vue'\nimport type { GroupItem } from '../types'\nimport { useGroupAssignment, type GroupAssignmentState, type GroupAssignmentZone } from '../composables/useGroupAssignment'\n\ninterface Props {\n groups: GroupItem[]\n group1: string[]\n group2: string[]\n label1?: string\n label2?: string\n color1?: string\n color2?: string\n minPerGroup?: number\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n label1: 'Control',\n label2: 'Treatment',\n color1: '#3B82F6',\n color2: '#F43F5E',\n minPerGroup: 1,\n})\n\nconst emit = defineEmits<{\n 'update:group1': [names: string[]]\n 'update:group2': [names: string[]]\n}>()\n\nconst dragOverZone = ref<'zone1' | 'zone2' | null>(null)\nconst draggingGroup = ref<string | null>(null)\n\nconst assignment = useGroupAssignment({\n groups: () => props.groups,\n group1: () => props.group1,\n group2: () => props.group2,\n label1: () => props.label1,\n label2: () => props.label2,\n minPerGroup: () => props.minPerGroup,\n})\nconst { unassignedGroups, zone1Groups, zone2Groups, zone1Count, zone2Count, validationMessage } = assignment\n\nfunction handleDragStart(event: DragEvent, groupName: string) {\n const transfer = event.dataTransfer\n if (!transfer) return\n\n draggingGroup.value = groupName\n transfer.setData('groupName', groupName)\n transfer.effectAllowed = 'move'\n}\n\nfunction handleDragEnd() {\n draggingGroup.value = null\n dragOverZone.value = null\n}\n\nfunction handleDragOver(event: DragEvent, zone: GroupAssignmentZone) {\n event.preventDefault()\n if (event.dataTransfer) {\n event.dataTransfer.dropEffect = 'move'\n }\n dragOverZone.value = zone\n}\n\nfunction handleDragLeave() {\n dragOverZone.value = null\n}\n\nfunction handleDrop(event: DragEvent, zone: GroupAssignmentZone) {\n event.preventDefault()\n dragOverZone.value = null\n\n const groupName = event.dataTransfer?.getData('groupName')\n if (!groupName) return\n\n assignToZone(groupName, zone)\n}\n\nfunction assignToZone(groupName: string, zone: GroupAssignmentZone) {\n emitAssignment(assignment.assignToZone(groupName, zone))\n}\n\nfunction removeFromZone(groupName: string, zone: GroupAssignmentZone) {\n const state = assignment.removeFromZone(groupName, zone)\n if (zone === 'zone1') {\n emit('update:group1', state.group1)\n } else {\n emit('update:group2', state.group2)\n }\n}\n\nfunction clearAll() {\n emitAssignment(assignment.clearAll())\n}\n\nfunction emitAssignment(state: GroupAssignmentState) {\n emit('update:group1', state.group1)\n emit('update:group2', state.group2)\n}\n</script>\n\n<template>\n <div class=\"mint-group-assigner\">\n <!-- Drop zones -->\n <div class=\"mint-group-assigner__zones\">\n <!-- Zone 1 (Control) -->\n <div\n :class=\"[\n 'mint-group-assigner__zone',\n 'mint-group-assigner__zone--1',\n dragOverZone === 'zone1' ? 'mint-group-assigner__zone--dragover' : '',\n ]\"\n :style=\"{ '--zone-color': color1 }\"\n @dragover=\"handleDragOver($event, 'zone1')\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop($event, 'zone1')\"\n >\n <div class=\"mint-group-assigner__zone-header\">\n <span class=\"mint-group-assigner__zone-label\">{{ label1 }}</span>\n <span class=\"mint-group-assigner__zone-count\">{{ zone1Count }} samples</span>\n </div>\n\n <div class=\"mint-group-assigner__zone-content\">\n <div\n v-for=\"group in zone1Groups\"\n :key=\"group.name\"\n class=\"mint-group-assigner__pill\"\n draggable=\"true\"\n @dragstart=\"handleDragStart($event, group.name)\"\n @dragend=\"handleDragEnd\"\n >\n <span\n class=\"mint-group-assigner__pill-color\"\n :style=\"{ backgroundColor: group.color }\"\n />\n <span class=\"mint-group-assigner__pill-name\">{{ group.name }}</span>\n <span class=\"mint-group-assigner__pill-count\">{{ group.count }}</span>\n <button\n type=\"button\"\n class=\"mint-group-assigner__pill-remove\"\n @click=\"removeFromZone(group.name, 'zone1')\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div v-if=\"zone1Groups.length === 0\" class=\"mint-group-assigner__zone-empty\">\n Drag groups here\n </div>\n </div>\n </div>\n\n <!-- Zone 2 (Treatment) -->\n <div\n :class=\"[\n 'mint-group-assigner__zone',\n 'mint-group-assigner__zone--2',\n dragOverZone === 'zone2' ? 'mint-group-assigner__zone--dragover' : '',\n ]\"\n :style=\"{ '--zone-color': color2 }\"\n @dragover=\"handleDragOver($event, 'zone2')\"\n @dragleave=\"handleDragLeave\"\n @drop=\"handleDrop($event, 'zone2')\"\n >\n <div class=\"mint-group-assigner__zone-header\">\n <span class=\"mint-group-assigner__zone-label\">{{ label2 }}</span>\n <span class=\"mint-group-assigner__zone-count\">{{ zone2Count }} samples</span>\n </div>\n\n <div class=\"mint-group-assigner__zone-content\">\n <div\n v-for=\"group in zone2Groups\"\n :key=\"group.name\"\n class=\"mint-group-assigner__pill\"\n draggable=\"true\"\n @dragstart=\"handleDragStart($event, group.name)\"\n @dragend=\"handleDragEnd\"\n >\n <span\n class=\"mint-group-assigner__pill-color\"\n :style=\"{ backgroundColor: group.color }\"\n />\n <span class=\"mint-group-assigner__pill-name\">{{ group.name }}</span>\n <span class=\"mint-group-assigner__pill-count\">{{ group.count }}</span>\n <button\n type=\"button\"\n class=\"mint-group-assigner__pill-remove\"\n @click=\"removeFromZone(group.name, 'zone2')\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n\n <div v-if=\"zone2Groups.length === 0\" class=\"mint-group-assigner__zone-empty\">\n Drag groups here\n </div>\n </div>\n </div>\n </div>\n\n <!-- Unassigned groups -->\n <div v-if=\"unassignedGroups.length > 0\" class=\"mint-group-assigner__unassigned\">\n <div class=\"mint-group-assigner__unassigned-header\">\n <span class=\"mint-group-assigner__unassigned-title\">Available Groups</span>\n <button\n v-if=\"group1.length > 0 || group2.length > 0\"\n type=\"button\"\n class=\"mint-group-assigner__clear-btn\"\n @click=\"clearAll\"\n >\n Clear all\n </button>\n </div>\n\n <div class=\"mint-group-assigner__unassigned-list\">\n <div\n v-for=\"group in unassignedGroups\"\n :key=\"group.name\"\n :class=\"[\n 'mint-group-assigner__pill',\n 'mint-group-assigner__pill--unassigned',\n draggingGroup === group.name ? 'mint-group-assigner__pill--dragging' : '',\n ]\"\n draggable=\"true\"\n @dragstart=\"handleDragStart($event, group.name)\"\n @dragend=\"handleDragEnd\"\n >\n <span\n class=\"mint-group-assigner__pill-color\"\n :style=\"{ backgroundColor: group.color }\"\n />\n <span class=\"mint-group-assigner__pill-name\">{{ group.name }}</span>\n <span class=\"mint-group-assigner__pill-count\">{{ group.count }}</span>\n </div>\n </div>\n </div>\n\n <!-- Validation message -->\n <div v-if=\"validationMessage\" class=\"mint-group-assigner__validation\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\" />\n <line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\" />\n </svg>\n {{ validationMessage }}\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/group-assigner.css';\n</style>\n","<script setup lang=\"ts\">\n/** Editor for a single reagent definition with axis orientation, dilution-series generator, and position-count validation for 96/384-well plates. */\nimport { computed, ref } from 'vue'\nimport NumberInput from './NumberInput.vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport {\n useReagentSeries,\n DEFAULT_PRESETS,\n DEFAULT_UNITS,\n type LevelEntry,\n type DilutionPreset,\n} from '../composables/useReagentSeries'\n\nexport interface ReagentDefinition {\n id: string\n name: string\n color: string\n axis: 'row' | 'column'\n startPosition: string\n levels: LevelEntry[]\n unit: string\n}\n\ninterface Props {\n modelValue: ReagentDefinition\n plateFormat?: 96 | 384\n units?: string[]\n presets?: DilutionPreset[]\n maxLevels?: number\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n plateFormat: 96,\n units: () => DEFAULT_UNITS,\n presets: () => DEFAULT_PRESETS,\n maxLevels: 24,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: ReagentDefinition]\n remove: []\n}>()\n\nconst { generateSeries, sortLevels, totalPositions } = useReagentSeries()\n\n// --- Local generator state ---\nconst generatorStart = ref(100)\nconst generatorCount = ref(8)\nconst manualValue = ref<number | undefined>(undefined)\n\n// --- Computed plate dimensions ---\nconst plateRows = computed(() => (props.plateFormat === 384 ? 16 : 8))\nconst plateCols = computed(() => (props.plateFormat === 384 ? 24 : 12))\n\nconst axisOptions = [\n { value: 'row', label: 'Rows' },\n { value: 'column', label: 'Columns' },\n]\n\n// --- Start position validation ---\nconst startPositionError = computed<string | null>(() => {\n const { axis, startPosition } = props.modelValue\n if (!startPosition) return null\n\n if (axis === 'row') {\n const letter = startPosition.toUpperCase()\n if (!/^[A-Z]$/.test(letter)) return 'Must be a letter'\n const idx = letter.charCodeAt(0) - 65\n if (idx >= plateRows.value) return `Max row: ${String.fromCharCode(64 + plateRows.value)}`\n } else {\n const num = parseInt(startPosition, 10)\n if (isNaN(num) || num < 1) return 'Must be a number ≥ 1'\n if (num > plateCols.value) return `Max col: ${plateCols.value}`\n }\n return null\n})\n\n// --- Position summary ---\nconst positionCount = computed(() => totalPositions(props.modelValue.levels))\n\nconst positionOverflows = computed(() => {\n const { axis, startPosition, levels } = props.modelValue\n if (!startPosition || levels.length === 0 || startPositionError.value) return false\n\n const span = positionCount.value\n if (axis === 'row') {\n const startIdx = startPosition.toUpperCase().charCodeAt(0) - 65\n return startIdx + span > plateRows.value\n } else {\n const startIdx = parseInt(startPosition, 10)\n return startIdx + span - 1 > plateCols.value\n }\n})\n\nconst positionRange = computed(() => {\n const { axis, startPosition, levels } = props.modelValue\n if (!startPosition || levels.length === 0 || startPositionError.value) return null\n\n const span = positionCount.value\n if (span === 0) return null\n\n if (axis === 'row') {\n const startIdx = startPosition.toUpperCase().charCodeAt(0) - 65\n const endIdx = Math.min(startIdx + span - 1, plateRows.value - 1)\n const startLetter = String.fromCharCode(65 + startIdx)\n const endLetter = String.fromCharCode(65 + endIdx)\n return `${startLetter}–${endLetter}`\n } else {\n const startIdx = parseInt(startPosition, 10)\n if (isNaN(startIdx)) return null\n const endIdx = Math.min(startIdx + span - 1, plateCols.value)\n return `${startIdx}–${endIdx}`\n }\n})\n\n// --- Update helpers ---\nfunction update(partial: Partial<ReagentDefinition>) {\n emit('update:modelValue', { ...props.modelValue, ...partial })\n}\n\nfunction updateName(event: Event) {\n update({ name: (event.target as HTMLInputElement).value })\n}\n\nfunction updateColor(event: Event) {\n update({ color: (event.target as HTMLInputElement).value })\n}\n\nfunction updateAxis(value: string | number) {\n update({ axis: value as 'row' | 'column', startPosition: '' })\n}\n\nfunction updateStartPosition(event: Event) {\n const raw = (event.target as HTMLInputElement).value\n update({ startPosition: raw })\n}\n\nfunction updateUnit(event: Event) {\n update({ unit: (event.target as HTMLSelectElement).value })\n}\n\n// --- Level manipulation ---\nfunction setLevels(levels: LevelEntry[]) {\n update({ levels: sortLevels(levels) })\n}\n\nfunction applyPreset(preset: DilutionPreset) {\n if (generatorStart.value <= 0 || generatorCount.value < 1) return\n const count = Math.min(generatorCount.value, props.maxLevels)\n const levels = generateSeries(generatorStart.value, count, preset.factor)\n setLevels(levels)\n}\n\nfunction addManualLevel() {\n if (manualValue.value === undefined || manualValue.value < 0) return\n if (props.modelValue.levels.length >= props.maxLevels) return\n\n const exists = props.modelValue.levels.some((l) => l.value === manualValue.value)\n if (exists) return\n\n setLevels([...props.modelValue.levels, { value: manualValue.value, replicates: 1 }])\n manualValue.value = undefined\n}\n\nfunction handleManualKeydown(event: KeyboardEvent) {\n if (event.key === 'Enter') {\n event.preventDefault()\n addManualLevel()\n }\n}\n\nfunction removeLevel(index: number) {\n const next = [...props.modelValue.levels]\n next.splice(index, 1)\n setLevels(next)\n}\n\nfunction updateLevelReplicates(index: number, replicates: number | undefined) {\n if (replicates === undefined || replicates < 1) return\n const next = [...props.modelValue.levels]\n next[index] = { ...next[index], replicates }\n update({ levels: next })\n}\n\nfunction formatLevel(value: number): string {\n if (!Number.isFinite(value)) return '–'\n if (value === 0) return 'Ctrl (0)'\n if (value >= 1000) return value.toLocaleString('en-US', { maximumSignificantDigits: 4 })\n if (value >= 1) return value.toString()\n if (value >= 0.001) return value.toFixed(3)\n return value.toExponential(1)\n}\n</script>\n\n<template>\n <div\n class=\"mint-reagent-editor\"\n :style=\"{ '--reagent-color': modelValue.color }\"\n >\n <!-- Identity -->\n <div class=\"mint-reagent-editor__section\">\n <div class=\"mint-reagent-editor__identity\">\n <label class=\"mint-reagent-editor__color-wrapper\">\n <input\n type=\"color\"\n :value=\"modelValue.color\"\n class=\"mint-reagent-editor__color-input\"\n aria-label=\"Reagent color\"\n @input=\"updateColor\"\n />\n <span\n class=\"mint-reagent-editor__color-swatch\"\n :style=\"{ backgroundColor: modelValue.color }\"\n />\n </label>\n <input\n type=\"text\"\n :value=\"modelValue.name\"\n placeholder=\"Reagent name\"\n class=\"mint-reagent-editor__name-input\"\n maxlength=\"64\"\n aria-label=\"Reagent name\"\n @input=\"updateName\"\n />\n </div>\n </div>\n\n <!-- Position Mapping -->\n <div class=\"mint-reagent-editor__section\">\n <div class=\"mint-reagent-editor__section-header\">\n <svg class=\"mint-reagent-editor__section-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" /><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" /><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" /><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" />\n </svg>\n Position\n </div>\n <div class=\"mint-reagent-editor__position-row\">\n <div class=\"mint-reagent-editor__field mint-reagent-editor__field--flex\">\n <label class=\"mint-reagent-editor__label\">Axis</label>\n <BaseSelect\n :model-value=\"modelValue.axis\"\n :options=\"axisOptions\"\n size=\"sm\"\n @update:model-value=\"updateAxis\"\n />\n </div>\n <div class=\"mint-reagent-editor__field mint-reagent-editor__field--flex\">\n <label class=\"mint-reagent-editor__label\">Start</label>\n <input\n type=\"text\"\n :value=\"modelValue.startPosition\"\n :placeholder=\"modelValue.axis === 'row' ? 'e.g. B' : 'e.g. 2'\"\n :class=\"[\n 'mint-reagent-editor__inline-input',\n startPositionError ? 'mint-reagent-editor__inline-input--error' : '',\n ]\"\n :maxlength=\"modelValue.axis === 'row' ? 1 : 2\"\n :aria-invalid=\"!!startPositionError\"\n aria-label=\"Start position\"\n @input=\"updateStartPosition\"\n />\n <span v-if=\"startPositionError\" class=\"mint-reagent-editor__field-error\">\n {{ startPositionError }}\n </span>\n </div>\n </div>\n </div>\n\n <!-- Series -->\n <div class=\"mint-reagent-editor__section\">\n <div class=\"mint-reagent-editor__section-header\">\n <svg class=\"mint-reagent-editor__section-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 3v18h18\" /><path d=\"m19 9-5 5-4-4-3 3\" />\n </svg>\n Series\n <select\n :value=\"modelValue.unit\"\n class=\"mint-reagent-editor__unit-select\"\n @change=\"updateUnit\"\n >\n <option v-for=\"u in units\" :key=\"u\" :value=\"u\">{{ u }}</option>\n </select>\n </div>\n\n <!-- Dilution generator -->\n <div class=\"mint-reagent-editor__generator\">\n <div class=\"mint-reagent-editor__generator-inputs\">\n <div class=\"mint-reagent-editor__field mint-reagent-editor__field--flex\">\n <label class=\"mint-reagent-editor__label\">Start</label>\n <NumberInput v-model=\"generatorStart\" :min=\"0\" :step=\"10\" size=\"sm\" />\n </div>\n <div class=\"mint-reagent-editor__field mint-reagent-editor__field--flex\">\n <label class=\"mint-reagent-editor__label\">Doses</label>\n <NumberInput v-model=\"generatorCount\" :min=\"2\" :max=\"maxLevels\" :step=\"1\" size=\"sm\" />\n </div>\n </div>\n <div class=\"mint-reagent-editor__presets\">\n <button\n v-for=\"preset in presets\"\n :key=\"preset.label\"\n type=\"button\"\n class=\"mint-reagent-editor__preset-btn\"\n @click=\"applyPreset(preset)\"\n >\n {{ preset.label }}\n </button>\n </div>\n </div>\n\n <!-- Manual entry -->\n <div class=\"mint-reagent-editor__manual\" @keydown=\"handleManualKeydown\">\n <NumberInput\n v-model=\"manualValue\"\n :min=\"0\"\n :step=\"1\"\n size=\"sm\"\n placeholder=\"Add value\"\n />\n <button\n type=\"button\"\n class=\"mint-reagent-editor__add-btn\"\n :disabled=\"manualValue === undefined || manualValue < 0 || modelValue.levels.length >= maxLevels\"\n aria-label=\"Add level\"\n @click=\"addManualLevel\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n </svg>\n </button>\n </div>\n\n <!-- Level table -->\n <div v-if=\"modelValue.levels.length > 0\" class=\"mint-reagent-editor__table\">\n <div class=\"mint-reagent-editor__table-header\">\n <span class=\"mint-reagent-editor__table-col mint-reagent-editor__table-col--level\">Level</span>\n <span class=\"mint-reagent-editor__table-col mint-reagent-editor__table-col--reps\">Reps</span>\n <span class=\"mint-reagent-editor__table-col mint-reagent-editor__table-col--action\" />\n </div>\n <div class=\"mint-reagent-editor__table-body\">\n <div\n v-for=\"(level, i) in modelValue.levels\"\n :key=\"level.value\"\n class=\"mint-reagent-editor__table-row\"\n >\n <span class=\"mint-reagent-editor__table-col mint-reagent-editor__table-col--level\">\n <span class=\"mint-reagent-editor__level-value\">{{ formatLevel(level.value) }}</span>\n <span v-if=\"level.value !== 0\" class=\"mint-reagent-editor__level-unit\">{{ modelValue.unit }}</span>\n </span>\n <span class=\"mint-reagent-editor__table-col mint-reagent-editor__table-col--reps\">\n <NumberInput\n :model-value=\"level.replicates\"\n :min=\"1\"\n :max=\"12\"\n :step=\"1\"\n size=\"sm\"\n @update:model-value=\"updateLevelReplicates(i, $event)\"\n />\n </span>\n <span class=\"mint-reagent-editor__table-col mint-reagent-editor__table-col--action\">\n <button\n type=\"button\"\n class=\"mint-reagent-editor__remove-level-btn\"\n aria-label=\"Remove level\"\n @click=\"removeLevel(i)\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </span>\n </div>\n </div>\n </div>\n\n <div v-else class=\"mint-reagent-editor__empty\">\n Use a preset above or add levels manually\n </div>\n </div>\n\n <!-- Position Summary -->\n <div\n v-if=\"modelValue.levels.length > 0 && modelValue.startPosition && !startPositionError\"\n :class=\"[\n 'mint-reagent-editor__summary',\n positionOverflows ? 'mint-reagent-editor__summary--overflow' : '',\n ]\"\n >\n <div class=\"mint-reagent-editor__summary-item\">\n <span class=\"mint-reagent-editor__summary-value\">{{ modelValue.levels.length }}</span>\n <span class=\"mint-reagent-editor__summary-label\">levels</span>\n </div>\n <div class=\"mint-reagent-editor__summary-dot\" />\n <div class=\"mint-reagent-editor__summary-item\">\n <span class=\"mint-reagent-editor__summary-value\">{{ positionCount }}</span>\n <span class=\"mint-reagent-editor__summary-label\">positions</span>\n </div>\n <div v-if=\"positionRange\" class=\"mint-reagent-editor__summary-dot\" />\n <div v-if=\"positionRange\" class=\"mint-reagent-editor__summary-item\">\n <span class=\"mint-reagent-editor__summary-value\">{{ positionRange }}</span>\n <span class=\"mint-reagent-editor__summary-label\">{{ modelValue.axis === 'row' ? 'rows' : 'cols' }}</span>\n </div>\n <span v-if=\"positionOverflows\" class=\"mint-reagent-editor__summary-warning\">\n Exceeds plate\n </span>\n </div>\n\n <!-- Remove -->\n <div class=\"mint-reagent-editor__footer\">\n <BaseButton variant=\"danger\" size=\"sm\" @click=\"emit('remove')\">\n Remove Reagent\n </BaseButton>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/reagent-editor.css';\n</style>\n","<script setup lang=\"ts\">\n/** Editor for a single reagent definition with axis orientation, dilution-series generator, and position-count validation for 96/384-well plates. */\nimport { computed, ref } from 'vue'\nimport NumberInput from './NumberInput.vue'\nimport BaseButton from './BaseButton.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport {\n useReagentSeries,\n DEFAULT_PRESETS,\n DEFAULT_UNITS,\n type LevelEntry,\n type DilutionPreset,\n} from '../composables/useReagentSeries'\n\nexport interface ReagentDefinition {\n id: string\n name: string\n color: string\n axis: 'row' | 'column'\n startPosition: string\n levels: LevelEntry[]\n unit: string\n}\n\ninterface Props {\n modelValue: ReagentDefinition\n plateFormat?: 96 | 384\n units?: string[]\n presets?: DilutionPreset[]\n maxLevels?: number\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n plateFormat: 96,\n units: () => DEFAULT_UNITS,\n presets: () => DEFAULT_PRESETS,\n maxLevels: 24,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: ReagentDefinition]\n remove: []\n}>()\n\nconst { generateSeries, sortLevels, totalPositions } = useReagentSeries()\n\n// --- Local generator state ---\nconst generatorStart = ref(100)\nconst generatorCount = ref(8)\nconst manualValue = ref<number | undefined>(undefined)\n\n// --- Computed plate dimensions ---\nconst plateRows = computed(() => (props.plateFormat === 384 ? 16 : 8))\nconst plateCols = computed(() => (props.plateFormat === 384 ? 24 : 12))\n\nconst axisOptions = [\n { value: 'row', label: 'Rows' },\n { value: 'column', label: 'Columns' },\n]\n\n// --- Start position validation ---\nconst startPositionError = computed<string | null>(() => {\n const { axis, startPosition } = props.modelValue\n if (!startPosition) return null\n\n if (axis === 'row') {\n const letter = startPosition.toUpperCase()\n if (!/^[A-Z]$/.test(letter)) return 'Must be a letter'\n const idx = letter.charCodeAt(0) - 65\n if (idx >= plateRows.value) return `Max row: ${String.fromCharCode(64 + plateRows.value)}`\n } else {\n const num = parseInt(startPosition, 10)\n if (isNaN(num) || num < 1) return 'Must be a number ≥ 1'\n if (num > plateCols.value) return `Max col: ${plateCols.value}`\n }\n return null\n})\n\n// --- Position summary ---\nconst positionCount = computed(() => totalPositions(props.modelValue.levels))\n\nconst positionOverflows = computed(() => {\n const { axis, startPosition, levels } = props.modelValue\n if (!startPosition || levels.length === 0 || startPositionError.value) return false\n\n const span = positionCount.value\n if (axis === 'row') {\n const startIdx = startPosition.toUpperCase().charCodeAt(0) - 65\n return startIdx + span > plateRows.value\n } else {\n const startIdx = parseInt(startPosition, 10)\n return startIdx + span - 1 > plateCols.value\n }\n})\n\nconst positionRange = computed(() => {\n const { axis, startPosition, levels } = props.modelValue\n if (!startPosition || levels.length === 0 || startPositionError.value) return null\n\n const span = positionCount.value\n if (span === 0) return null\n\n if (axis === 'row') {\n const startIdx = startPosition.toUpperCase().charCodeAt(0) - 65\n const endIdx = Math.min(startIdx + span - 1, plateRows.value - 1)\n const startLetter = String.fromCharCode(65 + startIdx)\n const endLetter = String.fromCharCode(65 + endIdx)\n return `${startLetter}–${endLetter}`\n } else {\n const startIdx = parseInt(startPosition, 10)\n if (isNaN(startIdx)) return null\n const endIdx = Math.min(startIdx + span - 1, plateCols.value)\n return `${startIdx}–${endIdx}`\n }\n})\n\n// --- Update helpers ---\nfunction update(partial: Partial<ReagentDefinition>) {\n emit('update:modelValue', { ...props.modelValue, ...partial })\n}\n\nfunction updateName(event: Event) {\n update({ name: (event.target as HTMLInputElement).value })\n}\n\nfunction updateColor(event: Event) {\n update({ color: (event.target as HTMLInputElement).value })\n}\n\nfunction updateAxis(value: string | number) {\n update({ axis: value as 'row' | 'column', startPosition: '' })\n}\n\nfunction updateStartPosition(event: Event) {\n const raw = (event.target as HTMLInputElement).value\n update({ startPosition: raw })\n}\n\nfunction updateUnit(event: Event) {\n update({ unit: (event.target as HTMLSelectElement).value })\n}\n\n// --- Level manipulation ---\nfunction setLevels(levels: LevelEntry[]) {\n update({ levels: sortLevels(levels) })\n}\n\nfunction applyPreset(preset: DilutionPreset) {\n if (generatorStart.value <= 0 || generatorCount.value < 1) return\n const count = Math.min(generatorCount.value, props.maxLevels)\n const levels = generateSeries(generatorStart.value, count, preset.factor)\n setLevels(levels)\n}\n\nfunction addManualLevel() {\n if (manualValue.value === undefined || manualValue.value < 0) return\n if (props.modelValue.levels.length >= props.maxLevels) return\n\n const exists = props.modelValue.levels.some((l) => l.value === manualValue.value)\n if (exists) return\n\n setLevels([...props.modelValue.levels, { value: manualValue.value, replicates: 1 }])\n manualValue.value = undefined\n}\n\nfunction handleManualKeydown(event: KeyboardEvent) {\n if (event.key === 'Enter') {\n event.preventDefault()\n addManualLevel()\n }\n}\n\nfunction removeLevel(index: number) {\n const next = [...props.modelValue.levels]\n next.splice(index, 1)\n setLevels(next)\n}\n\nfunction updateLevelReplicates(index: number, replicates: number | undefined) {\n if (replicates === undefined || replicates < 1) return\n const next = [...props.modelValue.levels]\n next[index] = { ...next[index], replicates }\n update({ levels: next })\n}\n\nfunction formatLevel(value: number): string {\n if (!Number.isFinite(value)) return '–'\n if (value === 0) return 'Ctrl (0)'\n if (value >= 1000) return value.toLocaleString('en-US', { maximumSignificantDigits: 4 })\n if (value >= 1) return value.toString()\n if (value >= 0.001) return value.toFixed(3)\n return value.toExponential(1)\n}\n</script>\n\n<template>\n <div\n class=\"mint-reagent-editor\"\n :style=\"{ '--reagent-color': modelValue.color }\"\n >\n <!-- Identity -->\n <div class=\"mint-reagent-editor__section\">\n <div class=\"mint-reagent-editor__identity\">\n <label class=\"mint-reagent-editor__color-wrapper\">\n <input\n type=\"color\"\n :value=\"modelValue.color\"\n class=\"mint-reagent-editor__color-input\"\n aria-label=\"Reagent color\"\n @input=\"updateColor\"\n />\n <span\n class=\"mint-reagent-editor__color-swatch\"\n :style=\"{ backgroundColor: modelValue.color }\"\n />\n </label>\n <input\n type=\"text\"\n :value=\"modelValue.name\"\n placeholder=\"Reagent name\"\n class=\"mint-reagent-editor__name-input\"\n maxlength=\"64\"\n aria-label=\"Reagent name\"\n @input=\"updateName\"\n />\n </div>\n </div>\n\n <!-- Position Mapping -->\n <div class=\"mint-reagent-editor__section\">\n <div class=\"mint-reagent-editor__section-header\">\n <svg class=\"mint-reagent-editor__section-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" /><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" /><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" /><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" />\n </svg>\n Position\n </div>\n <div class=\"mint-reagent-editor__position-row\">\n <div class=\"mint-reagent-editor__field mint-reagent-editor__field--flex\">\n <label class=\"mint-reagent-editor__label\">Axis</label>\n <BaseSelect\n :model-value=\"modelValue.axis\"\n :options=\"axisOptions\"\n size=\"sm\"\n @update:model-value=\"updateAxis\"\n />\n </div>\n <div class=\"mint-reagent-editor__field mint-reagent-editor__field--flex\">\n <label class=\"mint-reagent-editor__label\">Start</label>\n <input\n type=\"text\"\n :value=\"modelValue.startPosition\"\n :placeholder=\"modelValue.axis === 'row' ? 'e.g. B' : 'e.g. 2'\"\n :class=\"[\n 'mint-reagent-editor__inline-input',\n startPositionError ? 'mint-reagent-editor__inline-input--error' : '',\n ]\"\n :maxlength=\"modelValue.axis === 'row' ? 1 : 2\"\n :aria-invalid=\"!!startPositionError\"\n aria-label=\"Start position\"\n @input=\"updateStartPosition\"\n />\n <span v-if=\"startPositionError\" class=\"mint-reagent-editor__field-error\">\n {{ startPositionError }}\n </span>\n </div>\n </div>\n </div>\n\n <!-- Series -->\n <div class=\"mint-reagent-editor__section\">\n <div class=\"mint-reagent-editor__section-header\">\n <svg class=\"mint-reagent-editor__section-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M3 3v18h18\" /><path d=\"m19 9-5 5-4-4-3 3\" />\n </svg>\n Series\n <select\n :value=\"modelValue.unit\"\n class=\"mint-reagent-editor__unit-select\"\n @change=\"updateUnit\"\n >\n <option v-for=\"u in units\" :key=\"u\" :value=\"u\">{{ u }}</option>\n </select>\n </div>\n\n <!-- Dilution generator -->\n <div class=\"mint-reagent-editor__generator\">\n <div class=\"mint-reagent-editor__generator-inputs\">\n <div class=\"mint-reagent-editor__field mint-reagent-editor__field--flex\">\n <label class=\"mint-reagent-editor__label\">Start</label>\n <NumberInput v-model=\"generatorStart\" :min=\"0\" :step=\"10\" size=\"sm\" />\n </div>\n <div class=\"mint-reagent-editor__field mint-reagent-editor__field--flex\">\n <label class=\"mint-reagent-editor__label\">Doses</label>\n <NumberInput v-model=\"generatorCount\" :min=\"2\" :max=\"maxLevels\" :step=\"1\" size=\"sm\" />\n </div>\n </div>\n <div class=\"mint-reagent-editor__presets\">\n <button\n v-for=\"preset in presets\"\n :key=\"preset.label\"\n type=\"button\"\n class=\"mint-reagent-editor__preset-btn\"\n @click=\"applyPreset(preset)\"\n >\n {{ preset.label }}\n </button>\n </div>\n </div>\n\n <!-- Manual entry -->\n <div class=\"mint-reagent-editor__manual\" @keydown=\"handleManualKeydown\">\n <NumberInput\n v-model=\"manualValue\"\n :min=\"0\"\n :step=\"1\"\n size=\"sm\"\n placeholder=\"Add value\"\n />\n <button\n type=\"button\"\n class=\"mint-reagent-editor__add-btn\"\n :disabled=\"manualValue === undefined || manualValue < 0 || modelValue.levels.length >= maxLevels\"\n aria-label=\"Add level\"\n @click=\"addManualLevel\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n </svg>\n </button>\n </div>\n\n <!-- Level table -->\n <div v-if=\"modelValue.levels.length > 0\" class=\"mint-reagent-editor__table\">\n <div class=\"mint-reagent-editor__table-header\">\n <span class=\"mint-reagent-editor__table-col mint-reagent-editor__table-col--level\">Level</span>\n <span class=\"mint-reagent-editor__table-col mint-reagent-editor__table-col--reps\">Reps</span>\n <span class=\"mint-reagent-editor__table-col mint-reagent-editor__table-col--action\" />\n </div>\n <div class=\"mint-reagent-editor__table-body\">\n <div\n v-for=\"(level, i) in modelValue.levels\"\n :key=\"level.value\"\n class=\"mint-reagent-editor__table-row\"\n >\n <span class=\"mint-reagent-editor__table-col mint-reagent-editor__table-col--level\">\n <span class=\"mint-reagent-editor__level-value\">{{ formatLevel(level.value) }}</span>\n <span v-if=\"level.value !== 0\" class=\"mint-reagent-editor__level-unit\">{{ modelValue.unit }}</span>\n </span>\n <span class=\"mint-reagent-editor__table-col mint-reagent-editor__table-col--reps\">\n <NumberInput\n :model-value=\"level.replicates\"\n :min=\"1\"\n :max=\"12\"\n :step=\"1\"\n size=\"sm\"\n @update:model-value=\"updateLevelReplicates(i, $event)\"\n />\n </span>\n <span class=\"mint-reagent-editor__table-col mint-reagent-editor__table-col--action\">\n <button\n type=\"button\"\n class=\"mint-reagent-editor__remove-level-btn\"\n aria-label=\"Remove level\"\n @click=\"removeLevel(i)\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </span>\n </div>\n </div>\n </div>\n\n <div v-else class=\"mint-reagent-editor__empty\">\n Use a preset above or add levels manually\n </div>\n </div>\n\n <!-- Position Summary -->\n <div\n v-if=\"modelValue.levels.length > 0 && modelValue.startPosition && !startPositionError\"\n :class=\"[\n 'mint-reagent-editor__summary',\n positionOverflows ? 'mint-reagent-editor__summary--overflow' : '',\n ]\"\n >\n <div class=\"mint-reagent-editor__summary-item\">\n <span class=\"mint-reagent-editor__summary-value\">{{ modelValue.levels.length }}</span>\n <span class=\"mint-reagent-editor__summary-label\">levels</span>\n </div>\n <div class=\"mint-reagent-editor__summary-dot\" />\n <div class=\"mint-reagent-editor__summary-item\">\n <span class=\"mint-reagent-editor__summary-value\">{{ positionCount }}</span>\n <span class=\"mint-reagent-editor__summary-label\">positions</span>\n </div>\n <div v-if=\"positionRange\" class=\"mint-reagent-editor__summary-dot\" />\n <div v-if=\"positionRange\" class=\"mint-reagent-editor__summary-item\">\n <span class=\"mint-reagent-editor__summary-value\">{{ positionRange }}</span>\n <span class=\"mint-reagent-editor__summary-label\">{{ modelValue.axis === 'row' ? 'rows' : 'cols' }}</span>\n </div>\n <span v-if=\"positionOverflows\" class=\"mint-reagent-editor__summary-warning\">\n Exceeds plate\n </span>\n </div>\n\n <!-- Remove -->\n <div class=\"mint-reagent-editor__footer\">\n <BaseButton variant=\"danger\" size=\"sm\" @click=\"emit('remove')\">\n Remove Reagent\n </BaseButton>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/reagent-editor.css';\n</style>\n","<script setup lang=\"ts\">\n/** Collapsible tree visualizing the biological sample hierarchy (study → experiment → plate → sample → cell line → passage → clone → treatment). */\nimport { h, Transition, type VNode } from 'vue'\nimport type { TreeNodeType, BadgeVariant, TreeNode } from '../types'\nimport { useExpansionSet } from '../composables/useExpansionSet'\n\ninterface Props {\n nodes: TreeNode[]\n defaultExpandedIds?: string[]\n expandAll?: boolean\n showIcons?: boolean\n showCounts?: boolean\n maxDepth?: number\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n defaultExpandedIds: () => [],\n expandAll: false,\n showIcons: true,\n showCounts: true,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'node-click': [node: TreeNode]\n 'expand': [nodeId: string]\n 'collapse': [nodeId: string]\n}>()\n\n// Default icons by type (Lucide SVG paths - arrays to support multi-path icons)\ninterface IconElement {\n tag: 'path' | 'circle' | 'rect'\n attrs: Record<string, string | number>\n}\n\nconst typeIcons: Record<TreeNodeType, IconElement[]> = {\n study: [\n { tag: 'path', attrs: { d: 'M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z' } },\n ],\n experiment: [\n { tag: 'path', attrs: { d: 'M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2' } },\n { tag: 'path', attrs: { d: 'M6.453 15h11.094' } },\n { tag: 'path', attrs: { d: 'M8.5 2h7' } },\n ],\n plate: [\n { tag: 'rect', attrs: { width: '7', height: '7', x: '3', y: '3', rx: '1' } },\n { tag: 'rect', attrs: { width: '7', height: '7', x: '14', y: '3', rx: '1' } },\n { tag: 'rect', attrs: { width: '7', height: '7', x: '14', y: '14', rx: '1' } },\n { tag: 'rect', attrs: { width: '7', height: '7', x: '3', y: '14', rx: '1' } },\n ],\n sample: [\n { tag: 'circle', attrs: { cx: '12', cy: '12', r: '10' } },\n { tag: 'path', attrs: { d: 'M12 6v6l4 2' } },\n ],\n cell_line: [\n { tag: 'path', attrs: { d: 'M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5' } },\n { tag: 'path', attrs: { d: 'M9 18h6' } },\n { tag: 'path', attrs: { d: 'M10 22h4' } },\n ],\n passage: [\n { tag: 'path', attrs: { d: 'M3 5h.01' } },\n { tag: 'path', attrs: { d: 'M3 12h.01' } },\n { tag: 'path', attrs: { d: 'M3 19h.01' } },\n { tag: 'path', attrs: { d: 'M8 5h13' } },\n { tag: 'path', attrs: { d: 'M8 12h13' } },\n { tag: 'path', attrs: { d: 'M8 19h13' } },\n ],\n clone: [\n { tag: 'rect', attrs: { width: '14', height: '14', x: '8', y: '8', rx: '2', ry: '2' } },\n { tag: 'path', attrs: { d: 'M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2' } },\n ],\n treatment: [\n { tag: 'path', attrs: { d: 'M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2' } },\n { tag: 'path', attrs: { d: 'M6.453 15h11.094' } },\n { tag: 'path', attrs: { d: 'M8.5 2h7' } },\n ],\n folder: [\n { tag: 'path', attrs: { d: 'M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z' } },\n ],\n custom: [\n { tag: 'path', attrs: { d: 'M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915' } },\n { tag: 'circle', attrs: { cx: '12', cy: '12', r: '3' } },\n ],\n}\n\n// Collect all node IDs for expand all\nfunction collectAllIds(nodes: TreeNode[]): string[] {\n const ids: string[] = []\n function traverse(node: TreeNode) {\n ids.push(node.id)\n if (node.children) {\n node.children.forEach(traverse)\n }\n }\n nodes.forEach(traverse)\n return ids\n}\n\nconst expansion = useExpansionSet({\n defaultIds: () => props.defaultExpandedIds,\n allIds: () => collectAllIds(props.nodes),\n expandAll: () => props.expandAll,\n})\n\nfunction isExpanded(nodeId: string): boolean {\n return expansion.isExpanded(nodeId)\n}\n\nfunction toggleExpand(node: TreeNode) {\n const expanded = expansion.toggle(node.id)\n if (expanded) {\n emit('expand', node.id)\n } else {\n emit('collapse', node.id)\n }\n}\n\nfunction handleNodeClick(node: TreeNode) {\n emit('node-click', node)\n}\n\nfunction getIconElements(node: TreeNode): IconElement[] {\n if (node.icon) return [{ tag: 'path', attrs: { d: node.icon } }]\n return typeIcons[node.type || 'custom']\n}\n\nfunction getBadgeContent(node: TreeNode): string | number | undefined {\n if (node.badge !== undefined) return node.badge\n if (props.showCounts && node.children && node.children.length > 0) {\n return node.children.length\n }\n return undefined\n}\n\nfunction getBadgeVariant(node: TreeNode): BadgeVariant {\n return node.badgeVariant || 'default'\n}\n\nfunction hasChildren(node: TreeNode): boolean {\n return !!node.children && node.children.length > 0\n}\n\nfunction canShowChildren(node: TreeNode, depth: number): boolean {\n if (!hasChildren(node)) return false\n if (props.maxDepth !== undefined && depth >= props.maxDepth) return false\n return isExpanded(node.id)\n}\n\n// Render tree node recursively\nfunction renderNode(node: TreeNode, depth: number): VNode {\n const children = node.children\n const expanded = isExpanded(node.id)\n const canExpand = hasChildren(node)\n const showChildNodes = canShowChildren(node, depth)\n const badge = getBadgeContent(node)\n const badgeVar = getBadgeVariant(node)\n const iconElements = getIconElements(node)\n\n const header = h(\n 'div',\n {\n class: 'mint-sample-tree__node-header',\n onClick: () => handleNodeClick(node),\n },\n [\n canExpand\n ? h(\n 'button',\n {\n type: 'button',\n class: 'mint-sample-tree__toggle',\n 'aria-label': expanded ? 'Collapse' : 'Expand',\n onClick: (e: Event) => {\n e.stopPropagation()\n toggleExpand(node)\n },\n },\n [\n h('svg', { fill: 'none', stroke: 'currentColor', viewBox: '0 0 24 24', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' }, [\n h('path', { d: 'm9 18 6-6-6-6' }),\n ]),\n ]\n )\n : h('span', { class: 'mint-sample-tree__toggle-placeholder' }),\n props.showIcons\n ? h('span', { class: ['mint-sample-tree__icon', `mint-sample-tree__icon--${node.type || 'custom'}`] }, [\n h('svg', { fill: 'none', stroke: 'currentColor', viewBox: '0 0 24 24', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' },\n iconElements.map(el => h(el.tag, el.attrs))\n ),\n ])\n : null,\n h('span', { class: 'mint-sample-tree__label' }, node.label),\n badge !== undefined\n ? h('span', { class: ['mint-sample-tree__badge', `mint-sample-tree__badge--${badgeVar}`] }, String(badge))\n : null,\n ]\n )\n\n const childNodes =\n showChildNodes && children\n ? h(\n Transition,\n { enterActiveClass: 'mint-sample-tree__children--entering', leaveActiveClass: 'mint-sample-tree__children--leaving' },\n () => h('div', { class: 'mint-sample-tree__children' }, children.map((child) => renderNode(child, depth + 1)))\n )\n : null\n\n return h(\n 'div',\n {\n key: node.id,\n class: ['mint-sample-tree__node', expanded ? 'mint-sample-tree__node--expanded' : '', !canExpand ? 'mint-sample-tree__node--leaf' : ''],\n role: 'treeitem',\n 'aria-expanded': canExpand ? expanded : undefined,\n },\n [header, childNodes]\n )\n}\n\n// Render the entire tree\nfunction renderTree(): VNode[] {\n return props.nodes.map((node) => renderNode(node, 0))\n}\n</script>\n\n<template>\n <div :class=\"['mint-sample-tree', `mint-sample-tree--${size}`]\" role=\"tree\">\n <!-- Empty state -->\n <div v-if=\"nodes.length === 0\" class=\"mint-sample-tree__empty\">\n <svg class=\"mint-sample-tree__empty-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\" />\n </svg>\n <p class=\"mint-sample-tree__empty-text\">No items</p>\n </div>\n\n <!-- Tree nodes rendered via render function -->\n <template v-else>\n <component :is=\"() => renderTree()\" />\n </template>\n </div>\n</template>\n\n<style>\n@import '../styles/components/sample-hierarchy-tree.css';\n</style>\n","<script setup lang=\"ts\">\n/** Collapsible tree visualizing the biological sample hierarchy (study → experiment → plate → sample → cell line → passage → clone → treatment). */\nimport { h, Transition, type VNode } from 'vue'\nimport type { TreeNodeType, BadgeVariant, TreeNode } from '../types'\nimport { useExpansionSet } from '../composables/useExpansionSet'\n\ninterface Props {\n nodes: TreeNode[]\n defaultExpandedIds?: string[]\n expandAll?: boolean\n showIcons?: boolean\n showCounts?: boolean\n maxDepth?: number\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n defaultExpandedIds: () => [],\n expandAll: false,\n showIcons: true,\n showCounts: true,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'node-click': [node: TreeNode]\n 'expand': [nodeId: string]\n 'collapse': [nodeId: string]\n}>()\n\n// Default icons by type (Lucide SVG paths - arrays to support multi-path icons)\ninterface IconElement {\n tag: 'path' | 'circle' | 'rect'\n attrs: Record<string, string | number>\n}\n\nconst typeIcons: Record<TreeNodeType, IconElement[]> = {\n study: [\n { tag: 'path', attrs: { d: 'M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z' } },\n ],\n experiment: [\n { tag: 'path', attrs: { d: 'M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2' } },\n { tag: 'path', attrs: { d: 'M6.453 15h11.094' } },\n { tag: 'path', attrs: { d: 'M8.5 2h7' } },\n ],\n plate: [\n { tag: 'rect', attrs: { width: '7', height: '7', x: '3', y: '3', rx: '1' } },\n { tag: 'rect', attrs: { width: '7', height: '7', x: '14', y: '3', rx: '1' } },\n { tag: 'rect', attrs: { width: '7', height: '7', x: '14', y: '14', rx: '1' } },\n { tag: 'rect', attrs: { width: '7', height: '7', x: '3', y: '14', rx: '1' } },\n ],\n sample: [\n { tag: 'circle', attrs: { cx: '12', cy: '12', r: '10' } },\n { tag: 'path', attrs: { d: 'M12 6v6l4 2' } },\n ],\n cell_line: [\n { tag: 'path', attrs: { d: 'M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5' } },\n { tag: 'path', attrs: { d: 'M9 18h6' } },\n { tag: 'path', attrs: { d: 'M10 22h4' } },\n ],\n passage: [\n { tag: 'path', attrs: { d: 'M3 5h.01' } },\n { tag: 'path', attrs: { d: 'M3 12h.01' } },\n { tag: 'path', attrs: { d: 'M3 19h.01' } },\n { tag: 'path', attrs: { d: 'M8 5h13' } },\n { tag: 'path', attrs: { d: 'M8 12h13' } },\n { tag: 'path', attrs: { d: 'M8 19h13' } },\n ],\n clone: [\n { tag: 'rect', attrs: { width: '14', height: '14', x: '8', y: '8', rx: '2', ry: '2' } },\n { tag: 'path', attrs: { d: 'M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2' } },\n ],\n treatment: [\n { tag: 'path', attrs: { d: 'M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2' } },\n { tag: 'path', attrs: { d: 'M6.453 15h11.094' } },\n { tag: 'path', attrs: { d: 'M8.5 2h7' } },\n ],\n folder: [\n { tag: 'path', attrs: { d: 'M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z' } },\n ],\n custom: [\n { tag: 'path', attrs: { d: 'M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915' } },\n { tag: 'circle', attrs: { cx: '12', cy: '12', r: '3' } },\n ],\n}\n\n// Collect all node IDs for expand all\nfunction collectAllIds(nodes: TreeNode[]): string[] {\n const ids: string[] = []\n function traverse(node: TreeNode) {\n ids.push(node.id)\n if (node.children) {\n node.children.forEach(traverse)\n }\n }\n nodes.forEach(traverse)\n return ids\n}\n\nconst expansion = useExpansionSet({\n defaultIds: () => props.defaultExpandedIds,\n allIds: () => collectAllIds(props.nodes),\n expandAll: () => props.expandAll,\n})\n\nfunction isExpanded(nodeId: string): boolean {\n return expansion.isExpanded(nodeId)\n}\n\nfunction toggleExpand(node: TreeNode) {\n const expanded = expansion.toggle(node.id)\n if (expanded) {\n emit('expand', node.id)\n } else {\n emit('collapse', node.id)\n }\n}\n\nfunction handleNodeClick(node: TreeNode) {\n emit('node-click', node)\n}\n\nfunction getIconElements(node: TreeNode): IconElement[] {\n if (node.icon) return [{ tag: 'path', attrs: { d: node.icon } }]\n return typeIcons[node.type || 'custom']\n}\n\nfunction getBadgeContent(node: TreeNode): string | number | undefined {\n if (node.badge !== undefined) return node.badge\n if (props.showCounts && node.children && node.children.length > 0) {\n return node.children.length\n }\n return undefined\n}\n\nfunction getBadgeVariant(node: TreeNode): BadgeVariant {\n return node.badgeVariant || 'default'\n}\n\nfunction hasChildren(node: TreeNode): boolean {\n return !!node.children && node.children.length > 0\n}\n\nfunction canShowChildren(node: TreeNode, depth: number): boolean {\n if (!hasChildren(node)) return false\n if (props.maxDepth !== undefined && depth >= props.maxDepth) return false\n return isExpanded(node.id)\n}\n\n// Render tree node recursively\nfunction renderNode(node: TreeNode, depth: number): VNode {\n const children = node.children\n const expanded = isExpanded(node.id)\n const canExpand = hasChildren(node)\n const showChildNodes = canShowChildren(node, depth)\n const badge = getBadgeContent(node)\n const badgeVar = getBadgeVariant(node)\n const iconElements = getIconElements(node)\n\n const header = h(\n 'div',\n {\n class: 'mint-sample-tree__node-header',\n onClick: () => handleNodeClick(node),\n },\n [\n canExpand\n ? h(\n 'button',\n {\n type: 'button',\n class: 'mint-sample-tree__toggle',\n 'aria-label': expanded ? 'Collapse' : 'Expand',\n onClick: (e: Event) => {\n e.stopPropagation()\n toggleExpand(node)\n },\n },\n [\n h('svg', { fill: 'none', stroke: 'currentColor', viewBox: '0 0 24 24', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' }, [\n h('path', { d: 'm9 18 6-6-6-6' }),\n ]),\n ]\n )\n : h('span', { class: 'mint-sample-tree__toggle-placeholder' }),\n props.showIcons\n ? h('span', { class: ['mint-sample-tree__icon', `mint-sample-tree__icon--${node.type || 'custom'}`] }, [\n h('svg', { fill: 'none', stroke: 'currentColor', viewBox: '0 0 24 24', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' },\n iconElements.map(el => h(el.tag, el.attrs))\n ),\n ])\n : null,\n h('span', { class: 'mint-sample-tree__label' }, node.label),\n badge !== undefined\n ? h('span', { class: ['mint-sample-tree__badge', `mint-sample-tree__badge--${badgeVar}`] }, String(badge))\n : null,\n ]\n )\n\n const childNodes =\n showChildNodes && children\n ? h(\n Transition,\n { enterActiveClass: 'mint-sample-tree__children--entering', leaveActiveClass: 'mint-sample-tree__children--leaving' },\n () => h('div', { class: 'mint-sample-tree__children' }, children.map((child) => renderNode(child, depth + 1)))\n )\n : null\n\n return h(\n 'div',\n {\n key: node.id,\n class: ['mint-sample-tree__node', expanded ? 'mint-sample-tree__node--expanded' : '', !canExpand ? 'mint-sample-tree__node--leaf' : ''],\n role: 'treeitem',\n 'aria-expanded': canExpand ? expanded : undefined,\n },\n [header, childNodes]\n )\n}\n\n// Render the entire tree\nfunction renderTree(): VNode[] {\n return props.nodes.map((node) => renderNode(node, 0))\n}\n</script>\n\n<template>\n <div :class=\"['mint-sample-tree', `mint-sample-tree--${size}`]\" role=\"tree\">\n <!-- Empty state -->\n <div v-if=\"nodes.length === 0\" class=\"mint-sample-tree__empty\">\n <svg class=\"mint-sample-tree__empty-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\" />\n </svg>\n <p class=\"mint-sample-tree__empty-text\">No items</p>\n </div>\n\n <!-- Tree nodes rendered via render function -->\n <template v-else>\n <component :is=\"() => renderTree()\" />\n </template>\n </div>\n</template>\n\n<style>\n@import '../styles/components/sample-hierarchy-tree.css';\n</style>\n","<script setup lang=\"ts\">\n/** Form for creating or editing a single protocol step (incubation, wash, addition, centrifuge, etc.) with template picker, typed parameters, and duration. */\nimport { ref, computed, watch, onMounted } from 'vue'\nimport type { ProtocolStep, ProtocolStepType, ProtocolStepStatus } from '../types'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport {\n useProtocolTemplates,\n type StepTemplate,\n} from '../composables/useProtocolTemplates'\nimport ConcentrationInput from './ConcentrationInput.vue'\nimport type { ConcentrationValue } from '../composables/useConcentrationUnits'\n\ninterface Props {\n modelValue?: ProtocolStep\n templates?: StepTemplate[]\n customTemplates?: StepTemplate[]\n mode?: 'create' | 'edit'\n showPreview?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n mode: 'create',\n showPreview: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [step: ProtocolStep]\n 'save': [step: ProtocolStep]\n 'save-template': [template: StepTemplate]\n 'cancel': []\n}>()\n\nconst {\n allTemplates,\n getTemplateByType,\n validateStep,\n formatParameterValue,\n} = useProtocolTemplates()\n\n// State\nconst selectedTemplateId = ref<string | null>(null)\nconst {\n isOpen: templateDropdownOpen,\n rootRef: templateDropdownRef,\n close: closeTemplateDropdown,\n toggle: toggleTemplateDropdown,\n} = useDropdownState()\nconst stepName = ref('')\nconst stepDescription = ref('')\nconst stepDuration = ref<number | undefined>()\nconst parameters = ref<Record<string, unknown>>({})\nconst validationErrors = ref<Record<string, string>>({})\n\n// Combined templates from props and composable\nconst availableTemplates = computed(() => {\n if (props.templates && props.templates.length > 0) {\n return props.templates\n }\n const combined = [...allTemplates.value]\n if (props.customTemplates) {\n combined.push(...props.customTemplates)\n }\n return combined\n})\n\n// Selected template\nconst selectedTemplate = computed(() => {\n if (!selectedTemplateId.value) return null\n return availableTemplates.value.find((t) => t.id === selectedTemplateId.value) || null\n})\n\n// Step type icons (same as ExperimentTimeline)\nconst stepTypeIcons: Record<ProtocolStepType, string> = {\n incubation: 'M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z',\n wash: 'M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z',\n addition: 'M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z',\n measurement: 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z',\n transfer: 'M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4',\n centrifuge: 'M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15',\n mix: 'M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z',\n custom: 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z',\n}\n\n// Preview computed\nconst previewStep = computed<ProtocolStep | null>(() => {\n if (!selectedTemplate.value) return null\n return {\n id: props.modelValue?.id || 'preview',\n type: selectedTemplate.value.type,\n name: stepName.value || selectedTemplate.value.name,\n description: stepDescription.value,\n duration: stepDuration.value ?? selectedTemplate.value.defaultDuration,\n status: (props.modelValue?.status || 'pending') as ProtocolStepStatus,\n parameters: { ...parameters.value },\n order: props.modelValue?.order || 0,\n }\n})\n\nconst previewParams = computed(() => {\n if (!selectedTemplate.value || !previewStep.value) return ''\n return selectedTemplate.value.parameters\n .filter((p) => parameters.value[p.key] !== undefined && parameters.value[p.key] !== '')\n .map((p) => formatParameterValue(parameters.value[p.key], p))\n .join(', ')\n})\n\n// Initialize from modelValue\nfunction initFromStep(step: ProtocolStep) {\n const template = getTemplateByType(step.type)\n if (template) {\n selectedTemplateId.value = template.id\n }\n stepName.value = step.name\n stepDescription.value = step.description || ''\n stepDuration.value = step.duration\n parameters.value = { ...(step.parameters || {}) }\n}\n\n// Handle template selection\nfunction selectTemplate(template: StepTemplate) {\n selectedTemplateId.value = template.id\n closeTemplateDropdown()\n\n // Reset to template defaults if creating new\n if (props.mode === 'create') {\n stepName.value = template.name\n stepDescription.value = template.description || ''\n stepDuration.value = template.defaultDuration\n\n parameters.value = {}\n for (const param of template.parameters) {\n if (param.default !== undefined) {\n parameters.value[param.key] = param.default\n }\n }\n }\n}\n\n// Handle parameter change\nfunction handleParamChange(key: string, value: unknown) {\n parameters.value[key] = value\n // Clear validation error when user changes value\n if (validationErrors.value[key]) {\n delete validationErrors.value[key]\n }\n}\n\n// Handle concentration change\nfunction handleConcentrationChange(key: string, value: ConcentrationValue | undefined) {\n parameters.value[key] = value\n if (validationErrors.value[key]) {\n delete validationErrors.value[key]\n }\n}\n\n// Validate and save\nfunction handleSave() {\n if (!selectedTemplate.value || !previewStep.value) return\n\n const result = validateStep(previewStep.value, selectedTemplate.value)\n validationErrors.value = result.errors\n\n if (result.valid) {\n emit('save', previewStep.value)\n emit('update:modelValue', previewStep.value)\n }\n}\n\n// Save as template\nfunction handleSaveTemplate() {\n if (!selectedTemplate.value) return\n\n const newTemplate: StepTemplate = {\n id: `custom-${Date.now()}`,\n type: selectedTemplate.value.type,\n name: stepName.value || selectedTemplate.value.name,\n description: stepDescription.value,\n defaultDuration: stepDuration.value,\n parameters: selectedTemplate.value.parameters.map((p) => ({\n ...p,\n default: parameters.value[p.key],\n })),\n isBuiltIn: false,\n }\n\n emit('save-template', newTemplate)\n}\n\n// Handle cancel\nfunction handleCancel() {\n emit('cancel')\n}\n\n// Format duration\nfunction formatDuration(minutes: number | undefined): string {\n if (minutes === undefined) return ''\n if (minutes < 60) return `${minutes}m`\n const hours = Math.floor(minutes / 60)\n const mins = minutes % 60\n return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`\n}\n\nonMounted(() => {\n if (props.modelValue) {\n initFromStep(props.modelValue)\n } else if (availableTemplates.value.length > 0) {\n selectTemplate(availableTemplates.value[0])\n }\n})\n\n// Watch for external changes\nwatch(\n () => props.modelValue,\n (step) => {\n if (step) {\n initFromStep(step)\n }\n }\n)\n</script>\n\n<template>\n <div class=\"mint-protocol-editor\">\n <!-- Header -->\n <div class=\"mint-protocol-editor__header\">\n <span class=\"mint-protocol-editor__title\">\n {{ mode === 'create' ? 'Create Protocol Step' : 'Edit Protocol Step' }}\n </span>\n <span\n :class=\"[\n 'mint-protocol-editor__mode-badge',\n `mint-protocol-editor__mode-badge--${mode}`,\n ]\"\n >\n {{ mode }}\n </span>\n </div>\n\n <!-- Form -->\n <div class=\"mint-protocol-editor__form\">\n <!-- Template selector -->\n <div class=\"mint-protocol-editor__template-select\">\n <label class=\"mint-protocol-editor__template-label\">Template</label>\n <div ref=\"templateDropdownRef\" class=\"mint-protocol-editor__template-dropdown\">\n <button\n type=\"button\"\n class=\"mint-protocol-editor__template-btn\"\n @click=\"toggleTemplateDropdown\"\n >\n <svg\n v-if=\"selectedTemplate\"\n class=\"mint-protocol-editor__template-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n :d=\"stepTypeIcons[selectedTemplate.type]\"\n />\n </svg>\n <span class=\"mint-protocol-editor__template-text\">\n {{ selectedTemplate?.name || 'Select template...' }}\n </span>\n <svg\n :class=\"[\n 'mint-protocol-editor__template-arrow',\n templateDropdownOpen ? 'mint-protocol-editor__template-arrow--open' : '',\n ]\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n\n <div v-if=\"templateDropdownOpen\" class=\"mint-protocol-editor__template-menu\">\n <button\n v-for=\"template in availableTemplates\"\n :key=\"template.id\"\n type=\"button\"\n :class=\"[\n 'mint-protocol-editor__template-option',\n selectedTemplateId === template.id ? 'mint-protocol-editor__template-option--active' : '',\n ]\"\n @click=\"selectTemplate(template)\"\n >\n <svg\n class=\"mint-protocol-editor__template-option-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n :d=\"stepTypeIcons[template.type]\"\n />\n </svg>\n <div class=\"mint-protocol-editor__template-option-text\">\n <div>{{ template.name }}</div>\n <div v-if=\"template.description\" class=\"mint-protocol-editor__template-option-desc\">\n {{ template.description }}\n </div>\n </div>\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"mint-protocol-editor__divider\" />\n\n <!-- Step name -->\n <div class=\"mint-protocol-editor__field\">\n <label class=\"mint-protocol-editor__field-label mint-protocol-editor__field-label--required\">\n Step Name\n </label>\n <input\n v-model=\"stepName\"\n type=\"text\"\n :class=\"[\n 'mint-protocol-editor__input',\n validationErrors.name ? 'mint-protocol-editor__input--error' : '',\n ]\"\n placeholder=\"Enter step name\"\n />\n <span v-if=\"validationErrors.name\" class=\"mint-protocol-editor__field-error\">\n {{ validationErrors.name }}\n </span>\n </div>\n\n <!-- Dynamic parameters -->\n <template v-if=\"selectedTemplate\">\n <div\n v-for=\"(param, index) in selectedTemplate.parameters\"\n :key=\"param.key\"\n :class=\"[\n 'mint-protocol-editor__field',\n index % 2 === 0 && selectedTemplate.parameters[index + 1]\n ? ''\n : '',\n ]\"\n >\n <label\n :class=\"[\n 'mint-protocol-editor__field-label',\n param.required ? 'mint-protocol-editor__field-label--required' : '',\n ]\"\n >\n {{ param.label }}\n </label>\n\n <!-- Number input with unit -->\n <div\n v-if=\"param.type === 'number' || param.type === 'temperature' || param.type === 'duration'\"\n class=\"mint-protocol-editor__input-unit\"\n >\n <input\n type=\"number\"\n :value=\"parameters[param.key]\"\n :min=\"param.min\"\n :max=\"param.max\"\n :placeholder=\"param.placeholder || `Enter ${param.label.toLowerCase()}`\"\n :class=\"[\n 'mint-protocol-editor__input',\n validationErrors[param.key] ? 'mint-protocol-editor__input--error' : '',\n ]\"\n @input=\"handleParamChange(param.key, ($event.target as HTMLInputElement).valueAsNumber)\"\n />\n <span v-if=\"param.unit\" class=\"mint-protocol-editor__unit-suffix\">\n {{ param.unit }}\n </span>\n </div>\n\n <!-- Text input -->\n <input\n v-else-if=\"param.type === 'text'\"\n type=\"text\"\n :value=\"parameters[param.key]\"\n :placeholder=\"param.placeholder || `Enter ${param.label.toLowerCase()}`\"\n :class=\"[\n 'mint-protocol-editor__input',\n validationErrors[param.key] ? 'mint-protocol-editor__input--error' : '',\n ]\"\n @input=\"handleParamChange(param.key, ($event.target as HTMLInputElement).value)\"\n />\n\n <!-- Select -->\n <select\n v-else-if=\"param.type === 'select'\"\n :value=\"parameters[param.key]\"\n :class=\"[\n 'mint-protocol-editor__select',\n validationErrors[param.key] ? 'mint-protocol-editor__input--error' : '',\n ]\"\n @change=\"handleParamChange(param.key, ($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"\" disabled>Select {{ param.label.toLowerCase() }}</option>\n <option\n v-for=\"option in param.options\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n\n <!-- Concentration input -->\n <ConcentrationInput\n v-else-if=\"param.type === 'concentration'\"\n :model-value=\"parameters[param.key] as ConcentrationValue | undefined\"\n :error=\"!!validationErrors[param.key]\"\n size=\"md\"\n @update:model-value=\"handleConcentrationChange(param.key, $event)\"\n />\n\n <!-- Reagent input (text for now) -->\n <input\n v-else-if=\"param.type === 'reagent'\"\n type=\"text\"\n :value=\"parameters[param.key]\"\n :placeholder=\"param.placeholder || 'Enter reagent name'\"\n :class=\"[\n 'mint-protocol-editor__input',\n validationErrors[param.key] ? 'mint-protocol-editor__input--error' : '',\n ]\"\n @input=\"handleParamChange(param.key, ($event.target as HTMLInputElement).value)\"\n />\n\n <span v-if=\"validationErrors[param.key]\" class=\"mint-protocol-editor__field-error\">\n {{ validationErrors[param.key] }}\n </span>\n </div>\n </template>\n\n <!-- Description -->\n <div class=\"mint-protocol-editor__field\">\n <label class=\"mint-protocol-editor__field-label\">Description</label>\n <textarea\n v-model=\"stepDescription\"\n class=\"mint-protocol-editor__textarea\"\n placeholder=\"Optional step description\"\n rows=\"2\"\n />\n </div>\n </div>\n\n <!-- Preview -->\n <div v-if=\"showPreview && previewStep\" class=\"mint-protocol-editor__preview\">\n <div class=\"mint-protocol-editor__preview-title\">Preview</div>\n <div class=\"mint-protocol-editor__preview-card\">\n <div class=\"mint-protocol-editor__preview-icon\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n :d=\"stepTypeIcons[previewStep.type]\"\n />\n </svg>\n </div>\n <div class=\"mint-protocol-editor__preview-content\">\n <div class=\"mint-protocol-editor__preview-name\">{{ previewStep.name }}</div>\n <div v-if=\"previewParams\" class=\"mint-protocol-editor__preview-params\">\n {{ previewParams }}\n </div>\n </div>\n <div v-if=\"previewStep.duration\" class=\"mint-protocol-editor__preview-duration\">\n {{ formatDuration(previewStep.duration) }}\n </div>\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"mint-protocol-editor__actions\">\n <div class=\"mint-protocol-editor__actions-left\">\n <button\n type=\"button\"\n class=\"mint-protocol-editor__btn mint-protocol-editor__btn--ghost\"\n @click=\"handleSaveTemplate\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4\" />\n </svg>\n Save as Template\n </button>\n </div>\n <button\n type=\"button\"\n class=\"mint-protocol-editor__btn mint-protocol-editor__btn--secondary\"\n @click=\"handleCancel\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mint-protocol-editor__btn mint-protocol-editor__btn--primary\"\n :disabled=\"!selectedTemplate\"\n @click=\"handleSave\"\n >\n {{ mode === 'create' ? 'Add Step' : 'Save Changes' }}\n </button>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/protocol-step-editor.css';\n</style>\n","<script setup lang=\"ts\">\n/** Form for creating or editing a single protocol step (incubation, wash, addition, centrifuge, etc.) with template picker, typed parameters, and duration. */\nimport { ref, computed, watch, onMounted } from 'vue'\nimport type { ProtocolStep, ProtocolStepType, ProtocolStepStatus } from '../types'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport {\n useProtocolTemplates,\n type StepTemplate,\n} from '../composables/useProtocolTemplates'\nimport ConcentrationInput from './ConcentrationInput.vue'\nimport type { ConcentrationValue } from '../composables/useConcentrationUnits'\n\ninterface Props {\n modelValue?: ProtocolStep\n templates?: StepTemplate[]\n customTemplates?: StepTemplate[]\n mode?: 'create' | 'edit'\n showPreview?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n mode: 'create',\n showPreview: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [step: ProtocolStep]\n 'save': [step: ProtocolStep]\n 'save-template': [template: StepTemplate]\n 'cancel': []\n}>()\n\nconst {\n allTemplates,\n getTemplateByType,\n validateStep,\n formatParameterValue,\n} = useProtocolTemplates()\n\n// State\nconst selectedTemplateId = ref<string | null>(null)\nconst {\n isOpen: templateDropdownOpen,\n rootRef: templateDropdownRef,\n close: closeTemplateDropdown,\n toggle: toggleTemplateDropdown,\n} = useDropdownState()\nconst stepName = ref('')\nconst stepDescription = ref('')\nconst stepDuration = ref<number | undefined>()\nconst parameters = ref<Record<string, unknown>>({})\nconst validationErrors = ref<Record<string, string>>({})\n\n// Combined templates from props and composable\nconst availableTemplates = computed(() => {\n if (props.templates && props.templates.length > 0) {\n return props.templates\n }\n const combined = [...allTemplates.value]\n if (props.customTemplates) {\n combined.push(...props.customTemplates)\n }\n return combined\n})\n\n// Selected template\nconst selectedTemplate = computed(() => {\n if (!selectedTemplateId.value) return null\n return availableTemplates.value.find((t) => t.id === selectedTemplateId.value) || null\n})\n\n// Step type icons (same as ExperimentTimeline)\nconst stepTypeIcons: Record<ProtocolStepType, string> = {\n incubation: 'M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z',\n wash: 'M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z',\n addition: 'M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z',\n measurement: 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z',\n transfer: 'M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4',\n centrifuge: 'M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15',\n mix: 'M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z',\n custom: 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z',\n}\n\n// Preview computed\nconst previewStep = computed<ProtocolStep | null>(() => {\n if (!selectedTemplate.value) return null\n return {\n id: props.modelValue?.id || 'preview',\n type: selectedTemplate.value.type,\n name: stepName.value || selectedTemplate.value.name,\n description: stepDescription.value,\n duration: stepDuration.value ?? selectedTemplate.value.defaultDuration,\n status: (props.modelValue?.status || 'pending') as ProtocolStepStatus,\n parameters: { ...parameters.value },\n order: props.modelValue?.order || 0,\n }\n})\n\nconst previewParams = computed(() => {\n if (!selectedTemplate.value || !previewStep.value) return ''\n return selectedTemplate.value.parameters\n .filter((p) => parameters.value[p.key] !== undefined && parameters.value[p.key] !== '')\n .map((p) => formatParameterValue(parameters.value[p.key], p))\n .join(', ')\n})\n\n// Initialize from modelValue\nfunction initFromStep(step: ProtocolStep) {\n const template = getTemplateByType(step.type)\n if (template) {\n selectedTemplateId.value = template.id\n }\n stepName.value = step.name\n stepDescription.value = step.description || ''\n stepDuration.value = step.duration\n parameters.value = { ...(step.parameters || {}) }\n}\n\n// Handle template selection\nfunction selectTemplate(template: StepTemplate) {\n selectedTemplateId.value = template.id\n closeTemplateDropdown()\n\n // Reset to template defaults if creating new\n if (props.mode === 'create') {\n stepName.value = template.name\n stepDescription.value = template.description || ''\n stepDuration.value = template.defaultDuration\n\n parameters.value = {}\n for (const param of template.parameters) {\n if (param.default !== undefined) {\n parameters.value[param.key] = param.default\n }\n }\n }\n}\n\n// Handle parameter change\nfunction handleParamChange(key: string, value: unknown) {\n parameters.value[key] = value\n // Clear validation error when user changes value\n if (validationErrors.value[key]) {\n delete validationErrors.value[key]\n }\n}\n\n// Handle concentration change\nfunction handleConcentrationChange(key: string, value: ConcentrationValue | undefined) {\n parameters.value[key] = value\n if (validationErrors.value[key]) {\n delete validationErrors.value[key]\n }\n}\n\n// Validate and save\nfunction handleSave() {\n if (!selectedTemplate.value || !previewStep.value) return\n\n const result = validateStep(previewStep.value, selectedTemplate.value)\n validationErrors.value = result.errors\n\n if (result.valid) {\n emit('save', previewStep.value)\n emit('update:modelValue', previewStep.value)\n }\n}\n\n// Save as template\nfunction handleSaveTemplate() {\n if (!selectedTemplate.value) return\n\n const newTemplate: StepTemplate = {\n id: `custom-${Date.now()}`,\n type: selectedTemplate.value.type,\n name: stepName.value || selectedTemplate.value.name,\n description: stepDescription.value,\n defaultDuration: stepDuration.value,\n parameters: selectedTemplate.value.parameters.map((p) => ({\n ...p,\n default: parameters.value[p.key],\n })),\n isBuiltIn: false,\n }\n\n emit('save-template', newTemplate)\n}\n\n// Handle cancel\nfunction handleCancel() {\n emit('cancel')\n}\n\n// Format duration\nfunction formatDuration(minutes: number | undefined): string {\n if (minutes === undefined) return ''\n if (minutes < 60) return `${minutes}m`\n const hours = Math.floor(minutes / 60)\n const mins = minutes % 60\n return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`\n}\n\nonMounted(() => {\n if (props.modelValue) {\n initFromStep(props.modelValue)\n } else if (availableTemplates.value.length > 0) {\n selectTemplate(availableTemplates.value[0])\n }\n})\n\n// Watch for external changes\nwatch(\n () => props.modelValue,\n (step) => {\n if (step) {\n initFromStep(step)\n }\n }\n)\n</script>\n\n<template>\n <div class=\"mint-protocol-editor\">\n <!-- Header -->\n <div class=\"mint-protocol-editor__header\">\n <span class=\"mint-protocol-editor__title\">\n {{ mode === 'create' ? 'Create Protocol Step' : 'Edit Protocol Step' }}\n </span>\n <span\n :class=\"[\n 'mint-protocol-editor__mode-badge',\n `mint-protocol-editor__mode-badge--${mode}`,\n ]\"\n >\n {{ mode }}\n </span>\n </div>\n\n <!-- Form -->\n <div class=\"mint-protocol-editor__form\">\n <!-- Template selector -->\n <div class=\"mint-protocol-editor__template-select\">\n <label class=\"mint-protocol-editor__template-label\">Template</label>\n <div ref=\"templateDropdownRef\" class=\"mint-protocol-editor__template-dropdown\">\n <button\n type=\"button\"\n class=\"mint-protocol-editor__template-btn\"\n @click=\"toggleTemplateDropdown\"\n >\n <svg\n v-if=\"selectedTemplate\"\n class=\"mint-protocol-editor__template-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n :d=\"stepTypeIcons[selectedTemplate.type]\"\n />\n </svg>\n <span class=\"mint-protocol-editor__template-text\">\n {{ selectedTemplate?.name || 'Select template...' }}\n </span>\n <svg\n :class=\"[\n 'mint-protocol-editor__template-arrow',\n templateDropdownOpen ? 'mint-protocol-editor__template-arrow--open' : '',\n ]\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n\n <div v-if=\"templateDropdownOpen\" class=\"mint-protocol-editor__template-menu\">\n <button\n v-for=\"template in availableTemplates\"\n :key=\"template.id\"\n type=\"button\"\n :class=\"[\n 'mint-protocol-editor__template-option',\n selectedTemplateId === template.id ? 'mint-protocol-editor__template-option--active' : '',\n ]\"\n @click=\"selectTemplate(template)\"\n >\n <svg\n class=\"mint-protocol-editor__template-option-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n :d=\"stepTypeIcons[template.type]\"\n />\n </svg>\n <div class=\"mint-protocol-editor__template-option-text\">\n <div>{{ template.name }}</div>\n <div v-if=\"template.description\" class=\"mint-protocol-editor__template-option-desc\">\n {{ template.description }}\n </div>\n </div>\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"mint-protocol-editor__divider\" />\n\n <!-- Step name -->\n <div class=\"mint-protocol-editor__field\">\n <label class=\"mint-protocol-editor__field-label mint-protocol-editor__field-label--required\">\n Step Name\n </label>\n <input\n v-model=\"stepName\"\n type=\"text\"\n :class=\"[\n 'mint-protocol-editor__input',\n validationErrors.name ? 'mint-protocol-editor__input--error' : '',\n ]\"\n placeholder=\"Enter step name\"\n />\n <span v-if=\"validationErrors.name\" class=\"mint-protocol-editor__field-error\">\n {{ validationErrors.name }}\n </span>\n </div>\n\n <!-- Dynamic parameters -->\n <template v-if=\"selectedTemplate\">\n <div\n v-for=\"(param, index) in selectedTemplate.parameters\"\n :key=\"param.key\"\n :class=\"[\n 'mint-protocol-editor__field',\n index % 2 === 0 && selectedTemplate.parameters[index + 1]\n ? ''\n : '',\n ]\"\n >\n <label\n :class=\"[\n 'mint-protocol-editor__field-label',\n param.required ? 'mint-protocol-editor__field-label--required' : '',\n ]\"\n >\n {{ param.label }}\n </label>\n\n <!-- Number input with unit -->\n <div\n v-if=\"param.type === 'number' || param.type === 'temperature' || param.type === 'duration'\"\n class=\"mint-protocol-editor__input-unit\"\n >\n <input\n type=\"number\"\n :value=\"parameters[param.key]\"\n :min=\"param.min\"\n :max=\"param.max\"\n :placeholder=\"param.placeholder || `Enter ${param.label.toLowerCase()}`\"\n :class=\"[\n 'mint-protocol-editor__input',\n validationErrors[param.key] ? 'mint-protocol-editor__input--error' : '',\n ]\"\n @input=\"handleParamChange(param.key, ($event.target as HTMLInputElement).valueAsNumber)\"\n />\n <span v-if=\"param.unit\" class=\"mint-protocol-editor__unit-suffix\">\n {{ param.unit }}\n </span>\n </div>\n\n <!-- Text input -->\n <input\n v-else-if=\"param.type === 'text'\"\n type=\"text\"\n :value=\"parameters[param.key]\"\n :placeholder=\"param.placeholder || `Enter ${param.label.toLowerCase()}`\"\n :class=\"[\n 'mint-protocol-editor__input',\n validationErrors[param.key] ? 'mint-protocol-editor__input--error' : '',\n ]\"\n @input=\"handleParamChange(param.key, ($event.target as HTMLInputElement).value)\"\n />\n\n <!-- Select -->\n <select\n v-else-if=\"param.type === 'select'\"\n :value=\"parameters[param.key]\"\n :class=\"[\n 'mint-protocol-editor__select',\n validationErrors[param.key] ? 'mint-protocol-editor__input--error' : '',\n ]\"\n @change=\"handleParamChange(param.key, ($event.target as HTMLSelectElement).value)\"\n >\n <option value=\"\" disabled>Select {{ param.label.toLowerCase() }}</option>\n <option\n v-for=\"option in param.options\"\n :key=\"option.value\"\n :value=\"option.value\"\n >\n {{ option.label }}\n </option>\n </select>\n\n <!-- Concentration input -->\n <ConcentrationInput\n v-else-if=\"param.type === 'concentration'\"\n :model-value=\"parameters[param.key] as ConcentrationValue | undefined\"\n :error=\"!!validationErrors[param.key]\"\n size=\"md\"\n @update:model-value=\"handleConcentrationChange(param.key, $event)\"\n />\n\n <!-- Reagent input (text for now) -->\n <input\n v-else-if=\"param.type === 'reagent'\"\n type=\"text\"\n :value=\"parameters[param.key]\"\n :placeholder=\"param.placeholder || 'Enter reagent name'\"\n :class=\"[\n 'mint-protocol-editor__input',\n validationErrors[param.key] ? 'mint-protocol-editor__input--error' : '',\n ]\"\n @input=\"handleParamChange(param.key, ($event.target as HTMLInputElement).value)\"\n />\n\n <span v-if=\"validationErrors[param.key]\" class=\"mint-protocol-editor__field-error\">\n {{ validationErrors[param.key] }}\n </span>\n </div>\n </template>\n\n <!-- Description -->\n <div class=\"mint-protocol-editor__field\">\n <label class=\"mint-protocol-editor__field-label\">Description</label>\n <textarea\n v-model=\"stepDescription\"\n class=\"mint-protocol-editor__textarea\"\n placeholder=\"Optional step description\"\n rows=\"2\"\n />\n </div>\n </div>\n\n <!-- Preview -->\n <div v-if=\"showPreview && previewStep\" class=\"mint-protocol-editor__preview\">\n <div class=\"mint-protocol-editor__preview-title\">Preview</div>\n <div class=\"mint-protocol-editor__preview-card\">\n <div class=\"mint-protocol-editor__preview-icon\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n :d=\"stepTypeIcons[previewStep.type]\"\n />\n </svg>\n </div>\n <div class=\"mint-protocol-editor__preview-content\">\n <div class=\"mint-protocol-editor__preview-name\">{{ previewStep.name }}</div>\n <div v-if=\"previewParams\" class=\"mint-protocol-editor__preview-params\">\n {{ previewParams }}\n </div>\n </div>\n <div v-if=\"previewStep.duration\" class=\"mint-protocol-editor__preview-duration\">\n {{ formatDuration(previewStep.duration) }}\n </div>\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"mint-protocol-editor__actions\">\n <div class=\"mint-protocol-editor__actions-left\">\n <button\n type=\"button\"\n class=\"mint-protocol-editor__btn mint-protocol-editor__btn--ghost\"\n @click=\"handleSaveTemplate\"\n >\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4\" />\n </svg>\n Save as Template\n </button>\n </div>\n <button\n type=\"button\"\n class=\"mint-protocol-editor__btn mint-protocol-editor__btn--secondary\"\n @click=\"handleCancel\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mint-protocol-editor__btn mint-protocol-editor__btn--primary\"\n :disabled=\"!selectedTemplate\"\n @click=\"handleSave\"\n >\n {{ mode === 'create' ? 'Add Step' : 'Save Changes' }}\n </button>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/protocol-step-editor.css';\n</style>\n","<script setup lang=\"ts\">\n/** Read-only display of a numeric measurement in plain, scientific (×10ⁿ), or SI-prefix compact notation with optional unit and copy-to-clipboard. */\nimport { computed, ref } from 'vue'\nimport type { NumberNotation } from '../types'\n\ninterface Props {\n value: number\n precision?: number\n notation?: NumberNotation\n unit?: string\n copyable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n precision: 3,\n notation: 'auto',\n copyable: false,\n})\n\nconst copied = ref(false)\n\nconst SUPERSCRIPT_DIGITS: Record<string, string> = {\n '0': '\\u2070',\n '1': '\\u00B9',\n '2': '\\u00B2',\n '3': '\\u00B3',\n '4': '\\u2074',\n '5': '\\u2075',\n '6': '\\u2076',\n '7': '\\u2077',\n '8': '\\u2078',\n '9': '\\u2079',\n '-': '\\u207B',\n}\n\nconst SI_SUFFIXES: [number, string][] = [\n [1e12, 'T'],\n [1e9, 'G'],\n [1e6, 'M'],\n [1e3, 'k'],\n [1e-3, 'm'],\n [1e-6, '\\u00B5'],\n [1e-9, 'n'],\n [1e-12, 'p'],\n]\n\nfunction toSuperscript(exp: number): string {\n return String(exp)\n .split('')\n .map(ch => SUPERSCRIPT_DIGITS[ch] ?? ch)\n .join('')\n}\n\nfunction roundToPrecision(n: number, digits: number): string {\n return n.toFixed(digits).replace(/\\.?0+$/, '') || '0'\n}\n\ninterface FormattedNumber {\n type: 'plain' | 'scientific' | 'compact'\n mantissa?: string\n exponent?: number\n exponentStr?: string\n plain?: string\n suffix?: string\n}\n\nconst formatted = computed<FormattedNumber>(() => {\n const v = props.value\n\n if (!Number.isFinite(v)) return { type: 'plain', plain: '' }\n if (v === 0) return { type: 'plain', plain: '0' }\n\n const absV = Math.abs(v)\n\n if (props.notation === 'compact') {\n for (const [threshold, suffix] of SI_SUFFIXES) {\n if (absV >= threshold * 0.999) {\n return {\n type: 'compact',\n plain: roundToPrecision(v / threshold, props.precision),\n suffix,\n }\n }\n }\n return { type: 'plain', plain: roundToPrecision(v, props.precision) }\n }\n\n const useScientific =\n props.notation === 'scientific' ||\n props.notation === 'engineering' ||\n (props.notation === 'auto' && (absV < 0.01 || absV >= 10000))\n\n if (!useScientific) {\n return { type: 'plain', plain: roundToPrecision(v, props.precision) }\n }\n\n let exp = Math.floor(Math.log10(absV))\n\n if (props.notation === 'engineering') {\n exp = Math.floor(exp / 3) * 3\n }\n\n const mantissa = v / Math.pow(10, exp)\n const mantissaStr = roundToPrecision(mantissa, props.precision)\n\n return {\n type: 'scientific',\n mantissa: mantissaStr,\n exponent: exp,\n exponentStr: toSuperscript(exp),\n }\n})\n\nconst specialDisplay = computed(() => {\n if (Number.isNaN(props.value)) return 'NaN'\n if (props.value === Infinity) return '\\u221E'\n if (props.value === -Infinity) return '-\\u221E'\n return null\n})\n\nconst plainTextValue = computed(() => {\n if (specialDisplay.value) return specialDisplay.value\n const f = formatted.value\n let text: string\n if (f.type === 'scientific') {\n text = `${f.mantissa}e${f.exponent}`\n } else if (f.type === 'compact') {\n text = `${f.plain}${f.suffix ?? ''}`\n } else {\n text = f.plain ?? '0'\n }\n if (props.unit) text += ` ${props.unit}`\n return text\n})\n\nasync function copyToClipboard() {\n try {\n await navigator.clipboard.writeText(plainTextValue.value)\n copied.value = true\n setTimeout(() => { copied.value = false }, 1500)\n } catch {\n // clipboard not available\n }\n}\n</script>\n\n<template>\n <span class=\"mint-sci-number\">\n <!-- Special values: NaN, Infinity -->\n <template v-if=\"specialDisplay\">\n <span class=\"mint-sci-number__plain\">{{ specialDisplay }}</span>\n </template>\n\n <!-- Scientific / Engineering notation -->\n <template v-else-if=\"formatted.type === 'scientific'\">\n <span class=\"mint-sci-number__mantissa\">{{ formatted.mantissa }}</span>\n <span class=\"mint-sci-number__times\">×</span>\n <span class=\"mint-sci-number__base\">10</span>\n <span class=\"mint-sci-number__exponent\">{{ formatted.exponentStr }}</span>\n </template>\n\n <!-- Compact notation with SI suffix -->\n <template v-else-if=\"formatted.type === 'compact'\">\n <span class=\"mint-sci-number__plain\">{{ formatted.plain }}</span>\n <span v-if=\"formatted.suffix\" class=\"mint-sci-number__suffix\">{{ formatted.suffix }}</span>\n </template>\n\n <!-- Plain number -->\n <template v-else>\n <span class=\"mint-sci-number__plain\">{{ formatted.plain }}</span>\n </template>\n\n <!-- Unit -->\n <span v-if=\"unit\" class=\"mint-sci-number__unit\">{{ unit }}</span>\n\n <!-- Copy button -->\n <button\n v-if=\"copyable\"\n :class=\"['mint-sci-number__copy-btn', { 'mint-sci-number__copy-btn--copied': copied }]\"\n type=\"button\"\n :title=\"copied ? 'Copied!' : 'Copy value'\"\n @click.stop=\"copyToClipboard\"\n >\n <svg v-if=\"!copied\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\" />\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n </svg>\n <svg v-else width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n </button>\n </span>\n</template>\n\n<style>\n@import '../styles/components/scientific-number.css';\n</style>\n","<script setup lang=\"ts\">\n/** Read-only display of a numeric measurement in plain, scientific (×10ⁿ), or SI-prefix compact notation with optional unit and copy-to-clipboard. */\nimport { computed, ref } from 'vue'\nimport type { NumberNotation } from '../types'\n\ninterface Props {\n value: number\n precision?: number\n notation?: NumberNotation\n unit?: string\n copyable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n precision: 3,\n notation: 'auto',\n copyable: false,\n})\n\nconst copied = ref(false)\n\nconst SUPERSCRIPT_DIGITS: Record<string, string> = {\n '0': '\\u2070',\n '1': '\\u00B9',\n '2': '\\u00B2',\n '3': '\\u00B3',\n '4': '\\u2074',\n '5': '\\u2075',\n '6': '\\u2076',\n '7': '\\u2077',\n '8': '\\u2078',\n '9': '\\u2079',\n '-': '\\u207B',\n}\n\nconst SI_SUFFIXES: [number, string][] = [\n [1e12, 'T'],\n [1e9, 'G'],\n [1e6, 'M'],\n [1e3, 'k'],\n [1e-3, 'm'],\n [1e-6, '\\u00B5'],\n [1e-9, 'n'],\n [1e-12, 'p'],\n]\n\nfunction toSuperscript(exp: number): string {\n return String(exp)\n .split('')\n .map(ch => SUPERSCRIPT_DIGITS[ch] ?? ch)\n .join('')\n}\n\nfunction roundToPrecision(n: number, digits: number): string {\n return n.toFixed(digits).replace(/\\.?0+$/, '') || '0'\n}\n\ninterface FormattedNumber {\n type: 'plain' | 'scientific' | 'compact'\n mantissa?: string\n exponent?: number\n exponentStr?: string\n plain?: string\n suffix?: string\n}\n\nconst formatted = computed<FormattedNumber>(() => {\n const v = props.value\n\n if (!Number.isFinite(v)) return { type: 'plain', plain: '' }\n if (v === 0) return { type: 'plain', plain: '0' }\n\n const absV = Math.abs(v)\n\n if (props.notation === 'compact') {\n for (const [threshold, suffix] of SI_SUFFIXES) {\n if (absV >= threshold * 0.999) {\n return {\n type: 'compact',\n plain: roundToPrecision(v / threshold, props.precision),\n suffix,\n }\n }\n }\n return { type: 'plain', plain: roundToPrecision(v, props.precision) }\n }\n\n const useScientific =\n props.notation === 'scientific' ||\n props.notation === 'engineering' ||\n (props.notation === 'auto' && (absV < 0.01 || absV >= 10000))\n\n if (!useScientific) {\n return { type: 'plain', plain: roundToPrecision(v, props.precision) }\n }\n\n let exp = Math.floor(Math.log10(absV))\n\n if (props.notation === 'engineering') {\n exp = Math.floor(exp / 3) * 3\n }\n\n const mantissa = v / Math.pow(10, exp)\n const mantissaStr = roundToPrecision(mantissa, props.precision)\n\n return {\n type: 'scientific',\n mantissa: mantissaStr,\n exponent: exp,\n exponentStr: toSuperscript(exp),\n }\n})\n\nconst specialDisplay = computed(() => {\n if (Number.isNaN(props.value)) return 'NaN'\n if (props.value === Infinity) return '\\u221E'\n if (props.value === -Infinity) return '-\\u221E'\n return null\n})\n\nconst plainTextValue = computed(() => {\n if (specialDisplay.value) return specialDisplay.value\n const f = formatted.value\n let text: string\n if (f.type === 'scientific') {\n text = `${f.mantissa}e${f.exponent}`\n } else if (f.type === 'compact') {\n text = `${f.plain}${f.suffix ?? ''}`\n } else {\n text = f.plain ?? '0'\n }\n if (props.unit) text += ` ${props.unit}`\n return text\n})\n\nasync function copyToClipboard() {\n try {\n await navigator.clipboard.writeText(plainTextValue.value)\n copied.value = true\n setTimeout(() => { copied.value = false }, 1500)\n } catch {\n // clipboard not available\n }\n}\n</script>\n\n<template>\n <span class=\"mint-sci-number\">\n <!-- Special values: NaN, Infinity -->\n <template v-if=\"specialDisplay\">\n <span class=\"mint-sci-number__plain\">{{ specialDisplay }}</span>\n </template>\n\n <!-- Scientific / Engineering notation -->\n <template v-else-if=\"formatted.type === 'scientific'\">\n <span class=\"mint-sci-number__mantissa\">{{ formatted.mantissa }}</span>\n <span class=\"mint-sci-number__times\">×</span>\n <span class=\"mint-sci-number__base\">10</span>\n <span class=\"mint-sci-number__exponent\">{{ formatted.exponentStr }}</span>\n </template>\n\n <!-- Compact notation with SI suffix -->\n <template v-else-if=\"formatted.type === 'compact'\">\n <span class=\"mint-sci-number__plain\">{{ formatted.plain }}</span>\n <span v-if=\"formatted.suffix\" class=\"mint-sci-number__suffix\">{{ formatted.suffix }}</span>\n </template>\n\n <!-- Plain number -->\n <template v-else>\n <span class=\"mint-sci-number__plain\">{{ formatted.plain }}</span>\n </template>\n\n <!-- Unit -->\n <span v-if=\"unit\" class=\"mint-sci-number__unit\">{{ unit }}</span>\n\n <!-- Copy button -->\n <button\n v-if=\"copyable\"\n :class=\"['mint-sci-number__copy-btn', { 'mint-sci-number__copy-btn--copied': copied }]\"\n type=\"button\"\n :title=\"copied ? 'Copied!' : 'Copy value'\"\n @click.stop=\"copyToClipboard\"\n >\n <svg v-if=\"!copied\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\" />\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n </svg>\n <svg v-else width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n </button>\n </span>\n</template>\n\n<style>\n@import '../styles/components/scientific-number.css';\n</style>\n","<script setup lang=\"ts\">\n/** Renders a chemical formula string (H₂O, C₆H₁₂O₆, Ca(OH)₂) with typographically correct subscripts and superscripts for charges. */\nimport { useChemicalFormula, type FormulaPart } from '../composables/useChemicalFormula'\n\ninterface Props {\n formula: string\n inline?: boolean\n}\n\nwithDefaults(defineProps<Props>(), {\n inline: true,\n})\n\nconst { renderFormulaParts } = useChemicalFormula()\n\nfunction getParts(f: string): FormulaPart[] {\n return renderFormulaParts(f)\n}\n</script>\n\n<template>\n <component\n :is=\"inline ? 'span' : 'div'\"\n :class=\"['mint-chem-formula', !inline ? 'mint-chem-formula--block' : '']\"\n >\n <template v-for=\"(part, i) in getParts(formula)\" :key=\"i\">\n <span v-if=\"part.type === 'element'\" class=\"mint-chem-formula__element\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'subscript'\" class=\"mint-chem-formula__subscript\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'superscript'\" class=\"mint-chem-formula__superscript\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'paren'\" class=\"mint-chem-formula__paren\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'dot'\" class=\"mint-chem-formula__dot\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'charge'\" class=\"mint-chem-formula__charge\">{{ part.text }}</span>\n </template>\n </component>\n</template>\n\n<style>\n@import '../styles/components/chemical-formula.css';\n</style>\n","<script setup lang=\"ts\">\n/** Renders a chemical formula string (H₂O, C₆H₁₂O₆, Ca(OH)₂) with typographically correct subscripts and superscripts for charges. */\nimport { useChemicalFormula, type FormulaPart } from '../composables/useChemicalFormula'\n\ninterface Props {\n formula: string\n inline?: boolean\n}\n\nwithDefaults(defineProps<Props>(), {\n inline: true,\n})\n\nconst { renderFormulaParts } = useChemicalFormula()\n\nfunction getParts(f: string): FormulaPart[] {\n return renderFormulaParts(f)\n}\n</script>\n\n<template>\n <component\n :is=\"inline ? 'span' : 'div'\"\n :class=\"['mint-chem-formula', !inline ? 'mint-chem-formula--block' : '']\"\n >\n <template v-for=\"(part, i) in getParts(formula)\" :key=\"i\">\n <span v-if=\"part.type === 'element'\" class=\"mint-chem-formula__element\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'subscript'\" class=\"mint-chem-formula__subscript\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'superscript'\" class=\"mint-chem-formula__superscript\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'paren'\" class=\"mint-chem-formula__paren\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'dot'\" class=\"mint-chem-formula__dot\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'charge'\" class=\"mint-chem-formula__charge\">{{ part.text }}</span>\n </template>\n </component>\n</template>\n\n<style>\n@import '../styles/components/chemical-formula.css';\n</style>\n","<script setup lang=\"ts\">\n/** Filterable, sortable timeline of audit entries for tracking experiment or resource history. */\nimport { computed, ref } from 'vue'\nimport type { AuditEntryType, AuditEntry } from '../types'\n\ninterface Props {\n entries: AuditEntry[]\n maxHeight?: string\n showFilters?: boolean\n emptyMessage?: string\n order?: 'newest' | 'oldest'\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showFilters: false,\n emptyMessage: 'No activity yet',\n order: 'newest',\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'entry-click': [entry: AuditEntry]\n}>()\n\nconst filterType = ref<AuditEntryType | ''>('')\nconst filterUser = ref('')\n\nconst uniqueTypes = computed(() => {\n const types = new Set(props.entries.map(e => e.type))\n return Array.from(types)\n})\n\nconst uniqueUsers = computed(() => {\n const users = new Set(props.entries.flatMap(e => e.user ? [e.user] : []))\n return Array.from(users)\n})\n\nconst filteredEntries = computed(() => {\n let result = props.entries\n if (filterType.value) {\n result = result.filter(e => e.type === filterType.value)\n }\n if (filterUser.value) {\n result = result.filter(e => e.user === filterUser.value)\n }\n return result\n})\n\nconst sortedEntries = computed(() => {\n const entries = [...filteredEntries.value]\n entries.sort((a, b) => {\n const timeA = new Date(a.timestamp).getTime()\n const timeB = new Date(b.timestamp).getTime()\n return props.order === 'newest' ? timeB - timeA : timeA - timeB\n })\n return entries\n})\n\nfunction getInitials(name: string): string {\n const words = name.trim().split(/\\s+/)\n if (words.length >= 2) {\n return (words[0][0] + words[1][0]).toUpperCase()\n }\n return (words[0]?.[0] ?? '').toUpperCase()\n}\n\nconst rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })\n\nfunction formatTimestamp(timestamp: Date | string): string {\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp)\n const now = Date.now()\n const diffMs = date.getTime() - now\n const diffSec = Math.round(diffMs / 1000)\n const diffMin = Math.round(diffSec / 60)\n const diffHr = Math.round(diffMin / 60)\n const diffDay = Math.round(diffHr / 24)\n const diffWeek = Math.round(diffDay / 7)\n\n if (Math.abs(diffSec) < 60) return rtf.format(diffSec, 'second')\n if (Math.abs(diffMin) < 60) return rtf.format(diffMin, 'minute')\n if (Math.abs(diffHr) < 24) return rtf.format(diffHr, 'hour')\n if (Math.abs(diffDay) < 7) return rtf.format(diffDay, 'day')\n if (Math.abs(diffWeek) < 5) return rtf.format(diffWeek, 'week')\n return date.toLocaleDateString()\n}\n\nfunction handleEntryClick(entry: AuditEntry) {\n emit('entry-click', entry)\n}\n</script>\n\n<template>\n <div :class=\"['mint-audit-trail', `mint-audit-trail--${size}`]\">\n <div v-if=\"showFilters\" class=\"mint-audit-trail__filters\">\n <select v-model=\"filterType\" class=\"mint-audit-trail__filter-select\">\n <option value=\"\">All types</option>\n <option v-for=\"t in uniqueTypes\" :key=\"t\" :value=\"t\">{{ t }}</option>\n </select>\n <select v-model=\"filterUser\" class=\"mint-audit-trail__filter-select\">\n <option value=\"\">All users</option>\n <option v-for=\"u in uniqueUsers\" :key=\"u\" :value=\"u\">{{ u }}</option>\n </select>\n </div>\n\n <div\n v-if=\"sortedEntries.length\"\n class=\"mint-audit-trail__list\"\n :style=\"maxHeight ? { maxHeight, overflowY: 'auto' } : {}\"\n >\n <div class=\"mint-audit-trail__line\" />\n <div\n v-for=\"entry in sortedEntries\"\n :key=\"entry.id\"\n :class=\"['mint-audit-trail__entry', `mint-audit-trail__entry--${entry.type}`]\"\n @click=\"handleEntryClick(entry)\"\n >\n <slot name=\"entry\" :entry=\"entry\">\n <div class=\"mint-audit-trail__dot\" />\n <div class=\"mint-audit-trail__content\">\n <div class=\"mint-audit-trail__header\">\n <div v-if=\"entry.user\" class=\"mint-audit-trail__avatar\">{{ getInitials(entry.user) }}</div>\n <span v-if=\"entry.user\" class=\"mint-audit-trail__user\">{{ entry.user }}</span>\n <span class=\"mint-audit-trail__timestamp\">{{ formatTimestamp(entry.timestamp) }}</span>\n </div>\n <div class=\"mint-audit-trail__action\">{{ entry.action }}</div>\n <div v-if=\"entry.detail\" class=\"mint-audit-trail__detail\">{{ entry.detail }}</div>\n <div v-if=\"entry.metadata && Object.keys(entry.metadata).length\" class=\"mint-audit-trail__metadata\">\n <span\n v-for=\"(value, key) in entry.metadata\"\n :key=\"String(key)\"\n class=\"mint-audit-trail__metadata-item\"\n >\n <span class=\"mint-audit-trail__metadata-key\">{{ key }}:</span>\n {{ value }}\n </span>\n </div>\n </div>\n </slot>\n </div>\n </div>\n\n <div v-if=\"!sortedEntries.length\" class=\"mint-audit-trail__empty\">\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/audit-trail.css';\n</style>\n","<script setup lang=\"ts\">\n/** Filterable, sortable timeline of audit entries for tracking experiment or resource history. */\nimport { computed, ref } from 'vue'\nimport type { AuditEntryType, AuditEntry } from '../types'\n\ninterface Props {\n entries: AuditEntry[]\n maxHeight?: string\n showFilters?: boolean\n emptyMessage?: string\n order?: 'newest' | 'oldest'\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showFilters: false,\n emptyMessage: 'No activity yet',\n order: 'newest',\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'entry-click': [entry: AuditEntry]\n}>()\n\nconst filterType = ref<AuditEntryType | ''>('')\nconst filterUser = ref('')\n\nconst uniqueTypes = computed(() => {\n const types = new Set(props.entries.map(e => e.type))\n return Array.from(types)\n})\n\nconst uniqueUsers = computed(() => {\n const users = new Set(props.entries.flatMap(e => e.user ? [e.user] : []))\n return Array.from(users)\n})\n\nconst filteredEntries = computed(() => {\n let result = props.entries\n if (filterType.value) {\n result = result.filter(e => e.type === filterType.value)\n }\n if (filterUser.value) {\n result = result.filter(e => e.user === filterUser.value)\n }\n return result\n})\n\nconst sortedEntries = computed(() => {\n const entries = [...filteredEntries.value]\n entries.sort((a, b) => {\n const timeA = new Date(a.timestamp).getTime()\n const timeB = new Date(b.timestamp).getTime()\n return props.order === 'newest' ? timeB - timeA : timeA - timeB\n })\n return entries\n})\n\nfunction getInitials(name: string): string {\n const words = name.trim().split(/\\s+/)\n if (words.length >= 2) {\n return (words[0][0] + words[1][0]).toUpperCase()\n }\n return (words[0]?.[0] ?? '').toUpperCase()\n}\n\nconst rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' })\n\nfunction formatTimestamp(timestamp: Date | string): string {\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp)\n const now = Date.now()\n const diffMs = date.getTime() - now\n const diffSec = Math.round(diffMs / 1000)\n const diffMin = Math.round(diffSec / 60)\n const diffHr = Math.round(diffMin / 60)\n const diffDay = Math.round(diffHr / 24)\n const diffWeek = Math.round(diffDay / 7)\n\n if (Math.abs(diffSec) < 60) return rtf.format(diffSec, 'second')\n if (Math.abs(diffMin) < 60) return rtf.format(diffMin, 'minute')\n if (Math.abs(diffHr) < 24) return rtf.format(diffHr, 'hour')\n if (Math.abs(diffDay) < 7) return rtf.format(diffDay, 'day')\n if (Math.abs(diffWeek) < 5) return rtf.format(diffWeek, 'week')\n return date.toLocaleDateString()\n}\n\nfunction handleEntryClick(entry: AuditEntry) {\n emit('entry-click', entry)\n}\n</script>\n\n<template>\n <div :class=\"['mint-audit-trail', `mint-audit-trail--${size}`]\">\n <div v-if=\"showFilters\" class=\"mint-audit-trail__filters\">\n <select v-model=\"filterType\" class=\"mint-audit-trail__filter-select\">\n <option value=\"\">All types</option>\n <option v-for=\"t in uniqueTypes\" :key=\"t\" :value=\"t\">{{ t }}</option>\n </select>\n <select v-model=\"filterUser\" class=\"mint-audit-trail__filter-select\">\n <option value=\"\">All users</option>\n <option v-for=\"u in uniqueUsers\" :key=\"u\" :value=\"u\">{{ u }}</option>\n </select>\n </div>\n\n <div\n v-if=\"sortedEntries.length\"\n class=\"mint-audit-trail__list\"\n :style=\"maxHeight ? { maxHeight, overflowY: 'auto' } : {}\"\n >\n <div class=\"mint-audit-trail__line\" />\n <div\n v-for=\"entry in sortedEntries\"\n :key=\"entry.id\"\n :class=\"['mint-audit-trail__entry', `mint-audit-trail__entry--${entry.type}`]\"\n @click=\"handleEntryClick(entry)\"\n >\n <slot name=\"entry\" :entry=\"entry\">\n <div class=\"mint-audit-trail__dot\" />\n <div class=\"mint-audit-trail__content\">\n <div class=\"mint-audit-trail__header\">\n <div v-if=\"entry.user\" class=\"mint-audit-trail__avatar\">{{ getInitials(entry.user) }}</div>\n <span v-if=\"entry.user\" class=\"mint-audit-trail__user\">{{ entry.user }}</span>\n <span class=\"mint-audit-trail__timestamp\">{{ formatTimestamp(entry.timestamp) }}</span>\n </div>\n <div class=\"mint-audit-trail__action\">{{ entry.action }}</div>\n <div v-if=\"entry.detail\" class=\"mint-audit-trail__detail\">{{ entry.detail }}</div>\n <div v-if=\"entry.metadata && Object.keys(entry.metadata).length\" class=\"mint-audit-trail__metadata\">\n <span\n v-for=\"(value, key) in entry.metadata\"\n :key=\"String(key)\"\n class=\"mint-audit-trail__metadata-item\"\n >\n <span class=\"mint-audit-trail__metadata-key\">{{ key }}:</span>\n {{ value }}\n </span>\n </div>\n </div>\n </slot>\n </div>\n </div>\n\n <div v-if=\"!sortedEntries.length\" class=\"mint-audit-trail__empty\">\n <slot name=\"empty\">{{ emptyMessage }}</slot>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/audit-trail.css';\n</style>\n","<script setup lang=\"ts\">\n/** Scrollable list tracking per-item status for a batch operation with overall progress summary. */\nimport { computed, ref, watch, nextTick } from 'vue'\nimport type { BatchItem, BatchSummary } from '../types'\nimport { useExpansionSet } from '../composables/useExpansionSet'\n\ninterface Props {\n items: BatchItem[]\n showSummary?: boolean\n title?: string\n maxHeight?: string\n autoScroll?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showSummary: true,\n autoScroll: true,\n})\n\nconst emit = defineEmits<{\n 'retry': [id: string]\n 'cancel': [id: string]\n}>()\n\nconst listRef = ref<HTMLElement | null>(null)\nconst errorExpansion = useExpansionSet()\n\nconst summary = computed<BatchSummary>(() => {\n const total = props.items.length\n const completed = props.items.filter(i => i.status === 'completed').length\n const processing = props.items.filter(i => i.status === 'processing').length\n const error = props.items.filter(i => i.status === 'error').length\n const pending = props.items.filter(i => i.status === 'pending').length\n const skipped = props.items.filter(i => i.status === 'skipped').length\n const percent = total > 0 ? Math.round(((completed + skipped) / total) * 100) : 0\n return { total, completed, processing, error, pending, skipped, percent }\n})\n\nfunction toggleError(id: string) {\n errorExpansion.toggle(id)\n}\n\nwatch(\n () => props.items.map(i => i.status),\n (newStatuses, oldStatuses) => {\n if (!props.autoScroll || !listRef.value) return\n for (let i = 0; i < newStatuses.length; i++) {\n if (newStatuses[i] === 'processing' && oldStatuses?.[i] !== 'processing') {\n nextTick(() => {\n const el = listRef.value?.querySelector(`[data-item-id=\"${props.items[i].id}\"]`)\n el?.scrollIntoView({ behavior: 'smooth', block: 'center' })\n })\n break\n }\n }\n },\n)\n</script>\n\n<template>\n <div class=\"mint-batch-progress\">\n <div v-if=\"title\" class=\"mint-batch-progress__header\">\n <span class=\"mint-batch-progress__title\">{{ title }}</span>\n <span class=\"mint-batch-progress__percent\">{{ summary.percent }}%</span>\n </div>\n\n <div class=\"mint-batch-progress__overall\">\n <div\n class=\"mint-batch-progress__overall-bar\"\n :style=\"{ width: `${summary.percent}%` }\"\n />\n </div>\n\n <slot v-if=\"showSummary\" name=\"summary\" :summary=\"summary\">\n <div class=\"mint-batch-progress__summary\">\n <span v-if=\"summary.completed\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--completed\">\n {{ summary.completed }} completed\n </span>\n <span v-if=\"summary.processing\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--processing\">\n {{ summary.processing }} processing\n </span>\n <span v-if=\"summary.error\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--error\">\n {{ summary.error }} failed\n </span>\n <span v-if=\"summary.pending\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--pending\">\n {{ summary.pending }} pending\n </span>\n <span v-if=\"summary.skipped\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--skipped\">\n {{ summary.skipped }} skipped\n </span>\n </div>\n </slot>\n\n <div\n ref=\"listRef\"\n class=\"mint-batch-progress__list\"\n :style=\"maxHeight ? { maxHeight, overflowY: 'auto' } : {}\"\n >\n <div\n v-for=\"item in items\"\n :key=\"item.id\"\n :data-item-id=\"item.id\"\n :class=\"['mint-batch-progress__item', `mint-batch-progress__item--${item.status}`]\"\n >\n <slot name=\"item\" :item=\"item\">\n <div class=\"mint-batch-progress__item-row\">\n <!-- Status icon -->\n <div :class=\"['mint-batch-progress__item-icon', `mint-batch-progress__item-icon--${item.status}`]\">\n <!-- Pending: clock -->\n <svg v-if=\"item.status === 'pending'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <circle cx=\"8\" cy=\"8\" r=\"6.5\" />\n <path d=\"M8 4.5V8l2.5 1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <!-- Processing: spinner -->\n <svg v-else-if=\"item.status === 'processing'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M8 1.5A6.5 6.5 0 1 1 1.5 8\" stroke-linecap=\"round\" />\n </svg>\n <!-- Completed: checkmark -->\n <svg v-else-if=\"item.status === 'completed'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M3.5 8.5L6.5 11.5L12.5 4.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <!-- Error: x -->\n <svg v-else-if=\"item.status === 'error'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M4.5 4.5l7 7M11.5 4.5l-7 7\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <!-- Skipped: dash -->\n <svg v-else viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M4 8h8\" stroke-linecap=\"round\" />\n </svg>\n </div>\n\n <span class=\"mint-batch-progress__item-label\">{{ item.label }}</span>\n\n <!-- Mini progress bar for processing items -->\n <div v-if=\"item.status === 'processing' && item.progress !== undefined\" class=\"mint-batch-progress__item-progress\">\n <div class=\"mint-batch-progress__item-progress-bar\" :style=\"{ width: `${item.progress}%` }\" />\n </div>\n\n <!-- Actions -->\n <div class=\"mint-batch-progress__item-actions\">\n <button\n v-if=\"item.status === 'error'\"\n type=\"button\"\n class=\"mint-batch-progress__retry-btn\"\n @click.stop=\"emit('retry', item.id)\"\n >\n Retry\n </button>\n <button\n v-if=\"item.status === 'processing'\"\n type=\"button\"\n class=\"mint-batch-progress__cancel-btn\"\n @click.stop=\"emit('cancel', item.id)\"\n >\n Cancel\n </button>\n </div>\n </div>\n\n <!-- Error message (expandable) -->\n <div v-if=\"item.status === 'error' && item.message\" class=\"mint-batch-progress__item-error\">\n <button\n type=\"button\"\n class=\"mint-batch-progress__error-toggle\"\n @click.stop=\"toggleError(item.id)\"\n >\n {{ errorExpansion.isExpanded(item.id) ? 'Hide error' : 'Show error' }}\n </button>\n <div v-if=\"errorExpansion.isExpanded(item.id)\" class=\"mint-batch-progress__item-message\">\n {{ item.message }}\n </div>\n </div>\n </slot>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/batch-progress-list.css';\n</style>\n","<script setup lang=\"ts\">\n/** Scrollable list tracking per-item status for a batch operation with overall progress summary. */\nimport { computed, ref, watch, nextTick } from 'vue'\nimport type { BatchItem, BatchSummary } from '../types'\nimport { useExpansionSet } from '../composables/useExpansionSet'\n\ninterface Props {\n items: BatchItem[]\n showSummary?: boolean\n title?: string\n maxHeight?: string\n autoScroll?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showSummary: true,\n autoScroll: true,\n})\n\nconst emit = defineEmits<{\n 'retry': [id: string]\n 'cancel': [id: string]\n}>()\n\nconst listRef = ref<HTMLElement | null>(null)\nconst errorExpansion = useExpansionSet()\n\nconst summary = computed<BatchSummary>(() => {\n const total = props.items.length\n const completed = props.items.filter(i => i.status === 'completed').length\n const processing = props.items.filter(i => i.status === 'processing').length\n const error = props.items.filter(i => i.status === 'error').length\n const pending = props.items.filter(i => i.status === 'pending').length\n const skipped = props.items.filter(i => i.status === 'skipped').length\n const percent = total > 0 ? Math.round(((completed + skipped) / total) * 100) : 0\n return { total, completed, processing, error, pending, skipped, percent }\n})\n\nfunction toggleError(id: string) {\n errorExpansion.toggle(id)\n}\n\nwatch(\n () => props.items.map(i => i.status),\n (newStatuses, oldStatuses) => {\n if (!props.autoScroll || !listRef.value) return\n for (let i = 0; i < newStatuses.length; i++) {\n if (newStatuses[i] === 'processing' && oldStatuses?.[i] !== 'processing') {\n nextTick(() => {\n const el = listRef.value?.querySelector(`[data-item-id=\"${props.items[i].id}\"]`)\n el?.scrollIntoView({ behavior: 'smooth', block: 'center' })\n })\n break\n }\n }\n },\n)\n</script>\n\n<template>\n <div class=\"mint-batch-progress\">\n <div v-if=\"title\" class=\"mint-batch-progress__header\">\n <span class=\"mint-batch-progress__title\">{{ title }}</span>\n <span class=\"mint-batch-progress__percent\">{{ summary.percent }}%</span>\n </div>\n\n <div class=\"mint-batch-progress__overall\">\n <div\n class=\"mint-batch-progress__overall-bar\"\n :style=\"{ width: `${summary.percent}%` }\"\n />\n </div>\n\n <slot v-if=\"showSummary\" name=\"summary\" :summary=\"summary\">\n <div class=\"mint-batch-progress__summary\">\n <span v-if=\"summary.completed\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--completed\">\n {{ summary.completed }} completed\n </span>\n <span v-if=\"summary.processing\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--processing\">\n {{ summary.processing }} processing\n </span>\n <span v-if=\"summary.error\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--error\">\n {{ summary.error }} failed\n </span>\n <span v-if=\"summary.pending\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--pending\">\n {{ summary.pending }} pending\n </span>\n <span v-if=\"summary.skipped\" class=\"mint-batch-progress__summary-item mint-batch-progress__summary-item--skipped\">\n {{ summary.skipped }} skipped\n </span>\n </div>\n </slot>\n\n <div\n ref=\"listRef\"\n class=\"mint-batch-progress__list\"\n :style=\"maxHeight ? { maxHeight, overflowY: 'auto' } : {}\"\n >\n <div\n v-for=\"item in items\"\n :key=\"item.id\"\n :data-item-id=\"item.id\"\n :class=\"['mint-batch-progress__item', `mint-batch-progress__item--${item.status}`]\"\n >\n <slot name=\"item\" :item=\"item\">\n <div class=\"mint-batch-progress__item-row\">\n <!-- Status icon -->\n <div :class=\"['mint-batch-progress__item-icon', `mint-batch-progress__item-icon--${item.status}`]\">\n <!-- Pending: clock -->\n <svg v-if=\"item.status === 'pending'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <circle cx=\"8\" cy=\"8\" r=\"6.5\" />\n <path d=\"M8 4.5V8l2.5 1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <!-- Processing: spinner -->\n <svg v-else-if=\"item.status === 'processing'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M8 1.5A6.5 6.5 0 1 1 1.5 8\" stroke-linecap=\"round\" />\n </svg>\n <!-- Completed: checkmark -->\n <svg v-else-if=\"item.status === 'completed'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M3.5 8.5L6.5 11.5L12.5 4.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <!-- Error: x -->\n <svg v-else-if=\"item.status === 'error'\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M4.5 4.5l7 7M11.5 4.5l-7 7\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\n </svg>\n <!-- Skipped: dash -->\n <svg v-else viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M4 8h8\" stroke-linecap=\"round\" />\n </svg>\n </div>\n\n <span class=\"mint-batch-progress__item-label\">{{ item.label }}</span>\n\n <!-- Mini progress bar for processing items -->\n <div v-if=\"item.status === 'processing' && item.progress !== undefined\" class=\"mint-batch-progress__item-progress\">\n <div class=\"mint-batch-progress__item-progress-bar\" :style=\"{ width: `${item.progress}%` }\" />\n </div>\n\n <!-- Actions -->\n <div class=\"mint-batch-progress__item-actions\">\n <button\n v-if=\"item.status === 'error'\"\n type=\"button\"\n class=\"mint-batch-progress__retry-btn\"\n @click.stop=\"emit('retry', item.id)\"\n >\n Retry\n </button>\n <button\n v-if=\"item.status === 'processing'\"\n type=\"button\"\n class=\"mint-batch-progress__cancel-btn\"\n @click.stop=\"emit('cancel', item.id)\"\n >\n Cancel\n </button>\n </div>\n </div>\n\n <!-- Error message (expandable) -->\n <div v-if=\"item.status === 'error' && item.message\" class=\"mint-batch-progress__item-error\">\n <button\n type=\"button\"\n class=\"mint-batch-progress__error-toggle\"\n @click.stop=\"toggleError(item.id)\"\n >\n {{ errorExpansion.isExpanded(item.id) ? 'Hide error' : 'Show error' }}\n </button>\n <div v-if=\"errorExpansion.isExpanded(item.id)\" class=\"mint-batch-progress__item-message\">\n {{ item.message }}\n </div>\n </div>\n </slot>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/batch-progress-list.css';\n</style>\n","<script setup lang=\"ts\">\n/** Tabbed viewer for experiment output data with summary, hierarchy tree, and table views. */\nimport { ref, computed, watch } from 'vue'\nimport type { TreeNode, DataFrameColumn, SummaryData } from '../types'\nimport { useExperimentData } from '../composables/useExperimentData'\nimport SampleHierarchyTree from './SampleHierarchyTree.vue'\nimport DataFrame from './DataFrame.vue'\nimport SegmentedControl from './SegmentedControl.vue'\nimport BaseButton from './BaseButton.vue'\nimport Skeleton from './Skeleton.vue'\n\ninterface Props {\n treeData?: TreeNode[]\n tableData?: Record<string, unknown>[]\n tableColumns?: DataFrameColumn[] | undefined\n summaryData?: SummaryData | null\n defaultView?: 'summary' | 'tree' | 'table'\n title?: string\n pluginName?: string\n pluginRoutePrefix?: string\n experimentId?: number\n loading?: boolean\n downloadJsonUrl?: string\n downloadCsvUrl?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: 'Data',\n defaultView: 'summary',\n loading: false,\n})\n\n// Auto-fetch when experimentId is provided and no prop data is given\nconst expData = useExperimentData()\nconst hasPropData = computed(() =>\n (props.treeData && props.treeData.length > 0) || props.summaryData,\n)\n\nwatch(\n () => props.experimentId,\n (id) => {\n if (id && !hasPropData.value) {\n expData.fetch(id)\n }\n },\n { immediate: true },\n)\n\n// Merge: props take priority over composable data\nconst mergedTreeData = computed(() =>\n props.treeData?.length ? props.treeData : expData.treeData.value,\n)\nconst mergedTableData = computed(() =>\n props.tableData?.length ? props.tableData : expData.tableData.value,\n)\nconst mergedSummaryData = computed(() =>\n props.summaryData ?? expData.summaryData.value,\n)\nconst isLoading = computed(() =>\n props.loading || expData.isLoading.value,\n)\n\nconst emit = defineEmits<{\n 'open-plugin': []\n 'download-json': []\n 'download-csv': []\n}>()\n\nconst viewMode = ref<string | number>(props.defaultView)\n\n// Reset view mode when defaultView prop changes\nwatch(() => props.defaultView, (val) => { viewMode.value = val })\n\nconst viewOptions = computed(() => {\n const opts = []\n if (mergedSummaryData.value) {\n opts.push({ value: 'summary', label: 'Summary' })\n }\n opts.push({ value: 'tree', label: 'Tree' })\n opts.push({ value: 'table', label: 'Table' })\n return opts\n})\n\n// Fall back to tree if summary is selected but no summary data\nwatch(mergedSummaryData, (val) => {\n if (!val && viewMode.value === 'summary') {\n viewMode.value = 'tree'\n }\n}, { immediate: true })\n\nconst hasTableData = computed(() =>\n mergedTableData.value && mergedTableData.value.length > 0,\n)\n\nconst metadataEntries = computed(() => {\n if (!mergedSummaryData.value?.metadata) return []\n return Object.entries(mergedSummaryData.value.metadata)\n .filter(([, v]) => v !== null && v !== undefined && v !== '')\n .map(([key, value]) => ({\n key: key.replace(/_/g, ' '),\n value: String(value),\n }))\n})\n\nfunction humanizeColumn(col: string): string {\n return col.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase())\n}\n\nfunction formatCellValue(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'object') return JSON.stringify(value)\n return String(value)\n}\n\nfunction columnsForSection(columns: string[]): DataFrameColumn[] {\n return columns.map((col) => ({\n key: col,\n label: humanizeColumn(col),\n sortable: true,\n formatter: (value: unknown) => formatCellValue(value),\n }))\n}\n\nfunction handleDownloadJson() {\n if (props.downloadJsonUrl) {\n window.open(props.downloadJsonUrl, '_blank')\n }\n emit('download-json')\n}\n\nfunction handleDownloadCsv() {\n if (props.downloadCsvUrl) {\n window.open(props.downloadCsvUrl, '_blank')\n }\n emit('download-csv')\n}\n</script>\n\n<template>\n <div class=\"mint-data-viewer\">\n <div class=\"mint-data-viewer__header\">\n <div class=\"mint-data-viewer__controls\">\n <SegmentedControl\n v-model=\"viewMode\"\n :options=\"viewOptions\"\n variant=\"card\"\n size=\"sm\"\n :full-width=\"false\"\n />\n </div>\n <div class=\"mint-data-viewer__actions\">\n <BaseButton\n v-if=\"pluginRoutePrefix && experimentId\"\n variant=\"secondary\"\n size=\"sm\"\n @click=\"emit('open-plugin')\"\n >\n Open in {{ pluginName || 'Plugin' }}\n </BaseButton>\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n @click=\"handleDownloadJson\"\n >\n JSON\n </BaseButton>\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n @click=\"handleDownloadCsv\"\n >\n CSV\n </BaseButton>\n </div>\n </div>\n\n <div class=\"mint-data-viewer__content\">\n <div v-if=\"isLoading\" class=\"mint-data-viewer__loading\">\n <div class=\"mint-data-viewer__skeleton\">\n <Skeleton width=\"40%\" height=\"16px\" />\n <Skeleton width=\"100%\" height=\"12px\" />\n <Skeleton width=\"100%\" height=\"12px\" />\n <Skeleton width=\"75%\" height=\"12px\" />\n </div>\n </div>\n <template v-else>\n <!-- Summary View -->\n <div v-if=\"viewMode === 'summary' && mergedSummaryData\" class=\"mint-summary\">\n <!-- Metadata pills -->\n <div v-if=\"metadataEntries.length\" class=\"mint-summary__metadata\">\n <span\n v-for=\"entry in metadataEntries\"\n :key=\"entry.key\"\n class=\"mint-summary__pill\"\n >\n <span class=\"mint-summary__pill-key\">{{ entry.key }}</span>\n <span class=\"mint-summary__pill-value\">{{ entry.value }}</span>\n </span>\n </div>\n\n <!-- Sections -->\n <div\n v-for=\"section in mergedSummaryData.sections\"\n :key=\"section.key\"\n class=\"mint-summary__section\"\n >\n <!-- Group section: cards with embedded tables -->\n <template v-if=\"section.type === 'group' && section.items\">\n <div\n v-for=\"(item, idx) in section.items\"\n :key=\"idx\"\n class=\"mint-summary__group-card\"\n >\n <div class=\"mint-summary__group-header\">\n <span class=\"mint-summary__group-label\">{{ item.label }}</span>\n <span class=\"mint-summary__group-count\">\n {{ item.item_count }} {{ item.item_key }}\n </span>\n </div>\n <div v-if=\"item.metadata && Object.keys(item.metadata).length\" class=\"mint-summary__group-meta\">\n <span\n v-for=\"(val, key) in item.metadata\"\n :key=\"String(key)\"\n class=\"mint-summary__pill mint-summary__pill--sm\"\n >\n <span class=\"mint-summary__pill-key\">{{ String(key).replace(/_/g, ' ') }}</span>\n <span class=\"mint-summary__pill-value\">{{ val }}</span>\n </span>\n </div>\n <DataFrame\n v-if=\"item.rows.length\"\n :data=\"item.rows\"\n :columns=\"columnsForSection(item.columns)\"\n :searchable=\"item.rows.length > 10\"\n :sortable=\"true\"\n :striped=\"true\"\n size=\"sm\"\n />\n </div>\n </template>\n\n <!-- Table section: flat table -->\n <template v-else-if=\"section.type === 'table' && section.rows\">\n <div class=\"mint-summary__table-header\">\n <span class=\"mint-summary__section-label\">{{ section.label }}</span>\n <span class=\"mint-summary__section-count\">{{ section.row_count }} rows</span>\n </div>\n <DataFrame\n :data=\"section.rows\"\n :columns=\"columnsForSection(section.columns || [])\"\n :searchable=\"(section.rows?.length || 0) > 10\"\n :sortable=\"true\"\n :striped=\"true\"\n size=\"sm\"\n />\n </template>\n </div>\n </div>\n\n <!-- Tree View -->\n <SampleHierarchyTree\n v-else-if=\"viewMode === 'tree'\"\n :nodes=\"mergedTreeData\"\n :expand-all=\"false\"\n :show-icons=\"true\"\n :show-counts=\"true\"\n size=\"sm\"\n />\n\n <!-- Table View -->\n <DataFrame\n v-else-if=\"viewMode === 'table' && hasTableData\"\n :data=\"mergedTableData!\"\n :columns=\"tableColumns ?? []\"\n :searchable=\"true\"\n :sortable=\"true\"\n />\n <div v-else-if=\"viewMode === 'table'\" class=\"mint-data-viewer__empty\">\n No tabular data available. Use tree view.\n </div>\n </template>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/experiment-data-viewer.css';\n</style>\n","<script setup lang=\"ts\">\n/** Tabbed viewer for experiment output data with summary, hierarchy tree, and table views. */\nimport { ref, computed, watch } from 'vue'\nimport type { TreeNode, DataFrameColumn, SummaryData } from '../types'\nimport { useExperimentData } from '../composables/useExperimentData'\nimport SampleHierarchyTree from './SampleHierarchyTree.vue'\nimport DataFrame from './DataFrame.vue'\nimport SegmentedControl from './SegmentedControl.vue'\nimport BaseButton from './BaseButton.vue'\nimport Skeleton from './Skeleton.vue'\n\ninterface Props {\n treeData?: TreeNode[]\n tableData?: Record<string, unknown>[]\n tableColumns?: DataFrameColumn[] | undefined\n summaryData?: SummaryData | null\n defaultView?: 'summary' | 'tree' | 'table'\n title?: string\n pluginName?: string\n pluginRoutePrefix?: string\n experimentId?: number\n loading?: boolean\n downloadJsonUrl?: string\n downloadCsvUrl?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: 'Data',\n defaultView: 'summary',\n loading: false,\n})\n\n// Auto-fetch when experimentId is provided and no prop data is given\nconst expData = useExperimentData()\nconst hasPropData = computed(() =>\n (props.treeData && props.treeData.length > 0) || props.summaryData,\n)\n\nwatch(\n () => props.experimentId,\n (id) => {\n if (id && !hasPropData.value) {\n expData.fetch(id)\n }\n },\n { immediate: true },\n)\n\n// Merge: props take priority over composable data\nconst mergedTreeData = computed(() =>\n props.treeData?.length ? props.treeData : expData.treeData.value,\n)\nconst mergedTableData = computed(() =>\n props.tableData?.length ? props.tableData : expData.tableData.value,\n)\nconst mergedSummaryData = computed(() =>\n props.summaryData ?? expData.summaryData.value,\n)\nconst isLoading = computed(() =>\n props.loading || expData.isLoading.value,\n)\n\nconst emit = defineEmits<{\n 'open-plugin': []\n 'download-json': []\n 'download-csv': []\n}>()\n\nconst viewMode = ref<string | number>(props.defaultView)\n\n// Reset view mode when defaultView prop changes\nwatch(() => props.defaultView, (val) => { viewMode.value = val })\n\nconst viewOptions = computed(() => {\n const opts = []\n if (mergedSummaryData.value) {\n opts.push({ value: 'summary', label: 'Summary' })\n }\n opts.push({ value: 'tree', label: 'Tree' })\n opts.push({ value: 'table', label: 'Table' })\n return opts\n})\n\n// Fall back to tree if summary is selected but no summary data\nwatch(mergedSummaryData, (val) => {\n if (!val && viewMode.value === 'summary') {\n viewMode.value = 'tree'\n }\n}, { immediate: true })\n\nconst hasTableData = computed(() =>\n mergedTableData.value && mergedTableData.value.length > 0,\n)\n\nconst metadataEntries = computed(() => {\n if (!mergedSummaryData.value?.metadata) return []\n return Object.entries(mergedSummaryData.value.metadata)\n .filter(([, v]) => v !== null && v !== undefined && v !== '')\n .map(([key, value]) => ({\n key: key.replace(/_/g, ' '),\n value: String(value),\n }))\n})\n\nfunction humanizeColumn(col: string): string {\n return col.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase())\n}\n\nfunction formatCellValue(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'object') return JSON.stringify(value)\n return String(value)\n}\n\nfunction columnsForSection(columns: string[]): DataFrameColumn[] {\n return columns.map((col) => ({\n key: col,\n label: humanizeColumn(col),\n sortable: true,\n formatter: (value: unknown) => formatCellValue(value),\n }))\n}\n\nfunction handleDownloadJson() {\n if (props.downloadJsonUrl) {\n window.open(props.downloadJsonUrl, '_blank')\n }\n emit('download-json')\n}\n\nfunction handleDownloadCsv() {\n if (props.downloadCsvUrl) {\n window.open(props.downloadCsvUrl, '_blank')\n }\n emit('download-csv')\n}\n</script>\n\n<template>\n <div class=\"mint-data-viewer\">\n <div class=\"mint-data-viewer__header\">\n <div class=\"mint-data-viewer__controls\">\n <SegmentedControl\n v-model=\"viewMode\"\n :options=\"viewOptions\"\n variant=\"card\"\n size=\"sm\"\n :full-width=\"false\"\n />\n </div>\n <div class=\"mint-data-viewer__actions\">\n <BaseButton\n v-if=\"pluginRoutePrefix && experimentId\"\n variant=\"secondary\"\n size=\"sm\"\n @click=\"emit('open-plugin')\"\n >\n Open in {{ pluginName || 'Plugin' }}\n </BaseButton>\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n @click=\"handleDownloadJson\"\n >\n JSON\n </BaseButton>\n <BaseButton\n variant=\"ghost\"\n size=\"sm\"\n @click=\"handleDownloadCsv\"\n >\n CSV\n </BaseButton>\n </div>\n </div>\n\n <div class=\"mint-data-viewer__content\">\n <div v-if=\"isLoading\" class=\"mint-data-viewer__loading\">\n <div class=\"mint-data-viewer__skeleton\">\n <Skeleton width=\"40%\" height=\"16px\" />\n <Skeleton width=\"100%\" height=\"12px\" />\n <Skeleton width=\"100%\" height=\"12px\" />\n <Skeleton width=\"75%\" height=\"12px\" />\n </div>\n </div>\n <template v-else>\n <!-- Summary View -->\n <div v-if=\"viewMode === 'summary' && mergedSummaryData\" class=\"mint-summary\">\n <!-- Metadata pills -->\n <div v-if=\"metadataEntries.length\" class=\"mint-summary__metadata\">\n <span\n v-for=\"entry in metadataEntries\"\n :key=\"entry.key\"\n class=\"mint-summary__pill\"\n >\n <span class=\"mint-summary__pill-key\">{{ entry.key }}</span>\n <span class=\"mint-summary__pill-value\">{{ entry.value }}</span>\n </span>\n </div>\n\n <!-- Sections -->\n <div\n v-for=\"section in mergedSummaryData.sections\"\n :key=\"section.key\"\n class=\"mint-summary__section\"\n >\n <!-- Group section: cards with embedded tables -->\n <template v-if=\"section.type === 'group' && section.items\">\n <div\n v-for=\"(item, idx) in section.items\"\n :key=\"idx\"\n class=\"mint-summary__group-card\"\n >\n <div class=\"mint-summary__group-header\">\n <span class=\"mint-summary__group-label\">{{ item.label }}</span>\n <span class=\"mint-summary__group-count\">\n {{ item.item_count }} {{ item.item_key }}\n </span>\n </div>\n <div v-if=\"item.metadata && Object.keys(item.metadata).length\" class=\"mint-summary__group-meta\">\n <span\n v-for=\"(val, key) in item.metadata\"\n :key=\"String(key)\"\n class=\"mint-summary__pill mint-summary__pill--sm\"\n >\n <span class=\"mint-summary__pill-key\">{{ String(key).replace(/_/g, ' ') }}</span>\n <span class=\"mint-summary__pill-value\">{{ val }}</span>\n </span>\n </div>\n <DataFrame\n v-if=\"item.rows.length\"\n :data=\"item.rows\"\n :columns=\"columnsForSection(item.columns)\"\n :searchable=\"item.rows.length > 10\"\n :sortable=\"true\"\n :striped=\"true\"\n size=\"sm\"\n />\n </div>\n </template>\n\n <!-- Table section: flat table -->\n <template v-else-if=\"section.type === 'table' && section.rows\">\n <div class=\"mint-summary__table-header\">\n <span class=\"mint-summary__section-label\">{{ section.label }}</span>\n <span class=\"mint-summary__section-count\">{{ section.row_count }} rows</span>\n </div>\n <DataFrame\n :data=\"section.rows\"\n :columns=\"columnsForSection(section.columns || [])\"\n :searchable=\"(section.rows?.length || 0) > 10\"\n :sortable=\"true\"\n :striped=\"true\"\n size=\"sm\"\n />\n </template>\n </div>\n </div>\n\n <!-- Tree View -->\n <SampleHierarchyTree\n v-else-if=\"viewMode === 'tree'\"\n :nodes=\"mergedTreeData\"\n :expand-all=\"false\"\n :show-icons=\"true\"\n :show-counts=\"true\"\n size=\"sm\"\n />\n\n <!-- Table View -->\n <DataFrame\n v-else-if=\"viewMode === 'table' && hasTableData\"\n :data=\"mergedTableData!\"\n :columns=\"tableColumns ?? []\"\n :searchable=\"true\"\n :sortable=\"true\"\n />\n <div v-else-if=\"viewMode === 'table'\" class=\"mint-data-viewer__empty\">\n No tabular data available. Use tree view.\n </div>\n </template>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/experiment-data-viewer.css';\n</style>\n","<script setup lang=\"ts\">\n/** Paired start/end time pickers that validate range order and display computed duration. */\nimport { computed } from 'vue'\nimport type { TimeRange } from '../types/components'\nimport TimePicker from './TimePicker.vue'\nimport { durationMinutes, formatDuration, compareTime } from '../composables/useTimeUtils'\n\ninterface Props {\n modelValue?: TimeRange\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n min?: string\n max?: string\n step?: number\n format?: '12h' | '24h'\n showDuration?: boolean\n blockedRanges?: TimeRange[]\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n error: false,\n size: 'md',\n step: 15,\n format: '24h',\n showDuration: true,\n blockedRanges: () => [],\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: TimeRange | undefined]\n}>()\n\nconst startValue = computed(() => props.modelValue?.start)\nconst endValue = computed(() => props.modelValue?.end)\n\nconst duration = computed(() => {\n if (!props.modelValue?.start || !props.modelValue?.end) return null\n return durationMinutes(props.modelValue.start, props.modelValue.end)\n})\n\nconst durationLabel = computed(() => {\n if (duration.value === null) return ''\n return formatDuration(duration.value)\n})\n\nconst isInvalid = computed(() => {\n if (!props.modelValue?.start || !props.modelValue?.end) return false\n return compareTime(props.modelValue.start, props.modelValue.end) >= 0\n})\n\nconst endMin = computed(() => {\n if (props.modelValue?.start) return props.modelValue.start\n return props.min\n})\n\nfunction onStartChange(value: string | undefined) {\n if (!value) {\n emit('update:modelValue', undefined)\n return\n }\n const end = props.modelValue?.end || ''\n emit('update:modelValue', { start: value, end })\n}\n\nfunction onEndChange(value: string | undefined) {\n if (!value) {\n emit('update:modelValue', undefined)\n return\n }\n const start = props.modelValue?.start || ''\n emit('update:modelValue', { start, end: value })\n}\n</script>\n\n<template>\n <div class=\"mint-time-range\">\n <div class=\"mint-time-range__start\">\n <TimePicker\n :model-value=\"startValue\"\n :disabled=\"disabled\"\n :error=\"error || isInvalid\"\n :size=\"size\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :format=\"format\"\n placeholder=\"Start\"\n @update:model-value=\"onStartChange\"\n />\n </div>\n\n <div class=\"mint-time-range__separator\">\n <span\n v-if=\"showDuration && duration !== null\"\n :class=\"['mint-time-range__duration', isInvalid ? 'mint-time-range__duration--error' : '']\"\n >\n {{ durationLabel }}\n </span>\n </div>\n\n <div class=\"mint-time-range__end\">\n <TimePicker\n :model-value=\"endValue\"\n :disabled=\"disabled\"\n :error=\"error || isInvalid\"\n :size=\"size\"\n :min=\"endMin\"\n :max=\"max\"\n :step=\"step\"\n :format=\"format\"\n placeholder=\"End\"\n @update:model-value=\"onEndChange\"\n />\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/time-range-input.css';\n</style>\n","<script setup lang=\"ts\">\n/** Paired start/end time pickers that validate range order and display computed duration. */\nimport { computed } from 'vue'\nimport type { TimeRange } from '../types/components'\nimport TimePicker from './TimePicker.vue'\nimport { durationMinutes, formatDuration, compareTime } from '../composables/useTimeUtils'\n\ninterface Props {\n modelValue?: TimeRange\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n min?: string\n max?: string\n step?: number\n format?: '12h' | '24h'\n showDuration?: boolean\n blockedRanges?: TimeRange[]\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n error: false,\n size: 'md',\n step: 15,\n format: '24h',\n showDuration: true,\n blockedRanges: () => [],\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: TimeRange | undefined]\n}>()\n\nconst startValue = computed(() => props.modelValue?.start)\nconst endValue = computed(() => props.modelValue?.end)\n\nconst duration = computed(() => {\n if (!props.modelValue?.start || !props.modelValue?.end) return null\n return durationMinutes(props.modelValue.start, props.modelValue.end)\n})\n\nconst durationLabel = computed(() => {\n if (duration.value === null) return ''\n return formatDuration(duration.value)\n})\n\nconst isInvalid = computed(() => {\n if (!props.modelValue?.start || !props.modelValue?.end) return false\n return compareTime(props.modelValue.start, props.modelValue.end) >= 0\n})\n\nconst endMin = computed(() => {\n if (props.modelValue?.start) return props.modelValue.start\n return props.min\n})\n\nfunction onStartChange(value: string | undefined) {\n if (!value) {\n emit('update:modelValue', undefined)\n return\n }\n const end = props.modelValue?.end || ''\n emit('update:modelValue', { start: value, end })\n}\n\nfunction onEndChange(value: string | undefined) {\n if (!value) {\n emit('update:modelValue', undefined)\n return\n }\n const start = props.modelValue?.start || ''\n emit('update:modelValue', { start, end: value })\n}\n</script>\n\n<template>\n <div class=\"mint-time-range\">\n <div class=\"mint-time-range__start\">\n <TimePicker\n :model-value=\"startValue\"\n :disabled=\"disabled\"\n :error=\"error || isInvalid\"\n :size=\"size\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :format=\"format\"\n placeholder=\"Start\"\n @update:model-value=\"onStartChange\"\n />\n </div>\n\n <div class=\"mint-time-range__separator\">\n <span\n v-if=\"showDuration && duration !== null\"\n :class=\"['mint-time-range__duration', isInvalid ? 'mint-time-range__duration--error' : '']\"\n >\n {{ durationLabel }}\n </span>\n </div>\n\n <div class=\"mint-time-range__end\">\n <TimePicker\n :model-value=\"endValue\"\n :disabled=\"disabled\"\n :error=\"error || isInvalid\"\n :size=\"size\"\n :min=\"endMin\"\n :max=\"max\"\n :step=\"step\"\n :format=\"format\"\n placeholder=\"End\"\n @update:model-value=\"onEndChange\"\n />\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/time-range-input.css';\n</style>\n","<script setup lang=\"ts\">\n/** Card displaying a lab resource with availability status, specs, tags, and a book action. */\nimport { computed } from 'vue'\nimport BasePill from './BasePill.vue'\nimport type { ResourceStatus, ResourceSpec } from '../types/components'\n\ninterface Props {\n name: string\n description?: string\n status?: ResourceStatus\n image?: string\n location?: string\n specs?: ResourceSpec[]\n tags?: string[]\n nextAvailable?: string\n showBookAction?: boolean\n compact?: boolean\n size?: 'sm' | 'md' | 'lg'\n statusLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n status: 'available',\n specs: () => [],\n tags: () => [],\n showBookAction: true,\n compact: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n book: []\n click: []\n}>()\n\nconst defaultStatusLabels: Record<ResourceStatus, string> = {\n 'available': 'Available',\n 'in-use': 'In Use',\n 'maintenance': 'Maintenance',\n 'offline': 'Offline',\n}\n\nconst displayStatusLabel = computed(() => props.statusLabel ?? defaultStatusLabels[props.status])\n\nconst cardClasses = computed(() => [\n 'mint-resource-card',\n `mint-resource-card--${props.size}`,\n { 'mint-resource-card--compact': props.compact },\n])\n\nfunction handleBook(event: MouseEvent) {\n event.stopPropagation()\n emit('book')\n}\n\nfunction handleClick() {\n emit('click')\n}\n</script>\n\n<template>\n <!-- Compact mode -->\n <div v-if=\"compact\" :class=\"cardClasses\" @click=\"handleClick\">\n <slot name=\"status\">\n <span :class=\"['mint-resource-card__status-dot', `mint-resource-card__status-dot--${status}`]\" />\n </slot>\n\n <span class=\"mint-resource-card__name\">{{ name }}</span>\n\n <span :class=\"['mint-resource-card__status-label', `mint-resource-card__status-label--${status}`]\">\n {{ displayStatusLabel }}\n </span>\n\n <slot name=\"action\">\n <button\n v-if=\"showBookAction\"\n type=\"button\"\n class=\"mint-resource-card__book-btn\"\n @click=\"handleBook\"\n >\n Book\n </button>\n </slot>\n </div>\n\n <!-- Card mode -->\n <div v-else :class=\"cardClasses\" @click=\"handleClick\">\n <div class=\"mint-resource-card__content\">\n <div class=\"mint-resource-card__header\">\n <!-- Image / fallback -->\n <div class=\"mint-resource-card__image\">\n <slot name=\"image\">\n <img v-if=\"image\" :src=\"image\" :alt=\"name\" />\n <div v-else class=\"mint-resource-card__image-fallback\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\" d=\"M9.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 014.5 0m0 0v5.714a2.25 2.25 0 00.659 1.591L19 14.5M14.25 3.104c.251.023.501.05.75.082M19 14.5l-2.47 2.47a2.25 2.25 0 00-.659 1.59V21a.75.75 0 01-.75.75h-6.24a.75.75 0 01-.75-.75v-2.44a2.25 2.25 0 00-.659-1.59L5 14.5m14 0h-2.25m-10.5 0H4\" />\n </svg>\n </div>\n </slot>\n </div>\n\n <!-- Body -->\n <div class=\"mint-resource-card__body\">\n <div class=\"mint-resource-card__title-row\">\n <h3 class=\"mint-resource-card__name\">{{ name }}</h3>\n\n <slot name=\"status\">\n <div class=\"mint-resource-card__status\">\n <span :class=\"['mint-resource-card__status-dot', `mint-resource-card__status-dot--${status}`]\" />\n <span class=\"mint-resource-card__status-label\">{{ displayStatusLabel }}</span>\n </div>\n </slot>\n </div>\n\n <p v-if=\"description\" class=\"mint-resource-card__description\">{{ description }}</p>\n\n <p v-if=\"location\" class=\"mint-resource-card__location\">Location: {{ location }}</p>\n\n <p v-if=\"nextAvailable\" class=\"mint-resource-card__next-available\">Next available: {{ nextAvailable }}</p>\n </div>\n </div>\n\n <!-- Specs -->\n <div v-if=\"specs.length > 0 || $slots.specs\" class=\"mint-resource-card__specs\">\n <slot name=\"specs\">\n <div v-for=\"spec in specs\" :key=\"spec.label\" class=\"mint-resource-card__spec\">\n <span class=\"mint-resource-card__spec-label\">{{ spec.label }}</span>\n <span class=\"mint-resource-card__spec-value\">{{ spec.value }}</span>\n </div>\n </slot>\n </div>\n\n <!-- Tags + action -->\n <div v-if=\"tags.length > 0 || showBookAction || $slots.action\" class=\"mint-resource-card__footer\">\n <div class=\"mint-resource-card__tags\">\n <BasePill v-for=\"tag in tags\" :key=\"tag\" size=\"sm\">{{ tag }}</BasePill>\n </div>\n\n <div class=\"mint-resource-card__action\">\n <slot name=\"action\">\n <button\n v-if=\"showBookAction\"\n type=\"button\"\n class=\"mint-resource-card__book-btn\"\n @click=\"handleBook\"\n >\n Book Now\n <svg width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </slot>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/resource-card.css';\n</style>\n","<script setup lang=\"ts\">\n/** Card displaying a lab resource with availability status, specs, tags, and a book action. */\nimport { computed } from 'vue'\nimport BasePill from './BasePill.vue'\nimport type { ResourceStatus, ResourceSpec } from '../types/components'\n\ninterface Props {\n name: string\n description?: string\n status?: ResourceStatus\n image?: string\n location?: string\n specs?: ResourceSpec[]\n tags?: string[]\n nextAvailable?: string\n showBookAction?: boolean\n compact?: boolean\n size?: 'sm' | 'md' | 'lg'\n statusLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n status: 'available',\n specs: () => [],\n tags: () => [],\n showBookAction: true,\n compact: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n book: []\n click: []\n}>()\n\nconst defaultStatusLabels: Record<ResourceStatus, string> = {\n 'available': 'Available',\n 'in-use': 'In Use',\n 'maintenance': 'Maintenance',\n 'offline': 'Offline',\n}\n\nconst displayStatusLabel = computed(() => props.statusLabel ?? defaultStatusLabels[props.status])\n\nconst cardClasses = computed(() => [\n 'mint-resource-card',\n `mint-resource-card--${props.size}`,\n { 'mint-resource-card--compact': props.compact },\n])\n\nfunction handleBook(event: MouseEvent) {\n event.stopPropagation()\n emit('book')\n}\n\nfunction handleClick() {\n emit('click')\n}\n</script>\n\n<template>\n <!-- Compact mode -->\n <div v-if=\"compact\" :class=\"cardClasses\" @click=\"handleClick\">\n <slot name=\"status\">\n <span :class=\"['mint-resource-card__status-dot', `mint-resource-card__status-dot--${status}`]\" />\n </slot>\n\n <span class=\"mint-resource-card__name\">{{ name }}</span>\n\n <span :class=\"['mint-resource-card__status-label', `mint-resource-card__status-label--${status}`]\">\n {{ displayStatusLabel }}\n </span>\n\n <slot name=\"action\">\n <button\n v-if=\"showBookAction\"\n type=\"button\"\n class=\"mint-resource-card__book-btn\"\n @click=\"handleBook\"\n >\n Book\n </button>\n </slot>\n </div>\n\n <!-- Card mode -->\n <div v-else :class=\"cardClasses\" @click=\"handleClick\">\n <div class=\"mint-resource-card__content\">\n <div class=\"mint-resource-card__header\">\n <!-- Image / fallback -->\n <div class=\"mint-resource-card__image\">\n <slot name=\"image\">\n <img v-if=\"image\" :src=\"image\" :alt=\"name\" />\n <div v-else class=\"mint-resource-card__image-fallback\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\" d=\"M9.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 014.5 0m0 0v5.714a2.25 2.25 0 00.659 1.591L19 14.5M14.25 3.104c.251.023.501.05.75.082M19 14.5l-2.47 2.47a2.25 2.25 0 00-.659 1.59V21a.75.75 0 01-.75.75h-6.24a.75.75 0 01-.75-.75v-2.44a2.25 2.25 0 00-.659-1.59L5 14.5m14 0h-2.25m-10.5 0H4\" />\n </svg>\n </div>\n </slot>\n </div>\n\n <!-- Body -->\n <div class=\"mint-resource-card__body\">\n <div class=\"mint-resource-card__title-row\">\n <h3 class=\"mint-resource-card__name\">{{ name }}</h3>\n\n <slot name=\"status\">\n <div class=\"mint-resource-card__status\">\n <span :class=\"['mint-resource-card__status-dot', `mint-resource-card__status-dot--${status}`]\" />\n <span class=\"mint-resource-card__status-label\">{{ displayStatusLabel }}</span>\n </div>\n </slot>\n </div>\n\n <p v-if=\"description\" class=\"mint-resource-card__description\">{{ description }}</p>\n\n <p v-if=\"location\" class=\"mint-resource-card__location\">Location: {{ location }}</p>\n\n <p v-if=\"nextAvailable\" class=\"mint-resource-card__next-available\">Next available: {{ nextAvailable }}</p>\n </div>\n </div>\n\n <!-- Specs -->\n <div v-if=\"specs.length > 0 || $slots.specs\" class=\"mint-resource-card__specs\">\n <slot name=\"specs\">\n <div v-for=\"spec in specs\" :key=\"spec.label\" class=\"mint-resource-card__spec\">\n <span class=\"mint-resource-card__spec-label\">{{ spec.label }}</span>\n <span class=\"mint-resource-card__spec-value\">{{ spec.value }}</span>\n </div>\n </slot>\n </div>\n\n <!-- Tags + action -->\n <div v-if=\"tags.length > 0 || showBookAction || $slots.action\" class=\"mint-resource-card__footer\">\n <div class=\"mint-resource-card__tags\">\n <BasePill v-for=\"tag in tags\" :key=\"tag\" size=\"sm\">{{ tag }}</BasePill>\n </div>\n\n <div class=\"mint-resource-card__action\">\n <slot name=\"action\">\n <button\n v-if=\"showBookAction\"\n type=\"button\"\n class=\"mint-resource-card__book-btn\"\n @click=\"handleBook\"\n >\n Book Now\n <svg width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </slot>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/resource-card.css';\n</style>\n","<script setup lang=\"ts\">\n/** Control panel for running a curve-fit job with progress bar, results table, and error display. */\nimport { computed } from 'vue'\nimport type { FitState, FitResultSummary } from '../types'\nimport BaseButton from './BaseButton.vue'\nimport ProgressBar from './ProgressBar.vue'\nimport AlertBox from './AlertBox.vue'\n\ninterface Props {\n state?: FitState\n progress?: number\n progressLabel?: string\n indeterminate?: boolean\n results?: FitResultSummary[]\n errorMessage?: string\n runLabel?: string\n cancelLabel?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n state: 'idle',\n progress: 0,\n progressLabel: 'Fitting...',\n indeterminate: false,\n results: () => [],\n runLabel: 'Run Fit',\n cancelLabel: 'Cancel',\n disabled: false,\n})\n\nconst emit = defineEmits<{\n run: []\n cancel: []\n}>()\n\nconst isRunning = computed(() => props.state === 'running')\nconst showRunButton = computed(() => !isRunning.value)\nconst showResults = computed(() => props.state === 'completed' && props.results.length > 0)\nconst showError = computed(() => props.state === 'error' && props.errorMessage)\n\nfunction variantClass(variant?: string): string {\n if (!variant || variant === 'default') return ''\n return `mint-fit-panel__result-value--${variant}`\n}\n</script>\n\n<template>\n <div class=\"mint-fit-panel\">\n <!-- Plugin controls slot -->\n <slot name=\"controls\" />\n\n <!-- Action bar -->\n <div class=\"mint-fit-panel__actions\">\n <BaseButton\n v-if=\"showRunButton\"\n variant=\"primary\"\n size=\"sm\"\n :disabled=\"disabled\"\n full-width\n @click=\"emit('run')\"\n >\n {{ runLabel }}\n </BaseButton>\n <BaseButton\n v-if=\"isRunning\"\n variant=\"ghost\"\n size=\"sm\"\n full-width\n @click=\"emit('cancel')\"\n >\n {{ cancelLabel }}\n </BaseButton>\n </div>\n\n <!-- Progress -->\n <div v-if=\"isRunning\" class=\"mint-fit-panel__progress\">\n <ProgressBar\n :value=\"progress\"\n :label=\"progressLabel\"\n :indeterminate=\"indeterminate\"\n :show-value=\"!indeterminate\"\n size=\"sm\"\n />\n </div>\n\n <!-- Results -->\n <div v-if=\"showResults\" class=\"mint-fit-panel__results\">\n <slot name=\"results\">\n <div class=\"mint-fit-panel__result-list\">\n <div\n v-for=\"(item, idx) in results\"\n :key=\"idx\"\n class=\"mint-fit-panel__result-row\"\n >\n <span class=\"mint-fit-panel__result-label\">{{ item.label }}</span>\n <span\n class=\"mint-fit-panel__result-value\"\n :class=\"variantClass(item.variant)\"\n >\n {{ item.value }}\n </span>\n </div>\n </div>\n </slot>\n </div>\n\n <!-- Error -->\n <AlertBox v-if=\"showError\" type=\"error\">\n {{ errorMessage }}\n </AlertBox>\n\n <!-- Footer slot -->\n <slot name=\"footer\" />\n </div>\n</template>\n\n<style>\n@import '../styles/components/fit-panel.css';\n</style>\n","<script setup lang=\"ts\">\n/** Control panel for running a curve-fit job with progress bar, results table, and error display. */\nimport { computed } from 'vue'\nimport type { FitState, FitResultSummary } from '../types'\nimport BaseButton from './BaseButton.vue'\nimport ProgressBar from './ProgressBar.vue'\nimport AlertBox from './AlertBox.vue'\n\ninterface Props {\n state?: FitState\n progress?: number\n progressLabel?: string\n indeterminate?: boolean\n results?: FitResultSummary[]\n errorMessage?: string\n runLabel?: string\n cancelLabel?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n state: 'idle',\n progress: 0,\n progressLabel: 'Fitting...',\n indeterminate: false,\n results: () => [],\n runLabel: 'Run Fit',\n cancelLabel: 'Cancel',\n disabled: false,\n})\n\nconst emit = defineEmits<{\n run: []\n cancel: []\n}>()\n\nconst isRunning = computed(() => props.state === 'running')\nconst showRunButton = computed(() => !isRunning.value)\nconst showResults = computed(() => props.state === 'completed' && props.results.length > 0)\nconst showError = computed(() => props.state === 'error' && props.errorMessage)\n\nfunction variantClass(variant?: string): string {\n if (!variant || variant === 'default') return ''\n return `mint-fit-panel__result-value--${variant}`\n}\n</script>\n\n<template>\n <div class=\"mint-fit-panel\">\n <!-- Plugin controls slot -->\n <slot name=\"controls\" />\n\n <!-- Action bar -->\n <div class=\"mint-fit-panel__actions\">\n <BaseButton\n v-if=\"showRunButton\"\n variant=\"primary\"\n size=\"sm\"\n :disabled=\"disabled\"\n full-width\n @click=\"emit('run')\"\n >\n {{ runLabel }}\n </BaseButton>\n <BaseButton\n v-if=\"isRunning\"\n variant=\"ghost\"\n size=\"sm\"\n full-width\n @click=\"emit('cancel')\"\n >\n {{ cancelLabel }}\n </BaseButton>\n </div>\n\n <!-- Progress -->\n <div v-if=\"isRunning\" class=\"mint-fit-panel__progress\">\n <ProgressBar\n :value=\"progress\"\n :label=\"progressLabel\"\n :indeterminate=\"indeterminate\"\n :show-value=\"!indeterminate\"\n size=\"sm\"\n />\n </div>\n\n <!-- Results -->\n <div v-if=\"showResults\" class=\"mint-fit-panel__results\">\n <slot name=\"results\">\n <div class=\"mint-fit-panel__result-list\">\n <div\n v-for=\"(item, idx) in results\"\n :key=\"idx\"\n class=\"mint-fit-panel__result-row\"\n >\n <span class=\"mint-fit-panel__result-label\">{{ item.label }}</span>\n <span\n class=\"mint-fit-panel__result-value\"\n :class=\"variantClass(item.variant)\"\n >\n {{ item.value }}\n </span>\n </div>\n </div>\n </slot>\n </div>\n\n <!-- Error -->\n <AlertBox v-if=\"showError\" type=\"error\">\n {{ errorMessage }}\n </AlertBox>\n\n <!-- Footer slot -->\n <slot name=\"footer\" />\n </div>\n</template>\n\n<style>\n@import '../styles/components/fit-panel.css';\n</style>\n","// Base components\nexport { default as BaseButton } from './BaseButton.vue'\nexport { default as BaseInput } from './BaseInput.vue'\nexport { default as BaseTextarea } from './BaseTextarea.vue'\nexport { default as BaseSelect } from './BaseSelect.vue'\nexport { default as BaseCheckbox } from './BaseCheckbox.vue'\nexport { default as BaseToggle } from './BaseToggle.vue'\nexport { default as BaseRadioGroup } from './BaseRadioGroup.vue'\nexport { default as BaseSlider } from './BaseSlider.vue'\nexport { default as ColorSlider } from './ColorSlider.vue'\nexport { default as BaseTabs } from './BaseTabs.vue'\nexport { default as BaseModal } from './BaseModal.vue'\nexport { default as SegmentedControl } from './SegmentedControl.vue'\nexport { default as MultiSelect } from './MultiSelect.vue'\nexport { default as BasePill } from './BasePill.vue'\nexport { default as DropdownButton } from './DropdownButton.vue'\nexport { default as Calendar } from './Calendar.vue'\nexport { default as DataFrame } from './DataFrame.vue'\n\n// Form components\nexport { default as FormField } from './FormField.vue'\nexport { default as DatePicker } from './DatePicker.vue'\nexport { default as TimePicker } from './TimePicker.vue'\nexport { default as TagsInput } from './TagsInput.vue'\nexport { default as NumberInput } from './NumberInput.vue'\nexport { default as FileUploader } from './FileUploader.vue'\n\n// Feedback components\nexport { default as AlertBox } from './AlertBox.vue'\nexport { default as AppToastContainer } from './AppToastContainer.vue'\n\n// Action components\nexport { default as IconButton } from './IconButton.vue'\nexport { default as ThemeToggle } from './ThemeToggle.vue'\n\n// Layout components\nexport { default as CollapsibleCard } from './CollapsibleCard.vue'\nexport { default as AppTopBar } from './AppTopBar.vue'\nexport { default as AppAvatarMenu } from './AppAvatarMenu.vue'\nexport { default as AppPluginSwitcher } from './AppPluginSwitcher.vue'\nexport { default as AppSidebar } from './AppSidebar.vue'\nexport { default as AppLayout } from './AppLayout.vue'\nexport { default as PluginWorkspaceView } from './PluginWorkspaceView.vue'\nexport { default as ComponentBindingRenderer } from './ComponentBindingRenderer.vue'\nexport { default as ControlWorkspaceView } from './ControlWorkspaceView.vue'\nexport { default as DoseDesignWorkspaceView } from './DoseDesignWorkspaceView.vue'\nexport { default as AppContainer } from './AppContainer.vue'\nexport { default as PluginIcon } from './PluginIcon.vue'\n\n// Utility components\nexport { default as Skeleton } from './Skeleton.vue'\nexport { default as LoadingSpinner } from './LoadingSpinner.vue'\nexport { default as Divider } from './Divider.vue'\nexport { default as StatusIndicator } from './StatusIndicator.vue'\nexport { default as ProgressBar } from './ProgressBar.vue'\nexport { default as Avatar } from './Avatar.vue'\nexport { default as EmptyState } from './EmptyState.vue'\nexport { default as Breadcrumb } from './Breadcrumb.vue'\nexport { default as Tooltip } from './Tooltip.vue'\nexport { default as ConfirmDialog } from './ConfirmDialog.vue'\nexport { default as ChartContainer } from './ChartContainer.vue'\nexport { default as SettingsModal } from './SettingsModal.vue'\n\n// Biological experiment components\nexport { default as BioTemplateRenderer } from './BioTemplateRenderer.vue'\nexport { default as BioTemplateExperimentWorkspaceView } from './BioTemplateExperimentWorkspaceView.vue'\nexport { default as BioTemplatePackWorkspaceView } from './BioTemplatePackWorkspaceView.vue'\nexport { default as BioTemplatePresetWorkspaceView } from './BioTemplatePresetWorkspaceView.vue'\nexport { default as WellPlate } from './WellPlate.vue'\nexport { default as RackEditor } from './RackEditor.vue'\nexport { default as SampleLegend } from './SampleLegend.vue'\nexport { default as PlateMapEditor } from './PlateMapEditor.vue'\nexport { default as ExperimentTimeline } from './ExperimentTimeline.vue'\n\n// Sample management components\nexport { default as SampleSelector } from './SampleSelector.vue'\nexport { default as AutoGroupModal } from './AutoGroupModal.vue'\nexport { default as GroupAssigner } from './GroupAssigner.vue'\n\n// Lab/Experiment components\nexport { default as MoleculeInput } from './MoleculeInput.vue'\nexport { default as ConcentrationInput } from './ConcentrationInput.vue'\nexport { default as DoseCalculator } from './DoseCalculator.vue'\nexport { default as ReagentEditor, type ReagentDefinition } from './ReagentEditor.vue'\nexport { default as ReagentList } from './ReagentList.vue'\nexport { default as SampleHierarchyTree } from './SampleHierarchyTree.vue'\nexport { default as ProtocolStepEditor } from './ProtocolStepEditor.vue'\n\n// Scientific display components\nexport { default as ScientificNumber } from './ScientificNumber.vue'\nexport { default as ChemicalFormula } from './ChemicalFormula.vue'\n\n// Scientific input components\nexport { default as FormulaInput } from './FormulaInput.vue'\nexport { default as SequenceInput } from './SequenceInput.vue'\nexport { default as UnitInput } from './UnitInput.vue'\n\n// Workflow components\nexport { default as StepWizard } from './StepWizard.vue'\nexport { default as AuditTrail } from './AuditTrail.vue'\nexport { default as BatchProgressList } from './BatchProgressList.vue'\n\n// Form builder components\nexport { default as FormBuilder } from './FormBuilder.vue'\nexport { default as FormActions } from './FormActions.vue'\n\n// Experiment data display components\nexport { default as ExperimentDataViewer } from './ExperimentDataViewer.vue'\nexport { default as ExperimentCodeBadge } from './ExperimentCodeBadge.vue'\n\n// Scheduling / booking components\nexport { default as DateTimePicker } from './DateTimePicker.vue'\nexport { default as TimeRangeInput } from './TimeRangeInput.vue'\nexport { default as ScheduleCalendar } from './ScheduleCalendar.vue'\nexport { default as ResourceCard } from './ResourceCard.vue'\n\n// Experiment / analysis components\nexport { default as ExperimentSelectorModal } from './ExperimentSelectorModal.vue'\nexport { default as ExperimentPopover } from './ExperimentPopover.vue'\nexport { default as FitPanel } from './FitPanel.vue'\n"],"x_google_ignoreList":[117,118,119,120],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECaA,MAAM,QAAQ;EASd,MAAM,OAAO;EAIb,SAAS,YAAY,OAAmB;AACtC,OAAI,CAAC,MAAM,YAAY,CAAC,MAAM,QAC5B,MAAK,SAAS,MAAK;;;uBAMrB,mBAiCS,UAAA;IAhCN,MAAM,QAAA;IACN,UAAU,QAAA,YAAY,QAAA;IACtB,OAAK,eAAA;;qBAA+C,QAAA;qBAAiC,QAAA;KAAc,QAAA,YAAS,4BAAA;KAA0C,QAAA,YAAY,QAAA,UAAO,0BAAA;;IAOzK,SAAO;OAGA,QAAA,WAAA,WAAA,EADR,mBAmBM,OAnBN,eAmBM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAbJ,mBAOE,UAAA;IANA,OAAA,EAAA,WAAA,QAAqB;IACrB,IAAG;IACH,IAAG;IACH,GAAE;IACF,QAAO;IACP,gBAAa;iBAEf,mBAIE,QAAA;IAHA,OAAA,EAAA,WAAA,QAAqB;IACrB,MAAK;IACL,GAAE;qDAGN,WAAQ,KAAA,QAAA,UAAA,CAAA,EAAA,IAAA,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EExCZ,MAAM,QAAQ;EAUd,MAAM,OAAO;EAKb,MAAM,oBAAiC;GACrC;IAAE,OAAO;IAAG,OAAO;IAAW;GAC9B;IAAE,OAAO;IAAI,OAAO;IAAW;GAC/B;IAAE,OAAO;IAAK,OAAO;IAAW;GAClC;EAGA,MAAM,oBAAsC,CAAC,IAAI,GAAE;EAEnD,MAAM,aAAa,eAAe,MAAM,cAAc,kBAAiB;EACvE,MAAM,aAAa,eAAe,MAAM,cAAc,kBAAiB;EAEvE,MAAM,aAAa,eAAe;AAChC,WAAQ,MAAM,MAAd;IACE,KAAK,KAAM,QAAO;KAAE,OAAO;KAAI,WAAW;KAAQ,UAAU;KAAQ,eAAe;KAAW;IAC9F,KAAK,KAAM,QAAO;KAAE,OAAO;KAAI,WAAW;KAAQ,UAAU;KAAQ,eAAe;KAAU;IAC7F,QAAS,QAAO;KAAE,OAAO;KAAI,WAAW;KAAQ,UAAU;KAAQ,eAAe;KAAW;;IAE/F;EAED,MAAM,eAAe,eAAe,MAAM,cAAc,MAAM,IAAG;EAEjE,MAAM,aAAa,eAAe;GAChC,MAAM,QAAQ,MAAM,MAAM,MAAM;AAChC,OAAI,UAAU,EAAG,QAAO;AACxB,WAAS,aAAa,QAAQ,MAAM,OAAO,QAAS;IACrD;EAGD,MAAM,gBAAgB,eAAe;AAInC,UAAO,6BAHO,WAAW,MACtB,KAAI,SAAQ,GAAG,KAAK,MAAM,GAAG,KAAK,MAAM,GAAE,CAC1C,KAAK,KAAI,CAC8B;IAC3C;EAGD,MAAM,kBAAkB,eAAe;GACrC,MAAM,MAAM,WAAW;GACvB,MAAM,CAAC,IAAI,MAAM,WAAW;AAE5B,OAAI,OAAO,GACT,QAAO;IACL,iBAAiB;IACjB,OAAO;IACP,QAAQ;IACV;AAEF,OAAI,OAAO,GACT,QAAO;IACL,iBAAiB;IACjB,OAAO;IACP,QAAQ;IACV;AAEF,UAAO;IACL,iBAAiB;IACjB,OAAO;IACP,QAAQ;IACV;IACD;EAED,SAAS,YAAY,OAAc;GACjC,MAAM,SAAS,MAAM;AACrB,QAAK,qBAAqB,OAAO,OAAO,MAAM,CAAA;;;uBAK9C,mBAgDM,OAAA,EAhDA,OAAK,eAAA,CAAA,qBAAA,EAAA,+BAAyD,QAAA,UAAQ,CAAA,CAAA,EAAA,EAAA,CAC1E,mBAoCM,OApCN,eAoCM,CAlCJ,mBAmBM,OAnBN,eAmBM,CAlBJ,mBAiBE,SAAA;IAhBA,MAAK;IACJ,OAAO,aAAA;IACP,KAAK,QAAA;IACL,KAAK,QAAA;IACL,MAAM,QAAA;IACN,UAAU,QAAA;IACV,cAAU,UAAY,aAAA;IACtB,iBAAe,QAAA;IACf,iBAAe,QAAA;IACf,iBAAe,aAAA;IAChB,OAAM;IACL,OAAK,eAAA;0BAAqC,cAAA;wBAA8C,WAAA,MAAW,MAAK;;IAIxG,SAAO;kCAMJ,QAAA,aAAA,WAAA,EADR,mBAWM,OAAA;;IATJ,OAAM;IACL,OAAK,eAAA;QAAiB,gBAAA;eAAqC,WAAA,MAAW;aAA6B,WAAA,MAAW;eAA+B,WAAA,MAAW;;sBAOtJ,aAAA,MAAY,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,EAMX,QAAA,cAAA,WAAA,EADR,mBAOM,OAAA;;IALJ,OAAM;IACL,OAAK,eAAA,EAAA,UAAc,WAAA,MAAW,eAAa,CAAA;OAE5C,mBAAkC,QAAA,MAAA,gBAAzB,QAAA,YAAY,QAAA,IAAG,EAAA,EAAA,EACxB,mBAAkC,QAAA,MAAA,gBAAzB,QAAA,YAAY,QAAA,IAAG,EAAA,EAAA,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,EAAA;;;;;;ACpJ9B,SAAgB,mBACd,MACO;AACP,KAAI,OAAO,SAAS,SAAU,QAAO;AAErC,QAAO;EACL,IAAI;EACJ,OAAO;EACR;;AAGH,SAAgB,wBACd,MACO;AACP,KAAI,OAAO,SAAS,SAAU,QAAO;AAErC,QAAO,EACL,OAAO,MACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEdH,MAAM,QAAQ;EAId,MAAM,OAAO;EAIb,MAAM,YAAY,eAAe,MAAM,WAAU;EACjD,MAAM,iBAAiB,eAA0B,MAAM,KAAK,IAAI,mBAAmB,CAAA;EAEnF,SAAS,UAAU,OAAe;GAChC,MAAM,MAAM,eAAe,MAAM,MAAK,MAAK,EAAE,OAAO,MAAK;AACzD,OAAI,OAAO,CAAC,IAAI,SACd,MAAK,qBAAqB,MAAK;;;uBAMjC,mBAuBM,OAAA;IAvBA,OAAK,eAAA,CAAA,aAAA,cAA8B,QAAA,UAAO,CAAA;IAAK,MAAK;yBACxD,mBAqBS,UAAA,MAAA,WApBO,eAAA,QAAP,QAAG;wBADZ,mBAqBS,UAAA;KAnBN,KAAK,IAAI;KACV,MAAK;KACL,MAAK;KACJ,iBAAe,UAAA,UAAc,IAAI;KACjC,iBAAe,IAAI;KACnB,OAAK,eAAA;;MAAgC,UAAA,UAAc,IAAI,KAAE,qBAAA;MAAoC,IAAI,WAAQ,uBAAA;;KAKzG,UAAK,WAAE,UAAU,IAAI,GAAE;QAExB,mBAMO,QANP,eAMO;KALO,IAAI,QAAA,WAAA,EAAhB,mBAAkE,QAAlE,eAAkE,gBAAlB,IAAI,KAAI,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;qBAAU,MAClE,gBAAG,IAAI,MAAK,GAAG,KACf,EAAA;KAAY,IAAI,UAAU,KAAA,KAAA,WAAA,EAA1B,mBAEO,QAFP,eAEO,gBADF,IAAI,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EElCtB,MAAM,QAAQ;EAQd,MAAM,OAAO;EAKb,MAAM,eAAe,IAAwB,KAAI;EACjD,IAAI,oBAAwC;EAE5C,MAAM,qBAAqB;GACzB;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAI;EAEX,SAAS,uBAAsC;AAC7C,OAAI,CAAC,aAAa,MAAO,QAAO,EAAC;AACjC,UAAO,MAAM,KAAK,aAAa,MAAM,iBAA8B,mBAAmB,CAAA;;EAGxF,SAAS,cAAc,OAAsB;AAC3C,OAAI,MAAM,QAAQ,YAAY,MAAM,iBAAiB,MAAM,YAAY;AACrE,WAAM;AACN;;AAGF,OAAI,MAAM,QAAQ,SAAS,CAAC,aAAa,MAAO;GAEhD,MAAM,YAAY,sBAAqB;AACvC,OAAI,UAAU,WAAW,GAAG;AAC1B,UAAM,gBAAe;AACrB;;GAGF,MAAM,QAAQ,UAAU;GACxB,MAAM,OAAO,UAAU,UAAU,SAAS;AAE1C,OAAI,MAAM,YAAY,SAAS,kBAAkB,OAAO;AACtD,UAAM,gBAAe;AACrB,SAAK,OAAM;cACF,CAAC,MAAM,YAAY,SAAS,kBAAkB,MAAM;AAC7D,UAAM,gBAAe;AACrB,UAAM,OAAM;;;EAIhB,SAAS,QAAQ;AACf,OAAI,MAAM,UAAU;AAClB,SAAK,qBAAqB,MAAK;AAC/B,SAAK,QAAO;;;EAIhB,SAAS,mBAAmB,OAAmB;AAC7C,OAAI,MAAM,kBAAkB,MAAM,WAAW,MAAM,cACjD,QAAM;;AAIV,cAAY,MAAM,YAAY,OAAO,WAAW;AAC9C,OAAI,QAAQ;AACV,wBAAoB,SAAS;AAC7B,aAAS,KAAK,MAAM,WAAW;AAC/B,UAAM,UAAS;IACf,MAAM,YAAY,sBAAqB;AACvC,QAAI,UAAU,SAAS,EACrB,WAAU,GAAG,OAAM;QAEnB,cAAa,OAAO,OAAM;UAEvB;AACL,aAAS,KAAK,MAAM,WAAW;AAC/B,uBAAmB,OAAM;AACzB,wBAAoB;;IAEvB;AAED,yBAAuB,UAAU,WAAW,cAAa;AAEzD,oBAAkB;AAChB,YAAS,KAAK,MAAM,WAAW;IAChC;;uBAIC,YA8DW,UAAA,EA9DD,IAAG,QAAM,EAAA,CACjB,YA4Da,YAAA,EA5DA,MAAI,SAAW,QAAA,WAAA,EAAA;2BA2DpB,CAzDE,QAAA,cAAA,WAAA,EADR,mBA0DM,OAAA;;KAxDH,OAAK,eAAA,CAAA,cAAA,eAAgC,QAAA,UAAO,CAAA;KAC5C,SAAO;kCAGR,mBAAmC,OAAA,EAA9B,OAAM,uBAAqB,EAAA,MAAA,GAAA,GAGhC,mBAgDM,OAAA;cA/CA;KAAJ,KAAI;KACH,OAAK,eAAA;;gCAA+E,QAAA;gCAA8C,QAAA;;KAKnI,MAAK;KACL,cAAW;KACX,UAAS;;KAGE,QAAA,YAAO,WAAA,WAAA,EAAlB,mBAAmF,OAAnF,cAAmF,IAAA,mBAAA,IAAA,KAAA;KAGxE,QAAA,SAAS,QAAA,YAAY,QAAA,YAAYA,KAAAA,OAAO,UAAA,WAAA,EAAnD,mBAsBM,OAtBN,eAsBM,CArBO,QAAA,SAAS,QAAA,YAAYA,KAAAA,OAAO,UAAA,WAAA,EAAvC,mBASM,OATN,eASM,CARJ,WAOO,KAAA,QAAA,UAAA,EAAA,QAAA,CANK,QAAA,SAAA,WAAA,EAAV,mBAEK,MAFL,eAEK,gBADA,QAAA,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,EAED,QAAA,YAAA,WAAA,EAAT,mBAEI,KAFJ,eAEI,gBADC,QAAA,SAAQ,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,EAKT,QAAA,YAAA,WAAA,EADR,mBAUS,UAAA;;MARP,MAAK;MACL,OAAM;MACN,cAAW;MACV,SAAO;uCAER,mBAEM,OAAA;MAFD,OAAM;MAAyB,MAAK;MAAO,QAAO;MAAe,gBAAa;MAAI,kBAAe;MAAQ,mBAAgB;MAAQ,SAAQ;SAC5I,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,EAAG,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;KAMjD,mBAEM,OAFN,eAEM,CADJ,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA;KAICA,KAAAA,OAAO,UAAA,WAAA,EAAlB,mBAEM,OAFN,eAEM,CADJ,WAAsB,KAAA,QAAA,SAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1JlC,MAAM,QAAQ;EAOd,MAAM,OAAO;EAIb,MAAM,oBAAoB,eAAkC,MAAM,QAAQ,IAAI,qBAAqB,CAAA;EAEnG,SAAS,aAAa,QAAyB;AAC7C,OAAI,MAAM,YAAY,OAAO,SAAU;AACvC,QAAK,qBAAqB,OAAO,MAAK;;EAGxC,SAAS,cAAc,OAAsB,QAAyB;AACpE,OAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC9C,UAAM,gBAAe;AACrB,iBAAa,OAAM;;;;uBAMrB,mBAmCM,OAAA;IAlCH,OAAK,eAAA;;gCAAqE,QAAA;gCAA4C,QAAA;KAAc,QAAA,YAAS,uCAAA;KAAoD,QAAA,WAAQ,qCAAA;;IAO1M,MAAK;yBAEL,mBAwBS,UAAA,MAAA,WAvBU,kBAAA,QAAV,WAAM;wBADf,mBAwBS,UAAA;KAtBN,KAAK,OAAO,OAAO,MAAK;KACzB,MAAK;KACL,MAAK;KACJ,gBAAc,QAAA,eAAe,OAAO;KACpC,UAAU,QAAA,YAAY,OAAO;KAC7B,OAAK,eAAA;;yCAAyF,QAAA;yCAAsD,QAAA;MAAgB,QAAA,eAAe,OAAO,QAAK,2CAAA;MAA0D,OAAO,WAAQ,6CAAA;;KAOxQ,UAAK,WAAE,aAAa,OAAM;KAC1B,YAAO,WAAE,cAAc,QAAQ,OAAM;QAEtC,mBAAqE,QAArE,eAAqE,gBAAtB,OAAO,MAAK,EAAA,EAAA,EAEnD,OAAO,eAAe,QAAA,YAAO,UAAA,WAAA,EADrC,mBAKO,QALP,eAKO,gBADF,OAAO,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,IAAA,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1C7B,MAAM,QAAQ;;;;EAYd,MAAM,OAAO;EAIb,SAAS,aAAa,OAAmB;AACvC,SAAM,iBAAgB;AACtB,OAAI,CAAC,MAAM,SACT,MAAK,SAAQ;;;uBAMf,mBA0BO,QAAA,EAzBJ,OAAK,eAAA;;kBAA2C,QAAA;IAAiB,QAAA,SAAK,cAAkB,QAAA;kBAA6B,QAAA;;4BAAuC,QAAA;KAAQ,wBAA0B,QAAA;KAAI;;IAQvL,QAAA,QAAA,WAAA,EAAZ,mBAEO,QAFP,eAEO,CADL,WAAoB,KAAA,QAAA,OAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAEtB,mBAEO,QAFP,eAEO,CADL,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA;IAGF,QAAA,aAAS,CAAK,QAAA,YAAA,WAAA,EADtB,mBAUS,UAAA;;KARP,MAAK;KACL,OAAM;KACN,cAAW;KACV,SAAO;sCAER,mBAEM,OAAA;KAFD,OAAM;KAAyB,MAAK;KAAO,QAAO;KAAe,gBAAa;KAAI,kBAAe;KAAQ,mBAAgB;KAAQ,SAAQ;QAC5I,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,EAAG,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEzCnD,MAAM,QAAQ;;;;;EAWd,MAAM,OAAO;EAKb,MAAM,EAAE,QAAQ,SAAS,OAAO,QAAQ,mBAAmB,kBAAiB;EAC5E,MAAM,oBAAoB,eACxB,MAAM,QAAQ,IAAI,qBAAoB,CACxC;EAEA,MAAM,gBAAgB,eAAe;AACnC,OAAI,MAAM,eAAe,KAAA,EAAW,QAAO,MAAM,eAAe;AAEhE,UADc,kBAAkB,MAAM,MAAK,MAAK,EAAE,UAAU,MAAM,WAAU,EAC9D,SAAS,OAAO,MAAM,WAAU;IAC/C;EAED,SAAS,SAAS;AAChB,OAAI,MAAM,YAAY,MAAM,QAAS;AACrC,mBAAe;;EAGjB,SAAS,aAAa,QAAuC;AAC3D,OAAI,OAAO,SAAU;AACrB,QAAK,qBAAqB,OAAO,MAAK;AACtC,QAAK,UAAU,OAAM;AACrB,UAAM;;;uBAKN,mBA6EM,OAAA;aA7EG;IAAJ,KAAI;IAAU,OAAM;OACvB,mBAgCS,UAAA;IA/BP,MAAK;IACJ,UAAU,QAAA,YAAY,QAAA;IACtB,OAAK,eAAA;;uCAAuF,QAAA;uCAAqD,QAAA;;iDAAuE,QAAA,YAAY,QAAA;6CAA0D,MAAA,OAAM;;;IASpS,iBAAe,MAAA,OAAM;IACtB,iBAAc;IACb,SAAO;;IAEG,QAAA,WAAA,WAAA,EAAX,mBAGM,OAHN,eAGM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAFJ,mBAAuF,UAAA;KAA/E,IAAG;KAAK,IAAG;KAAK,GAAE;KAAK,QAAO;KAAe,gBAAa;KAAI,SAAQ;kBAC9E,mBAAkG,QAAA;KAA5F,GAAE;KAA0B,QAAO;KAAe,gBAAa;KAAI,kBAAe;;IAE1F,mBAAoE,QAApE,eAAoE,gBAAvB,cAAA,MAAa,EAAA,EAAA;kBAC1D,mBAUM,OAAA;KATH,OAAK,eAAA,CAAA,iCAAA,EAAA,uCAA6E,MAAA,OAAM,EAAA,CAAA,CAAA;KACzF,SAAQ;KACR,MAAK;KACL,QAAO;KACP,gBAAa;KACb,kBAAe;KACf,mBAAgB;sCAEhB,mBAAyB,QAAA,EAAnB,GAAE,gBAAc,EAAA,MAAA,GAAA,CAAA,EAAA,EAAA,EAAA;0BAI1B,YAyCa,YAAA,EAzCD,MAAK,6BAA2B,EAAA;2BAwCrC,CAtCG,MAAA,OAAM,IAAA,WAAA,EADd,mBAuCK,MAAA;;KArCH,OAAM;KACN,MAAK;KACJ,yBAAuB,QAAA,eAAe,KAAA,IAAS,wBAA2B,QAAA,eAAe,KAAA;0BAE1F,mBAgCK,UAAA,MAAA,WA/Bc,kBAAA,QAAV,WAAM;yBADf,mBAgCK,MAAA;MA9BF,IAAE,wBAA0B,OAAO;MACnC,KAAK,OAAO,OAAO,MAAK;MACzB,MAAK;MACJ,iBAAe,OAAO,UAAU,QAAA;MAChC,iBAAe,OAAO;MACtB,OAAK,eAAA,CAAA,gCAAA;iDAAsH,OAAO,UAAU,QAAA;iDAAoE,OAAO;;MAOvN,UAAK,WAAE,aAAa,OAAM;;MAE3B,mBAA0E,QAA1E,eAA0E,gBAAtB,OAAO,MAAK,EAAA,EAAA;MACpD,OAAO,eAAA,WAAA,EAAnB,mBAEO,QAFP,eAEO,gBADF,OAAO,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;MAGf,OAAO,UAAU,QAAA,cAAA,WAAA,EADzB,mBAWM,OAXN,eAWM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAA4B,QAAA,EAAtB,GAAE,mBAAiB,EAAA,MAAA,GAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EErGrC,MAAM,QAAQ;;;;;;;;EAkBd,MAAM,OAAO;EAQb,MAAM,wBAAQ,IAAI,MAAK;AACvB,QAAM,SAAS,GAAG,GAAG,GAAG,EAAC;EAEzB,MAAM,eAAe,IAAI,MAAM,SAAS,MAAM,UAAU,CAAA;EACxD,MAAM,cAAc,IAAI,MAAM,QAAQ,MAAM,aAAa,CAAA;EAGzD,MAAM,cAAc,IAAiB,KAAI;AAEzC,cAAY,MAAM,QAAQ,QAAQ;AAAE,OAAI,QAAQ,KAAA,EAAW,cAAa,QAAQ;IAAK;AACrF,cAAY,MAAM,OAAO,QAAQ;AAAE,OAAI,QAAQ,KAAA,EAAW,aAAY,QAAQ;IAAK;EAEnF,MAAM,aAAa,eAAe;AAEhC,UADa,IAAI,KAAK,YAAY,OAAO,aAAa,OAAO,EAAC,CAClD,mBAAmB,MAAM,QAAQ;IAAE,OAAO;IAAQ,MAAM;IAAW,CAAA;IAChF;EAED,MAAM,gBAAgB,eAAe;GACnC,MAAM,SAAmB,EAAC;GAE1B,MAAM,OAAO,IAAI,KAAK,MAAM,GAAG,IAAI,MAAM,aAAY;AACrD,QAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;IAC1B,MAAM,IAAI,IAAI,KAAK,KAAI;AACvB,MAAE,QAAQ,EAAE,SAAS,GAAG,EAAC;AACzB,WAAO,KAAK,EAAE,mBAAmB,MAAM,QAAQ,EAAE,SAAS,SAAS,CAAC,CAAA;;AAEtE,UAAO;IACR;EAED,SAAS,UAAU,GAAiB;AAClC,UAAO,GAAG,EAAE,aAAa,CAAC,GAAG,OAAO,EAAE,UAAU,GAAG,EAAE,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,OAAO,EAAE,SAAS,CAAC,CAAC,SAAS,GAAG,IAAI;;EAGhH,SAAS,UAAU,KAA0B;AAC3C,OAAI,eAAe,KAAM,QAAO;AAEhC,0BADU,IAAI,KAAK,MAAM,YAAW;;EAItC,SAAS,UAAU,GAAS,GAAkB;AAC5C,UAAO,EAAE,aAAa,KAAK,EAAE,aAAa,IACxC,EAAE,UAAU,KAAK,EAAE,UAAU,IAC7B,EAAE,SAAS,KAAK,EAAE,SAAQ;;EAG9B,MAAM,kBAAkB,eAAe;GACrC,MAAM,sBAAM,IAAI,KAAY;AAC5B,QAAK,MAAM,KAAK,MAAM,eAAe;IACnC,MAAM,SAAS,UAAU,EAAC;AAC1B,QAAI,IAAI,UAAU,OAAO,CAAA;;AAE3B,UAAO;IACR;EAED,MAAM,YAAY,eAAe;GAC/B,MAAM,sBAAM,IAAI,KAA8B;AAC9C,QAAK,MAAM,UAAU,MAAM,SAAS;IAElC,MAAM,MAAM,UADC,UAAU,OAAO,KAAI,CACR;IAC1B,MAAM,UAAU,IAAI,IAAI,IAAG;AAC3B,QAAI,QACF,SAAQ,KAAK,OAAM;QAEnB,KAAI,IAAI,KAAK,CAAC,OAAO,CAAA;;AAGzB,UAAO;IACR;EAED,SAAS,eAAe,MAAqB;GAC3C,MAAM,MAAM,MAAM;AAClB,OAAI,CAAC,IAAK,QAAO;AACjB,OAAI,eAAe,KAAM,QAAO,UAAU,MAAM,IAAG;AACnD,OAAI,MAAM,QAAQ,IAAI,CAAE,QAAO,IAAI,MAAK,MAAK,UAAU,MAAM,EAAE,CAAA;AAC/D,OAAI,WAAW,OAAO,SAAS,IAC7B,QAAO,UAAU,MAAM,IAAI,MAAM,IAAI,UAAU,MAAM,IAAI,IAAG;AAE9D,UAAO;;EAGT,SAAS,UAAU,MAAqB;AACtC,OAAI,MAAM,kBAAkB,QAAS,QAAO;GAC5C,MAAM,MAAM,MAAM;AAClB,OAAI,CAAC,OAAO,EAAE,WAAY,KAAiB,QAAO;GAClD,MAAM,QAAQ;GAEd,MAAM,IAAI,KAAK,SAAQ;GACvB,MAAM,QAAQ,MAAM,OAAO,SAAQ;AACnC,OAAI,CAAC,MAAO,QAAO;AAGnB,OAAI,CAAC,MAAM,OAAO,YAAY,OAAO;IACnC,MAAM,MAAM,YAAY,MAAM,SAAQ;IACtC,MAAM,CAAC,IAAI,MAAM,SAAS,MAAM,CAAC,OAAO,IAAI,GAAG,CAAC,KAAK,MAAK;AAC1D,WAAO,IAAI,MAAM,IAAI;;AAIvB,OAAI,MAAM,KAAK;IACb,MAAM,MAAM,MAAM,IAAI,SAAQ;AAC9B,WAAO,IAAI,SAAS,IAAI;;AAG1B,UAAO;;EAGT,SAAS,WAAW,MAAqB;AACvC,OAAI,gBAAgB,MAAM,IAAI,UAAU,KAAK,CAAC,CAAE,QAAO;AACvD,OAAI,MAAM,iBAAiB,KAAK,CAAE,QAAO;AACzC,OAAI,MAAM;QAEJ,OADQ,UAAU,MAAM,QAAO,CACnB,QAAO;;AAEzB,OAAI,MAAM;QAEJ,OADQ,UAAU,MAAM,QAAO,CACnB,QAAO;;AAEzB,UAAO;;EAGT,MAAM,eAAe,eAAqC;GAGxD,MAAM,UAFQ,IAAI,KAAK,YAAY,OAAO,aAAa,OAAO,EAAC,CACxC,QAAO,GACH,MAAM,eAAe,KAAK;GAErD,MAAM,aAAa,MAAM,aAAa,YAAY;IAEhD,MAAM,SAAS,SADK,IAAI,KAAK,YAAY,OAAO,aAAa,QAAQ,GAAG,EAAE,CAAC,SAAQ;AAEnF,WAAO,KAAK,KAAK,SAAS,EAAE,GAAG;OAC9B;GAEH,MAAM,OAA6B,EAAC;AACpC,QAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAAK;IACnC,MAAM,OAAO,IAAI,KAAK,YAAY,OAAO,aAAa,OAAO,IAAI,SAAS,EAAC;AAC3E,SAAK,SAAS,GAAG,GAAG,GAAG,EAAC;IACxB,MAAM,MAAM,UAAU,KAAI;AAC1B,SAAK,KAAK;KACR;KACA,YAAY,KAAK,SAAS;KAC1B,SAAS,UAAU,MAAM,MAAM;KAC/B,YAAY,eAAe,KAAK;KAChC,WAAW,UAAU,KAAK;KAC1B,YAAY,WAAW,KAAK;KAC5B,gBAAgB,KAAK,UAAU,KAAK,aAAa;KACjD,SAAS,UAAU,MAAM,IAAI,IAAI,IAAI,EAAE;KACxC,CAAA;;AAEH,UAAO;IACR;EAGD,MAAM,aAAa,IAAiB,KAAI;EAExC,SAAS,eAAe,KAAyB;AAC/C,OAAI,IAAI,WAAY;AACpB,OAAI,CAAC,MAAM,mBAAmB,IAAI,eAAgB;AAElD,QAAK,aAAa,IAAG;AAErB,OAAI,MAAM,kBAAkB,OAAQ;AAEpC,OAAI,MAAM,kBAAkB,SAC1B,MAAK,qBAAqB,IAAI,KAAI;YACzB,MAAM,kBAAkB,YAAY;IAC7C,MAAM,UAAW,MAAM,cAAqC,EAAC;IAC7D,MAAM,MAAM,QAAQ,WAAU,MAAK,UAAU,GAAG,IAAI,KAAK,CAAA;AACzD,QAAI,OAAO,GAAG;KACZ,MAAM,OAAO,CAAC,GAAG,QAAO;AACxB,UAAK,OAAO,KAAK,EAAC;AAClB,UAAK,qBAAqB,KAAI;UAE9B,MAAK,qBAAqB,CAAC,GAAG,SAAS,IAAI,KAAK,CAAA;cAEzC,MAAM,kBAAkB,QACjC,KAAI,CAAC,WAAW,MACd,YAAW,QAAQ,IAAI;QAClB;IACL,MAAM,QAAQ,WAAW;IACzB,MAAM,MAAM,IAAI;AAChB,eAAW,QAAQ;AAInB,SAAK,qBAHW,MAAM,SAAS,IAAI,IAAI,SAAQ,GAC3C;KAAE;KAAO;KAAI,GACb;KAAE,OAAO;KAAK,KAAK;KAAM,CACI;;;EAKvC,SAAS,eAAe,KAAyB;AAC/C,OAAI,MAAM,kBAAkB,WAAW,WAAW,MAChD,aAAY,QAAQ,IAAI;;EAI5B,SAAS,YAAY;AACnB,OAAI,aAAa,UAAU,GAAG;AAC5B,iBAAa,QAAQ;AACrB,gBAAY;AACZ,SAAK,eAAe,YAAY,MAAK;SAErC,cAAa;AAEf,QAAK,gBAAgB,aAAa,MAAK;AACvC,QAAK,YAAY,QAAQ,aAAa,OAAO,YAAY,MAAK;;EAGhE,SAAS,YAAY;AACnB,OAAI,aAAa,UAAU,IAAI;AAC7B,iBAAa,QAAQ;AACrB,gBAAY;AACZ,SAAK,eAAe,YAAY,MAAK;SAErC,cAAa;AAEf,QAAK,gBAAgB,aAAa,MAAK;AACvC,QAAK,YAAY,QAAQ,aAAa,OAAO,YAAY,MAAK;;;uBAK9D,mBAgEM,OAhEN,eAgEM;IA/DO,QAAA,kBAAA,WAAA,EAAX,mBAcM,OAdN,eAcM;KAbJ,mBAIS,UAAA;MAJD,MAAK;MAAS,OAAM;MAAyB,cAAW;MAAkB,SAAO;uCACvF,mBAEM,OAAA;MAFD,SAAQ;MAAY,MAAK;MAAO,QAAO;MAAe,gBAAa;MAAI,kBAAe;MAAQ,mBAAgB;SACjH,mBAA2B,QAAA,EAArB,GAAE,kBAAgB,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA;KAG5B,WAEO,KAAA,QAAA,UAAA;MAFc,OAAO,aAAA;MAAe,MAAM,YAAA;MAA0B;MAAwB;cAE5F,CADL,mBAA0D,QAA1D,eAA0D,gBAApB,WAAA,MAAU,EAAA,EAAA,CAAA,CAAA;KAElD,mBAIS,UAAA;MAJD,MAAK;MAAS,OAAM;MAAyB,cAAW;MAAc,SAAO;uCACnF,mBAEM,OAAA;MAFD,SAAQ;MAAY,MAAK;MAAO,QAAO;MAAe,gBAAa;MAAI,kBAAe;MAAQ,mBAAgB;SACjH,mBAA0B,QAAA,EAApB,GAAE,iBAAe,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA;;IAK7B,mBAQM,OARN,eAQM,EAAA,UAAA,KAAA,EAPJ,mBAMO,UAAA,MAAA,WALgB,cAAA,QAAb,OAAO,MAAC;yBADlB,mBAMO,QAAA;MAJJ,KAAK;MACN,OAAM;SAEN,WAAqE,KAAA,QAAA,YAAA;MAA9C,SAAU;MAAQ,OAAO;cAAqB,CAAA,gBAAA,gBAAf,MAAK,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA;;IAI/D,mBAoCM,OApCN,eAoCM,EAAA,UAAA,KAAA,EAnCJ,mBAkCS,UAAA,MAAA,WAjCY,aAAA,QAAX,KAAK,MAAC;yBADhB,mBAkCS,UAAA;MAhCN,KAAK;MACN,MAAK;MACJ,UAAU,IAAI;MACd,OAAK,eAAA,CAAA,sBAAA;oCAAyF,IAAI;uCAAqD,IAAI;uCAAwD,IAAI;uCAAuD,IAAI;sCAAuD,IAAI;0CAA+D,QAAA,kBAAa,UAAA,CAAgB,IAAI;;MAW7a,UAAK,WAAE,eAAe,IAAG;MACzB,eAAU,WAAE,eAAe,IAAG;SAE/B,WAcO,KAAA,QAAA,eAAA,EAdwB,KAAG,QAc3B,CAbL,mBAAmE,QAAnE,eAAmE,gBAAxB,IAAI,WAAU,EAAA,EAAA,EAC9C,IAAI,QAAQ,SAAM,KAAA,WAAA,EAA7B,mBAWM,OAXN,eAWM,EAAA,UAAA,KAAA,EAVJ,mBASE,UAAA,MAAA,WARuB,IAAI,QAAQ,MAAK,GAAA,EAAA,GAAhC,QAAQ,OAAE;0BADpB,mBASE,QAAA;OAPC,KAAK;OACL,OAAK,eAAA,CAAA,yBAAA,0BAAuF,OAAO,QAAI,QAAA,CAAA;OAIvG,OAAK,eAAE,OAAO,QAAK,EAAA,kBAAuB,OAAO,OAAK,GAAA,EAAA,CAAA;OACtD,OAAO,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEnS7B,MAAM,QAAQ;;;;;;;;EAsBd,MAAM,OAAO;EAQb,MAAM,cAAc,IAAI,GAAE;EAC1B,MAAM,eAAe,IAAsB,KAAI;EAE/C,MAAM,aAAa,eAAe,MAAM,SAAS,KAAA,IAAY,MAAM,OAAO,aAAa,MAAK;EAE5F,SAAS,UAAU,KAA8B,OAAgC;AAC/E,OAAI,CAAC,MAAM,OAAQ,QAAO;AAC1B,OAAI,OAAO,MAAM,WAAW,WAAY,QAAO,MAAM,OAAO,IAAG;AAC/D,UAAO,IAAI,MAAM;;EAGnB,SAAS,aAAa,KAA8B,KAAsB;AACxE,UAAO,IAAI,MAAM,IAAI,CAAC,QAAQ,KAAc,MAAM;AAChD,QAAI,OAAO,OAAO,QAAQ,SAAU,QAAQ,IAAgC;MAE3E,IAAG;;EAGR,SAAS,WAAW,KAAsB,KAA8B,OAAuB;GAC7F,MAAM,QAAQ,aAAa,KAAK,IAAI,IAAG;AACvC,OAAI,IAAI,UAAW,QAAO,IAAI,UAAU,OAAO,KAAK,MAAK;AACzD,OAAI,UAAU,QAAQ,UAAU,KAAA,EAAW,QAAO;AAClD,UAAO,OAAO,MAAK;;EAYrB,MAAM,eATa,cAAc;GAC/B,aAAa,MAAM;GACnB,OAAO;GACP,eAAe,MAAM;GACrB,UAAU,QAAQ;AAEhB,YADa,MAAM,cAAc,MAAM,QAAQ,KAAI,MAAK,EAAE,IAAG,EACjD,KAAI,QAAO,aAAa,KAAK,IAAI,CAAA;;GAEhD,CAAA,CAC+B;EAWhC,MAAM,aATY,eAAe;GAC/B,OAAO;GACP,MAAM;GACN,eAAe;IACb,MAAM,OAAO,WAAW;AACxB,WAAO,QAAQ,MAAM,aAAa,MAAM,QAAQ,MAAK,MAAK,EAAE,QAAQ,KAAK,IAAI,CAAA;;GAE/E,WAAW,KAAK,QAAQ,aAAa,KAAK,IAAI;GAC/C,CAAA,CAC4B;EAG7B,MAAM,gBAAgB,eAAe;AACnC,OAAI,CAAC,MAAM,WAAY,QAAO,WAAW;GACzC,MAAM,EAAE,MAAM,aAAa,MAAM;GACjC,MAAM,SAAS,OAAO,KAAK;AAC3B,UAAO,WAAW,MAAM,MAAM,OAAO,QAAQ,SAAQ;IACtD;EAED,MAAM,aAAa,eAAe;AAChC,OAAI,CAAC,MAAM,WAAY,QAAO;AAC9B,UAAO,KAAK,IAAI,GAAG,KAAK,KAAK,WAAW,MAAM,SAAS,MAAM,WAAW,SAAS,CAAA;IAClF;EAED,SAAS,WAAW,KAAsB;AACxC,OAAI,CAAC,IAAI,YAAY,CAAC,MAAM,SAAU;GAEtC,MAAM,UAAU,WAAW;GAC3B,IAAI;AAEJ,OAAI,SAAS,QAAQ,IAAI,IACvB,KAAI,QAAQ,cAAc,MACxB,QAAO;IAAE,KAAK,IAAI;IAAK,WAAW;IAAO;YAChC,QAAQ,cAAc,OAC/B,QAAO;OAEP,QAAO;IAAE,KAAK,IAAI;IAAK,WAAW;IAAM;OAG1C,QAAO;IAAE,KAAK,IAAI;IAAK,WAAW;IAAM;AAG1C,OAAI,MAAM,SAAS,KAAA,EACjB,cAAa,QAAQ;AAEvB,QAAK,eAAe,KAAI;;EAG1B,SAAS,iBAAiB,MAAc;AACtC,OAAI,CAAC,MAAM,WAAY;AACvB,QAAK,qBAAqB;IAAE,GAAG,MAAM;IAAY;IAAM,CAAA;;EAGzD,SAAS,iBAAiB,KAA+B;AACvD,UAAO,IAAI,aAAa,QAAS,MAAM,YAAY,IAAI,aAAa;;EAOtE,MAAM,eAAe,iBAAiB;GACpC,gBAAgB,MAAM;GACtB,OALkB,eAClB,cAAc,MAAM,KAAK,KAAK,MAAM,UAAU,KAAK,EAAE,CAAA,CACvD;GAIC,CAAA;EACD,MAAM,cAAc,eAAe;AACjC,UAAO,MAAM,cAAc,aAAa,cAAc;IACvD;EAED,MAAM,sBAAsB,eAAe;AACzC,UAAO,MAAM,aAAa,SAAS,KAAK,CAAC,YAAY;IACtD;EAED,SAAS,kBAAkB;AACzB,OAAI,YAAY,MACd,MAAK,uBAAuB,EAAE,CAAA;OAE9B,MAAK,uBAAuB,aAAa,WAAW,MAAK;;EAI7D,SAAS,gBAAgB,KAAsB;AAC7C,QAAK,uBAAuB,aAAa,YAAY,IAAI,CAAA;;EAG3D,MAAM,eAAe,eAAe;AAClC,OAAI,CAAC,MAAM,UAAW,QAAO,KAAA;AAE7B,UAAO;IAAE,WADS,OAAO,MAAM,cAAc,WAAW,GAAG,MAAM,UAAU,MAAM,MAAM;IACnE,WAAW;IAAgB;IAChD;;uBAIC,mBA8KM,OAAA,EA7KH,OAAK,eAAA,CAAA,kBAAA,EAAA,4BAAgE,QAAA,UAAQ,CAAA,CAAA,EAAA,EAAA;IAMnE,QAAA,cAAA,WAAA,EAAX,mBAYM,OAZN,eAYM,CAXJ,mBAUM,OAVN,eAUM,CAAA,OAAA,OAAA,OAAA,KATJ,mBAEM,OAAA;KAFD,OAAM;KAA8B,SAAQ;KAAY,MAAK;KAAO,QAAO;KAAe,gBAAa;KAAI,kBAAe;KAAQ,mBAAgB;QACrJ,mBAAgC,UAAA;KAAxB,IAAG;KAAK,IAAG;KAAK,GAAE;QAAM,mBAA6B,QAAA,EAAvB,GAAE,oBAAkB,CAAA,CAAA,EAAA,GAAA,GAAA,eAE5D,mBAKE,SAAA;8EAJoB,QAAA;KACpB,MAAK;KACL,OAAM;KACL,aAAa,QAAA;8CAHL,YAAA,MAAW,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAS1B,mBAsHM,OAAA;KAtHD,OAAM;KAAiC,OAAK,eAAE,aAAA,MAAY;;KAC7D,mBAgGQ,SAhGR,eAgGQ,CA/FN,mBAsDQ,SAAA,EAtDA,OAAK,eAAA,CAAA,yBAAA,EAAA,iCAA+D,QAAA,cAAY,CAAA,CAAA,EAAA,EAAA,CACtF,mBAoDK,MAAA,MAAA,CAnDO,QAAA,cAAA,WAAA,EAAV,mBAQK,MARL,eAQK,CAPH,mBAME,SAAA;MALA,MAAK;MACJ,SAAS,YAAA;MACT,eAAe,oBAAA;MAChB,OAAM;MACL,UAAQ;sFAGb,mBAyCK,UAAA,MAAA,WAxCW,QAAA,UAAP,QAAG;0BADZ,mBAyCK,MAAA;OAvCF,KAAK,IAAI;OACT,OAAK,eAAA;;+BAAiF,QAAA;qCAAqD,IAAI,SAAK;;yCAAkF,iBAAiB,IAAG;uCAAmD,WAAA,OAAY,QAAQ,IAAI,OAAO,WAAA,OAAY;;;OASxV,OAAK,eAAA;eAA2B,IAAI,QAAK,OAAW,IAAI,UAAK,WAAA,GAAmB,IAAI,MAAK,MAAO,IAAI,QAAS,KAAA;kBAAqC,IAAI,WAAQ,OAAW,IAAI,aAAQ,WAAA,GAAmB,IAAI,SAAQ,MAAO,IAAI,WAAY,KAAA;;OAI3O,UAAK,WAAE,iBAAiB,IAAG,GAAI,WAAW,IAAG,GAAI,KAAA;UAElD,WAsBO,KAAA,QAAA,UAtBgB,IAAI,OAAG,EAAK,QAAQ,KAAG,QAsBvC,CArBL,mBAoBM,OApBN,eAoBM,CAnBJ,mBAA4B,QAAA,MAAA,gBAAnB,IAAI,MAAK,EAAA,EAAA,EAEV,iBAAiB,IAAG,IAAA,WAAA,EAD5B,mBAiBM,OAAA;;OAfH,OAAK,eAAA,CAAA,6BAAA;0CAAyI,WAAA,OAAY,QAAQ,IAAI,OAAO,WAAA,OAAY,cAAS;2CAAuE,WAAA,OAAY,QAAQ,IAAI,OAAO,WAAA,OAAY,cAAS;;OAO9T,SAAQ;OACR,MAAK;OACL,QAAO;OACP,gBAAa;OACb,kBAAe;OACf,mBAAgB;;OAEhB,mBAA2B,QAAA,EAArB,GAAE,kBAAgB,EAAA,MAAA,GAAA;OAAG,mBAAqB,QAAA,EAAf,GAAE,YAAU,EAAA,MAAA,GAAA;OAAG,mBAAyB,QAAA,EAAnB,GAAE,gBAAc,EAAA,MAAA,GAAA;OAAG,mBAAoB,QAAA,EAAd,GAAE,WAAS,EAAA,MAAA,GAAA;;uBAQtG,mBAsCQ,SAAA,MAAA,EAAA,UAAA,KAAA,EArCN,mBAoCK,UAAA,MAAA,WAnCuB,cAAA,QAAlB,KAAK,aAAQ;0BADvB,mBAoCK,MAAA;OAlCF,KAAK,UAAU,KAAK,SAAQ;OAC5B,OAAK,eAAA,CAAA,uBAAA;wCAAyG,QAAA,WAAW,WAAQ,MAAA;yCAA6D,MAAA,aAAY,CAAC,WAAW,UAAU,KAAK,SAAQ,CAAA;;OAO7O,UAAK,WAAE,KAAI,aAAc,KAAK,SAAQ;UAE7B,QAAA,cAAA,WAAA,EAAV,mBAQK,MARL,gBAQK,CAPH,mBAME,SAAA;OALA,MAAK;OACJ,SAAS,MAAA,aAAY,CAAC,WAAW,UAAU,KAAK,SAAQ,CAAA;OACzD,OAAM;OACL,SAAK,OAAA,OAAA,OAAA,KAAA,oBAAN,IAAW,CAAA,OAAA,CAAA;OACV,WAAM,WAAE,gBAAgB,UAAU,KAAK,SAAQ,CAAA;wFAGpD,mBAcK,UAAA,MAAA,WAbW,QAAA,UAAP,QAAG;2BADZ,mBAcK,MAAA;QAZF,KAAK,IAAI;QACT,OAAK,eAAA;;gCAAiF,QAAA;sCAAqD,IAAI,SAAK;2CAAgE,IAAI,UAAQ;;QAMhO,SAAK,eAAA,WAAO,KAAI,cAAe,aAAa,KAAK,IAAI,IAAG,EAAG,KAAK,IAAG,EAAA,CAAA,OAAA,CAAA;WAEpE,WAEO,KAAA,QAAA,QAFc,IAAI,OAAG;QAAK,OAAO,aAAa,KAAK,IAAI,IAAG;QAAS;QAAM,QAAQ;QAAM,OAAO;gBAE9F,CAAA,gBAAA,gBADF,WAAW,KAAK,KAAK,SAAQ,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,EAAA,IAAA,eAAA;;;KAQ/B,cAAA,MAAc,WAAM,KAAA,CAAW,QAAA,WAAA,WAAA,EAA1C,mBAOM,OAPN,gBAOM,CANJ,WAKO,KAAA,QAAA,SAAA,EAAA,QAAA,CAAA,OAAA,OAAA,OAAA,KAJL,mBAEM,OAAA;MAFD,OAAM;MAA6B,SAAQ;MAAY,MAAK;MAAO,QAAO;MAAe,gBAAa;MAAI,kBAAe;MAAQ,mBAAgB;SACpJ,mBAAuD,YAAA,EAA7C,QAAO,qCAAmC,CAAA,EAAG,mBAAuH,QAAA,EAAjH,GAAE,8GAA4G,CAAA,CAAA,EAAA,GAAA,GAE7K,mBAAyD,KAAzD,gBAAyD,gBAAhB,QAAA,UAAS,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;KAK3C,QAAA,WAAA,WAAA,EAAX,mBAOM,OAPN,gBAOM,CANJ,WAKO,KAAA,QAAA,WAAA,EAAA,QAAA,CAAA,OAAA,OAAA,OAAA,KAJL,mBAGM,OAAA;MAHD,OAAM;MAAkC,SAAQ;MAAY,MAAK;MAAO,eAAY;SACvF,mBAAuF,UAAA;MAA/E,IAAG;MAAK,IAAG;MAAK,GAAE;MAAK,QAAO;MAAe,gBAAa;MAAI,SAAQ;SAC9E,mBAAkG,QAAA;MAA5F,GAAE;MAA0B,QAAO;MAAe,gBAAa;MAAI,kBAAe;;;IAOrF,QAAA,cAAA,WAAA,EAAX,mBA8BM,OA9BN,gBA8BM,CA7BJ,WA4BO,KAAA,QAAA,UAAA,EAAA,QAAA,CA3BL,mBAGO,QAHP,gBAGO,iBAFD,QAAA,WAAW,OAAI,KAAQ,QAAA,WAAW,WAAQ,EAAA,GAAO,MAAO,gBAAG,KAAK,IAAI,QAAA,WAAW,OAAO,QAAA,WAAW,UAAU,MAAA,WAAU,CAAC,OAAM,CAAA,GAAI,SACjI,gBAAG,MAAA,WAAU,CAAC,OAAM,EAAA,EAAA,EAEzB,mBAsBM,OAtBN,gBAsBM;KArBJ,mBASS,UAAA;MARP,MAAK;MACL,OAAM;MACL,UAAU,QAAA,WAAW,QAAI;MACzB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,iBAAiB,QAAA,WAAW,OAAI,EAAA;uCAExC,mBAEM,OAAA;MAFD,SAAQ;MAAY,MAAK;MAAO,QAAO;MAAe,gBAAa;MAAI,kBAAe;MAAQ,mBAAgB;SACjH,mBAA2B,QAAA,EAArB,GAAE,kBAAgB,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,EAAA,GAAA,eAAA;KAG5B,mBAAyF,QAAzF,gBAAyF,gBAA5C,QAAA,WAAW,KAAI,GAAG,QAAG,gBAAG,WAAA,MAAU,EAAA,EAAA;KAC/E,mBASS,UAAA;MARP,MAAK;MACL,OAAM;MACL,UAAU,QAAA,WAAW,QAAQ,WAAA;MAC7B,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,iBAAiB,QAAA,WAAW,OAAI,EAAA;uCAExC,mBAEM,OAAA;MAFD,SAAQ;MAAY,MAAK;MAAO,QAAO;MAAe,gBAAa;MAAI,kBAAe;MAAQ,mBAAgB;SACjH,mBAA0B,QAAA,EAApB,GAAE,iBAAe,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,EAAA,GAAA,eAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEzXrC,MAAM,QAAQ;EAEd,MAAM,UAAU,eACd,MAAM,SAAS,MAAM,UAAU,GAAG,MAAM,QAAQ,UAAU,KAAA,EAC5D;EAEA,MAAM,SAAS,eACb,CAAC,MAAM,SAAS,MAAM,QAAQ,MAAM,UAAU,GAAG,MAAM,QAAQ,SAAS,KAAA,EAC1E;EAEA,MAAM,cAAc,eAAe,QAAQ,SAAS,OAAO,MAAK;;uBAI9D,mBAyBM,OAzBN,eAyBM;IAxBO,QAAA,SAAA,WAAA,EAAX,mBAcM,OAdN,eAcM,CAbJ,mBAMQ,SAAA;KALL,KAAK,QAAA;KACN,OAAM;wCAEH,QAAA,MAAK,GAAG,KACX,EAAA,EAAY,QAAA,YAAA,WAAA,EAAZ,mBAAmF,QAAnF,eAA2E,IAAC,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,GAAA,cAAA,EAAA,CAGrE,QAAA,YAAY,QAAA,gBAAA,WAAA,EADrB,mBAKO,QALP,eAGC,aAED,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAGF,WAAoC,KAAA,QAAA,WAAA,EAA7B,aAAc,YAAA,OAAW,CAAA;IAEvB,QAAA,SAAA,WAAA,EAAT,mBAEI,KAAA;;KAFa,IAAI,QAAA;KAAS,OAAM;KAAyB,MAAK;uBAC7D,QAAA,MAAK,EAAA,GAAA,cAAA,IAEI,QAAA,QAAA,WAAA,EAAd,mBAEI,KAAA;;KAFiB,IAAI,OAAA;KAAQ,OAAM;uBAClC,QAAA,KAAI,EAAA,GAAA,cAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEvCb,MAAM,QAAQ;EAKd,MAAM,OAAO;EAKb,MAAM,QAAQ,UAAS;EACvB,MAAM,mBAAmB,CAAC,CAAC,MAAM,eAAe,CAAC,CAAC,MAAM;;uBAItD,mBAuDM,OAAA;IAtDJ,OAAK,eAAA,CAAC,cAAY,eACK,MAAM,OAAI,CAAA;IACjC,MAAK;;IAGM,MAAM,SAAI,aAAA,WAAA,EAArB,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAAiC,UAAA;KAAzB,IAAG;KAAK,IAAG;KAAK,GAAE;kBAAO,mBAA0B,QAAA,EAApB,GAAE,iBAAe,EAAA,MAAA,GAAA,CAAA,EAAA,CAAA,IAG1C,MAAM,SAAI,WAAA,WAAA,EAA1B,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA;KADJ,mBAAiC,UAAA;MAAzB,IAAG;MAAK,IAAG;MAAK,GAAE;;KAAO,mBAAsB,QAAA,EAAhB,GAAE,aAAW,EAAA,MAAA,GAAA;KAAG,mBAAqB,QAAA,EAAf,GAAE,YAAU,EAAA,MAAA,GAAA;YAG3D,MAAM,SAAI,aAAA,WAAA,EAA1B,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA;KADJ,mBAAqF,QAAA,EAA/E,GAAE,4EAA0E,EAAA,MAAA,GAAA;KAAG,mBAAoB,QAAA,EAAd,GAAE,WAAS,EAAA,MAAA,GAAA;KAAG,mBAAuB,QAAA,EAAjB,GAAE,cAAY,EAAA,MAAA,GAAA;0BAG/H,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA;KADJ,mBAAiC,UAAA;MAAzB,IAAG;MAAK,IAAG;MAAK,GAAE;;KAAO,mBAAsB,QAAA,EAAhB,GAAE,aAAW,EAAA,MAAA,GAAA;KAAG,mBAAsB,QAAA,EAAhB,GAAE,aAAW,EAAA,MAAA,GAAA;;IAG5E,mBAOM,OAPN,eAOM,CANM,MAAM,SAAA,WAAA,EAAhB,mBAEK,MAFL,eAEK,gBADA,MAAM,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,EAEhB,mBAEM,OAFN,eAEM,CADJ,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA,CAAA,CAAA;IAID,YAAU,IAAA,WAAA,EAArB,mBAWM,OAXN,eAWM,CAVJ,WASO,KAAA,QAAA,WAAA,EAAA,QAAA,CARL,mBAOS,UAAA;KANP,MAAK;KACL,OAAK,eAAA,CAAC,0BAAwB,2BACK,MAAM,OAAI,CAAA;KAC5C,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAA;uBAET,MAAM,YAAW,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAMlB,MAAM,eAAA,WAAA,EADd,mBAUS,UAAA;;KARP,MAAK;KACL,OAAM;KACN,cAAW;KACV,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,UAAA;sCAEZ,mBAEM,OAAA;KAFD,OAAM;KAA2B,SAAQ;KAAY,MAAK;KAAO,QAAO;KAAe,gBAAa;KAAI,kBAAe;KAAQ,mBAAgB;QAClJ,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,EAAG,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;EE1EnD,MAAM,EAAE,QAAQ,YAAY,UAAS;EAErC,MAAM,eAA8C;GAClD,SAAS;GACT,OAAO;GACP,SAAS;GACT,MAAM;GACR;;uBAIE,YAwCW,UAAA,EAxCD,IAAG,QAAM,EAAA,CACjB,mBAsCM,OAtCN,eAsCM,CArCJ,YAoCkB,iBAAA,EApCD,MAAK,SAAO,EAAA;2BAEF,EAAA,UAAA,KAAA,EADzB,mBAkCM,UAAA,MAAA,WAjCY,MAAA,OAAM,GAAf,UAAK;yBADd,mBAkCM,OAAA;MAhCH,KAAK,MAAM;MACX,OAAK,eAAA,CAAA,oBAAA,qBAA4C,MAAM,OAAI,CAAA;MAC3D,OAAK,eAAA,EAAA,iBAAiC,aAAa,MAAM,OAAA,CAAA;MAG1D,MAAK;MACJ,UAAK,WAAE,MAAA,QAAO,CAAC,MAAM,GAAE;;MAGb,MAAM,SAAI,aAAA,WAAA,EAArB,mBAEM,OAAA;;OAF+B,OAAM;OAAmB,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,gBAAa;OAAI,kBAAe;OAAQ,mBAAgB;OAAS,OAAK,eAAA,EAAA,OAAW,aAAa,MAAM,OAAI,CAAA;wCAC1N,mBAAiC,UAAA;OAAzB,IAAG;OAAK,IAAG;OAAK,GAAE;oBAAO,mBAA0B,QAAA,EAApB,GAAE,iBAAe,EAAA,MAAA,GAAA,CAAA,EAAA,EAAA,EAAA,IAG1C,MAAM,SAAI,WAAA,WAAA,EAA1B,mBAEM,OAAA;;OAFkC,OAAM;OAAmB,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,gBAAa;OAAI,kBAAe;OAAQ,mBAAgB;OAAS,OAAK,eAAA,EAAA,OAAW,aAAa,MAAM,OAAI,CAAA;;OAC7N,mBAAiC,UAAA;QAAzB,IAAG;QAAK,IAAG;QAAK,GAAE;;OAAO,mBAAsB,QAAA,EAAhB,GAAE,aAAW,EAAA,MAAA,GAAA;OAAG,mBAAqB,QAAA,EAAf,GAAE,YAAU,EAAA,MAAA,GAAA;iBAG3D,MAAM,SAAI,aAAA,WAAA,EAA1B,mBAEM,OAAA;;OAFoC,OAAM;OAAmB,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,gBAAa;OAAI,kBAAe;OAAQ,mBAAgB;OAAS,OAAK,eAAA,EAAA,OAAW,aAAa,MAAM,OAAI,CAAA;;OAC/N,mBAAqF,QAAA,EAA/E,GAAE,4EAA0E,EAAA,MAAA,GAAA;OAAG,mBAAoB,QAAA,EAAd,GAAE,WAAS,EAAA,MAAA,GAAA;OAAG,mBAAuB,QAAA,EAAjB,GAAE,cAAY,EAAA,MAAA,GAAA;+BAG/H,mBAEM,OAAA;;OAFM,OAAM;OAAmB,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,gBAAa;OAAI,kBAAe;OAAQ,mBAAgB;OAAS,OAAK,eAAA,EAAA,OAAW,aAAa,MAAM,OAAI,CAAA;;OACjM,mBAAiC,UAAA;QAAzB,IAAG;QAAK,IAAG;QAAK,GAAE;;OAAO,mBAAsB,QAAA,EAAhB,GAAE,aAAW,EAAA,MAAA,GAAA;OAAG,mBAAsB,QAAA,EAAhB,GAAE,aAAW,EAAA,MAAA,GAAA;;MAE5E,mBAA4D,QAA5D,eAA4D,gBAAvB,MAAM,QAAO,EAAA,EAAA;MAClD,mBAME,QAAA;OALA,OAAM;OACL,OAAK,eAAA;yBAAmC,aAAa,MAAM;8BAA2C,MAAM,YAAQ,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEpCjI,MAAM,QAAQ;EAOd,MAAM,OAAO;EAIb,SAAS,YAAY,OAAmB;AACtC,OAAI,CAAC,MAAM,YAAY,CAAC,MAAM,QAC5B,MAAK,SAAS,MAAK;;;uBAMrB,mBAoCS,UAAA;IAnCP,MAAK;IACJ,UAAU,QAAA,YAAY,QAAA;IACtB,cAAY,QAAA;IACZ,OAAO,QAAA;IACP,OAAK,eAAA;;0BAAyD,QAAA;0BAAsC,QAAA;KAAe,QAAA,YAAY,QAAA,UAAO,+BAAA;;IAMtI,SAAO;OAGA,QAAA,WAAA,WAAA,EADR,mBAmBM,OAAA;;IAjBH,OAAK,eAAA,CAAA,6BAAA,2BAA2D,QAAA,OAAI,CAAA;IACrE,MAAK;IACL,SAAQ;qCAER,mBAOE,UAAA;IANA,OAAA,EAAA,WAAA,QAAqB;IACrB,IAAG;IACH,IAAG;IACH,GAAE;IACF,QAAO;IACP,gBAAa;iBAEf,mBAIE,QAAA;IAHA,OAAA,EAAA,WAAA,QAAqB;IACrB,MAAK;IACL,GAAE;wCAGN,mBAEO,QAAA;;IAFO,OAAK,eAAA,2BAA6B,QAAA,OAAI;OAClD,WAAQ,KAAA,QAAA,UAAA,CAAA,EAAA,EAAA,EAAA,EAAA,IAAA,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEpDd,MAAM,EAAE,QAAQ,gBAAgB,UAAS;;uBAIvC,YAiCa,oBAAA;IAhCX,OAAM;IACN,SAAQ;IACP,MAAM,QAAA;IACN,OAAO,MAAA,OAAM,GAAA,yBAAA;IACb,SAAO,MAAA,YAAW;;2BAcb,CAVE,MAAA,OAAM,IAAA,WAAA,EADd,mBAWM,OAXN,eAWM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA;KADJ,mBAAgC,UAAA;MAAxB,IAAG;MAAK,IAAG;MAAK,GAAE;;KAAM,mBAAoB,QAAA,EAAd,GAAE,WAAS,EAAA,MAAA,GAAA;KAAG,mBAAqB,QAAA,EAAf,GAAE,YAAU,EAAA,MAAA,GAAA;KAAG,mBAAiC,QAAA,EAA3B,GAAE,wBAAsB,EAAA,MAAA,GAAA;KAAG,mBAAmC,QAAA,EAA7B,GAAE,0BAAwB,EAAA,MAAA,GAAA;KAAG,mBAAoB,QAAA,EAAd,GAAE,WAAS,EAAA,MAAA,GAAA;KAAG,mBAAqB,QAAA,EAAf,GAAE,YAAU,EAAA,MAAA,GAAA;KAAG,mBAAkC,QAAA,EAA5B,GAAE,yBAAuB,EAAA,MAAA,GAAA;KAAG,mBAAkC,QAAA,EAA5B,GAAE,yBAAuB,EAAA,MAAA,GAAA;0BAGzP,mBAWM,OAXN,eAWM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAA2H,QAAA,EAArH,GAAE,kHAAgH,EAAA,MAAA,GAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE9B9H,MAAM,QAAQ;EASd,MAAM,OAAO;EAIb,MAAM,SAAS,IAAI,MAAM,YAAW;EAEpC,MAAM,eAAe;AACnB,OAAI,CAAC,MAAM,SACT,QAAO,QAAQ,CAAC,OAAO;;EAI3B,MAAM,qBAAqB,UAAiB;AAC1C,SAAM,iBAAgB;AACtB,QAAK,sBAAsB,CAAC,MAAM,YAAW;;EAG/C,MAAM,gBAAgB,eAAe,CACnC,iCACA,MAAM,WAAW,4CAA4C,GAC9D,CAAA;EAED,MAAM,cAAc,gBAAgB,EAClC,iBAAiB,MAAM,UAAU,6BAClC,EAAC;EAEF,MAAM,iBAAiB,gBAAgB,EACrC,OAAO,MAAM,aAAa,wBAC3B,EAAC;EAEF,MAAM,YAAY,eAAe;AAC/B,OAAI,CAAC,MAAM,KAAM,QAAO;AACxB,UAAO,MAAM,QAAQ,MAAM,KAAK,IAAI,MAAM,KAAK,WAAW,IAAI,IAAI,MAAM,KAAK,WAAW,IAAG;IAC5F;EAED,MAAM,mBAAmB,eAAe;AACtC,OAAI,CAAC,MAAM,eAAe,CAAC,MAAM,YAAa,QAAO,EAAC;AACtD,UAAO;IACL,iBAAiB,MAAM;IACvB,aAAa,MAAM;IACrB;IACD;;uBAIC,mBAyFM,OAAA,EAzFA,OAAK,eAAA,CAAA,yBAA4B,QAAA,QAAK,iCAAA,GAAA,CAAA,EAAA,EAAA,CAC1C,mBA+ES,UAAA;IA9EP,MAAK;IACJ,OAAK,eAAE,cAAA,MAAa;IACpB,UAAU,QAAA;IACV,iBAAe,OAAA;IACf,SAAO;OAER,mBA0BM,OA1BN,eA0BM,CAxBO,QAAA,QAAA,WAAA,EAAX,mBAkBM,OAAA;;IAlBW,OAAM;IAAqC,OAAK,eAAE,YAAA,MAAW;OAEpE,UAAA,SAAA,WAAA,EADR,mBAeM,OAAA;;IAbJ,OAAM;IACL,OAAK,eAAE,eAAA,MAAc;IACtB,SAAQ;IACR,MAAK;IACL,QAAO;IACP,gBAAa;IACb,kBAAe;IACf,mBAAgB;OAEA,MAAM,QAAQ,QAAA,KAAI,IAAA,UAAA,KAAA,EAChC,mBAA+C,UAAA,EAAA,KAAA,GAAA,EAAA,WAAxB,QAAA,OAAT,GAAG,MAAC;wBAAlB,mBAA+C,QAAA;KAAjB,KAAK;KAAO;;6BAE5C,mBAAyB,QAAA;;IAAX,GAAG,QAAA;qDAEnB,mBAA+F,QAAA;;IAAlF,OAAM;IAAoC,OAAK,eAAE,eAAA,MAAc;sBAAK,QAAA,KAAI,EAAA,EAAA,EAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,EAGvF,mBAGM,OAHN,eAGM,CAFJ,mBAAyD,MAAzD,eAAyD,gBAAb,QAAA,MAAK,EAAA,EAAA,EACxC,QAAA,YAAA,WAAA,EAAT,mBAA6E,KAA7E,eAA6E,gBAAf,QAAA,SAAQ,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA,EAI1E,mBA2CM,OA3CN,eA2CM,CAxCI,QAAA,cAAA,WAAA,EADR,mBAwBM,OAAA;;IAtBJ,OAAM;IACL,SAAO;OAER,mBAkBM,OAAA;IAjBJ,MAAK;IACL,UAAS;IACR,gBAAc,QAAA;IACd,OAAK,eAAA,CAAA,uCAAuE,QAAA,cAAW,4CAAA,GAAA,CAAA;IAIvF,OAAK,eAAE,iBAAA,MAAgB;IACvB,WAAO,CAAA,SAAA,cAAgB,mBAAiB,CAAA,UAAA,CAAA,EAAA,CAAA,QAAA,CAAA,EAAA,SAAA,cACjB,mBAAiB,CAAA,UAAA,CAAA,EAAA,CAAA,QAAA,CAAA,CAAA;OAEzC,mBAKE,QAAA,EAJC,OAAK,eAAA,CAAA,sCAA0E,QAAA,cAAW,2CAAA,GAAA,CAAA,EAAA,EAAA,MAAA,EAAA,CAAA,EAAA,IAAA,cAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,GAAA,WAAA,EASjG,mBAaM,OAAA;IAZH,OAAK,eAAA,CAAA,kCAA8D,OAAA,QAAM,yCAAA,GAAA,CAAA;IAI1E,MAAK;IACL,QAAO;IACP,gBAAa;IACb,kBAAe;IACf,mBAAgB;IAChB,SAAQ;qCAER,mBAAyB,QAAA,EAAnB,GAAE,gBAAc,EAAA,MAAA,GAAA,CAAA,EAAA,EAAA,EAAA,EAAA,CAAA,CAAA,EAAA,IAAA,cAAA,EAK5B,YAMa,YAAA,EAND,MAAK,YAAU,EAAA;2BAKnB,CAAA,eAJN,mBAIM,OAJN,gBAIM,CAHJ,mBAEM,OAFN,gBAEM,CADJ,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA,CAAA,EAAA,IAAA,EAAA,CAAA,CAAA,OAFC,OAAA,MAAM,CAAA,CAAA,CAAA,CAAA;;;;;;;;;;;;;;;;EE9IzB,MAAM,QAAQ;EAEd,MAAM,QAAQ,eAAe,sBAAsB,MAAM,MAAM,KAAK,CAAA;EAEpE,MAAM,eAAe,eAAe;GAClC,MAAM,OAAO,MAAM,MAAM;AACzB,UAAO,MAAM,KAAK,QAAQ,QAAQ,MAAM,KAAK,OAAO,QAAQ;IAC7D;EAED,SAAS,aAAa,OAAe;AACnC,SAAM,KAAK,cAAc,MAAM,MAAM,MAAM,MAAK;;;uBAKhD,YAoBY,mBAAA;IAnBT,OAAO,QAAA,MAAM;IACb,OAAO,aAAA,SAAgB,KAAA;IACvB,MAAM,QAAA,MAAM;IACZ,UAAQ,CAAA,CAAI,QAAA,MAAM,YAAY;IAC9B,YAAU,QAAA,MAAM;;2BAcV,CAZP,WAYO,KAAA,QAAA,SAZe,QAAA,MAAM,QAAI;KAAK,OAAO,QAAA;KAAQ,MAAM,QAAA;KAAO,YAAa,QAAA,KAAK,cAAc,QAAA,MAAM,KAAI;aAYpG,CATG,MAAA,MAAM,UAAA,WAAA,EAFd,YAIE,wBAHK,MAAA,MAAM,UAAS,EAAA,eAAA,WAAA,EAAA,KAAA,GAAA,EAEZ,QAAA,cAAa,CAAA,EAAA,MAAA,GAAA,KAAA,WAAA,EAEvB,YAKE,wBAJK,MAAA,MAAM,UAAS,EADtB,WAKE,EAAA,KAAA,GAAA,EAFQ,QAAA,eAAa,EACpB,UAAQ,cAAY,CAAA,EAAA,MAAA,GAAA,EAAA,CAAA,CAAA,CAAA;;;;;;;;;;;;;;AC3C7B,SAAgB,yBACd,MACA,OACA,MACS;AACT,MAAK,MAAM,OAAO,KAChB,KAAI,CAAC,YAAY,KAAK,MAAM,MAAM,KAAK,CAAE,QAAO;AAElD,QAAO;;AAGT,SAAgB,eACd,QACA,MACyB;AACzB,QAAO,OAAO,YAAY,KAAK,KAAI,QAAO,CAAC,KAAK,OAAO,KAAK,CAAC,CAAC;;AAGhE,SAAgB,uBACd,QACA,MACyB;AACzB,QAAO,OAAO,YACZ,KACG,QAAO,QAAO,OAAO,UAAU,eAAe,KAAK,QAAQ,IAAI,CAAC,CAChE,KAAI,QAAO,CAAC,KAAK,OAAO,KAAK,CAAC,CAClC;;AAGH,SAAgB,qBAAqB,QAA8B;AAIjE,SAHiB,OAAO,QACpB,OAAO,MAAM,SAAQ,SAAQ,KAAK,SAAS,GAC3C,OAAO,UACK,SAAQ,YAAW,QAAQ,OAAO,KAAI,UAAS,MAAM,KAAK,CAAC;;AAG7E,SAAS,YAAY,MAAe,OAAyB;AAC3D,KAAI,OAAO,GAAG,MAAM,MAAM,CAAE,QAAO;AACnC,KAAI,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,MAAM,CAC7C,QAAO,KAAK,WAAW,MAAM,UAAU,KAAK,OAAO,MAAM,UAAU,YAAY,MAAM,MAAM,OAAO,CAAC;AAErG,KAAI,SAAS,KAAK,IAAI,SAAS,MAAM,CACnC,QAAO,yBAAyB,MAAM,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,KAAK,EAAE,GAAG,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;AAE3G,QAAO;;AAGT,SAAS,SAAS,OAAkD;AAClE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AEyD7E,IAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAnD1B,SAAS,gBAAgB,QAAyC;AAChE,UAAO,EACL,UAAU,OAAO,OAAO,KAAwB,OAAO;IACrD,IAAI,EAAE;IACN,OAAO;IACP,QAAQ,EAAE;IACV,SAAS,EAAE;IACX,WAAW,EAAE;IACd,EAAE,EACL;;EAyBF,MAAM,QAAQ;EASd,MAAM,OAAO;EAMb,MAAM,WAAW,kBAAiB;EAIlC,MAAM,iBAA8B;GAClC,IAAI;GACJ,OAAO;GACP,aAAa;GACf;EAEA,MAAM,aAAa,eAAe,MAAM,WAAW,WAAU;EAG7D,MAAM,MAAM,iBAAiB,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;EACnE,MAAM,SAAS,OAAe,GAAG,IAAI,OAAO;EAC5C,MAAM,WAAW,OAAe,GAAG,IAAI,SAAS;EAEhD,MAAM,gBAAgB,eAAgD;AACpE,OAAI,MAAM,UAAU,KAAA,EAAW,QAAO,KAAA;AACtC,UAAO,sBAAsB,MAAM,MAAM,GAAG,MAAM,QAAQ,mBAAmB,MAAM,MAAK;IACzF;EACD,MAAM,mBAAmB,eACvB,MAAM,YAAY,cAAc,OAAO,SACzC;EACA,MAAM,yBAAyB,eAC7B,6BAA6B,cAAc,OAAO,kBAAkB,EAAE,EAAE,MAAM,eAAc,CAC9F;EACA,MAAM,iBAAiB,eACrB,MAAM,WACJ,iBAAiB,QACb,yBAAyB,iBAAiB,OAAO,uBAAuB,MAAK,GAC7E,KAAA,GAER;EACA,MAAM,iBAAiB,eAAe,CAAC,CAAC,eAAe,MAAK;EAC5D,MAAM,iBAAiB,gBAAyC;GAC9D,GAAI,uBAAuB,MAAM,iBAAiB,EAAE;GACpD,GAAI,MAAM,UAAU,EAAE;GACvB,EAAC;EAIF,MAAM,UAAU,eACd,eAAe,QAAQ,gBAAgB,eAAe,MAAM,GAAG,EAAE,UAAU,EAAE,EAAE,EAC/E,eAAe,OACf,MAAM,aACR;AAEA,cACQ,eAAe,QACpB,WAAW;AACV,OAAI,CAAC,QAAQ;AACX,YAAQ,aAAa,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,CAAA;AACzC;;GAGF,MAAM,aAAa,gBAAgB,OAAM;GACzC,MAAM,aAAa,qBAAqB,WAAU;GAClD,MAAM,eAAe,MAAM,WAAW,KAAA,IAClC;IACE,GAAG,eAAe;IAClB,GAAI,QAAQ,KAAK;IACnB,GACA,eAAe;AAEnB,WAAQ,aAAa,YAAY,uBAAuB,gBAAgB,EAAE,EAAE,WAAW,CAAA;KAEzF,EAAE,MAAM,MAAM,CAChB;AAEA,eACS,EAAE,GAAG,eAAe,OAAO,IACjC,SAAS;AACR,OAAI,CAAC,eAAe,MAAO;GAC3B,MAAM,aAAa,mBAAkB;AACrC,OAAI,yBAAyB,MAAM,QAAQ,KAAK,MAAiC,WAAW,CAAE;AAC9F,WAAQ,MAAM,eAAe,MAAM,WAAW,CAAA;KAEhD,EAAE,MAAM,MAAM,CAChB;AAEA,eACS,EAAE,GAAG,QAAQ,KAAK,MAAM,IAC9B,SAAS;AACR,OAAI,eAAe,MAAO,MAAK,iBAAiB,KAA+B;KAEjF,EAAE,MAAM,MAAM,CAChB;EAKA,MAAM,sBAAsB,eAC1B,eAAe,QACX,eAAe,MAAM,OAAO,QAAQ,MAAM,QAAQ,iBAAiB,EAAE,GAAG,CAAA,GACxE,EAAE,CACR;EAEA,MAAM,aAAa,eAA8B,MAAM,KAAK,IAAI,mBAAmB,CAAA;EAEnF,MAAM,UAAU,eAA8B;GAC5C,MAAM,OAAsB,eAAe,QACvC,oBAAoB,MAAM,KAAK,OAAO;IAAE,IAAI,EAAE;IAAI,OAAO,EAAE;IAAO,MAAM,EAAE;IAAM,aAAa,EAAE;IAAa,EAAC,GAC7G,WAAW;AACf,UAAO,MAAM,iBAAiB,CAAC,GAAG,MAAM,eAAe,GAAG;IAC3D;EAED,MAAM,YAAY,IAAI,QAAQ,MAAM,IAAI,MAAM,kBAAiB;EAE/D,MAAM,gBAAgB,eACpB,QAAQ,MAAM,MAAM,MAAM,EAAE,OAAO,UAAU,MAAM,CACrD;AAGA,QAAM,UAAU,SAAS;AACvB,OAAI,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,UAAU,MAAM,CAC7C,WAAU,QAAQ,KAAK,IAAI,MAAM;IAEpC;EAED,MAAM,cAAc,eAClB,oBAAoB,MAAM,MAAM,MAAM,EAAE,OAAO,UAAU,MAAM,CACjE;EAEA,MAAM,2BAA2B,eAAe;AAC9C,OAAI,CAAC,YAAY,MAAO,QAAO,EAAC;AAChC,UAAO,YAAY,MAAM,OAAO,QAAQ,MAAM,QAAQ,eAAe,EAAE,KAAK,CAAA;IAC7E;EAED,MAAM,eAAsD;GAC1D;IAAE,OAAO;IAAS,OAAO;IAAS;GAClC;IAAE,OAAO;IAAQ,OAAO;IAAQ;GAChC;IAAE,OAAO;IAAU,OAAO;IAAU;GACtC;EAEA,MAAM,iBAA2D;GAC/D;IAAE,OAAO;IAAW,OAAO;IAAW;GACtC;IAAE,OAAO;IAAU,OAAO;IAAU;GACpC;IAAE,OAAO;IAAe,OAAO;IAAe;GAChD;EAEA,SAAS,cAAc;AACrB,QAAK,qBAAqB,MAAK;AAC/B,QAAK,QAAO;;EAGd,SAAS,oBAA8B;AACrC,UAAO,QAAQ,OAAO,KAAI,UAAS,MAAM,KAAI;;EAG/C,SAAS,sBAAsB,OAAyE;AACtG,UAAO,cAAc,SAAS,oBAAoB;;;uBAKlD,YAoKY,mBAAA;IAnKT,eAAa,QAAA;IACb,OAAO,QAAA;IACP,MAAM,QAAA;IACN,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,qBAAsB,OAAM;IACpD,SAAO;;2BAuJF,CArJN,mBAqJM,OAAA,EArJA,OAAK,eAAA,CAAA,uBAAA,wBAAkD,QAAA,SAAM,CAAA,EAAA,EAAA,CAAA,CAExD,WAAA,SAAc,QAAA,MAAQ,SAAM,KAAA,WAAA,EADrC,mBAaM,OAbN,eAaM,EAAA,UAAA,KAAA,EATJ,mBAQS,UAAA,MAAA,WAPO,QAAA,QAAP,QAAG;yBADZ,mBAQS,UAAA;MANN,KAAK,IAAI;MACV,MAAK;MACJ,OAAK,eAAA,CAAA,4BAAA,EAAA,oCAAqE,UAAA,UAAc,IAAI,IAAE,CAAA,CAAA;MAC9F,UAAK,WAAE,UAAA,QAAY,IAAI;wBAErB,IAAI,MAAK,EAAA,IAAA,cAAA;mBAKH,WAAA,SAAA,WAAA,EADb,mBA6BM,OA7BN,eA6BM,EAAA,UAAA,KAAA,EAtBJ,mBAqBS,UAAA,MAAA,WApBO,QAAA,QAAP,QAAG;yBADZ,mBAqBS,UAAA;MAnBN,IAAI,MAAM,IAAI,GAAE;MAChB,KAAK,IAAI;MACV,MAAK;MACL,MAAK;MACJ,OAAK,eAAA,CAAA,kCAAA,EAAA,0CAAiF,UAAA,UAAc,IAAI,IAAE,CAAA,CAAA;MAC1G,iBAAe,UAAA,UAAc,IAAI;MACjC,iBAAe,QAAQ,IAAI,GAAE;MAC7B,UAAU,UAAA,UAAc,IAAI,KAAE,IAAA;MAC9B,UAAK,WAAE,UAAA,QAAY,IAAI;SAExB,mBAEO,QAFP,eAEO,CADO,IAAI,QAAA,WAAA,EAAhB,mBAA0C,QAAA;;MAApB,WAAQ,IAAI;mEAEpC,mBAKO,QALP,eAKO,CAJL,mBAAyE,QAAzE,eAAyE,gBAAnB,IAAI,MAAK,EAAA,EAAA,EACnD,IAAI,eAAA,WAAA,EAAhB,mBAEO,QAFP,eAEO,gBADF,IAAI,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,EAAA,IAAA,cAAA;+DAO1B,YAoGY,wBAnGL,WAAA,QAAU,YAAA,MAAA,EAAA;KACd,OAAK,eAAE,WAAA,QAAU,8BAAA,+BAAA;KACjB,IAAI,WAAA,SAAc,cAAA,QAAgB,QAAQ,cAAA,MAAc,GAAE,GAAI,KAAA;KAC9D,MAAM,WAAA,QAAU,aAAgB,KAAA;KAChC,mBAAiB,WAAA,SAAc,cAAA,QAAgB,MAAM,cAAA,MAAc,GAAE,GAAI,KAAA;KACzE,UAAU,WAAA,QAAU,IAAO,KAAA;;4BAUnB,CAPD,WAAA,SAAc,cAAA,OAAe,SAAA,WAAA,EADrC,mBAQS,UART,gBAQS,CAJP,mBAA0E,MAA1E,gBAA0E,gBAA3B,cAAA,MAAc,MAAK,EAAA,EAAA,EACzD,cAAA,MAAc,eAAA,WAAA,EAAvB,mBAEI,KAFJ,gBAEI,gBADC,cAAA,MAAc,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,EAIhC,mBAiFM,OAAA,EAjFA,OAAK,eAAE,WAAA,QAAU,mCAAA,KAAA,EAAA,EAAA,CAEb,eAAA,SAAkB,YAAA,SAAA,WAAA,EAD1B,mBAqBM,OAAA;;MAnBJ,OAAM;MACL,OAAK,eAAA,EAAA,wBAA4B,YAAA,MAAY,WAAO,GAAA,CAAA;2BAErD,mBAeW,UAAA,MAAA,WAfe,yBAAA,QAAT,UAAK;0BACpB,mBAaM,OAAA;YAdkD,MAAM;OACxD,OAAK,eAAE,MAAM,UAAO,EAAA,YAAA,QAAyB,MAAM,WAAO,GAAO,KAAA,EAAS;UAC9E,WAWO,KAAA,QAAA,SAVW,MAAM,QAAI;OAClB;OACP,MAAM,MAAA,QAAO,CAAC;OACd,YAAa,MAAA,QAAO,CAAC,KAAK,cAAc,MAAM,KAAI;eAO9C,CALL,YAIE,mCAAA;OAHQ;OACP,kBAAgB,MAAA,QAAO,CAAC,sBAAsB,MAAK;OACnD,MAAM,MAAA,QAAO,CAAC;;;;;;wBAOH,eAAA,SAAA,UAAA,KAAA,EACpB,mBAIW,UAAA,EAAA,KAAA,GAAA,EAAA,WAJa,WAAA,QAAP,QAAG;0CAClB,mBAEM,OAAA,EAAA,KAHkC,IAAI,IAAA,EAAA,CAE1C,WAAgC,KAAA,QAAA,OAAZ,IAAI,KAAE,CAAA,EAAA,IAAA,GAAA,CAAA,CAAA,OADf,UAAA,UAAc,IAAI,GAAE,CAAA,CAAA;+CAM1B,QAAA,iBAAA,gBAAA,WAAA,EAAX,mBAgDM,OAAA,gBAAA;MA/CJ,mBAaM,OAbN,gBAaM,CAAA,OAAA,OAAA,OAAA,KAZJ,mBAA2D,OAAA,EAAtD,OAAM,sCAAoC,EAAC,SAAK,GAAA,GACrD,mBAUM,OAVN,gBAUM,EAAA,WAAA,EATJ,mBAQS,UAAA,MAAA,WAPO,eAAP,QAAG;cADZ,mBAQS,UAAA;QANN,KAAK,IAAI;QACV,MAAK;QACJ,OAAK,eAAA,CAAA,mCAAA,EAAA,2CAAmF,MAAA,SAAQ,CAAC,UAAU,IAAI,OAAK,CAAA,CAAA;QACpH,UAAK,WAAE,MAAA,SAAQ,CAAC,QAAQ,IAAI;0BAE1B,IAAI,MAAK,EAAA,IAAA,eAAA;;MAKlB,mBAaM,OAbN,gBAaM,CAAA,OAAA,OAAA,OAAA,KAZJ,mBAAmE,OAAA,EAA9D,OAAM,sCAAoC,EAAC,iBAAa,GAAA,GAC7D,mBAUM,OAVN,gBAUM,EAAA,UAAA,KAAA,EATJ,mBAQS,UAAA,MAAA,WAPkB,MAAA,cAAa,GAA9B,SAAS,QAAG;2BADtB,mBAQS,UAAA;QAND;QACN,MAAK;QACJ,OAAK,eAAA,CAAA,mCAAA,EAAA,2CAAmF,MAAA,SAAQ,CAAC,iBAAiB,KAAG,CAAA,CAAA;QACrH,UAAK,WAAE,MAAA,SAAQ,CAAC,eAAe;0BAE7B,QAAQ,KAAI,EAAA,IAAA,eAAA;;MAKrB,mBAcM,OAdN,gBAcM;iCAbJ,mBAAmE,OAAA,EAA9D,OAAM,sCAAoC,EAAC,iBAAa,GAAA;OAC7D,mBAUM,OAVN,gBAUM,EAAA,WAAA,EATJ,mBAQS,UAAA,MAAA,WAPO,iBAAP,QAAG;eADZ,mBAQS,UAAA;SANN,KAAK,IAAI;SACV,MAAK;SACJ,OAAK,eAAA,CAAA,mCAAA,EAAA,2CAAmF,MAAA,SAAQ,CAAC,iBAAiB,IAAI,OAAK,CAAA,CAAA;SAC3H,UAAK,WAAE,MAAA,SAAQ,CAAC,eAAe,IAAI;2BAEjC,IAAI,MAAK,EAAA,IAAA,eAAA;;iCAGhB,mBAA2E,KAAA,EAAxE,OAAM,6BAA2B,EAAC,sCAAkC,GAAA;;MAGzE,WAA0B,KAAA,QAAA,aAAA;wBA/CO,UAAA,UAAc,kBAAiB,CAAA,CAAA,GAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,EAAA,CAAA,CAAA;;;;;;;;cAoD7DC,KAAAA,OAAO,UAAA,WAAA,EAAlB,mBAMM,OANN,eAMM,CALJ,WAIE,KAAA,QAAA,UAAA;KAFC,QAAQ,MAAA,QAAO,CAAC,KAAK;KACrB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE/YhB,MAAM,OAAO;EAMb,SAAS,eAAe;AACtB,QAAK,qBAAqB,MAAK;AAC/B,QAAK,SAAQ;;EAGf,SAAS,gBAAgB;AACvB,QAAK,UAAS;;;uBAKd,YA+CY,mBAAA;IA9CT,eAAa,QAAA;IACb,OAAO,QAAA;IACP,UAAU,QAAA;IACX,MAAK;IACJ,UAAQ,CAAG,QAAA;IACX,oBAAgB,CAAG,QAAA;IACnB,mBAAe,CAAG,QAAA;IAClB,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,qBAAsB,OAAM;;IAe1C,QAAM,cAsBT,CArBN,mBAqBM,OArBN,eAqBM,CApBJ,mBAOS,UAAA;KANP,MAAK;KACL,OAAM;KACL,UAAU,QAAA;KACV,SAAO;uBAEL,QAAA,YAAW,EAAA,GAAA,cAAA,EAEhB,mBAWS,UAAA;KAVP,MAAK;KACJ,OAAK,eAAA,CAAA,6BAAA,8BAA8D,QAAA,UAAO,CAAA;KAC1E,UAAU,QAAA;KACV,SAAO;QAEG,QAAA,WAAA,WAAA,EAAX,mBAGM,OAHN,eAGM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAFJ,mBAA8F,UAAA;KAAtF,OAAA,EAAA,WAAA,QAAqB;KAAC,IAAG;KAAK,IAAG;KAAK,GAAE;KAAK,QAAO;KAAe,gBAAa;kBACxF,mBAAsK,QAAA;KAAhK,OAAA,EAAA,WAAA,QAAqB;KAAC,MAAK;KAAe,GAAE;sEAC9C,MACN,gBAAG,QAAA,aAAY,EAAA,EAAA,CAAA,EAAA,IAAA,cAAA,CAAA,CAAA,CAAA,CAAA;2BAtBf,CAXN,mBAWM,OAXN,eAWM;KAPIC,KAAAA,OAAO,QAAA,WAAA,EADf,mBAKM,OAAA;;MAHH,OAAK,eAAA,CAAA,sBAAA,uBAAgD,QAAA,UAAO,CAAA;SAE7D,WAAoB,KAAA,QAAA,OAAA,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAEb,QAAA,WAAA,WAAA,EAAT,mBAAiE,KAAjE,eAAiE,gBAAd,QAAA,QAAO,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAC1D,WAAQ,KAAA,QAAA,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEtCd,MAAM,QAAQ;EAQd,MAAM,OAAO;EAMb,MAAM,EAAE,QAAQ,SAAS,YAAY,OAAO,WAAW,iBAAiB,EACtE,eAAe,OAChB,CAAA;EACD,MAAM,cAAc,IAAI,MAAK;EAC7B,MAAM,cAAc,IAAI,MAAK;EAE7B,SAAS,eAAe;AACtB,QAAK,SAAQ;AACb,UAAM;;EAGR,SAAS,aAAa;AACpB,OAAI,MAAM,gBAAgB,MAAM,YAAa;AAC7C,OAAI,MAAM,YACR,aAAY,QAAQ;OAEpB,MAAK,OAAM;;EAIf,SAAS,oBAAoB;AAC3B,eAAY,QAAQ;AACpB,QAAK,OAAM;;EAGb,SAAS,eAAe;AACtB,QAAK,SAAQ;AACb,UAAM;;EAGR,IAAI,eAAqD;AAGzD,cAAY,MAAM,qBAAqB,QAAQ;AAC7C,OAAI,aAAc,cAAa,aAAY;AAC3C,OAAI,KAAK;AACP,gBAAY,QAAQ;AACpB,mBAAe,iBAAiB;AAC9B,iBAAY,QAAQ;AACpB,oBAAe;OACd,IAAI;;IAEV;AAED,oBAAkB;AAChB,OAAI,aAAc,cAAa,aAAY;IAC5C;;uBAIC,mBAgIM,OAAA;aAhIG;IAAJ,KAAI;IAAa,OAAM;;IAE1B,mBA8DM,OAAA,EA7DH,OAAK,eAAA,CAAA,kCAAA,EAAA,6CAAqG,QAAA,YAAY,QAAA,gBAAc,CAAA,CAAA,EAAA,EAAA,CAOrI,mBA2BS,UAAA;KA1BP,MAAK;KACJ,OAAK,eAAA;;oDAA0G,MAAA,OAAM,EAAA;oDAA4D,QAAA,kBAAc,CAAK,QAAA,gBAAc;;KAKlN,OAAO,QAAA,kBAAkB,KAAA;KACzB,SAAK,OAAA,OAAA,OAAA,KAAA,eAAA,GAAA,SAAO,MAAA,OAAA,IAAA,MAAA,OAAA,CAAA,GAAA,KAAM,EAAA,CAAA,OAAA,CAAA;;+BAGnB,mBAOM,OAAA;MAPD,OAAM;MAAwC,MAAK;MAAO,QAAO;MAAe,SAAQ;SAC3F,mBAKE,QAAA;MAJA,kBAAe;MACf,mBAAgB;MAChB,gBAAa;MACb,GAAE;;KAIM,QAAA,kBAAA,WAAA,EAAZ,mBAAqG,QAArG,eAAqG,gBAAxB,QAAA,eAAc,EAAA,EAAA,IAC1E,QAAA,kBAAA,WAAA,EAAjB,mBAA0G,QAA1G,eAA0G,gBAAxB,QAAA,eAAc,EAAA,EAAA,KAAA,WAAA,EAChG,mBAA+E,QAA/E,eAA2D,gBAAa;+BAExE,mBAEM,OAAA;MAFD,OAAM;MAA2C,SAAQ;MAAY,MAAK;MAAO,QAAO;MAAe,gBAAa;MAAI,kBAAe;MAAQ,mBAAgB;SAClK,mBAAyB,QAAA,EAAnB,GAAE,gBAAc,CAAA,CAAA,EAAA,GAAA;2BAMlB,QAAA,YAAY,QAAA,kBAAA,WAAA,EADpB,mBAuBS,UAAA;;KArBP,MAAK;KACJ,OAAK,eAAA;;0DAAqH,QAAA,aAAW;0DAAkE,YAAA,OAAW;2DAAmE,QAAA,gBAAY,CAAK,YAAA,OAAW;;KAMjT,UAAU,QAAA,gBAAY,CAAK,YAAA;KAC3B,OAAO,QAAA,gBAAgB,QAAA,sBAAsB,QAAA,sBAAsB,YAAA,SAAe,QAAA,qBAAqB,QAAA,qBAAkB;KACzH,SAAK,cAAO,YAAU,CAAA,OAAA,CAAA;QAGX,QAAA,eAAA,WAAA,EAAZ,mBAA4E,QAA5E,cAA4E,IAE5D,YAAA,SAAA,WAAA,EAAhB,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAA6F,QAAA;KAAvF,kBAAe;KAAQ,mBAAgB;KAAQ,gBAAa;KAAM,GAAE;sCAG5E,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAAwK,QAAA;KAAlK,kBAAe;KAAQ,mBAAgB;KAAQ,gBAAa;KAAI,GAAE;;IAMnE,MAAA,OAAM,IAAA,WAAA,EAAjB,mBAiDM,OAjDN,eAiDM,CA/CJ,mBAKM,OALN,gBAKM,CAAA,OAAA,OAAA,OAAA,KAJJ,mBAA4D,OAAA,EAAvD,OAAM,kCAAgC,EAAC,cAAU,GAAA,GACtD,mBAEM,OAFN,gBAEM,gBADD,QAAA,iBAAc,8BAAA,6BAAA,EAAA,EAAA,CAAA,CAAA,EAAA,CAKT,QAAA,kBAAA,WAAA,EAAZ,mBAOM,OAPN,gBAOM,CANJ,mBAKS,UAAA;KALD,MAAK;KAAS,OAAM;KAAuC,SAAO;sCACxE,mBAEM,OAAA;KAFD,OAAM;KAAK,QAAO;KAAK,MAAK;KAAO,QAAO;KAAe,SAAQ;QACpE,mBAA2F,QAAA;KAArF,kBAAe;KAAQ,mBAAgB;KAAQ,gBAAa;KAAI,GAAE;8BACpE,uBAER,GAAA,CAAA,EAAA,CAAA,CAAA,CAAA,KAAA,WAAA,EAIF,mBA4BM,OA5BN,gBA4BM,CA3BJ,mBAkBM,OAlBN,gBAkBM,CAAA,OAAA,OAAA,OAAA,KAjBJ,mBASM,OAAA,EATD,OAAM,sCAAoC,EAAA,CAC7C,mBAOM,OAAA;KAPD,MAAK;KAAO,QAAO;KAAe,SAAQ;QAC7C,mBAKE,QAAA;KAJA,kBAAe;KACf,mBAAgB;KAChB,gBAAa;KACb,GAAE;iBAIR,mBAMM,OANN,gBAMM;KALQ,QAAA,kBAAA,WAAA,EAAZ,mBAAkG,QAAlG,gBAAkG,gBAAxB,QAAA,eAAc,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KACxF,mBAA0E,OAA1E,gBAA0E,gBAAvB,QAAA,eAAc,EAAA,EAAA;KACtD,QAAA,oBAAA,WAAA,EAAX,mBAEM,OAFN,gBAEM,gBADD,MAAA,uBAAsB,CAAC,QAAA,iBAAgB,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;UAIhD,mBAOM,OAPN,gBAOM,CANJ,mBAES,UAAA;KAFD,MAAK;KAAS,OAAM;KAAuC,SAAO;OAAc,WAExF,EACc,QAAA,cAAA,WAAA,EAAd,mBAES,UAAA;;KAFiB,MAAK;KAAS,OAAM;KAAuC,SAAO;OAAc,WAE1G,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAMN,YAQE,uBAAA;iBAPS,YAAA;8EAAW,QAAA;KACnB,OAAO,QAAA,gBAAY;KACnB,SAAS,QAAA,kBAAc,wBAA4B,QAAA,eAAc;KAClE,SAAQ;KACR,iBAAc;KACb,SAAS,QAAA;KACT,WAAS;;;;;;;;;;;;;;;;;;;;;;;EEzMhB,MAAM,QAAQ;EAKd,MAAM,QAAQ,eAAe;GAC3B,MAAM,IAA4B,EAAC;AAEnC,OAAI,MAAM,MACR,GAAE,QAAQ,OAAO,MAAM,UAAU,WAAW,GAAG,MAAM,MAAM,MAAM,MAAM;AAGzE,OAAI,MAAM,OACR,GAAE,SAAS,OAAO,MAAM,WAAW,WAAW,GAAG,MAAM,OAAO,MAAM,MAAM;AAG5E,UAAO;IACR;EAED,MAAM,UAAU,eAAe;GAC7B,MAAM,OAAO,CAAC,cAAa;AAE3B,WAAQ,MAAM,SAAd;IACE,KAAK;AACH,UAAK,KAAK,eAAc;AACxB,SAAI,CAAC,MAAM,SAAS,CAAC,MAAM,OACzB,MAAK,KAAK,YAAW;AAEvB;IACF,KAAK;AACH,UAAK,KAAK,eAAc;AACxB,SAAI,CAAC,MAAM,OAAQ,MAAK,KAAK,OAAM;AACnC;IACF,KAAK;AACH,UAAK,KAAK,eAAc;AACxB,SAAI,CAAC,MAAM,OAAQ,MAAK,KAAK,OAAM;AACnC;IACF;AACE,UAAK,KAAK,UAAS;AACnB,SAAI,CAAC,MAAM,OAAQ,MAAK,KAAK,MAAK;AAClC,SAAI,CAAC,MAAM,MAAO,MAAK,KAAK,SAAQ;;AAGxC,WAAQ,MAAM,WAAd;IACE,KAAK;AACH,UAAK,KAAK,gBAAe;AACzB;IACF,KAAK;AACH,UAAK,KAAK,gBAAe;AACzB;;AAGJ,UAAO;IACR;;uBAIC,mBAAuC,OAAA;IAAjC,OAAK,eAAE,QAAA,MAAO;IAAG,OAAK,eAAE,MAAA,MAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEhDrC,MAAM,OAAO;EAIb,MAAM,mBAAmB;GACvB;GACA;GACA;GACA;GACF;;uBAIE,mBAgCM,OAAA,EAhCA,OAAK,eAAA;IAAA;IAAA,qBAA4C,QAAA;IAAO,qBAAyB,QAAA;IAAI,CAAA,EAAA,EAAA;IACzF,mBAoBM,OAAA,EApBA,OAAK,eAAA,CAAA,kCAAA,mCAAwE,QAAA,QAAK,CAAA,EAAA,EAAA,CACtF,WAkBO,KAAA,QAAA,QAAA,EAAA,QAAA,EAAA,WAAA,EAjBL,mBAgBM,OAAA;KAfJ,OAAK,eAAA,CAAC,0BAAwB,2BACK,QAAA,QAAK,CAAA;KACxC,SAAQ;KACR,MAAK;KACL,QAAO;KACP,gBAAa;KACb,kBAAe;KACf,mBAAgB;QAEA,QAAA,YAAA,WAAA,EACd,mBAAsB,QAAA;;KAAf,GAAG,QAAA;gDAGV,mBAA2D,UAAA,EAAA,KAAA,GAAA,EAAA,WAApC,mBAAT,GAAG,MAAC;YAAlB,mBAA2D,QAAA;MAAjB,KAAK;MAAO;;;IAK9D,mBAIM,OAJN,eAIM;KAHM,QAAA,SAAA,WAAA,EAAV,mBAAiE,MAAjE,eAAiE,gBAAb,QAAA,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAChD,QAAA,eAAA,WAAA,EAAT,mBAAiF,KAAjF,eAAiF,gBAAlB,QAAA,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAC1E,WAAQ,KAAA,QAAA,UAAA;;IAEC,QAAA,eAAA,WAAA,EAAX,mBAIM,OAJN,eAIM,CAHJ,YAEa,oBAAA,EAFA,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAA,GAAA,EAAA;4BACL,CAAA,gBAAA,gBAAd,QAAA,YAAW,EAAA,EAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEpDtB,MAAM,QAAQ;EAKd,MAAM,OAAO;EAIb,MAAM,SAAS,IAAI,MAAK;EACxB,IAAI,cAAoD;EAExD,eAAe,aAAa;AAC1B,OAAI,CAAC,MAAM,SAAU;AACrB,OAAI;AACF,UAAM,UAAU,UAAU,UAAU,MAAM,KAAI;AAC9C,WAAO,QAAQ;AACf,SAAK,QAAQ,MAAM,KAAI;AACvB,QAAI,YAAa,cAAa,YAAW;AACzC,kBAAc,iBAAiB;AAAE,YAAO,QAAQ;OAAS,KAAI;WACvD;;EAKV,SAAS,cAAc,OAAsB;AAC3C,OAAI,CAAC,MAAM,SAAU;AACrB,OAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC9C,UAAM,gBAAe;AACrB,gBAAW;;;;uBAMb,mBAaO,QAAA;IAZJ,OAAK,eAAA;;uBAAmD,QAAA;;iCAA2C,QAAA;MAAQ,yBAA2B,OAAA;MAAM;;IAK5I,MAAM,QAAA,WAAQ,WAAc,KAAA;IAC5B,UAAU,QAAA,WAAQ,IAAO,KAAA;IACzB,OAAO,QAAA,WAAY,OAAA,QAAM,YAAA,kBAAkC,KAAA;IAC3D,SAAO;IACP,WAAS;sBAEP,OAAA,QAAM,YAAe,QAAA,KAAI,EAAA,IAAA,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1BhC,MAAM,QAAQ;EAQd,MAAM,OAAO;EAMb,MAAM,EACJ,aACA,SACA,WACA,OACA,SACA,iBACA,UACA,kBACA,OAAO,kBACP,uBACE,sBAAsB,EACxB,gBAAgB,MAAM,gBACvB,CAAA;EAED,MAAM,cAAc,IAAI,GAAE;EAC1B,MAAM,UAAU,IAAwB,KAAI;EAC5C,MAAM,eAAe,IAAI,MAAM,YAAW;EAC1C,MAAM,cAAc,IAAI,MAAM,eAAc;EAG5C,MAAM,2BAA2B,eAC/B,CAAC,EAAE,QAAQ,WAAW,QAAQ,kBAAkB,QAAQ,cAAc,QAAQ,UAAU,mBAC1F;EAGA,MAAM,oBAAoB,eAAe,CACvC;GAAE,OAAO;GAAI,OAAO;GAAa,EACjC,GAAG,gBAAgB,MAAM,KAAI,OAAM;GAAE,OAAO,EAAE;GAAO,OAAO,EAAE;GAAO,EAAE,CACxE,CAAA;EAGD,MAAM,uBAAuB,eAAe,CAC1C;GAAE,OAAO;GAAI,OAAO;GAAgB,EACpC,GAAG,SAAS,MACb,CAAA;EAGD,MAAM,kBAAkB,eAAe;AACrC,OAAI,CAAC,YAAY,MAAO,QAAO,YAAY;AAC3C,UAAO,iBAAiB,MAAM,SAAS,GAAG,UAAU,KAAI;IACzD;EAED,SAAS,UAA6C,KAAQ,OAAwB;AAClF,WAAoC,OAAO,OAAO,MAAM,IAAI,KAAA;;EAGhE,SAAS,iBAAiB,OAAwB;AAChD,WAAQ,QAAQ,OAAO,MAAM,IAAI;;EAGnC,SAAS,aAAa,YAA+B;AACnD,QAAK,UAAU,WAAU;AACzB,QAAK,qBAAqB,MAAK;;EAGjC,SAAS,iBAAiB;AACxB,QAAK,WAAU;AACf,QAAK,qBAAqB,MAAK;;EAGjC,SAAS,cAAc,OAAsB;GAC3C,MAAM,OAAO,gBAAgB;AAC7B,OAAI,CAAC,KAAK,OAAQ;AAElB,WAAQ,MAAM,KAAd;IACE,KAAK;AACH,WAAM,gBAAe;AACrB,iBAAY,QAAQ,KAAK,IAAI,YAAY,QAAQ,GAAG,KAAK,SAAS,EAAC;AACnE,2BAAqB;AACrB;IACF,KAAK;AACH,WAAM,gBAAe;AACrB,iBAAY,QAAQ,KAAK,IAAI,YAAY,QAAQ,GAAG,EAAC;AACrD,2BAAqB;AACrB;IACF,KAAK;AACH,WAAM,gBAAe;AACrB,SAAI,YAAY,SAAS,KAAK,YAAY,QAAQ,KAAK,OACrD,cAAa,KAAK,YAAY,OAAM;AAEtC;;;EAIN,SAAS,uBAAuB;AAC9B,kBAAe;AAEb,KADY,QAAQ,OAAO,cAAc,0CAAyC,GAC7E,eAAe,EAAE,OAAO,WAAW,CAAA;KACzC;;EAIH,MAAM,eAAe,eAAe;GAClC,MAAM,sBAAM,IAAI,KAAoB;AACpC,mBAAgB,MAAM,SAAS,KAAK,MAAM,IAAI,IAAI,IAAI,IAAI,EAAE,CAAA;AAC5D,UAAO;IACR;EAED,SAAS,aAAa,YAAuC;AAC3D,UAAO,aAAa,MAAM,IAAI,WAAW,GAAG,IAAI;;EAIlD,MAAM,kBAAkB,yBAAS,IAAI,KAAa,CAAA;EAElD,SAAS,YAAY,WAAmB;AACtC,OAAI,gBAAgB,IAAI,UAAU,CAChC,iBAAgB,OAAO,UAAS;OAEhC,iBAAgB,IAAI,UAAS;;AAKjC,QAAM,mBAAmB;AAAE,eAAY,QAAQ;IAAI;AAGnD,cACQ,MAAM,aACX,WAAW;AACV,OAAI,QAAQ;AACV,gBAAY,QAAQ;AACpB,oBAAgB,OAAM;AACtB,wBAAmB;AACnB,sBAAiB;;IAGvB;;uBAIE,YA6MY,mBAAA;IA5MT,eAAa,QAAA;IACb,OAAO,QAAA;IACP,MAAM,QAAA;IACN,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,qBAAsB,OAAM;;2BAwM/C,CAtMN,mBAsMM,OAAA;KAtMD,OAAM;KAA4B,WAAS;;KAE9C,mBAsCM,OAtCN,eAsCM;MArCJ,mBAOM,OAPN,eAOM,CANJ,YAKE,mBAAA;mBAJS,MAAA,QAAO,CAAC;0EAAR,QAAO,CAAC,SAAM;OACvB,aAAY;OACZ,MAAK;OACL,MAAK;;MAGT,mBAOM,OAPN,eAOM,CANJ,YAKE,oBAAA;OAJC,eAAa,MAAA,QAAO,CAAC,UAAM;OAC3B,SAAS,MAAA,0BAAyB;OACnC,MAAK;OACJ,uBAAkB,OAAA,OAAA,OAAA,MAAE,MAAK,UAAS,UAAW,EAAC;;MAGxC,kBAAA,MAAkB,SAAM,KAAA,WAAA,EAAnC,mBAOM,OAPN,eAOM,CANJ,YAKE,oBAAA;OAJC,eAAa,MAAA,QAAO,CAAC,kBAAc;OACnC,SAAS,kBAAA;OACV,MAAK;OACJ,uBAAkB,OAAA,OAAA,OAAA,MAAE,MAAK,UAAS,kBAAmB,EAAC;;MAG3D,mBAYS,UAAA;OAXP,OAAK,eAAA,CAAC,4CAA0C,EAAA,oDACc,yBAAA,OAAwB,CAAA,CAAA;OACtF,MAAK;OACJ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,aAAA,QAAY,CAAI,aAAA;;iCAExB,mBAGM,OAAA;QAHD,OAAM;QAAK,QAAO;QAAK,SAAQ;QAAY,MAAK;QAAO,QAAO;QAAe,gBAAa;QAAI,kBAAe;QAAQ,mBAAgB;;QACxI,mBAAqC,QAAA;SAA/B,IAAG;SAAI,IAAG;SAAI,IAAG;SAAK,IAAG;;QAAM,mBAAuC,QAAA;SAAjC,IAAG;SAAI,IAAG;SAAK,IAAG;SAAK,IAAG;;QAAO,mBAAwC,QAAA;SAAlC,IAAG;SAAK,IAAG;SAAK,IAAG;SAAK,IAAG;;QAC7G,mBAA+B,UAAA;SAAvB,IAAG;SAAI,IAAG;SAAK,GAAE;;QAAM,mBAAgC,UAAA;SAAxB,IAAG;SAAK,IAAG;SAAK,GAAE;;QAAM,mBAA8B,UAAA;SAAtB,IAAG;SAAI,IAAG;SAAI,GAAE;;;iDACnF,aAEN,GAAA;OAAY,yBAAA,SAAA,WAAA,EAAZ,mBAAsF,QAAtF,cAAsF,IAAA,mBAAA,IAAA,KAAA;;;KAK/E,aAAA,SAAA,WAAA,EAAX,mBAiCM,OAjCN,eAiCM;MAhCO,qBAAA,MAAqB,SAAM,KAAA,WAAA,EAAtC,mBAOM,OAPN,eAOM,CANJ,YAKE,oBAAA;OAJC,eAAa,MAAA,QAAO,CAAC,WAAO;OAC5B,SAAS,qBAAA;OACV,MAAK;OACJ,uBAAkB,OAAA,OAAA,OAAA,MAAE,MAAK,UAAS,WAAY,EAAC;;MAGpD,mBAOM,OAPN,eAOM,CANJ,YAKE,oBAAA;OAJC,eAAa,MAAA,QAAO,CAAC,cAAU;OAC/B,SAAS,MAAA,oBAAmB;OAC7B,MAAK;OACJ,uBAAkB,OAAA,OAAA,OAAA,MAAE,MAAK,UAAS,cAAe,EAAC;;MAGvD,mBAOM,OAPN,eAOM,CANJ,YAKE,oBAAA;OAJC,eAAa,MAAA,QAAO;OACpB,SAAS,MAAA,aAAY;OACtB,MAAK;OACJ,uBAAoB;;MAGzB,mBAOQ,SAPR,gBAOQ,CAAA,eANN,mBAIE,SAAA;gFAHoB,QAAA;OACpB,MAAK;OACL,OAAM;uCAFG,YAAA,MAAW,CAAA,CAAA,EAAA,OAAA,QAAA,OAAA,MAAA,gBAGpB,sBAEJ,GAAA,EAAA,CAAA;;KAIS,MAAA,UAAS,IAAA,WAAA,EAApB,mBAQM,OARN,gBAQM,EAAA,WAAA,EAPJ,mBAMM,UAAA,MAAA,WANW,IAAL,MAAC;aAAb,mBAMM,OAAA;OANe,KAAK;OAAG,OAAM;UACjC,mBAGM,OAHN,gBAGM,CAFJ,YAAgD,kBAAA;OAArC,OAAK,MAAQ,IAAC;OAAO,QAAO;8BACvC,YAAuC,kBAAA;OAA7B,OAAM;OAAO,QAAO;YAEhC,YAAyD,kBAAA;OAA/C,OAAM;OAAO,QAAO;OAAO,SAAQ;;mBAKjC,MAAA,MAAK,IAAA,WAAA,EAArB,mBAEM,OAFN,gBAEM,gBADD,MAAA,MAAK,CAAA,EAAA,EAAA,IAKG,MAAA,YAAW,CAAC,WAAM,KAAA,WAAA,EAD/B,YAKE,oBAAA;;MAHA,OAAM;MACN,aAAY;MACZ,MAAK;WAIS,YAAA,SAAA,WAAA,EAAhB,mBAiDM,OAAA;;eAjD2B;MAAJ,KAAI;MAAU,OAAM;2BAC/C,mBA+CW,UAAA,MAAA,WA/CkC,MAAA,iBAAgB,GAAA,CAA1C,WAAW,eAAS;8DAA8B,WAAS,EAAA,CAC5E,mBAcS,UAAA;OAbP,MAAK;OACL,OAAM;OACL,UAAK,WAAE,YAAY,UAAS;;qBAE7B,mBAMM,OAAA;QALJ,OAAK,eAAA,CAAC,2CAAyC,EAAA,sDACiB,gBAAgB,IAAI,UAAS,EAAA,CAAA,CAAA;QAC7F,OAAM;QAAK,QAAO;QAAK,SAAQ;QAAY,MAAK;QAAO,QAAO;QAAe,gBAAa;QAAI,kBAAe;QAAQ,mBAAgB;2CAErI,mBAAoC,YAAA,EAA1B,QAAO,kBAAgB,EAAA,MAAA,GAAA,CAAA,EAAA,EAAA,EAAA;OAEnC,mBAAyE,QAAzE,gBAAyE,gBAAnB,UAAS,EAAA,EAAA;OAC/D,mBAAiF,QAAjF,gBAAiF,gBAA1B,UAAU,OAAM,EAAA,EAAA;8BAExD,gBAAgB,IAAI,UAAS,IAAA,UAAA,KAAA,EAC5C,mBA4BM,UAAA,EAAA,KAAA,GAAA,EAAA,WA3BU,YAAP,QAAG;2BADZ,mBA4BM,OAAA;QA1BH,KAAK,IAAI;QACV,OAAK,eAAA,CAAC,iCAA+B;kDAC8B,IAAI,OAAO,QAAA;mDAA+E,aAAa,IAAG,KAAM,YAAA;;QAIlL,UAAK,WAAE,aAAa,IAAG;QACvB,eAAU,WAAE,YAAA,QAAc,aAAa,IAAG;WAE3C,mBAaM,OAbN,gBAaM,CAZJ,mBAQM,OARN,gBAQM,CAAA,gBAAA,gBAPD,IAAI,KAAI,GAAG,KACd,EAAA,EACQ,IAAI,mBAAA,WAAA,EADZ,YAKE,6BAAA;;QAHC,MAAM,IAAI;QACX,MAAK;QACJ,UAAU;gEAGf,mBAEM,OAFN,gBAEM,CADJ,mBAAuD,QAAA,MAAA,gBAA9C,MAAA,qBAAoB,CAAC,IAAI,WAAU,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,EAGhD,YAEW,kBAAA;QAFA,SAAS,MAAA,2BAA0B,CAAC,IAAI,OAAM;QAAG,MAAK;;+BACvB,CAAA,gBAAA,gBAArC,MAAA,uBAAsB,CAAC,IAAI,OAAM,CAAA,EAAA,EAAA,CAAA,CAAA;;;;uCAQ9C,mBA+BM,OAAA;;eA/BU;MAAJ,KAAI;MAAU,OAAM;2BAC9B,mBA6BM,UAAA,MAAA,WA5BiB,MAAA,YAAW,GAAxB,KAAK,QAAG;0BADlB,mBA6BM,OAAA;OA3BH,KAAK,IAAI;OACV,OAAK,eAAA,CAAC,iCAA+B;iDAC0B,IAAI,OAAO,QAAA;kDAA2E,QAAQ,YAAA;;OAI5J,UAAK,WAAE,aAAa,IAAG;OACvB,eAAU,WAAE,YAAA,QAAc;UAE3B,mBAcM,OAdN,gBAcM,CAbJ,mBAQM,OARN,eAQM,CAAA,gBAAA,gBAPD,IAAI,KAAI,GAAG,KACd,EAAA,EACQ,IAAI,mBAAA,WAAA,EADZ,YAKE,6BAAA;;OAHC,MAAM,IAAI;OACX,MAAK;OACJ,UAAU;+DAGf,mBAGM,OAHN,eAGM,CAFQ,IAAI,gBAAgB,IAAI,WAAA,WAAA,EAApC,mBAAyF,QAAA,eAAA,gBAAzC,IAAI,gBAAgB,IAAI,QAAO,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,EAC/E,mBAAuD,QAAA,MAAA,gBAA9C,MAAA,qBAAoB,CAAC,IAAI,WAAU,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,EAGhD,YAEW,kBAAA;OAFA,SAAS,MAAA,2BAA0B,CAAC,IAAI,OAAM;OAAG,MAAK;;8BACvB,CAAA,gBAAA,gBAArC,MAAA,uBAAsB,CAAC,IAAI,OAAM,CAAA,EAAA,EAAA,CAAA,CAAA;;;;KAM/B,QAAA,uBAAmB,QAAA,WAAA,EAA9B,mBAQM,OARN,eAQM,CAPJ,mBAMS,UAAA;MALP,MAAK;MACL,OAAM;MACL,SAAO;QACT,oBAED,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;ACnXR,IAAa,4BAA4B;AAEzC,IAAM,aAAa;AACnB,IAAM,wBAAwB;AAE9B,SAAgB,0BAA0B,MAAuB;AAC/D,SAAQ,QAAQ,IAAI,QAAQ,WAAW,GAAG;;AAG5C,SAAgB,iBAAiB,MAAmC;CAClE,MAAM,MAAM,0BAA0B,KAAK;AAC3C,KAAI,CAAC,IAAK,QAAO;EAAE,QAAQ;EAAY,OAAO;EAA2B;AACzE,KAAI,WAAW,KAAK,IAAI,CAAE,QAAO;EAAE,QAAQ;EAAQ,OAAO;EAAK;AAC/D,KAAI,sBAAsB,KAAK,IAAI,CAAE,QAAO;EAAE,QAAQ;EAAY,OAAO;EAAK;AAC9E,KAAI,IAAI,WAAW,WAAW,CAAE,QAAO;EAAE,QAAQ;EAAa,OAAO;EAAK;AAC1E,QAAO;EAAE,QAAQ;EAAY,OAAO;EAA2B;;AAGjE,SAAgB,mBAAmB,MAAwB;CACzD,MAAM,MAAM,0BAA0B,KAAK;AAC3C,QAAO,CAAC,CAAC,OAAO,iBAAiB,IAAI,CAAC,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEjBnD,MAAM,QAAQ;EAKd,MAAM,OAAO;EAMb,SAAS,mBAAmB,OAAmB;AAC7C,SAAM,gBAAe;AACrB,SAAM,0BAAyB;;EAGjC,SAAS,YAAY,OAAmB;AACtC,OAAI,MAAM,UAAU;AAClB,uBAAmB,MAAK;AACxB;;AAEF,QAAK,SAAS,MAAK;;EAGrB,SAAS,kBAAkB,OAAmB,UAA0B;AACtE,OAAI,MAAM,UAAU;AAClB,uBAAmB,MAAK;AACxB;;AAEF,QAAK,SAAS,MAAK;AACd,YAAS,MAAK;;;;UAMX,QAAA,QAAA,WAAA,EADR,mBASI,KATJ,WASI,EAAA,KAAA,GAAA,EAPMC,KAAAA,QAAM;IACb,MAAM,QAAA,WAAW,KAAA,IAAY,QAAA;IAC7B,iBAAe,QAAA,YAAY,KAAA;IAC3B,UAAU,QAAA,WAAQ,KAAQ,KAAA;IAC1B,SAAO;QAER,WAAQ,KAAA,QAAA,UAAA,CAAA,EAAA,IAAA,cAAA,IAGG,QAAA,MAAA,WAAA,EADb,YAec,wBAAA;;IAbX,IAAI,QAAA;IACL,QAAA;;sBAWI,EAAA,MAVY,YAAY,eAAQ,CAEpC,mBAQI,KARJ,WACUA,KAON,QAPY;KACb,MAAM,QAAA,WAAW,KAAA,IAAY;KAC7B,iBAAe,QAAA,YAAY,KAAA;KAC3B,UAAU,QAAA,WAAQ,KAAQ,KAAA;KAC1B,UAAK,WAAE,kBAAkB,QAAQ,SAAQ;SAE1C,WAAQ,KAAA,QAAA,UAAA,CAAA,EAAA,IAAA,cAAA,CAAA,CAAA;;kCAGZ,mBAQS,UART,WAQS,EAAA,KAAA,GAAA,EANCA,KAAAA,QAAM;IACb,MAAM,QAAA;IACN,UAAU,QAAA;IACV,SAAO;QAER,WAAQ,KAAA,QAAA,UAAA,CAAA,EAAA,IAAA,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1DZ,MAAM,QAAQ;EAKd,MAAM,WAAW,eAAmC,iBAAiB,MAAM,KAAK,CAAA;EAEhF,MAAM,cAAc,eAAe;GACjC;GACA,qBAAqB,MAAM;GAC3B,qBAAqB,MAAM;GAC5B,CAAA;EAED,MAAM,YAAY,eAChB,MAAM,OAAO,EAAE,2BAA2B,MAAM,MAAM,GAAG,KAAA,EAC3D;;uBAIE,mBAsBO,QAAA;IAtBA,OAAK,eAAE,YAAA,MAAW;IAAG,OAAK,eAAE,UAAA,MAAS;OAElC,SAAA,MAAS,WAAM,UAAe,SAAA,MAAS,WAAM,cAAA,WAAA,EADrD,mBAYM,OAZN,eAYM,CADJ,mBAA4B,QAAA,EAArB,GAAG,SAAA,MAAS,OAAA,EAAA,MAAA,GAAA,cAAA,CAAA,CAAA,KAAA,WAAA,EAErB,mBAOE,OAAA;;IALA,OAAM;IACL,KAAK,SAAA,MAAS;IACf,KAAI;IACJ,gBAAe;IACf,SAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE3Cd,MAAM,QAAQ;EAKd,MAAM,OAAO;EAIb,MAAM,EAAE,QAAQ,SAAS,OAAO,WAAW,kBAAiB;EAC5D,MAAM,kBAAkB,eAAmC,MAAM,MAAM,IAAI,mBAAmB,CAAA;EAE9F,MAAM,UAAU,eACd,gBAAgB,MAAM,MAAM,MAAM,EAAE,OAAO,MAAM,cAAc,IAAI,gBAAgB,MAAM,GAC3F;EAEA,SAAS,aAAa,MAAwB;AAC5C,OAAI,KAAK,SAAU;AACnB,OAAI,KAAK,QAAQ,KAAK,IAAI;AACxB,WAAM;AACN;;AAEF,QAAK,UAAU,KAAI;AACnB,UAAM;;;uBAKN,mBA6EM,OAAA;aA7EG;IAAJ,KAAI;IAAU,OAAM;IAAsB,OAAK,eAAA,EAAA,UAAI,QAAA,UAAQ,CAAA;OAC9D,mBAuCS,UAAA;IAtCP,MAAK;IACL,OAAK,eAAA,CAAC,+BAA6B,EAAA,qCACY,MAAA,OAAM,EAAA,CAAA,CAAA;IACpD,iBAAe,MAAA,OAAM;IACtB,iBAAc;IACb,SAAK,OAAA,OAAA,OAAA,KAAA,eAAA,GAAA,SAAO,MAAA,OAAA,IAAA,MAAA,OAAA,CAAA,GAAA,KAAM,EAAA,CAAA,OAAA,CAAA;;IAGX,QAAA,SAAA,WAAA,EADR,mBAeO,QAAA;;KAbL,OAAK,eAAA,CAAC,4BAA0B,EAAA,sCACgB,MAAA,mBAAkB,CAAC,QAAA,MAAQ,KAAI,EAAA,CAAA,CAAA;QAE/E,WASO,KAAA,QAAA,QAAA,EATY,MAAM,QAAA,OAAO,QASzB,CAPG,MAAA,mBAAkB,CAAC,QAAA,MAAQ,KAAI,IAAA,WAAA,EADvC,YAME,oBAAA;;KAJA,OAAM;KACL,MAAM,QAAA,MAAQ;KACf,MAAK;KACL,SAAQ;2CAEV,mBAA2F,QAA3F,eAA2F,gBAAjC,QAAA,MAAQ,MAAM,OAAM,EAAA,CAAA,EAAA,EAAA,EAAA,CAAA,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;IAGlF,mBAAkF,QAAlF,eAAkF,gBAAvC,QAAA,OAAS,SAAS,QAAA,YAAW,EAAA,EAAA;IACxE,mBAaO,QAbP,eAaO,EAAA,WAAA,EAZL,mBAWM,OAAA;KAVJ,OAAK,eAAA,CAAC,+BAA6B,EAAA,qCACY,MAAA,OAAM,EAAA,CAAA,CAAA;KACrD,SAAQ;KACR,MAAK;KACL,QAAO;KACP,gBAAa;KACb,kBAAe;KACf,mBAAgB;sCAEhB,mBAAyB,QAAA,EAAnB,GAAE,gBAAc,EAAA,MAAA,GAAA,CAAA,EAAA,EAAA,EAAA,EAAA,CAAA;0BAKjB,MAAA,OAAM,IAAA,WAAA,EAAjB,mBAkCM,OAlCN,eAkCM,CAAA,OAAA,OAAA,OAAA,KAjCJ,mBAAuD,OAAA,EAAlD,OAAM,kCAAgC,EAAC,SAAK,GAAA,IAAA,UAAA,KAAA,EACjD,mBA+BqB,UAAA,MAAA,WA9BJ,gBAAA,QAAR,SAAI;wBADb,YA+BqB,4BAAA;KA7BlB,KAAK,KAAK;KACV,MAAM,KAAK;KACX,IAAI,KAAK;KACT,UAAU,KAAK;KACf,OAAK,eAAA;;4CAA0F,KAAK,OAAO,QAAA,OAAS,IAAE;8CAAsD,KAAK,UAAQ;;KAK1L,MAAK;KACJ,UAAK,WAAE,aAAa,KAAI;;4BAgBlB;MAdP,mBAcO,QAAA,EAbL,OAAK,eAAA,CAAC,iCAA+B,EAAA,2CACgB,MAAA,mBAAkB,CAAC,KAAK,KAAI,EAAA,CAAA,CAAA,EAAA,EAAA,CAEjF,WASO,KAAA,QAAA,aAAA,EATuB,MAAI,QAS3B,CAPG,MAAA,mBAAkB,CAAC,KAAK,KAAI,IAAA,WAAA,EADpC,YAME,oBAAA;;OAJA,OAAM;OACL,MAAM,KAAK;OACZ,MAAK;OACL,SAAQ;6CAEV,mBAAwF,QAAxF,eAAwF,gBAA9B,KAAK,MAAM,OAAM,EAAA,CAAA,EAAA,EAAA,EAAA,CAAA,CAAA,EAAA,EAAA;MAG/E,mBAAoE,QAApE,eAAoE,gBAApB,KAAK,MAAK,EAAA,EAAA;MAC9C,KAAK,QAAA,WAAA,EAAjB,mBAAmF,QAAnF,eAAmF,gBAAnB,KAAK,KAAI,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1GjF,MAAM,QAAQ;EAEd,MAAM,OAAO;EAKb,MAAM,aAAa,IAAmB,KAAI;EAC1C,MAAM,WAAW,oBAA8B,IAAI,KAAK,CAAA;EAExD,MAAM,kBAAkB,eAA8B,MAAM,MAAM,IAAI,qBAAqB,CAAA;EAE3F,SAAS,YAAY,MAAmB;AACtC,OAAI,KAAK,SAAU;AACnB,OAAI,KAAK,UAAU,QAAQ;AACzB,mBAAe,KAAK,GAAE;AACtB;;AAEF,OAAI,KAAK,QAAQ,KAAK,GAAI;AAC1B,QAAK,UAAU,KAAI;;EAGrB,SAAS,kBAAkB,QAAuB,MAAmB;AACnE,OAAI,OAAO,SAAU;AACrB,OAAI,CAAC,OAAO,QAAQ,CAAC,OAAO,GAAI,MAAK,iBAAiB,QAAQ,KAAI;AAClE,cAAW,QAAQ;;EAGrB,SAAS,qBAAqB,MAAqC;GACjE,MAAM,aAAa,mBAAwF,KAAI;AAC/G,UAAO;IACL,GAAG;IACH,UAAU,WAAW,UAAU,IAAI,mBAAmB;IACxD;;EAGF,SAAS,eAAe,QAAgB;AACtC,cAAW,QAAQ,WAAW,UAAU,SAAS,OAAO;;EAG1D,SAAS,WAAW,IAAwB,QAAgB;AAC1D,OAAI,GACF,UAAS,MAAM,IAAI,QAAQ,GAAE;OAE7B,UAAS,MAAM,OAAO,OAAM;;EAIhC,SAAS,mBAAmB,OAAmB;GAC7C,MAAM,SAAS,MAAM;AAErB,OAAI,CADkB,MAAM,KAAK,SAAS,MAAM,QAAQ,CAAC,CAAC,MAAK,OAAM,GAAG,SAAS,OAAO,CAAA,CACpE,YAAW,QAAQ;;EAGzC,MAAM,kBAAkB,SACtB,KAAK,UAAU,MAAK,UAAS,MAAM,OAAO,MAAM,cAAc,IAAI;EAEpE,SAAS,UAAU,MAAsD;AACvE,OAAI,CAAC,KAAM,QAAO;AAClB,UAAO,MAAM,QAAQ,KAAK,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,IAAG;;AAG3E,yBAAuB,UAAU,SAAS,mBAAkB;;uBAI1D,mBA6GM,OA7GN,eA6GM,EAAA,UAAA,KAAA,EA5GJ,mBA2GM,UAAA,MAAA,WA1GW,gBAAA,QAAR,SAAI;wBADb,mBA2GM,OAAA;KAzGH,KAAK,KAAK;;KACV,MAAM,OAAO,WAAW,IAA0B,KAAK,GAAE;KAC1D,OAAM;QAGE,KAAK,UAAU,UAAA,WAAA,EADvB,mBA2CS,UAAA;;KAzCP,MAAK;KACJ,OAAK,eAAA;;uCAAgF,KAAK,OAAO,QAAA,iBAAiB,eAAe,KAAI,EAAA;yCAAkD,KAAK,UAAQ;;KAKpM,UAAU,KAAK;KACf,iBAAe,WAAA,UAAe,KAAK;KACpC,iBAAc;KACb,SAAK,eAAA,WAAO,YAAY,KAAI,EAAA,CAAA,OAAA,CAAA;;KAGrB,UAAU,KAAK,KAAI,IAAA,WAAA,EAD3B,mBAeM,OAfN,eAeM,CAJY,MAAM,QAAQ,KAAK,KAAI,IAAA,UAAA,KAAA,EACrC,mBAA4D,UAAA,EAAA,KAAA,GAAA,EAAA,WAAjC,KAAK,OAAlB,GAAG,UAAK;0BAAtB,mBAA4D,QAAA;OAArB,KAAK;OAAW;;+BAEzD,mBAA8B,QAAA;;MAAhB,GAAG,KAAK;;qBAClB,MACN,gBAAG,KAAK,MAAK,GAAG,KAChB,EAAA;mBAAA,mBAYM,OAAA;MAXJ,OAAK,eAAA,CAAC,0BAAwB,EAAA,gCACY,WAAA,UAAe,KAAK,IAAE,CAAA,CAAA;MAChE,SAAQ;MACR,MAAK;MACL,QAAO;MACP,gBAAa;MACb,kBAAe;MACf,mBAAgB;MAChB,eAAY;uCAEZ,mBAAyB,QAAA,EAAnB,GAAE,gBAAc,EAAA,MAAA,GAAA,CAAA,EAAA,EAAA,EAAA;2CAI1B,YA6BqB,4BAAA;;KA3BlB,MAAM,KAAK;KACX,IAAI,KAAK;KACT,UAAU,KAAK;KACf,OAAK,eAAA;;uCAAgF,KAAK,OAAO,QAAA,eAAa;yCAAiD,KAAK,UAAQ;;KAK5K,UAAK,WAAE,YAAY,KAAI;;4BAiBlB,CAdE,UAAU,KAAK,KAAI,IAAA,WAAA,EAD3B,mBAeM,OAfN,eAeM,CAJY,MAAM,QAAQ,KAAK,KAAI,IAAA,UAAA,KAAA,EACrC,mBAA4D,UAAA,EAAA,KAAA,GAAA,EAAA,WAAjC,KAAK,OAAlB,GAAG,UAAK;0BAAtB,mBAA4D,QAAA;OAArB,KAAK;OAAW;;+BAEzD,mBAA8B,QAAA;;MAAhB,GAAG,KAAK;oFAClB,MACN,gBAAG,KAAK,MAAK,EAAA,EAAA,CAAA,CAAA;;;;;;;;SAIP,KAAK,UAAU,UAAU,WAAA,UAAe,KAAK,MAAA,WAAA,EADrD,mBAwBM,OAxBN,eAwBM,EAAA,UAAA,KAAA,EAnBJ,mBAkBqB,UAAA,MAAA,WAjBF,KAAK,WAAf,WAAM;yBADf,YAkBqB,4BAAA;MAhBlB,KAAK,OAAO;MACZ,MAAM,OAAO;MACb,IAAI,OAAO;MACX,UAAU,OAAO;MACjB,OAAK,eAAA;;iDAAsG,OAAO,OAAO,QAAA,eAAa;mDAA4D,OAAO,UAAQ;;MAKlN,MAAK;MACJ,UAAK,WAAE,kBAAkB,QAAQ,KAAI;;6BAE+B,CAArE,mBAAqE,QAArE,gBAAqE,gBAAtB,OAAO,MAAK,EAAA,EAAA,EAC/C,OAAO,eAAA,WAAA,EAAnB,mBAEO,QAFP,gBAEO,gBADF,OAAO,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEvKjC,MAAM,QAAQ;EAMd,MAAM,OAAO;EAOb,MAAM,EAAE,QAAQ,SAAS,OAAO,WAAW,iBAAiB;GAC1D,cAAc,KAAK,YAAY;GAC/B,eAAe,KAAK,aAAa;GAClC,CAAA;EAED,MAAM,WAAW,eAAe;AAC9B,OAAI,MAAM,YAAa,QAAO,MAAM,YAAY,MAAM,GAAG,EAAE,CAAC,aAAY;AACxE,OAAI,MAAM,UAAU;IAClB,MAAM,QAAQ,MAAM,SAAS,MAAM,CAAC,MAAM,MAAM,CAAC,OAAO,QAAO;IAC/D,MAAM,QAAQ,MAAM;AACpB,QAAI,CAAC,MAAO,QAAO;IACnB,MAAM,SAAS,MAAM;AACrB,QAAI,OAAQ,SAAQ,MAAM,OAAO,EAAE,GAAG,OAAO,OAAO,EAAE,EAAE,aAAY;AACpE,WAAO,MAAM,MAAM,GAAG,EAAE,CAAC,aAAY;;AAEvC,UAAO;IACR;EAED,SAAS,gBAAgB;AACvB,UAAO,QAAQ;;EAGjB,SAAS,aAAa,MAAuB;AAC3C,OAAI,KAAK,QAAS;AAClB,QAAK,UAAU,KAAI;AACnB,UAAM;;EAGR,SAAS,gBAAgB;AACvB,QAAK,WAAU;AACf,UAAM;;;uBAKN,mBA+DM,OAAA;aA/DG;IAAJ,KAAI;IAAU,OAAM;OACvB,mBAeS,UAAA;IAdP,MAAK;IACL,OAAK,eAAA,CAAC,6BAA2B,EAAA,mCACY,MAAA,OAAM,EAAA,CAAA,CAAA;IAClD,iBAAe,MAAA,OAAM;IACtB,iBAAc;IACb,OAAO,QAAA,WAAQ,GAAM,QAAA,SAAQ,mBAAA;IAC7B,SAAK,OAAA,OAAA,OAAA,KAAA,eAAA,GAAA,SAAO,MAAA,OAAA,IAAA,MAAA,OAAA,CAAA,GAAA,KAAM,EAAA,CAAA,OAAA,CAAA;OAEnB,mBAIO,QAJP,eAIO,CAHL,WAEO,KAAA,QAAA,UAAA,EAFc,UAAU,SAAA,OAAQ,QAEhC,CAAA,gBAAA,gBADF,SAAA,MAAQ,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,EAGH,QAAA,YAAA,WAAA,EAAZ,mBAA0E,QAA1E,eAA0E,gBAAlB,QAAA,SAAQ,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,IAAA,cAAA,EAGvD,MAAA,OAAM,IAAA,WAAA,EAAjB,mBA4CM,OA5CN,eA4CM;IA3CO,QAAA,YAAY,QAAA,aAAA,WAAA,EAAvB,mBAGM,OAHN,eAGM,CAFO,QAAA,YAAA,WAAA,EAAX,mBAA6E,OAA7E,eAA6E,gBAAjB,QAAA,SAAQ,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,EACzD,QAAA,aAAA,WAAA,EAAX,mBAAgF,OAAhF,eAAgF,gBAAlB,QAAA,UAAS,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAGzE,WAkBO,KAAA,QAAA,SAAA,EAlBa,OAAO,eAAa,QAkBjC,EAAA,UAAA,KAAA,EAjBL,mBAgBW,UAAA,MAAA,WAhBc,QAAA,QAAR,SAAI;6DAAiB,KAAK,IAAA,EAAA,CAC9B,KAAK,WAAA,WAAA,EAAhB,mBAA8E,OAA9E,cAA8E,KAAA,WAAA,EAC9E,YAaqB,4BAAA;;MAXlB,MAAM,KAAK;MACX,IAAI,KAAK;MACT,OAAK,eAAA,CAAA,0BAAA,EAAA,kCAAiE,KAAK,QAAM,CAAA,CAAA;MAClF,MAAK;MACJ,UAAK,WAAE,aAAa,KAAI;;6BAIlB;OAFK,KAAK,QAAA,WAAA,EAAjB,mBAEO,QAFP,eAEO,CADL,WAAsC,KAAA,QAAA,aAAA,EAAR,MAAI,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;OAEpC,mBAAkE,QAAlE,gBAAkE,gBAApB,KAAK,MAAK,EAAA,EAAA;OAC5C,KAAK,cAAA,WAAA,EAAjB,mBAA8F,QAA9F,gBAA8F,gBAAzB,KAAK,WAAU,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;IAK1E,QAAA,eAAA,WAAA,EAAhB,mBAiBW,UAAA,EAAA,KAAA,GAAA,EAAA,CAAA,OAAA,OAAA,OAAA,KAhBT,mBAA0D,OAAA;KAArD,OAAM;KAA4B,MAAK;mBAC5C,mBAcS,UAAA;KAbP,MAAK;KACL,OAAM;KACN,MAAK;KACJ,SAAO;kbASR,mBAAoE,QAApE,gBAAoE,gBAAtB,QAAA,aAAY,EAAA,EAAA,CAAA,CAAA,CAAA,EAAA,GAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE7GpE,MAAM,QAAQ;EAKd,MAAM,OAAO;EAKb,MAAM,EAAE,QAAQ,SAAS,OAAO,WAAW,kBAAiB;EAE5D,SAAS,WAAW,QAA8B;AAEhD,WADc,OAAO,SAAS,OAAO,IACxB,MAAM,GAAG,EAAE,CAAC,aAAY;;EAGvC,SAAS,aAAa,QAA8B;AAClD,OAAI,OAAO,OAAO,MAAM,QAAQ,IAAI;AAClC,WAAM;AACN;;AAEF,QAAK,UAAU,OAAM;AACrB,UAAM;;EAGR,SAAS,gBAAgB;AACvB,QAAK,gBAAe;AACpB,UAAM;;;uBAKN,mBAoFM,OAAA;aApFG;IAAJ,KAAI;IAAU,OAAM;OACvB,mBAwBS,UAAA;IAvBP,MAAK;IACL,OAAK,eAAA,CAAC,iCAA+B,EAAA,uCACY,MAAA,OAAM,EAAA,CAAA,CAAA;IACtD,iBAAe,MAAA,OAAM;IACtB,iBAAc;IACb,SAAK,OAAA,OAAA,OAAA,KAAA,eAAA,GAAA,SAAO,MAAA,OAAA,IAAA,MAAA,OAAA,CAAA,GAAA,KAAM,EAAA,CAAA,OAAA,CAAA;;IAEnB,mBAAoE,QAApE,eAAoE,gBAAvB,QAAA,QAAQ,MAAK,EAAA,EAAA;IAC9C,QAAA,QAAQ,WAAA,WAAA,EAApB,mBAAgG,QAAhG,eAAmE,MAAC,gBAAG,QAAA,QAAQ,QAAO,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;IACtF,mBAaO,QAbP,eAaO,EAAA,WAAA,EAZL,mBAWM,OAAA;KAVJ,OAAK,eAAA,CAAC,iCAA+B,EAAA,uCACY,MAAA,OAAM,EAAA,CAAA,CAAA;KACvD,SAAQ;KACR,MAAK;KACL,QAAO;KACP,gBAAa;KACb,kBAAe;KACf,mBAAgB;sCAEhB,mBAAyB,QAAA,EAAnB,GAAE,gBAAc,EAAA,MAAA,GAAA,CAAA,EAAA,EAAA,EAAA,EAAA,CAAA;0BAKjB,MAAA,OAAM,IAAA,WAAA,EAAjB,mBAwDM,OAxDN,eAwDM;8BAvDJ,mBAAiE,OAAA,EAA5D,OAAM,oCAAkC,EAAC,iBAAa,GAAA;sBAC3D,mBAiCqB,UAAA,MAAA,WAhCF,QAAA,UAAV,WAAM;yBADf,YAiCqB,4BAAA;MA/BlB,KAAK,OAAO;MACZ,MAAM,OAAO;MACb,IAAI,OAAO;MACX,OAAK,eAAA,CAAA,8BAAA,EAAA,sCAA8F,OAAO,OAAO,QAAA,QAAQ,IAAE,CAAA,CAAA;MAI5H,MAAK;MACJ,UAAK,WAAE,aAAa,OAAM;;6BAOpB;OALP,mBAKO,QAAA;QAJL,OAAM;QACL,OAAK,eAAA,EAAA,YAAgB,OAAO,SAAK,wBAAA,CAAA;0BAE/B,WAAW,OAAM,CAAA,EAAA,EAAA;OAEtB,mBAAwE,QAAxE,eAAwE,gBAAtB,OAAO,MAAK,EAAA,EAAA;OAClD,OAAO,WAAA,WAAA,EAAnB,mBAAmG,QAAnG,eAAuE,MAAC,gBAAG,OAAO,QAAO,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;OAEjF,OAAO,OAAO,QAAA,QAAQ,MAAA,WAAA,EAD9B,mBAYM,OAZN,eAYM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAA2B,QAAA,EAArB,GAAE,kBAAgB,EAAA,MAAA,GAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;IAIZ,QAAA,QAAQ,WAAW,QAAA,eAAe,QAAA,aAAaC,KAAAA,OAAO,YAAA,WAAA,EACpE,mBAA8D,OAA9D,cAA8D,IAAA,mBAAA,IAAA,KAAA;IAEhE,WAeO,KAAA,QAAA,WAAA,EAAA,QAAA,CAbG,QAAA,eAAe,QAAA,aAAA,WAAA,EADvB,YAaqB,4BAAA;;KAXlB,MAAM,QAAA;KACN,IAAI,QAAA;KACL,OAAM;KACN,MAAK;KACJ,SAAO;;4BAKF,CAAA,OAAA,OAAA,OAAA,KAHN,mBAGM,OAAA;MAHD,SAAQ;MAAY,MAAK;MAAO,QAAO;MAAe,gBAAa;MAAI,kBAAe;MAAQ,mBAAgB;MAAQ,eAAY;SACrI,mBAAuC,QAAA;MAAjC,IAAG;MAAK,IAAG;MAAI,IAAG;MAAK,IAAG;SAChC,mBAAuC,QAAA;MAAjC,IAAG;MAAI,IAAG;MAAK,IAAG;MAAK,IAAG;gBAElC,mBAA+B,QAAA,MAAA,gBAAtB,QAAA,aAAY,EAAA,EAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1C/B,MAAM,QAAQ;EAed,MAAM,OAAO;EAcb,MAAM,eAAe,IAAI,MAAK;EAC9B,MAAM,EAAE,cAAc,WAAW,oBAAmB;EACpD,MAAM,eAAe,eAAe,CAAC,aAAa,MAAK;EACvD,MAAM,gBAAgB,OAAO,oBAAoB,KAAI;EAErD,MAAM,aAAa,eAAe;GAChC,MAAM,gBAAgB,OAAO;AAC7B,UAAO,eAAe,OAClB;IAAE,MAAM,cAAc;IAAM,OAAO,cAAc;IAAM,GACvD;IACL;EAED,MAAM,iBAAiB,eAAe;AACpC,OAAI,MAAM,YAAa,QAAO,MAAM;AACpC,OAAI,MAAM,SAAU,QAAO,MAAM,SAAS,OAAO,EAAE,CAAC,aAAY;AAChE,UAAO;IACR;EAED,MAAM,0BAA0B,eAC9B,MAAM,iBAAiB,KAAA,MAAc,OAAO,OAAO,WAAW,UAAU,KAAK,EAC/E;EACA,MAAM,kBAAkB,gBAAgB,MAAM,cAAc,UAAU,KAAK,KAAK,wBAAwB,MAAK;EAC7G,MAAM,oBAAoB,eAAe,CAAC,CAAC,MAAM,eAAc;EAC/D,MAAM,aAAa,eAAe,CAAC,CAAC,MAAM,SAAS,OAAM;EACzD,MAAM,iBAAiB,eAAe,CAAC,CAAC,MAAM,aAAa,OAAM;EACjE,MAAM,gBAAgB,eAElB,CAAC,gBAAgB,SACjB,CAAC,kBAAkB,SACnB,CAAC,CAAC,MAAM,SACR,CAAC,CAAC,MAAM,SACZ;EACA,MAAM,eAAe,eAEjB,CAAC,gBAAgB,SACjB,CAAC,kBAAkB,SACnB,CAAC,cAAc,SACf,CAAC,CAAC,MAAM,MACZ;EACA,MAAM,uBAAuB,eAC3B,wBAAwB,QACpB,OAAO,OAAO,WAAW,IAAI,gCAAgC,IAAI,EAAC,GAClE,EAAE,CACR;EACA,MAAM,yBAAyB,eAC7B,MAAM,iBAAiB,KAAA,IACnB,MAAM,aAAa,IAAI,mBAAkB,GACzC,qBAAqB,MAC3B;EACA,MAAM,oBAAoB,eAAmC,MAAM,WAAW,EAAE,CAAA;EAChF,MAAM,yBAAyB,eAC7B,MAAM,gBAAgB,MAAM,IAAI,mBAAmB,IAAI,EAAE,CAC3D;EACA,MAAM,iCAAiC,eACrC,MAAM,0BACA,wBAAwB,QAAQ,0BAA0B,uBAAuB,MAAM,GAAG,KAAA,OAC1F,wBAAwB,QAAQ,uBAAuB,MAAM,IAAI,KAAK,KAAA,GAC9E;EAEA,SAAS,iBAAiB,MAAuB;GAC/C,MAAM,MAAM,MAAM,MAAM,IAAI;AAE5B,WADiB,IAAI,WAAW,IAAI,GAAG,MAAM,IAAI,OACjC,QAAQ,QAAQ,GAAG,IAAI;;EAGzC,SAAS,eAAe,MAAc,UAA0B;GAC9D,MAAM,aAAa,iBAAiB,KAAI;AACxC,OAAI,eAAe,IAAK,QAAO;AAC/B,UAAO,WAAW,QAAQ,QAAQ,GAAG,CAAC,QAAQ,QAAQ,IAAI,IAAI;;EAGhE,SAAS,gCAAgC,MAAqB,OAAiC;GAC7F,MAAM,OAAO,iBAAiB,KAAK,KAAI;AACvC,UAAO;IACL,IAAI,KAAK,MAAM,eAAe,MAAM,QAAQ,QAAQ,IAAI;IACxD,OAAO,KAAK;IACZ,IAAI;IACJ,MAAM,KAAK,QAAQ,OAAO,OAAO;IACjC,MAAM,KAAK,eAAe,OAAO,OAAO;IAC1C;;EAGF,SAAS,kBAAsC;AAC7C,OAAI,OAAO,WAAW,YAAa,QAAO,KAAA;GAC1C,MAAM,WAAW,iBAAiB,OAAO,SAAS,SAAQ;GAC1D,MAAM,cAAc,iBAAiB,OAAO,OAAO,aAAY;AAE/D,OAAI,aAAa,YAAa,QAAO;AACrC,OAAI,gBAAgB,OAAO,SAAS,WAAW,GAAG,YAAY,GAAG,CAC/D,QAAO,iBAAiB,SAAS,MAAM,YAAY,OAAO,CAAA;AAE5D,UAAO;;EAGT,SAAS,0BAA0B,OAAgF;GACjH,MAAM,OAAO,iBAAgB;AAC7B,OAAI,CAAC,KAAM,QAAO,KAAA;AAClB,UAAO,MAAM,MAAM,SAAS,iBAAiB,KAAK,MAAM,KAAK,KAAK,KAAK,KAAK,EAAE;;;;;IAK9E,mBA6NS,UAAA,EA5NN,OAAK,eAAA,CAAA,eAAA,gBAA+C,MAAM,UAAA,CAAA,EAAA,EAAA,CAK3D,mBAsNM,OAtNN,eAsNM;KApNJ,mBAkEM,OAlEN,eAkEM,CAhEI,QAAA,aAAa,QAAA,SAAS,WAAU,OAAA,IAAY,QAAA,SAAS,WAAU,IAAA,KAAA,WAAA,EADvE,mBAsBI,KAAA;;MApBD,MAAM,QAAA;MACP,OAAM;SAEN,WAgBO,KAAA,QAAA,QAAA,EAAA,QAAA,CAdG,WAAA,SAAA,WAAA,EADR,YAOE,oBAAA;;MALA,OAAM;MACL,MAAM,WAAA,MAAW;MACjB,MAAM,WAAA,MAAW;MAClB,MAAK;MACL,SAAQ;sCAEV,WAMO,KAAA,QAAA,QAAA,EAAA,KAAA,GAAA,QAAA,CALM,QAAA,YAAA,WAAA,EAAX,mBAIM,OAJN,eAIM,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAHJ,mBAEM,OAAA,EAFD,OAAM,0BAAwB,EAAA,CACjC,mBAA6C,QAAA,EAAvC,OAAM,0BAAwB,EAAC,IAAC,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA,CAAA,EAAA,GAAA,cAAA,IAOnC,QAAA,YAAA,WAAA,EADb,YAsBc,wBAAA;;MApBX,IAAI,QAAA;MACL,OAAM;;6BAkBC,CAhBP,WAgBO,KAAA,QAAA,QAAA,EAAA,QAAA,CAdG,WAAA,SAAA,WAAA,EADR,YAOE,oBAAA;;OALA,OAAM;OACL,MAAM,WAAA,MAAW;OACjB,MAAM,WAAA,MAAW;OAClB,MAAK;OACL,SAAQ;uCAEV,WAMO,KAAA,QAAA,QAAA,EAAA,KAAA,GAAA,QAAA,CALM,QAAA,YAAA,WAAA,EAAX,mBAIM,OAJN,eAIM,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAHJ,mBAEM,OAAA,EAFD,OAAM,0BAAwB,EAAA,CACjC,mBAA6C,QAAA,EAAvC,OAAM,0BAAwB,EAAC,IAAC,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;sBAO9C,WAgBO,KAAA,QAAA,QAAA,EAAA,KAAA,GAAA,QAAA,CAdG,WAAA,SAAA,WAAA,EADR,YAOE,oBAAA;;MALA,OAAM;MACL,MAAM,WAAA,MAAW;MACjB,MAAM,WAAA,MAAW;MAClB,MAAK;MACL,SAAQ;sCAEV,WAMO,KAAA,QAAA,QAAA,EAAA,KAAA,GAAA,QAAA,CALM,QAAA,YAAA,WAAA,EAAX,mBAIM,OAJN,eAIM,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAHJ,mBAEM,OAAA,EAFD,OAAM,0BAAwB,EAAA,CACjC,mBAA6C,QAAA,EAAvC,OAAM,0BAAwB,EAAC,IAAC,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;KAU1C,kBAAA,SAAqB,QAAA,kBAAA,WAAA,EAD7B,YAQE,2BAAA;;MANC,SAAS,QAAA,eAAe;MACxB,SAAS,QAAA,eAAe;MACxB,cAAY,QAAA,eAAe;MAC3B,gBAAc,QAAA,eAAe;MAC7B,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,0BAA2B,OAAM;MAC7C,gBAAa,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,0BAAA;;;;;;WAGT,gBAAA,SAAA,WAAA,EADb,YAYgC,uCAAA;;MAV7B,OAAO,uBAAA;MACP,mBAAiB,+BAAA;MACjB,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,wBAAyB,OAAM;+BAE5BC,KAAAA,OAAM,wBAAA;YAAyB;mBAAM,cAAS,CAC5D,WAAqD,KAAA,QAAA,sBAAA,eAAA,mBAAb,UAAS,CAAA,CAAA,CAAA,CAAA;;iBAEnCA,KAAAA,OAAM,6BAAA;YAA8B;mBAAW,cAAS,CACtE,WAA0D,KAAA,QAAA,2BAAA,eAAA,mBAAb,UAAS,CAAA,CAAA,CAAA,CAAA;;;KAK/C,cAAA,SAAA,WAAA,EAAX,mBAGM,OAHN,eAGM,CAFJ,mBAAkD,QAAlD,eAAkD,gBAAf,QAAA,MAAK,EAAA,EAAA,EACxC,mBAAwD,QAAxD,eAAwD,gBAAlB,QAAA,SAAQ,EAAA,EAAA,CAAA,CAAA,IAG/B,aAAA,SAAA,WAAA,EAAjB,mBAAiF,QAAjF,gBAAiF,gBAAf,QAAA,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAGvE,WAAmB,KAAA,QAAA,MAAA;KAGR,WAAA,SAAcA,KAAAA,OAAO,UAAA,WAAA,EAAhC,mBAUM,OAVN,gBAUM,CATJ,WAQO,KAAA,QAAA,UAAA,EAAA,QAAA,CANG,WAAA,SAAc,QAAA,WAAA,WAAA,EADtB,YAME,kCAAA;;MAJC,OAAO,kBAAA;MACP,mBAAiB,QAAA;MACjB,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,eAAgB,OAAM;MAClC,gBAAa,OAAA,OAAA,OAAA,MAAG,QAAQ,SAAS,KAAI,sBAAuB,QAAQ,KAAI;;KAM/E,mBA8FM,OA9FN,gBA8FM;MA7FQ,QAAA,uBAAuB,aAAA,SAAY,CAAK,MAAA,cAAa,IAAA,WAAA,EAAjE,mBAEO,QAFP,gBAEO,gBADF,QAAA,gBAAe,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;MAIZ,MAAA,cAAa,IAAA,CAAK,aAAA,SAAA,WAAA,EAD1B,YAME,2BANF,WAME,EAAA,KAAA,GAAA,EAJQ,MAAA,cAAa,CAAC,QAAQ,OAAK;OAClC,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,cAAa,CAAC,WAAS;OAC/B,QAAI,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,cAAa,CAAC,YAAU;OAC9B,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,cAAa,CAAC,cAAY;;MAGzB,MAAA,cAAa,IAAI,aAAA,SAAA,WAAA,EAA7B,mBAEO,QAFP,gBAEO,gBADF,QAAA,gBAAe,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;MAGpB,WAAuB,KAAA,QAAA,UAAA;MAIf,QAAA,qBAAA,WAAA,EADR,mBAYS,UAAA;;OAVP,MAAK;OACL,OAAM;OACN,cAAW;OACV,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,sBAAA;sCAEZ,mBAGM,OAAA;OAHD,OAAM;OAA6B,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,gBAAa;OAAI,kBAAe;OAAQ,mBAAgB;UACpJ,mBAAsD,QAAA,EAAhD,GAAE,6CAA2C,CAAA,EACnD,mBAA2C,QAAA,EAArC,GAAE,kCAAgC,CAAA,CAAA,EAAA,GAAA,GAE9B,QAAA,sBAAA,WAAA,EAAZ,mBAAuF,QAAvF,eAAuF,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;MAItE,QAAA,mBAAA,WAAA,EAAnB,YAAgD,qBAAA;;OAAZ,MAAK;;MAIjC,QAAA,gBAAA,WAAA,EADR,mBAUS,UAAA;;OARP,MAAK;OACL,OAAM;OACN,cAAW;OACV,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,aAAA,QAAY;0CAEpB,mBAEM,OAAA;OAFD,OAAM;OAA6B,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,gBAAa;OAAI,kBAAe;OAAQ,mBAAgB;UACpJ,mBAAoV,QAAA,EAA9U,GAAE,2UAAyU,CAAA,EAAG,mBAAgC,UAAA;OAAxB,IAAG;OAAK,IAAG;OAAK,GAAE;;MAM1W,QAAA,aAAA,WAAA,EADR,YAUc,wBAAA;;OARX,IAAI,QAAA;OACL,OAAM;OACN,cAAW;OACV,SAAK,OAAA,QAAA,OAAA,OAAA,WAAE,KAAI,cAAA;;8BAIN,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAFN,mBAEM,OAAA;QAFD,OAAM;QAA0B,SAAQ;QAAY,MAAK;QAAO,QAAO;QAAe,gBAAa;QAAI,kBAAe;QAAQ,mBAAgB;WACjJ,mBAA+K,QAAA,EAAzK,GAAE,sKAAoK,CAAA,EAAG,mBAA0B,QAAA,EAApB,GAAE,iBAAe,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA;;;MAMlM,eAAA,SAAkBA,KAAAA,OAAM,yBAAA,WAAA,EADhC,YAegB,uBAAA;;OAbb,aAAW,QAAA;OACX,gBAAc,QAAA;OACd,cAAY,QAAA;OACZ,OAAO,QAAA;OACP,UAAM,OAAA,QAAA,OAAA,OAAA,WAAE,KAAI,uBAAwB,OAAM;OAC1C,WAAQ,OAAA,QAAA,OAAA,OAAA,WAAE,KAAI,WAAA;gCAECA,KAAAA,OAAM,wBAAA;aAAyB;oBAAO,cAAS,CAC7D,WAAqD,KAAA,QAAA,sBAAA,eAAA,mBAAb,UAAS,CAAA,CAAA,CAAA,CAAA;;kBAEnCA,KAAAA,OAAM,4BAAA;aAA6B;oBAAW,cAAS,CACrE,WAAyD,KAAA,QAAA,0BAAA,eAAA,mBAAb,UAAS,CAAA,CAAA,CAAA,CAAA;;;;;;;YAM5C,QAAA,eAAA,WAAA,EADb,mBAWS,UAAA;;OATP,MAAK;OACL,OAAM;OACN,cAAW;OACV,SAAK,OAAA,QAAA,OAAA,OAAA,WAAE,KAAI,gBAAA;UAEZ,mBAEM,OAFN,gBAEM,gBADD,eAAA,MAAc,EAAA,EAAA,EAEP,QAAA,YAAA,WAAA,EAAZ,mBAA6E,QAA7E,gBAA6E,gBAAlB,QAAA,SAAQ,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;IAOnE,QAAA,gBAAA,WAAA,EADR,YAsBgB,uBAAA;;iBApBL,aAAA;iFAAY,QAAA;KACpB,OAAO,QAAA,gBAAgB;KACvB,MAAM,uBAAA;KACN,mBAAiB,QAAA,gBAAgB,kBAAc;KAC/C,MAAM,QAAA,gBAAgB;KACtB,QAAQ,QAAA,gBAAgB;KACxB,QAAQ,QAAA,gBAAgB;KACxB,OAAO,QAAA,gBAAgB;KACvB,UAAU,QAAA,gBAAgB;KAC1B,mBAAiB,QAAA,gBAAgB;KACjC,QAAQ,QAAA,gBAAgB;KACxB,cAAc,QAAA,gBAAgB;KAC9B,mBAAa,OAAA,QAAA,OAAA,OAAA,WAAE,KAAI,0BAA2B,OAAM;;KAK1C,YAAU,cACgB,CAAnC,WAAmC,KAAA,QAAA,sBAAA,CAAA,CAAA;;mBAJb,uBAAA,QAAP,QAAG;;mBAAkD,IAAI;wBAC/B,CAAzC,WAAyC,KAAA,QAAA,gBAAZ,IAAI,KAAE,CAAA,CAAA;;;;;;;;;;;;;;;;IAQ/B,MAAA,cAAa,IAAA,CAAK,aAAA,SAAA,WAAA,EAD1B,YAME,iCANF,WAME,EAAA,KAAA,GAAA,EAJQ,MAAA,cAAa,CAAC,cAAc,OAAK;KACxC,uBAAkB,OAAA,QAAA,OAAA,OAAA,WAAE,SAAS,MAAA,cAAa,CAAC,WAAS,GAAK,MAAA,cAAa,CAAC,YAAU;KACjF,UAAM,OAAA,QAAA,OAAA,OAAA,WAAE,MAAA,cAAa,CAAC,aAAa,OAAM;KACzC,YAAQ,OAAA,QAAA,OAAA,OAAA,WAAE,MAAA,cAAa,CAAC,cAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEtczC,MAAM,QAAQ;EAQd,MAAM,OAAO;EAOb,MAAM,eAAe,IAAe,EAAE,CAAA;AAGtC,MAAI,aAAa,MAAM,WAAW,EAChC,cAAa,QAAQ,MAAM,MAAM,UAAU,KAAI;EAGjD,MAAM,aAAa,eAAkC;AACnD,UAAO,MAAM,MAAM,KAAK,GAAG,UAAU;AACnC,QAAI,UAAU,MAAM,WAAY,QAAO;AACvC,QAAI,QAAQ,MAAM,cAAc,aAAa,MAAM,WAAW,MAAO,QAAO;AAC5E,QAAI,QAAQ,MAAM,cAAc,MAAM;UAC/B,IAAI,IAAI,MAAM,YAAY,IAAI,OAAO,IACxC,KAAI,aAAa,MAAM,OAAO,MAAO,QAAO;;AAGhD,WAAO;KACR;IACF;EAED,SAAS,aAAa,OAAe,OAAgB;AACnD,OAAI,SAAS,KAAK,QAAQ,MAAM,MAAM,OACpC,cAAa,MAAM,SAAS;;EAIhC,SAAS,SAAS,OAAe;AAC/B,OAAI,QAAQ,KAAK,SAAS,MAAM,MAAM,OAAQ;AAC9C,OAAI,WAAW,MAAM,WAAW,WAAY;AAE5C,OAAI,MAAM,UAAU,QAAQ,MAAM;SAC3B,IAAI,IAAI,MAAM,YAAY,IAAI,OAAO,IACxC,KAAI,aAAa,MAAM,OAAO,MAAO;;GAIzC,MAAM,OAAO,MAAM;AACnB,QAAK,eAAe,CAAC,MAAM,MAAM,CAAA;AACjC,QAAK,qBAAqB,MAAK;;EAGjC,SAAS,SAAS;AAChB,OAAI,aAAa,MAAM,MAAM,gBAAgB,MAAO;AACpD,OAAI,MAAM,aAAa,MAAM,MAAM,SAAS,EAC1C,UAAS,MAAM,aAAa,EAAC;;EAIjC,SAAS,SAAS;AAChB,OAAI,MAAM,aAAa,EACrB,UAAS,MAAM,aAAa,EAAC;;EAIjC,SAAS,SAAS;AAChB,OAAI,aAAa,MAAM,MAAM,gBAAgB,MAAO;AACpD,OAAI,MAAM,eAAe,MAAM,MAAM,SAAS,EAC5C,MAAK,WAAU;;EAInB,SAAS,SAAS;AAChB,QAAK,SAAQ;;EAGf,SAAS,cAAc,OAAsB;AAC3C,WAAQ,MAAM,KAAd;IACE,KAAK;AACH,WAAM,gBAAe;AACrB,aAAO;AACP;IACF,KAAK;AACH,WAAM,gBAAe;AACrB,aAAO;AACP;IACF,KAAK;AACH,aAAO;AACP;;;AAIN,WAAa,EAAE,cAAc,CAAA;;uBAI3B,mBAuGM,OAAA;IAtGH,OAAK,eAAA,CAAA,eAAA,gBAAkC,MAAM,OAAI,CAAA;IAClD,UAAS;IACR,WAAS;;IAGC,MAAM,gBAAA,WAAA,EAAjB,mBAmCM,OAnCN,eAmCM,CAlCJ,WAiCO,KAAA,QAAA,YAAA;KAjCgB,OAAO,MAAM;KAAQ,SAAS,MAAM;KAAa,QAAQ,WAAA;aAiCzE,CAhCL,mBA+BM,OA/BN,eA+BM,EAAA,UAAA,KAAA,EA9BJ,mBA6BW,UAAA,MAAA,WA7BuB,MAAM,QAAtB,MAAM,UAAK;6DAAwB,KAAK,IAAA,EAAA,CAGhD,QAAK,KAAA,WAAA,EADb,mBAME,OAAA;;MAJC,OAAK,eAAA,CAAA,+BAAmE,WAAA,MAAW,QAAK,OAAA,cAAA,2CAAA,GAAA,CAAA;kDAM3F,mBAkBM,OAAA;MAjBH,OAAK,eAAA,CAAA,+BAAA,gCAAmG,WAAA,MAAW,SAAA,CAAA;MAInH,UAAK,WAAE,SAAS,MAAK;SAEtB,mBASM,OATN,eASM,CARY,WAAA,MAAW,WAAK,eAAA,WAAA,EAC9B,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAA4B,QAAA,EAAtB,GAAE,mBAAiB,EAAA,MAAA,GAAA,CAAA,EAAA,CAAA,IAGR,MAAM,mBAAA,WAAA,EAA3B,mBAEW,UAAA,EAAA,KAAA,GAAA,EAAA,CAAA,gBAAA,gBADN,QAAK,EAAA,EAAA,EAAA,CAAA,EAAA,GAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,EAGZ,mBAA2D,OAA3D,eAA2D,gBAAnB,KAAK,MAAK,EAAA,EAAA,CAAA,EAAA,IAAA,cAAA,CAAA,EAAA,GAAA;;IAQ5D,mBASM,OATN,eASM,EAAA,UAAA,KAAA,EARJ,mBAOM,UAAA,MAAA,WANoB,MAAM,QAAtB,MAAM,UAAK;yCADrB,mBAOM,OAAA;MALH,KAAK,KAAK;MAEX,OAAM;SAEN,WAA8D,KAAA,QAAA,QAAzC,KAAK,MAAE;MAAW;MAAc;qBAH7C,UAAU,MAAM,WAAU,CAAA,CAAA;;IAQtC,mBA8CM,OA9CN,eA8CM,CA7CJ,WA4CO,KAAA,QAAA,cAAA;KA1CK;KACA;KACD;KACA;KACR,SAAU,MAAM,eAAU;KAC1B,QAAS,MAAM,eAAe,MAAM,MAAM,SAAM;KAChD,YAAa,aAAA,MAAa,MAAM,gBAAU;aAoCtC;KAlCL,mBAMS,UAAA;MALP,MAAK;MACL,OAAM;MACL,SAAO;QACT,WAED;+BACA,mBAAuB,OAAA,EAAlB,OAAA,EAAA,QAAA,KAAe,EAAA,EAAA,MAAA,GAAA;KACpB,mBAOS,UAAA;MANP,MAAK;MACL,OAAM;MACL,UAAU,MAAM,eAAU;MAC1B,SAAO;QACT,UAED,GAAA,cAAA;KAEQ,MAAM,aAAa,MAAM,MAAM,SAAM,KAAA,WAAA,EAD7C,mBAQS,UAAA;;MANP,MAAK;MACL,OAAM;MACL,UAAU,aAAA,MAAa,MAAM,gBAAU;MACvC,SAAO;QACT,UAED,GAAA,eAAA,KAAA,WAAA,EACA,mBAQS,UAAA;;MANP,MAAK;MACL,OAAM;MACL,UAAU,aAAA,MAAa,MAAM,gBAAU;MACvC,SAAO;QACT,YAED,GAAA,eAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE3MR,MAAM,QAAQ;EAEd,MAAM,gBAAgB,eACpB,MAAM,QAAQ,OAAO,QAAQ,MAAM,MAAM,QAAQ,eAAe,EAAE,KAAK,CAAC,CAC1E;EAEA,MAAM,YAAY,gBAAgB,EAChC,qBAAqB,UAAU,MAAM,QAAQ,WAAW,EAAE,SAC3D,EAAC;;UAIW,cAAA,MAAc,SAAM,KAAA,WAAA,EAA/B,mBAiDM,OAjDN,eAiDM,CAhDJ,WA6CO,KAAA,QAAA,WA7CiB,QAAA,QAAQ,MAAE;IAAK,SAAS,QAAA;IAAU,MAAM,QAAA,QAAQ;YA6CjE,CA3CG,QAAA,QAAQ,eAAA,WAAA,EADhB,YAqBkB,yBAAA;;IAnBf,OAAO,QAAA,QAAQ;IACf,UAAU,QAAA,QAAQ;IAClB,gBAAc,QAAA,QAAQ,eAAW;;2BAgB5B,CAdN,mBAcM,OAAA;KAdD,OAAM;KAA2B,OAAK,eAAE,UAAA,MAAS;0BACpD,mBAYM,UAAA,MAAA,WAXY,cAAA,QAAT,UAAK;yBADd,mBAYM,OAAA;MAVH,KAAK,MAAM;MACX,OAAK,eAAE,MAAM,UAAO,EAAA,YAAA,QAAyB,MAAM,WAAO,GAAO,KAAA,EAAS;SAE3E,WAMO,KAAA,QAAA,SANe,MAAM,QAAI;MAAY;MAAQ,MAAM,QAAA,QAAQ;MAAO,YAAa,QAAA,QAAQ,KAAK,cAAc,MAAM,KAAI;cAMpH,CALL,YAIE,mCAAA;MAHQ;MACP,kBAAgB,QAAA,QAAQ,sBAAsB,MAAK;MACnD,MAAM,QAAA,QAAQ;;;;;;;;;;;;uBAOzB,mBAoBM,OApBN,eAoBM,CAnBO,QAAA,QAAQ,SAAS,QAAA,QAAQ,eAAA,WAAA,EAApC,mBAGM,OAHN,eAGM,CAFM,QAAA,QAAQ,SAAA,WAAA,EAAlB,mBAAkF,MAAlF,eAAkF,gBAArB,QAAA,QAAQ,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,EACjE,QAAA,QAAQ,eAAA,WAAA,EAAjB,mBAAkG,KAAlG,eAAkG,gBAA1B,QAAA,QAAQ,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,EAE7F,mBAcM,OAAA;IAdD,OAAM;IAA2B,OAAK,eAAE,UAAA,MAAS;yBACpD,mBAYM,UAAA,MAAA,WAXY,cAAA,QAAT,UAAK;wBADd,mBAYM,OAAA;KAVH,KAAK,MAAM;KACX,OAAK,eAAE,MAAM,UAAO,EAAA,YAAA,QAAyB,MAAM,WAAO,GAAO,KAAA,EAAS;QAE3E,WAMO,KAAA,QAAA,SANe,MAAM,QAAI;KAAY;KAAQ,MAAM,QAAA,QAAQ;KAAO,YAAa,QAAA,QAAQ,KAAK,cAAc,MAAM,KAAI;aAMpH,CALL,YAIE,mCAAA;KAHQ;KACP,kBAAgB,QAAA,QAAQ,sBAAsB,MAAK;KACnD,MAAM,QAAA,QAAQ;;;;;;wBAQ3B,WAAmE,KAAA,QAAA,WAA3C,QAAA,QAAQ,GAAE,SAAA,EAAW,MAAM,QAAA,QAAQ,MAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEjC/D,MAAM,OAAO;;uBASX,mBAuCM,OAvCN,eAuCM;IArCI,QAAA,cAAA,WAAA,EADR,YAOa,oBAAA;;KALX,SAAQ;KACP,UAAU,QAAA,WAAW,QAAA;KACrB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAA;;4BAEK,CAAA,gBAAA,gBAAd,QAAA,YAAW,EAAA,EAAA,CAAA,CAAA;;;8BAGhB,mBAAuB,OAAA,EAAlB,OAAA,EAAA,QAAA,KAAe,EAAA,EAAA,MAAA,GAAA;IAGZ,QAAA,YAAQ,CAAK,QAAA,WAAA,WAAA,EADrB,YAOa,oBAAA;;KALX,SAAQ;KACP,UAAU,QAAA,WAAW,QAAA;KACrB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA;;4BAGd,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAFC,UAED,GAAA,CAAA,EAAA,CAAA;;;IAGQ,QAAA,YAAQ,CAAK,QAAA,UAAA,WAAA,EADrB,YAOa,oBAAA;;KALX,SAAQ;KACP,UAAQ,CAAG,QAAA,cAAc,QAAA,WAAW,QAAA;KACpC,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA;;4BAGd,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAFC,UAED,GAAA,CAAA,EAAA,CAAA;;;KAGS,QAAA,YAAY,QAAA,UAAA,WAAA,EADrB,YAQa,oBAAA;;KANX,SAAQ;KACP,SAAS,QAAA;KACT,UAAQ,CAAG,QAAA,cAAc,QAAA,WAAW,QAAA;KACpC,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAA;;4BAEK,CAAA,gBAAA,gBAAd,QAAA,YAAW,EAAA,EAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EElBpB,MAAM,QAAQ;EASd,MAAM,OAAO;EAMb,MAAM,gBAAgB,eAAgD;AACpE,OAAI,MAAM,UAAU,KAAA,EAAW,QAAO,KAAA;AACtC,UAAO,sBAAsB,MAAM,MAAM,GAAG,MAAM,QAAQ,mBAAmB,MAAM,MAAK;IACzF;EAED,MAAM,mBAAmB,eACvB,MAAM,YAAY,cAAc,OAAO,SACzC;EAEA,MAAM,yBAAyB,eAC7B,6BAA6B,cAAc,OAAO,kBAAkB,EAAE,EAAE,MAAM,eAAc,CAC9F;EAEA,MAAM,iBAAiB,eAA2B;AAChD,OAAI,MAAM,OAAQ,QAAO,MAAM;AAC/B,OAAI,iBAAiB,MAAO,QAAO,qBAAqB,iBAAiB,OAAO,uBAAuB,MAAK;AAC5G,UAAO,EAAE,UAAU,EAAE,EAAC;IACvB;EAED,MAAM,qBAAqB,gBAAyC;GAClE,GAAI,iBAAiB,QAAQ,mBAAmB,iBAAiB,MAAM,GAAG,EAAE;GAC5E,GAAI,uBAAuB,MAAM,iBAAiB,EAAE;GACpD,GAAI,MAAM,cAAc,EAAE;GAC3B,EAAC;EAEF,MAAM,UAAU,eACd,eAAe,OACf,mBAAmB,OACnB,MAAM,aACR;AAGA,QACE,iBACC,WAAW;GACV,MAAM,aAAa,qBAAqB,OAAM;GAC9C,MAAM,eAAe,uBAAuB,mBAAmB,OAAO,WAAU;GAChF,MAAM,iBAAiB,uBACrB,QAAQ,KAAK,MACb,WACF;GACA,MAAM,aAAa,MAAM,eAAe,KAAA,IACpC;IAAE,GAAG;IAAc,GAAG;IAAe,GACrC;AAEJ,WAAQ,aAAa,QAAQ,WAAU;KAEzC,EAAE,MAAM,MAAM,CAChB;AAGA,cACQ,MAAM,kBACN;GACJ,MAAM,OAAO,mBAAmB;GAChC,MAAM,aAAa,mBAAkB;AACrC,OAAI,yBAAyB,MAAM,QAAQ,KAAK,MAAiC,WAAW,CAAE;AAC9F,WAAQ,MAAM,uBAAuB,MAAM,WAAW,CAAA;KAExD,EAAE,MAAM,MAAM,CAChB;AAGA,eACS,EAAE,GAAG,QAAQ,KAAK,MAAM,IAC9B,SAAS,KAAK,qBAAqB,KAAgC,EACpE,EAAE,MAAM,MAAM,CAChB;EAGA,MAAM,WAAW,eAAe,CAAC,CAAC,eAAe,MAAM,MAAK;EAC5D,MAAM,YAAY,IAA4C,KAAI;EAElE,MAAM,cAAc,eAA6B;AAC/C,OAAI,CAAC,eAAe,MAAM,MAAO,QAAO,EAAC;AACzC,UAAO,eAAe,MAAM,MAAM,KAAK,UAAU;IAC/C,IAAI,KAAK;IACT,OAAO,KAAK;IACZ,aAAa,KAAK;IAClB,MAAM,KAAK;IACX,UAAU,KAAK;IAChB,EAAC;IACH;AAGD,cACQ,QAAQ,mBAAmB,QAChC,UAAU;AACT,aAAU,OAAO,aAAa,QAAQ,YAAY,OAAO,MAAK;IAElE;EAEA,SAAS,aAAa;AAEpB,OADgB,QAAQ,QAAO,CAE7B,WAAU,OAAO,aAAa,QAAQ,YAAY,QAAQ,GAAG,KAAI;;EAIrE,eAAe,eAAe;AAC5B,SAAM,QAAQ,QAAO;GACrB,MAAM,WAAW,QAAQ,KAAK;AAM9B,QAAK,UALe,OAAO,YACzB,QAAQ,OACL,QAAQ,MAAM,QAAQ,eAAe,EAAE,KAAK,CAAA,CAC5C,KAAK,MAAM,CAAC,EAAE,MAAM,SAAS,EAAE,MAAM,CAAC,CAC3C,CAC0B;;EAG5B,SAAS,eAAe;AACtB,QAAK,SAAQ;;EAGf,SAAS,oBAA8B;AACrC,UAAO,QAAQ,OAAO,KAAI,UAAS,MAAM,KAAI;;EAG/C,SAAS,sBAAsB,OAAyE;AACtG,UAAO,cAAc,SAAS,oBAAoB;;AAGpD,WAAa;GACX,MAAM,QAAQ;GACd,UAAU,QAAQ;GAClB,OAAO,QAAQ;GACf,cAAc,QAAQ;GACtB,QAAQ,QAAQ;GAChB,QAAQ,QAAQ;GAChB,UAAU,QAAQ;GAClB;GACD,CAAA;;uBAIC,mBAyFM,OAAA,EAzFA,OAAK,eAAA,CAAA,qBAAwB,QAAA,OAAI,sBAAyB,QAAA,SAAI,GAAA,CAAA,EAAA,EAAA,CAElD,SAAA,SAAY,eAAA,MAAe,SAAA,WAAA,EACzC,YAiDa,oBAAA;;aAhDP;IAAJ,KAAI;IACH,OAAO,YAAA;IACP,eAAa,MAAA,QAAO,CAAC,YAAY;IACjC,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,QAAO,CAAC,SAAS,OAAM;wCAEnB,eAAA,MAAe,QAAvB,SAAI;;mBAAkD,KAAK;uBAqBpE,CApBN,mBAoBM,OApBN,eAoBM,EAAA,UAAA,KAAA,EAnBJ,mBAkBW,UAAA,MAAA,WAlBiB,KAAK,WAAhB,YAAO;8DAAyB,QAAQ,IAAA,EAAA,CAE/C,MAAA,QAAO,CAAC,iBAAiB,QAAQ,GAAE,IAAA,WAAA,EAD3C,YAgBsB,6BAAA;;OAdV;OACT,SAAU,MAAA,QAAO;;mBAOI,QAAQ,OAAE,SAAM,cAAS,CAC7C,WAA2D,KAAA,QAAA,WAAnC,QAAQ,MAAhC,WAA2D,EAAA,SAAA,MAAA,EAAb,UAAS,CAAA,CAAA,CAAA;mBAEnC,QAAQ,GAAE,UAAA,SAAY,cAAS,CACnD,WAAiE,KAAA,QAAA,WAAzC,QAAQ,GAAE,SAAlC,WAAiE,EAAA,SAAA,MAAA,EAAb,UAAS,CAAA,CAAA,CAAA;;qBARrC,QAAQ,SAAjB,UAAK;;uBAAgD,MAAM;qBAAU,cAAS,CAC7F,WAAyD,KAAA,QAAA,SAAnC,MAAM,QAA5B,WAAyD,EAAA,SAAA,MAAA,EAAb,UAAS,CAAA,CAAA,CAAA;;;;;OAc/C,QAAA,cAAA;UAAc;iBAiBrB,EAjBmC,SAAS,aAAM,CACzD,WAgBO,KAAA,QAAA,WAAA;KAhBe,MAAM,MAAA,QAAO,CAAC;KAAO,SAAS,MAAA,QAAO;aAgBpD,CAfL,YAcE,qBAAA;KAbA,aAAA;KACC,YAAU;KACV,WAAS;KACT,eAAa,MAAA,QAAO,CAAC,mBAAmB;KACxC,SAAS,QAAA,WAAW,MAAA,QAAO,CAAC,KAAK,aAAa;KAC9C,UAAU,QAAA;KACV,gBAAc,eAAA,MAAe,eAAW;KACxC,gBAAc,eAAA,MAAe,eAAW;KACxC,eAAa,eAAA,MAAe,cAAU;KACtC,QAAM;KACN,QAAM,MAAA,QAAO,CAAC;KACd,UAAQ;KACR,UAAQ;;;;;;;;;;;;;oDAQE,eAAA,MAAe,YAAA,WAAA,EAApC,mBAgCW,UAAA,EAAA,KAAA,GAAA,EAAA,EAAA,UAAA,KAAA,EA/BT,mBAkBW,UAAA,MAAA,WAlBiB,eAAA,MAAe,WAA1B,YAAO;4DAAmC,QAAQ,IAAA,EAAA,CAEzD,MAAA,QAAO,CAAC,iBAAiB,QAAQ,GAAE,IAAA,WAAA,EAD3C,YAgBsB,6BAAA;;KAdV;KACT,SAAU,MAAA,QAAO;;iBAOI,QAAQ,OAAE,SAAM,cAAS,CAC7C,WAA2D,KAAA,QAAA,WAAnC,QAAQ,MAAhC,WAA2D,EAAA,SAAA,MAAA,EAAb,UAAS,CAAA,CAAA,CAAA;iBAEnC,QAAQ,GAAE,UAAA,SAAY,cAAS,CACnD,WAAiE,KAAA,QAAA,WAAzC,QAAQ,GAAE,SAAlC,WAAiE,EAAA,SAAA,MAAA,EAAb,UAAS,CAAA,CAAA,CAAA;;mBARrC,QAAQ,SAAjB,UAAK;;qBAAgD,MAAM;mBAAU,cAAS,CAC7F,WAAyD,KAAA,QAAA,SAAnC,MAAM,QAA5B,WAAyD,EAAA,SAAA,MAAA,EAAb,UAAS,CAAA,CAAA,CAAA;;;cAY/C,QAAA,cAAZ,WAUO,KAAA,QAAA,WAAA;;IAVkC,MAAM,MAAA,QAAO,CAAC;IAAO,SAAS,MAAA,QAAO;YAUvE,CATL,YAQE,qBAAA;IAPC,SAAS,QAAA,WAAW,MAAA,QAAO,CAAC,KAAK,aAAa;IAC9C,UAAU,QAAA;IACV,gBAAc,eAAA,MAAe,eAAW;IACxC,gBAAc,eAAA,MAAe,eAAW;IACxC,eAAa,eAAA,MAAe,cAAU;IACtC,UAAQ;IACR,UAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1LnB,MAAM,QAAQ;EAmCd,MAAM,OAAO;EASb,MAAM,QAAe,UAAS;EAC9B,MAAM,oBAAoB,IAAI,MAAM,iBAAgB;EACpD,MAAM,iBAAiB,SAAS;GAC9B,WAAW,MAAM,aAAa,kBAAkB;GAChD,MAAM,UAAmB;AACvB,sBAAkB,QAAQ;AAC1B,SAAK,oBAAoB,MAAK;;GAEjC,CAAA;EAED,MAAM,oBAAoB,eAAe,MAAM,YAAY,WAAU;EACrE,MAAM,mBAAmB,eAAe,MAAM,YAAY,CAAC,kBAAkB,MAAK;EAClF,MAAM,gBAAgB,eAAe,MAAM,UAAU,kBAAkB,QAAQ,UAAU,SAAQ;EACjG,MAAM,sBAAsB,eAAe,MAAM,eAAe,kBAAkB,MAAK;EAEvF,MAAM,gBAAgB,eAAgD;AACpE,OAAI,MAAM,UAAU,KAAA,EAAW,QAAO,KAAA;AACtC,UAAO,sBAAsB,MAAM,MAAM,GAAG,MAAM,QAAQ,mBAAmB,MAAM,MAAK;IACzF;EAED,MAAM,mBAAmB,eACvB,MAAM,YAAY,cAAc,OAAO,SACzC;EAEA,MAAM,yBAAyB,eAC7B,6BAA6B,cAAc,OAAO,kBAAkB,EAAE,EAAE,MAAM,eAAc,CAC9F;EAEA,MAAM,kBAAkB,eAAqD;AAC3E,OAAI,CAAC,iBAAiB,MAAO,QAAO,EAAC;AACrC,UAAO,wBAAwB,iBAAiB,OAAO,uBAAuB,MAAK;IACpF;EAED,MAAM,iBAAiB,eAA2C;AAChE,OAAI,CAAC,iBAAiB,MAAO,QAAO,EAAC;AACrC,UAAO,6BAA6B,iBAAiB,OAAO,uBAAuB,MAAK;IACzF;EAED,MAAM,oBAAoB,eAAwC;AAChE,OAAI,CAAC,iBAAiB,MAAO,QAAO,EAAC;AACrC,UAAO;IACL,GAAG,mBAAmB,iBAAiB,MAAM;IAC7C,GAAI,uBAAuB,MAAM,iBAAiB,EAAE;IACtD;IACD;EAED,MAAM,iBAAiB,eACrB,mBAAmB,gBAAgB,OAAO,MAAM,OAAO,CACzD;EAEA,MAAM,gBAAgB,gBAA4C;GAChE,GAAG,eAAe;GAClB,GAAG,MAAM;GACV,EAAC;EAEF,MAAM,iBAAiB,gBAAyC;GAC9D,GAAG,kBAAkB;GACrB,GAAI,MAAM,cAAc,MAAM;GAC/B,EAAC;EAEF,MAAM,qBAAqB,eAAe;AACxC,OAAI,MAAM,WAAY,QAAO,MAAM;AACnC,OAAI,MAAM,YAAa,QAAO,MAAM;AACpC,UAAO,mBAAmB,eAAe,MAAK;IAC/C;EAED,MAAM,iBAAiB,eAAqC;AAC1D,OAAI,CAAC,mBAAmB,SAAS,CAAC,eAAe,MAAM,mBAAmB,OAAQ,QAAO,EAAC;AAC1F,UAAO,eAAe,MAAM,mBAAmB;IAChD;EAED,MAAM,YAAY,eAChB,eAAe,MAAM,SAAS,KAAK,MAAM,iBAAiB,QAAQ,MAAM,QAAQ,CAClF;EAEA,MAAM,iBAAiB,eAAyB;GAC9C;GACA,iBAAiB,MAAM;GACvB,iBAAiB,MAAM;GACvB,iBAAiB,QAAQ,2BAA2B;GACpD,MAAM,QAAQ,wBAAwB;GACtC,oBAAoB,QAAQ,8BAA8B;GAC1D,eAAe,QAAQ,4BAA4B;GACnD,CAAC,UAAU,QAAQ,yBAAyB;GAC7C,CAAA;EAED,MAAM,eAAe,gBAAwC,EAC3D,OAAO,eAAe,QAAQ,MAAM,iBAAiB,cAAc,OACpE,EAAC;EAEF,SAAS,iBAAiB,QAAiC;GACzD,MAAM,aAAa;IAAE,GAAG,eAAe;IAAO,GAAG;IAAO;AACxD,QAAK,qBAAqB,WAAU;AACpC,QAAK,iBAAiB,WAAU;;EAGlC,SAAS,iBAAiB,WAAmB,QAAiC;AAC5E,QAAK,eAAe,WAAW,OAAM;;EAGvC,SAAS,iBAAiB,WAAmB;AAC3C,QAAK,eAAe,UAAS;;EAG/B,SAAS,kBAAkB;AACzB,kBAAe,QAAQ,CAAC,eAAe;;EAGzC,SAAS,kBAAkB;AACzB,kBAAe,QAAQ;;EAGzB,SAAS,mBACP,WACA,UACsC;GACtC,MAAM,SAA+C,EAAC;GACtD,MAAM,UAAU,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,UAAU,EAAE,GAAG,OAAO,KAAK,SAAS,CAAC,CAAA;AAE7E,QAAK,MAAM,UAAU,SAAS;IAC5B,MAAM,+BAAe,IAAI,KAAgC;AACzD,SAAK,MAAM,WAAW,UAAU,WAAW,EAAE,CAC3C,cAAa,IAAI,QAAQ,IAAI,QAAO;AAEtC,SAAK,MAAM,WAAW,SAAS,WAAW,EAAE,CAC1C,cAAa,IAAI,QAAQ,IAAI,QAAO;AAEtC,WAAO,UAAU,CAAC,GAAG,aAAa,QAAQ,CAAA;;AAG5C,UAAO;;EAGT,SAAS,mBAAmB,QAAsD;AAChF,QAAK,MAAM,CAAC,QAAQ,aAAa,OAAO,QAAQ,OAAO,CACrD,KAAI,SAAS,SAAS,EAAG,QAAO;AAElC,UAAO;;EAGT,SAAS,sBAAsB,OAAyE;AACtG,UAAO,cAAc,SAAS,oBAAoB;;;uBAKlD,mBAuGQ,SAAA;IAtGL,OAAK,eAAE,eAAA,MAAc;IACrB,OAAK,eAAE,aAAA,MAAY;;IAIZC,KAAAA,OAAO,UAAU,QAAA,SAAS,QAAA,YAAY,QAAA,UAAU,KAAA,KAAa,oBAAA,SAAA,WAAA,EADrE,mBAuCM,OAvCN,eAuCM,CAnCJ,WAkCO,KAAA,QAAA,UAAA;KAhCJ,WAAW,eAAA;KACO;aA+Bd,CAAA,CA7BO,eAAA,SAAA,WAAA,EAAZ,mBAMM,OANN,eAMM,CALJ,mBAGM,OAHN,eAGM,CAFM,QAAA,SAAA,WAAA,EAAV,mBAA6D,MAA7D,eAA6D,gBAAb,QAAA,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,EAC5C,QAAA,YAAA,WAAA,EAAT,mBAAoE,KAApE,eAAoE,gBAAf,QAAA,SAAQ,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,EAEnD,QAAA,UAAU,KAAA,KAAA,WAAA,EAAtB,mBAA+E,QAA/E,eAA+E,gBAAf,QAAA,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,EAG/D,oBAAA,SAAA,WAAA,EADR,mBAqBS,UAAA;;KAnBP,MAAK;KACL,OAAM;KACL,cAAY,eAAA,QAAiB,QAAA,oBAAoB,QAAA;KACjD,iBAAa,CAAG,eAAA;KAChB,SAAO;sBAER,mBAYM,OAAA;KAXJ,OAAK,eAAA,CAAC,+BAA6B,EAAA,0CACiB,eAAA,OAAc,CAAA,CAAA;KAClE,SAAQ;KACR,MAAK;KACL,QAAO;KACP,gBAAa;KACb,kBAAe;KACf,mBAAgB;KAChB,eAAY;sCAEZ,mBAA0C,QAAA,EAApC,GAAE,iCAA+B,EAAA,MAAA,GAAA,CAAA,EAAA,EAAA,EAAA,EAAA,EAAA,GAAA,cAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;KAQtC,eAAA,SAAA,WAAA,EADT,mBA0CM,OAAA;;KAxCH,IAAI,QAAA;KACL,OAAM;0BAEN,mBA8BkB,UAAA,MAAA,WA7BE,eAAA,QAAX,YAAO;yBADhB,YA8BkB,yBAAA;MA5Bf,KAAK,QAAQ;MACb,OAAO,QAAQ;MACf,UAAU,QAAQ;MAClB,MAAM,QAAQ;MACd,cAAY,QAAQ;MACpB,WAAS,QAAQ;MACjB,OAAO,QAAA;MACP,gBAAc,QAAQ,gBAAW;MACjC,eAAa,QAAQ;MACrB,gBAAc,QAAA,YAAY,QAAQ,OAAE;MACpC,yBAAmB,WAAE,KAAI,iBAAkB,QAAQ,IAAI,OAAM;;6BAiBvD,CAfP,WAeO,KAAA,QAAA,WAfiB,QAAQ,MAAE,EAAA,QAe3B,CAbG,cAAA,MAAc,QAAQ,OAAA,WAAA,EAD9B,YAaE,qBAAA;;OAXC,eAAa,eAAA;OACb,QAAQ,cAAA,MAAc,QAAQ;OAC9B,cAAc,QAAA;OACd,SAAS,QAAA;OACT,UAAU,QAAA;OACV,UAAU,QAAA;OACV,MAAM,QAAA;OACN,gBAAc,QAAA;OACd,uBAAoB;OACpB,WAAM,WAAE,iBAAiB,QAAQ,IAAI,OAAM;OAC3C,WAAM,WAAE,iBAAiB,QAAQ,GAAE;;;;;;;;;;;;;;;;;;;;;;;;;;eAK1C,WAIE,KAAA,QAAA,WAAA;KAHC,UAAU,eAAA;KACV,YAAa,mBAAA;KACb,QAAQ,eAAA;8BAIGA,KAAAA,OAAO,aAAA,WAAA,EAAvB,mBAMM,OANN,eAMM,CALJ,WAIE,KAAA,QAAA,aAAA;KAFC,UAAU,eAAA;KACV,QAAQ;;KAKD,eAAA,SAAkBA,KAAAA,OAAO,UAAA,WAAA,EAArC,mBAEM,OAFN,gBAEM,CADJ,WAAsB,KAAA,QAAA,SAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1W5B,MAAM,QAAQ;EAWd,MAAM,OAAO;EAIb,MAAM,sBAAsB,IAAI,MAAM,mBAAkB;EACxD,MAAM,mBAAmB,SAAS;GAChC,WAAW,MAAM,eAAe,oBAAoB;GACpD,MAAM,UAAmB;AACvB,wBAAoB,QAAQ;AAC5B,SAAK,sBAAsB,MAAK;;GAEnC,CAAA;EAED,MAAM,gBAAgB,eAAe;GACnC;GACA,MAAM,oBAAoB,UAAU,+BAA+B;GACnE,MAAM,WAAW,0BAA0B;GAC3C,MAAM,oBAAoB,oCAAoC;GAC9D,iBAAiB,QAAQ,8BAA8B;GACxD,CAAA;EAED,MAAM,eAAe,eAAe;AAClC,UAAO,MAAM,iBAAiB,SAAS,EAAE,OAAO,MAAM,cAAc,GAAG,KAAA;IACxE;EAED,SAAS,gBAAgB;AACvB,oBAAiB,QAAQ,CAAC,iBAAiB;;EAG7C,SAAS,eAAe;AACtB,oBAAiB,QAAQ;;;uBAKzB,mBA4DM,OAAA,EA5DA,OAAK,eAAE,cAAA,MAAa,EAAA,EAAA,CACbC,KAAAA,OAAO,UAAA,WAAA,EAAlB,mBAEM,OAFN,eAEM,CADJ,WAAsB,KAAA,QAAA,SAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,EAGxB,mBAsDM,OAtDN,eAsDM;IApDI,QAAA,qBAAqBA,KAAAA,OAAO,WAAA,WAAA,EADpC,mBAkCS,UAAA;;KAhCP,MAAK;KACL,OAAM;KACL,cAAY,iBAAA,QAAmB,QAAA,oBAAoB,QAAA;KACnD,iBAAe,iBAAA;KACf,SAAO;SAGC,iBAAA,SAAA,WAAA,EADT,mBAYM,OAZN,eAYM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAAoC,QAAA,EAA9B,GAAE,2BAAyB,EAAA,MAAA,GAAA,CAAA,EAAA,CAAA,KAAA,WAAA,EAEnC,mBAYM,OAZN,eAYM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAAiC,QAAA,EAA3B,GAAE,wBAAsB,EAAA,MAAA,GAAA,CAAA,EAAA,CAAA,EAAA,EAAA,GAAA,cAAA,IAAA,mBAAA,IAAA,KAAA;IAK1B,QAAA,qBAAqBA,KAAAA,OAAO,WAAW,iBAAA,SAAA,WAAA,EAD/C,mBAIE,OAAA;;KAFA,OAAM;KACL,SAAO;;IAIFA,KAAAA,OAAO,WAAA,WAAA,EADf,mBAMM,OAAA;;KAJJ,OAAM;KACL,OAAK,eAAE,aAAA,MAAY;QAEpB,WAAuB,KAAA,QAAA,UAAA,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;IAGzB,mBAEO,QAFP,eAEO,CADL,WAAQ,KAAA,QAAA,UAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEwChB,MAAM,QAAQ;EAsEd,MAAM,OAAO;EAsDb,MAAM,uBAAuB,IAAI,IAAI;GACnC;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAA;EAED,MAAM,wBAAwB,IAAI,IAAI;GACpC;GACA;GACA;GACD,CAAA;EAED,MAAM,QAAQ,UAAS;EACvB,MAAM,qBAAqB,IAAI,MAAM,kBAAiB;AAEtD,MAAI,MAAM,gBACR,kBAAiB;GACf,YAAY,eAAe,MAAM,WAAW;GAC5C,WAAU,eAAc,KAAK,qBAAqB,WAAW;GAC7D,QAAQ,MAAM;GACd,gBAAgB,KAAK,oBAAoB;GACzC,cAAc,eAAe,MAAM,uBAAuB;GAC1D,qBAAqB,eAAe,MAAM,8BAA8B;GACzE,CAAA;EAGH,MAAM,kBAAkB,eACtB,OAAO,KAAK,MAAM,CAAC,QAAO,SACxB,qBAAqB,IAAI,KAAK,IAAI,KAAK,WAAW,gBAAgB,CACnE,CACH;EAEA,MAAM,mBAAmB,eACvB,OAAO,KAAK,MAAM,CAAC,QAAO,SACxB,sBAAsB,IAAI,KAAK,IAAI,KAAK,WAAW,WAAW,CAC/D,CACH;EAEA,MAAM,gBAAgB,eAAgD;AACpE,OAAI,MAAM,UAAU,KAAA,EAAW,QAAO,KAAA;AACtC,UAAO,sBAAsB,MAAM,MAAM,GAAG,MAAM,QAAQ,mBAAmB,MAAM,MAAK;IACzF;EAED,MAAM,mBAAmB,eACvB,MAAM,YAAY,cAAc,OAAO,SACzC;EAEA,MAAM,yBAAyB,eAC7B,6BAA6B,cAAc,OAAO,kBAAkB,EAAE,EAAE,MAAM,eAAe,CAC/F;EAEA,MAAM,wBAAwB,eAAe,MAAM,cAAc,MAAM,OAAM;EAC7E,MAAM,wBAAwB,SAAkC,EAAE,CAAA;EAClE,IAAI,uBAAuB;EAE3B,MAAM,2BAA2B,eAAwC;AACvE,OAAI,CAAC,iBAAiB,MAAO,QAAO,EAAC;AACrC,UAAO;IACL,GAAG,mBAAmB,iBAAiB,MAAM;IAC7C,GAAI,uBAAuB,MAAM,iBAAiB,EAAE;IACtD;IACD;AAED,6BAA0B;AAE1B,QACE,CAAC,0BAA0B,sBAAsB,EACjD,2BACA,EAAE,MAAM,MAAM,CAChB;AAEA,QACE,6BACM;AACJ,OAAI,qBAAsB;AAC1B,sBAAkB;KAEpB;GAAE,MAAM;GAAM,OAAO;GAAQ,CAC/B;EAEA,MAAM,4BAA4B,eAChC,iCACE,uBACA,MAAM,qBAAqB,cAAc,OAAO,kBACjD,CACH;EAEA,MAAM,gCAAgC,eACpC,qCACE,uBACA,MAAM,qBAAqB,cAAc,OAAO,kBACjD,CACH;EAEA,MAAM,yBAAyB,eAAe;GAC5C,MAAM,UAAU,MAAM,kBAAkB,cAAc,OAAO;AAC7D,UAAO,YAAY,KAAA,IAAY,EAAE,GAAG,8BAA8B,uBAAuB,QAAO;IACjG;EAED,MAAM,6BAA6B,eAAe;GAChD,MAAM,WAAW,MAAM,sBAAsB,cAAc,OAAO;AAClE,OAAI,aAAa,KAAA,EAAW,QAAO,EAAC;AAEpC,UAAO,OAAO,YACZ,OAAO,QAAQ,SAAS,CAAC,KAAK,CAAC,IAAI,aAAa,CAC9C,IACA,8BAA8B,uBAAuB,QAAQ,CAC9D,CAAC,CACJ;IACD;EAED,MAAM,mBAAmB,eAA8B;AACrD,OAAI,CAAC,iBAAiB,MAAO,QAAO,EAAC;AACrC,UAAO,oBAAoB,iBAAiB,OAAO,uBAAuB,MAAK;IAChF;EAED,MAAM,uBAAuB,eAAe;AAC1C,OAAI,CAAC,iBAAiB,MAAO,QAAO;AACpC,UAAO,sBAAsB,iBAAiB,OAAO,uBAAuB,MAAK;IAClF;EAED,MAAM,kBAAkB,eAA+C;AACrE,OAAI,MAAM,YAAY,KAAA,EAAW,QAAO,MAAM;AAC9C,UAAO,iBAAiB,MAAM,SAAS,IAAI,iBAAiB,QAAQ,KAAA;IACrE;EAED,MAAM,wBAAwB,eAC5B,MAAM,qBACD,YAAY,MAAM,QAAO,IACzB,qBAAqB,SACrB,YAAY,MAAM,aAAY,IAC9B,OAAO,KAAK,MAAM,OAAO,CAAC,MAC1B,GACP;EAEA,MAAM,qBAAqB,SAAS;GAClC,WACE,MAAM,cACD,MAAM,iBACN,MAAM,yBACN,mBAAmB,SACnB,sBAAsB;GAC7B,MAAM,UAAkB;AACtB,uBAAmB,QAAQ;AAC3B,SAAK,qBAAqB,MAAK;;GAElC,CAAA;EAED,MAAM,wBAAwB,eAAe,MAAM,iBAAiB,mBAAmB,MAAK;EAC5F,MAAM,gCAAgC,eAAe,MAAM,yBAAyB,mBAAmB,MAAK;EAE5G,MAAM,oBAAoB,eAAe;AACvC,OAAI,CAAC,MAAM,YAAa,QAAO;AAC/B,UACE,OAAO,KAAK,MAAM,OAAO,CAAC,SAAS,KAChC,OAAO,KAAK,MAAM,MAAM,CAAC,SAAS,KAClC,MAAM,UAAU,KAAA,KAChB,MAAM,aAAa,KAAA,KACnB,MAAM,wBACN,MAAM,qBAAqB,KAAA,KAC3B,MAAM,iBAAiB,KAAA,KACvB,MAAM,oBAAoB,KAAA,KAC1B,MAAM,iBAAiB,KAAA,KACvB,QAAQ,MAAM,QAAO,IACrB,iBAAiB,MAAM,SAAS;IAEtC;EAED,SAAS,YAAY,OAAkE;GACrF,MAAM,OAAO,QAAQ;AACrB,OAAI,SAAS,KAAA,EAAW,QAAO,KAAA;AAC/B,UAAO,OAAO,SAAS,WAAW,OAAO,KAAK;;EAGhD,SAAS,sBAAsB,OAAyE;AACtG,UAAO,cAAc,SAAS,oBAAoB;;EAGpD,SAAS,cAAc,QAAgB;AACrC,sBAAmB,QAAQ;;EAG7B,SAAS,yBAAyB,MAAwB;AACxD,iBAAc,KAAK,GAAE;AACrB,QAAK,wBAAwB,KAAI;;EAGnC,SAAS,iBAAiB,MAAmB;AAC3C,iBAAc,KAAK,GAAE;AACrB,QAAK,eAAe,KAAI;;EAG1B,SAAS,uBAAuB,QAAuB,MAAmB;AACxE,QAAK,sBAAsB,QAAQ,KAAI;;EAGzC,SAAS,aAAa,WAAmB,OAAgB;AACvD,QAAK,iBAAiB,WAAW,MAAK;;EAGxC,SAAS,0BAA0B,QAAiC;AAClE,0BAAuB;AACvB,wBAAqB;IACnB,GAAG,yBAAyB;IAC5B,GAAG;IACJ,CAAA;AACD,0BAAuB;AACvB,sBAAkB;;EAGpB,SAAS,iBAAiB,WAAmB,QAAiC;AAC5E,QAAK,eAAe,WAAW,OAAM;;EAGvC,SAAS,iBAAiB,WAAmB;AAC3C,QAAK,eAAe,UAAS;;EAG/B,SAAS,4BAA4B;AACnC,0BAAuB;AACvB,wBAAqB;IACnB,GAAG,yBAAyB;IAC5B,GAAI,sBAAsB,UAAU,KAAA,IAAY,wBAAwB,sBAAsB;IAC/F,CAAA;AACD,0BAAuB;;EAGzB,SAAS,qBAAqB,QAAiC;AAC7D,QAAK,MAAM,OAAO,OAAO,KAAK,sBAAsB,CAClD,KAAI,EAAE,OAAO,QACX,QAAO,sBAAsB;AAGjC,UAAO,OAAO,uBAAuB,OAAM;;EAG7C,SAAS,oBAAoB;GAC3B,MAAM,aAAa,EAAE,GAAG,uBAAsB;AAC9C,QAAK,qBAAqB,WAAU;AACpC,QAAK,iBAAiB,WAAU;;;uBAMhC,YA+IY,mBAAA;IA9IV,OAAM;IACL,oBAAkB,QAAA;IAClB,iBAAe,QAAA;IACf,UAAU,QAAA;IACV,sBAAoB,QAAA;IACpB,wBAAsB,QAAA;IACtB,uBAAqB,QAAA;;IAEX,QAAM,cA4DR,CA3DP,WA2DO,KAAA,QAAA,UAAA;KAzDJ,YAAa,mBAAA;KACI;KACjB,QAAQ;KACR,mBAAoB,0BAAA;KACpB,uBAA0B,8BAAA;KAC1B,gBAAiB,uBAAA;KACjB,oBAAuB,2BAAA;aAmDnB,CAjDL,YAgDY,mBAAA;KA/CT,OAAO,QAAA;KACP,UAAU,QAAA;KACV,SAAS,QAAA;KACT,aAAW,QAAA;KACX,aAAW,QAAA;KACX,iBAAe,QAAA;KACf,4BAA0B,8BAAA;KAC1B,mBAAiB,QAAA;KACjB,YAAU,gBAAA;KACV,mBAAiB,sBAAA;KACjB,qBAAmB,QAAA;KACnB,iBAAe,QAAA;KACf,mBAAiB,QAAA;KACjB,yBAAuB,QAAA;KACvB,oBAAkB,QAAA;KAClB,gBAAc,QAAA;KACd,sBAAoB,QAAA;KACpB,wBAAsB,QAAA;KACtB,cAAY,QAAA;KACZ,cAAY,QAAA;KACZ,gBAAc,QAAA;KACd,aAAW,QAAA;KACX,gBAAc,QAAA;KACd,cAAY,QAAA;KACZ,sBAAsB;KACtB,cAAa;KACb,oBAAoB;KACpB,wBAAsB,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,0BAA2B,OAAM;KAC7D,yBAAuB,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,0BAAA;KAC7B,qBAAmB,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,uBAAwB,OAAM;KACvD,WAAQ,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,WAAA;KACd,sBAAmB,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,sBAAA;KACzB,wBAAsB,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,0BAA2B,OAAM;KAC7D,gBAAa,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,gBAAA;KACnB,cAAW,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,cAAA;yCAGG,gBAAA,QAAZ,aAAQ;;YAEd;mBAAY,cAAS,CAGtB,WAGE,KAAA,QAFO,UAAQ,eAAA,mBACP,aAAS,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BA6EzB,CARF,WAQE,KAAA,QAAA,WAAA;KAPC,YAAa,mBAAA;KACI;KACjB,QAAQ;KACR,mBAAoB,0BAAA;KACpB,uBAA0B,8BAAA;KAC1B,gBAAiB,uBAAA;KACjB,oBAAuB,2BAAA;;;OArEV,kBAAA,QAAA;UAAoB;sBA2D3B,CA1DP,WA0DO,KAAA,QAAA,WAAA;KAxDJ,YAAa,mBAAA;KACI;KACjB,QAAQ;KACR,mBAAoB,0BAAA;KACpB,uBAA0B,8BAAA;KAC1B,gBAAiB,uBAAA;KACjB,oBAAuB,2BAAA;aAkDnB,CAhDL,YA+Ca,oBAAA;KA9CV,OAAO,QAAA;KACP,UAAU,QAAA;KACV,OAAO,QAAA;KACP,SAAS,QAAA;KACT,QAAQ,QAAA;KACR,eAAa,mBAAA;KACb,UAAU;KACV,OAAO,QAAA;KACP,OAAO,QAAA;KACP,MAAM,QAAA;KACN,OAAO,QAAA;KACP,cAAY,QAAA;KACZ,mBAAiB,QAAA,wBAAwB,QAAA,qBAAqB,KAAA;KAC9D,OAAO,QAAA;KACP,UAAU,QAAA;KACV,mBAAiB,QAAA;KACjB,QAAQ;KACR,qBAAmB,QAAA;KACnB,qBAAmB,QAAA;KACnB,gBAAc,QAAA;KACd,iBAAe,QAAA;KACf,iBAAe,QAAA;KACf,aAAW,QAAA;KACX,aAAa,QAAA;KACb,WAAW,QAAA;KACX,qBAAmB,QAAA;KACnB,mBAAiB,QAAA;KACjB,yBAAuB,QAAA;KACvB,uBAAqB,QAAA;KACrB,uBAAoB;KACpB,sBAAgB,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,2BAA4B,OAAM;KACxD,mBAAe;KACf,cAAa;KACb,cAAa;yCAGO,iBAAA,QAAZ,aAAQ;;YAEd;mBAAY,cAAS,CAGtB,WAGE,KAAA,QAFO,UAAQ,eAAA,mBACP,aAAS,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EExpB/B,MAAM,QAAQ;EAKd,MAAM,OAAO;EAKb,MAAM,EAAE,mBAAmB,uBAAsB;EACjD,MAAM,EACJ,aACA,mBACA,yBACA,cACA,+BACE,mBAAkB;EAGtB,MAAM,aAAa,IAAoB,MAAM,SAAS,SAAS,aAAa,MAAM,KAAI;EAGtF,MAAM,qBAAqB,IAAwB;GAAE,OAAO;GAAI,MAAM;GAAM,CAAA;EAC5E,MAAM,qBAAqB,IAAwB;GAAE,OAAO;GAAK,MAAM;GAAM,CAAA;EAC7E,MAAM,cAAc,IAAiB;GAAE,OAAO;GAAG,MAAM;GAAM,CAAA;EAG7D,MAAM,2BAA2B,IAAwB;GAAE,OAAO;GAAK,MAAM;GAAM,CAAA;EACnF,MAAM,uBAAuB,IAAI,EAAC;EAClC,MAAM,0BAA0B,IAAI,EAAC;EACrC,MAAM,sBAAsB,IAAiB;GAAE,OAAO;GAAK,MAAM;GAAM,CAAA;EAGvE,MAAM,iBAAiB,IAA2B,KAAI;EACtD,MAAM,eAAe,IAAiC,KAAI;EAG1D,MAAM,WAAW,eAAe,MAAM,SAAS,OAAM;EAErD,MAAM,iBAAiB,eAAe;AACpC,UAAO,MAAM,eAAe,MAAM,YAAY,SAAS;IACxD;EAED,MAAM,kBAAkB,eAAe;AACrC,OAAI,CAAC,eAAe,MAAO,QAAO;AAClC,OAAI,WAAW,UAAU,YAAY,aAAa,OAAO,MAAO,QAAO;AACvE,OAAI,WAAW,UAAU,cAAc,eAAe,OAAO,MAAO,QAAO;AAC3E,UAAO;IACR;EAGD,SAAS,sBAAsB;AAC7B,OAAI,CAAC,mBAAmB,SAAS,CAAC,mBAAmB,SAAS,CAAC,YAAY,OAAO;AAChF,mBAAe,QAAQ;AACvB;;AAGF,kBAAe,QAAQ,kBAAkB;IACvC,oBAAoB,mBAAmB;IACvC,oBAAoB,mBAAmB;IACvC,aAAa,YAAY;IAC1B,CAAA;AAED,OAAI,eAAe,MAAM,MACvB,MAAK,aAAa,eAAe,MAAK;;EAK1C,SAAS,oBAAoB;AAC3B,OACE,CAAC,yBAAyB,SAC1B,qBAAqB,SAAS,KAC9B,wBAAwB,QAAQ,KAChC,CAAC,oBAAoB,OACrB;AACA,iBAAa,QAAQ;AACrB;;AAGF,gBAAa,QAAQ,wBAAwB;IAC3C,uBAAuB,yBAAyB;IAChD,gBAAgB,qBAAqB;IACrC,mBAAmB,wBAAwB;IAC3C,eAAe,oBAAoB;IACpC,CAAA;AAED,OAAI,aAAa,MAAM,MACrB,MAAK,aAAa,aAAa,MAAK;;EAKxC,SAAS,qBAAqB;AAC5B,OAAI,CAAC,MAAM,eAAe,MAAM,YAAY,WAAW,EAAG;AAE1D,OAAI,WAAW,UAAU,YAAY,aAAa,OAAO,MAKvD,MAAK,kBAJsB,2BACzB,aAAa,OACb,MAAM,YACR,CACyC;AAG3C,OAAI,WAAW,UAAU,cAAc,eAAe,OAAO,MAO3D,MAAK,kBAL2C,MAAM,YAAY,KAAI,YAAW;IAC/E;IACA,eAAe,mBAAmB;IAClC,QAAQ,YAAY;IACrB,EAAC,CACuC;;EAK7C,SAAS,mBAAmB,OAAc,QAA4B;GACpE,MAAM,QAAQ,MAAM;GACpB,MAAM,QAAQ,WAAW,MAAM,MAAK;AACpC,OAAI,MAAM,MAAM,CAAE;AAElB,OAAI,WAAW,QACb,aAAY,QAAQ;IAAE,GAAG,YAAY;IAAO;IAAM;OAElD,qBAAoB,QAAQ;IAAE,GAAG,oBAAoB;IAAO;IAAM;;EAItE,SAAS,uBAAuB,OAAc,QAA4B;GAExE,MAAM,OADS,MAAM,OACD;AAEpB,OAAI,WAAW,QACb,aAAY,QAAQ;IAAE,GAAG,YAAY;IAAO;IAAK;OAEjD,qBAAoB,QAAQ;IAAE,GAAG,oBAAoB;IAAO;IAAK;;AAKrE,QACE;GAAC;GAAoB;GAAoB;GAAY,QAC/C;AACJ,OAAI,WAAW,UAAU,WACvB,sBAAoB;KAGxB;GAAE,MAAM;GAAM,WAAW;GAAK,CAChC;AAEA,QACE;GAAC;GAA0B;GAAsB;GAAyB;GAAoB,QACxF;AACJ,OAAI,WAAW,UAAU,SACvB,oBAAkB;KAGtB;GAAE,MAAM;GAAM,WAAW;GAAK,CAChC;AAEA,QAAM,aAAa,SAAS;AAC1B,OAAI,SAAS,WACX,sBAAoB;YACX,SAAS,SAClB,oBAAkB;IAErB;;uBAIC,mBAiRM,OAAA,EAhRH,OAAK,eAAA,CAAA,wBAAwC,QAAA,WAAQ,mCAAA,GAAA,CAAA,EAAA,EAAA;IAM3C,SAAA,SAAA,WAAA,EAAX,mBAqCM,OArCN,eAqCM;KApCJ,mBAWS,UAAA;MAVP,MAAK;MACJ,OAAK,eAAA,CAAA,6BAAqD,WAAA,UAAU,aAAA,sCAAA,GAAA,CAAA;MAIrE,MAAK;MACJ,iBAAe,WAAA,UAAU;MACzB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,WAAA,QAAU;QACnB,cAED,IAAA,cAAA;KACA,mBAWS,UAAA;MAVP,MAAK;MACJ,OAAK,eAAA,CAAA,6BAAqD,WAAA,UAAU,WAAA,sCAAA,GAAA,CAAA;MAIrE,MAAK;MACJ,iBAAe,WAAA,UAAU;MACzB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,WAAA,QAAU;QACnB,YAED,IAAA,cAAA;KACA,mBAWS,UAAA;MAVP,MAAK;MACJ,OAAK,eAAA,CAAA,6BAAqD,WAAA,UAAU,eAAA,sCAAA,GAAA,CAAA;MAIrE,MAAK;MACJ,iBAAe,WAAA,UAAU;MACzB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,WAAA,QAAU;QACnB,gBAED,IAAA,cAAA;;IAKM,WAAA,UAAU,cAAA,WAAA,EADlB,mBA8EM,OA9EN,eA8EM;KAzEJ,mBAQM,OARN,eAQM,CAAA,OAAA,QAAA,OAAA,MAPJ,mBAA4E,SAAA,EAArE,OAAM,qCAAmC,EAAC,uBAAmB,GAAA,GACpE,YAKE,4BAAA;kBAJS,mBAAA;sFAAkB,QAAA;MAC1B,UAAU,QAAA;MACV,mBAAiB;MAClB,MAAK;;KAIT,mBAQM,OARN,eAQM,CAAA,OAAA,QAAA,OAAA,MAPJ,mBAA4E,SAAA,EAArE,OAAM,qCAAmC,EAAC,uBAAmB,GAAA,GACpE,YAKE,4BAAA;kBAJS,mBAAA;sFAAkB,QAAA;MAC1B,UAAU,QAAA;MACV,mBAAiB;MAClB,MAAK;;KAIT,mBAuBM,OAvBN,eAuBM,CAAA,OAAA,QAAA,OAAA,MAtBJ,mBAAqE,SAAA,EAA9D,OAAM,qCAAmC,EAAC,gBAAY,GAAA,GAC7D,mBAoBM,OApBN,eAoBM,CAnBJ,mBAQE,SAAA;MAPA,MAAK;MACJ,OAAO,YAAA,MAAY;MACnB,UAAU,QAAA;MACX,OAAM;MACN,KAAI;MACJ,MAAK;MACJ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,mBAAmB,QAAM,QAAA;mCAEnC,mBASS,UAAA;MARN,OAAO,YAAA,MAAY;MACnB,UAAU,QAAA;MACX,OAAM;MACL,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,uBAAuB,QAAM,QAAA;2BAEtC,mBAES,UAAA,MAAA,WAFc,MAAA,YAAW,GAAnB,SAAI;0BAAnB,mBAES,UAAA;OAF4B,KAAK;OAAO,OAAO;yBACnD,KAAI,EAAA,GAAA,eAAA;;iCAMf,mBAA6C,OAAA,EAAxC,OAAM,iCAA+B,EAAA,MAAA,GAAA;KAG/B,eAAA,OAAgB,SAAA,WAAA,EAA3B,mBAoBM,OApBN,gBAoBM;kCAnBJ,mBAA4D,OAAA,EAAvD,OAAM,sCAAoC,EAAC,UAAM,GAAA;MACtD,mBAKM,OALN,gBAKM,CAAA,OAAA,QAAA,OAAA,MAJJ,mBAAoE,QAAA,EAA9D,OAAM,sCAAoC,EAAC,gBAAY,GAAA,GAC7D,mBAEO,QAFP,gBAEO,gBADF,MAAA,aAAY,CAAC,eAAA,MAAe,YAAW,CAAA,EAAA,EAAA,CAAA,CAAA;MAG9C,mBAKM,OALN,gBAKM,CAAA,OAAA,QAAA,OAAA,MAJJ,mBAAsE,QAAA,EAAhE,OAAM,sCAAoC,EAAC,kBAAc,GAAA,GAC/D,mBAEO,QAFP,gBAEO,gBADF,MAAA,aAAY,CAAC,eAAA,MAAe,cAAa,CAAA,EAAA,EAAA,CAAA,CAAA;MAGhD,mBAKM,OALN,gBAKM,CAAA,OAAA,QAAA,OAAA,MAJJ,mBAAuE,QAAA,EAAjE,OAAM,sCAAoC,EAAC,mBAAe,GAAA,GAChE,mBAEO,QAFP,gBAEO,gBADF,eAAA,MAAe,eAAe,QAAO,EAAA,CAAA,GAAM,MAChD,EAAA,CAAA,CAAA;WAIY,eAAA,OAAgB,SAAA,WAAA,EAAhC,mBAEM,OAFN,gBAEM,gBADD,eAAA,MAAe,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;UAMd,WAAA,UAAU,YAAA,WAAA,EADvB,mBA+FM,OA/FN,gBA+FM;KA1FJ,mBAQM,OARN,eAQM,CAAA,OAAA,QAAA,OAAA,MAPJ,mBAA+E,SAAA,EAAxE,OAAM,qCAAmC,EAAC,0BAAsB,GAAA,GACvE,YAKE,4BAAA;kBAJS,yBAAA;4FAAwB,QAAA;MAChC,UAAU,QAAA;MACV,mBAAiB;MAClB,MAAK;;KAIT,mBA2BM,OA3BN,eA2BM,CA1BJ,mBAaM,OAbN,eAaM,CAAA,OAAA,QAAA,OAAA,MAZJ,mBAAwE,SAAA,EAAjE,OAAM,qCAAmC,EAAC,mBAAe,GAAA,GAChE,mBAUM,OAVN,eAUM,CAAA,eATJ,mBAOE,SAAA;wFANoC,QAAA;MACpC,MAAK;MACJ,UAAU,QAAA;MACX,OAAM;MACN,KAAI;MACJ,MAAK;;;MALW,qBAAA;;QAAR,QAAR,MAAqC;sCAOvC,mBAAwD,QAAA,EAAlD,OAAM,qCAAmC,EAAC,KAAC,GAAA,EAAA,CAAA,CAAA,CAAA,EAIrD,mBAUM,OAVN,eAUM,CAAA,OAAA,QAAA,OAAA,MATJ,mBAAkE,SAAA,EAA3D,OAAM,qCAAmC,EAAC,aAAS,GAAA,GAAA,eAC1D,mBAOE,SAAA;2FANuC,QAAA;MACvC,MAAK;MACJ,UAAU,QAAA;MACX,OAAM;MACN,KAAI;MACJ,KAAI;;;MALY,wBAAA;;QAAR,QAAR,MAAwC;;KAU9C,mBAuBM,OAvBN,eAuBM,CAAA,OAAA,QAAA,OAAA,MAtBJ,mBAAwE,SAAA,EAAjE,OAAM,qCAAmC,EAAC,mBAAe,GAAA,GAChE,mBAoBM,OApBN,eAoBM,CAnBJ,mBAQE,SAAA;MAPA,MAAK;MACJ,OAAO,oBAAA,MAAoB;MAC3B,UAAU,QAAA;MACX,OAAM;MACN,KAAI;MACJ,MAAK;MACJ,SAAK,OAAA,QAAA,OAAA,OAAA,WAAE,mBAAmB,QAAM,SAAA;kCAEnC,mBASS,UAAA;MARN,OAAO,oBAAA,MAAoB;MAC3B,UAAU,QAAA;MACX,OAAM;MACL,UAAM,OAAA,QAAA,OAAA,OAAA,WAAE,uBAAuB,QAAM,SAAA;2BAEtC,mBAES,UAAA,MAAA,WAFc,MAAA,YAAW,GAAnB,SAAI;0BAAnB,mBAES,UAAA;OAF4B,KAAK;OAAO,OAAO;yBACnD,KAAI,EAAA,GAAA,cAAA;;iCAMf,mBAA6C,OAAA,EAAxC,OAAM,iCAA+B,EAAA,MAAA,GAAA;KAG/B,aAAA,OAAc,SAAA,WAAA,EAAzB,mBAkBM,OAlBN,eAkBM,CAAA,OAAA,QAAA,OAAA,MAjBJ,mBAA8D,OAAA,EAAzD,OAAM,uCAAqC,EAAC,WAAO,GAAA,GACxD,mBAeM,OAfN,eAeM,EAAA,UAAA,KAAA,EAdJ,mBAaW,UAAA,MAAA,WAbuB,aAAA,MAAa,QAA7B,MAAM,UAAK;8DAA+B,KAAK,YAAA,EAAA,CAC/D,mBAKM,OALN,eAKM,CAJJ,mBAAkF,QAAlF,eAAkF,gBAA1B,KAAK,WAAU,GAAG,KAAC,EAAA,EAC3E,mBAEO,QAFP,eAEO,gBADF,MAAA,eAAc,CAAC,KAAK,eAAa,EAAA,CAAA,EAAA,EAAA,CAAA,CAAA,EAIhC,QAAQ,aAAA,MAAa,MAAM,SAAM,KAAA,WAAA,EADzC,mBAKO,QALP,eAGC,MAED,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,GAAA;sBAKU,aAAA,OAAc,SAAA,WAAA,EAA9B,mBAEM,OAFN,eAEM,gBADD,aAAA,MAAa,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;UAMZ,WAAA,UAAU,gBAAA,WAAA,EADvB,mBAqBM,OArBN,eAqBM,CAhBJ,mBAQM,OARN,eAQM,CAAA,OAAA,QAAA,OAAA,MAPJ,mBAA4E,SAAA,EAArE,OAAM,qCAAmC,EAAC,uBAAmB,GAAA,GACpE,YAKE,4BAAA;KAJC,UAAU,QAAA;KACV,oBAAkB,QAAA;KAClB,mBAAiB;KAClB,MAAK;sDAKA,QAAA,mBAAA,WAAA,EADT,mBAKM,OALN,eAGC,4DAED,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAKM,QAAA,mBAAA,WAAA,EADR,mBAOM,OAPN,eAOM,CAHJ,mBAEO,QAFP,eAA6C,wBACzB,gBAAG,QAAA,gBAAe,GAAG,WACzC,EAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAIS,eAAA,SAAA,WAAA,EAAX,mBAYM,OAZN,eAYM,CAXJ,mBAUS,UAAA;KATP,MAAK;KACL,OAAM;KACL,UAAQ,CAAG,gBAAA,SAAmB,QAAA;KAC9B,SAAO;oCAER,mBAEM,OAAA;KAFD,MAAK;KAAO,QAAO;KAAe,SAAQ;QAC7C,mBAA0H,QAAA;KAApH,kBAAe;KAAQ,mBAAgB;KAAQ,gBAAa;KAAI,GAAE;+BACpE,eACG,gBAAG,QAAA,aAAa,OAAM,GAAG,mBAAc,gBAAG,QAAA,aAAa,WAAM,IAAA,KAAA,IAAA,EAAA,EAAA,CAAA,EAAA,GAAA,cAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEjc9E,MAAM,QAAQ;EAad,MAAM,OAAO;EAWb,MAAM,kBAAkB,IAAwB,MAAM,eAAc;EACpE,MAAM,gBAAgB,IAAmB,KAAI;EAC7C,MAAM,iBAAiB,IAAmB,KAAI;EAE9C,MAAM,aAAa,SAAS;GAC1B,WAAW,MAAM,kBAAkB,gBAAgB;GACnD,MAAM,OAAO;AACX,oBAAgB,QAAQ;AACxB,SAAK,eAAe,GAAE;;GAEzB,CAAA;EAED,MAAM,cAAc,eAAe;AACjC,OAAI,MAAM,WAAW,OAAO,MAAM,GAAG,QAAQ,MAAM,KAAK,KAAK,SAAS,IAAI,IAAI,GAAG,MAAM,CACrF,QAAO,MAAM;AAEf,UAAO,CAAC,GAAG,MAAM,WAAW,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAK;IAC9D;EAED,MAAM,gBAAkD;GACtD,YAAY;GACZ,MAAM;GACN,UAAU;GACV,aAAa;GACb,UAAU;GACV,YAAY;GACZ,KAAK;GACL,QAAQ;GACV;EAEA,MAAM,iBAAmD;GACvD,YAAY;GACZ,MAAM;GACN,UAAU;GACV,aAAa;GACb,UAAU;GACV,YAAY;GACZ,KAAK;GACL,QAAQ;GACV;EAEA,MAAM,gBAAoD;GACxD,SAAS;GACT,aAAa;GACb,WAAW;GACX,QAAQ;GACR,SAAS;GACX;EAEA,MAAM,oBAAwD;GAC5D,SAAS;GACT,aAAa;GACb,WAAW;GACX,QAAQ;GACR,SAAS;GACX;EAEA,SAAS,eAAe,SAAqC;AAC3D,OAAI,YAAY,KAAA,EAAW,QAAO;AAClC,OAAI,UAAU,GAAI,QAAO,GAAG,QAAQ;GACpC,MAAM,QAAQ,KAAK,MAAM,UAAU,GAAE;GACrC,MAAM,OAAO,UAAU;AACvB,UAAO,OAAO,IAAI,GAAG,MAAM,IAAI,KAAK,KAAK,GAAG,MAAM;;EAGpD,SAAS,aAAa,MAA4B;AAChD,UAAO,MAAM,eAAe,CAAC,MAAM,gBAAgB,eAAe,KAAK,QAAQ;;EAGjF,SAAS,eAAe,MAA4B;AAClD,UAAO,MAAM,gBAAgB,cAAc,KAAK,UAAU,cAAc;;EAG1E,SAAS,mBAAmB,MAA4B;AACtD,UAAO,MAAM,gBAAgB,kBAAkB,KAAK,UAAU,kBAAkB;;EAGlF,SAAS,aAAa,MAAoB;AACxC,cAAW,QAAQ,WAAW,UAAU,KAAK,KAAK,KAAA,IAAY,KAAK;;EAGrE,SAAS,gBAAgB,MAAoB;AAC3C,QAAK,cAAc,KAAI;AACvB,OAAI,MAAM,YACR,cAAa,KAAI;;EAIrB,SAAS,mBAAmB,MAAoB,QAA4B;GAC1E,MAAM,eAAe,MAAM,WAAW,KAAI,MACxC,EAAE,OAAO,KAAK,KAAK;IAAE,GAAG;IAAG;IAAQ,GAAG,EACxC;AACA,QAAK,sBAAsB,KAAK,IAAI,OAAM;AAC1C,QAAK,qBAAqB,aAAY;;EAGxC,SAAS,iBAAiB,QAAgB;GACxC,MAAM,eAAe,MAAM,WACxB,QAAO,MAAK,EAAE,OAAO,OAAM,CAC3B,KAAK,GAAG,OAAO;IAAE,GAAG;IAAG,OAAO;IAAG,EAAC;AACrC,QAAK,eAAe,OAAM;AAC1B,QAAK,qBAAqB,aAAY;;EAGxC,SAAS,cAAc,aAAsB;AAC3C,QAAK,YAAY,YAAW;;EAG9B,SAAS,gBAAgB,OAAkB,MAAoB;AAC7D,OAAI,CAAC,MAAM,SAAU;AACrB,iBAAc,QAAQ,KAAK;AAC3B,OAAI,MAAM,cAAc;AACtB,UAAM,aAAa,gBAAgB;AACnC,UAAM,aAAa,QAAQ,cAAc,KAAK,GAAE;;;EAIpD,SAAS,eAAe,OAAkB,MAAoB;AAC5D,OAAI,CAAC,MAAM,YAAY,CAAC,cAAc,MAAO;AAC7C,SAAM,gBAAe;AACrB,kBAAe,QAAQ,KAAK;;EAG9B,SAAS,kBAAkB;AACzB,kBAAe,QAAQ;;EAGzB,SAAS,WAAW,OAAkB,YAA0B;AAC9D,OAAI,CAAC,MAAM,YAAY,CAAC,cAAc,MAAO;AAC7C,SAAM,gBAAe;GAErB,MAAM,cAAc,MAAM,WAAW,MAAK,MAAK,EAAE,OAAO,cAAc,MAAK;AAC3E,OAAI,CAAC,eAAe,YAAY,OAAO,WAAW,IAAI;AACpD,kBAAc,QAAQ;AACtB,mBAAe,QAAQ;AACvB;;GAGF,MAAM,QAAQ,CAAC,GAAG,MAAM,WAAU;GAClC,MAAM,eAAe,MAAM,WAAU,MAAK,EAAE,OAAO,cAAc,MAAK;GACtE,MAAM,cAAc,MAAM,WAAU,MAAK,EAAE,OAAO,WAAW,GAAE;AAE/D,SAAM,OAAO,cAAc,EAAC;AAC5B,SAAM,OAAO,aAAa,GAAG,YAAW;GAExC,MAAM,YAAY,MAAM,KAAK,GAAG,OAAO;IAAE,GAAG;IAAG,OAAO;IAAG,EAAC;AAC1D,QAAK,gBAAgB,cAAc,OAAO,YAAW;AACrD,QAAK,qBAAqB,UAAS;AAEnC,iBAAc,QAAQ;AACtB,kBAAe,QAAQ;;EAGzB,SAAS,gBAAgB;AACvB,iBAAc,QAAQ;AACtB,kBAAe,QAAQ;;;uBAMvB,mBA6KM,OAAA;IA5KH,OAAK,eAAA,CAAA,iBAAoB,QAAA,gBAAW,eAAA,8BAAA,0BAAA,CAAA;IACrC,MAAK;IACJ,cAAU,0BAA4B,YAAA,MAAY,OAAM;;sBAEzD,mBA0IM,UAAA,MAAA,WAzIoB,YAAA,QAAhB,MAAM,UAAK;yBADrB,mBA0IM,OAAA;MAxIH,KAAK,KAAK;MACX,OAAM;MACN,MAAK;MACJ,WAAW,QAAA;MACX,cAAS,WAAE,gBAAgB,QAAQ,KAAI;MACvC,aAAQ,WAAE,eAAe,QAAQ,KAAI;MACrC,aAAW;MACX,SAAI,WAAE,WAAW,QAAQ,KAAI;MAC7B,WAAS;;MAGF,QAAQ,YAAA,MAAY,SAAM,KAAA,WAAA,EADlC,mBAGE,OAHF,cAGE,IAAA,mBAAA,IAAA,KAAA;MAEF,mBAyGM,OAAA;OAxGH,OAAK,eAAA;;gCAAuE,QAAA;QAAkB,eAAe,KAAI;QAAa,eAAA,UAAmB,KAAK,KAAE,mCAAA;QAAoD,cAAA,UAAkB,KAAK,KAAE,kCAAA;QAAmD,QAAA,WAAQ,kCAAA;;OAQhS,OAAK,eAAE,QAAA,cAAW,EAAA,aAAkB,aAAa,KAAI,EAAA,GAAA,EAAA,CAAA;OACrD,UAAK,WAAE,gBAAgB,KAAI;UAE5B,mBAuDM,OAAA,EAvDA,OAAK,eAAA,CAAA,yBAAA,0BAAsD,QAAA,OAAI,CAAA,EAAA,EAAA;OACnE,mBAWM,OAAA;QAVH,OAAK,eAAA;;iCAA+E,QAAA;SAAsB,mBAAmB,KAAI;;QAKjI,OAAK,eAAE,QAAA,cAAW;SAAA,iBAAA,GAAyB,aAAa,KAAI,CAAA;SAAA,OAAc,aAAa,KAAI;SAAA,GAAA,EAAA,CAAA;yBAE5F,mBAEM,OAFN,eAEM,CADJ,mBAAsG,QAAA;QAAhG,kBAAe;QAAQ,mBAAgB;QAAQ,gBAAa;QAAK,GAAG,cAAc,KAAK;;OAIjG,mBAeM,OAfN,eAeM,CAdJ,mBAUM,OAVN,eAUM,CATJ,mBAEO,QAAA,EAFA,OAAK,eAAA,CAAA,wBAAA,yBAAoD,QAAA,OAAI,CAAA,EAAA,EAAA,gBAC/D,KAAK,KAAI,EAAA,EAAA,EAGN,QAAA,gBAAgB,KAAK,YAAA,WAAA,EAD7B,mBAKO,QAAA;;QAHJ,OAAK,eAAA,CAAA,2BAA8B,QAAA,SAAI,OAAA,gCAAA,GAAA,CAAA;0BAErC,eAAe,KAAK,SAAQ,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,EAGnC,mBAEM,OAAA,EAFA,OAAK,eAAA,CAAA,uBAAA,wBAAkD,QAAA,OAAI,CAAA,EAAA,EAAA,gBAC5D,KAAK,KAAK,QAAO,KAAA,IAAA,CAAA,EAAA,EAAA,CAAA,CAAA;OAKhB,QAAA,gBAAgB,KAAK,eAAe,KAAK,eAAA,WAAA,EADjD,mBAWM,OAAA;;QATH,OAAK,eAAA,CAAA,8BAA8D,WAAA,UAAe,KAAK,KAAE,yCAAA,GAAA,CAAA;QAI1F,MAAK;QACL,QAAO;QACP,SAAQ;yCAER,mBAA2F,QAAA;QAArF,kBAAe;QAAQ,mBAAgB;QAAQ,gBAAa;QAAI,GAAE;;OAIlE,QAAA,YAAA,WAAA,EADR,mBAUS,UAAA;;QARP,MAAK;QACL,OAAM;QACL,cAAU,UAAY,KAAK;QAC3B,SAAK,eAAA,WAAO,iBAAiB,KAAK,GAAE,EAAA,CAAA,OAAA,CAAA;yCAErC,mBAEM,OAAA;QAFD,MAAK;QAAO,QAAO;QAAe,SAAQ;WAC7C,mBAAiG,QAAA;QAA3F,kBAAe;QAAQ,mBAAgB;QAAQ,gBAAa;QAAI,GAAE;;aAMtE,WAAA,UAAe,KAAK,OAAO,KAAK,eAAe,KAAK,eAAA,WAAA,EAD5D,mBAmCM,OAAA;;OAjCH,iBAAe,WAAA,UAAe,KAAK;OACpC,OAAM;;OAEG,KAAK,eAAA,WAAA,EAAd,mBAEI,KAAA;;QAFwB,OAAK,eAAA,CAAA,8BAAA,+BAAgE,QAAA,OAAI,CAAA;0BAChG,KAAK,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;OAGV,KAAK,cAAc,OAAO,KAAK,KAAK,WAAU,CAAE,SAAM,KAAA,WAAA,EAAjE,mBASM,OATN,gBASM,EAAA,UAAA,KAAA,EARJ,mBAOM,UAAA,MAAA,WANmB,KAAK,aAApB,OAAO,QAAG;4BADpB,mBAOM,OAAA;SALH,KAAK,OAAO,IAAG;SACf,OAAK,eAAA,CAAA,wBAAA,yBAAoD,QAAA,OAAI,CAAA;YAE9D,mBAAwD,QAAxD,gBAAwD,gBAAd,IAAG,GAAG,KAAC,EAAA,EACjD,mBAA2D,QAA3D,gBAA2D,gBAAf,MAAK,EAAA,EAAA,CAAA,EAAA,EAAA;;OAI1C,QAAA,YAAA,WAAA,EAAX,mBAcM,OAdN,gBAcM,EAAA,WAAA,EAbJ,mBAYS,UAAA,MAAA,WAXU;QAAA;QAAA;QAAA;QAAA;QAAA;QAAA,GAAV,WAAM;eADf,mBAYS,UAAA;SAVN,KAAK;SACN,MAAK;SACJ,OAAK,eAAA;;wCAA+F;UAA0B,KAAK,WAAW,SAAM,sCAAA;;SAKpJ,SAAK,eAAA,WAAO,mBAAmB,MAAM,OAAM,EAAA,CAAA,OAAA,CAAA;2BAEzC,OAAO,QAAO,KAAA,IAAA,CAAA,EAAA,IAAA,eAAA;;;MAOjB,QAAA,YAAY,QAAA,gBAAW,cAAA,WAAA,EAD/B,mBAUS,UAAA;;OARP,MAAK;OACL,OAAM;OACL,cAAU,kBAAoB,KAAK;OACnC,SAAK,eAAA,WAAO,cAAc,KAAK,GAAE,EAAA,CAAA,OAAA,CAAA;wCAElC,mBAEM,OAAA;OAFD,MAAK;OAAO,QAAO;OAAe,SAAQ;UAC7C,mBAA2F,QAAA;OAArF,kBAAe;OAAQ,mBAAgB;OAAQ,gBAAa;OAAI,GAAE;;MAIjE,QAAA,gBAAW,cAAmB,QAAQ,YAAA,MAAY,SAAM,KAAA,WAAA,EAAnE,mBAAmH,OAAnH,eAAmH,IAAA,mBAAA,IAAA,KAAA;MACxG,QAAA,gBAAW,gBAAqB,QAAQ,YAAA,MAAY,SAAM,KAAA,WAAA,EAArE,mBAAuH,OAAvH,eAAuH,IAAA,mBAAA,IAAA,KAAA;;;IAIjH,YAAA,MAAY,WAAM,KAAA,WAAA,EAD1B,mBAeM,OAfN,gBAeM,CAXJ,mBAUM,OAVN,gBAUM,CAAA,OAAA,OAAA,OAAA,KATJ,mBAAkE,KAAA,EAA/D,OAAM,6BAA2B,EAAC,6BAAyB,GAAA,GAEtD,QAAA,YAAA,WAAA,EADR,mBAOS,UAAA;;KALP,MAAK;KACL,OAAM;KACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,eAAa;OACtB,mBAED,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAKI,QAAA,YAAY,YAAA,MAAY,SAAM,KAAA,WAAA,EADtC,mBAUS,UAAA;;KARP,MAAK;KACJ,OAAK,eAAA,CAAA,0BAAA,2BAAwD,QAAA,OAAI,CAAA;KACjE,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,eAAa;kCAErB,mBAEM,OAAA;KAFD,MAAK;KAAO,QAAO;KAAe,SAAQ;QAC7C,mBAA2F,QAAA;KAArF,kBAAe;KAAQ,mBAAgB;KAAQ,gBAAa;KAAI,GAAE;eAE1E,mBAAqE,QAAA,EAA9D,OAAK,eAAA,gCAAkC,QAAA,OAAI,EAAA,EAAI,YAAQ,EAAA,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEzWpE,MAAM,QAAQ;EAMd,MAAM,OAAO;EAMb,MAAM,eAAe,IAAI,MAAK;EAE9B,MAAM,WAAW,IAAI;GACnB,OAAO;GACP,YAAY;GACZ,iBAAiB,MAAM;GACvB,gBAAgB;GAChB,cAAc;GACf,CAAA;AAED,cACQ,CAAC,MAAM,QAAQ,MAAM,SAAS,QAC9B;GACJ,MAAM,OAAO,MAAM,UAAU;AAC7B,YAAS,QAAQ;IACf,OAAQ,MAAM,SAAoB;IAClC,YAAY,MAAM,UAAU,cAAc;IAC1C,iBAAkB,MAAM,mBAA8B,MAAM;IAC5D,gBAAiB,MAAM,kBAA6B;IACpD,cAAe,MAAM,gBAA2B;IAClD;AACA,gBAAa,QAAS,SAAS,MAAM,iBAAiB,KAAM,CAAC,CAAC,SAAS,MAAM;KAE/E,EAAE,WAAW,MAAM,CACrB;EAEA,MAAM,YAAY,UAAyB,MAAM,WAAW,SAAS,MAAK;EAE1E,MAAM,aAAa,eAAe;GAChC,MAAM,IAAI,MAAM,SAAS;GACzB,MAAM,IAAI,MAAM,SAAS;AAKzB,UAJsC;IACpC,MAAM,GAAG,EAAE;IACX,KAAK,GAAG,EAAE;IACZ;IAED;EAED,SAAS,OAAO;AACd,QAAK,QAAQ;IACX,QAAQ,MAAM;IACd,OAAO,SAAS,MAAM,MAAM,MAAM;IAClC,YAAY,SAAS,MAAM;IAC3B,iBAAiB,SAAS,MAAM;IAChC,gBAAgB,SAAS,MAAM,kBAAkB;IACjD,cAAc,SAAS,MAAM,cAAc,MAAM,IAAI;IACtD,CAAA;;EAGH,SAAS,QAAQ;AACf,QAAK,QAAO;;EAGd,SAAS,QAAQ;AACf,QAAK,QAAO;;EAGd,SAAS,cAAc,GAAkB;AACvC,OAAI,EAAE,QAAQ,SAAU,QAAM;;AAGhC,yBAAuB,UAAU,WAAW,cAAa;EAEzD,MAAM,oBAAoB;GACxB;IAAE,MAAM;IAAU,OAAO;IAAK,SAAS;IAAU;GACjD;IAAE,MAAM;IAAS,OAAO;IAAK,SAAS;IAAS;GAC/C;IAAE,MAAM;IAAM,OAAO;IAAK,SAAS;IAAM;GAC3C;;uBAIE,YA+HW,UAAA,EA/HD,IAAG,QAAM,EAAA,CAEjB,mBAA6D,OAAA;IAAxD,OAAM;IAAkC,SAAO;OAGpD,mBAyHM,OAAA;IAxHJ,OAAM;IACL,OAAK,eAAE,WAAA,MAAU;IACjB,SAAK,OAAA,OAAA,OAAA,KAAA,oBAAN,IAAW,CAAA,OAAA,CAAA;;IAGX,mBAUM,OAVN,eAUM,CATJ,mBAGM,OAHN,eAGM,CAFJ,mBAA+D,QAA/D,eAA+D,gBAAhB,QAAA,OAAM,EAAA,EAAA,EAAA,OAAA,OAAA,OAAA,KACrD,mBAA0D,QAAA,EAApD,OAAM,+BAA6B,EAAC,aAAS,GAAA,EAAA,CAAA,EAErD,mBAIS,UAAA;KAJD,OAAM;KAA+B,SAAO;sCAClD,mBAEM,OAAA;KAFD,OAAM;KAAK,QAAO;KAAK,SAAQ;KAAY,MAAK;KAAO,QAAO;KAAe,gBAAa;KAAI,kBAAe;KAAQ,mBAAgB;QACxI,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,EAAG,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA,CAAA,CAAA;IAMjD,mBA2FM,OA3FN,eA2FM;KAzFO,SAAQ,QAAA,IAAA,WAAA,EAAnB,mBAUM,OAAA,eAAA,CAAA,OAAA,OAAA,OAAA,KATJ,mBAA8D,SAAA,EAAvD,OAAM,+BAA6B,EAAC,eAAW,GAAA,GAAA,eACtD,mBAOE,SAAA;4EANS,MAAS,QAAK;MACvB,MAAK;MACL,aAAY;MACZ,OAAM;MACL,WAAO,SAAQ,MAAI,CAAA,QAAA,CAAA;MACpB,WAAA;kCALS,SAAA,MAAS,MAAK,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;KAU3B,mBA6BM,OA7BN,eA6BM,CA5BO,SAAQ,aAAA,IAAA,WAAA,EAAnB,mBAgBM,OAAA,eAAA,CAAA,OAAA,OAAA,OAAA,KAfJ,mBAAuD,SAAA,EAAhD,OAAM,+BAA6B,EAAC,QAAI,GAAA,GAC/C,mBAaM,OAbN,eAaM,EAAA,WAAA,EAZJ,mBAWS,UAAA,MAAA,WAVO,oBAAP,QAAG;aADZ,mBAWS,UAAA;OATN,KAAK,IAAI;OACT,OAAO,IAAI;OACZ,OAAK,eAAA,CAAC,kCAAgC,GAAA,mCAC0B,IAAI,SAAS,SAAA,MAAS,eAAe,IAAI,MAAA,CAAA,CAAA;OAGxG,UAAK,WAAE,SAAA,MAAS,aAAa,IAAI;yBAE/B,IAAI,MAAK,EAAA,IAAA,cAAA;mDAIP,SAAQ,kBAAA,IAAA,WAAA,EAAnB,mBAUM,OAAA,gBAAA,CAAA,OAAA,QAAA,OAAA,MATJ,mBAA8D,SAAA,EAAvD,OAAM,+BAA6B,EAAC,eAAW,GAAA,GAAA,eACtD,mBAOE,SAAA;4EANgB,MAAS,kBAAe;MACxC,MAAK;MACL,KAAI;MACJ,KAAI;MACJ,MAAK;MACL,OAAM;;;MALU,SAAA,MAAS;;QAAjB,QAAR,MAAyC;;KAYvC,SAAQ,iBAAA,IAAsB,SAAQ,eAAA,IAAA,WAAA,EAD9C,mBAMQ,SANR,gBAMQ,CAAA,eAFN,mBAAgD,SAAA;MAAzC,MAAK;gFAAgC,QAAA;sCAAZ,aAAA,MAAY,CAAA,CAAA,EAAA,OAAA,QAAA,OAAA,MAC5C,mBAA6B,QAAA,MAAvB,oBAAgB,GAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;KAIR,aAAA,SAAA,WAAA,EAAhB,mBAkCW,UAAA,EAAA,KAAA,GAAA,EAAA,CAhCE,SAAQ,iBAAA,IAAA,WAAA,EAAnB,mBAoBM,OAAA,gBAAA,CAAA,OAAA,QAAA,OAAA,MAnBJ,mBAAoE,SAAA,EAA7D,OAAM,+BAA6B,EAAC,qBAAiB,GAAA,GAC5D,mBAiBM,OAjBN,gBAiBM,EAAA,WAAA,EAhBJ,mBAQS,UAAA,MAAA,WAPK;MAAA;MAAA;MAAA;MAAA;MAAA;MAAe,GAApB,MAAC;aADV,mBAQS,UAAA;OANN,KAAK;OACN,OAAK,eAAA,CAAC,mCAAiC,EAAA,2CACc,SAAA,MAAS,mBAAmB,GAAC,CAAA,CAAA;OACjF,UAAK,WAAE,SAAA,MAAS,iBAAiB;yBAE/B,EAAC,GAAG,MACT,IAAA,eAAA;8BACA,mBAME,SAAA;4EALgB,MAAS,iBAAc;MACvC,MAAK;MACL,KAAI;MACJ,KAAI;MACJ,OAAM;;;MAJU,SAAA,MAAS;;QAAjB,QAAR,MAAwC;8CAUnC,SAAQ,eAAA,IAAA,WAAA,EAAnB,mBAQM,OAAA,gBAAA,CAAA,OAAA,QAAA,OAAA,MAPJ,mBAAqE,SAAA,EAA9D,OAAM,+BAA6B,EAAC,sBAAkB,GAAA,GAAA,eAC7D,mBAKE,SAAA;4EAJS,MAAS,eAAY;MAC9B,MAAK;MACL,aAAY;MACZ,OAAM;kCAHG,SAAA,MAAS,aAAY,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,GAAA,IAAA,mBAAA,IAAA,KAAA;;IAUtC,mBAOM,OAAA,EAPD,OAAM,gCAA8B,EAAA,CACvC,mBAES,UAAA;KAFD,OAAM;KAA8D,SAAO;OAAO,eAE1F,EACA,mBAES,UAAA;KAFD,OAAM;KAA6D,SAAO;OAAM,SAExF,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1LR,MAAM,iBAAiB,IAAmB,KAAI;EAC9C,MAAM,iBAAiB,IAAmB,KAAI;EAE9C,MAAM,QAAQ;EA0Bd,MAAM,OAAO;EAWb,MAAM,WAAW,IAAwB,KAAI;EAC7C,MAAM,WAAW,IAAwB,KAAI;EAC7C,MAAM,aAAa,IAAI,MAAK;EAC5B,MAAM,YAAY,IAAyC,KAAI;EAC/D,MAAM,UAAU,IAAyC,KAAI;EAC7D,MAAM,cAAc,IAAmB,KAAI;EAG3C,MAAM,gBAAgB,IAAmB,KAAI;EAC7C,MAAM,oBAAoB,IAAI;GAAE,GAAG;GAAG,GAAG;GAAG,CAAA;EAE5C,MAAM,gBAAyE;GAC7E,GAAG;IAAE,MAAM;IAAG,MAAM;IAAG;GACvB,IAAI;IAAE,MAAM;IAAG,MAAM;IAAG;GACxB,IAAI;IAAE,MAAM;IAAG,MAAM;IAAG;GACxB,IAAI;IAAE,MAAM;IAAG,MAAM;IAAG;GACxB,IAAI;IAAE,MAAM;IAAG,MAAM;IAAG;GACxB,IAAI;IAAE,MAAM;IAAG,MAAM;IAAI;GACzB,KAAK;IAAE,MAAM;IAAI,MAAM;IAAI;GAC7B;EAEA,MAAM,cAAc,eAAe,cAAc,MAAM,QAAO;EAE9D,MAAM,YAAY,eAChB,MAAM,KAAK,EAAE,QAAQ,YAAY,MAAM,MAAM,GAAG,GAAG,MAAM,OAAO,aAAa,KAAK,EAAE,CAAA,CACtF;EAEA,MAAM,YAAY,eAChB,MAAM,KAAK,EAAE,QAAQ,YAAY,MAAM,MAAM,GAAG,GAAG,MAAM,IAAI,EAAC,CAChE;EAEA,MAAM,WAAW,eAAe;GAC9B,MAAM,OAAiB,EAAC;AACxB,QAAK,IAAI,MAAM,GAAG,MAAM,YAAY,MAAM,MAAM,OAAO;IACrD,MAAM,WAAmB,EAAC;AAC1B,SAAK,IAAI,MAAM,GAAG,MAAM,YAAY,MAAM,MAAM,OAAO;KACrD,MAAM,KAAK,GAAG,UAAU,MAAM,OAAO,MAAM;KAC3C,MAAM,WAAW,MAAM,MAAM,OAAO,EAAC;AACrC,cAAS,KAAK;MACZ;MACA;MACA;MACA,OAAO,SAAS,SAAS;MACzB,YAAY,SAAS;MACrB,OAAO,SAAS;MAChB,UAAU,SAAS;MACpB,CAAA;;AAEH,SAAK,KAAK,SAAQ;;AAEpB,UAAO;IACR;EAED,MAAM,kBAAkB,eAAe,IAAI,IAAI,MAAM,WAAW,CAAA;EAEhE,MAAM,oBAAoB,eAAe;AACvC,OAAI,CAAC,WAAW,SAAS,CAAC,UAAU,SAAS,CAAC,QAAQ,MAAO,wBAAO,IAAI,KAAY;GAEpF,MAAM,SAAS,KAAK,IAAI,UAAU,MAAM,KAAK,QAAQ,MAAM,IAAG;GAC9D,MAAM,SAAS,KAAK,IAAI,UAAU,MAAM,KAAK,QAAQ,MAAM,IAAG;GAC9D,MAAM,SAAS,KAAK,IAAI,UAAU,MAAM,KAAK,QAAQ,MAAM,IAAG;GAC9D,MAAM,SAAS,KAAK,IAAI,UAAU,MAAM,KAAK,QAAQ,MAAM,IAAG;GAE9D,MAAM,wBAAQ,IAAI,KAAY;AAC9B,QAAK,IAAI,MAAM,QAAQ,OAAO,QAAQ,MACpC,MAAK,IAAI,MAAM,QAAQ,OAAO,QAAQ,OAAO;IAC3C,MAAM,KAAK,GAAG,UAAU,MAAM,OAAO,MAAM;AAC3C,UAAM,IAAI,GAAE;;AAGhB,UAAO;IACR;EAGD,MAAM,aAAa,eAAe;AAQhC,UAPc;IACZ,IAAI;KAAE,WAAW;KAAQ,YAAY;KAAQ,aAAa;KAAQ,cAAc;KAAQ,UAAU;KAAY,KAAK;KAAO;IAC1H,IAAI;KAAE,WAAW;KAAQ,YAAY;KAAQ,aAAa;KAAQ,cAAc;KAAQ,UAAU;KAAW,KAAK;KAAO;IACzH,IAAI;KAAE,WAAW;KAAQ,YAAY;KAAQ,aAAa;KAAQ,cAAc;KAAQ,UAAU;KAAY,KAAK;KAAO;IAC1H,IAAI;KAAE,WAAW;KAAQ,YAAY;KAAQ,aAAa;KAAQ,cAAc;KAAQ,UAAU;KAAQ,KAAK;KAAO;IACtH,MAAM;KAAE,WAAW;KAAQ,YAAY;KAAQ,aAAa;KAAQ,cAAc;KAAQ,UAAU;KAAW,KAAK;KAAO;IAC7H,CACa,MAAM;IACpB;EAED,MAAM,aAAa,eAAe,MAAM,SAAS,OAAM;EAGvD,MAAM,qBAAuC;GAC3C;IAAE,MAAM;IAAU,OAAO;IAAU,OAAO;IAAW;GACrD;IAAE,MAAM;IAAS,OAAO;IAAS,OAAO;IAAW;GACnD;IAAE,MAAM;IAAM,OAAO;IAAM,OAAO;IAAW;GAC/C;EAEA,MAAM,oBAAoB,eAAe,MAAM,eAAe,mBAAkB;EAIhF,MAAM,sBAAsB,eAAe,MAAM,iBAAiB,SAAS,EAAC;EAC5E,MAAM,mBAAmB,eAAe,MAAM,cAAc,SAAS,EAAC;EAGtE,MAAM,kBAAkB,eAAe;GACrC,MAAM,sBAAM,IAAI,KAAkE;AAClF,QAAK,MAAM,QAAQ,MAAM,iBACvB,MAAK,KAAK,SAAS,KAAK,MAAM;AAC5B,QAAI,IAAI,KAAK;KAAE,WAAW;KAAM,cAAc;KAAG,CAAA;KAClD;AAEH,UAAO;IACR;EAGD,MAAM,kBAAkB,eAAe;GACrC,MAAM,sBAAM,IAAI,KAA+D;AAC/E,QAAK,MAAM,QAAQ,MAAM,cACvB,MAAK,KAAK,SAAS,KAAK,MAAM;AAC5B,QAAI,IAAI,KAAK;KAAE,WAAW;KAAM,cAAc;KAAG,CAAA;KAClD;AAEH,UAAO;IACR;EAMD,MAAM,oBAAoB,eAA0B;GAClD,MAAM,QAAmB,EAAC;GAC1B,MAAM,OAAO,UAAU;GACvB,IAAI,IAAI;AACR,UAAO,IAAI,KAAK,QAAQ;IACtB,MAAM,QAAQ,gBAAgB,MAAM,IAAI,KAAK,GAAE;AAC/C,QAAI,OAAO,iBAAiB,GAAG;AAC7B,WAAM,KAAK;MAAE,WAAW,MAAM;MAAW,SAAS,MAAM,UAAU,KAAK;MAAQ,CAAA;AAC/E,UAAK,MAAM,UAAU,KAAK;WACrB;KACL,IAAI,WAAW;AACf,YAAO,IAAI,WAAW,KAAK,UAAU,CAAC,gBAAgB,MAAM,IAAI,KAAK,IAAI,UAAU,CACjF;AAEF,WAAM,KAAK;MAAE,KAAK;MAAM,SAAS,YAAY;MAAG,CAAA;AAChD,UAAK,YAAY;;;AAGrB,UAAO;IACR;EAGD,MAAM,oBAAoB,eAA0B;GAClD,MAAM,QAAmB,EAAC;GAC1B,MAAM,OAAO,UAAU;GACvB,IAAI,IAAI;AACR,UAAO,IAAI,KAAK,QAAQ;IACtB,MAAM,QAAQ,gBAAgB,MAAM,IAAI,KAAK,GAAE;AAC/C,QAAI,OAAO,iBAAiB,GAAG;AAC7B,WAAM,KAAK;MAAE,WAAW,MAAM;MAAW,SAAS,MAAM,UAAU,KAAK;MAAQ,UAAU;MAAG,CAAA;AAC5F,UAAK,MAAM,UAAU,KAAK;WACrB;KACL,IAAI,WAAW;AACf,YAAO,IAAI,WAAW,KAAK,UAAU,CAAC,gBAAgB,MAAM,IAAI,KAAK,IAAI,UAAU,CACjF;AAEF,WAAM,KAAK;MAAE,KAAK;MAAM,SAAS,YAAY;MAAG,UAAU;MAAG,CAAA;AAC7D,UAAK,YAAY;;;AAGrB,UAAO;IACR;EAGD,MAAM,wBAAwB,eAAe;GAC3C,MAAM,sBAAM,IAAI,KAAqB;AACrC,QAAK,MAAM,QAAQ,kBAAkB,MACnC,KAAI,IAAI,KAAK,UAAU,KAAI;AAE7B,UAAO;IACR;EAID,SAAS,uBAAuB,OAAe,MAAc,gBAAkD;GAC7G,MAAM,MAAM,KAAK,IAAI,GAAG,eAAc;GACtC,MAAM,MAAM,KAAK,IAAI,GAAG,eAAc;GAEtC,MAAM,UAAU,OADN,OAAO,MAAM,KAAK,OAAO,QAAQ,MAAM,QACtB;GAC3B,MAAM,IAAI,SAAS,MAAM,MAAM,GAAG,EAAE,EAAE,GAAE;GACxC,MAAM,IAAI,SAAS,MAAM,MAAM,GAAG,EAAE,EAAE,GAAE;GACxC,MAAM,IAAI,SAAS,MAAM,MAAM,GAAG,EAAE,EAAE,GAAE;GAExC,MAAM,KAAK,OAAO,IAAI,WAAW,IAAI;GACrC,MAAM,KAAK,OAAO,IAAI,WAAW,IAAI;GACrC,MAAM,KAAK,OAAO,IAAI,WAAW,IAAI;GACrC,MAAM,aAAa,OAAQ,KAAK,OAAQ,KAAK,OAAQ,MAAM;AAC3D,UAAO;IACL,iBAAiB,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,QAAQ;IACnD,OAAO,YAAY,MAAO,YAAY;IACxC;;EAIF,SAAS,WAAW,OAAuB;AACzC,OAAI,SAAS,IAAM,QAAO,GAAG,QAAQ,IAAK;AAC1C,OAAI,QAAQ,IAAM,QAAO,MAAM,cAAc,EAAC;AAC9C,UAAO,OAAO,MAAK;;EAIrB,MAAM,0BAA0E;GAC9E,QAAQ;IAAE,IAAI;IAA4B,QAAQ;IAA2B;GAC7E,SAAS;IAAE,IAAI;IAA4B,QAAQ;IAA2B;GAC9E,OAAO;IAAE,IAAI;IAA4B,QAAQ;IAA2B;GAC5E,IAAI;IAAE,IAAI;IAA4B,QAAQ;IAA2B;GAC3E;EAEA,MAAM,gBAA0C;GAC9C,SAAS;IAAC;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAU;GACvH,QAAQ;IAAC;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAU;GACtH,OAAO;IAAC;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAW;IAAU;GACxJ;EAEA,SAAS,gBAAgB,OAA0C;AACjE,OAAI,CAAC,MAAM,SAAS,WAAW,UAAU,KAAA,EAAW,QAAO;GAE3D,MAAM,MAAM,MAAM,QAAQ,OAAO;GACjC,MAAM,MAAM,MAAM,QAAQ,OAAO;GACjC,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,QAAQ,QAAQ,MAAM,KAAK,CAAA;GAEvE,MAAM,SAAS,MAAM,QAAQ,eAAe,YAAY,MAAM,QAAQ,cAAc,SAChF,MAAM,QAAQ,eACd,cAAc,MAAM,QAAQ,cAAc;AAG9C,UAAO,OADO,KAAK,IAAI,KAAK,MAAM,cAAc,OAAO,SAAS,GAAG,EAAE,OAAO,SAAS,EAAC;;EAIxF,SAAS,eAAe,MAAsB;GAC5C,MAAM,iBAAiB,WAAW,KAAK,GAAE;GACzC,MAAM,aAAa,kBAAkB,MAAM,IAAI,KAAK,GAAE;GACtD,MAAM,YAAY,YAAY,UAAU,KAAK;GAC7C,MAAM,aAAa,MAAM,YAAY,KAAK,UAAU;GACpD,MAAM,eAAe,eAAe,UAAU,KAAK;GACnD,MAAM,eAAe,eAAe,UAAU,KAAK;GAEnD,MAAM,UAAU,CACd,yBACA,MAAM,cAAc,WAAW,kCAAkC,iCACnE;AAEA,OAAI,MAAM,kBAAkB,UAAU,KAAK,WACzC,SAAQ,KAAK,mCAAkC;AAGjD,OAAI,aAAc,SAAQ,KAAK,qCAAoC;YAC1D,aAAc,SAAQ,KAAK,qCAAoC;YAC/D,eAAgB,SAAQ,KAAK,kCAAiC;YAC9D,WAAY,SAAQ,KAAK,mCAAkC;YAC3D,aAAa,CAAC,MAAM,SAAU,SAAQ,KAAK,iCAAgC;AAEpF,OAAI,WAAY,SAAQ,KAAK,kCAAiC;AAC9D,OAAI,KAAK,UAAU,SAAU,SAAQ,KAAK,gCAA+B;AAEzE,UAAO;;EAGT,SAAS,aAAa,MAAoC;GACxD,MAAM,eAAe,gBAAgB,KAAK,MAAK;AAC/C,OAAI,aACF,QAAO;IAAE,iBAAiB;IAAc,QAAQ;IAAwB;AAG1E,OAAI,KAAK,cAAc,MAAM,aAAa,KAAK,aAAa;IAC1D,MAAM,QAAQ,MAAM,aAAa,KAAK;AACtC,WAAO;KACL,iBAAiB,GAAG,MAAM;KAC1B,QAAQ,aAAa,MAAM;KAC7B;;AAGF,OAAI,KAAK,cAAc,wBAAwB,KAAK,aAAa;IAC/D,MAAM,SAAS,wBAAwB,KAAK;AAC5C,WAAO;KACL,iBAAiB,OAAO;KACxB,QAAQ,aAAa,OAAO;KAC9B;;AAIF,UAAO;IACL,iBAAiB;IACjB,QAAQ,OAHU,KAAK,UAAU,WAAW,UAAU,SAG3B;IAC7B;;EAGF,SAAS,uBAAuB,MAA2B;AACzD,OAAI,CAAC,MAAM,2BAA2B,CAAC,KAAK,WAAY,QAAO;AAO/D,UANwC;IACtC,QAAQ;IACR,SAAS;IACT,OAAO;IACP,IAAI;IACN,CACe,KAAK,eAAe,KAAK,WAAW,OAAO,EAAE,CAAC,aAAY;;EAG3E,SAAS,aAAa,MAAgC;AACpD,OAAI,CAAC,MAAM,eAAgB,QAAO,KAAA;AAClC,UAAO,KAAK,UAAU;;EAGxB,SAAS,aAAa,MAAoD;AACxE,OAAI,CAAC,MAAM,cAAc,CAAC,KAAK,SAAU,QAAO;GAChD,MAAM,QAAQ,KAAK,SAAS;AAC5B,OAAI,SAAS,QAAQ,EACnB,QAAO;IAAE,MAAM,OAAO,MAAM;IAAE,OAAO;IAAU;AAEjD,OAAI,KAAK,SAAS,aAChB,QAAO;IAAE,MAAM;IAAK,OAAO;IAAU;AAEvC,UAAO;;EAGT,SAAS,WAAW,QAAyB;AAC3C,UAAO,gBAAgB,MAAM,IAAI,OAAO,IAAI,kBAAkB,MAAM,IAAI,OAAM;;EAGhF,SAAS,gBAAgB,MAAY,OAAmB;AACtD,OAAI,MAAM,YAAY,MAAM,SAAU;AAEtC,QAAK,cAAc,KAAK,IAAI,MAAK;AAGjC,OAAI,MAAM,UAAU;AAClB,kBAAc,KAAK,IAAI,MAAK;AAC5B;;AAGF,OAAI,MAAM,kBAAkB,OAAQ;GAEpC,MAAM,sBAAsB,gBAAgB,MAAM,IAAI,KAAK,GAAE;GAC7D,MAAM,gBAAgB,MAAM,YAAY,MAAM,WAAW,MAAM;GAE/D,IAAI;AACJ,OAAI,MAAM,kBAAkB,SAC1B,gBAAe,sBAAsB,EAAE,GAAG,CAAC,KAAK,GAAE;YACzC,cACT,gBAAe,sBACX,MAAM,WAAW,QAAO,OAAM,OAAO,KAAK,GAAE,GAC5C,CAAC,GAAG,MAAM,YAAY,KAAK,GAAE;OAEjC,gBAAe,uBAAuB,MAAM,WAAW,WAAW,IAAI,EAAE,GAAG,CAAC,KAAK,GAAE;AAGrF,QAAK,qBAAqB,aAAY;AACtC,QAAK,oBAAoB,aAAY;;EAGvC,SAAS,cAAc,QAAgB,OAAmB;GAExD,MAAM,OADS,MAAM,cACD,uBAAsB;AAC1C,qBAAkB,QAAQ;IACxB,GAAG,KAAK,QAAQ;IAChB,GAAG,KAAK;IACV;AACA,iBAAc,QAAQ;;EAGxB,SAAS,eAAe,MAAoB;AAC1C,QAAK,aAAa,KAAK,QAAQ,KAAI;AACnC,iBAAc,QAAQ;;EAGxB,SAAS,kBAAkB;AACzB,OAAI,cAAc,MAChB,MAAK,cAAc,cAAc,MAAK;AAExC,iBAAc,QAAQ;;EAGxB,SAAS,kBAAkB;AACzB,iBAAc,QAAQ;;EAGxB,SAAS,oBAAoB,MAAY,OAAmB;AAC1D,OAAI,MAAM,YAAY,MAAM,YAAY,MAAM,kBAAkB,YAAa;AAC7E,OAAI,MAAM,SAAU;AACpB,OAAI,MAAM,WAAW,EAAG;AAExB,cAAW,QAAQ;AACnB,aAAU,QAAQ;IAAE,KAAK,KAAK;IAAK,KAAK,KAAK;IAAI;AACjD,WAAQ,QAAQ;IAAE,KAAK,KAAK;IAAK,KAAK,KAAK;IAAI;;EAGjD,SAAS,qBAAqB,MAAY,OAAmB;AAC3D,eAAY,QAAQ,KAAK;AACzB,QAAK,cAAc,KAAK,IAAI,MAAK;AAEjC,OAAI,WAAW,SAAS,MAAM,kBAAkB,YAC9C,SAAQ,QAAQ;IAAE,KAAK,KAAK;IAAK,KAAK,KAAK;IAAI;;EAInD,SAAS,uBAAuB;AAC9B,eAAY,QAAQ;AACpB,QAAK,cAAc,KAAI;;EAGzB,SAAS,gBAAgB;AACvB,OAAI,CAAC,WAAW,SAAS,MAAM,kBAAkB,YAAa;GAE9D,MAAM,eAAe,MAAM,KAAK,kBAAkB,MAAK;AACvD,cAAW,QAAQ;AACnB,aAAU,QAAQ;AAClB,WAAQ,QAAQ;AAEhB,OAAI,aAAa,SAAS,GAAG;AAC3B,SAAK,qBAAqB,aAAY;AACtC,SAAK,oBAAoB,aAAY;;;EAIzC,SAAS,kBAAkB,MAAY,OAAmB;AACxD,SAAM,gBAAe;AACrB,QAAK,gBAAgB,KAAK,IAAI,MAAK;;EAIrC,SAAS,gBAAgB,MAAY,OAAkB;AACrD,OAAI,MAAM,YAAY,MAAM,YAAY,MAAM,kBAAkB,OAAQ;AACxE,OAAI,CAAC,KAAK,WAAY;AAEtB,kBAAe,QAAQ,KAAK;AAC5B,OAAI,MAAM,cAAc;AACtB,UAAM,aAAa,gBAAgB;AACnC,UAAM,aAAa,QAAQ,cAAc,KAAK,GAAE;;;EAIpD,SAAS,eAAe,MAAY,OAAkB;AACpD,OAAI,MAAM,kBAAkB,UAAU,CAAC,eAAe,MAAO;AAC7D,SAAM,gBAAe;AACrB,OAAI,MAAM,aACR,OAAM,aAAa,aAAa;AAElC,kBAAe,QAAQ,KAAK;;EAG9B,SAAS,kBAAkB;AACzB,kBAAe,QAAQ;;EAGzB,SAAS,WAAW,MAAY,OAAkB;AAChD,SAAM,gBAAe;AACrB,OAAI,MAAM,kBAAkB,UAAU,CAAC,eAAe,MAAO;GAE7D,MAAM,WAAW,eAAe;GAChC,MAAM,WAAW,KAAK;AAEtB,OAAI,aAAa,SACf,MAAK,aAAa,UAAU,SAAQ;AAGtC,kBAAe,QAAQ;AACvB,kBAAe,QAAQ;;EAGzB,SAAS,gBAAgB;AACvB,kBAAe,QAAQ;AACvB,kBAAe,QAAQ;;EAGzB,SAAS,cAAc,OAAsB;AAC3C,OAAI,MAAM,YAAY,MAAM,SAAU;AAEtC,OAAI,MAAM,QAAQ,UAAU;AAC1B,QAAI,cAAc,OAAO;AACvB,mBAAc,QAAQ;AACtB;;AAEF,SAAK,qBAAqB,EAAE,CAAA;AAC5B,SAAK,oBAAoB,EAAE,CAAA;;AAG7B,QAAK,MAAM,QAAQ,OAAO,MAAM,QAAQ,SAAS,MAAM,WAAW,MAAM,UAAU;AAChF,UAAM,gBAAe;IACrB,MAAM,WAAW,SAAS,MAAM,MAAM,CAAC,KAAI,MAAK,EAAE,GAAE;AACpD,SAAK,qBAAqB,SAAQ;AAClC,SAAK,oBAAoB,SAAQ;;;AAIrC,yBAAuB,UAAU,WAAW,cAAa;AACzD,yBAAuB,UAAU,WAAW,cAAa;EAEzD,MAAM,iBAAiB,eACrB,WAAW,QAAQ,EAAE,GAAG;GACtB,WAAW,SAAS,MAAM,KAAK;GAC/B,iBAAiB;GACnB,CACF;EAEA,MAAM,aAAa,gBAAgB;GACjC,gBAAgB;GAChB,eAAe,WAAW,MAAM;GAChC,GAAI,WAAW,QAAQ;IAAE,OAAO;IAAQ,aAAa;IAAkB,GAAG,EAAE;GAC7E,EAAC;;uBAIA,mBA2OM,OAAA;aA1OA;IAAJ,KAAI;IACH,OAAK,eAAA,CAAA,mBAAsB,WAAA,QAAU,0BAAA,0BAAA,CAAA;IACrC,OAAK,eAAE,eAAA,MAAc;OAEtB,mBAwNM,OAxNN,eAwNM,CAvNJ,mBAsNM,OAtNN,eAsNM;IArNJ,mBAoLQ,SAAA;cAnLF;KAAJ,KAAI;KACJ,OAAM;KACN,MAAK;KACJ,cAAU,GAAK,MAAM,OAAM;KAC3B,OAAK,eAAE,WAAA,MAAU;;KAGP,oBAAA,SAAA,WAAA,EAAb,mBAsBQ,SAAA,eAAA,CArBN,mBAoBK,MAAA,MAAA;MAlBH,mBAAoD,MAAA,EAA/C,OAAK,eAAA,EAAA,OAAW,WAAA,MAAW,aAAW,CAAA,EAAA,EAAA,MAAA,EAAA;MAEjC,iBAAA,SAAA,WAAA,EAAV,mBAA4E,MAAA;;OAA/C,OAAK,eAAA,EAAA,OAAW,WAAA,MAAW,aAAW,CAAA;;wBACnE,mBAcW,UAAA,MAAA,WAdqB,kBAAA,QAAd,MAAM,QAAG;2EAA0C,KAAA,EAAA,CAAA,eAE5C,QAAA,WAAA,EADvB,mBAWK,MAAA;;QATF,SAAS,KAAK;QACf,OAAM;QACL,OAAK,eAAA;iBAA8B,WAAA,MAAW;mBAA0C,WAAA,MAAW;gBAAmC,KAAK,UAAU;;2CAMnJ,KAAK,UAAU,MAAK,EAAA,EAAA,EAAmB,KAAK,UAAU,QAAA,WAAA,EAA/B,mBAA4E,UAAA,EAAA,KAAA,GAAA,EAAA,CAAA,gBAAvC,OAAE,gBAAG,KAAK,UAAU,KAAI,GAAG,KAAC,EAAA,CAAA,EAAA,GAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,IAAA,cAAA,KAAA,WAAA,EAE7F,mBAAwC,MAAA;;QAA5B,SAAS,KAAK;;;;KAKnB,MAAM,cAAA,WAAA,EAAnB,mBA2BQ,SAAA,eAAA,CA1BN,mBAyBK,MAAA,MAAA;MAxBH,mBAAqF,MAAA,EAAhF,OAAK,eAAA;OAAA,OAAW,WAAA,MAAW;OAAW,QAAU,WAAA,MAAW;OAAY,CAAA,EAAA,EAAA,MAAA,EAAA;MAElE,iBAAA,SAAA,WAAA,EAAV,mBAA6G,MAAA;;OAAhF,OAAK,eAAA;QAAA,OAAW,WAAA,MAAW;QAAW,QAAU,WAAA,MAAW;QAAY,CAAA;;wBACpG,mBAoBK,UAAA,MAAA,WAnBW,UAAA,QAAP,QAAG;2BADZ,mBAoBK,MAAA;QAlBF,KAAK;QACN,OAAM;QACL,OAAK,eAAA;SAAA,QAAY,WAAA,MAAW;SAAY,UAAY,WAAA,MAAW;SAAQ,CAAA;6BAExE,mBAaW,UAAA,MAAA,WAAA,CAbgB,gBAAA,MAAgB,IAAI,IAAG,CAAA,GAAjC,UAAK;gEAAsC,KAAG,EAAA,CAErD,SAAA,WAAA,EADR,mBAUO,QAAA;;SARL,OAAM;SACL,OAAK,eAAE,uBAA4C,MAAM,UAAU,OAA2B,MAAM,UAAU,eAAe,MAAM,eAAmC,MAAM,UAAU,eAAA,CAAA;2BAMpL,WAAW,MAAM,UAAU,eAAe,MAAM,cAAY,CAAA,EAAA,EAAA,KAAA,WAAA,EAEjE,mBAAqC,UAAA,EAAA,KAAA,GAAA,EAAA,CAAA,gBAAA,gBAAjB,IAAG,EAAA,EAAA,CAAA,EAAA,GAAA,EAAA,EAAA,GAAA;;;;KAK/B,mBAuHQ,SAAA,MAAA,EAAA,UAAA,KAAA,EAtHN,mBAqHK,UAAA,MAAA,WArHyB,SAAA,QAAlB,KAAK,aAAQ;0BAAzB,mBAqHK,MAAA;OArHoC,KAAK;OAAU,MAAK;;OAE3C,iBAAA,SAAoB,sBAAA,MAAsB,IAAI,SAAQ,IAAA,UAAA,KAAA,EACpE,mBAkBK,UAAA,EAAA,KAAA,GAAA,EAAA,WAAA,CAjBa,sBAAA,MAAsB,IAAI,SAAQ,CAAA,GAA3C,SAAI;4BADb,mBAkBK,MAAA;SAhBF,KAAK;SACL,SAAS,KAAK;SACd,OAAK,eAAA,CAAA,wCAAA,SAAyF,OAAI,gDAAA,GAAA,CAAA;SAIlG,OAAK,eAAA;iBAA6B,WAAA,MAAW;oBAAyC,WAAA,MAAW;oBAAyC,WAAA,MAAW;4BAA+C,OAAI,EAAA,OAAY,KAAK,UAAU,OAAK,GAAA,EAAA;;2BAO/M,QAAA,WAAA,EAA1B,mBAEM,OAFN,eAEM,CADJ,mBAAmF,QAAnF,gBAAmF,gBAA9B,KAAK,UAAU,MAAK,EAAA,EAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,IAAA,cAAA;;OAMvE,MAAM,cAAA,WAAA,EADd,mBAyBK,MAAA;;QAvBH,OAAM;QACL,OAAK,eAAA;gBAA2B,WAAA,MAAW;iBAAqC,WAAA,MAAW;mBAAsC,WAAA,MAAW;oBAAwC,WAAA,MAAW;mBAAsC,WAAA,MAAW;;6BAQjP,mBAaW,UAAA,MAAA,WAAA,CAbgB,gBAAA,MAAgB,IAAI,UAAA,MAAU,UAAQ,CAAA,GAAhD,UAAK;gEAAsD,UAAQ,EAAA,CAE1E,SAAA,WAAA,EADR,mBAUO,QAAA;;SARL,OAAM;SACL,OAAK,eAAE,uBAA4C,MAAM,UAAU,OAA2B,MAAM,UAAU,eAAe,MAAM,eAAmC,MAAM,UAAU,eAAA,CAAA;2BAMpL,WAAW,MAAM,UAAU,eAAe,MAAM,cAAY,CAAA,EAAA,EAAA,KAAA,WAAA,EAEjE,mBAAqD,UAAA,EAAA,KAAA,GAAA,EAAA,CAAA,gBAAA,gBAAjC,UAAA,MAAU,UAAQ,EAAA,EAAA,CAAA,EAAA,GAAA,EAAA,EAAA,GAAA;;yBAK1C,mBAgEK,UAAA,MAAA,WAhEc,MAAR,SAAI;4BAAf,mBAgEK,MAAA;SAhEoB,KAAK,KAAK;SAAI,OAAM;YAC3C,mBA8DM,OAAA;SA7DJ,MAAK;SACL,UAAS;SACR,cAAU,QAAU,KAAK,KAAK,KAAK,aAAU,kBAAqB,KAAK,eAAU,KAAU,KAAK,UAAU,KAAA,IAAS,YAAe,KAAK,UAAK;SAC5I,iBAAe,WAAW,KAAK,GAAE;SACjC,iBAAe,MAAM,YAAY,KAAK,UAAK;SAC3C,WAAW,MAAM,kBAAa,UAAA,CAAA,CAAiB,KAAK;SACpD,OAAK,eAAE,eAAe,KAAI,CAAA;SAC1B,OAAK,eAAA;iBAA6B,WAAA,QAAU,SAAY,WAAA,MAAW;kBAAqC,WAAA,MAAW;oBAAwC,WAAA,QAAU,MAAS,WAAA,MAAW;qBAAwC,WAAA,MAAW;;oBAAmF,WAAA,MAAW;aAA+B,aAAa,KAAI;;SAS1X,OAAK,GAAK,KAAK,KAAK,KAAK,aAAU,KAAQ,KAAK,eAAU,KAAU,aAAa,KAAI,GAAA,MAAU,aAAa,KAAI,KAAA;SAChH,UAAK,WAAE,gBAAgB,MAAM,OAAM;SACnC,cAAS,WAAE,oBAAoB,MAAM,OAAM;SAC3C,eAAU,WAAE,qBAAqB,MAAM,OAAM;SAC7C,cAAY;SACZ,gBAAW,WAAE,kBAAkB,MAAM,OAAM;SAC3C,cAAS,WAAE,gBAAgB,MAAM,OAAM;SACvC,YAAQ,eAAA,WAAU,eAAe,MAAM,OAAM,EAAA,CAAA,UAAA,CAAA;SAC7C,aAAW;SACX,QAAI,eAAA,WAAU,WAAW,MAAM,OAAM,EAAA,CAAA,UAAA,CAAA;SACrC,WAAS;SACT,WAAO,CAAA,UAAA,WAAQ,gBAAgB,MAAM,OAAM,EAAA,CAAA,QAAA,CAAA,EAAA,SAAA,eAAA,WACpB,gBAAgB,MAAM,OAAM,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,QAAA,CAAA,CAAA;YAI5C,aAAa,KAAI,IAAA,WAAA,EADzB,mBAKO,QALP,gBAKO,gBADF,aAAa,KAAI,CAAA,EAAA,EAAA,IAIT,uBAAuB,KAAI,IAAA,WAAA,EADxC,mBAKO,QALP,gBAKO,gBADF,uBAAuB,KAAI,CAAA,EAAA,EAAA,IAInB,MAAM,eAAA,WAAA,EADnB,mBAKO,QALP,gBAKO,gBADF,KAAK,GAAE,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,EAKJ,aAAa,KAAI,IAAA,WAAA,EADzB,mBAOO,QAAA;;SALL,OAAM;SACL,OAAK,eAAA,EAAA,iBAAqB,aAAa,KAAI,EAAG,OAAK,CAAA;SACnD,OAAO,aAAa,KAAI,EAAG,SAAI,MAAA,kBAAA,GAAgC,aAAa,KAAI,EAAG,QAAI,GAAA;2BAErF,aAAa,KAAI,EAAG,KAAI,EAAA,IAAA,eAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,IAAA,eAAA,CAAA,CAAA;;;;;IAU7B,MAAM,SAAS,WAAW,MAAM,QAAQ,cAAA,WAAA,EADhD,mBAcM,OAdN,gBAcM;KAVJ,mBAA+E,QAA/E,gBAA+E,gBAAhC,MAAM,QAAQ,OAAG,EAAA,EAAA,EAAA;KAChE,mBAOM,OAPN,gBAOM,EAAA,UAAA,KAAA,EANJ,mBAKE,UAAA,MAAA,WAJ0B,MAAM,QAAQ,eAAU,YAAiB,MAAM,QAAQ,cAAc,SAAS,MAAM,QAAQ,eAAe,cAAc,MAAM,QAAQ,cAAU,aAAnK,OAAO,UAAK;0BADtB,mBAKE,OAAA;OAHC,KAAK;OACN,OAAM;OACL,OAAK,eAAA,EAAA,iBAAqB,OAAK,CAAA;;;KAGpC,mBAA+E,QAA/E,gBAA+E,gBAAhC,MAAM,QAAQ,OAAG,EAAA,EAAA,EAAA;;IAIvD,MAAM,cAAA,WAAA,EAAjB,mBAYM,OAZN,gBAYM,EAAA,UAAA,KAAA,EAXJ,mBAUM,UAAA,MAAA,WATW,kBAAA,QAAR,SAAI;yBADb,mBAUM,OAAA;MARH,KAAK,KAAK;MACX,OAAM;SAEN,mBAGE,QAAA;MAFA,OAAM;MACL,OAAK,eAAA;OAAA,iBAAA,GAAwB,KAAK,MAAK;OAAA,QAAA,aAA2B,KAAK,MAAK;OAAA,CAAA;kBAE/E,mBAAyE,QAAzE,gBAAyE,gBAApB,KAAK,MAAK,EAAA,EAAA,CAAA,CAAA;;SAQ/D,QAAA,YAAY,cAAA,SAAA,WAAA,EADpB,YAUE,+BAAA;;IARC,WAAS,cAAA;IACT,aAAW,QAAA,MAAM,cAAA;IACjB,eAAa,QAAA;IACb,4BAA0B,QAAA;IAC1B,UAAU,kBAAA;IACV,QAAM;IACN,SAAO;IACP,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EElyBd,MAAM,QAAQ;EAad,MAAM,OAAO;EASb,SAAS,eAAe,QAAoB,OAAuB;AACjE,UAAO,OAAO,SAAS,MAAM,aAAa,QAAQ,MAAM,aAAa;;EAGvE,SAAS,kBAAkB,QAAoB;GAC7C,MAAM,WAAW,MAAM,eAAe,OAAO,KAAK,KAAA,IAAY,OAAO;AACrE,QAAK,gBAAgB,OAAM;AAC3B,QAAK,qBAAqB,SAAQ;;EAGpC,SAAS,aAAa,OAAc,UAAkB;AACpD,SAAM,iBAAgB;AACtB,QAAK,iBAAiB,SAAQ;;EAGhC,SAAS,YAAY;AACnB,QAAK,aAAY;;;uBAKjB,mBAwDM,OAAA;IAxDA,OAAK,eAAA,CAAA,sBAAA,uBAAgD,QAAA,cAAW,CAAA;IAAK,MAAK;IAAO,cAAW;yBAChG,mBAyCS,UAAA,MAAA,WAxCmB,QAAA,UAAlB,QAAQ,UAAK;wBADvB,mBAyCS,UAAA;KAvCN,KAAK,OAAO;KACb,MAAK;KACL,MAAK;KACJ,gBAAc,QAAA,eAAe,OAAO;KACpC,OAAK,eAAA;;mCAA6E,QAAA;MAAgB,QAAA,eAAe,OAAO,KAAE,uCAAA;;KAK1H,UAAK,WAAE,kBAAkB,OAAM;;KAEhC,mBAIE,QAAA;MAHC,OAAK,eAAA,CAAA,8BAAA,+BAAgE,QAAA,OAAI,CAAA;MACzE,OAAK,eAAA,EAAA,iBAAqB,eAAe,QAAQ,MAAK,EAAA,CAAA;MACtD,eAAa;;KAGhB,mBAEO,QAAA,EAFA,OAAK,eAAA,CAAA,4BAAA,6BAA4D,QAAA,OAAI,CAAA,EAAA,EAAA,gBACvE,OAAO,KAAI,EAAA,EAAA;KAIR,QAAA,cAAc,OAAO,UAAU,KAAA,KAAA,WAAA,EADvC,mBAKO,QAAA;;MAHJ,OAAK,eAAA,CAAA,6BAAA,8BAA8D,QAAA,OAAI,CAAA;wBAErE,OAAO,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAIT,QAAA,YAAA,WAAA,EADR,mBAUS,UAAA;;MARP,MAAK;MACL,OAAM;MACL,cAAU,UAAY,OAAO;MAC7B,UAAK,WAAE,aAAa,QAAQ,OAAO,GAAE;uCAEtC,mBAEM,OAAA;MAFD,OAAM;MAAkC,MAAK;MAAO,QAAO;MAAe,SAAQ;SACrF,mBAAiG,QAAA;MAA3F,kBAAe;MAAQ,mBAAgB;MAAQ,gBAAa;MAAI,GAAE;;;cAMtE,QAAA,YAAA,WAAA,EADR,mBAWS,UAAA;;IATP,MAAK;IACJ,OAAK,eAAA,CAAA,2BAAA,4BAA0D,QAAA,OAAI,CAAA;IACpE,cAAW;IACV,SAAO;qBAER,mBAEM,OAAA;IAFA,OAAK,eAAA,iCAAmC,QAAA,OAAI;IAAI,MAAK;IAAO,QAAO;IAAe,SAAQ;qCAC9F,mBAA2F,QAAA;IAArF,kBAAe;IAAQ,mBAAgB;IAAQ,gBAAa;IAAI,GAAE;yBAE1E,mBAAiE,QAAA,EAA1D,OAAK,eAAA,iCAAmC,QAAA,OAAI,EAAA,EAAI,OAAG,EAAA,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEpGhE,MAAM,cAA4C;GAChD,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG;GACL;EACA,MAAM,aAA6B;GAAC;GAAK;GAAK;GAAK;GAAG;EActD,MAAM,QAAQ;EAYd,MAAM,OAAO;EAYb,MAAM,SAAS,mBAAmB,MAAM,YAAY,EAClD,eAAe,MAAM,QACtB,CAAA;EAED,MAAM,gBAAgB,IAAI,GAAE;EAC5B,MAAM,kBAAkB,IAAI,MAAK;EACjC,MAAM,aAAa,IAAI,GAAE;EACzB,MAAM,eAAe,IAAoB,OAAM;EAG/C,MAAM,aAAa,oBAA+B,IAAI,KAAK,CAAA;EAG3D,SAAS,aAAa,SAAiB,YAAkC;AACvE,OAAI,CAAC,WAAW,MAAM,IAAI,QAAQ,CAChC,YAAW,MAAM,IAAI,SAAS,WAAW,aAAa,WAAW,QAAO;AAE1E,UAAO,WAAW,MAAM,IAAI,QAAQ;;EAGtC,MAAM,eAAe,eAAe;GAClC,MAAM,SAAiC,EAAC;AACxC,QAAK,MAAM,UAAU,OAAO,QAAQ,MAClC,KAAI,OAAO,MACT,QAAO,OAAO,MAAM,OAAO;AAG/B,UAAO;IACR;EAED,MAAM,YAAY,eAAe;GAC/B,MAAM,QAAQ,OAAO,YAAY;AACjC,OAAI,CAAC,MAAO,QAAO,EAAC;GAEpB,MAAM,QAAuC,EAAC;AAC9C,QAAK,MAAM,CAAC,QAAQ,SAAS,OAAO,QAAQ,MAAM,MAAM,CACtD,OAAM,UAAU;IACd,OAAO,KAAK;IACZ,YAAY,KAAK;IACjB,OAAO,KAAK;IACd;AAEF,UAAO;IACR;EAGD,SAAS,kBAAkB,SAAyB;GAClD,MAAM,QAAQ,OAAO,OAAO,MAAM,MAAK,MAAK,EAAE,OAAO,QAAO;AAC5D,OAAI,CAAC,MAAO,QAAO;AACnB,UAAO,OAAO,OAAO,MAAM,MAAM,CAAC,QAAO,MAAK,EAAE,WAAW,CAAC;;AAG9D,cACQ,OAAO,MAAM,QAClB,aAAa,KAAK,qBAAqB,EAAE,GAAG,UAAU,CAAC,EACxD,EAAE,MAAM,MAAK,CACf;AAEA,cACQ,MAAM,aACX,aAAa;AACZ,OAAI,SAAU,QAAO,UAAU,SAAQ;IAE3C;EAEA,SAAS,sBAAsB,SAAmB;AAChD,UAAO,iBAAiB,QAAO;;EAGjC,SAAS,kBAAkB,QAAoB;GAC7C,MAAM,cAAc,OAAO,eAAe,UAAU,OAAO,KAAK,KAAA,IAAY,OAAO;AACnF,UAAO,gBAAgB,YAAW;;EAGpC,SAAS,qBAAqB;GAC5B,MAAM,QAAQ,OAAO,cAAc;AACnC,OAAI,MAAM,WAAW,EAAG;AAExB,UAAO,aAAa,OAAO,OAAO,eAAe,MAAK;AACtD,QAAK,iBAAiB,OAAO,OAAO,eAAe,MAAK;;EAG1D,SAAS,mBAAmB;GAC1B,MAAM,QAAQ,OAAO,cAAc;AACnC,OAAI,MAAM,WAAW,EAAG;AAExB,UAAO,WAAW,MAAK;AACvB,QAAK,eAAe,MAAK;;EAG3B,SAAS,kBAAkB;AACzB,OAAI,CAAC,cAAc,MAAM,MAAM,CAAE;AACjC,UAAO,UAAU,cAAc,MAAM,MAAM,CAAA;AAC3C,iBAAc,QAAQ;;EAGxB,SAAS,mBAAmB,UAAkB;AAC5C,UAAO,aAAa,SAAQ;;EAG9B,SAAS,iBAAiB;AACxB,OAAI,OAAO,OAAO,MAAM,UAAU,MAAM,UAAW;GACnD,MAAM,QAAQ,OAAO,UAAS;AAC9B,QAAK,aAAa;IAAE,IAAI,MAAM;IAAI,MAAM,MAAM;IAAM,CAAA;;EAGtD,SAAS,kBAAkB,SAAiB;AAC1C,UAAO,YAAY,QAAO;AAC1B,cAAW,MAAM,OAAO,QAAO;AAC/B,QAAK,gBAAgB,QAAO;;EAG9B,SAAS,aAAa;AACpB,UAAO,MAAK;AACZ,QAAK,OAAM;;EAGb,SAAS,aAAa;AACpB,UAAO,MAAK;AACZ,QAAK,OAAM;;EAGb,SAAS,aAAa,QAAwB;GAC5C,MAAM,OAAO,OAAO,WAAW,OAAM;AACrC,QAAK,UAAU,MAAM,OAAM;GAE3B,MAAM,OAAO,IAAI,KAAK,CAAC,KAAK,EAAE,EAAE,MAAM,WAAW,SAAS,qBAAqB,YAAY,CAAA;GAC3F,MAAM,MAAM,IAAI,gBAAgB,KAAI;GACpC,MAAM,IAAI,SAAS,cAAc,IAAG;AACpC,KAAE,OAAO;AACT,KAAE,WAAW,aAAa;AAC1B,KAAE,OAAM;AACR,OAAI,gBAAgB,IAAG;;EAGzB,SAAS,eAAe;GACtB,MAAM,UAAU,OAAO,WAAW,WAAW,OAAO,aAAa,MAAK;AACtE,QAAK,UAAU,QAAO;AACtB,OAAI,SAAS;AACX,oBAAgB,QAAQ;AACxB,eAAW,QAAQ;;;EAIvB,SAAS,cAAc,OAAsB;AAE3C,QADgB,MAAM,WAAW,MAAM,YAAY,MAAM,QAAQ,KACrD;AACV,UAAM,gBAAe;AACrB,UAAM,WAAW,YAAY,GAAG,YAAW;AAC3C;;AAIF,QADiB,MAAM,QAAQ,YAAY,MAAM,QAAQ,gBACzC,OAAO,cAAc,MAAM,SAAS,GAAG;AACrD,UAAM,gBAAe;AACrB,sBAAiB;AACjB;;GAGF,MAAM,MAAM,SAAS,MAAM,IAAG;AAE9B,OADyB,OAAO,KAAK,OAAO,KAAK,OAAO,QAAQ,MAAM,UAAU,KAC1D;AACpB,WAAO,gBAAgB,OAAO,QAAQ,MAAM,MAAM,GAAG,GAAE;AACvD,QAAI,OAAO,cAAc,MAAM,SAAS,EACtC,qBAAmB;;;AAKzB,yBAAuB,UAAU,WAAW,cAAa;;uBAIvD,mBAkRM,OAAA,EAlRA,OAAK,eAAA,CAAA,qBAAA,EAAA,mCAA6D,QAAA,aAAW,CAAA,CAAA,EAAA,EAAA;IAEjF,mBA0KM,OA1KN,eA0KM;KAxKO,QAAA,eAAA,WAAA,EAAX,mBAoGM,OApGN,eAoGM;MAlGJ,mBA4CM,OA5CN,eA4CM,EAAA,UAAA,KAAA,EA3CJ,mBA6BS,UAAA,MAAA,WA5BkB,MAAA,OAAM,CAAC,OAAO,QAA/B,OAAO,UAAK;2BADtB,mBA6BS,UAAA;QA3BN,KAAK,MAAM;QACZ,MAAK;QACJ,OAAK,eAAA,CAAA,0BAAA,EAAA,kCAAiE,MAAM,OAAO,MAAA,OAAM,CAAC,YAAY,OAAO,IAAE,CAAA,CAAA;QAC/G,UAAK,WAAE,MAAA,OAAM,CAAC,eAAe,MAAM,GAAE;;QAEtC,mBAGE,QAAA;SAFA,OAAM;SACL,OAAK,eAAA,EAAA,iBAAqB,YAAY,aAAa,MAAM,IAAI,MAAK,GAAA,CAAA;;QAErE,mBAAiE,QAAjE,eAAiE,gBAApB,MAAM,KAAI,EAAA,EAAA;QAE/C,kBAAkB,MAAM,GAAE,GAAA,KAAA,WAAA,EADlC,mBAKO,QALP,eAKO,gBADF,kBAAkB,MAAM,GAAE,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;QAGvB,MAAA,OAAM,CAAC,OAAO,MAAM,SAAM,KAAA,WAAA,EADlC,mBAUS,UAAA;;SARP,MAAK;SACL,OAAM;SACL,cAAU,UAAY,MAAM;SAC5B,SAAK,eAAA,WAAO,kBAAkB,MAAM,GAAE,EAAA,CAAA,OAAA,CAAA;0CAEvC,mBAEM,OAAA;SAFD,MAAK;SAAO,QAAO;SAAe,SAAQ;SAAY,gBAAa;SAAI,kBAAe;SAAQ,mBAAgB;YACjH,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,EAAG,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,EAAA,GAAA,cAAA,IAAA,mBAAA,IAAA,KAAA;;iBAMzC,QAAA,kBAAkB,MAAA,OAAM,CAAC,OAAO,MAAM,SAAS,QAAA,aAAA,WAAA,EADvD,mBAWS,UAAA;;OATP,MAAK;OACL,OAAM;OACN,cAAW;OACV,SAAO;wCAER,mBAEM,OAAA;OAFD,MAAK;OAAO,QAAO;OAAe,SAAQ;OAAY,gBAAa;OAAI,kBAAe;OAAQ,mBAAgB;UACjH,mBAAqB,QAAA,EAAf,GAAE,YAAU,CAAA,EAAG,mBAAqB,QAAA,EAAf,GAAE,YAAU,CAAA,CAAA,EAAA,GAAA,EAEzC,mBAAgB,QAAA,MAAV,OAAG,GAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;kCAIb,mBAAyC,OAAA,EAApC,OAAM,6BAA2B,EAAA,MAAA,GAAA;MAGtC,mBAgDM,OAhDN,eAgDM;OA/CJ,mBAUS,UAAA;QATP,MAAK;QACJ,UAAQ,CAAG,MAAA,OAAM,CAAC,QAAQ;QAC3B,OAAM;QACN,OAAM;QACL,SAAO;2CAER,mBAEM,OAAA;QAFD,MAAK;QAAO,QAAO;QAAe,SAAQ;QAAY,gBAAa;QAAI,kBAAe;QAAQ,mBAAgB;WACjH,mBAA0B,QAAA,EAApB,GAAE,iBAAe,CAAA,EAAG,mBAAqE,QAAA,EAA/D,GAAE,4DAA0D,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,EAAA,GAAA,cAAA;OAIhG,mBAUS,UAAA;QATP,MAAK;QACJ,UAAQ,CAAG,MAAA,OAAM,CAAC,QAAQ;QAC3B,OAAM;QACN,OAAM;QACL,SAAO;2CAER,mBAEM,OAAA;QAFD,MAAK;QAAO,QAAO;QAAe,SAAQ;QAAY,gBAAa;QAAI,kBAAe;QAAQ,mBAAgB;WACjH,mBAA2B,QAAA,EAArB,GAAE,kBAAgB,CAAA,EAAG,mBAAmE,QAAA,EAA7D,GAAE,0DAAwD,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,EAAA,GAAA,eAAA;mCAI/F,mBAA0C,OAAA,EAArC,OAAM,8BAA4B,EAAA,MAAA,GAAA;OAEvC,mBASS,UAAA;QARP,MAAK;QACL,OAAM;QACN,OAAM;QACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,gBAAA,QAAe;2CAEvB,mBAEM,OAAA;QAFD,MAAK;QAAO,QAAO;QAAe,SAAQ;QAAY,gBAAa;QAAI,kBAAe;QAAQ,mBAAgB;;QACjH,mBAAqB,QAAA,EAAf,GAAE,YAAU,CAAA;QAAG,mBAA0B,QAAA,EAApB,GAAE,iBAAe,CAAA;QAAG,mBAAsD,QAAA,EAAhD,GAAE,6CAA2C,CAAA;;OAItG,mBASS,UAAA;QARP,MAAK;QACL,OAAM;QACN,OAAM;QACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,aAAY,OAAA;2CAEpB,mBAEM,OAAA;QAFD,MAAK;QAAO,QAAO;QAAe,SAAQ;QAAY,gBAAa;QAAI,kBAAe;QAAQ,mBAAgB;;QACjH,mBAAqB,QAAA,EAAf,GAAE,YAAU,CAAA;QAAG,mBAA0B,QAAA,EAApB,GAAE,iBAAe,CAAA;QAAG,mBAAsD,QAAA,EAAhD,GAAE,6CAA2C,CAAA;;;;KAQlG,MAAA,OAAM,CAAC,YAAY,SAAA,WAAA,EAD3B,YAUE,mBAAA;;MARC,eAAa,MAAA,OAAM,CAAC,cAAc;MAClC,QAAQ,MAAA,OAAM,CAAC,YAAY,MAAM;MACjC,OAAO,UAAA;MACP,iBAAe,aAAA;MACf,MAAM,QAAA;MACP,kBAAe;MACf,8BAAA;MACC,uBAAoB;;;;;;;;KAKf,MAAA,OAAM,CAAC,cAAc,MAAM,SAAM,KAAA,WAAA,EADzC,mBAuBM,OAvBN,gBAuBM;MAnBJ,mBAEO,QAFP,gBAEO,CADL,mBAAwD,UAAA,MAAA,gBAA7C,MAAA,OAAM,CAAC,cAAc,MAAM,OAAM,EAAA,EAAA,EAAA,OAAA,QAAA,OAAA,MAAA,gBAAY,oBAC1D,GAAA,EAAA,CAAA;kCACA,mBAAyC,OAAA,EAApC,OAAM,6BAA2B,EAAA,MAAA,GAAA;MAE9B,MAAA,OAAM,CAAC,eAAe,SAAA,WAAA,EAD9B,mBAOS,UAAA;;OALP,MAAK;OACL,OAAM;OACL,SAAO;SACT,aACQ,gBAAG,MAAA,OAAM,CAAC,QAAQ,MAAM,MAAK,MAAK,EAAE,OAAO,MAAA,OAAM,CAAC,eAAe,MAAK,EAAG,KAAI,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;MAEtF,mBAMS,UAAA;OALP,MAAK;OACL,OAAM;OACL,SAAO;SACT,UAED;;;;IAiCO,QAAA,eAAA,WAAA,EAAX,mBA8CM,OA9CN,gBA8CM,CA7CJ,mBA4CM,OA5CN,gBA4CM;iCA3CJ,mBAA8D,MAAA,EAA1D,OAAM,oCAAkC,EAAC,gBAAY,GAAA;KAEzD,YAQE,sBAAA;MAPC,eAAa,MAAA,OAAM,CAAC,eAAe;MACnC,SAAS,MAAA,OAAM,CAAC,QAAQ;MACxB,UAAU,QAAA;MACV,MAAM,QAAA,SAAI,OAAA,OAAA;MACV,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,OAAM,CAAC,gBAAgB,OAAM;MACjD,eAAc;MACd,gBAAe;;;;;;;KAIP,QAAA,mBAAA,WAAA,EAAX,mBAkBM,OAlBN,gBAkBM,CAjBJ,mBAgBM,OAhBN,gBAgBM,CAAA,eAfJ,mBAME,SAAA;iFALsB,QAAA;MACtB,MAAK;MACL,aAAY;MACZ,OAAM;MACL,SAAK,SAAQ,iBAAe,CAAA,QAAA,CAAA;kCAJpB,cAAA,MAAa,CAAA,CAAA,EAMxB,mBAOS,UAAA;MANP,MAAK;MACJ,UAAQ,CAAG,cAAA,MAAc,MAAI;MAC9B,OAAM;MACL,SAAO;QACT,SAED,GAAA,eAAA,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;kBAkBR,YAiDW,UAAA,EAjDD,IAAG,QAAM,EAAA,CAET,gBAAA,SAAA,WAAA,EADR,mBA+CM,OAAA;;KA7CJ,OAAM;KACL,SAAK,OAAA,OAAA,OAAA,KAAA,eAAA,WAAO,gBAAA,QAAe,OAAA,CAAA,OAAA,CAAA;QAE5B,mBAyCM,OAzCN,gBAyCM;iCAxCJ,mBAAgE,MAAA,EAA5D,OAAM,kCAAgC,EAAC,oBAAgB,GAAA;KAE3D,mBASM,OATN,gBASM,CAAA,OAAA,QAAA,OAAA,MARJ,mBAA4D,SAAA,EAArD,OAAM,kCAAgC,EAAC,UAAM,GAAA,GAAA,eACpD,mBAMS,UAAA;gFALc,QAAA;MACrB,OAAM;yCAEN,mBAAkC,UAAA,EAA1B,OAAM,QAAM,EAAC,QAAI,GAAA,EACzB,mBAAgC,UAAA,EAAxB,OAAM,OAAK,EAAC,OAAG,GAAA,CAAA,EAAA,EAAA,IAAA,EAAA,CAAA,CAAA,cAJd,aAAA,MAAY,CAAA,CAAA,CAAA,CAAA;KAQzB,mBAQM,OARN,gBAQM,CAAA,OAAA,QAAA,OAAA,MAPJ,mBAA0D,SAAA,EAAnD,OAAM,kCAAgC,EAAC,QAAI,GAAA,GAAA,eAClD,mBAKE,YAAA;8EAJmB,QAAA;MACnB,MAAK;MACL,OAAM;MACN,aAAY;kCAHH,WAAA,MAAU,CAAA,CAAA,CAAA,CAAA;KAOvB,mBAgBM,OAhBN,eAgBM,CAfJ,mBAMS,UAAA;MALP,MAAK;MACL,OAAM;MACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,gBAAA,QAAe;QACxB,WAED,EACA,mBAOS,UAAA;MANP,MAAK;MACJ,UAAQ,CAAG,WAAA,MAAW,MAAI;MAC3B,OAAM;MACL,SAAO;QACT,YAED,GAAA,cAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEheZ,MAAM,QAAQ;EAUd,MAAM,OAAO;EASb,MAAM,cAAc,IAAI,GAAE;EAC1B,MAAM,aAAa,IAA0B,KAAI;EACjD,MAAM,gBAAgB,IAAoB,MAAK;EAC/C,MAAM,YAAY,IAAmB,KAAI;EACzC,MAAM,aAAa,IAAmB,KAAI;EAG1C,MAAM,eAA8C;GAClD,MAAM;GACN,SAAS;GACT,KAAK;GACL,QAAQ;GACR,SAAS;GACT,UAAU;GACV,OAAO;GACP,UAAU;GACZ;EAEA,MAAM,gBAAgB,cAAc;GAClC,aAAa,MAAM,cAAc,EAAE;GACnC,OAAO;GACP,eAAe,MAAM;GACrB,UAAU,YAAY;IACpB,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;IACT;GACF,CAAA;EAED,MAAM,cAAc,eAAe;AACjC,OAAI,CAAC,WAAW,MAAO,QAAO;AAC9B,UAAO;IACL,KAAK,WAAW;IAChB,WAAW,cAAc;IAC3B;IACD;EAQD,MAAM,mBANiB,eAAuC;GAC5D,OAAO,cAAc;GACrB,MAAM;GACN,eAAe;GACf,WAAW,SAAS,WAAW,eAAe,SAAS,OAAO;GAC/D,CAAA,CACuC;EAExC,SAAS,eAAe,SAAkB,QAAgC;AACxE,WAAQ,QAAR;IACE,KAAK,OACH,QAAO,QAAQ;IACjB,KAAK,UACH,QAAO,QAAQ;IACjB,KAAK,MACH,QAAO,QAAQ;IACjB,KAAK,SACH,QAAO,QAAQ,aAAa,IAAI,KAAK,QAAQ,WAAW,CAAC,SAAS,GAAG;IACvE,KAAK,UACH,QAAO,QAAQ;IACjB,KAAK,WACH,QAAO,QAAQ;IACjB,KAAK,QACH,QAAO,QAAQ;IACjB,KAAK,WACH,QAAO,QAAQ;IACjB,QACE,QAAO;;;EAKb,SAAS,UAAU,SAA2B;AAC5C,OAAI,CAAC,QAAQ,WAAY,QAAO;AAEhC,UADe,IAAI,KAAK,QAAQ,WAAU,mBAC1B,IAAI,MAAK;;EAG3B,SAAS,eAAe,SAAkB,gBAAgB,IAAa;AACrE,OAAI,CAAC,QAAQ,WAAY,QAAO;GAChC,MAAM,SAAS,IAAI,KAAK,QAAQ,WAAU;GAC1C,MAAM,sBAAM,IAAI,MAAK;GACrB,MAAM,mBAAmB,OAAO,SAAS,GAAG,IAAI,SAAS,KAAK,MAAO,KAAK,KAAK;AAC/E,UAAO,kBAAkB,KAAK,mBAAmB;;EAGnD,SAAS,WAAW,SAA2B;AAC7C,OAAI,QAAQ,eAAe,KAAA,EAAW,QAAO;AAC7C,UAAO,QAAQ,cAAc,MAAM;;EAGrC,SAAS,iBAAiB,MAAyC;AACjE,OAAI,CAAC,KAAM,QAAO;AAElB,UADU,IAAI,KAAK,KAAI,CACd,mBAAmB,SAAS;IAAE,MAAM;IAAW,OAAO;IAAS,CAAA;;EAG1E,SAAS,cAAc,SAA0B;AAC/C,UAAO,QAAQ,cAAc;;EAG/B,SAAS,kBAAkB,OAAuB;AAChD,OAAI,SAAS,GAAI,QAAO;AACxB,OAAI,SAAS,GAAI,QAAO;AACxB,UAAO;;EAGT,SAAS,YAAY,SAA4B;GAC/C,MAAM,UAAoB,CAAC,yBAAwB;AACnD,OAAI,UAAU,QAAQ,CAAE,SAAQ,KAAK,kCAAiC;YAC7D,eAAe,QAAQ,CAAE,SAAQ,KAAK,wCAAuC;AACtF,OAAI,WAAW,QAAQ,CAAE,SAAQ,KAAK,oCAAmC;AACzE,OAAI,UAAU,UAAU,QAAQ,GAAI,SAAQ,KAAK,mCAAkC;AACnF,OAAI,WAAW,UAAU,QAAQ,GAAI,SAAQ,KAAK,oCAAmC;AACrF,UAAO;;EAIT,SAAS,WAAW,QAAuB;AACzC,OAAI,CAAC,MAAM,SAAU;AACrB,OAAI,WAAW,UAAU,OACvB,eAAc,QAAQ,cAAc,UAAU,QAAQ,SAAS;QAC1D;AACL,eAAW,QAAQ;AACnB,kBAAc,QAAQ;;;EAI1B,SAAS,WAAW,SAAkB;AACpC,QAAK,QAAQ,QAAO;;EAGtB,SAAS,aAAa,WAAmB;AACvC,QAAK,UAAU,UAAS;AAExB,QAAK,sBADY,MAAM,cAAc,EAAE,EAAE,QAAQ,MAAM,EAAE,OAAO,UAAS,CACxC;;EAGnC,SAAS,YAAY;AACnB,QAAK,MAAK;;EAIZ,SAAS,gBAAgB,OAAkB,SAAkB;AAC3D,OAAI,MAAM,YAAY,CAAC,MAAM,SAAU;AACvC,aAAU,QAAQ,QAAQ;AAC1B,OAAI,MAAM,cAAc;AACtB,UAAM,aAAa,gBAAgB;AACnC,UAAM,aAAa,QAAQ,cAAc,QAAQ,GAAE;;;EAIvD,SAAS,eAAe,OAAkB,SAAkB;AAC1D,OAAI,MAAM,YAAY,CAAC,MAAM,YAAY,CAAC,UAAU,MAAO;AAC3D,SAAM,gBAAe;AACrB,cAAW,QAAQ,QAAQ;;EAG7B,SAAS,kBAAkB;AACzB,cAAW,QAAQ;;EAGrB,SAAS,WAAW,OAAkB,eAAwB;AAC5D,OAAI,MAAM,YAAY,CAAC,MAAM,YAAY,CAAC,UAAU,MAAO;AAC3D,SAAM,gBAAe;GAErB,MAAM,WAAW,CAAC,GAAI,MAAM,cAAc,EAAE,CAAC;GAC7C,MAAM,YAAY,SAAS,WAAW,MAAM,EAAE,OAAO,UAAU,MAAK;GACpE,MAAM,UAAU,SAAS,WAAW,MAAM,EAAE,OAAO,cAAc,GAAE;AAEnE,OAAI,cAAc,MAAM,YAAY,MAAM,cAAc,SAAS;AAC/D,cAAU,QAAQ;AAClB,eAAW,QAAQ;AACnB;;GAGF,MAAM,CAAC,WAAW,SAAS,OAAO,WAAW,EAAC;AAC9C,YAAS,OAAO,SAAS,GAAG,QAAO;AAEnC,QAAK,WAAW,UAAU,OAAO,WAAW,QAAO;AACnD,QAAK,qBAAqB,SAAQ;AAElC,aAAU,QAAQ;AAClB,cAAW,QAAQ;;EAGrB,SAAS,gBAAgB;AACvB,aAAU,QAAQ;AAClB,cAAW,QAAQ;;;uBAKnB,mBA8NM,OA9NN,eA8NM,CA5NJ,mBA0BM,OA1BN,eA0BM,CAxBO,QAAA,cAAA,WAAA,EAAX,mBAUM,OAVN,eAUM,CAAA,OAAA,OAAA,OAAA,KATJ,mBAEM,OAAA;IAFD,OAAM;IAAiC,MAAK;IAAO,QAAO;IAAe,SAAQ;OACpF,mBAAwH,QAAA;IAAlH,kBAAe;IAAQ,mBAAgB;IAAQ,gBAAa;IAAI,GAAE;6BAE1E,mBAKE,SAAA;6EAJoB,QAAA;IACpB,MAAK;IACL,OAAM;IACN,aAAY;gCAHH,YAAA,MAAW,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,EAAA,CASf,QAAA,YAAA,WAAA,EADT,mBAUS,UAAA;;IARP,MAAK;IACL,OAAM;IACL,SAAO;qCAER,mBAEM,OAAA;IAFD,MAAK;IAAO,QAAO;IAAe,SAAQ;OAC7C,mBAA2F,QAAA;IAArF,kBAAe;IAAQ,mBAAgB;IAAQ,gBAAa;IAAI,GAAE;6BACpE,SAER,GAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,EAIW,MAAA,iBAAgB,CAAC,SAAM,KAAA,WAAA,EAApC,mBA4KQ,SA5KR,eA4KQ,CA3KN,mBAsCQ,SAAA,MAAA,CArCN,mBAoCK,MAAA,MAAA;IAnCO,QAAA,YAAQ,CAAK,QAAA,YAAA,WAAA,EAAvB,mBAAuD,MAAvD,cAAuD,IAAA,mBAAA,IAAA,KAAA;sBACvD,mBAgCK,UAAA,MAAA,WA/BW,QAAA,UAAP,QAAG;yBADZ,mBAgCK,MAAA;MA9BF,KAAK;MACL,OAAK,eAAA,CAAG,QAAA,WAAQ,qCAAA,GAAA,CAAA;MAChB,UAAK,WAAE,WAAW,IAAG;yCAEnB,aAAa,KAAG,GAAI,KACvB,EAAA,EACQ,QAAA,YAAA,WAAA,EADR,mBAwBM,OAAA;;MAtBH,OAAK,eAAA,CAAA,gCAAoE,WAAA,UAAe,MAAG,yCAAA,GAAA,CAAA;MAI5F,MAAK;MACL,QAAO;MACP,SAAQ;SAGA,WAAA,UAAe,OAAO,cAAA,UAAa,UAAA,WAAA,EAD3C,mBAME,QANF,cAME,KAAA,WAAA,EACF,mBAME,QANF,cAME,EAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,IAAA,cAAA;;KAGK,QAAA,YAAA,WAAA,EAAX,mBAA2C,MAA3C,cAA2C,IAAA,mBAAA,IAAA,KAAA;SAG/C,mBAmIQ,SAAA,MAAA,EAAA,UAAA,KAAA,EAlIN,mBAiIK,UAAA,MAAA,WAhIe,MAAA,iBAAgB,GAA3B,YAAO;wBADhB,mBAiIK,MAAA;KA/HF,KAAK,QAAQ;KACb,OAAK,eAAE,YAAY,QAAO,CAAA;KAC1B,WAAW,QAAA,YAAQ,CAAK,QAAA;KACxB,cAAS,WAAE,gBAAgB,QAAQ,QAAO;KAC1C,aAAQ,WAAE,eAAe,QAAQ,QAAO;KACxC,aAAW;KACX,SAAI,WAAE,WAAW,QAAQ,QAAO;KAChC,WAAS;;KAGA,QAAA,YAAQ,CAAK,QAAA,YAAA,WAAA,EAAvB,mBAIK,MAAA,gBAAA,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAHH,mBAEM,OAAA;MAFD,OAAM;MAAiC,MAAK;MAAO,QAAO;MAAe,SAAQ;SACpF,mBAA4F,QAAA;MAAtF,kBAAe;MAAQ,mBAAgB;MAAQ,gBAAa;MAAI,GAAE;;KAKlE,QAAA,QAAQ,SAAQ,OAAA,IAAA,WAAA,EAA1B,mBAWK,MAXL,gBAWK,CATK,QAAQ,OAAA,WAAA,EADhB,mBAQI,KAAA;;MAND,MAAM,QAAQ;MACf,QAAO;MACP,KAAI;MACJ,OAAM;wBAEH,QAAQ,KAAI,EAAA,GAAA,eAAA,KAAA,WAAA,EAEjB,mBAAsC,QAAA,gBAAA,gBAAtB,QAAQ,KAAI,EAAA,EAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;KAIpB,QAAA,QAAQ,SAAQ,UAAA,IAAA,WAAA,EAA1B,mBAEK,MAFL,eAEK,gBADA,QAAQ,iBAAa,IAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAIhB,QAAA,QAAQ,SAAQ,MAAA,IAAA,WAAA,EAA1B,mBAEK,MAFL,eAEK,gBADA,QAAQ,aAAS,IAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAIZ,QAAA,QAAQ,SAAQ,SAAA,IAAA,WAAA,EAA1B,mBAwBK,MAxBL,eAwBK,CAvBS,UAAU,QAAO,IAAA,WAAA,EAA7B,mBAEO,QAFP,eAEO,gBADF,iBAAiB,QAAQ,WAAU,CAAA,EAAA,EAAA,IAEvB,eAAe,QAAO,IAAA,WAAA,EAAvC,mBAEO,QAFP,eAEO,gBADF,iBAAiB,QAAQ,WAAU,CAAA,EAAA,EAAA,KAAA,WAAA,EAExC,mBAEO,QAAA,eAAA,gBADF,iBAAiB,QAAQ,WAAU,CAAA,EAAA,EAAA,GAI5B,UAAU,QAAO,IAAA,WAAA,EAA7B,mBAKO,QALP,eAKO,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAJL,mBAEM,OAAA;MAFD,OAAM;MAAgC,MAAK;MAAO,QAAO;MAAe,SAAQ;SACnF,mBAAiG,QAAA;MAA3F,kBAAe;MAAQ,mBAAgB;MAAQ,gBAAa;MAAI,GAAE;+BACpE,aAER,GAAA,CAAA,EAAA,CAAA,IACiB,eAAe,QAAO,IAAA,WAAA,EAAvC,mBAKO,QALP,eAKO,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAJL,mBAEM,OAAA;MAFD,OAAM;MAAgC,MAAK;MAAO,QAAO;MAAe,SAAQ;SACnF,mBAAiN,QAAA;MAA3M,kBAAe;MAAQ,mBAAgB;MAAQ,gBAAa;MAAI,GAAE;+BACpE,UAER,GAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;KAIQ,QAAA,QAAQ,SAAQ,UAAA,IAAA,WAAA,EAA1B,mBAQK,MARL,eAQK,CANK,QAAQ,oBAAA,WAAA,EADhB,mBAKO,QAAA;;MAHJ,OAAK,eAAA,CAAA,8BAAA,+BAAgE,QAAQ,mBAAgB,CAAA;wBAE3F,QAAQ,iBAAgB,EAAA,EAAA,KAAA,WAAA,EAE7B,mBAA4D,QAA5D,eAAoD,IAAC,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;KAI7C,QAAA,QAAQ,SAAQ,WAAA,IAAA,WAAA,EAA1B,mBAEK,MAFL,eAEK,gBADA,QAAQ,YAAQ,IAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAIX,QAAA,QAAQ,SAAQ,QAAA,IAAa,QAAA,kBAAA,WAAA,EAAvC,mBAaK,MAbL,eAaK,CAZQ,QAAQ,eAAe,KAAA,KAAA,WAAA,EAAlC,mBAUM,OAVN,eAUM,CATJ,mBAKM,OALN,eAKM,CAJJ,mBAGE,OAAA;MAFC,OAAK,eAAA,CAAA,iCAAoC,kBAAkB,cAAc,QAAO,CAAA,CAAA,CAAA;MAChF,OAAK,eAAA,EAAA,OAAA,GAAc,KAAK,IAAG,KAAM,cAAc,QAAO,CAAA,CAAA,IAAA,CAAA;oBAG3D,mBAEO,QAFP,eAEO,gBADF,cAAc,QAAO,CAAA,GAAI,MAC9B,EAAA,CAAA,CAAA,KAAA,WAAA,EAEF,mBAA4D,QAA5D,eAAoD,IAAC,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;KAI7C,QAAA,QAAQ,SAAQ,WAAA,IAAA,WAAA,EAA1B,mBAEK,MAFL,eAEK,gBADA,QAAQ,YAAQ,IAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;MAIV,QAAA,YAAA,WAAA,EAAX,mBAqBK,MArBL,eAqBK,CApBH,mBASS,UAAA;MARP,MAAK;MACL,OAAM;MACN,cAAW;MACV,UAAK,WAAE,WAAW,QAAO;uCAE1B,mBAEM,OAAA;MAFD,MAAK;MAAO,QAAO;MAAe,SAAQ;SAC7C,mBAAmM,QAAA;MAA7L,kBAAe;MAAQ,mBAAgB;MAAQ,gBAAa;MAAI,GAAE;qCAG5E,mBASS,UAAA;MARP,MAAK;MACL,OAAM;MACN,cAAW;MACV,UAAK,WAAE,aAAa,QAAQ,GAAE;uCAE/B,mBAEM,OAAA;MAFD,MAAK;MAAO,QAAO;MAAe,SAAQ;SAC7C,mBAAyM,QAAA;MAAnM,kBAAe;MAAQ,mBAAgB;MAAQ,gBAAa;MAAI,GAAE;;;kCASpF,mBAeM,OAfN,eAeM;8BAdJ,mBAEM,OAAA;KAFD,OAAM;KAAgC,MAAK;KAAO,QAAO;KAAe,SAAQ;QACnF,mBAAkV,QAAA;KAA5U,kBAAe;KAAQ,mBAAgB;KAAQ,gBAAa;KAAI,GAAE;;IAE1E,mBAEI,KAFJ,eAEI,gBADC,YAAA,QAAW,sBAAA,wBAAA,EAAA,EAAA;KAGP,QAAA,YAAQ,CAAK,YAAA,SAAA,WAAA,EADtB,mBAOS,UAAA;;KALP,MAAK;KACL,OAAM;KACL,SAAO;OACT,gBAED,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;uBE/aJ,mBA4BM,OAAA;IA3BH,OAAK,eAAA;;sBAAiD,QAAA;sBAA+B,QAAA;;IAKtF,MAAK;iCAEL,mBAkBM,OAAA;IAjBJ,OAAM;IACN,MAAK;IACL,SAAQ;OAER,mBAOE,UAAA;IANA,OAAA,EAAA,WAAA,QAAqB;IACrB,IAAG;IACH,IAAG;IACH,GAAE;IACF,QAAO;IACP,gBAAa;OAEf,mBAIE,QAAA;IAHA,OAAA,EAAA,WAAA,QAAqB;IACrB,MAAK;IACL,GAAE;cAGN,mBAAoD,QAApD,eAAoD,gBAAf,QAAA,MAAK,EAAA,EAAA,CAAA,EAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EErB9C,MAAM,QAAQ;EAMd,MAAM,OAAO;EAKb,MAAM,YAAY,cAAa;EAC/B,MAAM,MAAM,QAAO;EACnB,MAAM,YAAY,IAA4C,KAAI;EAClE,MAAM,cAAc,IAAI,EAAC;EACzB,MAAM,aAAa,IAAI,MAAK;EAC5B,MAAM,cAAc,IAAI,GAAE;EAC1B,MAAM,oBAAoB,IAAI,MAAK;EACnC,MAAM,kBAAkB,IAAmB,KAAI;EAC/C,MAAM,WAAW,IAAmB,KAAI;AAGxC,cAAY,MAAM,aAAa,SAAS;AACtC,OAAI,MAAM;AACR,cAAU,OAAM;AAChB,gBAAY,QAAQ;AACpB,gBAAY,QAAQ;AACpB,oBAAgB,QAAQ;AACxB,aAAS,QAAQ;AACjB,QAAI,MAAM,gBAAgB,MAAM,WAC9B,sBAAoB;aACX,MAAM,QAAQ,SAAS,EAChC,WAAU,QAAQ,QAAQ,MAAM,QAAQ,KAAK,KAAI;;KAGpD,EAAE,WAAW,MAAM,CAAA;EAGtB,MAAM,WAAyB;GAC7B;IAAE,IAAI;IAAS,OAAO;IAAS;GAC/B;IAAE,IAAI;IAAY,OAAO;IAAY;GACrC;IAAE,IAAI;IAAU,OAAO;IAAU;GACjC;IAAE,IAAI;IAAW,OAAO;IAAW;GACrC;EAEA,MAAM,eAAe,eAA6B;AAChD,OAAI,UAAU,YAAY,MACxB,QAAO;AAET,UAAO,SAAS,QAAO,MAAK,EAAE,OAAO,WAAU;IAChD;EAGD,MAAM,gBAAgB,eAAe;AAEnC,UADa,aAAa,MAAM,YAAY,QAC/B,MAAM;IACpB;AAGD,cAAY,aAAa,MAAM,SAAS,WAAW;AACjD,OAAI,YAAY,SAAS,OACvB,aAAY,QAAQ,SAAS;IAEhC;EAGD,MAAM,aAAa,eAAe,UAAU,QAAQ,MAAM,SAAS,EAAC;EACpE,MAAM,cAAc,eAAe,UAAU,cAAc,MAAM,OAAO,EAAC;AAEzE,QAAM;GAAC;GAAY;GAAa;GAAa,QAAQ;AACnD,OAAI,CAAC,UAAU,MAAO;GACtB,MAAM,QAAQ,aAAa;AAC3B,QAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IACrC,MAAM,KAAK,MAAM,GAAG;AACpB,QAAI,OAAO,QAAS,WAAU,MAAM,aAAa,GAAG,WAAW,MAAK;aAC3D,OAAO,WAAY,WAAU,MAAM,aAAa,GAAG,KAAI;aACvD,OAAO,SAAU,WAAU,MAAM,aAAa,GAAG,YAAY,MAAK;aAClE,OAAO,UAAW,WAAU,MAAM,aAAa,GAAG,KAAI;;KAEhE,EAAE,WAAW,MAAM,CAAA;EAGtB,SAAS,aAAa;AACpB,OAAI,cAAc,UAAU,QAC1B,WAAU,YAAW;;EAKzB,SAAS,cAAc;AACrB,QAAK,SAAS,UAAU,OAAO,MAAK;AACpC,QAAK,qBAAqB,MAAK;;EAGjC,SAAS,eAAe;AACtB,QAAK,qBAAqB,MAAK;;EAIjC,eAAe,sBAAsB;AACnC,OAAI,CAAC,MAAM,gBAAgB,CAAC,MAAM,WAAY;AAE9C,aAAU,UAAU,QAAQ;AAC5B,qBAAkB,QAAQ;AAC1B,mBAAgB,QAAQ;AAExB,OAAI;IACF,IAAI;AAEJ,QAAI,MAAM,WACR,cAAa,MAAM;QAKnB,eAHe,MAAM,IAAI,IACvB,gBAAgB,MAAM,aAAa,OACrC,EACoB;AAGtB,QAAI,CAAC,cAAc,OAAO,eAAe,UAAU;AACjD,qBAAgB,QAAQ;AACxB;;AAIF,QAAI,CADY,UAAU,mBAAmB,WAAU,EACzC;AACZ,qBAAgB,QAAQ;AACxB;;AAIF,UAAM,UAAS;IACf,MAAM,YAAY,aAAa,MAAM,WAAU,MAAK,EAAE,OAAO,SAAQ;AACrE,QAAI,aAAa,EACf,aAAY,QAAQ;YAEf,GAAG;AACV,oBAAgB,QAAQ,aAAa,QAAQ,EAAE,UAAU;aACjD;AACR,sBAAkB,QAAQ;;;EAK9B,SAAS,eAAe,OAAkB;AACxC,SAAM,gBAAe;AACrB,cAAW,QAAQ;GACnB,MAAM,QAAQ,MAAM,cAAc;AAClC,OAAI,SAAS,MAAM,SAAS,EAC1B,aAAY,MAAM,GAAE;;EAIxB,SAAS,gBAAgB,OAAc;GACrC,MAAM,SAAS,MAAM;AACrB,OAAI,OAAO,SAAS,OAAO,MAAM,SAAS,EACxC,aAAY,OAAO,MAAM,GAAE;;EAI/B,eAAe,YAAY,MAAY;AACrC,OAAI,CAAC,KAAK,KAAK,SAAS,OAAO,IAAI,CAAC,KAAK,KAAK,SAAS,OAAO,CAAE;AAEhE,YAAS,QAAQ;AACjB,OAAI;IAEF,MAAM,SAAS,SADF,MAAM,KAAK,MAAK,CACD;AAC5B,cAAU,UAAU,QAAQ;AAC5B,cAAU,QAAQ,QAAQ;AAC1B,gBAAY,QAAQ,KAAK;YAClB,GAAG;AACV,aAAS,QAAQ,aAAa,QAAQ,EAAE,UAAU;;;EAItD,SAAS,eAAe;AACtB,aAAU,QAAQ,QAAQ;AAC1B,aAAU,UAAU,QAAQ;AAC5B,eAAY,QAAQ;;EAItB,MAAM,uBAAuB,eAC3B,UAAU,QAAQ,OAAO,QAAQ,QAAO,MAAK,MAAM,cAAc,IAAI,EAAE,CACzE;EAGA,MAAM,aAAa,eAAe,YAAY,UAAU,aAAa,MAAM,SAAS,EAAC;EACrF,MAAM,cAAc,eAAe,YAAY,UAAU,EAAC;;uBAIxD,YA2VY,mBAAA;IA1VT,eAAa,QAAA;IACd,OAAM;IACN,MAAK;IACJ,uBAAkB,OAAA,QAAA,OAAA,OAAA,WAAE,KAAI,qBAAsB,OAAM;IACpD,SAAO;;2BAqVF,CAnVN,mBAmVM,OAnVN,eAmVM,CAlVJ,YAiVa,oBAAA;cAhVP;KAAJ,KAAI;iBACK,YAAA;8EAAW,QAAA;KACnB,OAAO,aAAA;KACP,QAAQ;KACT,MAAK;;KAGM,cAAU,cAwHb,CAvHN,mBAuHM,OAvHN,eAuHM;MArHJ,mBAwBM,OAxBN,eAwBM;OAtBI,QAAA,gBAAgB,QAAA,cAAA,WAAA,EADxB,YAQa,oBAAA;;QANV,SAAS,MAAA,UAAS,CAAC,UAAU,UAAK,eAAA,YAAA;QACnC,MAAK;QACJ,UAAU,kBAAA;QACV,SAAO;;+BAGV,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAFC,gBAED,GAAA,CAAA,EAAA,CAAA;;;OACA,YAMa,oBAAA;QALV,SAAS,MAAA,UAAS,CAAC,UAAU,UAAK,UAAA,YAAA;QACnC,MAAK;QACJ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,UAAS,CAAC,UAAU,QAAK;;+BAGnC,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAFC,WAED,GAAA,CAAA,EAAA,CAAA;;;OACA,YAMa,oBAAA;QALV,SAAS,MAAA,UAAS,CAAC,UAAU,UAAK,QAAA,YAAA;QACnC,MAAK;QACJ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,UAAS,CAAC,UAAU,QAAK;;+BAGnC,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAFC,SAED,GAAA,CAAA,EAAA,CAAA;;;;MAIS,MAAA,UAAS,CAAC,UAAU,UAAK,gBAAA,WAAA,EAApC,mBA6BM,OA7BN,eA6BM,CA5BO,kBAAA,SAAA,WAAA,EAAX,mBAGM,OAHN,eAGM,CAFJ,YAA4B,wBAAA,EAAZ,MAAK,MAAI,CAAA,EAAA,OAAA,QAAA,OAAA,MACzB,mBAAyC,QAAA,MAAnC,gCAA4B,GAAA,EAAA,CAAA,IAEpB,gBAAA,SAAA,WAAA,EAAhB,mBAMM,OANN,eAMM,CALJ,mBAA4B,KAAA,MAAA,gBAAtB,gBAAA,MAAe,EAAA,EAAA,EACrB,mBAGI,KAAA,MAAA;mDAHD,eACS,GAAA;OAAA,mBAAmH,UAAA;QAA3G,MAAK;QAAS,OAAM;QAA6B,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,UAAS,CAAC,UAAU,QAAK;UAAY,QAAK;mDAAS,QAC1H,GAAA;OAAA,mBAA+G,UAAA;QAAvG,MAAK;QAAS,OAAM;QAA6B,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,UAAS,CAAC,UAAU,QAAK;UAAU,MAAG;mDAAS,mBACpH,GAAA;cAEc,MAAA,UAAS,CAAC,QAAQ,SAAA,WAAA,EAAlC,mBAgBM,OAhBN,eAgBM,CAfJ,mBAOM,OAPN,eAOM,CANJ,mBAEO,QAFP,eAEO,CADL,mBAA0D,UAAA,MAAA,gBAA/C,MAAA,UAAS,CAAC,QAAQ,MAAM,KAAK,OAAM,EAAA,EAAA,EAAA,OAAA,QAAA,OAAA,MAAA,gBAAY,aAC5D,GAAA,EAAA,CAAA,EACA,mBAEO,QAFP,gBAEO,CADL,mBAAkD,UAAA,MAAA,gBAAvC,qBAAA,MAAqB,OAAM,EAAA,EAAA,EAAA,OAAA,QAAA,OAAA,MAAA,gBAAY,qBACpD,GAAA,EAAA,CAAA,CAAA,CAAA,EAEF,mBAMM,OANN,gBAMM,EAAA,UAAA,KAAA,EALJ,mBAIiB,UAAA,MAAA,WAHD,qBAAA,QAAP,QAAG;2BADZ,mBAIiB,QAAA;QAFd,KAAK;QACN,OAAM;0BACJ,IAAG,EAAA,EAAA;;MAMF,MAAA,UAAS,CAAC,UAAU,UAAK,WAAA,WAAA,EAApC,mBAUM,OAVN,gBAUM,CAAA,eATJ,mBAKE,YAAA;0EAJS,UAAS,CAAC,QAAQ,QAAK;OAChC,OAAM;OACN,MAAK;OACL,aAAY;mCAHH,MAAA,UAAS,CAAC,QAAQ,MAAK,CAAA,CAAA,EAKvB,MAAA,UAAS,CAAC,QAAQ,MAAM,SAAM,KAAA,WAAA,EAAzC,mBAEM,OAFN,gBAEM,gBADD,MAAA,UAAS,CAAC,QAAQ,MAAM,OAAM,GAAG,aACtC,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;MAIS,MAAA,UAAS,CAAC,UAAU,UAAK,SAAA,WAAA,EAApC,mBA4CM,OA5CN,eA4CM;QA3CQ,MAAA,UAAS,CAAC,QAAQ,SAAA,WAAA,EAA9B,mBA2BM,OAAA;;QA1BH,OAAK,eAAA,CAAA,6BAAqE,WAAA,QAAU,wCAAA,GAAA,CAAA;QAIpF,QAAM;QACN,YAAQ,OAAA,OAAA,OAAA,KAAA,eAAA,WAAU,WAAA,QAAU,MAAA,CAAA,UAAA,CAAA;QAC5B,aAAS,OAAA,OAAA,OAAA,MAAA,WAAE,WAAA,QAAU;;QAEtB,mBAKE,SAAA;SAJA,MAAK;SACL,QAAO;SACP,OAAM;SACL,UAAQ;;oCAEX,mBAEM,OAAA;SAFD,OAAM;SAA+B,MAAK;SAAO,QAAO;SAAe,SAAQ;YAClF,mBAA4M,QAAA;SAAtM,kBAAe;SAAQ,mBAAgB;SAAQ,gBAAa;SAAM,GAAE;;oCAE5E,mBAGI,KAAA,EAHD,OAAM,gCAA8B,EAAA,CACrC,mBAAsE,QAAA,EAAhE,OAAM,qCAAmC,EAAC,kBAAe,EAAA,gBAAO,gCAExE,CAAA,EAAA,GAAA;oCACA,mBAII,KAAA,EAJD,OAAM,gCAA8B,EAAC,+JAIxC,GAAA;;OAGS,SAAA,SAAQ,CAAK,MAAA,UAAS,CAAC,QAAQ,SAAA,WAAA,EAA1C,mBAEM,OAFN,eAEM,gBADD,SAAA,MAAQ,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;OAGF,MAAA,UAAS,CAAC,QAAQ,SAAA,WAAA,EAA7B,mBASM,OATN,eASM;oCARJ,mBAEM,OAAA;SAFD,OAAM;SAA6B,MAAK;SAAO,QAAO;SAAe,SAAQ;YAChF,mBAAiM,QAAA;SAA3L,kBAAe;SAAQ,mBAAgB;SAAQ,gBAAa;SAAI,GAAE;;QAE1E,mBAAiE,QAAjE,eAAiE,gBAArB,YAAA,MAAW,EAAA,EAAA;QACvD,mBAA8F,QAA9F,eAA8F,gBAAlD,MAAA,UAAS,CAAC,QAAQ,MAAM,KAAK,OAAM,GAAG,SAAK,EAAA;QACvF,mBAES,UAAA;SAFD,MAAK;SAAS,OAAM;SAA+B,SAAO;WAAc,gBAEhF;;;;KAOG,iBAAa,cA+ChB,CA9CN,mBA8CM,OA9CN,eA8CM;MA7CJ,mBAKM,OALN,eAKM;uCAJD,MAAA,UAAS,CAAC,SAAS,MAAM,OAAM,GAAG,SAAI,gBAAG,MAAA,UAAS,CAAC,QAAQ,MAAM,OAAM,GAAG,mDAEjE,gBAAG,MAAA,UAAS,CAAC,mBAAmB,MAAK,GAAG,uBACpD,EAAA;OAAA,mBAA4C,QAAA,MAAA,gBAAnC,MAAA,UAAS,CAAC,UAAU,MAAK,EAAA,EAAA;mDAAU,MAC9C,GAAA;;MAEA,mBAOM,OAPN,eAOM,CANJ,YAEa,oBAAA;OAFD,MAAK;OAAK,SAAQ;OAAa,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,UAAS,CAAC,qBAAoB,UAAA;;8BAEhF,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAF6F,iBAE7F,GAAA,CAAA,EAAA,CAAA;;UACA,YAEa,oBAAA;OAFD,MAAK;OAAK,SAAQ;OAAa,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,UAAS,CAAC,qBAAoB,UAAA;;8BAEhF,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAF6F,iBAE7F,GAAA,CAAA,EAAA,CAAA;;;MAGF,mBA4BM,OA5BN,eA4BM,EAAA,UAAA,KAAA,EA3BJ,mBA0BM,UAAA,MAAA,WAzBc,MAAA,UAAS,CAAC,SAAS,QAA9B,YAAO;2BADhB,mBA0BM,OAAA;QAxBH,KAAK,QAAQ;QACd,OAAM;WAEN,mBAGM,OAHN,eAGM,CAFJ,mBAAuE,QAAvE,eAAuE,gBAAxB,QAAQ,OAAM,EAAA,EAAA,EAC7D,mBAAmF,QAAnF,eAAmF,gBAAnC,QAAQ,WAAU,GAAG,WAAO,EAAA,CAAA,CAAA,EAE9E,mBAgBM,OAhBN,eAgBM;QAfJ,mBAIiB,UAAA;SAHf,MAAK;SACJ,OAAK,eAAA,CAAA,+BAAkC,QAAQ,WAAM,YAAA,wCAAA,GAAA,CAAA;SACrD,UAAK,WAAE,MAAA,UAAS,CAAC,iBAAiB,QAAQ,OAAK,UAAA;WACjD,WAAO,IAAA,cAAA;QACR,mBAIiB,UAAA;SAHf,MAAK;SACJ,OAAK,eAAA,CAAA,+BAAkC,QAAQ,WAAM,YAAA,6EAAA,GAAA,CAAA;SACrD,UAAK,WAAE,MAAA,UAAS,CAAC,iBAAiB,QAAQ,OAAK,UAAA;WACjD,WAAO,IAAA,cAAA;QACR,mBAIY,UAAA;SAHV,MAAK;SACJ,OAAK,eAAA,CAAA,+BAAkC,QAAQ,WAAM,OAAA,wEAAA,GAAA,CAAA;SACrD,UAAK,WAAE,MAAA,UAAS,CAAC,iBAAiB,QAAQ,OAAK,KAAA;WACjD,MAAE,IAAA,cAAA;;;;KAQF,eAAW,cAyDd,CAxDN,mBAwDM,OAxDN,eAwDM;MAvDJ,mBAwCM,OAxCN,eAwCM,EAAA,UAAA,KAAA,EAvCJ,mBAsCM,UAAA,MAAA,WArCU,MAAA,UAAS,CAAC,iBAAiB,QAAlC,QAAG;2BADZ,mBAsCM,OAAA;QApCH,KAAK,IAAI;QACT,OAAK,eAAA,CAAA,+BAAuE,MAAA,UAAS,CAAC,cAAc,MAAM,IAAI,IAAI,MAAK,GAAA,yCAAA,GAAA,CAAA;;QAKxH,mBAUM,OAVN,eAUM,CATJ,mBAOQ,SAPR,eAOQ,CANN,mBAIE,SAAA;SAHA,MAAK;SACJ,SAAS,MAAA,UAAS,CAAC,cAAc,MAAM,IAAI,IAAI,MAAK;SACpD,WAAM,WAAE,MAAA,UAAS,CAAC,YAAY,IAAI,MAAK;iEAE1C,mBAAiE,QAAA,EAA3D,OAAM,uCAAqC,EAAC,YAAQ,GAAA,EAAA,CAAA,EAE5D,mBAAoF,QAApF,eAAoF,gBAAhC,IAAI,YAAW,GAAG,WAAO,EAAA,CAAA,CAAA;QAG/E,YAKE,mBAAA;SAJC,eAAa,IAAI;SAClB,aAAY;SACZ,OAAM;SACL,wBAAkB,WAAE,MAAA,UAAS,CAAC,YAAY,IAAI,OAAO,OAAO,UAAM,GAAA,CAAA;;QAGrE,mBAUM,OAVN,eAUM,EAAA,UAAA,KAAA,EATJ,mBAIiB,UAAA,MAAA,WAHD,IAAI,aAAa,MAAK,GAAA,EAAA,GAA7B,QAAG;6BADZ,mBAIiB,QAAA;UAFd,KAAK;UACN,OAAM;4BACJ,IAAG,EAAA,EAAA;mBAEC,IAAI,aAAa,SAAM,KAAA,WAAA,EAD/B,mBAG+C,QAH/C,eAGC,MAAC,gBAAG,IAAI,aAAa,SAAM,EAAA,GAAO,SAAK,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;;;MAKnC,MAAA,UAAS,CAAC,cAAc,MAAM,OAAI,KAAA,WAAA,EAA7C,mBAQM,OARN,eAQM,CAAA,OAAA,QAAA,OAAA,MAAA,gBARoF,kBAExF,GAAA,GAAA,mBAKW,UAAA,MAAA,gBAJT,MAAA,UAAS,CAAC,iBAAiB,MAAyB,QAAO,MAAK,MAAA,UAAS,CAAC,cAAc,MAAM,IAAI,EAAE,MAAK,CAAA,CAAsB,KAAI,MAAK,EAAE,KAAI,CAAqB,KAAI,MAAA,CAAA,EAAA,EAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;MAOhK,MAAA,UAAS,CAAC,cAAc,MAAM,SAAI,KAAA,WAAA,EAA7C,mBAEM,OAFN,eAA4F,2CAE5F,IAAA,mBAAA,IAAA,KAAA;;KAKO,gBAAY,cA2Df,CA1DN,mBA0DM,OA1DN,eA0DM;MAzDJ,mBAUM,OAVN,eAUM;OATJ,mBAEO,QAFP,eAEO,CADL,mBAAoD,UAAA,MAAA,gBAAzC,MAAA,UAAS,CAAC,OAAO,MAAM,OAAM,EAAA,EAAA,EAAA,OAAA,QAAA,OAAA,MAAA,gBAAY,YACtD,GAAA,EAAA,CAAA;OACA,mBAEO,QAFP,eAEO,CADL,mBAA2F,UAAA,MAAA,gBAAhF,MAAA,UAAS,CAAC,OAAO,MAAM,QAAQ,KAAK,MAAM,MAAM,EAAE,QAAQ,QAAM,EAAA,CAAA,EAAA,EAAA,EAAA,OAAA,QAAA,OAAA,MAAA,gBAAgB,aAC7F,GAAA,EAAA,CAAA;OACY,MAAA,UAAS,CAAC,gBAAgB,MAAM,SAAM,KAAA,WAAA,EAAlD,mBAEO,QAFP,eAEO,CADL,mBAA6D,UAAA,MAAA,gBAAlD,MAAA,UAAS,CAAC,gBAAgB,MAAM,OAAM,EAAA,EAAA,EAAA,OAAA,QAAA,OAAA,MAAA,gBAAY,cAC/D,GAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;MAIM,MAAA,UAAS,CAAC,cAAc,SAAA,WAAA,EADhC,YAMW,kBAAA;;OAJT,MAAK;OACL,OAAM;;8BAE6F,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA;wBADpG,wGACoG,GAAA;QAAA,mBAAe,MAAA,MAAX,UAAM,GAAA;wBAAK,gEACpH,GAAA;;;;MAEA,mBAyBM,OAzBN,eAyBM,EAAA,UAAA,KAAA,EAxBJ,mBAuBU,UAAA,MAAA,WAtBQ,MAAA,UAAS,CAAC,OAAO,QAA1B,UAAK;2BADd,mBAuBU,WAAA;QArBP,KAAK,MAAM;QACZ,OAAM;WAEN,mBAUU,WAVV,eAUU;QATR,mBAGE,QAAA;SAFA,OAAM;SACL,OAAK,eAAA,EAAA,iBAAqB,MAAM,OAAK,CAAA;;QAExC,mBAAyE,QAAzE,eAAyE,gBAApB,MAAM,KAAI,EAAA,EAAA;QAC/D,mBAGkC,QAAA;SAFhC,OAAM;SACL,OAAK,eAAA;UAAA,iBAAqB,MAAM,QAAK;UAAA,OAAgB,MAAM;UAAK,CAAA;2BAC/D,MAAM,QAAQ,OAAM,EAAA,EAAA;WAE1B,mBAMM,OANN,eAMM,EAAA,UAAA,KAAA,EALJ,mBAIoB,UAAA,MAAA,WAHD,MAAM,UAAhB,WAAM;4BADf,mBAIoB,QAAA;SAFjB,KAAK;SACN,OAAM;2BACJ,OAAM,EAAA,EAAA;;;MAKL,MAAA,UAAS,CAAC,gBAAgB,MAAM,SAAM,KAAA,WAAA,EAAjD,mBASM,OATN,eASM,CAAA,OAAA,QAAA,OAAA,MARJ,mBAA2E,OAAA,EAAtE,OAAM,2CAAyC,EAAC,oBAAgB,GAAA,GACrE,mBAMM,OANN,aAMM,EAAA,UAAA,KAAA,EALJ,mBAIoB,UAAA,MAAA,WAHD,MAAA,UAAS,CAAC,gBAAgB,QAApC,WAAM;2BADf,mBAIoB,QAAA;QAFjB,KAAK;QACN,OAAM;0BACJ,OAAM,EAAA,EAAA;;;KAOP,YAAU,SA4Bb,EA5BiB,QAAQ,QAAQ,iBAAU,CACjD,mBA2BM,OA3BN,aA2BM;MA1BJ,YAEa,oBAAA;OAFD,SAAQ;OAAa,SAAO;;8BAExC,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAFsD,YAEtD,GAAA,CAAA,EAAA,CAAA;;;kCACA,mBAA2C,OAAA,EAAtC,OAAM,+BAA6B,EAAA,MAAA,GAAA;OAE/B,YAAA,SAAA,WAAA,EADT,YAMa,oBAAA;;OAJX,SAAQ;OACP,SAAO;;8BAGV,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAFC,UAED,GAAA,CAAA,EAAA,CAAA;;;OAES,WAAA,SAAA,WAAA,EADT,YAOa,oBAAA;;OALX,SAAQ;OACP,UAAQ,CAAG;OACX,eAAK;AAAU,oBAAU;AAAI,gBAAM;;;8BAGtC,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAFC,UAED,GAAA,CAAA,EAAA,CAAA;;;MAEQ,WAAA,SAAA,WAAA,EADR,YAMa,oBAAA;;OAJX,SAAQ;OACP,SAAO;;8BAGV,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAFC,WAED,GAAA,CAAA,EAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEnhBZ,MAAM,QAAQ;EAQd,MAAM,OAAO;EAWb,MAAM,sBAAsB,IAAI,MAAK;EACrC,MAAM,eAAe,IAAI,GAAE;EAC3B,MAAM,eAAe,IAAsB,KAAI;EAC/C,MAAM,mBAAmB,IAA6B,KAAI;EAC1D,MAAM,cAAc,IAAI,GAAE;EAC1B,MAAM,iBAAiB,iBAAgB;EAGvC,MAAM,iBAAiB,IAAmB,KAAI;EAC9C,MAAM,kBAAkB,IAAmB,KAAI;EAC/C,MAAM,gBAAgB,IAAmB,KAAI;EAI7C,MAAM,gBAAgB,IAAmB,KAAI;EAC7C,MAAM,oBAAoB,IAA0B,KAAI;EACxD,MAAM,gBAAgB,IAAmB,KAAI;EAC7C,MAAM,kBAAkB,IAA+B,KAAI;EAG3D,MAAM,iBAAiB,SAAS;GAC9B,WAAW,MAAM;GACjB,MAAM,UAAU,KAAK,iBAAiB,MAAM;GAC7C,CAAA;EAED,MAAM,eAAe,gBAAgB;GACnC,eAAe,MAAM;GACrB,QAAQ;GACT,CAAA;EACD,MAAM,qBAAqB,aAAa;EACxC,MAAM,gBAAgB,aAAa;EAEnC,MAAM,kBAAkB,eAAe,eAAe,MAAM,SAAS,EAAC;EACtE,MAAM,mBAAmB,aAAa;EAOtC,MAAM,kBALe,cAAc;GACjC,aAAa,MAAM;GACnB,OAAO;GACP,UAAS,WAAU;GACpB,CAAA,CACoC;EAErC,MAAM,kBAAkB,iBAAiB;GACvC,gBAAgB,MAAM;GACtB,aAAa,MAAM;GACpB,CAAA;EACD,MAAM,gBAAgB,gBAAgB;EAGtC,SAAS,kBAAkB;AACzB,QAAK,qBAAqB,gBAAgB,WAAW,CAAA;;EAGvD,SAAS,aAAa,QAAgB;AACpC,QAAK,qBAAqB,gBAAgB,YAAY,OAAO,CAAA;;EAG/D,SAAS,uBAAuB,SAAmB;AACjD,QAAK,qBAAqB,gBAAgB,aAAa,QAAQ,CAAA;;EAGjE,SAAS,mBAAmB,WAAmB;GAC7C,MAAM,QAAQ,aAAa,UAAU,UAAS;AAC9C,OAAI,CAAC,MAAO;AACZ,0BAAuB,MAAM,QAAO;;EAGtC,SAAS,wBAAwB,YAA8B;AAC7D,0BAAuB,WAAW,WAAU;;EAI9C,SAAS,gBAAgB,SAA4B;AACnD,UAAO,gBAAgB,gBAAgB,QAAO;;EAGhD,SAAS,oBAAoB,SAA4B;AACvD,UAAO,gBAAgB,oBAAoB,QAAO;;EAGpD,SAAS,qBAAqB,WAA4B;GACxD,MAAM,QAAQ,aAAa,UAAU,UAAS;AAC9C,UAAO,QAAQ,gBAAgB,MAAM,QAAQ,GAAG;;EAGlD,SAAS,yBAAyB,WAA4B;GAC5D,MAAM,QAAQ,aAAa,UAAU,UAAS;AAC9C,UAAO,QAAQ,oBAAoB,MAAM,QAAQ,GAAG;;EAGtD,SAAS,0BAA0B,YAAuC;AACxE,UAAO,gBAAgB,WAAW,WAAU;;EAG9C,SAAS,8BAA8B,YAAuC;AAC5E,UAAO,oBAAoB,WAAW,WAAU;;EAIlD,SAAS,oBAAoB,WAAmB;AAC9C,kBAAe,OAAO,UAAS;;EAGjC,SAAS,gBAAgB,WAA4B;AACnD,UAAO,eAAe,WAAW,UAAS;;EAG5C,SAAS,kBAAkB;GACzB,MAAM,WAAqB,EAAC;AAC5B,QAAK,MAAM,SAAS,mBAAmB,OAAO;AAC5C,aAAS,KAAK,SAAS,MAAM,OAAM;AACnC,SAAK,MAAM,OAAO,MAAM,UACtB,UAAS,KAAK,IAAI,KAAI;;AAG1B,YAAS,KAAK,gBAAe;AAC7B,kBAAe,YAAY,SAAQ;;EAGrC,SAAS,oBAAoB;AAC3B,kBAAe,aAAY;;EAI7B,SAAS,sBAAsB,QAAyB;AACtD,QAAK,cAAc,OAAM;AACzB,QAAK,iBAAiB,OAAO,OAAM;;EAIrC,SAAS,cAAc;AACrB,kBAAe,QAAQ,EAAC;;EAG1B,SAAS,iBAAiB,YAA8B;AACtD,kBAAe,QAAQ,eAAe,MAAM,QAC1C,MAAK,CAAC,WAAW,UAAU,MAAK,OAAM,GAAG,SAAS,EAAE,KAAI,CAC1D;;EAGF,SAAS,YAAY,WAAmB;AACtC,kBAAe,QAAQ,eAAe,MAAM,QAAO,MAAK,EAAE,SAAS,UAAS;;EAG9E,SAAS,sBAAsB,QAAgB,WAAmB;AAChE,kBAAe,QAAQ,eAAe,MAAM,KAAI,MAC9C,EAAE,SAAS,YACP;IAAE,GAAG;IAAG,SAAS,EAAE,QAAQ,QAAQ,MAAc,MAAM,OAAO;IAAC,GAC/D,EACN;;EAIF,SAAS,gBAAgB,QAAgB,aAA4B,OAAkB;AACrF,kBAAe,QAAQ;AACvB,mBAAgB,QAAQ;AACxB,OAAI,MAAM,cAAc;AACtB,UAAM,aAAa,gBAAgB;AACnC,UAAM,aAAa,QAAQ,cAAc,OAAM;;;EAInD,SAAS,iBAAiB;AACxB,kBAAe,QAAQ;AACvB,mBAAgB,QAAQ;AACxB,iBAAc,QAAQ;;EAGxB,SAAS,gBAAgB;AACvB,mBAAe;;EAGjB,SAAS,eAAe,WAAmB,OAAkB;AAE3D,OAAI,CAAC,eAAe,MAAO;AAC3B,SAAM,gBAAe;AACrB,OAAI,MAAM,aACR,OAAM,aAAa,aAAa;AAElC,iBAAc,QAAQ;;EAGxB,SAAS,kBAAkB;AACzB,iBAAc,QAAQ;;EAGxB,SAAS,WAAW,iBAAyB,OAAkB;AAC7D,OAAI,CAAC,eAAe,MAAO;AAC3B,SAAM,gBAAe;GAErB,MAAM,SAAS,eAAe;GAC9B,MAAM,cAAc,gBAAgB;AAEpC,OAAI,gBAAgB,iBAAiB;AACnC,oBAAe;AACf;;AAGF,kBAAe,QAAQ,eAAe,MAAM,KAAI,MAAK;AACnD,QAAI,eAAe,EAAE,SAAS,YAC5B,QAAO;KAAE,GAAG;KAAG,SAAS,EAAE,QAAQ,QAAQ,MAAc,MAAM,OAAO;KAAC;AAExE,QAAI,EAAE,SAAS,mBAAmB,CAAC,EAAE,QAAQ,SAAS,OAAO,CAC3D,QAAO;KAAE,GAAG;KAAG,SAAS,CAAC,GAAG,EAAE,SAAS,OAAO;KAAC;AAEjD,WAAO;KACR;AAED,mBAAe;;EAIjB,SAAS,gBAAgB,QAA+B;AACtD,UAAO,OAAO,MAAK,MAAK,EAAE,KAAK,SAAS,IAAI,CAAC,GAAG,MAAM;;EAGxD,SAAS,eAAe,WAAmB,WAA2B;GACpE,MAAM,QAAQ,UAAU,MAAM,UAAS;AACvC,UAAO,MAAM,SAAS,IAAI,MAAM,KAAK;;EAGvC,SAAS,qBAAqB,MAAc,MAAqB,OAAkB;AACjF,iBAAc,QAAQ;AACtB,qBAAkB,QAAQ;AAC1B,OAAI,MAAM,cAAc;AACtB,UAAM,aAAa,gBAAgB;AACnC,UAAM,aAAa,QAAQ,cAAc,cAAc,OAAM;;;EAIjE,SAAS,qBAAqB;AAC5B,iBAAc,QAAQ;AACtB,qBAAkB,QAAQ;AAC1B,iBAAc,QAAQ;AACtB,mBAAgB,QAAQ;;EAG1B,SAAS,oBAAoB,MAAc,MAAqB,OAAkB;AAChF,OAAI,CAAC,cAAc,SAAS,kBAAkB,UAAU,KAAM;AAC9D,OAAI,cAAc,UAAU,KAAM;AAIlC,OAAI,SAAS,OAAO;IAClB,MAAM,MAAM,gBAAgB,eAAe,MAAK;AAChD,QAAI,eAAe,cAAc,OAAO,IAAI,KAAK,eAAe,MAAM,IAAI,CAAE;;AAG9E,SAAM,gBAAe;AACrB,OAAI,MAAM,aAAc,OAAM,aAAa,aAAa;GAExD,MAAM,OAAQ,MAAM,cAA8B,uBAAsB;AACxE,iBAAc,QAAQ;AACtB,mBAAgB,QAAQ,MAAM,UAAU,KAAK,MAAM,KAAK,SAAS,IAAI,WAAW;;EAGlF,SAAS,qBAAqB,OAAkB;GAC9C,MAAM,MAAM,MAAM;GAClB,MAAM,MAAM,MAAM;AAClB,OAAI,OAAO,IAAI,SAAS,IAAI,CAAE;AAC9B,iBAAc,QAAQ;AACtB,mBAAgB,QAAQ;;EAG1B,SAAS,gBAAgB,MAAc,MAAqB,OAAkB;AAC5E,OAAI,CAAC,cAAc,SAAS,kBAAkB,UAAU,MAAM;AAC5D,wBAAmB;AACnB;;GAEF,MAAM,WAAW,gBAAgB;AACjC,OAAI,CAAC,YAAY,cAAc,UAAU,MAAM;AAC7C,wBAAmB;AACnB;;AAGF,SAAM,gBAAe;AAErB,OAAI,SAAS,QACX,gBAAe,cAAc,OAAO,MAAM,SAAQ;OAElD,WAAU,cAAc,OAAO,MAAM,SAAQ;AAG/C,uBAAmB;;EAGrB,SAAS,UAAU,QAAgB,QAAgB,UAA8B;AAC/E,OAAI,WAAW,OAAQ;GACvB,MAAM,SAAS,CAAC,GAAG,eAAe,MAAK;GACvC,MAAM,SAAS,OAAO,WAAU,MAAK,EAAE,SAAS,OAAM;AACtD,OAAI,WAAW,GAAI;GACnB,MAAM,CAAC,QAAQ,OAAO,OAAO,QAAQ,EAAC;GACtC,MAAM,YAAY,OAAO,WAAU,MAAK,EAAE,SAAS,OAAM;AACzD,OAAI,cAAc,GAAI;AACtB,UAAO,OAAO,aAAa,WAAW,YAAY,YAAY,GAAG,GAAG,KAAI;AACxE,kBAAe,QAAQ;;EAGzB,SAAS,eAAe,QAAgB,QAAgB,UAA8B;AACpF,OAAI,WAAW,OAAQ;GACvB,MAAM,SAAS,CAAC,GAAG,eAAe,MAAK;GACvC,MAAM,MAAM,gBAAgB,OAAM;GAElC,MAAM,YAAY,MAAmB,eAAe,EAAE,MAAM,IAAI,KAAK;GACrE,MAAM,YAAY,MAAmB,eAAe,EAAE,MAAM,IAAI,KAAK;GAErE,MAAM,eAAe,OAAO,OAAO,SAAQ;AAC3C,OAAI,aAAa,WAAW,EAAG;GAC/B,MAAM,YAAY,OAAO,QAAO,MAAK,CAAC,SAAS,EAAE,CAAA;GAEjD,IAAI,cAAc;GAClB,IAAI,aAAa;AACjB,aAAU,SAAS,GAAG,MAAM;AAC1B,QAAI,SAAS,EAAE,EAAE;AACf,SAAI,gBAAgB,GAAI,eAAc;AACtC,kBAAa;;KAEhB;AACD,OAAI,gBAAgB,GAAI;GAExB,MAAM,YAAY,aAAa,WAAW,cAAc,aAAa;AACrE,aAAU,OAAO,WAAW,GAAG,GAAG,aAAY;AAC9C,kBAAe,QAAQ;;EAIzB,SAAS,gBAAgB,WAAmB,OAAc;AACxD,SAAM,iBAAgB;AACtB,gBAAa,QAAQ;IAAE,MAAM;IAAU,MAAM;IAAU;AACvD,oBAAiB,OAAO,OAAM;;EAGhC,SAAS,0BAA0B,YAA8B,OAAc;AAC7E,SAAM,iBAAgB;AACtB,gBAAa,QAAQ;IAAE,MAAM;IAAU,OAAO,WAAW,UAAU,KAAI,OAAM,GAAG,KAAK;IAAC;AACtF,oBAAiB,OAAO,OAAM;;EAGhC,SAAS,kBAAkB,OAAc;GACvC,MAAM,OAAO,aAAa;AAC1B,OAAI,CAAC,KAAM;GAEX,MAAM,WAAY,MAAM,OAA4B;GAGpD,MAAM,UAAU,KAAK,SAAS,WAAW,IAAI,IAAI,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,KAAK,CAAA;AAClF,kBAAe,QAAQ,eAAe,MAAM,KAAI,MAC9C,QAAQ,IAAI,EAAE,KAAK,GAAG;IAAE,GAAG;IAAG,OAAO;IAAU,GAAG,EACpD;AACA,gBAAa,QAAQ;;EAGvB,SAAS,cAAc,WAA2B;AAChD,UAAO,aAAa,cAAc,UAAS;;EAG7C,MAAM,kBAAkB,eAAe;GACrC,MAAM,OAAO,aAAa;AAC1B,OAAI,CAAC,KAAM,QAAO;AAClB,UAAO,cAAc,KAAK,SAAS,WAAW,KAAK,MAAM,KAAK,KAAK,KAAI;IACxE;EAGD,SAAS,cAAc;AACrB,OAAI,CAAC,aAAa,MAAM,MAAM,CAAE;GAEhC,MAAM,aAAa,IAAI,IAAI,eAAe,MAAM,KAAI,MAAK,EAAE,MAAM,CAAA;GACjE,MAAM,iBAAiB,eAAe,MAAK,MAAK,CAAC,WAAW,IAAI,EAAE,CAAC,IAAI,eAAe;GAEtF,MAAM,WAAwB;IAC5B,MAAM,aAAa,MAAM,MAAM;IAC/B,OAAO;IACP,SAAS,EAAE;IACb;AAEA,kBAAe,QAAQ,CAAC,GAAG,eAAe,OAAO,SAAQ;AACzD,gBAAa,QAAQ;;;uBAMrB,mBA0gBM,OA1gBN,eA0gBM;IAxgBJ,mBASQ,SATR,eASQ;KARN,mBAKE,SAAA;MAJA,MAAK;MACJ,SAAS,MAAA,cAAa;MACtB,UAAQ;MACT,OAAM;;iCAER,mBAAsE,QAAA,EAAhE,OAAM,0CAAwC,EAAC,cAAU,GAAA;KAC/D,mBAAwF,QAAxF,eAAwF,gBAAhC,QAAA,QAAQ,OAAM,GAAG,YAAQ,EAAA;;IAIxE,QAAA,kBAAA,WAAA,EAAX,mBA+BM,OA/BN,eA+BM,CA9BJ,mBA6BM,OA7BN,eA6BM,CA1BI,QAAA,oBAAA,WAAA,EADR,YAYa,oBAAA;;KAVV,SAAS,gBAAA,QAAe,YAAA;KACzB,MAAK;KACJ,UAAU,QAAA,QAAQ,WAAM;KACzB,OAAM;KACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,oBAAA,QAAmB;;4BAIrB,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAFN,mBAEM,OAAA;MAFD,OAAM;MAAoC,MAAK;MAAO,QAAO;MAAe,SAAQ;SACvF,mBAAuG,QAAA;MAAjG,kBAAe;MAAQ,mBAAgB;MAAQ,gBAAa;MAAI,GAAE;eAE1E,mBAAwB,QAAA,MAAlB,eAAW,GAAA,CAAA,EAAA,CAAA;;oEAInB,YAWa,oBAAA;KAVX,SAAQ;KACR,MAAK;KACJ,UAAU,eAAA,MAAe,WAAM;KAChC,OAAM;KACL,SAAO;KACR,OAAM;;4BAIA,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAFN,mBAEM,OAAA;MAFD,OAAM;MAAoC,MAAK;MAAO,QAAO;MAAe,SAAQ;SACvF,mBAAwL,QAAA;MAAlL,kBAAe;MAAQ,mBAAgB;MAAQ,gBAAa;MAAI,GAAE;;;;IAOrE,gBAAA,SAAA,WAAA,EAAX,mBA2aM,OA3aN,eA2aM;KAzaJ,mBAcM,OAdN,eAcM,CAbJ,mBAA4F,QAA5F,eAAiD,aAAQ,gBAAG,eAAA,MAAe,OAAM,GAAG,KAAC,EAAA,EACrF,mBAWM,OAAA,EAXD,OAAM,yCAAuC,EAAA,CAChD,mBAIS,UAAA;MAJD,MAAK;MAAS,OAAM;MAAoC,SAAO;MAAiB,OAAM;yCAC5F,mBAEM,OAAA;MAFD,OAAM;MAAoC,MAAK;MAAO,QAAO;MAAe,SAAQ;SACvF,mBAA2F,QAAA;MAArF,kBAAe;MAAQ,mBAAgB;MAAQ,gBAAa;MAAI,GAAE;mBAG5E,mBAIS,UAAA;MAJD,MAAK;MAAS,OAAM;MAAoC,SAAO;MAAmB,OAAM;yCAC9F,mBAEM,OAAA;MAFD,OAAM;MAAoC,MAAK;MAAO,QAAO;MAAe,SAAQ;SACvF,mBAAyF,QAAA;MAAnF,kBAAe;MAAQ,mBAAgB;MAAQ,gBAAa;MAAI,GAAE;;KAOhF,mBAyUM,OAzUN,gBAyUM,CAvUY,MAAA,cAAa,IAAA,UAAA,KAAA,EAC3B,mBAoMI,UAAA,EAAA,KAAA,GAAA,EAAA,WAnMmB,MAAA,mBAAkB,GAAhC,eAAU;0BADnB,mBAoMI,OAAA;OAlMD,KAAK,WAAW;OACjB,OAAM;UAGN,mBA8DM,OAAA;OA7DH,OAAK,eAAA;;QAA0E,cAAA,UAAkB,WAAW,QAAQ,kBAAA,UAAiB,UAAA,2CAAA;QAA8E,cAAA,UAAkB,WAAW,QAAQ,kBAAA,UAAiB,WAAgB,gBAAA,UAAe,WAAA,mDAAA;QAAuF,cAAA,UAAkB,WAAW,QAAQ,kBAAA,UAAiB,WAAgB,gBAAA,UAAe,UAAA,kDAAA;;OAMrd,WAAU;OACT,UAAK,WAAE,oBAAmB,SAAU,WAAW,OAAI;OACnD,cAAS,WAAE,qBAAqB,WAAW,MAAI,SAAW,OAAM;OAChE,WAAS;OACT,aAAQ,WAAE,oBAAoB,WAAW,MAAI,SAAW,OAAM;OAC9D,aAAS,OAAA,OAAA,OAAA,MAAA,WAAE,qBAAqB,OAAM;OACtC,SAAI,WAAE,gBAAgB,WAAW,MAAI,SAAW,OAAM;;qBAEvD,mBAQM,OAAA;QAPH,OAAK,eAAA,CAAA,iCAAyE,gBAAe,SAAU,WAAW,OAAI,GAAA,wCAAA,GAAA,CAAA;QAIvH,MAAK;QAAO,QAAO;QAAe,SAAQ;2CAE1C,mBAAyF,QAAA;QAAnF,kBAAe;QAAQ,mBAAgB;QAAQ,gBAAa;QAAI,GAAE;;OAG1E,mBAQE,SAAA;QAPA,MAAK;QACJ,SAAS,0BAA0B,WAAU;QAC7C,eAAe,8BAA8B,WAAU;QACxD,OAAM;QACL,OAAK,eAAA,EAAA,aAAiB,WAAW,OAAK,CAAA;QACtC,SAAK,OAAA,OAAA,OAAA,KAAA,oBAAN,IAAW,CAAA,OAAA,CAAA;QACV,WAAM,WAAE,wBAAwB,WAAU;;OAG7C,mBAME,UAAA;QALA,MAAK;QACL,OAAM;QACL,OAAK,eAAA,EAAA,iBAAqB,WAAW,OAAK,CAAA;QAC1C,SAAK,eAAA,WAAO,0BAA0B,YAAY,OAAM,EAAA,CAAA,OAAA,CAAA;QACzD,OAAM;;OAGR,mBAA2E,QAA3E,eAA2E,gBAAzB,WAAW,KAAI,EAAA,EAAA;OAEjE,mBAKO,QAAA;QAJL,OAAM;QACL,OAAK,eAAA;SAAA,iBAAqB,WAAW,QAAK;SAAA,OAAgB,WAAW;SAAK,CAAA;0BAExE,WAAW,WAAW,OAAM,EAAA,EAAA;OAGjC,mBASS,UAAA;QARP,MAAK;QACL,OAAM;QACL,SAAK,eAAA,WAAO,iBAAiB,WAAU,EAAA,CAAA,OAAA,CAAA;QACxC,OAAM;2CAEN,mBAEM,OAAA;QAFD,MAAK;QAAO,QAAO;QAAe,SAAQ;WAC7C,mBAAiG,QAAA;QAA3F,kBAAe;QAAQ,mBAAgB;QAAQ,gBAAa;QAAI,GAAE;;8BAM9E,YA4HW,YAAA,EA5HC,MAAK,iBAAe,EAAA;8BA2H1B,CAzHI,gBAAe,SAAU,WAAW,OAAI,IAAA,WAAA,EADhD,mBA0HI,OAAA;;QAxHF,OAAM;QACL,OAAK,eAAA,EAAA,aAAiB,WAAW,QAAK,MAAA,CAAA;6BAEzC,mBAoHM,UAAA,MAAA,WAnHe,WAAW,YAAvB,aAAQ;4BADjB,mBAoHM,OAAA;SAlHH,KAAK,SAAS;SACd,OAAK,eAAA,CAAA,mCAA2E,cAAA,UAAkB,SAAS,OAAI,+CAAA,GAAA,CAAA;SAI/G,aAAQ,WAAE,eAAe,SAAS,MAAM,OAAM;SAC9C,aAAW;SACX,SAAI,WAAE,WAAW,SAAS,MAAM,OAAM;YAGvC,mBA4DM,OAAA;SA3DH,OAAK,eAAA;;UAAgF,cAAA,UAAkB,SAAS,QAAQ,kBAAA,UAAiB,QAAA,2CAAA;UAAgF,cAAA,UAAkB,SAAS,QAAQ,kBAAA,UAAiB,SAAc,gBAAA,UAAe,WAAA,mDAAA;UAA2F,cAAA,UAAkB,SAAS,QAAQ,kBAAA,UAAiB,SAAc,gBAAA,UAAe,UAAA,kDAAA;;SAMvd,WAAU;SACT,UAAK,WAAE,oBAAoB,SAAS,KAAI;SACxC,cAAS,WAAE,qBAAqB,SAAS,MAAI,OAAS,OAAM;SAC5D,WAAS;SACT,aAAQ,WAAE,oBAAoB,SAAS,MAAI,OAAS,OAAM;SAC1D,aAAS,OAAA,OAAA,OAAA,MAAA,WAAE,qBAAqB,OAAM;SACtC,SAAI,WAAE,gBAAgB,SAAS,MAAI,OAAS,OAAM;;uBAEnD,mBAQM,OAAA;UAPH,OAAK,eAAA,CAAA,sEAAsH,gBAAgB,SAAS,KAAI,GAAA,wCAAA,GAAA,CAAA;UAIzJ,MAAK;UAAO,QAAO;UAAe,SAAQ;6CAE1C,mBAAyF,QAAA;UAAnF,kBAAe;UAAQ,mBAAgB;UAAQ,gBAAa;UAAI,GAAE;;SAG1E,mBAQE,SAAA;UAPA,MAAK;UACJ,SAAS,qBAAqB,SAAS,KAAI;UAC3C,eAAe,yBAAyB,SAAS,KAAI;UACtD,OAAM;UACL,OAAK,eAAA,EAAA,aAAiB,SAAS,cAAY,CAAA;UAC3C,SAAK,OAAA,OAAA,OAAA,KAAA,oBAAN,IAAW,CAAA,OAAA,CAAA;UACV,WAAM,WAAE,mBAAmB,SAAS,KAAI;;SAG3C,mBAIE,OAAA;UAHA,OAAM;UACL,OAAK,eAAA,EAAA,iBAAqB,SAAS,cAAY,CAAA;UAChD,eAAY;;SAGd,mBAAuE,QAAvE,eAAuE,gBAAvB,SAAS,KAAI,EAAA,EAAA;SAE7D,mBAKO,QAAA;UAJL,OAAM;UACL,OAAK,eAAA;WAAA,iBAAqB,SAAS;WAAS,OAAS,SAAS;WAAY,CAAA;4BAExE,SAAS,QAAQ,OAAM,EAAA,EAAA;SAG5B,mBASS,UAAA;UARP,MAAK;UACL,OAAM;UACL,SAAK,eAAA,WAAO,YAAY,SAAS,KAAI,EAAA,CAAA,OAAA,CAAA;UACtC,OAAM;6CAEN,mBAEM,OAAA;UAFD,MAAK;UAAO,QAAO;UAAe,SAAQ;aAC7C,mBAAiG,QAAA;UAA3F,kBAAe;UAAQ,mBAAgB;UAAQ,gBAAa;UAAI,GAAE;;+BAM9E,YAwCa,YAAA,EAxCD,MAAK,iBAAe,EAAA;gCAuCxB,CArCE,gBAAgB,SAAS,KAAI,IAAA,WAAA,EADrC,mBAsCM,OAAA;;UApCJ,OAAM;UACL,OAAK,eAAA,EAAA,aAAiB,SAAS,eAAa,CAAA;+BAE7C,mBAgCM,UAAA,MAAA,WA/Ba,SAAS,UAAnB,WAAM;8BADf,mBAgCM,OAAA;WA9BH,KAAK;WACL,OAAK,eAAA,CAAA,gCAAoF,eAAA,UAAmB,SAAM,2CAAA,GAAA,CAAA;WAInH,WAAU;WACT,cAAS,WAAE,gBAAgB,QAAQ,SAAS,MAAM,OAAM;WACxD,WAAS;;uCAEV,mBAEM,OAAA;YAFD,OAAM;YAAoC,MAAK;YAAO,QAAO;YAAe,SAAQ;eACvF,mBAA4F,QAAA;YAAtF,kBAAe;YAAQ,mBAAgB;YAAQ,gBAAa;YAAI,GAAE;;WAE1E,mBAME,SAAA;YALA,MAAK;YACJ,SAAS,QAAA,WAAW,SAAS,OAAM;YACpC,OAAM;YACL,OAAK,eAAA,EAAA,aAAiB,SAAS,cAAY,CAAA;YAC3C,WAAM,WAAE,aAAa,OAAM;;WAE9B,mBAAmE,QAAnE,eAAmE,gBAAhB,OAAM,EAAA,EAAA;WACzD,mBASS,UAAA;YARP,MAAK;YACL,OAAM;YACL,UAAK,WAAE,sBAAsB,QAAQ,SAAS,KAAI;YACnD,OAAM;+CAEN,mBAEM,OAAA;YAFD,MAAK;YAAO,QAAO;YAAe,SAAQ;eAC7C,mBAAqF,QAAA;YAA/E,kBAAe;YAAQ,mBAAgB;YAAQ,gBAAa;YAAI,GAAE;;;;;;;;;mCAcxF,mBAsHM,UAAA,EAAA,KAAA,GAAA,EAAA,WArHY,eAAA,QAAT,UAAK;0BADd,mBAsHM,OAAA;OApHH,KAAK,MAAM;OACX,OAAK,eAAA,CAAA,mCAAmE,cAAA,UAAkB,MAAM,OAAI,+CAAA,GAAA,CAAA;OAIpG,aAAQ,WAAE,eAAe,MAAM,MAAM,OAAM;OAC3C,aAAW;OACX,SAAI,WAAE,WAAW,MAAM,MAAM,OAAM;UAGpC,mBA8DM,OAAA;OA7DH,OAAK,eAAA;;QAA0E,cAAA,UAAkB,MAAM,QAAQ,kBAAA,UAAiB,SAAA,2CAAA;QAA6E,cAAA,UAAkB,MAAM,QAAQ,kBAAA,UAAiB,UAAe,gBAAA,UAAe,WAAA,mDAAA;QAAuF,cAAA,UAAkB,MAAM,QAAQ,kBAAA,UAAiB,UAAe,gBAAA,UAAe,UAAA,kDAAA;;OAMnc,WAAU;OACT,UAAK,WAAE,oBAAoB,MAAM,KAAI;OACrC,cAAS,WAAE,qBAAqB,MAAM,MAAI,QAAU,OAAM;OAC1D,WAAS;OACT,aAAQ,WAAE,oBAAoB,MAAM,MAAI,QAAU,OAAM;OACxD,aAAS,OAAA,OAAA,OAAA,MAAA,WAAE,qBAAqB,OAAM;OACtC,SAAI,WAAE,gBAAgB,MAAM,MAAI,QAAU,OAAM;;qBAEjD,mBAQM,OAAA;QAPH,OAAK,eAAA,CAAA,iCAAyE,gBAAgB,MAAM,KAAI,GAAA,wCAAA,GAAA,CAAA;QAIzG,MAAK;QAAO,QAAO;QAAe,SAAQ;2CAE1C,mBAAyF,QAAA;QAAnF,kBAAe;QAAQ,mBAAgB;QAAQ,gBAAa;QAAI,GAAE;;OAG1E,mBAQE,SAAA;QAPA,MAAK;QACJ,SAAS,qBAAqB,MAAM,KAAI;QACxC,eAAe,yBAAyB,MAAM,KAAI;QACnD,OAAM;QACL,OAAK,eAAA,EAAA,aAAiB,MAAM,OAAK,CAAA;QACjC,SAAK,OAAA,OAAA,OAAA,KAAA,oBAAN,IAAW,CAAA,OAAA,CAAA;QACV,WAAM,WAAE,mBAAmB,MAAM,KAAI;;OAGxC,mBAME,UAAA;QALA,MAAK;QACL,OAAM;QACL,OAAK,eAAA,EAAA,iBAAqB,MAAM,OAAK,CAAA;QACrC,SAAK,eAAA,WAAO,gBAAgB,MAAM,MAAM,OAAM,EAAA,CAAA,OAAA,CAAA;QAC/C,OAAM;;OAGR,mBAAsE,QAAtE,eAAsE,gBAApB,MAAM,KAAI,EAAA,EAAA;OAE5D,mBAKO,QAAA;QAJL,OAAM;QACL,OAAK,eAAA;SAAA,iBAAqB,MAAM,QAAK;SAAA,OAAgB,MAAM;SAAK,CAAA;0BAE9D,MAAM,QAAQ,OAAM,EAAA,EAAA;OAGzB,mBASS,UAAA;QARP,MAAK;QACL,OAAM;QACL,SAAK,eAAA,WAAO,YAAY,MAAM,KAAI,EAAA,CAAA,OAAA,CAAA;QACnC,OAAM;2CAEN,mBAEM,OAAA;QAFD,MAAK;QAAO,QAAO;QAAe,SAAQ;WAC7C,mBAAiG,QAAA;QAA3F,kBAAe;QAAQ,mBAAgB;QAAQ,gBAAa;QAAI,GAAE;;6BAM9E,YAwCa,YAAA,EAxCD,MAAK,iBAAe,EAAA;8BAuCxB,CArCE,gBAAgB,MAAM,KAAI,IAAA,WAAA,EADlC,mBAsCM,OAAA;;QApCJ,OAAM;QACL,OAAK,eAAA,EAAA,aAAiB,MAAM,QAAK,MAAA,CAAA;6BAElC,mBAgCM,UAAA,MAAA,WA/Ba,MAAM,UAAhB,WAAM;4BADf,mBAgCM,OAAA;SA9BH,KAAK;SACL,OAAK,eAAA,CAAA,gCAA4E,eAAA,UAAmB,SAAM,2CAAA,GAAA,CAAA;SAI3G,WAAU;SACT,cAAS,WAAE,gBAAgB,QAAQ,MAAM,MAAM,OAAM;SACrD,WAAS;;qCAEV,mBAEM,OAAA;UAFD,OAAM;UAAoC,MAAK;UAAO,QAAO;UAAe,SAAQ;aACvF,mBAA4F,QAAA;UAAtF,kBAAe;UAAQ,mBAAgB;UAAQ,gBAAa;UAAI,GAAE;;SAE1E,mBAME,SAAA;UALA,MAAK;UACJ,SAAS,QAAA,WAAW,SAAS,OAAM;UACpC,OAAM;UACL,OAAK,eAAA,EAAA,aAAiB,MAAM,OAAK,CAAA;UACjC,WAAM,WAAE,aAAa,OAAM;;SAE9B,mBAAmE,QAAnE,eAAmE,gBAAhB,OAAM,EAAA,EAAA;SACzD,mBASS,UAAA;UARP,MAAK;UACL,OAAM;UACL,UAAK,WAAE,sBAAsB,QAAQ,MAAM,KAAI;UAChD,OAAM;6CAEN,mBAEM,OAAA;UAFD,MAAK;UAAO,QAAO;UAAe,SAAQ;aAC7C,mBAAqF,QAAA;UAA/E,kBAAe;UAAQ,mBAAgB;UAAQ,gBAAa;UAAI,GAAE;;;;;;gBAU3E,eAAA,MAAe,WAAM,KAAA,WAAA,EAAhC,mBAEM,OAFN,eAA4E,4CAE5E,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;KAIS,MAAA,iBAAgB,CAAC,SAAM,KAAA,WAAA,EAAlC,mBA+CM,OA/CN,eA+CM,CA9CJ,mBAeM,OAAA;MAdJ,OAAM;MACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,oBAAmB,gBAAA;;oBAE3B,mBAQM,OAAA;OAPH,OAAK,eAAA,CAAA,iCAAiE,gBAAe,gBAAA,GAAA,wCAAA,GAAA,CAAA;OAItF,MAAK;OAAO,QAAO;OAAe,SAAQ;0CAE1C,mBAAyF,QAAA;OAAnF,kBAAe;OAAQ,mBAAgB;OAAQ,gBAAa;OAAI,GAAE;;kCAE1E,mBAAoE,QAAA,EAA9D,OAAM,yCAAuC,EAAC,aAAS,GAAA;MAC7D,mBAAwF,QAAxF,eAAwF,gBAAjC,MAAA,iBAAgB,CAAC,OAAM,EAAA,EAAA;SAGhF,YA4Ba,YAAA,EA5BD,MAAK,iBAAe,EAAA;6BA2BxB,CAzBE,gBAAe,gBAAA,IAAA,WAAA,EADvB,mBA0BM,OA1BN,eA0BM,EAAA,UAAA,KAAA,EAtBJ,mBAqBM,UAAA,MAAA,WApBa,MAAA,iBAAgB,GAA1B,WAAM;2BADf,mBAqBM,OAAA;QAnBH,KAAK;QACL,OAAK,eAAA,CAAA,gCAAoE,eAAA,UAAmB,SAAM,2CAAA,GAAA,CAAA;QAInG,WAAU;QACT,cAAS,WAAE,gBAAgB,QAAM,MAAQ,OAAM;QAC/C,WAAS;;oCAEV,mBAEM,OAAA;SAFD,OAAM;SAAoC,MAAK;SAAO,QAAO;SAAe,SAAQ;YACvF,mBAA4F,QAAA;SAAtF,kBAAe;SAAQ,mBAAgB;SAAQ,gBAAa;SAAI,GAAE;;QAE1E,mBAKE,SAAA;SAJA,MAAK;SACJ,SAAS,QAAA,WAAW,SAAS,OAAM;SACpC,OAAM;SACL,WAAM,WAAE,aAAa,OAAM;;QAE9B,mBAAmE,QAAnE,eAAmE,gBAAhB,OAAM,EAAA,EAAA;;;;;KAOjE,mBAgBM,OAhBN,aAgBM,CAfJ,YAKE,mBAAA;kBAJS,aAAA;gFAAY,QAAA;MACrB,aAAY;MACZ,OAAM;MACL,SAAK,SAAQ,aAAW,CAAA,QAAA,CAAA;kCAE3B,YAQa,oBAAA;MAPX,SAAQ;MACR,MAAK;MACJ,UAAQ,CAAG,aAAA,MAAa,MAAI;MAC7B,OAAM;MACL,SAAO;;6BAGV,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAFC,SAED,GAAA,CAAA,EAAA,CAAA;;;KAIF,mBAME,SAAA;eALI;MAAJ,KAAI;MACJ,MAAK;MACL,OAAM;MACL,OAAO,gBAAA;MACP,UAAQ;;;KAKD,gBAAA,SAAA,WAAA,EAAZ,mBAkCM,OAlCN,aAkCM,CAhCJ,mBAUM,OAVN,aAUM,CAAA,OAAA,QAAA,OAAA,MATJ,mBAEM,OAAA;KAFD,OAAM;KAAoC,MAAK;KAAO,QAAO;KAAe,SAAQ;QACvF,mBAAwH,QAAA;KAAlH,kBAAe;KAAQ,mBAAgB;KAAQ,gBAAa;KAAI,GAAE;8BAE1E,mBAKE,SAAA;8EAJoB,QAAA;KACpB,MAAK;KACL,aAAY;KACZ,OAAM;iCAHG,YAAA,MAAW,CAAA,CAAA,CAAA,CAAA,EAQxB,mBAkBM,OAlBN,aAkBM,EAAA,UAAA,KAAA,EAjBJ,mBAYM,UAAA,MAAA,WAXa,MAAA,gBAAe,GAAzB,WAAM;yBADf,mBAYM,OAAA;MAVH,KAAK;MACN,OAAM;SAEN,mBAKE,SAAA;MAJA,MAAK;MACJ,SAAS,QAAA,WAAW,SAAS,OAAM;MACpC,OAAM;MACL,WAAM,WAAE,aAAa,OAAM;gCAE9B,mBAAiE,QAAjE,aAAiE,gBAAhB,OAAM,EAAA,EAAA,CAAA,CAAA;eAG9C,MAAA,gBAAe,CAAC,WAAM,KAAU,YAAA,MAAY,MAAI,IAAA,WAAA,EAA3D,mBAEM,OAFN,aAAmG,yBAC/E,gBAAG,YAAA,MAAW,GAAG,OACrC,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAKJ,YAME,wBAAA;iBALS,oBAAA;wFAAmB,QAAA;KAC3B,SAAS,QAAA;KACT,iBAAe,QAAA;KACf,eAAa,QAAA;KACb,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AE33Bd,IAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAzBpB,MAAM,QAAQ;EAed,MAAM,OAAO;EAWb,MAAM,cAAc,IAAkB,MAAM,KAAI;EAChD,MAAM,cAAc,IAAU,eAAe,MAAM,WAAW,CAAA;EAC9D,MAAM,WAAW,KAAoC;EACrD,MAAM,MAAM,oBAAI,IAAI,MAAM,CAAA;EAC1B,MAAM,UAAU,KAAoB;AAEpC,cAAY,MAAM,OAAO,MAAM;AAAE,eAAY,QAAQ;IAAG;AACxD,cAAY,MAAM,aAAa,MAAM;AAAE,OAAI,EAAG,aAAY,QAAQ,eAAe,EAAE;IAAE;EAErF,MAAM,gBAAgB,eAAe,YAAW;EAMhD,MAAM,EAAE,YAAY,OAAO,aAAa,WAAW,gBAAgB,gBAAgB;GACjF,cANsB,eAAe,MAAM,aAAY;GAOvD,cANkB,eAAe,MAAM,aAAY;GAOnD,YANgB,eAAe,MAAM,WAAU;GAO/C,UANkB,eAAe,MAAM,SAAQ;GAO/C,YAAY;GACZ,iBAAiB,OAAO,KAAK;AAC3B,SAAK,gBAAgB;KAAE;KAAO;KAAK,CAAA;;GAErC,eAAe,OAAO,UAAU,QAAQ;AACtC,SAAK,gBAAgB;KAAE;KAAO;KAAU;KAAQ,CAAA;;GAElD,iBAAiB,OAAO,UAAU,QAAQ;AACxC,SAAK,gBAAgB;KAAE;KAAO;KAAU;KAAQ,CAAA;;GAEnD,CAAA;EAED,MAAM,aAAa,eAAe;AAChC,WAAS,MAAM,aAAa,MAAM,gBAAgB,KAAM,MAAM;IAC/D;EAED,MAAM,aAAa,eAAe;GAChC,MAAM,SAAmB,EAAC;AAC1B,QAAK,IAAI,IAAI,GAAG,KAAK,WAAW,OAAO,KAAK;IAC1C,MAAM,eAAe,MAAM,eAAe,KAAK,IAAI,MAAM;IACzD,MAAM,OAAO,KAAK,MAAM,eAAe,GAAE;IACzC,MAAM,SAAS,eAAe;AAC9B,WAAO,KAAK,WAAW,MAAM,QAAQ,MAAM,CAAA;;AAE7C,UAAO;IACR;EAED,MAAM,WAAW,eAAe;GAC9B,MAAM,OAAe,EAAC;GACtB,MAAM,QAAQ,aAAa,YAAY,MAAK;AAC5C,QAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;IAC1B,MAAM,IAAI,IAAI,KAAK,MAAK;AACxB,MAAE,QAAQ,EAAE,SAAS,GAAG,EAAC;AACzB,SAAK,KAAK,EAAC;;AAEb,UAAO;IACR;EAED,MAAM,cAAc,eAAe;AACjC,OAAI,YAAY,UAAU,MAAO,QAAO,CAAC,YAAY,MAAK;AAC1D,OAAI,YAAY,UAAU,OAAQ,QAAO,SAAS;AAClD,UAAO,EAAC;IACT;EAED,MAAM,cAAc,eAAe;AACjC,OAAI,YAAY,UAAU,MACxB,QAAO,YAAY,MAAM,mBAAmB,MAAM,QAAQ;IACxD,SAAS;IACT,MAAM;IACN,OAAO;IACP,KAAK;IACN,CAAA;AAEH,OAAI,YAAY,UAAU,QAAQ;IAChC,MAAM,OAAO,SAAS;IACtB,MAAM,QAAQ,KAAK;IACnB,MAAM,OAAO,KAAK;AAElB,QADkB,MAAM,UAAU,KAAK,KAAK,UAAS,CAEnD,QAAO,GAAG,MAAM,mBAAmB,MAAM,QAAQ;KAAE,OAAO;KAAS,KAAK;KAAW,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,IAAI,KAAK,aAAa;AAEjI,WAAO,GAAG,MAAM,mBAAmB,MAAM,QAAQ;KAAE,OAAO;KAAS,KAAK;KAAW,CAAC,CAAC,KAAK,KAAK,mBAAmB,MAAM,QAAQ;KAAE,OAAO;KAAS,KAAK;KAAW,CAAC,CAAC,IAAI,KAAK,aAAa;;AAE5L,UAAO,YAAY,MAAM,mBAAmB,MAAM,QAAQ;IAAE,MAAM;IAAW,OAAO;IAAQ,CAAA;IAC7F;EAGD,MAAM,YAAY,eAAe;GAC/B,MAAM,OAAO,YAAY,MAAM,aAAY;GAC3C,MAAM,QAAQ,YAAY,MAAM,UAAS;GACzC,MAAM,WAAW,IAAI,KAAK,MAAM,OAAO,EAAC;GACxC,MAAM,UAAU,IAAI,KAAK,MAAM,QAAQ,GAAG,EAAC;GAE3C,MAAM,OAAkD,EAAC;GAGzD,IAAI,eAAe,SAAS,QAAQ,GAAG,MAAM;AAC7C,OAAI,eAAe,EAAG,iBAAgB;AAEtC,QAAK,IAAI,IAAI,eAAe,GAAG,KAAK,GAAG,KAAK;IAC1C,MAAM,OAAO,IAAI,KAAK,MAAM,OAAO,CAAC,EAAC;AACrC,SAAK,KAAK;KAAE;KAAM,gBAAgB;KAAO,CAAA;;AAG3C,QAAK,IAAI,IAAI,GAAG,KAAK,QAAQ,SAAS,EAAE,IACtC,MAAK,KAAK;IAAE,MAAM,IAAI,KAAK,MAAM,OAAO,EAAE;IAAE,gBAAgB;IAAM,CAAA;GAGpE,MAAM,YAAY,KAAK,KAAK;AAC5B,QAAK,IAAI,IAAI,GAAG,KAAK,WAAW,IAC9B,MAAK,KAAK;IAAE,MAAM,IAAI,KAAK,MAAM,QAAQ,GAAG,EAAE;IAAE,gBAAgB;IAAO,CAAA;AAGzE,UAAO;IACR;EAED,MAAM,qBAAqB,eAAe;GACxC,MAAM,SAAmB,EAAC;GAC1B,MAAM,OAAO,IAAI,KAAK,MAAM,GAAG,MAAM,iBAAiB,IAAI,IAAI,EAAE;AAChE,QAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;IAC1B,MAAM,IAAI,IAAI,KAAK,KAAI;AACvB,MAAE,QAAQ,EAAE,SAAS,GAAG,EAAC;AACzB,WAAO,KAAK,EAAE,mBAAmB,MAAM,QAAQ,EAAE,SAAS,SAAS,CAAC,CAAA;;AAEtE,UAAO;IACR;EAED,SAAS,eAAe,OAA6B;AACnD,OAAI,CAAC,MAAO,wBAAO,IAAI,MAAK;AAC5B,OAAI,iBAAiB,KAAM,QAAO;GAClC,MAAM,IAAI,IAAI,KAAK,MAAK;AACxB,UAAO,MAAM,EAAE,SAAS,CAAC,mBAAG,IAAI,MAAM,GAAG;;EAG3C,SAAS,aAAa,MAAkB;GACtC,MAAM,IAAI,IAAI,KAAK,KAAI;GAEvB,MAAM,QADM,EAAE,QAAO,GACD,MAAM,eAAe,KAAK;AAC9C,KAAE,QAAQ,EAAE,SAAS,GAAG,KAAI;AAC5B,UAAO;;EAGT,SAAS,QAAQ,MAAqB;AACpC,UAAO,KAAK,cAAc,KAAK,IAAI,MAAM,cAAa;;EAGxD,SAAS,UAAU,GAAS,GAAkB;AAC5C,UAAO,EAAE,cAAc,KAAK,EAAE,cAAa;;EAG7C,SAAS,SAAS,WAAsC;GACtD,MAAM,IAAI,IAAI,KAAK,YAAY,MAAK;AACpC,OAAI,cAAc,QAChB,aAAY,wBAAQ,IAAI,MAAK;QACxB;IACL,MAAM,QAAQ,cAAc,SAAS,KAAK;AAC1C,QAAI,YAAY,UAAU,MAAO,GAAE,QAAQ,EAAE,SAAS,GAAG,MAAK;aACrD,YAAY,UAAU,OAAQ,GAAE,QAAQ,EAAE,SAAS,GAAG,QAAQ,EAAC;QACnE,GAAE,SAAS,EAAE,UAAU,GAAG,MAAK;AACpC,gBAAY,QAAQ;;AAEtB,QAAK,qBAAqB,YAAY,MAAK;AAC3C,QAAK,YAAY;IAAE;IAAW,MAAM,YAAY;IAAO,CAAA;;EAGzD,SAAS,QAAQ,MAAoB;AACnC,eAAY,QAAQ;AACpB,QAAK,eAAe,KAAI;;EAG1B,SAAS,cAAc,OAAsB,SAAe;GAC1D,MAAM,aAAa,IAAI,KAAK,MAAM,MAAK;GACvC,MAAM,WAAW,IAAI,KAAK,MAAM,IAAG;AACnC,OAAI,CAAC,UAAU,YAAY,QAAQ,IAAI,CAAC,UAAU,UAAU,QAAQ,CAAE,QAAO;GAE7E,MAAM,cAAc,MAAM,eAAe;GACzC,MAAM,WAAW,KAAK,IAAI,WAAW,UAAU,GAAG,KAAK,WAAW,YAAY,EAAE,YAAY,GAAG;GAC/F,MAAM,SAAS,KAAK,IAAI,SAAS,UAAU,GAAG,KAAK,SAAS,YAAY,EAAE,MAAM,aAAa,GAAG,GAAG;GAEnG,MAAM,eAAe,cAAc,MAAM;AACzC,UAAO;IACL,KAAK,GAAG,WAAW,aAAa;IAChC,QAAQ,GAAG,KAAK,KAAK,SAAS,YAAY,cAAc,cAAc,EAAE,CAAC;IAC3E;;EAGF,SAAS,gBAAgB,KAA4B;AACnD,UAAO,MAAM,OAAO,QAAQ,MAAM;AAEhC,WAAO,UADO,IAAI,KAAK,EAAE,MAAK,EACN,IAAG;KAC5B;;EAGH,SAAS,iBAAiB,MAA6B;AACrD,UAAO,MAAM,OAAO,QAAQ,MAAM;AAEhC,WAAO,UADO,IAAI,KAAK,EAAE,MAAK,EACN,KAAI;KAC7B;;EAGH,SAAS,iBAAiB,KAAkC;AAC1D,UAAO,MAAM,aAAa,QAAQ,MAAM;AAEtC,WAAO,UADO,IAAI,KAAK,EAAE,MAAK,EACN,IAAG;KAC5B;;EAGH,SAAS,gBAAgB,MAA2B;GAClD,MAAM,QAAQ,IAAI,KAAK,KAAK,MAAK;GACjC,MAAM,MAAM,IAAI,KAAK,KAAK,IAAG;GAC7B,MAAM,cAAc,MAAM,eAAe;GACzC,MAAM,WAAW,KAAK,IAAI,MAAM,UAAU,GAAG,KAAK,MAAM,YAAY,EAAE,YAAY,GAAG;GACrF,MAAM,SAAS,KAAK,IAAI,IAAI,UAAU,GAAG,KAAK,IAAI,YAAY,EAAE,MAAM,aAAa,GAAG,GAAG;GAEzF,MAAM,eAAe,cAAc,MAAM;AACzC,UAAO;IACL,KAAK,GAAG,WAAW,aAAa;IAChC,QAAQ,IAAI,SAAS,YAAY,aAAa;IAChD;;EAGF,SAAS,uBAAsD;AAC7D,OAAI,CAAC,MAAM,iBAAkB,QAAO;GACpC,MAAM,SAAS,IAAI,MAAM,UAAU,GAAG,KAAK,IAAI,MAAM,YAAW;GAChE,MAAM,cAAc,MAAM,eAAe;GACzC,MAAM,YAAY,MAAM,aAAa;AACrC,OAAI,SAAS,eAAe,SAAS,UAAW,QAAO;GAEvD,MAAM,eAAe,cAAc,MAAM;AACzC,UAAO,EAAE,KAAK,IAAI,SAAS,eAAe,aAAa,KAAI;;EAG7D,SAAS,eAAe,QAAsC;AAC5D,OAAI,CAAC,OAAQ,QAAO;AACpB,UAAO,yBAAyB;;EAGlC,SAAS,oBAAoB,QAAsC;AACjE,OAAI,CAAC,OAAQ,QAAO;AACpB,UAAO,+BAA+B;;EAGxC,SAAS,aAAa,OAAsB,GAAe;AACzD,KAAE,iBAAgB;AAClB,QAAK,eAAe,MAAK;;EAG3B,SAAS,YAAY,KAAW,WAAmB;AACjD,OAAI,MAAM,YAAY,WAAW,MAAO;GACxC,MAAM,eAAe,MAAM,eAAe,KAAK,YAAY,MAAM;GACjE,MAAM,OAAO,KAAK,MAAM,eAAe,GAAE;GACzC,MAAM,SAAS,eAAe;GAC9B,MAAM,OAAO,IAAI,KAAK,IAAG;AACzB,QAAK,SAAS,MAAM,QAAQ,GAAG,EAAC;AAChC,QAAK,cAAc;IAAE;IAAM;IAAM;IAAQ,CAAA;;EAG3C,SAAS,kBAAkB,KAAW,WAAmB,GAAiB;AACxE,OAAI,MAAM,SAAU;GACpB,MAAM,OAAO,IAAI,KAAK,IAAG;GACzB,MAAM,eAAe,MAAM,eAAe,KAAK,YAAY,MAAM;AACjE,QAAK,SAAS,KAAK,MAAM,eAAe,GAAG,EAAE,eAAe,IAAI,GAAG,EAAC;GACpE,MAAM,WAAW,YAAY,MAAM,WAAW,MAAM,UAAU,GAAG,IAAI,CAAA;AACrE,eAAY,MAAM,EAAE,SAAS,SAAQ;;EAGvC,SAAS,mBAAmB,OAAsB,UAAkB,GAAiB;AACnF,KAAE,iBAAgB;AAClB,aAAU,OAAO,EAAE,SAAS,SAAQ;;EAGtC,SAAS,oBAAoB,OAAsB,MAAwB,UAAkB,GAAiB;AAC5G,KAAE,iBAAgB;AAClB,eAAY,OAAO,MAAM,EAAE,SAAS,SAAQ;;EAG9C,SAAS,gBAAgB,MAAY;AACnC,eAAY,QAAQ;AACpB,QAAK,qBAAqB,KAAI;AAC9B,WAAQ,MAAK;;EAGf,SAAS,gBAAgB,OAA8B;GACrD,MAAM,QAAQ,IAAI,KAAK,MAAM,MAAK;GAClC,MAAM,MAAM,IAAI,KAAK,MAAM,IAAG;AAC9B,UAAO,GAAG,WAAW,MAAM,UAAU,EAAE,MAAM,YAAY,CAAC,CAAC,KAAK,WAAW,IAAI,UAAU,EAAE,IAAI,YAAY,CAAC;;AAG9G,kBAAgB;AACd,YAAS,QAAQ,kBAAkB;AAAE,QAAI,wBAAQ,IAAI,MAAM;MAAI,IAAK;AAGpE,OAAI,QAAQ,OAAO;IAIjB,MAAM,cAHa,IAAI,MAAM,UAAU,IAAI,MAAM,gBAAgB,IAAI,MAAM,UAAU,IAAI,MAAM,aAC3F,IAAI,MAAM,UAAS,GACnB,KAC4B,MAAM,gBAAgB;IACtD,MAAM,eAAe,cAAc,MAAM;AACzC,YAAQ,MAAM,YAAY,KAAK,IAAI,GAAG,YAAY,eAAe,IAAG;;IAEvE;AAED,oBAAkB;AAChB,OAAI,SAAS,MAAO,eAAc,SAAS,MAAK;IACjD;;uBAIC,mBAwMM,OAAA;IAxMD,OAAM;IAAiB,OAAK,eAAA,EAAA,iBAAA,GAAyB,YAAW,KAAA,CAAA;;IAExD,QAAA,kBAAkB,QAAA,kBAAA,WAAA,EAA7B,mBA4CM,OA5CN,eA4CM;KA3CO,QAAA,kBAAA,WAAA,EAAX,mBA4BM,OA5BN,eA4BM;MA3BJ,mBASS,UAAA;OARP,MAAK;OACL,OAAM;OACN,cAAW;OACV,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,SAAQ,OAAA;wCAEhB,mBAEM,OAAA;OAFD,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,gBAAa;OAAI,kBAAe;OAAQ,mBAAgB;UACjH,mBAA2B,QAAA,EAArB,GAAE,kBAAgB,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA;MAG5B,mBASS,UAAA;OARP,MAAK;OACL,OAAM;OACN,cAAW;OACV,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,SAAQ,OAAA;wCAEhB,mBAEM,OAAA;OAFD,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,gBAAa;OAAI,kBAAe;OAAQ,mBAAgB;UACjH,mBAA0B,QAAA,EAApB,GAAE,iBAAe,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA;MAG3B,mBAMS,UAAA;OALP,MAAK;OACL,OAAM;OACL,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,SAAQ,QAAA;SACjB,UAED;;KAGF,mBAA2D,QAA3D,eAA2D,gBAArB,YAAA,MAAW,EAAA,EAAA;KAEtC,QAAA,kBAAA,WAAA,EAAX,mBAUM,OAVN,eAUM,EAAA,WAAA,EATJ,mBAQS,UAAA,MAAA,WAPK;MAAA;MAAA;MAAA;MAAA,GAAL,MAAC;aADV,mBAQS,UAAA;OANN,KAAK;OACN,MAAK;OACJ,OAAK,eAAA,CAAA,2BAA8B,YAAA,UAAgB,IAAC,oCAAA,GAAA,CAAA;OACpD,UAAK,WAAE,QAAQ,EAAC;yBAEd,EAAE,OAAM,EAAA,CAAI,aAAW,GAAK,EAAE,MAAK,EAAA,CAAA,EAAA,IAAA,cAAA;;;IAM5B,YAAA,UAAW,SAAc,YAAA,UAAW,UAAA,WAAA,EAApD,mBA0GW,UAAA,EAAA,KAAA,GAAA,EAAA,CAxGE,YAAA,UAAW,UAAA,WAAA,EAAtB,mBAgBM,OAhBN,eAgBM,CAAA,OAAA,OAAA,OAAA,KAfJ,mBAAgD,OAAA,EAA3C,OAAM,oCAAkC,EAAA,MAAA,GAAA,IAAA,UAAA,KAAA,EAC7C,mBAaM,UAAA,MAAA,WAZe,YAAA,QAAX,KAAK,MAAC;yBADhB,mBAaM,OAAA;MAXH,KAAK;MACL,OAAK,eAAA,CAAA,6BAAgC,QAAQ,IAAG,GAAA,qCAAA,GAAA,CAAA;SAEjD,WAOO,KAAA,QAAA,cAAA;MAPkB,MAAM;MAAM,SAAS,QAAQ,IAAG;cAOlD,CANL,mBAEO,QAFP,eAEO,gBADF,IAAI,mBAAmB,QAAA,QAAM,EAAA,SAAA,SAAA,CAAA,CAAA,EAAA,EAAA,EAElC,mBAEO,QAFP,eAEO,gBADF,IAAI,SAAO,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,EAAA,EAAA;iDAOtB,mBAoFM,OAAA;cApFG;KAAJ,KAAI;KAAU,OAAM;QAEvB,mBAUM,OAVN,eAUM,EAAA,UAAA,KAAA,EATJ,mBAQM,UAAA,MAAA,WAPiB,WAAA,QAAb,OAAO,MAAC;yBADlB,mBAQM,OAAA;MANH,KAAK;MACN,OAAM;SAEN,WAEO,KAAA,QAAA,cAAA;MAFyB;MAAQ,OAAO;cAExC,CAAA,gBAAA,gBADF,IAAI,WAAA,MAAW,SAAM,IAAO,QAAK,GAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA;iBAM1C,mBAoEM,OApEN,gBAoEM,EAAA,UAAA,KAAA,EAnEJ,mBAkEM,UAAA,MAAA,WAjEoB,YAAA,QAAhB,KAAK,WAAM;yBADrB,mBAkEM,OAAA;MAhEH,KAAK;MACL,OAAK,eAAA,CAAA,6BAAgC,QAAQ,IAAG,GAAA,qCAAA,GAAA,CAAA;;wBAGjD,mBAME,UAAA,MAAA,WALkB,WAAA,QAAX,YAAO;2BADhB,mBAME,OAAA;QAJC,KAAK;QACL,OAAK,eAAA,CAAA,uBAAA,CAA2B,QAAA,WAAQ,qCAAA,GAAA,CAAA;QACxC,UAAK,WAAE,YAAY,KAAK,UAAO,EAAA;QAC/B,gBAAW,WAAE,kBAAkB,KAAK,UAAO,GAAM,OAAM;;;wBAI1D,mBAsBM,UAAA,MAAA,WArBY,gBAAgB,IAAG,GAA5B,UAAK;2BADd,mBAsBM,OAAA;QApBH,KAAK,MAAM;QACX,OAAK,eAAA,CAAA,wBAA2B,eAAe,MAAM,OAAM,CAAA,CAAA;QAC3D,OAAK,eAAE,cAAc,OAAO,IAAG,IAAK,KAAA,EAAS;QAC7C,UAAK,WAAE,aAAa,OAAO,OAAM;QACjC,gBAAW,WAAE,mBAAmB,OAAO,QAAQ,OAAM;WAEtD,WAaO,KAAA,QAAA,SAAA,EAboB,OAAK,QAazB;SAXI,QAAA,YAAY,MAAM,cAAS,SAAA,WAAA,EADpC,mBAIE,OAAA;;SAFA,OAAM;SACL,gBAAW,WAAE,oBAAoB,OAAK,OAAS,QAAQ,OAAM;;QAEhE,mBAA+D,OAA/D,eAA+D,gBAApB,MAAM,MAAK,EAAA,EAAA;QACtD,mBAAyE,OAAzE,eAAyE,gBAA/B,gBAAgB,MAAK,CAAA,EAAA,EAAA;SAEtD,QAAA,YAAY,MAAM,cAAS,SAAA,WAAA,EADpC,mBAIE,OAAA;;SAFA,OAAM;SACL,gBAAW,WAAE,oBAAoB,OAAK,UAAY,QAAQ,OAAM;;;;wBAMvE,mBASM,UAAA,MAAA,WARsB,iBAAiB,IAAG,GAAtC,SAAS,SAAI;2BADvB,mBASM,OAAA;QAPH,KAAG,KAAO;QACX,OAAM;QACL,OAAK,eAAE,gBAAgB,QAAO,CAAA;WAEnB,QAAQ,SAAA,WAAA,EAApB,mBAEO,QAFP,eAEO,gBADF,QAAQ,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,EAAA;;MAMZ,QAAA,oBAAoB,QAAQ,IAAG,IAAK,sBAAoB,IAAA,WAAA,EADhE,mBAMM,OAAA;;OAJJ,OAAM;OACL,OAAK,eAAE,sBAAoB,CAAA;wCAE5B,mBAAsC,OAAA,EAAjC,OAAM,0BAAwB,EAAA,MAAA,GAAA,CAAA,EAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;MAK7B,MAAA,WAAU,IAAI,MAAA,MAAK,IAAI,MAAA,MAAK,CAAC,aAAa,UAAA,WAAA,EADlD,mBAIE,OAAA;;OAFA,OAAM;OACL,OAAK,eAAE,MAAA,MAAK,CAAC,MAAK;;;;IAQb,YAAA,UAAW,WAAA,WAAA,EACzB,mBAuCM,OAvCN,eAuCM,EAAA,UAAA,KAAA,EArCJ,mBAMM,UAAA,MAAA,WALY,mBAAA,QAAT,UAAK;yBADd,mBAMM,OAAA;MAJH,KAAK;MACN,OAAM;wBAEH,MAAK,EAAA,EAAA;iCAIV,mBA2BM,UAAA,MAAA,WA1Be,UAAA,QAAX,KAAK,MAAC;yBADhB,mBA2BM,OAAA;MAzBH,KAAK;MACL,OAAK,eAAA;;OAAyD,QAAQ,IAAI,KAAI,GAAA,qCAAA;QAA0D,IAAI,iBAAc,uCAAA;;MAK1J,UAAK,WAAE,gBAAgB,IAAI,KAAI;SAEhC,WAgBO,KAAA,QAAA,aAAA;MAhBiB,MAAM,IAAI;MAAO,SAAS,QAAQ,IAAI,KAAI;MAAI,QAAQ,iBAAiB,IAAI,KAAI;cAgBhG;MAfL,mBAAqE,OAArE,eAAqE,gBAA3B,IAAI,KAAK,SAAO,CAAA,EAAA,EAAA;wBAC1D,mBAOM,UAAA,MAAA,WANY,iBAAiB,IAAI,KAAI,CAAE,MAAK,GAAA,EAAA,GAAzC,UAAK;2BADd,mBAOM,OAAA;QALH,KAAK,MAAM;QACX,OAAK,eAAA,CAAA,8BAAiC,oBAAoB,MAAM,OAAM,CAAA,CAAA;QACtE,SAAK,eAAA,WAAO,aAAa,OAAO,OAAM,EAAA,CAAA,OAAA,CAAA;0BAEpC,MAAM,MAAK,EAAA,IAAA,cAAA;;MAGR,iBAAiB,IAAI,KAAI,CAAE,SAAM,KAAA,WAAA,EADzC,mBAKM,OALN,eAGC,OACE,gBAAG,iBAAiB,IAAI,KAAI,CAAE,SAAM,EAAA,GAAO,UAC9C,EAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1fZ,MAAM,QAAQ;EAad,MAAM,oBAA+C;GACnD,WAAA;GACA,gBAAA;GACA,oBAAA;GACA,gBAAA;GACA,aAAA;GACA,gBAAA;GACA,kBAAA;GACA,WAAA;GACF;EAEA,MAAM,gBAAgB,eAAkD,CACtE,GAAI,MAAM,UAAU,CAAC,MAAM,QAAQ,GAAG,EAAE,EACxC,GAAG,MAAM,SACV,CAAA;EAED,MAAM,qBAAqB,eACzB,cAAc,MACX,QAAO,YAAW,WAAW,QAAQ,UAAU,CAAA,CAC/C,KAAK,SAAS,UAAU;GACvB,MAAM,gBAAgB,kBAAkB,QAAQ;AAChD,OAAI,CAAC,cAAe,QAAO;AAC3B,UAAO;IACL,GAAG;IACH,IAAI,QAAQ,MAAM,GAAG,QAAQ,UAAU,GAAG,QAAQ;IAClD;IACA,iBAAiB,eAAe,QAAQ;IAC1C;IACD,CACA,QAAQ,YAA0C,YAAY,KAAI,CACvE;EAEA,MAAM,kBAAkB,eAAe;GACrC;GACA,oCAAoC,MAAM;GAC1C,MAAM,QAAQ,2CAA2C;GAC1D,CAAA;EAED,SAAS,WAAW,eAAgC;AAClD,OAAI,MAAM,QAAQ,SAAS,KAAK,CAAC,MAAM,QAAQ,SAAS,cAAc,CAAE,QAAO;AAC/E,UAAO,CAAC,MAAM,QAAQ,SAAS,cAAa;;EAG9C,SAAS,UAAU,SAAmE;AACpF,OAAI,QAAQ,YAAa,QAAO,EAAE,GAAG,QAAQ,aAAY;AACzD,OAAI,SAAS,QAAQ,MAAM,CAAE,QAAO,EAAE,GAAG,QAAQ,OAAM;AACvD,UAAO,EAAC;;EAGV,SAAS,eAAe,SAAmE;GACzF,MAAM,OAAO,UAAU,QAAO;AAE9B,WAAQ,QAAQ,WAAhB;IACE,KAAK,YACH,QAAO;KACL,GAAG;KACH,UAAU,MAAM,YAAY,QAAQ,KAAK,SAAS;KAClD,MAAM,MAAM,QAAQ,OAAQ,KAAK,QAAQ;KAC3C;IACF,KAAK,iBACH,QAAO;KACL,GAAG;KACH,MAAM,MAAM,QAAQ,OAAQ,KAAK,QAAQ;KACzC,aAAa,MAAM,WAAW,QAAS,KAAK,eAAe;KAC3D,aAAa,MAAM,WAAW,QAAS,KAAK,eAAe;KAC3D,gBAAgB,MAAM,WAAW,QAAS,KAAK,kBAAkB;KACjE,iBAAiB,MAAM,WAAW,QAAS,KAAK,mBAAmB;KACrE;IACF,KAAK,YACH,QAAO;KACL,GAAG;KACH,MAAM,MAAM,QAAQ,OAAQ,KAAK,QAAQ;KACzC,WAAW,KAAK,cAAc,MAAM,QAAQ,UAAU,KAAA;KACxD;IACF,KAAK,qBACH,QAAO;KACL,GAAG;KACH,UAAU,MAAM,WAAW,QAAQ,KAAK;KACxC,MAAM,MAAM,QAAQ,OAAQ,KAAK,QAAQ;KAC3C;IACF,KAAK,cACH,QAAO;KACL,GAAG;KACH,UAAU,MAAM,YAAY,QAAQ,KAAK,SAAS;KACpD;IACF,KAAK,iBACH,QAAO;KACL,GAAG;KACH,gBAAgB,MAAM,WAAW,QAAS,KAAK,kBAAkB;KACjE,kBAAkB,MAAM,WAAW,QAAS,KAAK,oBAAoB;KACvE;IACF,KAAK,mBACH,QAAO;KACL,GAAG;KACH,UAAU,MAAM,YAAY,QAAQ,KAAK,SAAS;KAClD,gBAAgB,MAAM,QAAQ,QAAS,KAAK,kBAAkB;KAC9D,gBAAgB,MAAM,QAAQ,QAAS,KAAK,kBAAkB;KAChE;IACF,QACE,QAAO;;;EAIb,SAAS,SAAS,OAAkD;AAClE,UAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAK;;;uBAK1E,mBA8CM,OAAA,EA9CA,OAAK,eAAE,gBAAA,MAAe,EAAA,EAAA,EAAA,UAAA,KAAA,EAC1B,mBAqCU,UAAA,MAAA,WApCoB,mBAAA,QAArB,sBAAiB;wBAD1B,mBAqCU,WAAA;KAnCP,KAAK,kBAAkB;KACxB,OAAM;KACL,6BAA2B,kBAAkB;KAC7C,oBAAkB,kBAAkB;KACpC,2BAAyB,kBAAkB;QAGpC,QAAA,eAAA,WAAA,EADR,mBAqBS,UArBT,eAqBS,CAjBP,mBAUM,OAVN,eAUM,CATJ,mBAEI,KAFJ,eAEI,gBADC,kBAAkB,UAAS,EAAA,EAAA,EAGxB,kBAAkB,eAAe,kBAAkB,MAAA,WAAA,EAD3D,mBAKI,KALJ,eAKI,gBADC,kBAAkB,eAAe,kBAAkB,GAAE,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,EAIpD,QAAA,oBAAoB,kBAAkB,eAAA,WAAA,EAD9C,mBAKI,KALJ,eAKI,gBADC,kBAAkB,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,EAIpC,mBAKM,OALN,eAKM,EAAA,WAAA,EAJJ,YAGE,wBAFK,kBAAkB,cAAa,EADtC,WAGE,EAAA,SAAA,MAAA,EADQ,kBAAkB,gBAAe,EAAA,MAAA,GAAA,EAAA,CAAA,CAAA,EAAA,GAAA,cAAA;cAMvC,mBAAA,MAAmB,WAAM,KAAA,WAAA,EADjC,mBAKI,KALJ,eAKI,gBADC,QAAA,UAAS,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE/GlB,MAAM,OAAO;EAiBb,MAAM,QAAQ;EAgCd,MAAM,iBAAiB,eAAe,MAAM,cAAc,MAAM,OAAM;EACtE,MAAM,gBAAgB,eAAgD;AACpE,OAAI,MAAM,UAAU,KAAA,EAAW,QAAO,KAAA;AACtC,UAAO,sBAAsB,MAAM,MAAM,GAAG,MAAM,QAAQ,mBAAmB,MAAM,MAAK;IACzF;EACD,MAAM,mBAAmB,eAA8B,MAAM,YAAY,cAAc,OAAO,YAAY,EAAE,CAAA;EAC5G,MAAM,qBAAqB,eACzB,6BAA6B,cAAc,OAAO,kBAAkB,EAAE,EAAE,MAAM,eAAc,CAC9F;EAEA,MAAM,yBAAyB,eAAwC;AACrE,OAAI,MAAM,kBAAkB,KAAA,KAAa,eAAe,UAAU,KAAA,EAAW,QAAO,mBAAmB;AAEvG,UAAO;IACL,GAAG,mBAAmB;IACtB,eAAe;KACb,GAAI,mBAAmB,MAAM,iBAAiB,EAAE;KAChD,GAAI,MAAM,iBAAiB,EAAE;KAC7B,GAAI,eAAe,SAAS,EAAE;KAC/B;IACH;IACD;EAED,IAAI;EAEJ,SAAS,yBAAyB,aAAsC,EAAE,EAAE;AAC1E,4BAAyB,MAAK;AAC9B,6BAA0B,aAAY;AACtC,UAAO,wBAAwB,UAC7B,oBAAoB,iBAAiB,OAAO;IAC1C,GAAG,uBAAuB;IAC1B,eAAe;KACb,GAAG;KACH,GAAI,uBAAuB,MAAM,iBAAiB,EAAE;KACrD;IACF,CAAA,CACF;;EAGH,MAAM,qBAAqB,WAAqD,0BAA0B,CAAA;EAC1G,MAAM,oBAAoB,eAAyD,MAAM,aAAa,mBAAmB,MAAK;EAC9H,MAAM,4BAA4B,eAChC,kBAAkB,MAAM,qBAAqB,MAAM,qBAAqB,cAAc,OAAO,kBAAiB,CAChH;EACA,MAAM,gCAAgC,eACpC,kBAAkB,MAAM,yBAAyB,MAAM,qBAAqB,cAAc,OAAO,kBAAiB,CACpH;EACA,MAAM,yBAAyB,eAC7B,kBAAkB,MAAM,kBAAkB,MAAM,kBAAkB,cAAc,OAAO,eAAc,CACvG;EACA,MAAM,6BAA6B,eACjC,kBAAkB,MAAM,sBAAsB,MAAM,sBAAsB,cAAc,OAAO,mBAAkB,CACnH;EACA,MAAM,mBAAmB,gBAAsD;GAC7E,GAAG,kBAAkB,MAAM;GAC3B,mBAAmB;GACnB,uBAAuB;GACvB,gBAAgB;GAChB,oBAAoB;GACrB,EAAC;EACF,MAAM,qBAAqB,eAAe,kBAAkB,MAAM,SAAS,OAAO,MAAK;AAEvF,QACE;SAAO,MAAM;SAAa,MAAM;SAAgB,MAAM;SAAsB,MAAM;GAAc,QAC1F;GACJ,MAAM,oBAAoB,mBAAmB;GAC7C,MAAM,qBAAqB,kBAAkB,WAAW;AACxD,sBAAmB,QAAQ,yBAAyB,EAAE,GAAG,kBAAkB,QAAQ,CAAA;AACnF,sBAAmB,MAAM,cAAc,mBAAkB;KAE3D,EAAE,MAAM,MAAM,CAChB;AAEA,uBAAqB,yBAAyB,MAAM,CAAA;AAEpD,QACE,iBACC,WAAW;AACV,OAAI,WAAW,KAAA,KAAa,MAAM,cAAc,KAAA,EAAW;AAC3D,OAAI,aAAa,mBAAmB,MAAM,QAAQ,OAAO,CAAE;AAC3D,sBAAmB,MAAM,UAAU,OAAM;KAE3C,EAAE,MAAM,MAAM,CAChB;AAEA,cACQ,mBAAmB,MAAM,SAC9B,WAAW;AACV,OAAI,MAAM,cAAc,KAAA,EAAW;GACnC,MAAM,aAAa,EAAE,GAAG,QAAO;AAC/B,OAAI,eAAe,UAAU,KAAA,KAAa,aAAa,eAAe,OAAO,WAAW,CAAE;AAC1F,QAAK,qBAAqB,WAAU;AACpC,QAAK,iBAAiB,WAAU;KAElC,EAAE,MAAM,MAAM,CAChB;EAEA,SAAS,aAAa,MAA+B,OAAyC;GAC5F,MAAM,WAAW,OAAO,KAAK,KAAI;GACjC,MAAM,YAAY,OAAO,KAAK,MAAK;AACnC,OAAI,SAAS,WAAW,UAAU,OAAQ,QAAO;AACjD,UAAO,SAAS,OAAM,QAAO,YAAY,KAAK,MAAM,MAAM,KAAK,CAAA;;EAGjE,SAAS,YAAY,MAAe,OAAyB;GAC3D,MAAM,UAAU,MAAM,KAAI;GAC1B,MAAM,WAAW,MAAM,MAAK;AAE5B,OAAI,OAAO,GAAG,SAAS,SAAS,CAAE,QAAO;AAEzC,OAAI,MAAM,QAAQ,QAAQ,IAAI,MAAM,QAAQ,SAAS,EAAE;AACrD,QAAI,CAAC,MAAM,QAAQ,QAAQ,IAAI,CAAC,MAAM,QAAQ,SAAS,CAAE,QAAO;AAChE,QAAI,QAAQ,WAAW,SAAS,OAAQ,QAAO;AAC/C,WAAO,QAAQ,OAAO,MAAM,UAAU,YAAY,MAAM,SAAS,OAAO,CAAA;;AAG1E,OAAI,cAAc,QAAQ,IAAI,cAAc,SAAS,EAAE;AACrD,QAAI,CAAC,cAAc,QAAQ,IAAI,CAAC,cAAc,SAAS,CAAE,QAAO;AAChE,WAAO,aAAa,SAAS,SAAQ;;AAGvC,UAAO;;EAGT,SAAS,cAAc,OAAkD;AACvE,UAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAK;;EAG5E,SAAS,sBAAsB,OAAyE;AACtG,UAAO,oBAAoB;;;uBAK3B,YA2EY,mBAAA;IA1EV,OAAM;IACL,oBAAkB,QAAA;IAClB,iBAAe,QAAA;IACf,sBAAoB,QAAA;IACpB,UAAU,QAAA;;IAEA,QAAM,cAkBR,CAjBP,WAiBO,KAAA,QAAA,UAAA;KAfJ,WAAW,kBAAA;KACX,UAAU,iBAAA;KACV,QAAS,mBAAA;aAaL,CAXL,YAUE,mBAAA;KATC,OAAO,QAAA;KACP,UAAU,QAAA;KACV,SAAS,QAAA;KACT,YAAU,kBAAA,MAAkB,QAAQ;KACpC,mBAAiB,kBAAA,MAAkB,QAAQ;KAC3C,iBAAe,QAAA;KACf,mBAAiB,kBAAA,MAAkB,eAAe;KAClD,cAAa,kBAAA,MAAkB,QAAQ;KACvC,wBAAwB,kBAAA,MAAkB,eAAe;;;;;;;;;;;;IAKrD,SAAO,cAqBT,CApBP,WAoBO,KAAA,QAAA,WAAA;KAlBJ,WAAW,kBAAA;KACX,UAAU,iBAAA;KACV,SAAS,kBAAA,MAAkB;aAgBvB,CAdL,YAaE,oBAbF,WACU,kBAYR,MAZ0B,SAAO;KAChC,OAAO,QAAA;KACP,UAAU,QAAA;KACV,OAAO,QAAA;KACP,SAAS,QAAA;KACT,UAAU;KACV,OAAO,QAAA;KACP,OAAO,QAAA;KACP,qBAAmB,QAAA;KACnB,gBAAc,QAAA;KACd,iBAAe,QAAA;KACf,iBAAe,QAAA;;;;;;;;;;;;;2BA2Bf,CAtBP,mBAsBO,QAtBP,eAsBO,CArBL,WAoBO,KAAA,QAAA,WAAA;KAnBJ,WAAW,kBAAA;KACX,UAAU,iBAAA;KACV,QAAQ,kBAAA,MAAkB;KAC1B,mBAAoB,0BAAA;KACpB,uBAA0B,8BAAA;KAC1B,gBAAiB,uBAAA;KACjB,oBAAuB,2BAAA;aAanB,CAXL,YAUE,qBAVF,WACU,kBASR,MAT0B,MAAI;KAC7B,cAAc,QAAA;KACd,SAAS,QAAA;KACT,UAAU,QAAA;KACV,UAAU,QAAA;KACV,gBAAc,QAAA;KACd,MAAM,QAAA;KACN,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,UAAW,OAAM;KAC7B,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEtQvB,MAAM,QAAQ;EAiCd,MAAM,OAAO;EAiBb,MAAM,gBAAgB,eACpB,MAAM,SAAS,6BAA6B,MAAM,kBAAiB,CACrE;EAEA,SAAS,kBAA0B;AACjC,UAAO,MAAM,WAAW,MAAM,kBAAkB,gBAAgB,WAAW;;EAG7E,SAAS,iBAAyB;AAChC,UAAO,MAAM,UAAU,MAAM,kBAAkB,gBAAgB,UAAU;;EAG3E,SAAS,iBAAiB,oBAAwD;AAChF,UAAO;IACL,MAAM;IACN,eAAe;IACf,GAAI,mBAAmB,iBAAiB,KAAK,EAAE;IAC/C,GAAG,MAAM;IACX;;EAGF,SAAS,sBAAsB,oBAAwD;AACrF,UAAO;IACL,GAAI,mBAAmB,gBAAgB,KAAK,EAAE;IAC9C,GAAG,MAAM;IACX;;EAGF,SAAS,UAAU,WAOc;AAC/B,UAAO;IACL,GAAG;IACH,gBAAgB,iBAAiB,UAAU,mBAAmB;IAC9D,qBAAqB,sBAAsB,UAAU,mBAAmB;IAC1E;;;uBAKA,YA2CuB,8BAAA;IA1CpB,OAAO,cAAA;IACP,WAAW,QAAA;IACX,mBAAiB,QAAA;IACjB,kBAAgB,QAAA;IAChB,eAAa,QAAA;IACb,QAAQ,QAAA;IACR,OAAO,QAAA;IACP,UAAU,QAAA;IACV,mBAAiB,QAAA;IACjB,iBAAe,QAAA;IACf,mBAAiB,QAAA;IACjB,sBAAoB,QAAA;IACpB,oBAAkB,QAAA;IAClB,iBAAe,QAAA;IACf,oBAAkB,QAAA;IAClB,iBAAe,QAAA;IACf,UAAU,QAAA;IACV,OAAO,QAAA;IACP,iBAAe,QAAA;IACf,qBAAmB,QAAA;IACnB,qBAAmB,QAAA;IACnB,gBAAc,QAAA;IACd,iBAAe,QAAA;IACf,iBAAe,QAAA;IACf,aAAW,QAAA;IACX,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,qBAAsB,OAAM;IACpD,mBAAa,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,iBAAkB,OAAM;IAC3C,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,UAAW,OAAM;IAC7B,UAAM,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAA;;IAEF,SAAO,SAAE,uBAAkB,CACpC,WASO,KAAA,QAAA,WAAA,eAAA,mBATO,UAAU,mBAAkB,CAAA,CAAA,QASnC,CARL,mBAOM,OAPN,eAOM,CANJ,WAEO,KAAA,QAAA,SAAA,eAAA,mBAFoB,UAAU,mBAAkB,CAAA,CAAA,QAEhD,CADL,YAA8E,mBAAA,eAAA,mBAA3D,iBAAiB,mBAAmB,mBAAkB,CAAA,CAAA,EAAA,MAAA,GAAA,CAAA,EAAA,KAAA,EAE3E,WAEO,KAAA,QAAA,QAAA,eAAA,mBAFmB,UAAU,mBAAkB,CAAA,CAAA,QAE/C,CADL,YAAwF,wBAAA,eAAA,mBAAhE,sBAAsB,mBAAmB,mBAAkB,CAAA,CAAA,EAAA,MAAA,GAAA,CAAA,EAAA,KAAA,CAAA,CAAA,CAAA,EAAA,KAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE3N/F,MAAM,QAAQ;EAKd,MAAM,WAAW,eAAe,CAAC,CAAC,MAAM,UAAS;;uBAI/C,mBAUM,OAAA;IATJ,OAAK,eAAA,CAAC,kBACS,SAAA,QAAA,CAAA,0BAA8C,QAAA,cAAS,QAAA,wBAAA,yBAAA,GAAA,CAAA,wBAAkG,QAAA,aAAU,+BAAA,GAAA,CAAA,CAAA;IAKjL,OAAK,eAAE,SAAA,QAAQ,EAAA,KAAK,QAAA,KAAG,GAAK,KAAA,EAAS;OAEtC,WAAQ,KAAA,QAAA,UAAA,CAAA,EAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;UEZF,QAAA,YAAA,WAAA,EADR,mBASE,OAAA;;IAPC,OAAK,eAAA;;;sBAAiF,QAAA;;IAKvF,MAAK;IACL,oBAAiB;gCAEnB,mBAeM,OAAA;;IAbH,OAAK,eAAA;;sBAAiD,QAAA;KAAiB,QAAA,QAAK,uCAA0C,QAAA,UAAK;;IAK5H,MAAK;IACL,oBAAiB;OAED,QAAA,SAAA,WAAA,EAAhB,mBAIW,UAAA,EAAA,KAAA,GAAA,EAAA;IAHG,QAAA,UAAK,YAAA,WAAA,EAAjB,mBAA6D,QAA7D,cAA6D,IAAA,mBAAA,IAAA,KAAA;IAC7D,mBAAoD,QAApD,eAAoD,gBAAf,QAAA,MAAK,EAAA,EAAA;8BAC1C,mBAAmC,QAAA,EAA7B,OAAM,sBAAoB,EAAA,MAAA,GAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;EE9BtC,MAAM,QAAQ;EAOd,MAAM,WAAW,eACf,MAAM,QAAQ;GAAE,iBAAiB,MAAM;GAAO,OAAO,MAAM;GAAO,GAAG,EAAC,CACxE;;uBAIE,mBAUO,QAVP,eAUO,CATL,mBAOE,QAAA;IANC,OAAK,eAAA;;6BAAgE,QAAA,WAAM,CAAM,QAAA,OAAK;kCAAyC,QAAA,OAAK;;IAKpI,OAAK,eAAE,SAAA,MAAQ;gBAEN,QAAA,SAAA,WAAA,EAAZ,mBAAgE,QAAhE,eAAgE,gBAAf,QAAA,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEjB1D,MAAM,QAAQ;EAYd,MAAM,eAAe,eAAe,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,MAAM,MAAM,CAAC,CAAA;EAE3E,MAAM,WAAW,eAAe;AAC9B,OAAI,MAAM,cAAe,QAAO,KAAA;AAChC,UAAO,EAAE,OAAO,GAAG,aAAa,MAAM,IAAG;IAC1C;EAED,MAAM,eAAe,eAAe,MAAM,MAAM,UAAU,EAAC;;uBAIzD,mBAqEM,OAAA,EArED,OAAK,eAAA,CAAC,iBAAe,kBAA2B,QAAA,UAAO,CAAA,EAAA,EAAA;IAElD,QAAA,SAAS,QAAA,aAAA,WAAA,EADjB,mBAOM,OAPN,eAOM;KAHQ,QAAA,SAAA,WAAA,EAAZ,mBAAkE,QAAlE,eAAkE,gBAAf,QAAA,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAC5C,QAAA,aAAS,CAAK,QAAA,iBAAiB,QAAA,YAAO,UAAA,WAAA,EAAlD,mBAAsH,QAAtH,eAAsH,gBAAvB,aAAA,MAAY,GAAG,KAAC,EAAA,IAAA,mBAAA,IAAA,KAAA;KACnG,QAAA,aAAa,QAAA,YAAO,eAAoB,aAAA,SAAA,WAAA,EAApD,mBAA4I,QAA5I,eAA4I,gBAA1C,QAAA,YAAW,GAAG,QAAG,gBAAG,aAAA,MAAY,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;;IAK5H,QAAA,YAAO,UAAA,WAAA,EADf,mBAmBM,OAAA;;KAjBH,OAAK,eAAA,CAAA,wBAAA,yBAAqE,QAAA,OAAA,CAAA;KAI3E,MAAK;KACJ,iBAAe,QAAA,gBAAgB,KAAA,IAAY,aAAA;KAC3C,iBAAe,QAAA,gBAAgB,KAAA,IAAS;KACxC,iBAAe,QAAA,gBAAgB,KAAA,IAAS;QAEzC,mBAOE,OAAA;KANC,OAAK,eAAA;;6BAAqE,QAAA;MAAmB,QAAA,gBAAa,sCAAA;;KAK1G,OAAK,eAAE,SAAA,MAAQ;sDAKpB,mBAiBM,OAAA;;KAfH,OAAK,eAAA,CAAA,2BAAA,4BAA0D,QAAA,OAAI,CAAA;KACpE,MAAK;KACJ,iBAAe,QAAA;KACf,iBAAe;KACf,iBAAe,aAAA;0BAEhB,mBAQE,UAAA,MAAA,WAPqB,QAAA,QAAb,OAAO,MAAC;yBADlB,mBAQE,QAAA;MANC,KAAK;MACL,OAAK,eAAA;;OAAkD,IAAI,QAAA,cAAW,wDAA2D,QAAA,UAAK;OAAmB,MAAM,QAAA,cAAW,0DAA6D,QAAA,UAAK;;;;IAUzO,QAAA,YAAO,eAAoB,QAAA,MAAM,UAAA,WAAA,EADzC,mBAeM,OAfN,eAeM,EAAA,UAAA,KAAA,EAXJ,mBAUO,UAAA,MAAA,WATe,QAAA,QAAZ,MAAM,MAAC;yBADjB,mBAUO,QAAA;MARJ,KAAK;MACL,OAAK,eAAA;;OAAqD,MAAM,QAAA,cAAW,sCAAA;OAAuD,IAAI,QAAA,cAAW,oCAAA;;wBAM/I,KAAI,EAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE3Ff,MAAM,QAAQ;EAKd,MAAM,UAAU;GAAC;GAAW;GAAW;GAAW;GAAW;GAAW;GAAW;GAAW;GAAS;EAKvG,MAAM,mBAAmB,eACvB,MAAM,YAAY,MAAM,MAAM,OAAO,EAAE,CAAC,aAAa,IAAI,GAC3D;EAEA,MAAM,kBAAkB,eAAe;AACrC,OAAI,MAAM,MAAO,QAAO,MAAM;AAC9B,OAAI,CAAC,MAAM,KAAM,QAAO,KAAA;GAExB,IAAI,OAAO;AACX,QAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KAAK,QAAQ,IACrC,QAAO,MAAM,KAAK,WAAW,EAAE,KAAK,QAAQ,KAAK;AAEnD,UAAO,QAAQ,KAAK,IAAI,KAAK,GAAG,QAAQ;IACzC;;uBAIC,mBAgCO,QAAA;IA/BJ,OAAK,eAAA,CAAA,eAAA,gBAAkC,QAAA,OAAI,CAAA;IAC5C,MAAK;IACJ,cAAY,QAAA,OAAO,QAAA,QAAI;OAExB,mBAqBO,QAAA;IApBL,OAAM;IACL,OAAK,eAAE,gBAAA,QAAe,EAAA,iBAAK,gBAAA,OAAe,GAAK,KAAA,EAAS;OAGjD,QAAA,OAAA,WAAA,EADR,mBAKC,OAAA;;IAHE,KAAK,QAAA;IACL,KAAK,QAAA,OAAO,QAAA,QAAI;IACjB,OAAM;iCAES,iBAAA,SAAA,WAAA,EAAjB,mBAEO,QAFP,eAEO,gBADF,iBAAA,MAAgB,EAAA,EAAA,KAAA,WAAA,EAErB,mBAOM,OAPN,eAOM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAA0H,QAAA,EAApH,GAAE,iHAA+G,EAAA,MAAA,GAAA,CAAA,EAAA,CAAA,EAAA,EAAA,EAAA,EAInH,QAAA,UAAA,WAAA,EADR,mBAIE,QAAA;;IAFC,OAAK,eAAA,CAAA,uBAAA,wBAAkD,QAAA,SAAM,CAAA;IAC9D,eAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE5DlB,MAAM,QAAQ;EAId,MAAM,OAAO;EAIb,MAAM,kBAAkB,eACtB,MAAM,MAAM,IAAI,wBAAuB,CACzC;EAEA,SAAS,YAAY,MAAsB;AACzC,OAAI,CAAC,KAAK,KACR,MAAK,YAAY,KAAI;;;uBAMvB,mBA4CM,OA5CN,eA4CM,CA3CJ,mBA0CK,MA1CL,eA0CK,EAAA,UAAA,KAAA,EAzCH,mBAwCK,UAAA,MAAA,WAvCqB,gBAAA,QAAhB,MAAM,UAAK;wBADrB,mBAwCK,MAAA;KAtCF,KAAK;KACN,OAAM;QAEN,WAsBO,KAAA,QAAA,QAAA;KAtBkB;KAAc;KAAQ,QAAS,UAAU,gBAAA,MAAgB,SAAM;aAsBjF,CApBG,KAAK,QAAQ,UAAU,gBAAA,MAAgB,SAAM,KAAA,WAAA,EADrD,mBAMI,KAAA;;KAJD,MAAM,KAAK;KACZ,OAAM;uBAEH,KAAK,MAAK,EAAA,GAAA,cAAA,IAGF,UAAU,gBAAA,MAAgB,SAAM,KAAA,WAAA,EAD7C,mBAMS,UAAA;;KAJP,OAAM;KACL,UAAK,WAAE,YAAY,KAAI;uBAErB,KAAK,MAAK,EAAA,GAAA,cAAA,KAAA,WAAA,EAEf,mBAMO,QANP,eAMO,gBADF,KAAK,MAAK,EAAA,EAAA,EAAA,CAAA,EAIT,UAAU,gBAAA,MAAgB,SAAM,KAAA,WAAA,EADxC,mBAWO,QAXP,eAWO,CANL,WAKO,KAAA,QAAA,aAAA,EAAA,QAAA,CAJM,QAAA,cAAS,OAAA,WAAA,EAApB,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAA0B,QAAA,EAApB,GAAE,iBAAe,EAAA,MAAA,GAAA,CAAA,EAAA,CAAA,KAAA,WAAA,EAEzB,mBAA2C,UAAA,EAAA,KAAA,GAAA,EAAA,CAAA,gBAAA,gBAAvB,QAAA,UAAS,EAAA,EAAA,CAAA,EAAA,GAAA,EAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;;;;;;;AC9DzC,IAAM,MAAM,KAAK;AACjB,IAAM,MAAM,KAAK;AACjB,IAAM,QAAQ,KAAK;AAEnB,IAAM,gBAAe,OAAM;CACzB,GAAG;CACH,GAAG;CACJ;AACD,IAAM,kBAAkB;CACtB,MAAM;CACN,OAAO;CACP,QAAQ;CACR,KAAK;CACN;AACD,SAAS,MAAM,OAAO,OAAO,KAAK;AAChC,QAAO,IAAI,OAAO,IAAI,OAAO,IAAI,CAAC;;AAEpC,SAAS,SAAS,OAAO,OAAO;AAC9B,QAAO,OAAO,UAAU,aAAa,MAAM,MAAM,GAAG;;AAEtD,SAAS,QAAQ,WAAW;AAC1B,QAAO,UAAU,MAAM,IAAI,CAAC;;AAE9B,SAAS,aAAa,WAAW;AAC/B,QAAO,UAAU,MAAM,IAAI,CAAC;;AAE9B,SAAS,gBAAgB,MAAM;AAC7B,QAAO,SAAS,MAAM,MAAM;;AAE9B,SAAS,cAAc,MAAM;AAC3B,QAAO,SAAS,MAAM,WAAW;;AAEnC,SAAS,YAAY,WAAW;CAC9B,MAAM,YAAY,UAAU;AAC5B,QAAO,cAAc,OAAO,cAAc,MAAM,MAAM;;AAExD,SAAS,iBAAiB,WAAW;AACnC,QAAO,gBAAgB,YAAY,UAAU,CAAC;;AAEhD,SAAS,kBAAkB,WAAW,OAAO,KAAK;AAChD,KAAI,QAAQ,KAAK,EACf,OAAM;CAER,MAAM,YAAY,aAAa,UAAU;CACzC,MAAM,gBAAgB,iBAAiB,UAAU;CACjD,MAAM,SAAS,cAAc,cAAc;CAC3C,IAAI,oBAAoB,kBAAkB,MAAM,eAAe,MAAM,QAAQ,WAAW,UAAU,SAAS,cAAc,UAAU,WAAW;AAC9I,KAAI,MAAM,UAAU,UAAU,MAAM,SAAS,QAC3C,qBAAoB,qBAAqB,kBAAkB;AAE7D,QAAO,CAAC,mBAAmB,qBAAqB,kBAAkB,CAAC;;AAErE,SAAS,sBAAsB,WAAW;CACxC,MAAM,oBAAoB,qBAAqB,UAAU;AACzD,QAAO;EAAC,8BAA8B,UAAU;EAAE;EAAmB,8BAA8B,kBAAkB;EAAC;;AAExH,SAAS,8BAA8B,WAAW;AAChD,QAAO,UAAU,SAAS,QAAQ,GAAG,UAAU,QAAQ,SAAS,MAAM,GAAG,UAAU,QAAQ,OAAO,QAAQ;;AAE5G,IAAM,cAAc,CAAC,QAAQ,QAAQ;AACrC,IAAM,cAAc,CAAC,SAAS,OAAO;AACrC,IAAM,cAAc,CAAC,OAAO,SAAS;AACrC,IAAM,cAAc,CAAC,UAAU,MAAM;AACrC,SAAS,YAAY,MAAM,SAAS,KAAK;AACvC,SAAQ,MAAR;EACE,KAAK;EACL,KAAK;AACH,OAAI,IAAK,QAAO,UAAU,cAAc;AACxC,UAAO,UAAU,cAAc;EACjC,KAAK;EACL,KAAK,QACH,QAAO,UAAU,cAAc;EACjC,QACE,QAAO,EAAE;;;AAGf,SAAS,0BAA0B,WAAW,eAAe,WAAW,KAAK;CAC3E,MAAM,YAAY,aAAa,UAAU;CACzC,IAAI,OAAO,YAAY,QAAQ,UAAU,EAAE,cAAc,SAAS,IAAI;AACtE,KAAI,WAAW;AACb,SAAO,KAAK,KAAI,SAAQ,OAAO,MAAM,UAAU;AAC/C,MAAI,cACF,QAAO,KAAK,OAAO,KAAK,IAAI,8BAA8B,CAAC;;AAG/D,QAAO;;AAET,SAAS,qBAAqB,WAAW;CACvC,MAAM,OAAO,QAAQ,UAAU;AAC/B,QAAO,gBAAgB,QAAQ,UAAU,MAAM,KAAK,OAAO;;AAE7D,SAAS,oBAAoB,SAAS;AACpC,QAAO;EACL,KAAK;EACL,OAAO;EACP,QAAQ;EACR,MAAM;EACN,GAAG;EACJ;;AAEH,SAAS,iBAAiB,SAAS;AACjC,QAAO,OAAO,YAAY,WAAW,oBAAoB,QAAQ,GAAG;EAClE,KAAK;EACL,OAAO;EACP,QAAQ;EACR,MAAM;EACP;;AAEH,SAAS,iBAAiB,MAAM;CAC9B,MAAM,EACJ,GACA,GACA,OACA,WACE;AACJ,QAAO;EACL;EACA;EACA,KAAK;EACL,MAAM;EACN,OAAO,IAAI;EACX,QAAQ,IAAI;EACZ;EACA;EACD;;;;ACjIH,SAAS,2BAA2B,MAAM,WAAW,KAAK;CACxD,IAAI,EACF,WACA,aACE;CACJ,MAAM,WAAW,YAAY,UAAU;CACvC,MAAM,gBAAgB,iBAAiB,UAAU;CACjD,MAAM,cAAc,cAAc,cAAc;CAChD,MAAM,OAAO,QAAQ,UAAU;CAC/B,MAAM,aAAa,aAAa;CAChC,MAAM,UAAU,UAAU,IAAI,UAAU,QAAQ,IAAI,SAAS,QAAQ;CACrE,MAAM,UAAU,UAAU,IAAI,UAAU,SAAS,IAAI,SAAS,SAAS;CACvE,MAAM,cAAc,UAAU,eAAe,IAAI,SAAS,eAAe;CACzE,IAAI;AACJ,SAAQ,MAAR;EACE,KAAK;AACH,YAAS;IACP,GAAG;IACH,GAAG,UAAU,IAAI,SAAS;IAC3B;AACD;EACF,KAAK;AACH,YAAS;IACP,GAAG;IACH,GAAG,UAAU,IAAI,UAAU;IAC5B;AACD;EACF,KAAK;AACH,YAAS;IACP,GAAG,UAAU,IAAI,UAAU;IAC3B,GAAG;IACJ;AACD;EACF,KAAK;AACH,YAAS;IACP,GAAG,UAAU,IAAI,SAAS;IAC1B,GAAG;IACJ;AACD;EACF,QACE,UAAS;GACP,GAAG,UAAU;GACb,GAAG,UAAU;GACd;;AAEL,SAAQ,aAAa,UAAU,EAA/B;EACE,KAAK;AACH,UAAO,kBAAkB,eAAe,OAAO,aAAa,KAAK;AACjE;EACF,KAAK;AACH,UAAO,kBAAkB,eAAe,OAAO,aAAa,KAAK;AACjE;;AAEJ,QAAO;;;;;;;;;;AAWT,eAAe,eAAe,OAAO,SAAS;CAC5C,IAAI;AACJ,KAAI,YAAY,KAAK,EACnB,WAAU,EAAE;CAEd,MAAM,EACJ,GACA,GACA,UACA,OACA,UACA,aACE;CACJ,MAAM,EACJ,WAAW,qBACX,eAAe,YACf,iBAAiB,YACjB,cAAc,OACd,UAAU,MACR,SAAS,SAAS,MAAM;CAC5B,MAAM,gBAAgB,iBAAiB,QAAQ;CAE/C,MAAM,UAAU,SAAS,cADN,mBAAmB,aAAa,cAAc,aACb;CACpD,MAAM,qBAAqB,iBAAiB,MAAM,SAAS,gBAAgB;EACzE,WAAW,wBAAwB,OAAO,SAAS,aAAa,OAAO,KAAK,IAAI,SAAS,UAAU,QAAQ,MAAM,OAAO,wBAAwB,QAAQ,UAAU,QAAQ,kBAAmB,OAAO,SAAS,sBAAsB,OAAO,KAAK,IAAI,SAAS,mBAAmB,SAAS,SAAS;EACjS;EACA;EACA;EACD,CAAC,CAAC;CACH,MAAM,OAAO,mBAAmB,aAAa;EAC3C;EACA;EACA,OAAO,MAAM,SAAS;EACtB,QAAQ,MAAM,SAAS;EACxB,GAAG,MAAM;CACV,MAAM,eAAe,OAAO,SAAS,mBAAmB,OAAO,KAAK,IAAI,SAAS,gBAAgB,SAAS,SAAS;CACnH,MAAM,cAAe,OAAO,SAAS,aAAa,OAAO,KAAK,IAAI,SAAS,UAAU,aAAa,IAAM,OAAO,SAAS,YAAY,OAAO,KAAK,IAAI,SAAS,SAAS,aAAa,KAAM;EACvL,GAAG;EACH,GAAG;EACJ,GAAG;EACF,GAAG;EACH,GAAG;EACJ;CACD,MAAM,oBAAoB,iBAAiB,SAAS,wDAAwD,MAAM,SAAS,sDAAsD;EAC/K;EACA;EACA;EACA;EACD,CAAC,GAAG,KAAK;AACV,QAAO;EACL,MAAM,mBAAmB,MAAM,kBAAkB,MAAM,cAAc,OAAO,YAAY;EACxF,SAAS,kBAAkB,SAAS,mBAAmB,SAAS,cAAc,UAAU,YAAY;EACpG,OAAO,mBAAmB,OAAO,kBAAkB,OAAO,cAAc,QAAQ,YAAY;EAC5F,QAAQ,kBAAkB,QAAQ,mBAAmB,QAAQ,cAAc,SAAS,YAAY;EACjG;;AAIH,IAAM,kBAAkB;;;;;;;;AASxB,IAAMC,oBAAkB,OAAO,WAAW,UAAU,WAAW;CAC7D,MAAM,EACJ,YAAY,UACZ,WAAW,YACX,aAAa,EAAE,EACf,aACE;CACJ,MAAM,6BAA6B,SAAS,iBAAiB,WAAW;EACtE,GAAG;EACH;EACD;CACD,MAAM,MAAM,OAAO,SAAS,SAAS,OAAO,KAAK,IAAI,SAAS,MAAM,SAAS;CAC7E,IAAI,QAAQ,MAAM,SAAS,gBAAgB;EACzC;EACA;EACA;EACD,CAAC;CACF,IAAI,EACF,GACA,MACE,2BAA2B,OAAO,WAAW,IAAI;CACrD,IAAI,oBAAoB;CACxB,IAAI,aAAa;CACjB,MAAM,iBAAiB,EAAE;AACzB,MAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;EAC1C,MAAM,oBAAoB,WAAW;AACrC,MAAI,CAAC,kBACH;EAEF,MAAM,EACJ,MACA,OACE;EACJ,MAAM,EACJ,GAAG,OACH,GAAG,OACH,MACA,UACE,MAAM,GAAG;GACX;GACA;GACA,kBAAkB;GAClB,WAAW;GACX;GACA;GACA;GACA,UAAU;GACV,UAAU;IACR;IACA;IACD;GACF,CAAC;AACF,MAAI,SAAS,OAAO,QAAQ;AAC5B,MAAI,SAAS,OAAO,QAAQ;AAC5B,iBAAe,QAAQ;GACrB,GAAG,eAAe;GAClB,GAAG;GACJ;AACD,MAAI,SAAS,aAAa,iBAAiB;AACzC;AACA,OAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,MAAM,UACR,qBAAoB,MAAM;AAE5B,QAAI,MAAM,MACR,SAAQ,MAAM,UAAU,OAAO,MAAM,SAAS,gBAAgB;KAC5D;KACA;KACA;KACD,CAAC,GAAG,MAAM;AAEb,KAAC,CACC,GACA,KACE,2BAA2B,OAAO,mBAAmB,IAAI;;AAE/D,OAAI;;;AAGR,QAAO;EACL;EACA;EACA,WAAW;EACX;EACA;EACD;;;;;;;;AAkMH,IAAMC,SAAO,SAAU,SAAS;AAC9B,KAAI,YAAY,KAAK,EACnB,WAAU,EAAE;AAEd,QAAO;EACL,MAAM;EACN;EACA,MAAM,GAAG,OAAO;GACd,IAAI,uBAAuB;GAC3B,MAAM,EACJ,WACA,gBACA,OACA,kBACA,UACA,aACE;GACJ,MAAM,EACJ,UAAU,gBAAgB,MAC1B,WAAW,iBAAiB,MAC5B,oBAAoB,6BACpB,mBAAmB,WACnB,4BAA4B,QAC5B,gBAAgB,MAChB,GAAG,0BACD,SAAS,SAAS,MAAM;AAM5B,QAAK,wBAAwB,eAAe,UAAU,QAAQ,sBAAsB,gBAClF,QAAO,EAAE;GAEX,MAAM,OAAO,QAAQ,UAAU;GAC/B,MAAM,kBAAkB,YAAY,iBAAiB;GACrD,MAAM,kBAAkB,QAAQ,iBAAiB,KAAK;GACtD,MAAM,MAAM,OAAO,SAAS,SAAS,OAAO,KAAK,IAAI,SAAS,MAAM,SAAS,SAAS;GACtF,MAAM,qBAAqB,gCAAgC,mBAAmB,CAAC,gBAAgB,CAAC,qBAAqB,iBAAiB,CAAC,GAAG,sBAAsB,iBAAiB;GACjL,MAAM,+BAA+B,8BAA8B;AACnE,OAAI,CAAC,+BAA+B,6BAClC,oBAAmB,KAAK,GAAG,0BAA0B,kBAAkB,eAAe,2BAA2B,IAAI,CAAC;GAExH,MAAM,aAAa,CAAC,kBAAkB,GAAG,mBAAmB;GAC5D,MAAM,WAAW,MAAM,SAAS,eAAe,OAAO,sBAAsB;GAC5E,MAAM,YAAY,EAAE;GACpB,IAAI,kBAAkB,uBAAuB,eAAe,SAAS,OAAO,KAAK,IAAI,qBAAqB,cAAc,EAAE;AAC1H,OAAI,cACF,WAAU,KAAK,SAAS,MAAM;AAEhC,OAAI,gBAAgB;IAClB,MAAM,QAAQ,kBAAkB,WAAW,OAAO,IAAI;AACtD,cAAU,KAAK,SAAS,MAAM,KAAK,SAAS,MAAM,IAAI;;AAExD,mBAAgB,CAAC,GAAG,eAAe;IACjC;IACA;IACD,CAAC;AAGF,OAAI,CAAC,UAAU,OAAM,SAAQ,QAAQ,EAAE,EAAE;IACvC,IAAI,uBAAuB;IAC3B,MAAM,eAAe,wBAAwB,eAAe,SAAS,OAAO,KAAK,IAAI,sBAAsB,UAAU,KAAK;IAC1H,MAAM,gBAAgB,WAAW;AACjC,QAAI;SAEE,EAD4B,mBAAmB,cAAc,oBAAoB,YAAY,cAAc,GAAG,UAIlH,cAAc,OAAM,MAAK,YAAY,EAAE,UAAU,KAAK,kBAAkB,EAAE,UAAU,KAAK,IAAI,KAAK,CAEhG,QAAO;MACL,MAAM;OACJ,OAAO;OACP,WAAW;OACZ;MACD,OAAO,EACL,WAAW,eACZ;MACF;;IAML,IAAI,kBAAkB,wBAAwB,cAAc,QAAO,MAAK,EAAE,UAAU,MAAM,EAAE,CAAC,MAAM,GAAG,MAAM,EAAE,UAAU,KAAK,EAAE,UAAU,GAAG,CAAC,OAAO,OAAO,KAAK,IAAI,sBAAsB;AAG1L,QAAI,CAAC,eACH,SAAQ,kBAAR;KACE,KAAK,WACH;MACE,IAAI;MACJ,MAAM,aAAa,yBAAyB,cAAc,QAAO,MAAK;AACpE,WAAI,8BAA8B;QAChC,MAAM,kBAAkB,YAAY,EAAE,UAAU;AAChD,eAAO,oBAAoB,mBAG3B,oBAAoB;;AAEtB,cAAO;QACP,CAAC,KAAI,MAAK,CAAC,EAAE,WAAW,EAAE,UAAU,QAAO,aAAY,WAAW,EAAE,CAAC,QAAQ,KAAK,aAAa,MAAM,UAAU,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,OAAO,KAAK,IAAI,uBAAuB;AAChM,UAAI,UACF,kBAAiB;AAEnB;;KAEJ,KAAK;AACH,uBAAiB;AACjB;;AAGN,QAAI,cAAc,eAChB,QAAO,EACL,OAAO,EACL,WAAW,gBACZ,EACF;;AAGL,UAAO,EAAE;;EAEZ;;AA4MH,IAAM,8BAA2B,IAAI,IAAI,CAAC,QAAQ,MAAM,CAAC;AAKzD,eAAe,qBAAqB,OAAO,SAAS;CAClD,MAAM,EACJ,WACA,UACA,aACE;CACJ,MAAM,MAAM,OAAO,SAAS,SAAS,OAAO,KAAK,IAAI,SAAS,MAAM,SAAS,SAAS;CACtF,MAAM,OAAO,QAAQ,UAAU;CAC/B,MAAM,YAAY,aAAa,UAAU;CACzC,MAAM,aAAa,YAAY,UAAU,KAAK;CAC9C,MAAM,gBAAgB,YAAY,IAAI,KAAK,GAAG,KAAK;CACnD,MAAM,iBAAiB,OAAO,aAAa,KAAK;CAChD,MAAM,WAAW,SAAS,SAAS,MAAM;CAGzC,IAAI,EACF,UACA,WACA,kBACE,OAAO,aAAa,WAAW;EACjC,UAAU;EACV,WAAW;EACX,eAAe;EAChB,GAAG;EACF,UAAU,SAAS,YAAY;EAC/B,WAAW,SAAS,aAAa;EACjC,eAAe,SAAS;EACzB;AACD,KAAI,aAAa,OAAO,kBAAkB,SACxC,aAAY,cAAc,QAAQ,gBAAgB,KAAK;AAEzD,QAAO,aAAa;EAClB,GAAG,YAAY;EACf,GAAG,WAAW;EACf,GAAG;EACF,GAAG,WAAW;EACd,GAAG,YAAY;EAChB;;;;;;;;;AAUH,IAAMC,WAAS,SAAU,SAAS;AAChC,KAAI,YAAY,KAAK,EACnB,WAAU;AAEZ,QAAO;EACL,MAAM;EACN;EACA,MAAM,GAAG,OAAO;GACd,IAAI,uBAAuB;GAC3B,MAAM,EACJ,GACA,GACA,WACA,mBACE;GACJ,MAAM,aAAa,MAAM,qBAAqB,OAAO,QAAQ;AAI7D,OAAI,gBAAgB,wBAAwB,eAAe,WAAW,OAAO,KAAK,IAAI,sBAAsB,eAAe,wBAAwB,eAAe,UAAU,QAAQ,sBAAsB,gBACxM,QAAO,EAAE;AAEX,UAAO;IACL,GAAG,IAAI,WAAW;IAClB,GAAG,IAAI,WAAW;IAClB,MAAM;KACJ,GAAG;KACH;KACD;IACF;;EAEJ;;;;;;;AAQH,IAAMC,UAAQ,SAAU,SAAS;AAC/B,KAAI,YAAY,KAAK,EACnB,WAAU,EAAE;AAEd,QAAO;EACL,MAAM;EACN;EACA,MAAM,GAAG,OAAO;GACd,MAAM,EACJ,GACA,GACA,WACA,aACE;GACJ,MAAM,EACJ,UAAU,gBAAgB,MAC1B,WAAW,iBAAiB,OAC5B,UAAU,EACR,KAAI,SAAQ;IACV,IAAI,EACF,GACA,MACE;AACJ,WAAO;KACL;KACA;KACD;MAEJ,EACD,GAAG,0BACD,SAAS,SAAS,MAAM;GAC5B,MAAM,SAAS;IACb;IACA;IACD;GACD,MAAM,WAAW,MAAM,SAAS,eAAe,OAAO,sBAAsB;GAC5E,MAAM,YAAY,YAAY,QAAQ,UAAU,CAAC;GACjD,MAAM,WAAW,gBAAgB,UAAU;GAC3C,IAAI,gBAAgB,OAAO;GAC3B,IAAI,iBAAiB,OAAO;AAC5B,OAAI,eAAe;IACjB,MAAM,UAAU,aAAa,MAAM,QAAQ;IAC3C,MAAM,UAAU,aAAa,MAAM,WAAW;IAC9C,MAAM,MAAM,gBAAgB,SAAS;IACrC,MAAM,MAAM,gBAAgB,SAAS;AACrC,oBAAgB,MAAM,KAAK,eAAe,IAAI;;AAEhD,OAAI,gBAAgB;IAClB,MAAM,UAAU,cAAc,MAAM,QAAQ;IAC5C,MAAM,UAAU,cAAc,MAAM,WAAW;IAC/C,MAAM,MAAM,iBAAiB,SAAS;IACtC,MAAM,MAAM,iBAAiB,SAAS;AACtC,qBAAiB,MAAM,KAAK,gBAAgB,IAAI;;GAElD,MAAM,gBAAgB,QAAQ,GAAG;IAC/B,GAAG;KACF,WAAW;KACX,YAAY;IACd,CAAC;AACF,UAAO;IACL,GAAG;IACH,MAAM;KACJ,GAAG,cAAc,IAAI;KACrB,GAAG,cAAc,IAAI;KACrB,SAAS;OACN,WAAW;OACX,YAAY;MACd;KACF;IACF;;EAEJ;;;;ACt4BH,SAAS,YAAY;AACnB,QAAO,OAAO,WAAW;;AAE3B,SAAS,YAAY,MAAM;AACzB,KAAI,OAAO,KAAK,CACd,SAAQ,KAAK,YAAY,IAAI,aAAa;AAK5C,QAAO;;AAET,SAAS,UAAU,MAAM;CACvB,IAAI;AACJ,SAAQ,QAAQ,SAAS,sBAAsB,KAAK,kBAAkB,OAAO,KAAK,IAAI,oBAAoB,gBAAgB;;AAE5H,SAAS,mBAAmB,MAAM;CAChC,IAAI;AACJ,SAAQ,QAAQ,OAAO,KAAK,GAAG,KAAK,gBAAgB,KAAK,aAAa,OAAO,aAAa,OAAO,KAAK,IAAI,KAAK;;AAEjH,SAAS,OAAO,OAAO;AACrB,KAAI,CAAC,WAAW,CACd,QAAO;AAET,QAAO,iBAAiB,QAAQ,iBAAiB,UAAU,MAAM,CAAC;;AAEpE,SAAS,UAAU,OAAO;AACxB,KAAI,CAAC,WAAW,CACd,QAAO;AAET,QAAO,iBAAiB,WAAW,iBAAiB,UAAU,MAAM,CAAC;;AAEvE,SAAS,cAAc,OAAO;AAC5B,KAAI,CAAC,WAAW,CACd,QAAO;AAET,QAAO,iBAAiB,eAAe,iBAAiB,UAAU,MAAM,CAAC;;AAE3E,SAAS,aAAa,OAAO;AAC3B,KAAI,CAAC,WAAW,IAAI,OAAO,eAAe,YACxC,QAAO;AAET,QAAO,iBAAiB,cAAc,iBAAiB,UAAU,MAAM,CAAC;;AAE1E,SAAS,kBAAkB,SAAS;CAClC,MAAM,EACJ,UACA,WACA,WACA,YACEC,mBAAiB,QAAQ;AAC7B,QAAO,kCAAkC,KAAK,WAAW,YAAY,UAAU,IAAI,YAAY,YAAY,YAAY;;AAEzH,SAAS,eAAe,SAAS;AAC/B,QAAO,kBAAkB,KAAK,YAAY,QAAQ,CAAC;;AAErD,SAAS,WAAW,SAAS;AAC3B,KAAI;AACF,MAAI,QAAQ,QAAQ,gBAAgB,CAClC,QAAO;UAEF,IAAI;AAGb,KAAI;AACF,SAAO,QAAQ,QAAQ,SAAS;UACzB,IAAI;AACX,SAAO;;;AAGX,IAAM,eAAe;AACrB,IAAM,YAAY;AAClB,IAAM,aAAY,UAAS,CAAC,CAAC,SAAS,UAAU;AAChD,IAAI;AACJ,SAAS,kBAAkB,cAAc;CACvC,MAAM,MAAM,UAAU,aAAa,GAAGA,mBAAiB,aAAa,GAAG;AAIvE,QAAO,UAAU,IAAI,UAAU,IAAI,UAAU,IAAI,UAAU,IAAI,UAAU,IAAI,MAAM,IAAI,UAAU,IAAI,OAAO,IAAI,UAAU,IAAI,YAAY,IAAI,CAAC,UAAU,KAAK,UAAU,IAAI,eAAe,IAAI,UAAU,IAAI,OAAO,KAAK,aAAa,KAAK,IAAI,cAAc,GAAG,IAAI,UAAU,KAAK,IAAI,WAAW,GAAG;;AAEvS,SAAS,mBAAmB,SAAS;CACnC,IAAI,cAAc,cAAc,QAAQ;AACxC,QAAO,cAAc,YAAY,IAAI,CAAC,sBAAsB,YAAY,EAAE;AACxE,MAAI,kBAAkB,YAAY,CAChC,QAAO;WACE,WAAW,YAAY,CAChC,QAAO;AAET,gBAAc,cAAc,YAAY;;AAE1C,QAAO;;AAET,SAAS,WAAW;AAClB,KAAI,iBAAiB,KACnB,iBAAgB,OAAO,QAAQ,eAAe,IAAI,YAAY,IAAI,SAAS,2BAA2B,OAAO;AAE/G,QAAO;;AAET,SAAS,sBAAsB,MAAM;AACnC,QAAO,0BAA0B,KAAK,YAAY,KAAK,CAAC;;AAE1D,SAASA,mBAAiB,SAAS;AACjC,QAAO,UAAU,QAAQ,CAAC,iBAAiB,QAAQ;;AAErD,SAAS,cAAc,SAAS;AAC9B,KAAI,UAAU,QAAQ,CACpB,QAAO;EACL,YAAY,QAAQ;EACpB,WAAW,QAAQ;EACpB;AAEH,QAAO;EACL,YAAY,QAAQ;EACpB,WAAW,QAAQ;EACpB;;AAEH,SAAS,cAAc,MAAM;AAC3B,KAAI,YAAY,KAAK,KAAK,OACxB,QAAO;CAET,MAAM,SAEN,KAAK,gBAEL,KAAK,cAEL,aAAa,KAAK,IAAI,KAAK,QAE3B,mBAAmB,KAAK;AACxB,QAAO,aAAa,OAAO,GAAG,OAAO,OAAO;;AAE9C,SAAS,2BAA2B,MAAM;CACxC,MAAM,aAAa,cAAc,KAAK;AACtC,KAAI,sBAAsB,WAAW,CACnC,QAAO,KAAK,gBAAgB,KAAK,cAAc,OAAO,KAAK;AAE7D,KAAI,cAAc,WAAW,IAAI,kBAAkB,WAAW,CAC5D,QAAO;AAET,QAAO,2BAA2B,WAAW;;AAE/C,SAAS,qBAAqB,MAAM,MAAM,iBAAiB;CACzD,IAAI;AACJ,KAAI,SAAS,KAAK,EAChB,QAAO,EAAE;AAEX,KAAI,oBAAoB,KAAK,EAC3B,mBAAkB;CAEpB,MAAM,qBAAqB,2BAA2B,KAAK;CAC3D,MAAM,SAAS,yBAAyB,uBAAuB,KAAK,kBAAkB,OAAO,KAAK,IAAI,qBAAqB;CAC3H,MAAM,MAAM,UAAU,mBAAmB;AACzC,KAAI,QAAQ;EACV,MAAM,eAAe,gBAAgB,IAAI;AACzC,SAAO,KAAK,OAAO,KAAK,IAAI,kBAAkB,EAAE,EAAE,kBAAkB,mBAAmB,GAAG,qBAAqB,EAAE,EAAE,gBAAgB,kBAAkB,qBAAqB,aAAa,GAAG,EAAE,CAAC;OAE7L,QAAO,KAAK,OAAO,oBAAoB,qBAAqB,oBAAoB,EAAE,EAAE,gBAAgB,CAAC;;AAGzG,SAAS,gBAAgB,KAAK;AAC5B,QAAO,IAAI,UAAU,OAAO,eAAe,IAAI,OAAO,GAAG,IAAI,eAAe;;;;AC5J9E,SAAS,iBAAiB,SAAS;CACjC,MAAM,MAAM,mBAAmB,QAAQ;CAGvC,IAAI,QAAQ,WAAW,IAAI,MAAM,IAAI;CACrC,IAAI,SAAS,WAAW,IAAI,OAAO,IAAI;CACvC,MAAM,YAAY,cAAc,QAAQ;CACxC,MAAM,cAAc,YAAY,QAAQ,cAAc;CACtD,MAAM,eAAe,YAAY,QAAQ,eAAe;CACxD,MAAM,iBAAiB,MAAM,MAAM,KAAK,eAAe,MAAM,OAAO,KAAK;AACzE,KAAI,gBAAgB;AAClB,UAAQ;AACR,WAAS;;AAEX,QAAO;EACL;EACA;EACA,GAAG;EACJ;;AAGH,SAAS,cAAc,SAAS;AAC9B,QAAO,CAAC,UAAU,QAAQ,GAAG,QAAQ,iBAAiB;;AAGxD,SAAS,SAAS,SAAS;CACzB,MAAM,aAAa,cAAc,QAAQ;AACzC,KAAI,CAAC,cAAc,WAAW,CAC5B,QAAO,aAAa,EAAE;CAExB,MAAM,OAAO,WAAW,uBAAuB;CAC/C,MAAM,EACJ,OACA,QACA,MACE,iBAAiB,WAAW;CAChC,IAAI,KAAK,IAAI,MAAM,KAAK,MAAM,GAAG,KAAK,SAAS;CAC/C,IAAI,KAAK,IAAI,MAAM,KAAK,OAAO,GAAG,KAAK,UAAU;AAIjD,KAAI,CAAC,KAAK,CAAC,OAAO,SAAS,EAAE,CAC3B,KAAI;AAEN,KAAI,CAAC,KAAK,CAAC,OAAO,SAAS,EAAE,CAC3B,KAAI;AAEN,QAAO;EACL;EACA;EACD;;AAGH,IAAM,YAAyB,6BAAa,EAAE;AAC9C,SAAS,iBAAiB,SAAS;CACjC,MAAM,MAAM,UAAU,QAAQ;AAC9B,KAAI,CAAC,UAAU,IAAI,CAAC,IAAI,eACtB,QAAO;AAET,QAAO;EACL,GAAG,IAAI,eAAe;EACtB,GAAG,IAAI,eAAe;EACvB;;AAEH,SAAS,uBAAuB,SAAS,SAAS,sBAAsB;AACtE,KAAI,YAAY,KAAK,EACnB,WAAU;AAEZ,KAAI,CAAC,wBAAwB,WAAW,yBAAyB,UAAU,QAAQ,CACjF,QAAO;AAET,QAAO;;AAGT,SAAS,sBAAsB,SAAS,cAAc,iBAAiB,cAAc;AACnF,KAAI,iBAAiB,KAAK,EACxB,gBAAe;AAEjB,KAAI,oBAAoB,KAAK,EAC3B,mBAAkB;CAEpB,MAAM,aAAa,QAAQ,uBAAuB;CAClD,MAAM,aAAa,cAAc,QAAQ;CACzC,IAAI,QAAQ,aAAa,EAAE;AAC3B,KAAI,aACF,KAAI;MACE,UAAU,aAAa,CACzB,SAAQ,SAAS,aAAa;OAGhC,SAAQ,SAAS,QAAQ;CAG7B,MAAM,gBAAgB,uBAAuB,YAAY,iBAAiB,aAAa,GAAG,iBAAiB,WAAW,GAAG,aAAa,EAAE;CACxI,IAAI,KAAK,WAAW,OAAO,cAAc,KAAK,MAAM;CACpD,IAAI,KAAK,WAAW,MAAM,cAAc,KAAK,MAAM;CACnD,IAAI,QAAQ,WAAW,QAAQ,MAAM;CACrC,IAAI,SAAS,WAAW,SAAS,MAAM;AACvC,KAAI,YAAY;EACd,MAAM,MAAM,UAAU,WAAW;EACjC,MAAM,YAAY,gBAAgB,UAAU,aAAa,GAAG,UAAU,aAAa,GAAG;EACtF,IAAI,aAAa;EACjB,IAAI,gBAAgB,gBAAgB,WAAW;AAC/C,SAAO,iBAAiB,gBAAgB,cAAc,YAAY;GAChE,MAAM,cAAc,SAAS,cAAc;GAC3C,MAAM,aAAa,cAAc,uBAAuB;GACxD,MAAM,MAAM,mBAAmB,cAAc;GAC7C,MAAM,OAAO,WAAW,QAAQ,cAAc,aAAa,WAAW,IAAI,YAAY,IAAI,YAAY;GACtG,MAAM,MAAM,WAAW,OAAO,cAAc,YAAY,WAAW,IAAI,WAAW,IAAI,YAAY;AAClG,QAAK,YAAY;AACjB,QAAK,YAAY;AACjB,YAAS,YAAY;AACrB,aAAU,YAAY;AACtB,QAAK;AACL,QAAK;AACL,gBAAa,UAAU,cAAc;AACrC,mBAAgB,gBAAgB,WAAW;;;AAG/C,QAAO,iBAAiB;EACtB;EACA;EACA;EACA;EACD,CAAC;;AAKJ,SAAS,oBAAoB,SAAS,MAAM;CAC1C,MAAM,aAAa,cAAc,QAAQ,CAAC;AAC1C,KAAI,CAAC,KACH,QAAO,sBAAsB,mBAAmB,QAAQ,CAAC,CAAC,OAAO;AAEnE,QAAO,KAAK,OAAO;;AAGrB,SAAS,cAAc,iBAAiB,QAAQ;CAC9C,MAAM,WAAW,gBAAgB,uBAAuB;AAGxD,QAAO;EACL,GAHQ,SAAS,OAAO,OAAO,aAAa,oBAAoB,iBAAiB,SAAS;EAI1F,GAHQ,SAAS,MAAM,OAAO;EAI/B;;AAGH,SAAS,sDAAsD,MAAM;CACnE,IAAI,EACF,UACA,MACA,cACA,aACE;CACJ,MAAM,UAAU,aAAa;CAC7B,MAAM,kBAAkB,mBAAmB,aAAa;CACxD,MAAM,WAAW,WAAW,WAAW,SAAS,SAAS,GAAG;AAC5D,KAAI,iBAAiB,mBAAmB,YAAY,QAClD,QAAO;CAET,IAAI,SAAS;EACX,YAAY;EACZ,WAAW;EACZ;CACD,IAAI,QAAQ,aAAa,EAAE;CAC3B,MAAM,UAAU,aAAa,EAAE;CAC/B,MAAM,0BAA0B,cAAc,aAAa;AAC3D,KAAI,2BAA2B,CAAC,2BAA2B,CAAC,SAAS;AACnE,MAAI,YAAY,aAAa,KAAK,UAAU,kBAAkB,gBAAgB,CAC5E,UAAS,cAAc,aAAa;AAEtC,MAAI,yBAAyB;GAC3B,MAAM,aAAa,sBAAsB,aAAa;AACtD,WAAQ,SAAS,aAAa;AAC9B,WAAQ,IAAI,WAAW,IAAI,aAAa;AACxC,WAAQ,IAAI,WAAW,IAAI,aAAa;;;CAG5C,MAAM,aAAa,mBAAmB,CAAC,2BAA2B,CAAC,UAAU,cAAc,iBAAiB,OAAO,GAAG,aAAa,EAAE;AACrI,QAAO;EACL,OAAO,KAAK,QAAQ,MAAM;EAC1B,QAAQ,KAAK,SAAS,MAAM;EAC5B,GAAG,KAAK,IAAI,MAAM,IAAI,OAAO,aAAa,MAAM,IAAI,QAAQ,IAAI,WAAW;EAC3E,GAAG,KAAK,IAAI,MAAM,IAAI,OAAO,YAAY,MAAM,IAAI,QAAQ,IAAI,WAAW;EAC3E;;AAGH,SAAS,eAAe,SAAS;AAC/B,QAAO,MAAM,KAAK,QAAQ,gBAAgB,CAAC;;AAK7C,SAAS,gBAAgB,SAAS;CAChC,MAAM,OAAO,mBAAmB,QAAQ;CACxC,MAAM,SAAS,cAAc,QAAQ;CACrC,MAAM,OAAO,QAAQ,cAAc;CACnC,MAAM,QAAQ,IAAI,KAAK,aAAa,KAAK,aAAa,KAAK,aAAa,KAAK,YAAY;CACzF,MAAM,SAAS,IAAI,KAAK,cAAc,KAAK,cAAc,KAAK,cAAc,KAAK,aAAa;CAC9F,IAAI,IAAI,CAAC,OAAO,aAAa,oBAAoB,QAAQ;CACzD,MAAM,IAAI,CAAC,OAAO;AAClB,KAAI,mBAAmB,KAAK,CAAC,cAAc,MACzC,MAAK,IAAI,KAAK,aAAa,KAAK,YAAY,GAAG;AAEjD,QAAO;EACL;EACA;EACA;EACA;EACD;;AAMH,IAAM,gBAAgB;AACtB,SAAS,gBAAgB,SAAS,UAAU;CAC1C,MAAM,MAAM,UAAU,QAAQ;CAC9B,MAAM,OAAO,mBAAmB,QAAQ;CACxC,MAAM,iBAAiB,IAAI;CAC3B,IAAI,QAAQ,KAAK;CACjB,IAAI,SAAS,KAAK;CAClB,IAAI,IAAI;CACR,IAAI,IAAI;AACR,KAAI,gBAAgB;AAClB,UAAQ,eAAe;AACvB,WAAS,eAAe;EACxB,MAAM,sBAAsB,UAAU;AACtC,MAAI,CAAC,uBAAuB,uBAAuB,aAAa,SAAS;AACvE,OAAI,eAAe;AACnB,OAAI,eAAe;;;CAGvB,MAAM,mBAAmB,oBAAoB,KAAK;AAIlD,KAAI,oBAAoB,GAAG;EACzB,MAAM,MAAM,KAAK;EACjB,MAAM,OAAO,IAAI;EACjB,MAAM,aAAa,iBAAiB,KAAK;EACzC,MAAM,mBAAmB,IAAI,eAAe,eAAe,WAAW,WAAW,WAAW,GAAG,WAAW,WAAW,YAAY,IAAI,IAAI;EACzI,MAAM,+BAA+B,KAAK,IAAI,KAAK,cAAc,KAAK,cAAc,iBAAiB;AACrG,MAAI,gCAAgC,cAClC,UAAS;YAEF,oBAAoB,cAG7B,UAAS;AAEX,QAAO;EACL;EACA;EACA;EACA;EACD;;AAIH,SAAS,2BAA2B,SAAS,UAAU;CACrD,MAAM,aAAa,sBAAsB,SAAS,MAAM,aAAa,QAAQ;CAC7E,MAAM,MAAM,WAAW,MAAM,QAAQ;CACrC,MAAM,OAAO,WAAW,OAAO,QAAQ;CACvC,MAAM,QAAQ,cAAc,QAAQ,GAAG,SAAS,QAAQ,GAAG,aAAa,EAAE;AAK1E,QAAO;EACL,OALY,QAAQ,cAAc,MAAM;EAMxC,QALa,QAAQ,eAAe,MAAM;EAM1C,GALQ,OAAO,MAAM;EAMrB,GALQ,MAAM,MAAM;EAMrB;;AAEH,SAAS,kCAAkC,SAAS,kBAAkB,UAAU;CAC9E,IAAI;AACJ,KAAI,qBAAqB,WACvB,QAAO,gBAAgB,SAAS,SAAS;UAChC,qBAAqB,WAC9B,QAAO,gBAAgB,mBAAmB,QAAQ,CAAC;UAC1C,UAAU,iBAAiB,CACpC,QAAO,2BAA2B,kBAAkB,SAAS;MACxD;EACL,MAAM,gBAAgB,iBAAiB,QAAQ;AAC/C,SAAO;GACL,GAAG,iBAAiB,IAAI,cAAc;GACtC,GAAG,iBAAiB,IAAI,cAAc;GACtC,OAAO,iBAAiB;GACxB,QAAQ,iBAAiB;GAC1B;;AAEH,QAAO,iBAAiB,KAAK;;AAE/B,SAAS,yBAAyB,SAAS,UAAU;CACnD,MAAM,aAAa,cAAc,QAAQ;AACzC,KAAI,eAAe,YAAY,CAAC,UAAU,WAAW,IAAI,sBAAsB,WAAW,CACxF,QAAO;AAET,QAAO,mBAAmB,WAAW,CAAC,aAAa,WAAW,yBAAyB,YAAY,SAAS;;AAM9G,SAAS,4BAA4B,SAAS,OAAO;CACnD,MAAM,eAAe,MAAM,IAAI,QAAQ;AACvC,KAAI,aACF,QAAO;CAET,IAAI,SAAS,qBAAqB,SAAS,EAAE,EAAE,MAAM,CAAC,QAAO,OAAM,UAAU,GAAG,IAAI,YAAY,GAAG,KAAK,OAAO;CAC/G,IAAI,sCAAsC;CAC1C,MAAM,iBAAiB,mBAAmB,QAAQ,CAAC,aAAa;CAChE,IAAI,cAAc,iBAAiB,cAAc,QAAQ,GAAG;AAG5D,QAAO,UAAU,YAAY,IAAI,CAAC,sBAAsB,YAAY,EAAE;EACpE,MAAM,gBAAgB,mBAAmB,YAAY;EACrD,MAAM,0BAA0B,kBAAkB,YAAY;AAC9D,MAAI,CAAC,2BAA2B,cAAc,aAAa,QACzD,uCAAsC;AAGxC,MAD8B,iBAAiB,CAAC,2BAA2B,CAAC,sCAAsC,CAAC,2BAA2B,cAAc,aAAa,YAAY,CAAC,CAAC,wCAAwC,oCAAoC,aAAa,cAAc,oCAAoC,aAAa,YAAY,kBAAkB,YAAY,IAAI,CAAC,2BAA2B,yBAAyB,SAAS,YAAY,CAGrc,UAAS,OAAO,QAAO,aAAY,aAAa,YAAY;MAG5D,uCAAsC;AAExC,gBAAc,cAAc,YAAY;;AAE1C,OAAM,IAAI,SAAS,OAAO;AAC1B,QAAO;;AAKT,SAAS,gBAAgB,MAAM;CAC7B,IAAI,EACF,SACA,UACA,cACA,aACE;CAEJ,MAAM,oBAAoB,CAAC,GADM,aAAa,sBAAsB,WAAW,QAAQ,GAAG,EAAE,GAAG,4BAA4B,SAAS,KAAK,GAAG,GAAG,EAAE,CAAC,OAAO,SAAS,EAC1G,aAAa;CACrE,MAAM,YAAY,kCAAkC,SAAS,kBAAkB,IAAI,SAAS;CAC5F,IAAI,MAAM,UAAU;CACpB,IAAI,QAAQ,UAAU;CACtB,IAAI,SAAS,UAAU;CACvB,IAAI,OAAO,UAAU;AACrB,MAAK,IAAI,IAAI,GAAG,IAAI,kBAAkB,QAAQ,KAAK;EACjD,MAAM,OAAO,kCAAkC,SAAS,kBAAkB,IAAI,SAAS;AACvF,QAAM,IAAI,KAAK,KAAK,IAAI;AACxB,UAAQ,IAAI,KAAK,OAAO,MAAM;AAC9B,WAAS,IAAI,KAAK,QAAQ,OAAO;AACjC,SAAO,IAAI,KAAK,MAAM,KAAK;;AAE7B,QAAO;EACL,OAAO,QAAQ;EACf,QAAQ,SAAS;EACjB,GAAG;EACH,GAAG;EACJ;;AAGH,SAAS,cAAc,SAAS;CAC9B,MAAM,EACJ,OACA,WACE,iBAAiB,QAAQ;AAC7B,QAAO;EACL;EACA;EACD;;AAGH,SAAS,8BAA8B,SAAS,cAAc,UAAU;CACtE,MAAM,0BAA0B,cAAc,aAAa;CAC3D,MAAM,kBAAkB,mBAAmB,aAAa;CACxD,MAAM,UAAU,aAAa;CAC7B,MAAM,OAAO,sBAAsB,SAAS,MAAM,SAAS,aAAa;CACxE,IAAI,SAAS;EACX,YAAY;EACZ,WAAW;EACZ;CACD,MAAM,UAAU,aAAa,EAAE;CAI/B,SAAS,4BAA4B;AACnC,UAAQ,IAAI,oBAAoB,gBAAgB;;AAElD,KAAI,2BAA2B,CAAC,2BAA2B,CAAC,SAAS;AACnE,MAAI,YAAY,aAAa,KAAK,UAAU,kBAAkB,gBAAgB,CAC5E,UAAS,cAAc,aAAa;AAEtC,MAAI,yBAAyB;GAC3B,MAAM,aAAa,sBAAsB,cAAc,MAAM,SAAS,aAAa;AACnF,WAAQ,IAAI,WAAW,IAAI,aAAa;AACxC,WAAQ,IAAI,WAAW,IAAI,aAAa;aAC/B,gBACT,4BAA2B;;AAG/B,KAAI,WAAW,CAAC,2BAA2B,gBACzC,4BAA2B;CAE7B,MAAM,aAAa,mBAAmB,CAAC,2BAA2B,CAAC,UAAU,cAAc,iBAAiB,OAAO,GAAG,aAAa,EAAE;AAGrI,QAAO;EACL,GAHQ,KAAK,OAAO,OAAO,aAAa,QAAQ,IAAI,WAAW;EAI/D,GAHQ,KAAK,MAAM,OAAO,YAAY,QAAQ,IAAI,WAAW;EAI7D,OAAO,KAAK;EACZ,QAAQ,KAAK;EACd;;AAGH,SAAS,mBAAmB,SAAS;AACnC,QAAO,mBAAmB,QAAQ,CAAC,aAAa;;AAGlD,SAAS,oBAAoB,SAAS,UAAU;AAC9C,KAAI,CAAC,cAAc,QAAQ,IAAI,mBAAmB,QAAQ,CAAC,aAAa,QACtE,QAAO;AAET,KAAI,SACF,QAAO,SAAS,QAAQ;CAE1B,IAAI,kBAAkB,QAAQ;AAM9B,KAAI,mBAAmB,QAAQ,KAAK,gBAClC,mBAAkB,gBAAgB,cAAc;AAElD,QAAO;;AAKT,SAAS,gBAAgB,SAAS,UAAU;CAC1C,MAAM,MAAM,UAAU,QAAQ;AAC9B,KAAI,WAAW,QAAQ,CACrB,QAAO;AAET,KAAI,CAAC,cAAc,QAAQ,EAAE;EAC3B,IAAI,kBAAkB,cAAc,QAAQ;AAC5C,SAAO,mBAAmB,CAAC,sBAAsB,gBAAgB,EAAE;AACjE,OAAI,UAAU,gBAAgB,IAAI,CAAC,mBAAmB,gBAAgB,CACpE,QAAO;AAET,qBAAkB,cAAc,gBAAgB;;AAElD,SAAO;;CAET,IAAI,eAAe,oBAAoB,SAAS,SAAS;AACzD,QAAO,gBAAgB,eAAe,aAAa,IAAI,mBAAmB,aAAa,CACrF,gBAAe,oBAAoB,cAAc,SAAS;AAE5D,KAAI,gBAAgB,sBAAsB,aAAa,IAAI,mBAAmB,aAAa,IAAI,CAAC,kBAAkB,aAAa,CAC7H,QAAO;AAET,QAAO,gBAAgB,mBAAmB,QAAQ,IAAI;;AAGxD,IAAM,kBAAkB,eAAgB,MAAM;CAC5C,MAAM,oBAAoB,KAAK,mBAAmB;CAClD,MAAM,kBAAkB,KAAK;CAC7B,MAAM,qBAAqB,MAAM,gBAAgB,KAAK,SAAS;AAC/D,QAAO;EACL,WAAW,8BAA8B,KAAK,WAAW,MAAM,kBAAkB,KAAK,SAAS,EAAE,KAAK,SAAS;EAC/G,UAAU;GACR,GAAG;GACH,GAAG;GACH,OAAO,mBAAmB;GAC1B,QAAQ,mBAAmB;GAC5B;EACF;;AAGH,SAAS,MAAM,SAAS;AACtB,QAAO,mBAAmB,QAAQ,CAAC,cAAc;;AAGnD,IAAM,WAAW;CACf;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;AAgMD,IAAM,SAAS;;;;;;AAef,IAAM,QAAQ;;;;;;;AAQd,IAAM,OAAO;;;;;AAwCb,IAAM,mBAAmB,WAAW,UAAU,YAAY;CAIxD,MAAM,wBAAQ,IAAI,KAAK;CACvB,MAAM,gBAAgB;EACpB;EACA,GAAG;EACJ;CACD,MAAM,oBAAoB;EACxB,GAAG,cAAc;EACjB,IAAI;EACL;AACD,QAAO,kBAAkB,WAAW,UAAU;EAC5C,GAAG;EACH,UAAU;EACX,CAAC;;;;;;;;;;;;;;;;;;;;;;;EE3vBJ,MAAM,QAAQ;EAOd,MAAM,YAAY,gBAAgB,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;EACxE,MAAM,UAAU,IAAI,MAAK;EACzB,MAAM,kBAAkB,IAAe,MAAM,SAAQ;EACrD,MAAM,aAAa,IAAwB,KAAI;EAC/C,MAAM,aAAa,IAAwB,KAAI;EAC/C,MAAM,SAAS,IAAI;GAAE,KAAK;GAAO,MAAM;GAAO,CAAA;EAC9C,IAAI;EAEJ,eAAe,iBAAiB;AAC9B,OAAI,CAAC,WAAW,SAAS,CAAC,WAAW,MAAO;GAE5C,MAAM,EAAE,GAAG,GAAG,cAAc,MAAM,gBAAgB,WAAW,OAAO,WAAW,OAAO;IACpF,WAAW,MAAM;IACjB,YAAY;KACV,OAAO,EAAE;KACT,MAAM;KACN,MAAM,EAAE,SAAS,GAAG,CAAC;KACtB;IACF,CAAA;AAED,UAAO,QAAQ;IAAE,KAAK,GAAG,EAAE;IAAK,MAAM,GAAG,EAAE;IAAI;AAC/C,mBAAgB,QAAQ;;AAG1B,yBAAuB,QAAQ,UAAU,gBAAgB;GACvD,SAAS;GACT,SAAS;GACT,SAAS;GACV,CAAA;AACD,yBAAuB,QAAQ,UAAU,gBAAgB,EAAE,SAAS,SAAS,CAAA;EAE7E,eAAe,OAAO;AACpB,eAAY,WAAW,YAAY;AACjC,YAAQ,QAAQ;AAChB,UAAM,UAAS;AACf,UAAM,gBAAe;MACpB,MAAM,MAAK;;EAGhB,SAAS,OAAO;AACd,gBAAa,UAAS;AACtB,eAAY,KAAA;AACZ,WAAQ,QAAQ;;AAGlB,wBAAsB;AACpB,gBAAa,UAAS;IACvB;EAED,SAAS,iBAAiB,OAAwD;AAChF,OAAI,UAAU,KAAA,EAAW,QAAO,KAAA;AAChC,UAAO,OAAO,UAAU,WAAW,GAAG,MAAM,MAAM;;;uBAKlD,mBA+BO,QAAA;aA9BD;IAAJ,KAAI;IACJ,OAAM;IACL,oBAAkB,QAAA,QAAU,YAAY,KAAA;IACxC,cAAY;IACZ,cAAY;IACZ,WAAS;IACT,YAAU;OAEX,WAAQ,KAAA,QAAA,UAAA,GAAA,WAAA,EACR,YAoBW,UAAA,EApBD,IAAG,QAAM,EAAA,CAET,QAAA,SAAA,WAAA,EADR,mBAkBO,QAAA;;IAhBJ,IAAI;aACD;IAAJ,KAAI;IACH,OAAK,eAAA;;+BAA2E,gBAAA;KAA6B,QAAA,WAAQ,qCAAA;;IAKrH,OAAK,eAAA;UAAmB,OAAA,MAAO;WAAqB,OAAA,MAAO;eAA0B,iBAAiB,QAAA,SAAQ;;IAK/G,MAAK;OAEL,mBAAkD,QAAlD,eAAkD,gBAAd,QAAA,KAAI,EAAA,EAAA,EAC7B,QAAA,YAAA,WAAA,EAAX,mBAAwE,OAAxE,eAAwE,gBAAjB,QAAA,SAAQ,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,EAAA,EAAA,IAAA,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uBEpFrE,mBAqCM,OArCN,eAqCM;IApCO,QAAA,SAAS,QAAA,eAAeC,KAAAA,OAAO,WAAA,WAAA,EAA1C,mBAQM,OARN,eAQM,CAPO,QAAA,SAAS,QAAA,eAAA,WAAA,EAApB,mBAGM,OAHN,eAGM,CAFM,QAAA,SAAA,WAAA,EAAV,mBAA2D,MAA3D,eAA2D,gBAAb,QAAA,MAAK,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,EAC1C,QAAA,eAAA,WAAA,EAAT,mBAA2E,KAA3E,eAA2E,gBAAlB,QAAA,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,EAE3DA,KAAAA,OAAO,WAAA,WAAA,EAAlB,mBAEM,OAFN,eAEM,CADJ,WAAuB,KAAA,QAAA,UAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAI3B,mBAqBM,OArBN,eAqBM,CAnBO,QAAA,WAAA,WAAA,EAAX,mBAEM,OAFN,eAEM,CADJ,YAA4B,wBAAA,EAAZ,MAAK,MAAI,CAAA,CAAA,CAAA,IAIN,QAAA,QACnB,WAMO,KAAA,QAAA,SAAA,EAAA,KAAA,GAAA,QAAA,CALL,YAIE,oBAAA;KAHC,OAAO,QAAA;KACR,MAAK;KACL,aAAU;+BAOd,WAAQ,KAAA,QAAA,WAAA,EAAA,KAAA,GAAA,CAAA,CAAA,CAAA;IAIDA,KAAAA,OAAO,UAAA,WAAA,EAAlB,mBAEM,OAFN,eAEM,CADJ,WAAsB,KAAA,QAAA,SAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEzB5B,MAAM,QAAQ;EAUd,MAAM,oBAAoB,eAAe,+BAA+B,MAAM,OAAO,CAAA;;uBAInF,YAUE,kCAAA;IATA,OAAM;IACL,UAAU,kBAAA;IACV,SAAS,QAAA;IACT,SAAS,QAAA;IACT,OAAO,QAAA;IACP,UAAU,QAAA;IACV,gBAAc,QAAA;IACd,qBAAmB,QAAA;IACnB,cAAY,QAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEqBjB,MAAM,QAAQ;EAoBd,MAAM,OAAO;EAMb,MAAM,gBAAgB,eAAe;AACnC,OAAI,MAAM,QAAS,QAAO,MAAM;AAChC,OAAI,MAAM,SAAS,aACjB,QAAO,GAAG,MAAM,MAAM;AAExB,UAAO,GAAG,MAAM,MAAM;IACvB;EAED,MAAM,kBAAkB,eAAe;AACrC,OAAI,MAAM,QAAQ,YAAY,KAAA,KAAa,MAAM,QAAQ,WAAW,KAAA,EAClE,QAAO,QAAQ,MAAM,OAAO,QAAQ,IAAI,QAAQ,MAAM,OAAO,OAAM;AAErE,UAAO,MAAM;IACd;EACD,MAAM,gBAAgB,eACpB,MAAM,QAAQ,UAAU,KAAA,IAAY,MAAM,OAAO,QAAQ,MAAM,MACjE;EACA,MAAM,wBAAwB,eAC5B,MAAM,QAAQ,iBAAiB,MAAM,cACvC;EACA,MAAM,8BAA8B,eAClC,MAAM,QAAQ,uBAAuB,MAAM,oBAC7C;EACA,MAAM,uBAAuB,eAC3B,MAAM,QAAQ,iBAAiB,KAAA,IAAY,MAAM,OAAO,eAAe,MAAM,aAC/E;EACA,MAAM,sBAAsB,eAC1B,MAAM,QAAQ,gBAAgB,KAAA,IAAY,MAAM,OAAO,cAAc,MAAM,YAC7E;EAEA,MAAM,oBAAoB,eACxB,OAAO,OAAO,0BAA0B,MAAM,OAAO,CAAC,CAAC,KAAI,cAAa;GACtE,IAAI,OAAO,SAAS,YAAY;GAChC,SAAS,OAAO,SAAS,oBAAoB,UAAU;GACxD,EAAC,CACJ;EACA,MAAM,iBAAiB,eAAe,4BAA4B,MAAM,OAAO,CAAA;EAC/E,MAAM,wBAAwB,eAAe,mCAAmC,MAAM,OAAO,CAAA;EAC7F,MAAM,qBAAqB,eAAe,gCAAgC,MAAM,OAAO,CAAA;EACvF,MAAM,4BAA4B,eAAe,uCAAuC,MAAM,OAAO,CAAA;EACrG,MAAM,WAAW,gBAAgB,EAAE,QAAQ,MAAM,QAAQ,EAAC;EAE1D,SAAS,kBAAkB,WAAmB,SAAkD;AAC9F,UAAO,6BAA6B,MAAM,QAAQ,WAAW,QAAO;;EAYtE,MAAM,WAAW,gBAAwD;GACvE,UAAU,SAAS;GACnB,gBAAgB,eAAe;GAC/B,uBAAuB,sBAAsB;GAC7C,oBAAoB,mBAAmB;GACvC,2BAA2B,0BAA0B;GACrD;GACD,EAAC;EAgBF,SAAS,aAAa;AACpB,OAAI,MAAM,SAAS,KACjB,QAAO,MAAM,QAAQ,MAAK;AAE5B,QAAK,OAAM;;EAGb,SAAS,cAAc;AACrB,OAAI,MAAM,SAAS,MACjB,QAAO,MAAM,QAAQ,OAAM;AAE7B,QAAK,QAAO;;EAGd,SAAS,aAAa;AACpB,OAAI,MAAM,SAAS,KACjB,QAAO,MAAM,QAAQ,MAAK;AAE5B,QAAK,OAAM;;;uBAKX,mBA2EM,OA3EN,eA2EM;IA1EJ,mBA0BM,OA1BN,eA0BM,CAzBJ,YAEW,kBAAA;KAFD,MAAK;KAAO,OAAM;;4BACP,CAAA,gBAAA,gBAAhB,cAAA,MAAa,EAAA,EAAA,CAAA,CAAA;;QAGlB,mBAoBM,OApBN,eAoBM;KAnBJ,YAOa,oBAAA;MANX,SAAQ;MACP,SAAS,gBAAA;MACT,UAAQ,CAAG,sBAAA;MACX,SAAO;;6BAEO,CAAA,gBAAA,gBAAZ,QAAA,UAAS,EAAA,EAAA,CAAA,CAAA;;;KAEd,YAEa,oBAAA;MAFD,SAAQ;MAAa,SAAS,gBAAA;MAAkB,SAAO;;6BACjD,CAAA,gBAAA,gBAAb,QAAA,WAAU,EAAA,EAAA,CAAA,CAAA;;;KAEf,YAOa,oBAAA;MANX,SAAQ;MACP,SAAS,gBAAA;MACT,UAAQ,CAAG,sBAAA;MACX,SAAO;;6BAEO,CAAA,gBAAA,gBAAZ,QAAA,UAAS,EAAA,EAAA,CAAA,CAAA;;;;IAKF,cAAA,SAAA,WAAA,EAAhB,YAEW,kBAAA;;KAFoB,MAAK;;4BACf,CAAA,gBAAA,gBAAhB,cAAA,MAAa,EAAA,EAAA,CAAA,CAAA;;;IAGT,QAAA,cAAA,WAAA,EAAT,mBAGI,KAHJ,eAA0E,cAChE,gBAAG,qBAAA,OAAsB,gBAAc,IAAA,aAAA,GAAqB,eAC7D,gBAAG,oBAAA,OAAqB,gBAAc,IAAA,YAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;IAIvC,QAAA,uBAAA,WAAA,EADR,mBAgBM,OAhBN,eAgBM,EAAA,UAAA,KAAA,EAZJ,mBAWM,UAAA,MAAA,WAVe,kBAAA,QAAZ,aAAQ;yBADjB,mBAWM,OAAA;MATH,KAAK,SAAS;MACf,OAAM;SAEN,mBAEI,KAFJ,eAEI,gBADC,SAAS,GAAE,EAAA,EAAA,EAEhB,mBAEI,KAFJ,eAAoE,OACjE,gBAAG,SAAS,QAAO,EAAA,EAAA,CAAA,CAAA;;IAK1B,WAcO,KAAA,QAAA,WAAA;KAbJ,QAAQ,QAAA;KACR,UAAU,SAAA;KACV,gBAAiB,eAAA;KACjB,uBAA0B,sBAAA;KAC1B,oBAAuB,mBAAA;KACvB,2BAA8B,0BAAA;KACT;aAOjB,CALL,YAIE,6BAAA;KAHC,QAAQ,QAAA;KACR,OAAO,QAAA;KACP,UAAU,QAAA;;;;;;IAIf,mBAEI,KAFJ,eAAwD,kBAC1C,gBAAG,4BAAA,SAA2B,eAAA,EAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE3MhD,MAAM,QAAQ;EAqBd,MAAM,QAAQ,UAAS;EACvB,MAAM,iBAAiB,eAAe,QAAQ,MAAM,QAAQ,CAAA;EAE5D,IAAI;EAEJ,SAAS,2BAA8D;AACrE,4BAAyB,MAAK;AAC9B,6BAA0B,aAAY;AACtC,UAAO,wBAAwB,UAAU,4BAA4B,MAAM,MAAM,MAAM,iBAAiB,CAAC;;EAG3G,MAAM,qBAAqB,WAA8C,0BAA0B,CAAA;EACnG,MAAM,oBAAoB,eAAe,MAAM,aAAa,mBAAmB,MAAK;EACpF,MAAM,SAAS,eAAe,MAAM,kBAAkB,MAAM,WAAW,CAAA;EACvE,MAAM,QAAQ,eAAe,MAAM,SAAS,kBAAkB,MAAM,KAAK,MAAK;EAC9E,MAAM,OAAO,eAAe,MAAM,kBAAkB,MAAM,KAAK,CAAA;EAC/D,MAAM,UAAU,eAAe,MAAM,kBAAkB,MAAM,QAAQ,CAAA;EACrE,MAAM,SAAS,eAAe,MAAM,kBAAkB,MAAM,OAAO,CAAA;EACnE,MAAM,UAAU,eAAe,MAAM,kBAAkB,MAAM,QAAQ,CAAA;EACrE,MAAM,iBAAiB,eAAe,MAAM,kBAAkB,MAAM,eAAe,CAAA;EACnF,MAAM,WAAW,eAAe,MAAM,kBAAkB,MAAM,SAAS,CAAA;EACvE,MAAM,iBAAiB,gBAAqC;GAC1D,SAAS,MAAM,QAAQ,WAAW,MAAM,kBAAkB,MAAM,QAAQ;GACxE,QAAQ,MAAM,QAAQ,UAAU,MAAM,kBAAkB,MAAM,OAAO;GACrE,OAAO,MAAM,QAAQ,UAAU,KAAA,IAC3B,MAAM,OAAO,QACb,MAAM,kBAAkB,MAAM,MAAM;GACxC,eAAe,MAAM,QAAQ,iBAAiB,MAAM,kBAAkB,MAAM,qBAAqB;GACjG,qBAAqB,MAAM,QAAQ,uBAAuB,MAAM,kBAAkB,MAAM,oBAAoB;GAC5G,cAAc,MAAM,QAAQ,iBAAiB,KAAA,IACzC,MAAM,OAAO,eACb,MAAM,kBAAkB,MAAM,aAAa;GAC/C,aAAa,MAAM,QAAQ,gBAAgB,KAAA,IACvC,MAAM,OAAO,cACb,MAAM,kBAAkB,MAAM,YAAY;GAC/C,EAAC;EACF,MAAM,kBAAkB,gBAAsC;GAC5D,MAAM,MAAM,SAAS,eAAe,kBAAkB,MAAM,aAAa;GACzE,OAAO,MAAM,SAAS,gBAAgB,kBAAkB,MAAM,0BAA0B;GACxF,MAAM,MAAM,SAAS,eAAe,kBAAkB,MAAM,aAAa;GAC1E,EAAC;AAEF,QACE,OAAO,MAAM,YAAY,MAAM,iBAAiB,QAC1C;AACJ,sBAAmB,QAAQ,0BAAyB;KAEtD,EAAE,MAAM,MAAM,CAChB;AAEA,uBAAqB,yBAAyB,MAAM,CAAA;;uBAIlD,YA4BqC,4CAAA;IA3BlC,QAAQ,OAAA;IACT,MAAK;IACJ,OAAO,MAAA;IACP,SAAS,QAAA;IACT,QAAQ,eAAA;IACR,SAAS,gBAAA;IACT,OAAO,QAAA;IACP,UAAU,QAAA;IACV,eAAa,QAAA;IACb,yBAAuB,QAAA;IACvB,cAAY,QAAA;IACZ,eAAa,QAAA;IACb,cAAY,QAAA;6BAEG,eAAA,QAAA;UAAiB;iBAAS,cAAS,CACjD,WAUE,KAAA,QAAA,WAVF,WACU,WAAS;KAChB,WAAW,kBAAA;KACX,UAAU,SAAA;KACV,MAAM,kBAAA,MAAkB;KACxB,MAAM,KAAA;KACN,SAAS,QAAA;KACT,QAAS,OAAA;KACT,SAAU,QAAA;KACV,gBAAkB,eAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE9G3B,MAAM,OAAO;EAWb,MAAM,QAAQ;EAkBd,MAAM,iBAAiB,eAAe,MAAM,cAAc,MAAM,OAAM;EACtE,MAAM,2BAA2B,eAAqD;AACpF,OAAI,MAAM,kBAAkB,KAAA,KAAa,eAAe,UAAU,KAAA,EAAW,QAAO,MAAM;AAE1F,UAAO;IACL,GAAG,MAAM;IACT,eAAe;KACb,GAAI,MAAM,iBAAiB,iBAAiB,EAAE;KAC9C,GAAI,MAAM,iBAAiB,EAAE;KAC7B,GAAI,eAAe,SAAS,EAAE;KAC/B;IACH;IACD;EAED,IAAI;EAEJ,SAAS,2BAAgE;AACvE,4BAAyB,MAAK;AAC9B,6BAA0B,aAAY;AACtC,UAAO,wBAAwB,UAAU,8BAA8B,MAAM,QAAQ,yBAAyB,MAAM,CAAC;;EAGvH,MAAM,qBAAqB,WAAgD,0BAA0B,CAAA;EACrG,MAAM,oBAAoB,eAAe,MAAM,aAAa,mBAAmB,MAAK;AAEpF,QACE;SAAO,MAAM;SAAc,MAAM;SAAwB,MAAM;GAAc,QACvE;AACJ,sBAAmB,QAAQ,0BAAyB;KAEtD,EAAE,MAAM,MAAM,CAChB;AAEA,uBAAqB,yBAAyB,MAAM,CAAA;AAEpD,QACE,iBACC,WAAW;AACV,OAAI,WAAW,KAAA,KAAa,MAAM,cAAc,KAAA,EAAW;AAC3D,OAAI,aAAa,cAAc,mBAAmB,MAAM,cAAc,MAAM,EAAE,OAAO,CAAE;AACvF,sBAAmB,MAAM,iBAAiB,OAAM;KAElD,EAAE,MAAM,MAAM,CAChB;AAEA,cACQ,mBAAmB,MAAM,cAAc,QAC5C,WAAW;AACV,OAAI,MAAM,cAAc,KAAA,EAAW;GACnC,MAAM,aAAa,cAAc,OAAM;AACvC,OAAI,eAAe,UAAU,KAAA,KAAa,aAAa,YAAY,eAAe,MAAM,CAAE;AAC1F,QAAK,qBAAqB,WAAU;AACpC,QAAK,iBAAiB,WAAU;KAElC,EAAE,MAAM,MAAM,CAChB;EAEA,MAAM,WAAW,eAAe,kBAAkB,MAAM,SAAQ;EAChE,MAAM,QAAQ,eAAe,MAAM,SAAS,MAAM,kBAAkB,MAAM,YAAY,CAAA;EACtF,MAAM,aAAa,eAAe,MAAM,kBAAkB,MAAM,WAAW,CAAA;EAC3E,MAAM,cAAc,eAAe,MAAM,kBAAkB,MAAM,YAAY,CAAA;EAC7E,MAAM,sBAAsB,eAAe,MAAM,kBAAkB,MAAM,oBAAoB,CAAA;EAC7F,MAAM,uBAAuB,eAAe,MAAM,kBAAkB,MAAM,qBAAqB,CAAA;EAC/F,MAAM,WAAW,eAAe,MAAM,kBAAkB,MAAM,OAAO,CAAA;EACrE,MAAM,QAAQ,eAAe,MAAM,kBAAkB,MAAM,MAAM,CAAA;EACjE,MAAM,cAAc,eAAe,MAAM,kBAAkB,MAAM,YAAY,CAAA;EAC7E,MAAM,WAAW,eAAe,MAAM,kBAAkB,MAAM,SAAS,CAAA;EACvE,MAAM,wBAAwB,eAAe,MAAM,kBAAkB,MAAM,sBAAsB,CAAA;EACjG,MAAM,iBAAiB,eAAe,MAAM,kBAAkB,MAAM,eAAe,CAAA;EACnF,MAAM,qBAAqB,eAAe,MAAM,kBAAkB,MAAM,mBAAmB,CAAA;EAC3F,MAAM,4BAA4B,eAAe,MAAM,kBAAkB,MAAM,0BAA0B,CAAA;EACzG,MAAM,sBAAsB,eAAe,MAAM,kBAAkB,MAAM,eAAe,CAAC,OAAM;EAC/F,MAAM,WAAW,eAAe,kBAAkB,MAAM,SAAQ;EAEhE,SAAS,cAAc,OAAyD;AAC9E,UAAO,QAAQ,MAAM;;EAGvB,SAAS,QAAQ,OAAyB;GACxC,MAAM,MAAM,MAAM,MAAK;AACvB,OAAI,MAAM,QAAQ,IAAI,CAAE,QAAO,IAAI,KAAI,SAAQ,QAAQ,KAAK,CAAA;AAC5D,OAAI,OAAO,OAAO,QAAQ,SACxB,QAAO,OAAO,YACZ,OAAO,QAAQ,IAA+B,CAAC,KAAK,CAAC,KAAK,UAAU,CAAC,KAAK,QAAQ,KAAK,CAAC,CAAA,CAC1F;AAEF,UAAO;;EAGT,SAAS,aAAa,MAA+B,OAAyC;GAC5F,MAAM,WAAW,OAAO,KAAK,KAAI;GACjC,MAAM,YAAY,OAAO,KAAK,MAAK;AACnC,OAAI,SAAS,WAAW,UAAU,OAAQ,QAAO;AACjD,UAAO,SAAS,OAAM,QAAO,YAAY,KAAK,MAAM,MAAM,KAAK,CAAA;;EAGjE,SAAS,YAAY,MAAe,OAAyB;GAC3D,MAAM,UAAU,MAAM,KAAI;GAC1B,MAAM,WAAW,MAAM,MAAK;AAE5B,OAAI,OAAO,GAAG,SAAS,SAAS,CAAE,QAAO;AAEzC,OAAI,MAAM,QAAQ,QAAQ,IAAI,MAAM,QAAQ,SAAS,EAAE;AACrD,QAAI,CAAC,MAAM,QAAQ,QAAQ,IAAI,CAAC,MAAM,QAAQ,SAAS,CAAE,QAAO;AAChE,QAAI,QAAQ,WAAW,SAAS,OAAQ,QAAO;AAC/C,WAAO,QAAQ,OAAO,MAAM,UAAU,YAAY,MAAM,SAAS,OAAO,CAAA;;AAG1E,OAAI,cAAc,QAAQ,IAAI,cAAc,SAAS,EAAE;AACrD,QAAI,CAAC,cAAc,QAAQ,IAAI,CAAC,cAAc,SAAS,CAAE,QAAO;AAChE,WAAO,aAAa,SAAS,SAAQ;;AAGvC,UAAO;;EAGT,SAAS,cAAc,OAAkD;AACvE,UAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAK;;;uBAK1E,mBAkGM,OAlGN,eAkGM;IAjGJ,mBAsBM,OAtBN,eAsBM,CArBJ,YAEW,kBAAA;KAFD,MAAK;KAAO,OAAM;;4BACf,CAAA,gBAAA,gBAAR,MAAA,MAAK,GAAG,yDACb,EAAA,CAAA,CAAA;;QAEA,mBAgBM,OAhBN,eAgBM,CAfJ,YAMa,oBAAA;KALX,SAAQ;KACP,UAAU,SAAA;KACV,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,kBAAA,MAAkB,0BAAwB;;4BAElC,CAAA,gBAAA,gBAAb,QAAA,WAAU,EAAA,EAAA,CAAA,CAAA;;yBAEf,YAOa,oBAAA;KANX,SAAQ;KACP,SAAS,SAAA;KACT,UAAQ,CAAG,qBAAA;KACX,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,kBAAA,MAAkB,aAAW;;4BAEtB,CAAA,gBAAA,gBAAZ,QAAA,UAAS,EAAA,EAAA,CAAA,CAAA;;;IAKF,MAAA,SAAA,WAAA,EAAhB,YAEW,kBAAA;;KAFY,MAAK;;4BACf,CAAA,gBAAA,gBAAR,MAAA,MAAK,EAAA,EAAA,CAAA,CAAA;;;IAGD,QAAA,cAAA,WAAA,EAAT,mBAEI,KAFJ,eAAsE,aAC7D,gBAAG,YAAA,OAAa,gBAAc,IAAA,YAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;IAGvC,mBA0CM,OA1CN,eA0CM,CAzCJ,YAsBa,oBAtBb,WACU,kBAqBG,MArBe,SAAO;KAChC,SAAS,QAAA;KACT,UAAU;KACV,OAAO,QAAA;KACP,OAAO,QAAA;;KAEG,QAAM,cAGX;gCAFJ,mBAEI,KAAA,EAFD,OAAM,qDAAmD,EAAC,cAE7D,GAAA;MAEQ,kBAAA,MAAkB,QAAQ,MAAM,SAAM,KAAA,WAAA,EAD9C,YAME,kCAAA;;OAJA,OAAM;OACL,OAAO,kBAAA,MAAkB,QAAQ;OACjC,mBAAiB,kBAAA,MAAkB,QAAQ;OAC3C,UAAQ,kBAAA,MAAkB,QAAQ;;;;;;MAErC,mBAEI,KAFJ,eAEI,gBADC,SAAA,MAAQ,EAAA,EAAA;;;;;;;QAKjB,WAgBO,KAAA,QAAA,WAAA;KAfJ,WAAW,kBAAA;KACX,UAAU,SAAA;KACV,YAAY,WAAA;KACZ,UAAU,SAAA;KACV,uBAA0B,sBAAA;KAC1B,gBAAiB,eAAA;KACjB,oBAAuB,mBAAA;KACvB,2BAA8B,0BAAA;KAC9B,mBAAqB,kBAAA,MAAkB;aAOnC,CALL,YAIE,6BAJF,WACU,SAGR,OAHgB;KACf,OAAO,QAAA;KACP,UAAU,QAAA;;IAMT,QAAA,uBAAA,WAAA,EADR,mBAgBM,OAhBN,cAgBM,EAAA,UAAA,KAAA,EAZJ,mBAWM,UAAA,MAAA,WAViB,YAAA,QAAd,eAAU;yBADnB,mBAWM,OAAA;MATH,KAAK;MACN,OAAM;SAEN,mBAEI,KAFJ,cAEI,gBADC,WAAU,EAAA,EAAA,EAEf,mBAEI,KAFJ,cAEI,gBADC,SAAA,MAAQ,GAAG,mBAAc,gBAAG,oBAAA,SAAmB,eAAA,EAAA,EAAA,CAAA,CAAA;;IAK/C,QAAA,wBAAA,WAAA,EAAT,mBAEI,KAFJ,eAAgF,+BACrD,gBAAG,oBAAA,MAAmB,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEjSrD,MAAM,QAAQ;EAad,MAAM,OAAO;EASb,MAAM,SAAS,cAAc,MAAM,YAAY;GAC7C,UAAU,MAAM;GAChB,UAAU,MAAM;GACjB,CAAA;AAGD,cACQ,MAAM,aACX,aAAa;AACZ,OAAI,YAAY,KAAK,UAAU,SAAS,KAAK,KAAK,UAAU,OAAO,MAAM,MAAM,CAC7E,QAAO,MAAM,QAAQ,SAAS,KAAI,OAAM,EAAE,GAAG,GAAG,EAAC;IAGvD;AAGA,cACQ,MAAM,eACX,OAAO;AACN,OAAI,MAAM,OAAO,OAAO,aAAa,MACnC,QAAO,cAAc,GAAE;IAG7B;AAGA,QACE,OAAO,QACN,aAAa;AACZ,QAAK,qBAAqB,SAAQ;KAEpC,EAAE,MAAM,MAAM,CAChB;AAGA,QACE,OAAO,eACN,OAAO;AACN,QAAK,uBAAuB,GAAE;IAElC;EAEA,MAAM,cAA4C;GAChD,GAAG;GACH,GAAG;GACH,GAAG;GACH,GAAG;GACL;EAEA,MAAM,eAA+B;GAAC;GAAK;GAAK;GAAK;GAAG;EAExD,MAAM,iBAA8D,CAClE;GAAE,OAAO;GAAI,OAAO;GAAM,EAC1B;GAAE,OAAO;GAAI,OAAO;GAAM,CAC5B;EAGA,MAAM,iBAAiB,IAAmB,KAAI;EAC9C,MAAM,iBAAiB,IAAmB,KAAI;EAE9C,SAAS,gBAAgB;AAEvB,QAAK,YADQ,OAAO,SAAQ,CACP;;EAGvB,SAAS,iBAAiB,QAAgB;AACxC,UAAO,WAAW,OAAM;AACxB,QAAK,eAAe,OAAM;;EAG5B,SAAS,eAAe,QAAgB;AACtC,UAAO,cAAc,OAAM;;EAI7B,SAAS,oBAAoB,OAAkB,QAAgB;AAC7D,OAAI,CAAC,MAAM,aAAc;AACzB,kBAAe,QAAQ;AACvB,OAAI,MAAM,cAAc;AACtB,UAAM,aAAa,gBAAgB;AACnC,UAAM,aAAa,QAAQ,cAAc,OAAM;;;EAInD,SAAS,mBAAmB,OAAkB,QAAgB;AAC5D,SAAM,gBAAe;AACrB,OAAI,eAAe,SAAS,eAAe,UAAU,OACnD,gBAAe,QAAQ;;EAI3B,SAAS,sBAAsB;AAC7B,kBAAe,QAAQ;;EAGzB,SAAS,eAAe,OAAkB,cAAsB;AAC9D,SAAM,gBAAe;AACrB,OAAI,CAAC,eAAe,SAAS,eAAe,UAAU,cAAc;AAClE,oBAAe;AACf;;GAGF,MAAM,YAAY,OAAO,MAAM,MAAM,WAAU,MAAK,EAAE,OAAO,eAAe,MAAK;GACjF,MAAM,UAAU,OAAO,MAAM,MAAM,WAAU,MAAK,EAAE,OAAO,aAAY;AAEvE,OAAI,cAAc,MAAM,YAAY,IAAI;AACtC,WAAO,aAAa,WAAW,QAAO;AACtC,SAAK,gBAAgB,OAAO,MAAM,MAAM,KAAI,MAAK,EAAE,GAAG,CAAA;;AAGxD,mBAAe;;EAGjB,SAAS,oBAAoB;AAC3B,mBAAe;;EAGjB,SAAS,iBAAiB;AACxB,kBAAe,QAAQ;AACvB,kBAAe,QAAQ;;EAIzB,SAAS,mBAAmB,QAAyB;AACnD,OAAI,CAAC,OAAO,WAAW,MAAO;AAC9B,UAAO,WAAW,OAAO,WAAW,MAAM,IAAI,EAAE,QAAQ,CAAA;;EAG1D,SAAS,iBAAiB,MAAoB;AAC5C,OAAI,CAAC,OAAO,WAAW,MAAO;AAC9B,UAAO,WAAW,OAAO,WAAW,MAAM,IAAI,EAAE,MAAM,CAAA;;EAGxD,SAAS,iBAAiB;AACxB,OAAI,CAAC,OAAO,WAAW,MAAO;AAC9B,UAAO,cAAc,OAAO,WAAW,MAAM,GAAE;;EAGjD,SAAS,mBAAmB;AAC1B,OAAI,CAAC,OAAO,WAAW,MAAO;AAC9B,UAAO,WAAW,OAAO,WAAW,MAAM,GAAE;;EAI9C,SAAS,eAAe,QAAgB,MAAoB;AAC1D,OAAI,CAAC,OAAO,WAAW,MAAO;GAC9B,MAAM,SAAS,OAAO,WAAW,MAAM;GAEvC,MAAM,WAA0B;IAC9B,IAAI;IACJ,OAAO,KAAK,QAAQ,WAAW;IAC/B,YAAY,KAAK,cAAc,KAAA;IAC/B,UAAU;KACR,OAAO,KAAK;KACZ,iBAAiB,KAAK;KACtB,gBAAgB,KAAK;KACrB,cAAc,KAAK,gBAAgB;KACpC;IACH;AAEA,OAAI,KAAK,MACP,QAAO,YAAY,QAAQ,QAAQ,SAAQ;OAE3C,QAAO,UAAU,QAAQ,OAAM;AAGjC,QAAK,aAAa,QAAQ,QAAQ,KAAI;;EAGxC,SAAS,gBAAgB,QAAgB;AACvC,OAAI,CAAC,OAAO,WAAW,MAAO;AAC9B,UAAO,UAAU,OAAO,WAAW,MAAM,IAAI,OAAM;;EAIrD,SAAS,aAAa,MAAoB;AACxC,UAAO,OAAO,KAAK,KAAK,MAAM,CAAC;;EAGjC,MAAM,kBAAkB,eAAe,OAAO,WAAW,OAAO,SAAS,EAAE,CAAA;;uBAIzE,mBA4IM,OAAA,EA5IA,OAAK,eAAA,CAAA,oBAAA,EAAA,8BAAuD,QAAA,UAAQ,CAAA,CAAA,EAAA,EAAA;IAExE,mBAyDM,OAzDN,eAyDM,CAxDJ,mBA2CM,OA3CN,eA2CM,EAAA,UAAA,KAAA,EA1CJ,mBAyCS,UAAA,MAAA,WAxCQ,MAAA,OAAM,CAAC,MAAM,QAArB,SAAI;yBADb,mBAyCS,UAAA;MAvCN,KAAK,KAAK;MACV,WAAW,QAAA,gBAAY,CAAK,QAAA;MAC5B,OAAK,eAAA,CAAA,yBAAA;wCAAsG,MAAA,OAAM,CAAC,aAAa,UAAU,KAAK;0CAAqD,eAAA,UAAmB,KAAK;2CAAsD,eAAA,UAAmB,KAAK;;MAQzS,UAAK,WAAE,eAAe,KAAK,GAAE;MAC7B,cAAS,WAAE,oBAAoB,QAAQ,KAAK,GAAE;MAC9C,aAAQ,WAAE,mBAAmB,QAAQ,KAAK,GAAE;MAC5C,aAAW;MACX,SAAI,WAAE,eAAe,QAAQ,KAAK,GAAE;MACpC,WAAS;;MAEV,mBAGE,QAAA;OAFA,OAAM;OACL,OAAK,eAAA,EAAA,iBAAqB,YAAY,KAAK,OAAI,CAAA;;sBAChD,MACF,gBAAG,KAAK,KAAI,GAAG,KACf,EAAA;MACQ,aAAa,KAAI,GAAA,KAAA,WAAA,EADzB,mBAQO,QAAA;;OANJ,OAAK,eAAA,CAAA,2BAA2D,MAAA,OAAM,CAAC,aAAa,UAAU,KAAK,KAAE,oCAAA,oCAAA,CAAA;yBAKnG,aAAa,KAAI,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;MAId,MAAA,OAAM,CAAC,MAAM,MAAM,SAAS,QAAA,YAAQ,CAAK,QAAA,YAAA,WAAA,EADjD,mBAMS,UAAA;;OAJP,OAAM;OACL,SAAK,eAAA,WAAO,iBAAiB,KAAK,GAAE,EAAA,CAAA,OAAA,CAAA;SACtC,OAED,GAAA,cAAA,IAAA,mBAAA,IAAA,KAAA;;iBAMI,MAAA,OAAM,CAAC,MAAM,MAAM,SAAS,QAAA,YAAQ,CAAK,QAAA,YAAA,WAAA,EADjD,mBASS,UAAA;;KAPP,OAAM;KACL,SAAO;sCAER,mBAEM,OAAA;KAFD,OAAM;KAAK,QAAO;KAAK,SAAQ;KAAY,MAAK;KAAO,QAAO;KAAe,gBAAa;KAAI,kBAAe;KAAQ,mBAAgB;QACxI,mBAAqB,QAAA,EAAf,GAAE,YAAU,CAAA,EAAG,mBAAqB,QAAA,EAAf,GAAE,YAAU,CAAA,CAAA,EAAA,GAAA,EAAA,gBACnC,cAER,GAAA,CAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;IAIS,MAAA,OAAM,CAAC,WAAW,SAAK,CAAK,QAAA,YAAA,WAAA,EAAvC,mBA2DM,OA3DN,eA2DM;KA1DJ,mBAyCM,OAzCN,eAyCM;MAvCJ,mBAeM,OAfN,cAeM,CAAA,OAAA,OAAA,OAAA,KAdJ,mBAA0D,QAAA,EAApD,OAAM,mCAAiC,EAAC,SAAK,GAAA,GACnD,mBAYM,OAZN,cAYM,EAAA,WAAA,EAXJ,mBAUS,UAAA,MAAA,WATO,iBAAP,QAAG;cADZ,mBAUS,UAAA;QARN,KAAK,IAAI;QACT,OAAK,eAAA,CAAA,gCAAoE,MAAA,OAAM,CAAC,WAAW,MAAM,WAAW,IAAI,QAAK,yCAAA,yCAAA,CAAA;QAIrH,UAAK,WAAE,mBAAmB,IAAI,MAAK;0BAEjC,IAAI,MAAK,EAAA,IAAA,aAAA;;gCAKlB,mBAAiD,OAAA,EAA5C,OAAM,qCAAmC,EAAA,MAAA,GAAA;MAG9C,mBAkBM,OAlBN,eAkBM,CAAA,OAAA,OAAA,OAAA,KAjBJ,mBAAyD,QAAA,EAAnD,OAAM,mCAAiC,EAAC,QAAI,GAAA,GAClD,mBAeM,OAfN,eAeM,EAAA,WAAA,EAdJ,mBAaS,UAAA,MAAA,WAZK,eAAL,MAAC;cADV,mBAaS,UAAA;QAXN,KAAK;QACN,OAAM;QACL,OAAK,eAAA;0BAAqC,MAAA,OAAM,CAAC,WAAW,MAAM,SAAS,IAAI,YAAY,KAAC;gBAAkD,MAAA,OAAM,CAAC,WAAW,MAAM,SAAS,IAAC,UAAa,YAAY;sBAAiC,MAAA,OAAM,CAAC,WAAW,MAAM,SAAS,IAAC,gBAAA,GAAsB,YAAY,GAAC;oBAAkC,MAAA,OAAM,CAAC,WAAW,MAAM,SAAS,IAAC,+BAAA;;QAMnX,UAAK,WAAE,iBAAiB,EAAC;0BAEvB,EAAC,EAAA,IAAA,cAAA;;;+BAMZ,mBAAgD,OAAA,EAA3C,OAAM,oCAAkC,EAAA,MAAA,GAAA;KAG7C,mBAKS,UAAA;MALD,OAAM;MAAgC,SAAO;;KAMrD,mBAKS,UAAA;MALD,OAAM;MAAgC,SAAO;;;IAS5C,MAAA,OAAM,CAAC,WAAW,SAAA,WAAA,EAA7B,mBAeM,OAfN,eAeM,CAdJ,YAaE,mBAAA;KAZC,QAAQ,MAAA,OAAM,CAAC,WAAW,MAAM;KAChC,OAAO,gBAAA;KACP,UAAU,QAAA,YAAQ,CAAK,QAAA;KACvB,oBAAkB;KAClB,eAAa,QAAA;KACb,eAAa,QAAA;KACb,4BAA0B,MAAA,OAAM,CAAC,WAAW,MAAM;KAClD,UAAU,QAAA;KACV,MAAM,QAAA;KACN,8BAA4B;KAC5B,YAAW;KACX,aAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEzVrB,MAAM,QAAQ;EAQd,MAAM,OAAO;EAKb,MAAM,eAAe,IAA8B,KAAI;EACvD,MAAM,gBAAgB,IAAmB,KAAI;EAE7C,MAAM,aAAa,mBAAmB;GACpC,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,cAAc,MAAM;GACpB,mBAAmB,MAAM;GAC1B,CAAA;EACD,MAAM,EAAE,kBAAkB,aAAa,aAAa,YAAY,YAAY,sBAAsB;EAElG,SAAS,gBAAgB,OAAkB,WAAmB;GAC5D,MAAM,WAAW,MAAM;AACvB,OAAI,CAAC,SAAU;AAEf,iBAAc,QAAQ;AACtB,YAAS,QAAQ,aAAa,UAAS;AACvC,YAAS,gBAAgB;;EAG3B,SAAS,gBAAgB;AACvB,iBAAc,QAAQ;AACtB,gBAAa,QAAQ;;EAGvB,SAAS,eAAe,OAAkB,MAA2B;AACnE,SAAM,gBAAe;AACrB,OAAI,MAAM,aACR,OAAM,aAAa,aAAa;AAElC,gBAAa,QAAQ;;EAGvB,SAAS,kBAAkB;AACzB,gBAAa,QAAQ;;EAGvB,SAAS,WAAW,OAAkB,MAA2B;AAC/D,SAAM,gBAAe;AACrB,gBAAa,QAAQ;GAErB,MAAM,YAAY,MAAM,cAAc,QAAQ,YAAW;AACzD,OAAI,CAAC,UAAW;AAEhB,gBAAa,WAAW,KAAI;;EAG9B,SAAS,aAAa,WAAmB,MAA2B;AAClE,kBAAe,WAAW,aAAa,WAAW,KAAK,CAAA;;EAGzD,SAAS,eAAe,WAAmB,MAA2B;GACpE,MAAM,QAAQ,WAAW,eAAe,WAAW,KAAI;AACvD,OAAI,SAAS,QACX,MAAK,iBAAiB,MAAM,OAAM;OAElC,MAAK,iBAAiB,MAAM,OAAM;;EAItC,SAAS,WAAW;AAClB,kBAAe,WAAW,UAAU,CAAA;;EAGtC,SAAS,eAAe,OAA6B;AACnD,QAAK,iBAAiB,MAAM,OAAM;AAClC,QAAK,iBAAiB,MAAM,OAAM;;;uBAKlC,mBAoJM,OApJN,eAoJM;IAlJJ,mBAkGM,OAlGN,eAkGM,CAhGJ,mBA8CM,OAAA;KA7CH,OAAK,eAAA;;;MAA+F,aAAA,UAAY,UAAA,wCAAA;;KAKhH,OAAK,eAAA,EAAA,gBAAoB,QAAA,QAAM,CAAA;KAC/B,YAAQ,OAAA,OAAA,OAAA,MAAA,WAAE,eAAe,QAAM,QAAA;KAC/B,aAAW;KACX,QAAI,OAAA,OAAA,OAAA,MAAA,WAAE,WAAW,QAAM,QAAA;QAExB,mBAGM,OAHN,eAGM,CAFJ,mBAAiE,QAAjE,eAAiE,gBAAhB,QAAA,OAAM,EAAA,EAAA,EACvD,mBAA6E,QAA7E,cAA6E,gBAA5B,MAAA,WAAU,CAAA,GAAG,YAAQ,EAAA,CAAA,CAAA,EAGxE,mBA6BM,OA7BN,cA6BM,EAAA,UAAA,KAAA,EA5BJ,mBAuBM,UAAA,MAAA,WAtBY,MAAA,YAAW,GAApB,UAAK;yBADd,mBAuBM,OAAA;MArBH,KAAK,MAAM;MACZ,OAAM;MACN,WAAU;MACT,cAAS,WAAE,gBAAgB,QAAQ,MAAM,KAAI;MAC7C,WAAS;;MAEV,mBAGE,QAAA;OAFA,OAAM;OACL,OAAK,eAAA,EAAA,iBAAqB,MAAM,OAAK,CAAA;;MAExC,mBAAoE,QAApE,cAAoE,gBAApB,MAAM,KAAI,EAAA,EAAA;MAC1D,mBAAsE,QAAtE,cAAsE,gBAArB,MAAM,MAAK,EAAA,EAAA;MAC5D,mBAQS,UAAA;OAPP,MAAK;OACL,OAAM;OACL,UAAK,WAAE,eAAe,MAAM,MAAI,QAAA;wCAEjC,mBAEM,OAAA;OAFD,OAAM;OAAK,QAAO;OAAK,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,gBAAa;UAC7F,mBAAiC,QAAA,EAA3B,GAAE,wBAAsB,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,EAAA,GAAA,cAAA;;eAKzB,MAAA,YAAW,CAAC,WAAM,KAAA,WAAA,EAA7B,mBAEM,OAFN,eAA6E,qBAE7E,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,EAAA,GAAA,EAKJ,mBA8CM,OAAA;KA7CH,OAAK,eAAA;;;MAA+F,aAAA,UAAY,UAAA,wCAAA;;KAKhH,OAAK,eAAA,EAAA,gBAAoB,QAAA,QAAM,CAAA;KAC/B,YAAQ,OAAA,OAAA,OAAA,MAAA,WAAE,eAAe,QAAM,QAAA;KAC/B,aAAW;KACX,QAAI,OAAA,OAAA,OAAA,MAAA,WAAE,WAAW,QAAM,QAAA;QAExB,mBAGM,OAHN,eAGM,CAFJ,mBAAiE,QAAjE,eAAiE,gBAAhB,QAAA,OAAM,EAAA,EAAA,EACvD,mBAA6E,QAA7E,eAA6E,gBAA5B,MAAA,WAAU,CAAA,GAAG,YAAQ,EAAA,CAAA,CAAA,EAGxE,mBA6BM,OA7BN,eA6BM,EAAA,UAAA,KAAA,EA5BJ,mBAuBM,UAAA,MAAA,WAtBY,MAAA,YAAW,GAApB,UAAK;yBADd,mBAuBM,OAAA;MArBH,KAAK,MAAM;MACZ,OAAM;MACN,WAAU;MACT,cAAS,WAAE,gBAAgB,QAAQ,MAAM,KAAI;MAC7C,WAAS;;MAEV,mBAGE,QAAA;OAFA,OAAM;OACL,OAAK,eAAA,EAAA,iBAAqB,MAAM,OAAK,CAAA;;MAExC,mBAAoE,QAApE,eAAoE,gBAApB,MAAM,KAAI,EAAA,EAAA;MAC1D,mBAAsE,QAAtE,eAAsE,gBAArB,MAAM,MAAK,EAAA,EAAA;MAC5D,mBAQS,UAAA;OAPP,MAAK;OACL,OAAM;OACL,UAAK,WAAE,eAAe,MAAM,MAAI,QAAA;wCAEjC,mBAEM,OAAA;OAFD,OAAM;OAAK,QAAO;OAAK,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,gBAAa;UAC7F,mBAAiC,QAAA,EAA3B,GAAE,wBAAsB,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,EAAA,GAAA,cAAA;;eAKzB,MAAA,YAAW,CAAC,WAAM,KAAA,WAAA,EAA7B,mBAEM,OAFN,eAA6E,qBAE7E,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,EAAA,GAAA,CAAA,CAAA;IAMK,MAAA,iBAAgB,CAAC,SAAM,KAAA,WAAA,EAAlC,mBAkCM,OAlCN,eAkCM,CAjCJ,mBAUM,OAVN,eAUM,CAAA,OAAA,OAAA,OAAA,KATJ,mBAA2E,QAAA,EAArE,OAAM,yCAAuC,EAAC,oBAAgB,GAAA,GAE5D,QAAA,OAAO,SAAM,KAAQ,QAAA,OAAO,SAAM,KAAA,WAAA,EAD1C,mBAOS,UAAA;;KALP,MAAK;KACL,OAAM;KACL,SAAO;OACT,cAED,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,EAGF,mBAoBM,OApBN,eAoBM,EAAA,UAAA,KAAA,EAnBJ,mBAkBM,UAAA,MAAA,WAjBY,MAAA,iBAAgB,GAAzB,UAAK;yBADd,mBAkBM,OAAA;MAhBH,KAAK,MAAM;MACX,OAAK,eAAA;;;OAA8G,cAAA,UAAkB,MAAM,OAAI,wCAAA;;MAKhJ,WAAU;MACT,cAAS,WAAE,gBAAgB,QAAQ,MAAM,KAAI;MAC7C,WAAS;;MAEV,mBAGE,QAAA;OAFA,OAAM;OACL,OAAK,eAAA,EAAA,iBAAqB,MAAM,OAAK,CAAA;;MAExC,mBAAoE,QAApE,eAAoE,gBAApB,MAAM,KAAI,EAAA,EAAA;MAC1D,mBAAsE,QAAtE,eAAsE,gBAArB,MAAM,MAAK,EAAA,EAAA;;;IAMvD,MAAA,kBAAiB,IAAA,WAAA,EAA5B,mBAOM,OAPN,eAOM,CAAA,OAAA,OAAA,OAAA,KANJ,mBAIM,OAAA;KAJD,OAAM;KAAK,QAAO;KAAK,SAAQ;KAAY,MAAK;KAAO,QAAO;KAAe,gBAAa;;KAC7F,mBAAiC,UAAA;MAAzB,IAAG;MAAK,IAAG;MAAK,GAAE;;KAC1B,mBAAuC,QAAA;MAAjC,IAAG;MAAK,IAAG;MAAI,IAAG;MAAK,IAAG;;KAChC,mBAA2C,QAAA;MAArC,IAAG;MAAK,IAAG;MAAK,IAAG;MAAQ,IAAG;;6BAChC,MACN,gBAAG,MAAA,kBAAiB,CAAA,EAAA,EAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEzN1B,MAAM,QAAQ;EAOd,MAAM,OAAO;EAKb,MAAM,EAAE,gBAAgB,YAAY,mBAAmB,kBAAiB;EAGxE,MAAM,iBAAiB,IAAI,IAAG;EAC9B,MAAM,iBAAiB,IAAI,EAAC;EAC5B,MAAM,cAAc,IAAwB,KAAA,EAAS;EAGrD,MAAM,YAAY,eAAgB,MAAM,gBAAgB,MAAM,KAAK,EAAE;EACrE,MAAM,YAAY,eAAgB,MAAM,gBAAgB,MAAM,KAAK,GAAG;EAEtE,MAAM,cAAc,CAClB;GAAE,OAAO;GAAO,OAAO;GAAQ,EAC/B;GAAE,OAAO;GAAU,OAAO;GAAW,CACvC;EAGA,MAAM,qBAAqB,eAA8B;GACvD,MAAM,EAAE,MAAM,kBAAkB,MAAM;AACtC,OAAI,CAAC,cAAe,QAAO;AAE3B,OAAI,SAAS,OAAO;IAClB,MAAM,SAAS,cAAc,aAAY;AACzC,QAAI,CAAC,UAAU,KAAK,OAAO,CAAE,QAAO;AAEpC,QADY,OAAO,WAAW,EAAE,GAAG,MACxB,UAAU,MAAO,QAAO,YAAY,OAAO,aAAa,KAAK,UAAU,MAAM;UACnF;IACL,MAAM,MAAM,SAAS,eAAe,GAAE;AACtC,QAAI,MAAM,IAAI,IAAI,MAAM,EAAG,QAAO;AAClC,QAAI,MAAM,UAAU,MAAO,QAAO,YAAY,UAAU;;AAE1D,UAAO;IACR;EAGD,MAAM,gBAAgB,eAAe,eAAe,MAAM,WAAW,OAAO,CAAA;EAE5E,MAAM,oBAAoB,eAAe;GACvC,MAAM,EAAE,MAAM,eAAe,WAAW,MAAM;AAC9C,OAAI,CAAC,iBAAiB,OAAO,WAAW,KAAK,mBAAmB,MAAO,QAAO;GAE9E,MAAM,OAAO,cAAc;AAC3B,OAAI,SAAS,MAEX,QADiB,cAAc,aAAa,CAAC,WAAW,EAAE,GAAG,KAC3C,OAAO,UAAU;OAGnC,QADiB,SAAS,eAAe,GAAE,GACzB,OAAO,IAAI,UAAU;IAE1C;EAED,MAAM,gBAAgB,eAAe;GACnC,MAAM,EAAE,MAAM,eAAe,WAAW,MAAM;AAC9C,OAAI,CAAC,iBAAiB,OAAO,WAAW,KAAK,mBAAmB,MAAO,QAAO;GAE9E,MAAM,OAAO,cAAc;AAC3B,OAAI,SAAS,EAAG,QAAO;AAEvB,OAAI,SAAS,OAAO;IAClB,MAAM,WAAW,cAAc,aAAa,CAAC,WAAW,EAAE,GAAG;IAC7D,MAAM,SAAS,KAAK,IAAI,WAAW,OAAO,GAAG,UAAU,QAAQ,EAAC;AAGhE,WAAO,GAFa,OAAO,aAAa,KAAK,SAAQ,CAE/B,GADJ,OAAO,aAAa,KAAK,OAAM;UAE5C;IACL,MAAM,WAAW,SAAS,eAAe,GAAE;AAC3C,QAAI,MAAM,SAAS,CAAE,QAAO;AAE5B,WAAO,GAAG,SAAS,GADJ,KAAK,IAAI,WAAW,OAAO,GAAG,UAAU,MAAK;;IAG/D;EAGD,SAAS,OAAO,SAAqC;AACnD,QAAK,qBAAqB;IAAE,GAAG,MAAM;IAAY,GAAG;IAAS,CAAA;;EAG/D,SAAS,WAAW,OAAc;AAChC,UAAO,EAAE,MAAO,MAAM,OAA4B,OAAO,CAAA;;EAG3D,SAAS,YAAY,OAAc;AACjC,UAAO,EAAE,OAAQ,MAAM,OAA4B,OAAO,CAAA;;EAG5D,SAAS,WAAW,OAAwB;AAC1C,UAAO;IAAE,MAAM;IAA2B,eAAe;IAAI,CAAA;;EAG/D,SAAS,oBAAoB,OAAc;GACzC,MAAM,MAAO,MAAM,OAA4B;AAC/C,UAAO,EAAE,eAAe,KAAK,CAAA;;EAG/B,SAAS,WAAW,OAAc;AAChC,UAAO,EAAE,MAAO,MAAM,OAA6B,OAAO,CAAA;;EAI5D,SAAS,UAAU,QAAsB;AACvC,UAAO,EAAE,QAAQ,WAAW,OAAO,EAAE,CAAA;;EAGvC,SAAS,YAAY,QAAwB;AAC3C,OAAI,eAAe,SAAS,KAAK,eAAe,QAAQ,EAAG;GAC3D,MAAM,QAAQ,KAAK,IAAI,eAAe,OAAO,MAAM,UAAS;AAE5D,aADe,eAAe,eAAe,OAAO,OAAO,OAAO,OAAM,CACxD;;EAGlB,SAAS,iBAAiB;AACxB,OAAI,YAAY,UAAU,KAAA,KAAa,YAAY,QAAQ,EAAG;AAC9D,OAAI,MAAM,WAAW,OAAO,UAAU,MAAM,UAAW;AAGvD,OADe,MAAM,WAAW,OAAO,MAAM,MAAM,EAAE,UAAU,YAAY,MAAK,CACpE;AAEZ,aAAU,CAAC,GAAG,MAAM,WAAW,QAAQ;IAAE,OAAO,YAAY;IAAO,YAAY;IAAG,CAAC,CAAA;AACnF,eAAY,QAAQ,KAAA;;EAGtB,SAAS,oBAAoB,OAAsB;AACjD,OAAI,MAAM,QAAQ,SAAS;AACzB,UAAM,gBAAe;AACrB,oBAAe;;;EAInB,SAAS,YAAY,OAAe;GAClC,MAAM,OAAO,CAAC,GAAG,MAAM,WAAW,OAAM;AACxC,QAAK,OAAO,OAAO,EAAC;AACpB,aAAU,KAAI;;EAGhB,SAAS,sBAAsB,OAAe,YAAgC;AAC5E,OAAI,eAAe,KAAA,KAAa,aAAa,EAAG;GAChD,MAAM,OAAO,CAAC,GAAG,MAAM,WAAW,OAAM;AACxC,QAAK,SAAS;IAAE,GAAG,KAAK;IAAQ;IAAW;AAC3C,UAAO,EAAE,QAAQ,MAAM,CAAA;;EAGzB,SAAS,YAAY,OAAuB;AAC1C,OAAI,CAAC,OAAO,SAAS,MAAM,CAAE,QAAO;AACpC,OAAI,UAAU,EAAG,QAAO;AACxB,OAAI,SAAS,IAAM,QAAO,MAAM,eAAe,SAAS,EAAE,0BAA0B,GAAG,CAAA;AACvF,OAAI,SAAS,EAAG,QAAO,MAAM,UAAS;AACtC,OAAI,SAAS,KAAO,QAAO,MAAM,QAAQ,EAAC;AAC1C,UAAO,MAAM,cAAc,EAAC;;;uBAK5B,mBAwNM,OAAA;IAvNJ,OAAM;IACL,OAAK,eAAA,EAAA,mBAAuB,QAAA,WAAW,OAAK,CAAA;;IAG7C,mBAyBM,OAzBN,eAyBM,CAxBJ,mBAuBM,OAvBN,cAuBM,CAtBJ,mBAYQ,SAZR,cAYQ,CAXN,mBAME,SAAA;KALA,MAAK;KACJ,OAAO,QAAA,WAAW;KACnB,OAAM;KACN,cAAW;KACV,SAAO;gCAEV,mBAGE,QAAA;KAFA,OAAM;KACL,OAAK,eAAA,EAAA,iBAAqB,QAAA,WAAW,OAAK,CAAA;mBAG/C,mBAQE,SAAA;KAPA,MAAK;KACJ,OAAO,QAAA,WAAW;KACnB,aAAY;KACZ,OAAM;KACN,WAAU;KACV,cAAW;KACV,SAAO;;IAMd,mBAqCM,OArCN,cAqCM,CAAA,OAAA,OAAA,OAAA,KAAA,kBAAA,4dAAA,EAAA,GA9BJ,mBA6BM,OA7BN,cA6BM,CA5BJ,mBAQM,OARN,cAQM,CAAA,OAAA,OAAA,OAAA,KAPJ,mBAAsD,SAAA,EAA/C,OAAM,8BAA4B,EAAC,QAAI,GAAA,GAC9C,YAKE,oBAAA;KAJC,eAAa,QAAA,WAAW;KACxB,SAAS;KACV,MAAK;KACJ,uBAAoB;oCAGzB,mBAkBM,OAlBN,cAkBM;+BAjBJ,mBAAuD,SAAA,EAAhD,OAAM,8BAA4B,EAAC,SAAK,GAAA;KAC/C,mBAYE,SAAA;MAXA,MAAK;MACJ,OAAO,QAAA,WAAW;MAClB,aAAa,QAAA,WAAW,SAAI,QAAA,WAAA;MAC5B,OAAK,eAAA,CAAA,qCAAqE,mBAAA,QAAkB,6CAAA,GAAA,CAAA;MAI5F,WAAW,QAAA,WAAW,SAAI,QAAA,IAAA;MAC1B,gBAAY,CAAA,CAAI,mBAAA;MACjB,cAAW;MACV,SAAO;;KAEE,mBAAA,SAAA,WAAA,EAAZ,mBAEO,QAFP,eAEO,gBADF,mBAAA,MAAkB,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;;IAO7B,mBA4GM,OA5GN,eA4GM;KA3GJ,mBAYM,OAZN,eAYM;gCAXJ,mBAEM,OAAA;OAFD,OAAM;OAAoC,SAAQ;OAAY,MAAK;OAAO,QAAO;OAAe,gBAAa;OAAI,kBAAe;OAAQ,mBAAgB;UAC3J,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,EAAG,mBAA8B,QAAA,EAAxB,GAAE,qBAAmB,CAAA,CAAA,EAAA,GAAA;gDAC9C,YAEN,GAAA;MAAA,mBAMS,UAAA;OALN,OAAO,QAAA,WAAW;OACnB,OAAM;OACL,UAAQ;4BAET,mBAA+D,UAAA,MAAA,WAA3C,QAAA,QAAL,MAAC;2BAAhB,mBAA+D,UAAA;QAAnC,KAAK;QAAI,OAAO;0BAAM,EAAC,EAAA,GAAA,cAAA;;;KAKvD,mBAsBM,OAtBN,eAsBM,CArBJ,mBASM,OATN,eASM,CARJ,mBAGM,OAHN,eAGM,CAAA,OAAA,OAAA,OAAA,KAFJ,mBAAuD,SAAA,EAAhD,OAAM,8BAA4B,EAAC,SAAK,GAAA,GAC/C,YAAsE,qBAAA;kBAAhD,eAAA;kFAAc,QAAA;MAAG,KAAK;MAAI,MAAM;MAAI,MAAK;oCAEjE,mBAGM,OAHN,eAGM,CAAA,OAAA,QAAA,OAAA,MAFJ,mBAAuD,SAAA,EAAhD,OAAM,8BAA4B,EAAC,SAAK,GAAA,GAC/C,YAAsF,qBAAA;kBAAhE,eAAA;kFAAc,QAAA;MAAG,KAAK;MAAI,KAAK,QAAA;MAAY,MAAM;MAAG,MAAK;6CAGnF,mBAUM,OAVN,eAUM,EAAA,UAAA,KAAA,EATJ,mBAQS,UAAA,MAAA,WAPU,QAAA,UAAV,WAAM;0BADf,mBAQS,UAAA;OANN,KAAK,OAAO;OACb,MAAK;OACL,OAAM;OACL,UAAK,WAAE,YAAY,OAAM;yBAEvB,OAAO,MAAK,EAAA,GAAA,cAAA;;KAMrB,mBAmBM,OAAA;MAnBD,OAAM;MAA+B,WAAS;SACjD,YAME,qBAAA;kBALS,YAAA;+EAAW,QAAA;MACnB,KAAK;MACL,MAAM;MACP,MAAK;MACL,aAAY;kCAEd,mBAUS,UAAA;MATP,MAAK;MACL,OAAM;MACL,UAAU,YAAA,UAAgB,KAAA,KAAa,YAAA,QAAW,KAAQ,QAAA,WAAW,OAAO,UAAU,QAAA;MACvF,cAAW;MACV,SAAO;yCAER,mBAEM,OAAA;MAFD,OAAM;MAAK,QAAO;MAAK,SAAQ;MAAY,MAAK;MAAO,QAAO;MAAe,gBAAa;MAAI,kBAAe;MAAQ,mBAAgB;SACxI,mBAAqB,QAAA,EAAf,GAAE,YAAU,CAAA,EAAG,mBAAqB,QAAA,EAAf,GAAE,YAAU,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,EAAA,GAAA,cAAA,CAAA,EAAA,GAAA;KAMlC,QAAA,WAAW,OAAO,SAAM,KAAA,WAAA,EAAnC,mBAwCM,OAxCN,eAwCM,CAAA,OAAA,QAAA,OAAA,MAvCJ,mBAIM,OAAA,EAJD,OAAM,qCAAmC,EAAA;MAC5C,mBAA+F,QAAA,EAAzF,OAAM,wEAAsE,EAAC,QAAK;MACxF,mBAA6F,QAAA,EAAvF,OAAM,uEAAqE,EAAC,OAAI;MACtF,mBAAsF,QAAA,EAAhF,OAAM,yEAAuE,CAAA;cAErF,mBAiCM,OAjCN,eAiCM,EAAA,UAAA,KAAA,EAhCN,mBA+BM,UAAA,MAAA,WA9BiB,QAAA,WAAW,SAAxB,OAAO,MAAC;0BADlB,mBA+BM,OAAA;OA7BH,KAAK,MAAM;OACZ,OAAM;;OAEN,mBAGO,QAHP,eAGO,CAFL,mBAAoF,QAApF,eAAoF,gBAAlC,YAAY,MAAM,MAAK,CAAA,EAAA,EAAA,EAC7D,MAAM,UAAK,KAAA,WAAA,EAAvB,mBAAmG,QAAnG,eAAmG,gBAAzB,QAAA,WAAW,KAAI,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;OAE3F,mBASO,QATP,eASO,CARL,YAOE,qBAAA;QANC,eAAa,MAAM;QACnB,KAAK;QACL,KAAK;QACL,MAAM;QACP,MAAK;QACJ,wBAAkB,WAAE,sBAAsB,GAAG,OAAM;;OAGxD,mBAWO,QAXP,eAWO,CAVL,mBASS,UAAA;QARP,MAAK;QACL,OAAM;QACN,cAAW;QACV,UAAK,WAAE,YAAY,EAAC;2CAErB,mBAEM,OAAA;QAFD,OAAM;QAAK,QAAO;QAAK,SAAQ;QAAY,MAAK;QAAO,QAAO;QAAe,gBAAa;QAAI,kBAAe;QAAQ,mBAAgB;WACxI,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,EAAG,mBAAuB,QAAA,EAAjB,GAAE,cAAY,CAAA,CAAA,EAAA,GAAA,CAAA,EAAA,EAAA,GAAA,cAAA,CAAA,CAAA;;oCAQrD,mBAEM,OAFN,eAA+C,8CAE/C;;IAKM,QAAA,WAAW,OAAO,SAAM,KAAQ,QAAA,WAAW,iBAAa,CAAK,mBAAA,SAAA,WAAA,EADrE,mBAwBM,OAAA;;KAtBH,OAAK,eAAA,CAAA,gCAAoD,kBAAA,QAAiB,2CAAA,GAAA,CAAA;;KAK3E,mBAGM,OAHN,eAGM,CAFJ,mBAAsF,QAAtF,eAAsF,gBAAlC,QAAA,WAAW,OAAO,OAAM,EAAA,EAAA,EAAA,OAAA,QAAA,OAAA,MAC5E,mBAA8D,QAAA,EAAxD,OAAM,sCAAoC,EAAC,UAAM,GAAA,EAAA,CAAA;iCAEzD,mBAAgD,OAAA,EAA3C,OAAM,oCAAkC,EAAA,MAAA,GAAA;KAC7C,mBAGM,OAHN,eAGM,CAFJ,mBAA2E,QAA3E,eAA2E,gBAAvB,cAAA,MAAa,EAAA,EAAA,EAAA,OAAA,QAAA,OAAA,MACjE,mBAAiE,QAAA,EAA3D,OAAM,sCAAoC,EAAC,aAAS,GAAA,EAAA,CAAA;KAEjD,cAAA,SAAA,WAAA,EAAX,mBAAqE,OAArE,cAAqE,IAAA,mBAAA,IAAA,KAAA;KAC1D,cAAA,SAAA,WAAA,EAAX,mBAGM,OAHN,eAGM,CAFJ,mBAA2E,QAA3E,aAA2E,gBAAvB,cAAA,MAAa,EAAA,EAAA,EACjE,mBAAyG,QAAzG,aAAyG,gBAArD,QAAA,WAAW,SAAI,QAAA,SAAA,OAAA,EAAA,EAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;KAEzD,kBAAA,SAAA,WAAA,EAAZ,mBAEO,QAFP,aAA4E,kBAE5E,IAAA,mBAAA,IAAA,KAAA;;IAIF,mBAIM,OAJN,aAIM,CAHJ,YAEa,oBAAA;KAFD,SAAQ;KAAS,MAAK;KAAM,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAA;;4BAEnD,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAF+D,oBAE/D,GAAA,CAAA,EAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1YN,MAAM,QAAQ;EAQd,MAAM,OAAO;EAYb,MAAM,YAAiD;GACrD,OAAO,CACL;IAAE,KAAK;IAAQ,OAAO,EAAE,GAAG,0HAA0H;IAAE,CACxJ;GACD,YAAY;IACV;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,2GAA2G;KAAE;IACxI;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,oBAAoB;KAAE;IACjD;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,YAAY;KAAE;IAC1C;GACD,OAAO;IACL;KAAE,KAAK;KAAQ,OAAO;MAAE,OAAO;MAAK,QAAQ;MAAK,GAAG;MAAK,GAAG;MAAK,IAAI;MAAK;KAAE;IAC5E;KAAE,KAAK;KAAQ,OAAO;MAAE,OAAO;MAAK,QAAQ;MAAK,GAAG;MAAM,GAAG;MAAK,IAAI;MAAK;KAAE;IAC7E;KAAE,KAAK;KAAQ,OAAO;MAAE,OAAO;MAAK,QAAQ;MAAK,GAAG;MAAM,GAAG;MAAM,IAAI;MAAK;KAAE;IAC9E;KAAE,KAAK;KAAQ,OAAO;MAAE,OAAO;MAAK,QAAQ;MAAK,GAAG;MAAK,GAAG;MAAM,IAAI;MAAK;KAAE;IAC9E;GACD,QAAQ,CACN;IAAE,KAAK;IAAU,OAAO;KAAE,IAAI;KAAM,IAAI;KAAM,GAAG;KAAM;IAAE,EACzD;IAAE,KAAK;IAAQ,OAAO,EAAE,GAAG,eAAe;IAAE,CAC7C;GACD,WAAW;IACT;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,wGAAwG;KAAE;IACrI;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,WAAW;KAAE;IACxC;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,YAAY;KAAE;IAC1C;GACD,SAAS;IACP;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,YAAY;KAAE;IACzC;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,aAAa;KAAE;IAC1C;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,aAAa;KAAE;IAC1C;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,WAAW;KAAE;IACxC;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,YAAY;KAAE;IACzC;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,YAAY;KAAE;IAC1C;GACD,OAAO,CACL;IAAE,KAAK;IAAQ,OAAO;KAAE,OAAO;KAAM,QAAQ;KAAM,GAAG;KAAK,GAAG;KAAK,IAAI;KAAK,IAAI;KAAK;IAAE,EACvF;IAAE,KAAK;IAAQ,OAAO,EAAE,GAAG,2DAA2D;IAAE,CACzF;GACD,WAAW;IACT;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,2GAA2G;KAAE;IACxI;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,oBAAoB;KAAE;IACjD;KAAE,KAAK;KAAQ,OAAO,EAAE,GAAG,YAAY;KAAE;IAC1C;GACD,QAAQ,CACN;IAAE,KAAK;IAAQ,OAAO,EAAE,GAAG,0HAA0H;IAAE,CACxJ;GACD,QAAQ,CACN;IAAE,KAAK;IAAQ,OAAO,EAAE,GAAG,2UAA2U;IAAE,EACxW;IAAE,KAAK;IAAU,OAAO;KAAE,IAAI;KAAM,IAAI;KAAM,GAAG;KAAK;IAAE,CACzD;GACH;EAGA,SAAS,cAAc,OAA6B;GAClD,MAAM,MAAgB,EAAC;GACvB,SAAS,SAAS,MAAgB;AAChC,QAAI,KAAK,KAAK,GAAE;AAChB,QAAI,KAAK,SACP,MAAK,SAAS,QAAQ,SAAQ;;AAGlC,SAAM,QAAQ,SAAQ;AACtB,UAAO;;EAGT,MAAM,YAAY,gBAAgB;GAChC,kBAAkB,MAAM;GACxB,cAAc,cAAc,MAAM,MAAM;GACxC,iBAAiB,MAAM;GACxB,CAAA;EAED,SAAS,WAAW,QAAyB;AAC3C,UAAO,UAAU,WAAW,OAAM;;EAGpC,SAAS,aAAa,MAAgB;AAEpC,OADiB,UAAU,OAAO,KAAK,GAAE,CAEvC,MAAK,UAAU,KAAK,GAAE;OAEtB,MAAK,YAAY,KAAK,GAAE;;EAI5B,SAAS,gBAAgB,MAAgB;AACvC,QAAK,cAAc,KAAI;;EAGzB,SAAS,gBAAgB,MAA+B;AACtD,OAAI,KAAK,KAAM,QAAO,CAAC;IAAE,KAAK;IAAQ,OAAO,EAAE,GAAG,KAAK,MAAM;IAAE,CAAA;AAC/D,UAAO,UAAU,KAAK,QAAQ;;EAGhC,SAAS,gBAAgB,MAA6C;AACpE,OAAI,KAAK,UAAU,KAAA,EAAW,QAAO,KAAK;AAC1C,OAAI,MAAM,cAAc,KAAK,YAAY,KAAK,SAAS,SAAS,EAC9D,QAAO,KAAK,SAAS;;EAKzB,SAAS,gBAAgB,MAA8B;AACrD,UAAO,KAAK,gBAAgB;;EAG9B,SAAS,YAAY,MAAyB;AAC5C,UAAO,CAAC,CAAC,KAAK,YAAY,KAAK,SAAS,SAAS;;EAGnD,SAAS,gBAAgB,MAAgB,OAAwB;AAC/D,OAAI,CAAC,YAAY,KAAK,CAAE,QAAO;AAC/B,OAAI,MAAM,aAAa,KAAA,KAAa,SAAS,MAAM,SAAU,QAAO;AACpE,UAAO,WAAW,KAAK,GAAE;;EAI3B,SAAS,WAAW,MAAgB,OAAsB;GACxD,MAAM,WAAW,KAAK;GACtB,MAAM,WAAW,WAAW,KAAK,GAAE;GACnC,MAAM,YAAY,YAAY,KAAI;GAClC,MAAM,iBAAiB,gBAAgB,MAAM,MAAK;GAClD,MAAM,QAAQ,gBAAgB,KAAI;GAClC,MAAM,WAAW,gBAAgB,KAAI;GACrC,MAAM,eAAe,gBAAgB,KAAI;GAEzC,MAAM,SAAS,EACb,OACA;IACE,OAAO;IACP,eAAe,gBAAgB,KAAK;IACrC,EACD;IACE,YACI,EACE,UACA;KACE,MAAM;KACN,OAAO;KACP,cAAc,WAAW,aAAa;KACtC,UAAU,MAAa;AACrB,QAAE,iBAAgB;AAClB,mBAAa,KAAI;;KAEpB,EACD,CACE,EAAE,OAAO;KAAE,MAAM;KAAQ,QAAQ;KAAgB,SAAS;KAAa,gBAAgB;KAAK,kBAAkB;KAAS,mBAAmB;KAAS,EAAE,CACnJ,EAAE,QAAQ,EAAE,GAAG,iBAAiB,CAAC,CAClC,CAAC,CACJ,CACF,GACA,EAAE,QAAQ,EAAE,OAAO,wCAAwC,CAAC;IAChE,MAAM,YACF,EAAE,QAAQ,EAAE,OAAO,CAAC,0BAA0B,2BAA2B,KAAK,QAAQ,WAAW,EAAE,EAAE,CACnG,EAAE,OAAO;KAAE,MAAM;KAAQ,QAAQ;KAAgB,SAAS;KAAa,gBAAgB;KAAK,kBAAkB;KAAS,mBAAmB;KAAS,EACjJ,aAAa,KAAI,OAAM,EAAE,GAAG,KAAK,GAAG,MAAM,CAAA,CAC3C,CACF,CAAA,GACD;IACJ,EAAE,QAAQ,EAAE,OAAO,2BAA2B,EAAE,KAAK,MAAM;IAC3D,UAAU,KAAA,IACN,EAAE,QAAQ,EAAE,OAAO,CAAC,2BAA2B,4BAA4B,WAAW,EAAE,EAAE,OAAO,MAAM,CAAA,GACvG;IACN,CACF;GAEA,MAAM,aACJ,kBAAkB,WACd,EACE,YACA;IAAE,kBAAkB;IAAwC,kBAAkB;IAAuC,QAC/G,EAAE,OAAO,EAAE,OAAO,8BAA8B,EAAE,SAAS,KAAK,UAAU,WAAW,OAAO,QAAQ,EAAE,CAAC,CAAA,CAC/G,GACA;AAEN,UAAO,EACL,OACA;IACE,KAAK,KAAK;IACV,OAAO;KAAC;KAA0B,WAAW,qCAAqC;KAAI,CAAC,YAAY,iCAAiC;KAAG;IACvI,MAAM;IACN,iBAAiB,YAAY,WAAW,KAAA;IACzC,EACD,CAAC,QAAQ,WAAU,CACrB;;EAIF,SAAS,aAAsB;AAC7B,UAAO,MAAM,MAAM,KAAK,SAAS,WAAW,MAAM,EAAE,CAAA;;;uBAKpD,mBAaM,OAAA;IAbA,OAAK,eAAA,CAAA,oBAAA,qBAA4C,QAAA,OAAI,CAAA;IAAK,MAAK;OAExD,QAAA,MAAM,WAAM,KAAA,WAAA,EAAvB,mBAKM,OALN,cAKM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAJJ,mBAEM,OAAA;IAFD,OAAM;IAA+B,MAAK;IAAO,QAAO;IAAe,SAAQ;IAAY,gBAAa;IAAI,kBAAe;IAAQ,mBAAgB;OACtJ,mBAAmI,QAAA,EAA7H,GAAE,0HAAwH,CAAA,CAAA,EAAA,GAAA,EAElI,mBAAoD,KAAA,EAAjD,OAAM,gCAA8B,EAAC,YAAQ,GAAA,CAAA,EAAA,CAAA,KAAA,WAAA,EAKhD,YAAsC,8BAAhB,YAAU,CAAA,EAAA,EAAA,KAAA,GAAA,CAAA,EAAA,EAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1NtC,MAAM,QAAQ;EAKd,MAAM,OAAO;EAOb,MAAM,EACJ,cACA,mBACA,cACA,yBACE,sBAAqB;EAGzB,MAAM,qBAAqB,IAAmB,KAAI;EAClD,MAAM,EACJ,QAAQ,sBACR,SAAS,qBACT,OAAO,uBACP,QAAQ,2BACN,kBAAiB;EACrB,MAAM,WAAW,IAAI,GAAE;EACvB,MAAM,kBAAkB,IAAI,GAAE;EAC9B,MAAM,eAAe,KAAwB;EAC7C,MAAM,aAAa,IAA6B,EAAE,CAAA;EAClD,MAAM,mBAAmB,IAA4B,EAAE,CAAA;EAGvD,MAAM,qBAAqB,eAAe;AACxC,OAAI,MAAM,aAAa,MAAM,UAAU,SAAS,EAC9C,QAAO,MAAM;GAEf,MAAM,WAAW,CAAC,GAAG,aAAa,MAAK;AACvC,OAAI,MAAM,gBACR,UAAS,KAAK,GAAG,MAAM,gBAAe;AAExC,UAAO;IACR;EAGD,MAAM,mBAAmB,eAAe;AACtC,OAAI,CAAC,mBAAmB,MAAO,QAAO;AACtC,UAAO,mBAAmB,MAAM,MAAM,MAAM,EAAE,OAAO,mBAAmB,MAAM,IAAI;IACnF;EAGD,MAAM,gBAAkD;GACtD,YAAY;GACZ,MAAM;GACN,UAAU;GACV,aAAa;GACb,UAAU;GACV,YAAY;GACZ,KAAK;GACL,QAAQ;GACV;EAGA,MAAM,cAAc,eAAoC;AACtD,OAAI,CAAC,iBAAiB,MAAO,QAAO;AACpC,UAAO;IACL,IAAI,MAAM,YAAY,MAAM;IAC5B,MAAM,iBAAiB,MAAM;IAC7B,MAAM,SAAS,SAAS,iBAAiB,MAAM;IAC/C,aAAa,gBAAgB;IAC7B,UAAU,aAAa,SAAS,iBAAiB,MAAM;IACvD,QAAS,MAAM,YAAY,UAAU;IACrC,YAAY,EAAE,GAAG,WAAW,OAAO;IACnC,OAAO,MAAM,YAAY,SAAS;IACpC;IACD;EAED,MAAM,gBAAgB,eAAe;AACnC,OAAI,CAAC,iBAAiB,SAAS,CAAC,YAAY,MAAO,QAAO;AAC1D,UAAO,iBAAiB,MAAM,WAC3B,QAAQ,MAAM,WAAW,MAAM,EAAE,SAAS,KAAA,KAAa,WAAW,MAAM,EAAE,SAAS,GAAE,CACrF,KAAK,MAAM,qBAAqB,WAAW,MAAM,EAAE,MAAM,EAAE,CAAA,CAC3D,KAAK,KAAI;IACb;EAGD,SAAS,aAAa,MAAoB;GACxC,MAAM,WAAW,kBAAkB,KAAK,KAAI;AAC5C,OAAI,SACF,oBAAmB,QAAQ,SAAS;AAEtC,YAAS,QAAQ,KAAK;AACtB,mBAAgB,QAAQ,KAAK,eAAe;AAC5C,gBAAa,QAAQ,KAAK;AAC1B,cAAW,QAAQ,EAAE,GAAI,KAAK,cAAc,EAAE,EAAE;;EAIlD,SAAS,eAAe,UAAwB;AAC9C,sBAAmB,QAAQ,SAAS;AACpC,0BAAsB;AAGtB,OAAI,MAAM,SAAS,UAAU;AAC3B,aAAS,QAAQ,SAAS;AAC1B,oBAAgB,QAAQ,SAAS,eAAe;AAChD,iBAAa,QAAQ,SAAS;AAE9B,eAAW,QAAQ,EAAC;AACpB,SAAK,MAAM,SAAS,SAAS,WAC3B,KAAI,MAAM,YAAY,KAAA,EACpB,YAAW,MAAM,MAAM,OAAO,MAAM;;;EAO5C,SAAS,kBAAkB,KAAa,OAAgB;AACtD,cAAW,MAAM,OAAO;AAExB,OAAI,iBAAiB,MAAM,KACzB,QAAO,iBAAiB,MAAM;;EAKlC,SAAS,0BAA0B,KAAa,OAAuC;AACrF,cAAW,MAAM,OAAO;AACxB,OAAI,iBAAiB,MAAM,KACzB,QAAO,iBAAiB,MAAM;;EAKlC,SAAS,aAAa;AACpB,OAAI,CAAC,iBAAiB,SAAS,CAAC,YAAY,MAAO;GAEnD,MAAM,SAAS,aAAa,YAAY,OAAO,iBAAiB,MAAK;AACrE,oBAAiB,QAAQ,OAAO;AAEhC,OAAI,OAAO,OAAO;AAChB,SAAK,QAAQ,YAAY,MAAK;AAC9B,SAAK,qBAAqB,YAAY,MAAK;;;EAK/C,SAAS,qBAAqB;AAC5B,OAAI,CAAC,iBAAiB,MAAO;AAe7B,QAAK,iBAb6B;IAChC,IAAI,UAAU,KAAK,KAAK;IACxB,MAAM,iBAAiB,MAAM;IAC7B,MAAM,SAAS,SAAS,iBAAiB,MAAM;IAC/C,aAAa,gBAAgB;IAC7B,iBAAiB,aAAa;IAC9B,YAAY,iBAAiB,MAAM,WAAW,KAAK,OAAO;KACxD,GAAG;KACH,SAAS,WAAW,MAAM,EAAE;KAC7B,EAAE;IACH,WAAW;IACb,CAEiC;;EAInC,SAAS,eAAe;AACtB,QAAK,SAAQ;;EAIf,SAAS,eAAe,SAAqC;AAC3D,OAAI,YAAY,KAAA,EAAW,QAAO;AAClC,OAAI,UAAU,GAAI,QAAO,GAAG,QAAQ;GACpC,MAAM,QAAQ,KAAK,MAAM,UAAU,GAAE;GACrC,MAAM,OAAO,UAAU;AACvB,UAAO,OAAO,IAAI,GAAG,MAAM,IAAI,KAAK,KAAK,GAAG,MAAM;;AAGpD,kBAAgB;AACd,OAAI,MAAM,WACR,cAAa,MAAM,WAAU;YACpB,mBAAmB,MAAM,SAAS,EAC3C,gBAAe,mBAAmB,MAAM,GAAE;IAE7C;AAGD,cACQ,MAAM,aACX,SAAS;AACR,OAAI,KACF,cAAa,KAAI;IAGvB;;uBAIE,mBA6RM,OA7RN,cA6RM;IA3RJ,mBAYM,OAZN,cAYM,CAXJ,mBAEO,QAFP,cAEO,gBADF,QAAA,SAAI,WAAA,yBAAA,qBAAA,EAAA,EAAA,EAET,mBAOO,QAAA,EANJ,OAAK,eAAA,CAAA,oCAAA,qCAAiG,QAAA,OAAA,CAAA,EAAA,EAAA,gBAKpG,QAAA,KAAI,EAAA,EAAA,CAAA,CAAA;IAKX,mBAkNM,OAlNN,cAkNM;KAhNJ,mBAuEM,OAvEN,cAuEM,CAAA,OAAA,OAAA,OAAA,KAtEJ,mBAAoE,SAAA,EAA7D,OAAM,wCAAsC,EAAC,YAAQ,GAAA,GAC5D,mBAoEM,OAAA;eApEG;MAAJ,KAAI;MAAsB,OAAM;SACnC,mBAiCS,UAAA;MAhCP,MAAK;MACL,OAAM;MACL,SAAK,OAAA,OAAA,OAAA,MAAA,GAAA,SAAE,MAAA,uBAAA,IAAA,MAAA,uBAAA,CAAA,GAAA,KAAsB;;MAGtB,iBAAA,SAAA,WAAA,EADR,mBAaM,OAbN,cAaM,CANJ,mBAKE,QAAA;OAJA,kBAAe;OACf,mBAAgB;OAChB,gBAAa;OACZ,GAAG,cAAc,iBAAA,MAAiB;;MAGvC,mBAEO,QAFP,cAEO,gBADF,iBAAA,OAAkB,QAAI,qBAAA,EAAA,EAAA;oBAE3B,mBAUM,OAAA;OATH,OAAK,eAAA,CAAA,wCAA4E,MAAA,qBAAoB,GAAA,+CAAA,GAAA,CAAA;OAItG,MAAK;OACL,QAAO;OACP,SAAQ;wCAER,mBAA2F,QAAA;OAArF,kBAAe;OAAQ,mBAAgB;OAAQ,gBAAa;OAAI,GAAE;;SAIjE,MAAA,qBAAoB,IAAA,WAAA,EAA/B,mBA+BM,OA/BN,cA+BM,EAAA,UAAA,KAAA,EA9BJ,mBA6BS,UAAA,MAAA,WA5BY,mBAAA,QAAZ,aAAQ;0BADjB,mBA6BS,UAAA;OA3BN,KAAK,SAAS;OACf,MAAK;OACJ,OAAK,eAAA,CAAA,yCAA6E,mBAAA,UAAuB,SAAS,KAAE,kDAAA,GAAA,CAAA;OAIpH,UAAK,WAAE,eAAe,SAAQ;wBAE/B,mBAYM,OAZN,eAYM,CANJ,mBAKE,QAAA;OAJA,kBAAe;OACf,mBAAgB;OAChB,gBAAa;OACZ,GAAG,cAAc,SAAS;qCAG/B,mBAKM,OALN,eAKM,CAJJ,mBAA8B,OAAA,MAAA,gBAAtB,SAAS,KAAI,EAAA,EAAA,EACV,SAAS,eAAA,WAAA,EAApB,mBAEM,OAFN,eAEM,gBADD,SAAS,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,EAAA,IAAA,cAAA;;+BAQnC,mBAA6C,OAAA,EAAxC,OAAM,iCAA+B,EAAA,MAAA,GAAA;KAG1C,mBAgBM,OAhBN,eAgBM;gCAfJ,mBAEQ,SAAA,EAFD,OAAM,iFAA+E,EAAC,eAE7F,GAAA;qBACA,mBAQE,SAAA;6EAPiB,QAAA;OACjB,MAAK;OACJ,OAAK,eAAA,CAAA,+BAA2D,iBAAA,MAAiB,OAAI,uCAAA,GAAA,CAAA;OAItF,aAAY;iCANH,SAAA,MAAQ,CAAA,CAAA;MAQP,iBAAA,MAAiB,QAAA,WAAA,EAA7B,mBAEO,QAFP,eAEO,gBADF,iBAAA,MAAiB,KAAI,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;;KAKZ,iBAAA,SAAA,UAAA,KAAA,EACd,mBAmGM,UAAA,EAAA,KAAA,GAAA,EAAA,WAlGqB,iBAAA,MAAiB,aAAlC,OAAO,UAAK;0BADtB,mBAmGM,OAAA;OAjGH,KAAK,MAAM;OACX,OAAK,eAAA,CAAA,+BAA2D,QAAK,MAAA,KAAc,iBAAA,MAAiB,WAAW,QAAK,KAAA,KAAA,GAAA,CAAA;;OAOrH,mBAOQ,SAAA,EANL,OAAK,eAAA,CAAA,qCAAqE,MAAM,WAAQ,gDAAA,GAAA,CAAA,EAAA,EAAA,gBAKtF,MAAM,MAAK,EAAA,EAAA;OAKR,MAAM,SAAI,YAAiB,MAAM,SAAI,iBAAsB,MAAM,SAAI,cAAA,WAAA,EAD7E,mBAmBM,OAnBN,eAmBM,CAfJ,mBAWE,SAAA;QAVA,MAAK;QACJ,OAAO,WAAA,MAAW,MAAM;QACxB,KAAK,MAAM;QACX,KAAK,MAAM;QACX,aAAa,MAAM,eAAW,SAAa,MAAM,MAAM,aAAW;QAClE,OAAK,eAAA,CAAA,+BAAmE,iBAAA,MAAiB,MAAM,OAAG,uCAAA,GAAA,CAAA;QAIlG,UAAK,WAAE,kBAAkB,MAAM,KAAM,OAAO,OAA4B,cAAa;oCAE5E,MAAM,QAAA,WAAA,EAAlB,mBAEO,QAFP,eAEO,gBADF,MAAM,KAAI,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAMJ,MAAM,SAAI,UAAA,WAAA,EADvB,mBAUE,SAAA;;QARA,MAAK;QACJ,OAAO,WAAA,MAAW,MAAM;QACxB,aAAa,MAAM,eAAW,SAAa,MAAM,MAAM,aAAW;QAClE,OAAK,eAAA,CAAA,+BAA+D,iBAAA,MAAiB,MAAM,OAAG,uCAAA,GAAA,CAAA;QAI9F,UAAK,WAAE,kBAAkB,MAAM,KAAM,OAAO,OAA4B,MAAK;sCAKnE,MAAM,SAAI,YAAA,WAAA,EADvB,mBAiBS,UAAA;;QAfN,OAAO,WAAA,MAAW,MAAM;QACxB,OAAK,eAAA,CAAA,gCAAgE,iBAAA,MAAiB,MAAM,OAAG,uCAAA,GAAA,CAAA;QAI/F,WAAM,WAAE,kBAAkB,MAAM,KAAM,OAAO,OAA6B,MAAK;WAEhF,mBAAyE,UAAzE,eAA0B,YAAO,gBAAG,MAAM,MAAM,aAAW,CAAA,EAAA,EAAA,GAAA,UAAA,KAAA,EAC3D,mBAMS,UAAA,MAAA,WALU,MAAM,UAAhB,WAAM;4BADf,mBAMS,UAAA;SAJN,KAAK,OAAO;SACZ,OAAO,OAAO;2BAEZ,OAAO,MAAK,EAAA,GAAA,cAAA;yCAMN,MAAM,SAAI,mBAAA,WAAA,EADvB,YAME,4BAAA;;QAJC,eAAa,WAAA,MAAW,MAAM;QAC9B,OAAK,CAAA,CAAI,iBAAA,MAAiB,MAAM;QACjC,MAAK;QACJ,wBAAkB,WAAE,0BAA0B,MAAM,KAAK,OAAM;;;;;aAKrD,MAAM,SAAI,aAAA,WAAA,EADvB,mBAUE,SAAA;;QARA,MAAK;QACJ,OAAO,WAAA,MAAW,MAAM;QACxB,aAAa,MAAM,eAAW;QAC9B,OAAK,eAAA,CAAA,+BAA+D,iBAAA,MAAiB,MAAM,OAAG,uCAAA,GAAA,CAAA;QAI9F,UAAK,WAAE,kBAAkB,MAAM,KAAM,OAAO,OAA4B,MAAK;;OAGpE,iBAAA,MAAiB,MAAM,QAAA,WAAA,EAAnC,mBAEO,QAFP,eAEO,gBADF,iBAAA,MAAiB,MAAM,KAAG,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;;;KAMnC,mBAQM,OARN,eAQM,CAAA,OAAA,OAAA,OAAA,KAPJ,mBAAoE,SAAA,EAA7D,OAAM,qCAAmC,EAAC,eAAW,GAAA,GAAA,eAC5D,mBAKE,YAAA;mFAJwB,QAAA;MACxB,OAAM;MACN,aAAY;MACZ,MAAK;kCAHI,gBAAA,MAAe,CAAA,CAAA,CAAA,CAAA;;IASnB,QAAA,eAAe,YAAA,SAAA,WAAA,EAA1B,mBAuBM,OAvBN,aAuBM,CAAA,OAAA,OAAA,OAAA,KAtBJ,mBAA8D,OAAA,EAAzD,OAAM,uCAAqC,EAAC,WAAO,GAAA,GACxD,mBAoBM,OApBN,aAoBM;KAnBJ,mBASM,OATN,aASM,EAAA,WAAA,EARJ,mBAOM,OAPN,aAOM,CANJ,mBAKE,QAAA;MAJA,kBAAe;MACf,mBAAgB;MAChB,gBAAa;MACZ,GAAG,cAAc,YAAA,MAAY;;KAIpC,mBAKM,OALN,aAKM,CAJJ,mBAA4E,OAA5E,aAA4E,gBAAzB,YAAA,MAAY,KAAI,EAAA,EAAA,EACxD,cAAA,SAAA,WAAA,EAAX,mBAEM,OAFN,aAEM,gBADD,cAAA,MAAa,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;KAGT,YAAA,MAAY,YAAA,WAAA,EAAvB,mBAEM,OAFN,aAEM,gBADD,eAAe,YAAA,MAAY,SAAQ,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;;IAM5C,mBA4BM,OA5BN,aA4BM;KA3BJ,mBAWM,OAAA,EAXD,OAAM,sCAAoC,EAAA,CAC7C,mBASS,UAAA;MARP,MAAK;MACL,OAAM;MACL,SAAO;uCAER,mBAEM,OAAA;MAFD,MAAK;MAAO,QAAO;MAAe,SAAQ;SAC7C,mBAAwK,QAAA;MAAlK,kBAAe;MAAQ,mBAAgB;MAAQ,gBAAa;MAAI,GAAE;+BACpE,sBAER,GAAA,CAAA,EAAA,CAAA,CAAA,CAAA;KAEF,mBAMS,UAAA;MALP,MAAK;MACL,OAAM;MACL,SAAO;QACT,WAED;KACA,mBAOS,UAAA;MANP,MAAK;MACL,OAAM;MACL,UAAQ,CAAG,iBAAA;MACX,SAAO;wBAEL,QAAA,SAAI,WAAA,aAAA,eAAA,EAAA,GAAA,YAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE3ef,MAAM,QAAQ;EAMd,MAAM,SAAS,IAAI,MAAK;EAExB,MAAM,qBAA6C;GACjD,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACP;EAEA,MAAM,cAAkC;GACtC,CAAC,cAAM,IAAI;GACX,CAAC,KAAK,IAAI;GACV,CAAC,KAAK,IAAI;GACV,CAAC,KAAK,IAAI;GACV,CAAC,MAAM,IAAI;GACX,CAAC,MAAM,IAAS;GAChB,CAAC,MAAM,IAAI;GACX,CAAC,OAAO,IAAI;GACd;EAEA,SAAS,cAAc,KAAqB;AAC1C,UAAO,OAAO,IAAG,CACd,MAAM,GAAE,CACR,KAAI,OAAM,mBAAmB,OAAO,GAAE,CACtC,KAAK,GAAE;;EAGZ,SAAS,iBAAiB,GAAW,QAAwB;AAC3D,UAAO,EAAE,QAAQ,OAAO,CAAC,QAAQ,UAAU,GAAG,IAAI;;EAYpD,MAAM,YAAY,eAAgC;GAChD,MAAM,IAAI,MAAM;AAEhB,OAAI,CAAC,OAAO,SAAS,EAAE,CAAE,QAAO;IAAE,MAAM;IAAS,OAAO;IAAG;AAC3D,OAAI,MAAM,EAAG,QAAO;IAAE,MAAM;IAAS,OAAO;IAAI;GAEhD,MAAM,OAAO,KAAK,IAAI,EAAC;AAEvB,OAAI,MAAM,aAAa,WAAW;AAChC,SAAK,MAAM,CAAC,WAAW,WAAW,YAChC,KAAI,QAAQ,YAAY,KACtB,QAAO;KACL,MAAM;KACN,OAAO,iBAAiB,IAAI,WAAW,MAAM,UAAU;KACvD;KACF;AAGJ,WAAO;KAAE,MAAM;KAAS,OAAO,iBAAiB,GAAG,MAAM,UAAU;KAAC;;AAQtE,OAAI,EAJF,MAAM,aAAa,gBACnB,MAAM,aAAa,iBAClB,MAAM,aAAa,WAAW,OAAO,OAAQ,QAAQ,MAGtD,QAAO;IAAE,MAAM;IAAS,OAAO,iBAAiB,GAAG,MAAM,UAAU;IAAC;GAGtE,IAAI,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,CAAA;AAErC,OAAI,MAAM,aAAa,cACrB,OAAM,KAAK,MAAM,MAAM,EAAE,GAAG;AAM9B,UAAO;IACL,MAAM;IACN,UAJkB,iBADH,IAAI,KAAK,IAAI,IAAI,IAAG,EACU,MAAM,UAAS;IAK5D,UAAU;IACV,aAAa,cAAc,IAAI;IACjC;IACD;EAED,MAAM,iBAAiB,eAAe;AACpC,OAAI,OAAO,MAAM,MAAM,MAAM,CAAE,QAAO;AACtC,OAAI,MAAM,UAAU,SAAU,QAAO;AACrC,OAAI,MAAM,UAAU,UAAW,QAAO;AACtC,UAAO;IACR;EAED,MAAM,iBAAiB,eAAe;AACpC,OAAI,eAAe,MAAO,QAAO,eAAe;GAChD,MAAM,IAAI,UAAU;GACpB,IAAI;AACJ,OAAI,EAAE,SAAS,aACb,QAAO,GAAG,EAAE,SAAS,GAAG,EAAE;YACjB,EAAE,SAAS,UACpB,QAAO,GAAG,EAAE,QAAQ,EAAE,UAAU;OAEhC,QAAO,EAAE,SAAS;AAEpB,OAAI,MAAM,KAAM,SAAQ,IAAI,MAAM;AAClC,UAAO;IACR;EAED,eAAe,kBAAkB;AAC/B,OAAI;AACF,UAAM,UAAU,UAAU,UAAU,eAAe,MAAK;AACxD,WAAO,QAAQ;AACf,qBAAiB;AAAE,YAAO,QAAQ;OAAS,KAAI;WACzC;;;uBAOR,mBA4CO,QA5CP,cA4CO;IA1CW,eAAA,SAAA,WAAA,EACd,mBAAgE,QAAhE,cAAgE,gBAAxB,eAAA,MAAc,EAAA,EAAA,IAInC,UAAA,MAAU,SAAI,gBAAA,WAAA,EAAnC,mBAKW,UAAA,EAAA,KAAA,GAAA,EAAA;KAJT,mBAAuE,QAAvE,cAAuE,gBAA5B,UAAA,MAAU,SAAQ,EAAA,EAAA;+BAC7D,mBAAmD,QAAA,EAA7C,OAAM,0BAAwB,EAAC,KAAO,GAAA;+BAC5C,mBAA6C,QAAA,EAAvC,OAAM,yBAAuB,EAAC,MAAE,GAAA;KACtC,mBAA0E,QAA1E,cAA0E,gBAA/B,UAAA,MAAU,YAAW,EAAA,EAAA;cAI7C,UAAA,MAAU,SAAI,aAAA,WAAA,EAAnC,mBAGW,UAAA,EAAA,KAAA,GAAA,EAAA,CAFT,mBAAiE,QAAjE,cAAiE,gBAAzB,UAAA,MAAU,MAAK,EAAA,EAAA,EAC3C,UAAA,MAAU,UAAA,WAAA,EAAtB,mBAA2F,QAA3F,cAA2F,gBAA1B,UAAA,MAAU,OAAM,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,GAAA,KAAA,WAAA,EAKjF,mBAAiE,QAAjE,cAAiE,gBAAzB,UAAA,MAAU,MAAK,EAAA,EAAA;IAI7C,QAAA,QAAA,WAAA,EAAZ,mBAAiE,QAAjE,cAAiE,gBAAd,QAAA,KAAI,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;IAI/C,QAAA,YAAA,WAAA,EADR,mBAcS,UAAA;;KAZN,OAAK,eAAA,CAAA,6BAAA,EAAA,qCAAuE,OAAA,OAAM,CAAA,CAAA;KACnF,MAAK;KACJ,OAAO,OAAA,QAAM,YAAA;KACb,SAAK,cAAO,iBAAe,CAAA,OAAA,CAAA;SAEhB,OAAA,SAAA,WAAA,EAAZ,mBAGM,OAHN,eAGM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAFJ,mBAAyD,QAAA;KAAnD,GAAE;KAAI,GAAE;KAAI,OAAM;KAAK,QAAO;KAAK,IAAG;KAAI,IAAG;kBACnD,mBAAoE,QAAA,EAA9D,GAAE,2DAAyD,EAAA,MAAA,GAAA,CAAA,EAAA,CAAA,KAAA,WAAA,EAEnE,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAAoC,YAAA,EAA1B,QAAO,kBAAgB,EAAA,MAAA,GAAA,CAAA,EAAA,CAAA,EAAA,EAAA,IAAA,aAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE/KzC,MAAM,EAAE,uBAAuB,oBAAmB;EAElD,SAAS,SAAS,GAA0B;AAC1C,UAAO,mBAAmB,EAAC;;;uBAK3B,YAYY,wBAXL,QAAA,SAAM,SAAA,MAAA,EAAA,EACV,OAAK,eAAA,CAAA,qBAAA,CAAyB,QAAA,SAAM,6BAAA,GAAA,CAAA,EAAA,EAAA;2BAEW,EAAA,UAAA,KAAA,EAAhD,mBAOW,UAAA,MAAA,WAPmB,SAAS,QAAA,QAAO,GAA5B,MAAM,MAAC;6DAA8B,GAAC,EAAA,CAC1C,KAAK,SAAI,aAAA,WAAA,EAArB,mBAA8F,QAA9F,cAA8F,gBAAnB,KAAK,KAAI,EAAA,EAAA,IACnE,KAAK,SAAI,eAAA,WAAA,EAA1B,mBAAuG,QAAvG,cAAuG,gBAAnB,KAAK,KAAI,EAAA,EAAA,IAC5E,KAAK,SAAI,iBAAA,WAAA,EAA1B,mBAA2G,QAA3G,cAA2G,gBAAnB,KAAK,KAAI,EAAA,EAAA,IAChF,KAAK,SAAI,WAAA,WAAA,EAA1B,mBAA+F,QAA/F,cAA+F,gBAAnB,KAAK,KAAI,EAAA,EAAA,IACpE,KAAK,SAAI,SAAA,WAAA,EAA1B,mBAA2F,QAA3F,cAA2F,gBAAnB,KAAK,KAAI,EAAA,EAAA,IAChE,KAAK,SAAI,YAAA,WAAA,EAA1B,mBAAiG,QAAjG,cAAiG,gBAAnB,KAAK,KAAI,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,GAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEjB7F,MAAM,QAAQ;EAOd,MAAM,OAAO;EAIb,MAAM,aAAa,IAAyB,GAAE;EAC9C,MAAM,aAAa,IAAI,GAAE;EAEzB,MAAM,cAAc,eAAe;GACjC,MAAM,QAAQ,IAAI,IAAI,MAAM,QAAQ,KAAI,MAAK,EAAE,KAAK,CAAA;AACpD,UAAO,MAAM,KAAK,MAAK;IACxB;EAED,MAAM,cAAc,eAAe;GACjC,MAAM,QAAQ,IAAI,IAAI,MAAM,QAAQ,SAAQ,MAAK,EAAE,OAAO,CAAC,EAAE,KAAK,GAAG,EAAE,CAAC,CAAA;AACxE,UAAO,MAAM,KAAK,MAAK;IACxB;EAED,MAAM,kBAAkB,eAAe;GACrC,IAAI,SAAS,MAAM;AACnB,OAAI,WAAW,MACb,UAAS,OAAO,QAAO,MAAK,EAAE,SAAS,WAAW,MAAK;AAEzD,OAAI,WAAW,MACb,UAAS,OAAO,QAAO,MAAK,EAAE,SAAS,WAAW,MAAK;AAEzD,UAAO;IACR;EAED,MAAM,gBAAgB,eAAe;GACnC,MAAM,UAAU,CAAC,GAAG,gBAAgB,MAAK;AACzC,WAAQ,MAAM,GAAG,MAAM;IACrB,MAAM,QAAQ,IAAI,KAAK,EAAE,UAAU,CAAC,SAAQ;IAC5C,MAAM,QAAQ,IAAI,KAAK,EAAE,UAAU,CAAC,SAAQ;AAC5C,WAAO,MAAM,UAAU,WAAW,QAAQ,QAAQ,QAAQ;KAC3D;AACD,UAAO;IACR;EAED,SAAS,YAAY,MAAsB;GACzC,MAAM,QAAQ,KAAK,MAAM,CAAC,MAAM,MAAK;AACrC,OAAI,MAAM,UAAU,EAClB,SAAQ,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,aAAY;AAEjD,WAAQ,MAAM,KAAK,MAAM,IAAI,aAAY;;EAG3C,MAAM,MAAM,IAAI,KAAK,mBAAmB,MAAM,EAAE,SAAS,QAAQ,CAAA;EAEjE,SAAS,gBAAgB,WAAkC;GACzD,MAAM,OAAO,qBAAqB,OAAO,YAAY,IAAI,KAAK,UAAS;GACvE,MAAM,MAAM,KAAK,KAAI;GACrB,MAAM,SAAS,KAAK,SAAS,GAAG;GAChC,MAAM,UAAU,KAAK,MAAM,SAAS,IAAI;GACxC,MAAM,UAAU,KAAK,MAAM,UAAU,GAAE;GACvC,MAAM,SAAS,KAAK,MAAM,UAAU,GAAE;GACtC,MAAM,UAAU,KAAK,MAAM,SAAS,GAAE;GACtC,MAAM,WAAW,KAAK,MAAM,UAAU,EAAC;AAEvC,OAAI,KAAK,IAAI,QAAQ,GAAG,GAAI,QAAO,IAAI,OAAO,SAAS,SAAQ;AAC/D,OAAI,KAAK,IAAI,QAAQ,GAAG,GAAI,QAAO,IAAI,OAAO,SAAS,SAAQ;AAC/D,OAAI,KAAK,IAAI,OAAO,GAAG,GAAI,QAAO,IAAI,OAAO,QAAQ,OAAM;AAC3D,OAAI,KAAK,IAAI,QAAQ,GAAG,EAAG,QAAO,IAAI,OAAO,SAAS,MAAK;AAC3D,OAAI,KAAK,IAAI,SAAS,GAAG,EAAG,QAAO,IAAI,OAAO,UAAU,OAAM;AAC9D,UAAO,KAAK,oBAAmB;;EAGjC,SAAS,iBAAiB,OAAmB;AAC3C,QAAK,eAAe,MAAK;;;uBAKzB,mBAoDM,OAAA,EApDA,OAAK,eAAA,CAAA,oBAAA,qBAA4C,QAAA,OAAI,CAAA,EAAA,EAAA;IAC9C,QAAA,eAAA,WAAA,EAAX,mBASM,OATN,cASM,CAAA,eARJ,mBAGS,UAAA;6EAHkB,QAAA;KAAE,OAAM;kCACjC,mBAAmC,UAAA,EAA3B,OAAM,IAAE,EAAC,aAAS,GAAA,IAAA,UAAA,KAAA,EAC1B,mBAAqE,UAAA,MAAA,WAAjD,YAAA,QAAL,MAAC;yBAAhB,mBAAqE,UAAA;MAAnC,KAAK;MAAI,OAAO;wBAAM,EAAC,EAAA,GAAA,aAAA;sCAF1C,WAAA,MAAU,CAAA,CAAA,EAAA,eAI3B,mBAGS,UAAA;6EAHkB,QAAA;KAAE,OAAM;kCACjC,mBAAmC,UAAA,EAA3B,OAAM,IAAE,EAAC,aAAS,GAAA,IAAA,UAAA,KAAA,EAC1B,mBAAqE,UAAA,MAAA,WAAjD,YAAA,QAAL,MAAC;yBAAhB,mBAAqE,UAAA;MAAnC,KAAK;MAAI,OAAO;wBAAM,EAAC,EAAA,GAAA,aAAA;sCAF1C,WAAA,MAAU,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAOrB,cAAA,MAAc,UAAA,WAAA,EADtB,mBAmCM,OAAA;;KAjCJ,OAAM;KACL,OAAK,eAAE,QAAA,YAAS;MAAA,WAAK,QAAA;MAAS,WAAA;MAAA,GAAA,EAAA,CAAA;kCAE/B,mBAAsC,OAAA,EAAjC,OAAM,0BAAwB,EAAA,MAAA,GAAA,IAAA,UAAA,KAAA,EACnC,mBA4BM,UAAA,MAAA,WA3BY,cAAA,QAAT,UAAK;yBADd,mBA4BM,OAAA;MA1BH,KAAK,MAAM;MACX,OAAK,eAAA,CAAA,2BAAA,4BAA0D,MAAM,OAAI,CAAA;MACzE,UAAK,WAAE,iBAAiB,MAAK;SAE9B,WAqBO,KAAA,QAAA,SAAA,EArBoB,OAAK,QAqBzB,CAAA,OAAA,OAAA,OAAA,KApBL,mBAAqC,OAAA,EAAhC,OAAM,yBAAuB,EAAA,MAAA,GAAA,GAClC,mBAkBM,OAlBN,cAkBM;MAjBJ,mBAIM,OAJN,cAIM;OAHO,MAAM,QAAA,WAAA,EAAjB,mBAA2F,OAA3F,cAA2F,gBAAhC,YAAY,MAAM,KAAI,CAAA,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;OACrE,MAAM,QAAA,WAAA,EAAlB,mBAA8E,QAA9E,cAA8E,gBAApB,MAAM,KAAI,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;OACpE,mBAAuF,QAAvF,cAAuF,gBAA1C,gBAAgB,MAAM,UAAS,CAAA,EAAA,EAAA;;MAE9E,mBAA8D,OAA9D,eAA8D,gBAArB,MAAM,OAAM,EAAA,EAAA;MAC1C,MAAM,UAAA,WAAA,EAAjB,mBAAkF,OAAlF,eAAkF,gBAArB,MAAM,OAAM,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;MAC9D,MAAM,YAAY,OAAO,KAAK,MAAM,SAAQ,CAAE,UAAA,WAAA,EAAzD,mBASM,OATN,eASM,EAAA,UAAA,KAAA,EARJ,mBAOO,UAAA,MAAA,WANkB,MAAM,WAArB,OAAO,QAAG;2BADpB,mBAOO,QAAA;QALJ,KAAK,OAAO,IAAG;QAChB,OAAM;WAEN,mBAA8D,QAA9D,eAA8D,gBAAd,IAAG,GAAG,KAAC,EAAA,EAAA,gBAAO,MAC9D,gBAAG,MAAK,EAAA,EAAA,CAAA,CAAA;;;;KAQR,cAAA,MAAc,UAAA,WAAA,EAA1B,mBAEM,OAFN,eAEM,CADJ,WAA4C,KAAA,QAAA,SAAA,EAAA,QAAA,CAAA,gBAAA,gBAAtB,QAAA,aAAY,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEjIxC,MAAM,QAAQ;EAKd,MAAM,OAAO;EAKb,MAAM,UAAU,IAAwB,KAAI;EAC5C,MAAM,iBAAiB,iBAAgB;EAEvC,MAAM,UAAU,eAA6B;GAC3C,MAAM,QAAQ,MAAM,MAAM;GAC1B,MAAM,YAAY,MAAM,MAAM,QAAO,MAAK,EAAE,WAAW,YAAY,CAAC;GACpE,MAAM,aAAa,MAAM,MAAM,QAAO,MAAK,EAAE,WAAW,aAAa,CAAC;GACtE,MAAM,QAAQ,MAAM,MAAM,QAAO,MAAK,EAAE,WAAW,QAAQ,CAAC;GAC5D,MAAM,UAAU,MAAM,MAAM,QAAO,MAAK,EAAE,WAAW,UAAU,CAAC;GAChE,MAAM,UAAU,MAAM,MAAM,QAAO,MAAK,EAAE,WAAW,UAAU,CAAC;AAEhE,UAAO;IAAE;IAAO;IAAW;IAAY;IAAO;IAAS;IAAS,SADhD,QAAQ,IAAI,KAAK,OAAQ,YAAY,WAAW,QAAS,IAAI,GAAG;IACR;IACzE;EAED,SAAS,YAAY,IAAY;AAC/B,kBAAe,OAAO,GAAE;;AAG1B,cACQ,MAAM,MAAM,KAAI,MAAK,EAAE,OAAO,GACnC,aAAa,gBAAgB;AAC5B,OAAI,CAAC,MAAM,cAAc,CAAC,QAAQ,MAAO;AACzC,QAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,IACtC,KAAI,YAAY,OAAO,gBAAgB,cAAc,OAAO,cAAc;AACxE,mBAAe;AAEb,MADW,QAAQ,OAAO,cAAc,kBAAkB,MAAM,MAAM,GAAG,GAAG,IAAG,GAC3E,eAAe;MAAE,UAAU;MAAU,OAAO;MAAU,CAAA;MAC3D;AACD;;IAIR;;uBAIE,mBAmHM,OAnHN,cAmHM;IAlHO,QAAA,SAAA,WAAA,EAAX,mBAGM,OAHN,cAGM,CAFJ,mBAA2D,QAA3D,cAA2D,gBAAf,QAAA,MAAK,EAAA,EAAA,EACjD,mBAAwE,QAAxE,cAAwE,gBAA1B,QAAA,MAAQ,QAAO,GAAG,KAAC,EAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;IAGnE,mBAKM,OALN,cAKM,CAJJ,mBAGE,OAAA;KAFA,OAAM;KACL,OAAK,eAAA,EAAA,OAAA,GAAc,QAAA,MAAQ,QAAO,IAAA,CAAA;;IAI3B,QAAA,cAAZ,WAkBO,KAAA,QAAA,WAAA;;KAlBkC,SAAS,QAAA;aAkB3C,CAjBL,mBAgBM,OAhBN,cAgBM;KAfQ,QAAA,MAAQ,aAAA,WAAA,EAApB,mBAEO,QAFP,cAEO,gBADF,QAAA,MAAQ,UAAS,GAAG,eACzB,EAAA,IAAA,mBAAA,IAAA,KAAA;KACY,QAAA,MAAQ,cAAA,WAAA,EAApB,mBAEO,QAFP,cAEO,gBADF,QAAA,MAAQ,WAAU,GAAG,gBAC1B,EAAA,IAAA,mBAAA,IAAA,KAAA;KACY,QAAA,MAAQ,SAAA,WAAA,EAApB,mBAEO,QAFP,cAEO,gBADF,QAAA,MAAQ,MAAK,GAAG,YACrB,EAAA,IAAA,mBAAA,IAAA,KAAA;KACY,QAAA,MAAQ,WAAA,WAAA,EAApB,mBAEO,QAFP,eAEO,gBADF,QAAA,MAAQ,QAAO,GAAG,aACvB,EAAA,IAAA,mBAAA,IAAA,KAAA;KACY,QAAA,MAAQ,WAAA,WAAA,EAApB,mBAEO,QAFP,eAEO,gBADF,QAAA,MAAQ,QAAO,GAAG,aACvB,EAAA,IAAA,mBAAA,IAAA,KAAA;;IAIJ,mBAiFM,OAAA;cAhFA;KAAJ,KAAI;KACJ,OAAM;KACL,OAAK,eAAE,QAAA,YAAS;MAAA,WAAK,QAAA;MAAS,WAAA;MAAA,GAAA,EAAA,CAAA;0BAE/B,mBA2EM,UAAA,MAAA,WA1EW,QAAA,QAAR,SAAI;yBADb,mBA2EM,OAAA;MAzEH,KAAK,KAAK;MACV,gBAAc,KAAK;MACnB,OAAK,eAAA,CAAA,6BAAA,8BAA8D,KAAK,SAAM,CAAA;SAE/E,WAoEO,KAAA,QAAA,QAAA,EApEkB,MAAI,QAoEtB,CAnEL,mBAoDM,OApDN,eAoDM;MAlDJ,mBAsBM,OAAA,EAtBA,OAAK,eAAA,CAAA,kCAAA,mCAAwE,KAAK,SAAM,CAAA,EAAA,EAAA,CAEjF,KAAK,WAAM,aAAA,WAAA,EAAtB,mBAGM,OAHN,eAGM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAFJ,mBAAgC,UAAA;OAAxB,IAAG;OAAI,IAAG;OAAI,GAAE;oBACxB,mBAA4E,QAAA;OAAtE,GAAE;OAAmB,kBAAe;OAAQ,mBAAgB;0BAGpD,KAAK,WAAM,gBAAA,WAAA,EAA3B,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAA8D,QAAA;OAAxD,GAAE;OAA6B,kBAAe;0BAGtC,KAAK,WAAM,eAAA,WAAA,EAA3B,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAAsF,QAAA;OAAhF,GAAE;OAA6B,kBAAe;OAAQ,mBAAgB;0BAG9D,KAAK,WAAM,WAAA,WAAA,EAA3B,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAAsF,QAAA;OAAhF,GAAE;OAA6B,kBAAe;OAAQ,mBAAgB;wCAG9E,mBAEM,OAFN,eAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAA0C,QAAA;OAApC,GAAE;OAAS,kBAAe;;MAIpC,mBAAqE,QAArE,eAAqE,gBAApB,KAAK,MAAK,EAAA,EAAA;MAGhD,KAAK,WAAM,gBAAqB,KAAK,aAAa,KAAA,KAAA,WAAA,EAA7D,mBAEM,OAFN,eAEM,CADJ,mBAA8F,OAAA;OAAzF,OAAM;OAA0C,OAAK,eAAA,EAAA,OAAA,GAAc,KAAK,SAAQ,IAAA,CAAA;;MAIvF,mBAiBM,OAjBN,eAiBM,CAfI,KAAK,WAAM,WAAA,WAAA,EADnB,mBAOS,UAAA;;OALP,MAAK;OACL,OAAM;OACL,SAAK,eAAA,WAAO,KAAI,SAAU,KAAK,GAAE,EAAA,CAAA,OAAA,CAAA;SACnC,WAED,GAAA,YAAA,IAAA,mBAAA,IAAA,KAAA,EAEQ,KAAK,WAAM,gBAAA,WAAA,EADnB,mBAOS,UAAA;;OALP,MAAK;OACL,OAAM;OACL,SAAK,eAAA,WAAO,KAAI,UAAW,KAAK,GAAE,EAAA,CAAA,OAAA,CAAA;SACpC,YAED,GAAA,YAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;SAKO,KAAK,WAAM,WAAgB,KAAK,WAAA,WAAA,EAA3C,mBAWM,OAXN,aAWM,CAVJ,mBAMS,UAAA;MALP,MAAK;MACL,OAAM;MACL,SAAK,eAAA,WAAO,YAAY,KAAK,GAAE,EAAA,CAAA,OAAA,CAAA;wBAE7B,MAAA,eAAc,CAAC,WAAW,KAAK,GAAE,GAAA,eAAA,aAAA,EAAA,GAAA,YAAA,EAE3B,MAAA,eAAc,CAAC,WAAW,KAAK,GAAE,IAAA,WAAA,EAA5C,mBAEM,OAFN,aAEM,gBADD,KAAK,QAAO,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA,CAAA,EAAA,IAAA,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE/I7B,MAAM,QAAQ;EAOd,MAAM,UAAU,mBAAkB;EAClC,MAAM,cAAc,eACjB,MAAM,YAAY,MAAM,SAAS,SAAS,KAAM,MAAM,YACzD;AAEA,cACQ,MAAM,eACX,OAAO;AACN,OAAI,MAAM,CAAC,YAAY,MACrB,SAAQ,MAAM,GAAE;KAGpB,EAAE,WAAW,MAAM,CACrB;EAGA,MAAM,iBAAiB,eACrB,MAAM,UAAU,SAAS,MAAM,WAAW,QAAQ,SAAS,MAC7D;EACA,MAAM,kBAAkB,eACtB,MAAM,WAAW,SAAS,MAAM,YAAY,QAAQ,UAAU,MAChE;EACA,MAAM,oBAAoB,eACxB,MAAM,eAAe,QAAQ,YAAY,MAC3C;EACA,MAAM,YAAY,eAChB,MAAM,WAAW,QAAQ,UAAU,MACrC;EAEA,MAAM,OAAO;EAMb,MAAM,WAAW,IAAqB,MAAM,YAAW;AAGvD,cAAY,MAAM,cAAc,QAAQ;AAAE,YAAS,QAAQ;IAAK;EAEhE,MAAM,cAAc,eAAe;GACjC,MAAM,OAAO,EAAC;AACd,OAAI,kBAAkB,MACpB,MAAK,KAAK;IAAE,OAAO;IAAW,OAAO;IAAW,CAAA;AAElD,QAAK,KAAK;IAAE,OAAO;IAAQ,OAAO;IAAQ,CAAA;AAC1C,QAAK,KAAK;IAAE,OAAO;IAAS,OAAO;IAAS,CAAA;AAC5C,UAAO;IACR;AAGD,QAAM,oBAAoB,QAAQ;AAChC,OAAI,CAAC,OAAO,SAAS,UAAU,UAC7B,UAAS,QAAQ;KAElB,EAAE,WAAW,MAAM,CAAA;EAEtB,MAAM,eAAe,eACnB,gBAAgB,SAAS,gBAAgB,MAAM,SAAS,EAC1D;EAEA,MAAM,kBAAkB,eAAe;AACrC,OAAI,CAAC,kBAAkB,OAAO,SAAU,QAAO,EAAC;AAChD,UAAO,OAAO,QAAQ,kBAAkB,MAAM,SAAQ,CACnD,QAAQ,GAAG,OAAO,MAAM,QAAQ,MAAM,KAAA,KAAa,MAAM,GAAE,CAC3D,KAAK,CAAC,KAAK,YAAY;IACtB,KAAK,IAAI,QAAQ,MAAM,IAAI;IAC3B,OAAO,OAAO,MAAM;IACrB,EAAC;IACL;EAED,SAAS,eAAe,KAAqB;AAC3C,UAAO,IAAI,QAAQ,MAAM,IAAI,CAAC,QAAQ,UAAU,MAAM,EAAE,aAAa,CAAA;;EAGvE,SAAS,gBAAgB,OAAwB;AAC/C,OAAI,UAAU,QAAQ,UAAU,KAAA,EAAW,QAAO;AAClD,OAAI,OAAO,UAAU,SAAU,QAAO,KAAK,UAAU,MAAK;AAC1D,UAAO,OAAO,MAAK;;EAGrB,SAAS,kBAAkB,SAAsC;AAC/D,UAAO,QAAQ,KAAK,SAAS;IAC3B,KAAK;IACL,OAAO,eAAe,IAAI;IAC1B,UAAU;IACV,YAAY,UAAmB,gBAAgB,MAAM;IACtD,EAAC;;EAGJ,SAAS,qBAAqB;AAC5B,OAAI,MAAM,gBACR,QAAO,KAAK,MAAM,iBAAiB,SAAQ;AAE7C,QAAK,gBAAe;;EAGtB,SAAS,oBAAoB;AAC3B,OAAI,MAAM,eACR,QAAO,KAAK,MAAM,gBAAgB,SAAQ;AAE5C,QAAK,eAAc;;;uBAKnB,mBA+IM,OA/IN,cA+IM,CA9IJ,mBAkCM,OAlCN,cAkCM,CAjCJ,mBAQM,OARN,cAQM,CAPJ,YAME,0BAAA;gBALS,SAAA;0EAAQ,QAAA;IAChB,SAAS,YAAA;IACV,SAAQ;IACR,MAAK;IACJ,cAAY;6CAGjB,mBAuBM,OAvBN,cAuBM;IArBI,QAAA,qBAAqB,QAAA,gBAAA,WAAA,EAD7B,YAOa,oBAAA;;KALX,SAAQ;KACR,MAAK;KACJ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,cAAA;;4BAEJ,CAAA,gBADT,cACS,gBAAG,QAAA,cAAU,SAAA,EAAA,EAAA,CAAA,CAAA;;;IAEvB,YAMa,oBAAA;KALX,SAAQ;KACR,MAAK;KACJ,SAAO;;4BAGV,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAFC,UAED,GAAA,CAAA,EAAA,CAAA;;;IACA,YAMa,oBAAA;KALX,SAAQ;KACR,MAAK;KACJ,SAAO;;4BAGV,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAFC,SAED,GAAA,CAAA,EAAA,CAAA;;;SAIJ,mBAyGM,OAzGN,cAyGM,CAxGO,UAAA,SAAA,WAAA,EAAX,mBAOM,OAPN,cAOM,CANJ,mBAKM,OALN,cAKM;IAJJ,YAAsC,kBAAA;KAA5B,OAAM;KAAM,QAAO;;IAC7B,YAAuC,kBAAA;KAA7B,OAAM;KAAO,QAAO;;IAC9B,YAAuC,kBAAA;KAA7B,OAAM;KAAO,QAAO;;IAC9B,YAAsC,kBAAA;KAA5B,OAAM;KAAM,QAAO;;yBAGjC,mBA+FW,UAAA,EAAA,KAAA,GAAA,EAAA,CA7FE,SAAA,UAAQ,aAAkB,kBAAA,SAAA,WAAA,EAArC,mBAsEM,OAtEN,cAsEM,CApEO,gBAAA,MAAgB,UAAA,WAAA,EAA3B,mBASM,OATN,cASM,EAAA,UAAA,KAAA,EARJ,mBAOO,UAAA,MAAA,WANW,gBAAA,QAAT,UAAK;wBADd,mBAOO,QAAA;KALJ,KAAK,MAAM;KACZ,OAAM;QAEN,mBAA2D,QAA3D,eAA2D,gBAAnB,MAAM,IAAG,EAAA,EAAA,EACjD,mBAA+D,QAA/D,eAA+D,gBAArB,MAAM,MAAK,EAAA,EAAA,CAAA,CAAA;kEAKzD,mBAuDM,UAAA,MAAA,WAtDc,kBAAA,MAAkB,WAA7B,YAAO;wBADhB,mBAuDM,OAAA;KArDH,KAAK,QAAQ;KACd,OAAM;QAGU,QAAQ,SAAI,WAAgB,QAAQ,SAAA,UAAA,KAAA,EAClD,mBA8BM,UAAA,EAAA,KAAA,GAAA,EAAA,WA7BkB,QAAQ,QAAtB,MAAM,QAAG;yBADnB,mBA8BM,OAAA;MA5BH,KAAK;MACN,OAAM;;MAEN,mBAKM,OALN,eAKM,CAJJ,mBAA+D,QAA/D,eAA+D,gBAApB,KAAK,MAAK,EAAA,EAAA,EACrD,mBAEO,QAFP,eAEO,gBADF,KAAK,WAAU,GAAG,MAAC,gBAAG,KAAK,SAAQ,EAAA,EAAA,CAAA,CAAA;MAG/B,KAAK,YAAY,OAAO,KAAK,KAAK,SAAQ,CAAE,UAAA,WAAA,EAAvD,mBASM,OATN,eASM,EAAA,UAAA,KAAA,EARJ,mBAOO,UAAA,MAAA,WANgB,KAAK,WAAlB,KAAK,QAAG;2BADlB,mBAOO,QAAA;QALJ,KAAK,OAAO,IAAG;QAChB,OAAM;WAEN,mBAAgF,QAAhF,eAAgF,gBAAxC,OAAO,IAAG,CAAE,QAAO,MAAA,IAAA,CAAA,EAAA,EAAA,EAC3D,mBAAuD,QAAvD,eAAuD,gBAAb,IAAG,EAAA,EAAA,CAAA,CAAA;;MAIzC,KAAK,KAAK,UAAA,WAAA,EADlB,YAQE,mBAAA;;OANC,MAAM,KAAK;OACX,SAAS,kBAAkB,KAAK,QAAO;OACvC,YAAY,KAAK,KAAK,SAAM;OAC5B,UAAU;OACV,SAAS;OACV,MAAK;;;;;;;gBAMU,QAAQ,SAAI,WAAgB,QAAQ,QAAA,WAAA,EAAzD,mBAaW,UAAA,EAAA,KAAA,GAAA,EAAA,CAZT,mBAGM,OAHN,eAGM,CAFJ,mBAAoE,QAApE,eAAoE,gBAAvB,QAAQ,MAAK,EAAA,EAAA,EAC1D,mBAA6E,QAA7E,eAA6E,gBAAhC,QAAQ,UAAS,GAAG,SAAK,EAAA,CAAA,CAAA,EAExE,YAOE,mBAAA;KANC,MAAM,QAAQ;KACd,SAAS,kBAAkB,QAAQ,WAAO,EAAA,CAAA;KAC1C,aAAa,QAAQ,MAAM,UAAM,KAAA;KACjC,UAAU;KACV,SAAS;KACV,MAAK;;;;;;kBAQA,SAAA,UAAQ,UAAA,WAAA,EADrB,YAOE,6BAAA;;IALC,OAAO,eAAA;IACP,cAAY;IACZ,cAAY;IACZ,eAAa;IACd,MAAK;6BAKM,SAAA,UAAQ,WAAgB,aAAA,SAAA,WAAA,EADrC,YAME,mBAAA;;IAJC,MAAM,gBAAA;IACN,SAAS,QAAA,gBAAY,EAAA;IACrB,YAAY;IACZ,UAAU;uCAEG,SAAA,UAAQ,WAAA,WAAA,EAAxB,mBAEM,OAFN,aAAsE,8CAEtE,IAAA,mBAAA,IAAA,KAAA,CAAA,EAAA,GAAA,EAAA,CAAA,CAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEnQR,MAAM,QAAQ;EAUd,MAAM,OAAO;EAIb,MAAM,aAAa,eAAe,MAAM,YAAY,MAAK;EACzD,MAAM,WAAW,eAAe,MAAM,YAAY,IAAG;EAErD,MAAM,WAAW,eAAe;AAC9B,OAAI,CAAC,MAAM,YAAY,SAAS,CAAC,MAAM,YAAY,IAAK,QAAO;AAC/D,UAAO,gBAAgB,MAAM,WAAW,OAAO,MAAM,WAAW,IAAG;IACpE;EAED,MAAM,gBAAgB,eAAe;AACnC,OAAI,SAAS,UAAU,KAAM,QAAO;AACpC,UAAO,eAAe,SAAS,MAAK;IACrC;EAED,MAAM,YAAY,eAAe;AAC/B,OAAI,CAAC,MAAM,YAAY,SAAS,CAAC,MAAM,YAAY,IAAK,QAAO;AAC/D,UAAO,YAAY,MAAM,WAAW,OAAO,MAAM,WAAW,IAAI,IAAI;IACrE;EAED,MAAM,SAAS,eAAe;AAC5B,OAAI,MAAM,YAAY,MAAO,QAAO,MAAM,WAAW;AACrD,UAAO,MAAM;IACd;EAED,SAAS,cAAc,OAA2B;AAChD,OAAI,CAAC,OAAO;AACV,SAAK,qBAAqB,KAAA,EAAS;AACnC;;AAGF,QAAK,qBAAqB;IAAE,OAAO;IAAO,KAD9B,MAAM,YAAY,OAAO;IACU,CAAA;;EAGjD,SAAS,YAAY,OAA2B;AAC9C,OAAI,CAAC,OAAO;AACV,SAAK,qBAAqB,KAAA,EAAS;AACnC;;AAGF,QAAK,qBAAqB;IAAE,OADd,MAAM,YAAY,SAAS;IACN,KAAK;IAAO,CAAA;;;uBAK/C,mBAuCM,OAvCN,cAuCM;IAtCJ,mBAaM,OAbN,cAaM,CAZJ,YAWE,oBAAA;KAVC,eAAa,WAAA;KACb,UAAU,QAAA;KACV,OAAO,QAAA,SAAS,UAAA;KAChB,MAAM,QAAA;KACN,KAAK,QAAA;KACL,KAAK,QAAA;KACL,MAAM,QAAA;KACN,QAAQ,QAAA;KACT,aAAY;KACX,uBAAoB;;;;;;;;;;;IAIzB,mBAOM,OAPN,cAOM,CALI,QAAA,gBAAgB,SAAA,UAAQ,QAAA,WAAA,EADhC,mBAKO,QAAA;;KAHJ,OAAK,eAAA,CAAA,6BAAgC,UAAA,QAAS,qCAAA,GAAA,CAAA;uBAE5C,cAAA,MAAa,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;IAIpB,mBAaM,OAbN,cAaM,CAZJ,YAWE,oBAAA;KAVC,eAAa,SAAA;KACb,UAAU,QAAA;KACV,OAAO,QAAA,SAAS,UAAA;KAChB,MAAM,QAAA;KACN,KAAK,OAAA;KACL,KAAK,QAAA;KACL,MAAM,QAAA;KACN,QAAQ,QAAA;KACT,aAAY;KACX,uBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE5F7B,MAAM,QAAQ;EASd,MAAM,OAAO;EAKb,MAAM,sBAAsD;GAC1D,aAAa;GACb,UAAU;GACV,eAAe;GACf,WAAW;GACb;EAEA,MAAM,qBAAqB,eAAe,MAAM,eAAe,oBAAoB,MAAM,QAAO;EAEhG,MAAM,cAAc,eAAe;GACjC;GACA,uBAAuB,MAAM;GAC7B,EAAE,+BAA+B,MAAM,SAAS;GACjD,CAAA;EAED,SAAS,WAAW,OAAmB;AACrC,SAAM,iBAAgB;AACtB,QAAK,OAAM;;EAGb,SAAS,cAAc;AACrB,QAAK,QAAO;;;UAMD,QAAA,WAAA,WAAA,EAAX,mBAqBM,OAAA;;IArBe,OAAK,eAAE,YAAA,MAAW;IAAG,SAAO;;IAC/C,WAEO,KAAA,QAAA,UAAA,EAAA,QAAA,CADL,mBAAiG,QAAA,EAA1F,OAAK,eAAA,CAAA,kCAAA,mCAAwE,QAAA,SAAM,CAAA,EAAA,EAAA,MAAA,EAAA,CAAA,CAAA;IAG5F,mBAAwD,QAAxD,cAAwD,gBAAd,QAAA,KAAI,EAAA,EAAA;IAE9C,mBAEO,QAAA,EAFA,OAAK,eAAA,CAAA,oCAAA,qCAA4E,QAAA,SAAM,CAAA,EAAA,EAAA,gBACzF,mBAAA,MAAkB,EAAA,EAAA;IAGvB,WASO,KAAA,QAAA,UAAA,EAAA,QAAA,CAPG,QAAA,kBAAA,WAAA,EADR,mBAOS,UAAA;;KALP,MAAK;KACL,OAAM;KACL,SAAO;OACT,SAED,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;0BAKJ,mBAqEM,OAAA;;IArEO,OAAK,eAAE,YAAA,MAAW;IAAG,SAAO;OACvC,mBAmEM,OAnEN,cAmEM;IAlEJ,mBAgCM,OAhCN,cAgCM,CA9BJ,mBASM,OATN,cASM,CARJ,WAOO,KAAA,QAAA,SAAA,EAAA,QAAA,CANM,QAAA,SAAA,WAAA,EAAX,mBAA6C,OAAA;;KAA1B,KAAK,QAAA;KAAQ,KAAK,QAAA;+CACrC,mBAIM,OAJN,cAIM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAHJ,mBAEM,OAAA;KAFD,MAAK;KAAO,QAAO;KAAe,SAAQ;QAC7C,mBAA4a,QAAA;KAAta,kBAAe;KAAQ,mBAAgB;KAAQ,gBAAa;KAAM,GAAE;uBAOlF,mBAiBM,OAjBN,YAiBM;KAhBJ,mBASM,OATN,YASM,CARJ,mBAAoD,MAApD,YAAoD,gBAAZ,QAAA,KAAI,EAAA,EAAA,EAE5C,WAKO,KAAA,QAAA,UAAA,EAAA,QAAA,CAJL,mBAGM,OAHN,aAGM,CAFJ,mBAAiG,QAAA,EAA1F,OAAK,eAAA,CAAA,kCAAA,mCAAwE,QAAA,SAAM,CAAA,EAAA,EAAA,MAAA,EAAA,EAC1F,mBAA8E,QAA9E,aAA8E,gBAA5B,mBAAA,MAAkB,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;KAKjE,QAAA,eAAA,WAAA,EAAT,mBAAmF,KAAnF,aAAmF,gBAAlB,QAAA,YAAW,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAEnE,QAAA,YAAA,WAAA,EAAT,mBAAoF,KAApF,aAAwD,eAAU,gBAAG,QAAA,SAAQ,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAEpE,QAAA,iBAAA,WAAA,EAAT,mBAA0G,KAA1G,aAAmE,qBAAgB,gBAAG,QAAA,cAAa,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;;IAK5F,QAAA,MAAM,SAAM,KAAQC,KAAAA,OAAO,SAAA,WAAA,EAAtC,mBAOM,OAPN,aAOM,CANJ,WAKO,KAAA,QAAA,SAAA,EAAA,QAAA,EAAA,UAAA,KAAA,EAJL,mBAGM,UAAA,MAAA,WAHc,QAAA,QAAR,SAAI;yBAAhB,mBAGM,OAAA;MAHsB,KAAK,KAAK;MAAO,OAAM;SACjD,mBAAoE,QAApE,aAAoE,gBAApB,KAAK,MAAK,EAAA,EAAA,EAC1D,mBAAoE,QAApE,aAAoE,gBAApB,KAAK,MAAK,EAAA,EAAA,CAAA,CAAA;;IAMrD,QAAA,KAAK,SAAM,KAAQ,QAAA,kBAAkBA,KAAAA,OAAO,UAAA,WAAA,EAAvD,mBAoBM,OApBN,aAoBM,CAnBJ,mBAEM,OAFN,aAEM,EAAA,UAAA,KAAA,EADJ,mBAAuE,UAAA,MAAA,WAA/C,QAAA,OAAP,QAAG;yBAApB,YAAuE,kBAAA;MAAxC,KAAK;MAAK,MAAK;;6BAAc,CAAA,gBAAA,gBAAN,IAAG,EAAA,EAAA,CAAA,CAAA;;;iBAG3D,mBAcM,OAdN,aAcM,CAbJ,WAYO,KAAA,QAAA,UAAA,EAAA,QAAA,CAVG,QAAA,kBAAA,WAAA,EADR,mBAUS,UAAA;;KARP,MAAK;KACL,OAAM;KACL,SAAO;sDACT,cAEC,GAAA,EAAA,mBAEM,OAAA;KAFD,OAAM;KAAK,QAAO;KAAK,MAAK;KAAO,QAAO;KAAe,SAAQ;QACpE,mBAAyF,QAAA;KAAnF,kBAAe;KAAQ,mBAAgB;KAAQ,gBAAa;KAAI,GAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEhIxF,MAAM,QAAQ;EAWd,MAAM,OAAO;EAKb,MAAM,YAAY,eAAe,MAAM,UAAU,UAAS;EAC1D,MAAM,gBAAgB,eAAe,CAAC,UAAU,MAAK;EACrD,MAAM,cAAc,eAAe,MAAM,UAAU,eAAe,MAAM,QAAQ,SAAS,EAAC;EAC1F,MAAM,YAAY,eAAe,MAAM,UAAU,WAAW,MAAM,aAAY;EAE9E,SAAS,aAAa,SAA0B;AAC9C,OAAI,CAAC,WAAW,YAAY,UAAW,QAAO;AAC9C,UAAO,iCAAiC;;;uBAKxC,mBAkEM,OAlEN,YAkEM;IAhEJ,WAAwB,KAAA,QAAA,WAAA;IAGxB,mBAoBM,OApBN,YAoBM,CAlBI,cAAA,SAAA,WAAA,EADR,YASa,oBAAA;;KAPX,SAAQ;KACR,MAAK;KACJ,UAAU,QAAA;KACX,cAAA;KACC,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,MAAA;;4BAEE,CAAA,gBAAA,gBAAX,QAAA,SAAQ,EAAA,EAAA,CAAA,CAAA;;yDAGL,UAAA,SAAA,WAAA,EADR,YAQa,oBAAA;;KANX,SAAQ;KACR,MAAK;KACL,cAAA;KACC,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAA;;4BAEK,CAAA,gBAAA,gBAAd,QAAA,YAAW,EAAA,EAAA,CAAA,CAAA;;;IAKP,UAAA,SAAA,WAAA,EAAX,mBAQM,OARN,YAQM,CAPJ,YAME,qBAAA;KALC,OAAO,QAAA;KACP,OAAO,QAAA;KACP,eAAe,QAAA;KACf,cAAU,CAAG,QAAA;KACd,MAAK;;;;;;;IAKE,YAAA,SAAA,WAAA,EAAX,mBAkBM,OAlBN,YAkBM,CAjBJ,WAgBO,KAAA,QAAA,WAAA,EAAA,QAAA,CAfL,mBAcM,OAdN,YAcM,EAAA,UAAA,KAAA,EAbJ,mBAYM,UAAA,MAAA,WAXkB,QAAA,UAAd,MAAM,QAAG;yBADnB,mBAYM,OAAA;MAVH,KAAK;MACN,OAAM;SAEN,mBAAkE,QAAlE,YAAkE,gBAApB,KAAK,MAAK,EAAA,EAAA,EACxD,mBAKO,QAAA,EAJL,OAAK,eAAA,CAAC,gCACE,aAAa,KAAK,QAAO,CAAA,CAAA,EAAA,EAAA,gBAE9B,KAAK,MAAK,EAAA,EAAA,CAAA,CAAA;;IAQP,UAAA,SAAA,WAAA,EAAhB,YAEW,kBAAA;;KAFgB,MAAK;;4BACZ,CAAA,gBAAA,gBAAf,QAAA,aAAY,EAAA,EAAA,CAAA,CAAA;;;IAIjB,WAAsB,KAAA,QAAA,SAAA"}
|