@morscherlab/mint-sdk 1.4.0-beta.20 → 1.4.0-beta.21

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.
Files changed (97) hide show
  1. package/dist/{AlertBox-CzLg0UaL.js → AlertBox-DwgNTEqf.js} +3 -3
  2. package/dist/{AlertBox-CzLg0UaL.js.map → AlertBox-DwgNTEqf.js.map} +1 -1
  3. package/dist/{AppSidebar-6oTT-gPO.js → AppSidebar-CZZaxni0.js} +7 -7
  4. package/dist/{AppSidebar-6oTT-gPO.js.map → AppSidebar-CZZaxni0.js.map} +1 -1
  5. package/dist/{ConfirmDialog-CSls2Nuv.js → ConfirmDialog-CjWoxAD1.js} +2 -2
  6. package/dist/{ConfirmDialog-CSls2Nuv.js.map → ConfirmDialog-CjWoxAD1.js.map} +1 -1
  7. package/dist/{ExperimentPopover-9l4QZlvQ.js → ExperimentPopover-DujiuQIO.js} +5 -5
  8. package/dist/{ExperimentPopover-9l4QZlvQ.js.map → ExperimentPopover-DujiuQIO.js.map} +1 -1
  9. package/dist/{ExperimentSelectorModal-pR5fK3pA.js → ExperimentSelectorModal-B4j4eUOJ.js} +5 -5
  10. package/dist/{ExperimentSelectorModal-pR5fK3pA.js.map → ExperimentSelectorModal-B4j4eUOJ.js.map} +1 -1
  11. package/dist/{SegmentedControl-CJ7EAmOq.js → SegmentedControl-DyUPH3I5.js} +2 -2
  12. package/dist/{SegmentedControl-CJ7EAmOq.js.map → SegmentedControl-DyUPH3I5.js.map} +1 -1
  13. package/dist/{SettingsModal-Dyitgzfp.js → SettingsModal-D2MOOtuO.js} +7 -7
  14. package/dist/{SettingsModal-Dyitgzfp.js.map → SettingsModal-D2MOOtuO.js.map} +1 -1
  15. package/dist/__tests__/components/FloatingPanelsInModal.test.d.ts +1 -0
  16. package/dist/__tests__/composables/useFloatingPanel.test.d.ts +1 -0
  17. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -2
  18. package/dist/components/internal/AutoGroupModalInternal.vue.d.ts +2 -2
  19. package/dist/composables/useFileImport.d.ts +7 -0
  20. package/dist/composables/useFloatingPanel.d.ts +29 -0
  21. package/dist/{controlSchemaModel-DCUOx8ll.js → controlSchemaModel-Bsg0t5Ab.js} +2 -2
  22. package/dist/{controlSchemaModel-DCUOx8ll.js.map → controlSchemaModel-Bsg0t5Ab.js.map} +1 -1
  23. package/dist/{data-DBnmeBZX.js → data-Boeh4uPI.js} +12 -11
  24. package/dist/{data-DBnmeBZX.js.map → data-Boeh4uPI.js.map} +1 -1
  25. package/dist/data.d.ts +1 -1
  26. package/dist/data.js +9 -9
  27. package/dist/{experimentStatus-BFrKpHAH.js → experimentStatus-DCn2QKG7.js} +2 -2
  28. package/dist/{experimentStatus-BFrKpHAH.js.map → experimentStatus-DCn2QKG7.js.map} +1 -1
  29. package/dist/index.js +18 -18
  30. package/dist/{install-qjELyNwc.js → install-BEHp7Mo-.js} +15 -15
  31. package/dist/{install-qjELyNwc.js.map → install-BEHp7Mo-.js.map} +1 -1
  32. package/dist/install.js +1 -1
  33. package/dist/{lab-UyTbokQC.js → lab-D61eaX6f.js} +10 -10
  34. package/dist/{lab-UyTbokQC.js.map → lab-D61eaX6f.js.map} +1 -1
  35. package/dist/lab.js +4 -4
  36. package/dist/{records-CR_pYzIP.js → records-D4aQXyZu.js} +132 -72
  37. package/dist/records-D4aQXyZu.js.map +1 -0
  38. package/dist/{runtime-CoHhBP10.js → runtime-q-R0CNLh.js} +9 -9
  39. package/dist/{runtime-CoHhBP10.js.map → runtime-q-R0CNLh.js.map} +1 -1
  40. package/dist/runtime.js +6 -6
  41. package/dist/{shell-BDuZf2zb.js → shell-6CGFvUUw.js} +5 -5
  42. package/dist/{shell-BDuZf2zb.js.map → shell-6CGFvUUw.js.map} +1 -1
  43. package/dist/shell.js +5 -5
  44. package/dist/styles.css +36 -33
  45. package/dist/{useDropdownState-Bb2N4m40.js → useDropdownState-uFIz6Yv8.js} +2 -2
  46. package/dist/useDropdownState-uFIz6Yv8.js.map +1 -0
  47. package/dist/{useExperimentSamples-Dno1fFMq.js → useExperimentSamples-QEFoq4tf.js} +47 -5
  48. package/dist/useExperimentSamples-QEFoq4tf.js.map +1 -0
  49. package/dist/{useListReorder-CQ9OCScc.js → useListReorder-DsuzwWGS.js} +47 -97
  50. package/dist/useListReorder-DsuzwWGS.js.map +1 -0
  51. package/dist/{useRackEditor-wL3kPrRT.js → useRackEditor-KpZ4iJ6o.js} +20 -30
  52. package/dist/useRackEditor-KpZ4iJ6o.js.map +1 -0
  53. package/dist/{useRealtime-C3whhVvb.js → useRealtime-B58Lo-Tm.js} +3 -3
  54. package/dist/{useRealtime-C3whhVvb.js.map → useRealtime-B58Lo-Tm.js.map} +1 -1
  55. package/dist/{useRequest-CDkrXNU7.js → useRequest-BuDmW8Jc.js} +7 -36
  56. package/dist/useRequest-BuDmW8Jc.js.map +1 -0
  57. package/package.json +3 -2
  58. package/src/__tests__/components/ActionMenu.test.ts +2 -2
  59. package/src/__tests__/components/BaseModal.test.ts +13 -0
  60. package/src/__tests__/components/FloatingPanelsInModal.test.ts +94 -0
  61. package/src/__tests__/composables/useFileImport.test.ts +36 -0
  62. package/src/__tests__/composables/useFloatingPanel.test.ts +68 -0
  63. package/src/__tests__/fixtures/table-import/cells.xlsx +0 -0
  64. package/src/__tests__/fixtures/table-import/samples.xlsx +0 -0
  65. package/src/components/ActionMenu.vue +6 -37
  66. package/src/components/BaseModal.vue +2 -1
  67. package/src/components/Tooltip.vue +9 -32
  68. package/src/components/internal/DatePickerDateInternal.vue +7 -45
  69. package/src/components/internal/DatePickerDateTimeInternal.vue +7 -1
  70. package/src/components/internal/DatePickerTimeInternal.vue +8 -1
  71. package/src/components/internal/NumberUnitInputInternal.vue +4 -18
  72. package/src/components/internal/SelectMultipleInternal.vue +6 -1
  73. package/src/components/internal/SelectSearchableInternal.vue +6 -1
  74. package/src/components/internal/WellEditPopupInternal.vue +11 -22
  75. package/src/composables/useFileImport.ts +51 -1
  76. package/src/composables/useFloatingPanel.ts +107 -0
  77. package/src/data.ts +1 -0
  78. package/src/styles/components/action-menu.css +1 -1
  79. package/src/styles/components/app-layout.css +3 -1
  80. package/src/styles/components/date-picker.css +4 -2
  81. package/src/styles/components/datetime-picker.css +4 -4
  82. package/src/styles/components/jobs-status-tray.css +3 -3
  83. package/src/styles/components/modal.css +1 -1
  84. package/src/styles/components/onboarding.css +2 -2
  85. package/src/styles/components/searchable-select.css +3 -7
  86. package/src/styles/components/time-picker.css +3 -6
  87. package/src/styles/components/toast.css +1 -1
  88. package/src/styles/components/tooltip.css +2 -2
  89. package/src/styles/components/unit-input.css +1 -1
  90. package/src/styles/components/well-edit-popup.css +2 -2
  91. package/src/styles/variables.css +6 -0
  92. package/dist/records-CR_pYzIP.js.map +0 -1
  93. package/dist/useDropdownState-Bb2N4m40.js.map +0 -1
  94. package/dist/useExperimentSamples-Dno1fFMq.js.map +0 -1
  95. package/dist/useListReorder-CQ9OCScc.js.map +0 -1
  96. package/dist/useRackEditor-wL3kPrRT.js.map +0 -1
  97. package/dist/useRequest-CDkrXNU7.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useListReorder-DsuzwWGS.js","names":[],"sources":["../src/components/BaseRadioGroup.vue","../src/components/BaseRadioGroup.vue","../src/components/BaseSlider.vue","../src/components/BaseSlider.vue","../src/composables/useSortedItems.ts","../src/composables/componentSpecs.ts","../src/composables/useCalendarGrid.ts","../src/components/internal/CalendarGridPanelInternal.vue","../src/components/internal/CalendarGridPanelInternal.vue","../src/components/internal/DatePickerDateInternal.vue","../src/components/internal/DatePickerDateInternal.vue","../src/components/internal/DatePickerDateTimeInternal.vue","../src/components/internal/DatePickerDateTimeInternal.vue","../src/components/internal/DatePickerTimeInternal.vue","../src/components/internal/DatePickerTimeInternal.vue","../src/components/DatePicker.vue","../src/components/DatePicker.vue","../src/components/internal/NumberUnitInputInternal.vue","../src/components/internal/NumberUnitInputInternal.vue","../src/components/NumberInput.vue","../src/components/NumberInput.vue","../src/composables/usePlatform.ts","../src/composables/eventStream.ts","../src/composables/useJobsEndpoint.ts","../src/components/internal/generatedJobsApi.ts","../src/components/GeneratedPathInput.vue","../src/components/GeneratedPathInput.vue","../src/utils/formula.ts","../src/components/FormulaInput.vue","../src/components/FormulaInput.vue","../src/composables/useSequenceUtils.ts","../src/components/SequenceInput.vue","../src/components/SequenceInput.vue","../src/components/MoleculeInput.vue","../src/components/MoleculeInput.vue","../src/composables/useConcentrationUnits.ts","../src/components/ConcentrationInput.vue","../src/components/ConcentrationInput.vue","../src/composables/formBuilderRegistry.ts","../src/composables/controlSchemaUtils.ts","../src/composables/controlSchemaNormalize.ts","../src/components/StepWizard.vue","../src/components/StepWizard.vue","../src/composables/useListReorder.ts"],"sourcesContent":["<script setup lang=\"ts\">\n/** Renders a group of radio buttons in list or tile layout, horizontal or vertical. */\nimport { computed } from 'vue'\nimport type { OptionPrimitive, SelectOption, SelectOptionInput } from '../types'\nimport { normalizeOptionInput } from '../utils/options'\n\ninterface Props {\n modelValue?: string | number\n options: SelectOptionInput[]\n name: string\n disabled?: boolean\n direction?: 'horizontal' | 'vertical'\n size?: 'sm' | 'md' | 'lg'\n variant?: 'list' | 'tile'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n direction: 'vertical',\n size: 'md',\n variant: 'list',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number]\n}>()\n\nconst normalizedOptions = computed<SelectOption<OptionPrimitive>[]>(() => props.options.map(normalizeOptionInput<OptionPrimitive, SelectOption<OptionPrimitive>>))\n\nfunction handleChange(value: string | number) {\n emit('update:modelValue', value)\n}\n</script>\n\n<template>\n <div\n :class=\"['mint-radio-group', `mint-radio-group--${props.direction}`, `mint-radio-group--${props.variant}`]\"\n role=\"radiogroup\"\n >\n <label\n v-for=\"option in normalizedOptions\"\n :key=\"String(option.value)\"\n :class=\"[\n 'mint-radio-option',\n props.modelValue === option.value ? 'mint-radio-option--selected' : '',\n (props.disabled || option.disabled) ? 'mint-radio-option--disabled' : '',\n ]\"\n >\n <template v-if=\"props.variant === 'list'\">\n <div class=\"mint-radio-option__input-wrapper\">\n <input\n type=\"radio\"\n :name=\"props.name\"\n :value=\"option.value\"\n :checked=\"props.modelValue === option.value\"\n :disabled=\"props.disabled || option.disabled\"\n :aria-describedby=\"option.description ? `${props.name}-${option.value}-desc` : undefined\"\n class=\"mint-radio-option__native\"\n @change=\"handleChange(option.value)\"\n />\n <div\n :class=\"[\n 'mint-radio-option__circle',\n `mint-radio-option__circle--${props.size}`,\n props.modelValue === option.value ? 'mint-radio-option__circle--checked' : '',\n ]\"\n >\n <div\n v-if=\"props.modelValue === option.value\"\n :class=\"['mint-radio-option__dot', `mint-radio-option__dot--${props.size}`]\"\n />\n </div>\n </div>\n <div class=\"mint-radio-option__content\">\n <span :class=\"['mint-radio-option__label', `mint-radio-option__label--${props.size}`]\">\n {{ option.label }}\n </span>\n <span\n v-if=\"option.description\"\n :id=\"`${props.name}-${option.value}-desc`\"\n :class=\"['mint-radio-option__description', `mint-radio-option__description--${props.size}`]\"\n >\n {{ option.description }}\n </span>\n </div>\n </template>\n\n <template v-else>\n <input\n type=\"radio\"\n :name=\"props.name\"\n :value=\"option.value\"\n :checked=\"props.modelValue === option.value\"\n :disabled=\"props.disabled || option.disabled\"\n :aria-describedby=\"option.description ? `${props.name}-${option.value}-desc` : undefined\"\n class=\"mint-radio-option__native\"\n @change=\"handleChange(option.value)\"\n />\n <div class=\"mint-radio-option__head\">\n <div\n :class=\"[\n 'mint-radio-option__circle',\n `mint-radio-option__circle--${props.size}`,\n props.modelValue === option.value ? 'mint-radio-option__circle--checked' : '',\n ]\"\n >\n <div\n v-if=\"props.modelValue === option.value\"\n :class=\"['mint-radio-option__dot', `mint-radio-option__dot--${props.size}`]\"\n />\n </div>\n <span class=\"mint-radio-option__title\">{{ option.label }}</span>\n </div>\n <span\n v-if=\"option.description\"\n :id=\"`${props.name}-${option.value}-desc`\"\n class=\"mint-radio-option__description\"\n >\n {{ option.description }}\n </span>\n </template>\n </label>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Renders a group of radio buttons in list or tile layout, horizontal or vertical. */\nimport { computed } from 'vue'\nimport type { OptionPrimitive, SelectOption, SelectOptionInput } from '../types'\nimport { normalizeOptionInput } from '../utils/options'\n\ninterface Props {\n modelValue?: string | number\n options: SelectOptionInput[]\n name: string\n disabled?: boolean\n direction?: 'horizontal' | 'vertical'\n size?: 'sm' | 'md' | 'lg'\n variant?: 'list' | 'tile'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n direction: 'vertical',\n size: 'md',\n variant: 'list',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number]\n}>()\n\nconst normalizedOptions = computed<SelectOption<OptionPrimitive>[]>(() => props.options.map(normalizeOptionInput<OptionPrimitive, SelectOption<OptionPrimitive>>))\n\nfunction handleChange(value: string | number) {\n emit('update:modelValue', value)\n}\n</script>\n\n<template>\n <div\n :class=\"['mint-radio-group', `mint-radio-group--${props.direction}`, `mint-radio-group--${props.variant}`]\"\n role=\"radiogroup\"\n >\n <label\n v-for=\"option in normalizedOptions\"\n :key=\"String(option.value)\"\n :class=\"[\n 'mint-radio-option',\n props.modelValue === option.value ? 'mint-radio-option--selected' : '',\n (props.disabled || option.disabled) ? 'mint-radio-option--disabled' : '',\n ]\"\n >\n <template v-if=\"props.variant === 'list'\">\n <div class=\"mint-radio-option__input-wrapper\">\n <input\n type=\"radio\"\n :name=\"props.name\"\n :value=\"option.value\"\n :checked=\"props.modelValue === option.value\"\n :disabled=\"props.disabled || option.disabled\"\n :aria-describedby=\"option.description ? `${props.name}-${option.value}-desc` : undefined\"\n class=\"mint-radio-option__native\"\n @change=\"handleChange(option.value)\"\n />\n <div\n :class=\"[\n 'mint-radio-option__circle',\n `mint-radio-option__circle--${props.size}`,\n props.modelValue === option.value ? 'mint-radio-option__circle--checked' : '',\n ]\"\n >\n <div\n v-if=\"props.modelValue === option.value\"\n :class=\"['mint-radio-option__dot', `mint-radio-option__dot--${props.size}`]\"\n />\n </div>\n </div>\n <div class=\"mint-radio-option__content\">\n <span :class=\"['mint-radio-option__label', `mint-radio-option__label--${props.size}`]\">\n {{ option.label }}\n </span>\n <span\n v-if=\"option.description\"\n :id=\"`${props.name}-${option.value}-desc`\"\n :class=\"['mint-radio-option__description', `mint-radio-option__description--${props.size}`]\"\n >\n {{ option.description }}\n </span>\n </div>\n </template>\n\n <template v-else>\n <input\n type=\"radio\"\n :name=\"props.name\"\n :value=\"option.value\"\n :checked=\"props.modelValue === option.value\"\n :disabled=\"props.disabled || option.disabled\"\n :aria-describedby=\"option.description ? `${props.name}-${option.value}-desc` : undefined\"\n class=\"mint-radio-option__native\"\n @change=\"handleChange(option.value)\"\n />\n <div class=\"mint-radio-option__head\">\n <div\n :class=\"[\n 'mint-radio-option__circle',\n `mint-radio-option__circle--${props.size}`,\n props.modelValue === option.value ? 'mint-radio-option__circle--checked' : '',\n ]\"\n >\n <div\n v-if=\"props.modelValue === option.value\"\n :class=\"['mint-radio-option__dot', `mint-radio-option__dot--${props.size}`]\"\n />\n </div>\n <span class=\"mint-radio-option__title\">{{ option.label }}</span>\n </div>\n <span\n v-if=\"option.description\"\n :id=\"`${props.name}-${option.value}-desc`\"\n class=\"mint-radio-option__description\"\n >\n {{ option.description }}\n </span>\n </template>\n </label>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Renders a range slider with a filled track, optional value display, and size variants.\n * Optional color stops, value thresholds, and end labels turn it into a threshold slider.\n */\nimport { computed, ref } from 'vue'\n\ninterface SliderColorStop {\n /** Position along the track, in percent (0-100). */\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 size?: 'sm' | 'md' | 'lg'\n /** Paint the track with a gradient through these stops instead of the primary fill. */\n colorStops?: SliderColorStop[]\n /** Tone the value badge by percent of range: success up to the first, warning up to the second, error above. */\n thresholds?: [number, number]\n /** Show the range ends under the slider. */\n showLabels?: boolean\n /** Label for the low end. Defaults to `min`. */\n minLabel?: string\n /** Label for the high end. Defaults to `max`. */\n maxLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n min: 0,\n max: 100,\n step: 1,\n disabled: false,\n showValue: true,\n size: 'md',\n showLabels: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number]\n}>()\n\nconst isDragging = ref(false)\n\nconst sizeConfig = computed(() => {\n switch (props.size) {\n case 'sm': return { height: 2, thumb: 12 }\n case 'lg': return { height: 6, thumb: 18 }\n default: return { height: 4, thumb: 14 }\n }\n})\n\nconst currentValue = computed(() => {\n return props.modelValue ?? props.min\n})\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// Thumb-in-bounds offset (Radix UI pattern). Without this, the thumb's\n// center lands at left=0%/100% and overhangs the track by halfWidth at\n// each end. Linearly interpolating an inward push of halfWidth at percent=0\n// to halfWidth-pull at percent=100 keeps the thumb's *edges* — not its\n// center — aligned with the track edges.\nconst thumbPosition = computed(() => {\n const halfWidth = sizeConfig.value.thumb / 2\n const offsetPx = (halfWidth * (50 - percentage.value)) / 50\n return `calc(${percentage.value}% + ${offsetPx}px)`\n})\n\nconst trackBackground = computed(() => {\n if (!props.colorStops) return undefined\n const stops = props.colorStops.map(stop => `${stop.color} ${stop.value}%`).join(', ')\n return `linear-gradient(to right, ${stops})`\n})\n\nconst valueTone = computed(() => {\n if (!props.thresholds) return undefined\n const [warningAbove, errorAbove] = props.thresholds\n if (percentage.value <= warningAbove) return 'success'\n if (percentage.value <= errorAbove) return 'warning'\n return 'error'\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\n class=\"mint-slider\"\n :class=\"{ 'mint-slider--disabled': disabled, 'mint-slider--labeled': showLabels }\"\n >\n <div class=\"mint-slider__container\">\n <!-- Track background -->\n <div\n class=\"mint-slider__track\"\n :style=\"{ height: `${sizeConfig.height}px`, background: trackBackground }\"\n />\n\n <!-- Filled track — extends to the thumb's center so the two visually\n meet at every position (no gap at the right edge, no overshoot\n at the left). A gradient track has no fill. -->\n <div\n v-if=\"!colorStops\"\n class=\"mint-slider__fill\"\n :style=\"{\n height: `${sizeConfig.height}px`,\n width: thumbPosition,\n }\"\n />\n\n <!-- Native range input -->\n <input\n type=\"range\"\n class=\"mint-slider__input\"\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 @input=\"handleInput\"\n @mousedown=\"isDragging = true\"\n @mouseup=\"isDragging = false\"\n @touchstart.passive=\"isDragging = true\"\n @touchend=\"isDragging = false\"\n />\n\n <!-- Custom thumb — translateX(-50%) centers it on `left` -->\n <div\n class=\"mint-slider__thumb\"\n :class=\"{ 'mint-slider__thumb--dragging': isDragging }\"\n :style=\"{\n width: `${sizeConfig.thumb}px`,\n height: `${sizeConfig.thumb}px`,\n left: thumbPosition,\n }\"\n />\n </div>\n\n <!-- Value display -->\n <span\n v-if=\"showValue\"\n class=\"mint-slider__value\"\n :class=\"valueTone && `mint-slider__value--${valueTone}`\"\n >\n {{ currentValue }}\n </span>\n\n <!-- Range end labels -->\n <div v-if=\"showLabels\" class=\"mint-slider__labels\">\n <span>{{ minLabel ?? min }}</span>\n <span>{{ maxLabel ?? max }}</span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Renders a range slider with a filled track, optional value display, and size variants.\n * Optional color stops, value thresholds, and end labels turn it into a threshold slider.\n */\nimport { computed, ref } from 'vue'\n\ninterface SliderColorStop {\n /** Position along the track, in percent (0-100). */\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 size?: 'sm' | 'md' | 'lg'\n /** Paint the track with a gradient through these stops instead of the primary fill. */\n colorStops?: SliderColorStop[]\n /** Tone the value badge by percent of range: success up to the first, warning up to the second, error above. */\n thresholds?: [number, number]\n /** Show the range ends under the slider. */\n showLabels?: boolean\n /** Label for the low end. Defaults to `min`. */\n minLabel?: string\n /** Label for the high end. Defaults to `max`. */\n maxLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n min: 0,\n max: 100,\n step: 1,\n disabled: false,\n showValue: true,\n size: 'md',\n showLabels: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number]\n}>()\n\nconst isDragging = ref(false)\n\nconst sizeConfig = computed(() => {\n switch (props.size) {\n case 'sm': return { height: 2, thumb: 12 }\n case 'lg': return { height: 6, thumb: 18 }\n default: return { height: 4, thumb: 14 }\n }\n})\n\nconst currentValue = computed(() => {\n return props.modelValue ?? props.min\n})\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// Thumb-in-bounds offset (Radix UI pattern). Without this, the thumb's\n// center lands at left=0%/100% and overhangs the track by halfWidth at\n// each end. Linearly interpolating an inward push of halfWidth at percent=0\n// to halfWidth-pull at percent=100 keeps the thumb's *edges* — not its\n// center — aligned with the track edges.\nconst thumbPosition = computed(() => {\n const halfWidth = sizeConfig.value.thumb / 2\n const offsetPx = (halfWidth * (50 - percentage.value)) / 50\n return `calc(${percentage.value}% + ${offsetPx}px)`\n})\n\nconst trackBackground = computed(() => {\n if (!props.colorStops) return undefined\n const stops = props.colorStops.map(stop => `${stop.color} ${stop.value}%`).join(', ')\n return `linear-gradient(to right, ${stops})`\n})\n\nconst valueTone = computed(() => {\n if (!props.thresholds) return undefined\n const [warningAbove, errorAbove] = props.thresholds\n if (percentage.value <= warningAbove) return 'success'\n if (percentage.value <= errorAbove) return 'warning'\n return 'error'\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\n class=\"mint-slider\"\n :class=\"{ 'mint-slider--disabled': disabled, 'mint-slider--labeled': showLabels }\"\n >\n <div class=\"mint-slider__container\">\n <!-- Track background -->\n <div\n class=\"mint-slider__track\"\n :style=\"{ height: `${sizeConfig.height}px`, background: trackBackground }\"\n />\n\n <!-- Filled track — extends to the thumb's center so the two visually\n meet at every position (no gap at the right edge, no overshoot\n at the left). A gradient track has no fill. -->\n <div\n v-if=\"!colorStops\"\n class=\"mint-slider__fill\"\n :style=\"{\n height: `${sizeConfig.height}px`,\n width: thumbPosition,\n }\"\n />\n\n <!-- Native range input -->\n <input\n type=\"range\"\n class=\"mint-slider__input\"\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 @input=\"handleInput\"\n @mousedown=\"isDragging = true\"\n @mouseup=\"isDragging = false\"\n @touchstart.passive=\"isDragging = true\"\n @touchend=\"isDragging = false\"\n />\n\n <!-- Custom thumb — translateX(-50%) centers it on `left` -->\n <div\n class=\"mint-slider__thumb\"\n :class=\"{ 'mint-slider__thumb--dragging': isDragging }\"\n :style=\"{\n width: `${sizeConfig.thumb}px`,\n height: `${sizeConfig.thumb}px`,\n left: thumbPosition,\n }\"\n />\n </div>\n\n <!-- Value display -->\n <span\n v-if=\"showValue\"\n class=\"mint-slider__value\"\n :class=\"valueTone && `mint-slider__value--${valueTone}`\"\n >\n {{ currentValue }}\n </span>\n\n <!-- Range end labels -->\n <div v-if=\"showLabels\" class=\"mint-slider__labels\">\n <span>{{ minLabel ?? min }}</span>\n <span>{{ maxLabel ?? max }}</span>\n </div>\n </div>\n</template>\n","import { computed, toValue, type ComputedRef, type Ref } from 'vue'\n\nexport type SortedItemsSource<T> =\n | T\n | Ref<T>\n | ComputedRef<T>\n | (() => T)\n\nexport type SortOrder = 'asc' | 'desc'\n\nexport interface SortDescriptor<TKey extends string = string> {\n key: TKey\n direction?: SortOrder | null\n}\n\nexport interface CompareSortValuesOptions {\n caseSensitive?: boolean\n}\n\nexport interface SortComparatorContext<TItem, TKey extends string = string> {\n key: TKey\n direction: SortOrder\n a: TItem\n b: TItem\n}\n\nexport type SortComparator<TItem, TKey extends string = string> = (\n aValue: unknown,\n bValue: unknown,\n context: SortComparatorContext<TItem, TKey>,\n) => number\n\nexport interface UseSortedItemsOptions<TItem, TKey extends string = string> {\n items: SortedItemsSource<readonly TItem[]>\n sort: SortedItemsSource<SortDescriptor<TKey> | null | undefined>\n enabled?: SortedItemsSource<boolean | null | undefined>\n caseSensitive?: SortedItemsSource<boolean | null | undefined>\n getValue: (item: TItem, key: TKey) => unknown\n compare?: SortComparator<TItem, TKey>\n}\n\nexport interface UseSortedItemsReturn<TItem, TKey extends string = string> {\n sort: ComputedRef<SortDescriptor<TKey> | null | undefined>\n sortedItems: ComputedRef<TItem[]>\n}\n\n/** Shared sorting for SDK tables and lists with stable empty-value handling. */\nexport function useSortedItems<TItem, TKey extends string = string>(\n options: UseSortedItemsOptions<TItem, TKey>,\n): UseSortedItemsReturn<TItem, TKey> {\n const sort = computed(() => toValue(options.sort))\n const enabled = computed(() => toValue(options.enabled) ?? true)\n\n const sortedItems = computed(() => {\n const items = [...toValue(options.items)]\n const activeSort = sort.value\n\n if (!enabled.value || !activeSort?.key || !activeSort.direction) return items\n\n const direction = activeSort.direction\n const caseSensitive = toValue(options.caseSensitive) ?? true\n\n return items.sort((a, b) => {\n const aValue = options.getValue(a, activeSort.key)\n const bValue = options.getValue(b, activeSort.key)\n\n if (options.compare) {\n return options.compare(aValue, bValue, {\n key: activeSort.key,\n direction,\n a,\n b,\n })\n }\n\n return compareSortValues(aValue, bValue, direction, { caseSensitive })\n })\n })\n\n return {\n sort,\n sortedItems,\n }\n}\n\nexport function compareSortValues(\n aValue: unknown,\n bValue: unknown,\n direction: SortOrder = 'asc',\n options: CompareSortValuesOptions = {},\n): number {\n const aEmpty = aValue === null || aValue === undefined\n const bEmpty = bValue === null || bValue === undefined\n\n if (aEmpty && bEmpty) return 0\n if (aEmpty) return 1\n if (bEmpty) return -1\n\n const aComparable = toComparableSortValue(aValue, options)\n const bComparable = toComparableSortValue(bValue, options)\n\n let comparison = 0\n if (typeof aComparable === 'number' && typeof bComparable === 'number') {\n comparison = aComparable - bComparable\n } else {\n comparison = String(aComparable).localeCompare(String(bComparable))\n }\n\n return direction === 'asc' ? comparison : -comparison\n}\n\nfunction toComparableSortValue(value: unknown, options: CompareSortValuesOptions): unknown {\n if (value instanceof Date) return value.getTime()\n if (typeof value === 'string' && options.caseSensitive === false) {\n return value.toLowerCase()\n }\n return value\n}\n","// `define<ComponentName>` spec builders (spec R14, 3.2). Each one is a typed\n// identity: the component applies the defaults, so `:spec` stays optional.\nimport type { DataFrameSpec, PlateEditorSpec, PluginWorkspaceViewSpec, WellPlateSpec } from '../types'\n\n/** A spec whose defaulted keys are no longer optional. */\nexport type ResolvedSpec<S, D> = Omit<S, keyof D> & { [K in keyof D]-?: K extends keyof S ? Exclude<S[K], undefined> : never }\n\n/** Spread `spec` over `defaults`, skipping keys set to `undefined` (as prop defaults do). */\nexport function applySpecDefaults<S extends object, D extends { [K in keyof S]?: S[K] }>(\n defaults: D,\n spec: S | undefined,\n): ResolvedSpec<S, D> {\n const result: Record<string, unknown> = { ...defaults }\n for (const [key, value] of Object.entries(spec ?? {})) {\n if (value !== undefined) result[key] = value\n }\n return result as ResolvedSpec<S, D>\n}\n\n/**\n * Build the `:spec` of a PluginWorkspaceView: title, top bar, sidebar, experiment save.\n *\n * @example Recommended\n * ```ts\n * const workspace = definePluginWorkspaceView({\n * title: 'DRP Analysis',\n * topBar: { pills: pillNav, settings: () => (canConfigure.value ? settingsConfig : false) },\n * sidebar: { title: 'Parameters', controls: defineControlModel({ controls }) },\n * experiment: true,\n * save: { handler: saveDesign, disabled: saveBlocked, reason: saveBlockedReason },\n * })\n * ```\n */\nexport function definePluginWorkspaceView(spec: PluginWorkspaceViewSpec): PluginWorkspaceViewSpec {\n return spec\n}\n\n/**\n * Build the `:spec` of a DataFrame: columns, row key, paging, search, selection.\n *\n * @example Recommended\n * ```ts\n * const table = defineDataFrame({\n * columns: [{ key: 'sample', label: 'Sample', sortable: true }],\n * rowKey: 'id',\n * pagination: { pageSize: 25 },\n * })\n * ```\n */\nexport function defineDataFrame(spec: DataFrameSpec): DataFrameSpec {\n return spec\n}\n\n/** Build the `:spec` of a WellPlate: format, selection mode, display options, heatmap, well editor. */\nexport function defineWellPlate(spec: WellPlateSpec): WellPlateSpec {\n return spec\n}\n\n/** Build the `:spec` of a PlateEditor: formats, slots, groups, inspector, import/export, clear, fill series. */\nexport function definePlateEditor(spec: PlateEditorSpec): PlateEditorSpec {\n return spec\n}\n","import { computed, ref, type ComputedRef, type Ref } from 'vue'\n\nexport interface CalendarGridDay {\n date: Date\n isCurrentMonth: boolean\n isDisabled: boolean\n}\n\nexport interface UseCalendarGridOptions {\n min?: () => string | undefined\n max?: () => string | undefined\n locale?: () => string | undefined\n}\n\nexport interface UseCalendarGridReturn {\n currentMonth: Ref<Date>\n weekDays: readonly string[]\n monthYear: ComputedRef<string>\n calendarDays: ComputedRef<CalendarGridDay[]>\n isDateDisabled: (date: Date) => boolean\n isSameDay: (date: Date, other: Date | null | undefined) => boolean\n isToday: (date: Date) => boolean\n formatDateValue: (date: Date) => string\n setCurrentMonthFromDate: (date: Date) => void\n prevMonth: () => void\n nextMonth: () => void\n}\n\nconst WEEK_DAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'] as const\n\n/** Shared Monday-first calendar grid state for date and datetime pickers. */\nexport function useCalendarGrid(options: UseCalendarGridOptions = {}): UseCalendarGridReturn {\n const currentMonth = ref(new Date())\n\n const monthYear = computed(() => {\n return currentMonth.value.toLocaleDateString(options.locale?.() ?? 'en-US', {\n year: 'numeric',\n month: 'long',\n })\n })\n\n const calendarDays = computed<CalendarGridDay[]>(() => {\n const year = currentMonth.value.getFullYear()\n const month = currentMonth.value.getMonth()\n const firstDay = new Date(year, month, 1)\n const lastDay = new Date(year, month + 1, 0)\n const days: CalendarGridDay[] = []\n\n // Weeks start on Monday.\n const startPadding = (firstDay.getDay() + 6) % 7\n for (let i = startPadding - 1; i >= 0; i--) {\n const date = new Date(year, month, -i)\n days.push({ date, isCurrentMonth: false, isDisabled: isDateDisabled(date) })\n }\n\n for (let i = 1; i <= lastDay.getDate(); i++) {\n const date = new Date(year, month, i)\n days.push({ date, isCurrentMonth: true, isDisabled: isDateDisabled(date) })\n }\n\n const endPadding = 42 - days.length\n for (let i = 1; i <= endPadding; i++) {\n const date = new Date(year, month + 1, i)\n days.push({ date, isCurrentMonth: false, isDisabled: isDateDisabled(date) })\n }\n\n return days\n })\n\n function isDateDisabled(date: Date): boolean {\n const minDate = parseBoundaryDate(options.min?.())\n if (minDate && startOfDay(date) < startOfDay(minDate)) return true\n\n const maxDate = parseBoundaryDate(options.max?.())\n if (maxDate && startOfDay(date) > startOfDay(maxDate)) return true\n\n return false\n }\n\n function isSameDay(date: Date, other: Date | null | undefined): boolean {\n if (!other) return false\n return date.toDateString() === other.toDateString()\n }\n\n function isToday(date: Date): boolean {\n return isSameDay(date, new Date())\n }\n\n function formatDateValue(date: Date): string {\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const day = String(date.getDate()).padStart(2, '0')\n return `${year}-${month}-${day}`\n }\n\n function setCurrentMonthFromDate(date: Date): void {\n currentMonth.value = new Date(date.getFullYear(), date.getMonth(), 1)\n }\n\n function prevMonth(): void {\n currentMonth.value = new Date(\n currentMonth.value.getFullYear(),\n currentMonth.value.getMonth() - 1,\n 1,\n )\n }\n\n function nextMonth(): void {\n currentMonth.value = new Date(\n currentMonth.value.getFullYear(),\n currentMonth.value.getMonth() + 1,\n 1,\n )\n }\n\n return {\n currentMonth,\n weekDays: WEEK_DAYS,\n monthYear,\n calendarDays,\n isDateDisabled,\n isSameDay,\n isToday,\n formatDateValue,\n setCurrentMonthFromDate,\n prevMonth,\n nextMonth,\n }\n}\n\nfunction parseBoundaryDate(value: string | undefined): Date | null {\n if (!value) return null\n const date = value.includes('T') ? new Date(value) : new Date(`${value}T00:00:00`)\n return Number.isNaN(date.getTime()) ? null : date\n}\n\nfunction startOfDay(date: Date): Date {\n const copy = new Date(date)\n copy.setHours(0, 0, 0, 0)\n return copy\n}\n","<script setup lang=\"ts\">\n/** Shared calendar grid panel used by date-oriented picker components. */\nimport type { CalendarGridDay } from '../../composables/useCalendarGrid'\n\ninterface Props {\n weekDays: readonly string[]\n monthYear: string\n days: CalendarGridDay[]\n selectedDate?: Date | null\n locale?: string\n previousLabel?: string\n nextLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n selectedDate: null,\n locale: 'en-US',\n previousLabel: 'Previous month',\n nextLabel: 'Next month',\n})\n\nconst emit = defineEmits<{\n 'previous-month': []\n 'next-month': []\n select: [day: CalendarGridDay]\n}>()\n\nfunction isSameDay(date: Date, other: Date | null | undefined): boolean {\n if (!other) return false\n return date.toDateString() === other.toDateString()\n}\n\nfunction isToday(date: Date): boolean {\n return isSameDay(date, new Date())\n}\n\nfunction dayAriaLabel(date: Date): string {\n return date.toLocaleDateString(props.locale, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n })\n}\n</script>\n\n<template>\n <div class=\"mint-calendar-grid-panel\">\n <div class=\"mint-date-picker__header\">\n <button\n type=\"button\"\n class=\"mint-date-picker__nav-btn\"\n :aria-label=\"previousLabel\"\n @click=\"emit('previous-month')\"\n >\n <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=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <span class=\"mint-date-picker__month-year\">{{ monthYear }}</span>\n <button\n type=\"button\"\n class=\"mint-date-picker__nav-btn\"\n :aria-label=\"nextLabel\"\n @click=\"emit('next-month')\"\n >\n <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=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n </div>\n\n <div class=\"mint-date-picker__weekdays\">\n <div\n v-for=\"day in weekDays\"\n :key=\"day\"\n class=\"mint-date-picker__weekday\"\n >\n {{ day }}\n </div>\n </div>\n\n <div class=\"mint-date-picker__grid\">\n <button\n v-for=\"(day, index) in days\"\n :key=\"index\"\n type=\"button\"\n :disabled=\"day.isDisabled\"\n :aria-label=\"dayAriaLabel(day.date)\"\n :aria-selected=\"isSameDay(day.date, selectedDate)\"\n :class=\"[\n 'mint-date-picker__day',\n !day.isCurrentMonth ? 'mint-date-picker__day--other-month' : '',\n day.isDisabled ? 'mint-date-picker__day--disabled' : '',\n isSameDay(day.date, selectedDate) ? 'mint-date-picker__day--selected' : '',\n isToday(day.date) ? 'mint-date-picker__day--today' : '',\n ]\"\n @click=\"emit('select', day)\"\n >\n {{ day.date.getDate() }}\n </button>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Shared calendar grid panel used by date-oriented picker components. */\nimport type { CalendarGridDay } from '../../composables/useCalendarGrid'\n\ninterface Props {\n weekDays: readonly string[]\n monthYear: string\n days: CalendarGridDay[]\n selectedDate?: Date | null\n locale?: string\n previousLabel?: string\n nextLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n selectedDate: null,\n locale: 'en-US',\n previousLabel: 'Previous month',\n nextLabel: 'Next month',\n})\n\nconst emit = defineEmits<{\n 'previous-month': []\n 'next-month': []\n select: [day: CalendarGridDay]\n}>()\n\nfunction isSameDay(date: Date, other: Date | null | undefined): boolean {\n if (!other) return false\n return date.toDateString() === other.toDateString()\n}\n\nfunction isToday(date: Date): boolean {\n return isSameDay(date, new Date())\n}\n\nfunction dayAriaLabel(date: Date): string {\n return date.toLocaleDateString(props.locale, {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n })\n}\n</script>\n\n<template>\n <div class=\"mint-calendar-grid-panel\">\n <div class=\"mint-date-picker__header\">\n <button\n type=\"button\"\n class=\"mint-date-picker__nav-btn\"\n :aria-label=\"previousLabel\"\n @click=\"emit('previous-month')\"\n >\n <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=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <span class=\"mint-date-picker__month-year\">{{ monthYear }}</span>\n <button\n type=\"button\"\n class=\"mint-date-picker__nav-btn\"\n :aria-label=\"nextLabel\"\n @click=\"emit('next-month')\"\n >\n <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=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n </div>\n\n <div class=\"mint-date-picker__weekdays\">\n <div\n v-for=\"day in weekDays\"\n :key=\"day\"\n class=\"mint-date-picker__weekday\"\n >\n {{ day }}\n </div>\n </div>\n\n <div class=\"mint-date-picker__grid\">\n <button\n v-for=\"(day, index) in days\"\n :key=\"index\"\n type=\"button\"\n :disabled=\"day.isDisabled\"\n :aria-label=\"dayAriaLabel(day.date)\"\n :aria-selected=\"isSameDay(day.date, selectedDate)\"\n :class=\"[\n 'mint-date-picker__day',\n !day.isCurrentMonth ? 'mint-date-picker__day--other-month' : '',\n day.isDisabled ? 'mint-date-picker__day--disabled' : '',\n isSameDay(day.date, selectedDate) ? 'mint-date-picker__day--selected' : '',\n isToday(day.date) ? 'mint-date-picker__day--today' : '',\n ]\"\n @click=\"emit('select', day)\"\n >\n {{ day.date.getDate() }}\n </button>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** DatePicker type=\"date\": input that opens a calendar dropdown for selecting a single date, with clearable support. */\nimport { ref, computed, nextTick, watch, useTemplateRef } from 'vue'\nimport { useCalendarGrid, type CalendarGridDay } from '../../composables/useCalendarGrid'\nimport { useDropdownState } from '../../composables/useDropdownState'\nimport { useFloatingPanel } from '../../composables/useFloatingPanel'\nimport { useFocusTrap } from '../../composables/useFocusTrap'\nimport CalendarGridPanelInternal from './CalendarGridPanelInternal.vue'\n\ninterface Props {\n modelValue?: string\n placeholder?: string\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n min?: string\n max?: string\n clearable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n invalid: false,\n size: 'md',\n clearable: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | undefined]\n}>()\n\nconst dropdownRef = ref<HTMLElement | null>(null)\nconst { isOpen, triggerRef, open, close, toggle } = useDropdownState({\n rootRef: useTemplateRef<HTMLElement>('container'),\n insideRefs: [dropdownRef],\n})\n// floating-ui keeps the panel anchored (flip/shift) while the page scrolls,\n// instead of the panel closing on every scroll or resize.\nconst { style: dropdownStyle } = useFloatingPanel(triggerRef, dropdownRef, isOpen)\n// The panel is a non-modal dialog: Tab cycles inside it while it is open, and\n// focus returns to the input when it closes.\nuseFocusTrap({ containerRef: dropdownRef, active: isOpen })\nconst {\n weekDays,\n monthYear,\n calendarDays,\n formatDateValue,\n setCurrentMonthFromDate,\n prevMonth,\n nextMonth,\n} = useCalendarGrid({\n min: () => props.min,\n max: () => props.max,\n})\n\nconst selectedDate = computed(() => {\n if (!props.modelValue) return null\n const date = new Date(props.modelValue + 'T00:00:00')\n return isNaN(date.getTime()) ? null : date\n})\n\nconst displayValue = computed(() => (selectedDate.value ? formatDateValue(selectedDate.value) : ''))\n\nfunction selectDate(day: CalendarGridDay) {\n if (day.isDisabled || props.disabled) return\n emit('update:modelValue', formatDateValue(day.date))\n close()\n}\n\nfunction clear() {\n emit('update:modelValue', undefined)\n close()\n}\n\nfunction toggleCalendar() {\n if (props.disabled) return\n toggle()\n}\n\nfunction handleTriggerKeydown(event: KeyboardEvent) {\n if (props.disabled || isOpen.value) return\n if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {\n event.preventDefault()\n open()\n }\n}\n\n/** Moves focus into the calendar grid: selected day, else today, else the first day. */\nfunction focusCalendar() {\n const panel = dropdownRef.value\n if (!panel) return\n const target =\n panel.querySelector<HTMLElement>('.mint-date-picker__day--selected') ??\n panel.querySelector<HTMLElement>('.mint-date-picker__day--today') ??\n panel.querySelector<HTMLElement>('.mint-date-picker__day:not([disabled])')\n target?.focus()\n}\n\nwatch(isOpen, async (opened) => {\n if (!opened) return\n if (selectedDate.value) {\n setCurrentMonthFromDate(selectedDate.value)\n }\n await nextTick()\n // A second tick so this focus lands after the focus trap's initial focus.\n await nextTick()\n focusCalendar()\n})\n</script>\n\n<template>\n <div ref=\"container\" class=\"mint-date-picker\">\n <div class=\"mint-date-picker__input-wrapper\">\n <div class=\"mint-date-picker__icon\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M8 2v4\" /><path d=\"M16 2v4\" /><rect width=\"18\" height=\"18\" x=\"3\" y=\"4\" rx=\"2\" /><path d=\"M3 10h18\" />\n </svg>\n </div>\n <input\n ref=\"triggerRef\"\n type=\"text\"\n readonly\n aria-haspopup=\"dialog\"\n :aria-expanded=\"isOpen\"\n :value=\"displayValue\"\n :placeholder=\"placeholder || 'Select date'\"\n :disabled=\"disabled\"\n :class=\"[\n 'mint-date-picker__input',\n `mint-date-picker__input--${size}`,\n invalid ? 'mint-date-picker__input--error' : '',\n disabled ? 'mint-date-picker__input--disabled' : '',\n ]\"\n @click=\"toggleCalendar\"\n @keydown=\"handleTriggerKeydown\"\n />\n <button\n v-if=\"clearable && modelValue && !disabled\"\n type=\"button\"\n class=\"mint-date-picker__clear-btn\"\n aria-label=\"Clear date\"\n @click.stop=\"clear\"\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 <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </div>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"mint-date-picker__dropdown-enter-active\"\n enter-from-class=\"mint-date-picker__dropdown-enter-from\"\n enter-to-class=\"mint-date-picker__dropdown-enter-to\"\n leave-active-class=\"mint-date-picker__dropdown-leave-active\"\n leave-from-class=\"mint-date-picker__dropdown-leave-from\"\n leave-to-class=\"mint-date-picker__dropdown-leave-to\"\n >\n <div\n v-if=\"isOpen\"\n ref=\"dropdownRef\"\n class=\"mint-popover mint-date-picker__dropdown\"\n :style=\"dropdownStyle\"\n role=\"dialog\"\n aria-label=\"Date picker\"\n @keydown.escape.prevent=\"close(true)\"\n >\n <CalendarGridPanelInternal\n :week-days=\"weekDays\"\n :month-year=\"monthYear\"\n :days=\"calendarDays\"\n :selected-date=\"selectedDate\"\n @previous-month=\"prevMonth\"\n @next-month=\"nextMonth\"\n @select=\"selectDate\"\n />\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** DatePicker type=\"date\": input that opens a calendar dropdown for selecting a single date, with clearable support. */\nimport { ref, computed, nextTick, watch, useTemplateRef } from 'vue'\nimport { useCalendarGrid, type CalendarGridDay } from '../../composables/useCalendarGrid'\nimport { useDropdownState } from '../../composables/useDropdownState'\nimport { useFloatingPanel } from '../../composables/useFloatingPanel'\nimport { useFocusTrap } from '../../composables/useFocusTrap'\nimport CalendarGridPanelInternal from './CalendarGridPanelInternal.vue'\n\ninterface Props {\n modelValue?: string\n placeholder?: string\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n min?: string\n max?: string\n clearable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n invalid: false,\n size: 'md',\n clearable: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | undefined]\n}>()\n\nconst dropdownRef = ref<HTMLElement | null>(null)\nconst { isOpen, triggerRef, open, close, toggle } = useDropdownState({\n rootRef: useTemplateRef<HTMLElement>('container'),\n insideRefs: [dropdownRef],\n})\n// floating-ui keeps the panel anchored (flip/shift) while the page scrolls,\n// instead of the panel closing on every scroll or resize.\nconst { style: dropdownStyle } = useFloatingPanel(triggerRef, dropdownRef, isOpen)\n// The panel is a non-modal dialog: Tab cycles inside it while it is open, and\n// focus returns to the input when it closes.\nuseFocusTrap({ containerRef: dropdownRef, active: isOpen })\nconst {\n weekDays,\n monthYear,\n calendarDays,\n formatDateValue,\n setCurrentMonthFromDate,\n prevMonth,\n nextMonth,\n} = useCalendarGrid({\n min: () => props.min,\n max: () => props.max,\n})\n\nconst selectedDate = computed(() => {\n if (!props.modelValue) return null\n const date = new Date(props.modelValue + 'T00:00:00')\n return isNaN(date.getTime()) ? null : date\n})\n\nconst displayValue = computed(() => (selectedDate.value ? formatDateValue(selectedDate.value) : ''))\n\nfunction selectDate(day: CalendarGridDay) {\n if (day.isDisabled || props.disabled) return\n emit('update:modelValue', formatDateValue(day.date))\n close()\n}\n\nfunction clear() {\n emit('update:modelValue', undefined)\n close()\n}\n\nfunction toggleCalendar() {\n if (props.disabled) return\n toggle()\n}\n\nfunction handleTriggerKeydown(event: KeyboardEvent) {\n if (props.disabled || isOpen.value) return\n if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {\n event.preventDefault()\n open()\n }\n}\n\n/** Moves focus into the calendar grid: selected day, else today, else the first day. */\nfunction focusCalendar() {\n const panel = dropdownRef.value\n if (!panel) return\n const target =\n panel.querySelector<HTMLElement>('.mint-date-picker__day--selected') ??\n panel.querySelector<HTMLElement>('.mint-date-picker__day--today') ??\n panel.querySelector<HTMLElement>('.mint-date-picker__day:not([disabled])')\n target?.focus()\n}\n\nwatch(isOpen, async (opened) => {\n if (!opened) return\n if (selectedDate.value) {\n setCurrentMonthFromDate(selectedDate.value)\n }\n await nextTick()\n // A second tick so this focus lands after the focus trap's initial focus.\n await nextTick()\n focusCalendar()\n})\n</script>\n\n<template>\n <div ref=\"container\" class=\"mint-date-picker\">\n <div class=\"mint-date-picker__input-wrapper\">\n <div class=\"mint-date-picker__icon\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M8 2v4\" /><path d=\"M16 2v4\" /><rect width=\"18\" height=\"18\" x=\"3\" y=\"4\" rx=\"2\" /><path d=\"M3 10h18\" />\n </svg>\n </div>\n <input\n ref=\"triggerRef\"\n type=\"text\"\n readonly\n aria-haspopup=\"dialog\"\n :aria-expanded=\"isOpen\"\n :value=\"displayValue\"\n :placeholder=\"placeholder || 'Select date'\"\n :disabled=\"disabled\"\n :class=\"[\n 'mint-date-picker__input',\n `mint-date-picker__input--${size}`,\n invalid ? 'mint-date-picker__input--error' : '',\n disabled ? 'mint-date-picker__input--disabled' : '',\n ]\"\n @click=\"toggleCalendar\"\n @keydown=\"handleTriggerKeydown\"\n />\n <button\n v-if=\"clearable && modelValue && !disabled\"\n type=\"button\"\n class=\"mint-date-picker__clear-btn\"\n aria-label=\"Clear date\"\n @click.stop=\"clear\"\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 <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </div>\n\n <Teleport to=\"body\">\n <Transition\n enter-active-class=\"mint-date-picker__dropdown-enter-active\"\n enter-from-class=\"mint-date-picker__dropdown-enter-from\"\n enter-to-class=\"mint-date-picker__dropdown-enter-to\"\n leave-active-class=\"mint-date-picker__dropdown-leave-active\"\n leave-from-class=\"mint-date-picker__dropdown-leave-from\"\n leave-to-class=\"mint-date-picker__dropdown-leave-to\"\n >\n <div\n v-if=\"isOpen\"\n ref=\"dropdownRef\"\n class=\"mint-popover mint-date-picker__dropdown\"\n :style=\"dropdownStyle\"\n role=\"dialog\"\n aria-label=\"Date picker\"\n @keydown.escape.prevent=\"close(true)\"\n >\n <CalendarGridPanelInternal\n :week-days=\"weekDays\"\n :month-year=\"monthYear\"\n :days=\"calendarDays\"\n :selected-date=\"selectedDate\"\n @previous-month=\"prevMonth\"\n @next-month=\"nextMonth\"\n @select=\"selectDate\"\n />\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** DatePicker type=\"datetime\": combined date and time picker with a dropdown calendar, time slots, and 12/24 h format support. */\nimport { computed, ref, watch, nextTick, useTemplateRef } from 'vue'\nimport { useCalendarGrid, type CalendarGridDay } from '../../composables/useCalendarGrid'\nimport { useDropdownState } from '../../composables/useDropdownState'\nimport { useFloatingPanel } from '../../composables/useFloatingPanel'\nimport { useFocusTrap } from '../../composables/useFocusTrap'\nimport { formatTime, formatTimeSlot, generateTimeSlots, toMinutes } from '../../utils/time'\nimport CalendarGridPanelInternal from './CalendarGridPanelInternal.vue'\n\ninterface Props {\n modelValue?: string\n placeholder?: string\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n min?: string\n max?: string\n timeStep?: number\n timeFormat?: '12h' | '24h'\n clearable?: boolean\n locale?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n placeholder: 'Select date & time',\n disabled: false,\n invalid: false,\n size: 'md',\n timeStep: 15,\n timeFormat: '24h',\n clearable: false,\n locale: 'en-US',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | undefined]\n}>()\n\nconst dropdownRef = ref<HTMLElement | null>(null)\nconst rootRef = useTemplateRef<HTMLElement>('root')\nconst { isOpen, open, close, toggle } = useDropdownState({\n rootRef,\n triggerRef: useTemplateRef<HTMLElement>('trigger'),\n})\n// Fixed below the field, so a modal body's overflow does not clip it; it stays in place in the DOM.\nconst { style: dropdownStyle } = useFloatingPanel(rootRef, dropdownRef, isOpen)\n// Non-modal dialog: Tab stays inside the panel, focus returns to the input on close.\nuseFocusTrap({ containerRef: dropdownRef, active: isOpen })\nconst {\n weekDays,\n monthYear,\n calendarDays,\n formatDateValue,\n setCurrentMonthFromDate,\n prevMonth,\n nextMonth,\n} = useCalendarGrid({\n min: () => props.min,\n max: () => props.max,\n locale: () => props.locale,\n})\n\n// Parse model value into date and time parts\nconst selectedDate = computed(() => {\n if (!props.modelValue) return null\n const d = new Date(props.modelValue)\n return isNaN(d.getTime()) ? null : d\n})\n\nconst selectedDateStr = computed(() => {\n if (!selectedDate.value) return null\n return formatDateValue(selectedDate.value)\n})\n\nconst selectedTimeStr = computed(() => {\n if (!selectedDate.value) return null\n return formatTime(selectedDate.value.getHours(), selectedDate.value.getMinutes())\n})\n\nconst displayValue = computed(() => {\n if (!selectedDate.value) return ''\n const timeStr = formatTime(\n selectedDate.value.getHours(),\n selectedDate.value.getMinutes(),\n props.timeFormat,\n )\n return `${formatDateValue(selectedDate.value)} ${timeStr}`\n})\n\n// Time slots\nconst timeSlots = computed(() => {\n return generateTimeSlots('00:00', '23:59', props.timeStep)\n})\n\nfunction isTimeDisabled(time: string): boolean {\n if (!selectedDateStr.value) return false\n const timeMinutes = toMinutes(time)\n\n if (props.min) {\n const minDate = new Date(props.min)\n if (selectedDateStr.value === formatDateValue(minDate)) {\n const minMin = minDate.getHours() * 60 + minDate.getMinutes()\n if (timeMinutes < minMin) return true\n }\n }\n if (props.max) {\n const maxDate = new Date(props.max)\n if (selectedDateStr.value === formatDateValue(maxDate)) {\n const maxMin = maxDate.getHours() * 60 + maxDate.getMinutes()\n if (timeMinutes > maxMin) return true\n }\n }\n return false\n}\n\nfunction selectDate(day: CalendarGridDay) {\n if (day.isDisabled || props.disabled) return\n const dateStr = formatDateValue(day.date)\n const timeStr = selectedTimeStr.value || '09:00'\n emit('update:modelValue', `${dateStr}T${timeStr}`)\n}\n\nfunction selectTime(time: string) {\n if (isTimeDisabled(time)) return\n const dateStr = selectedDateStr.value || formatDateValue(new Date())\n emit('update:modelValue', `${dateStr}T${time}`)\n}\n\nfunction goToNow() {\n const now = new Date()\n setCurrentMonthFromDate(now)\n const dateStr = formatDateValue(now)\n const timeStr = formatTime(now.getHours(), now.getMinutes())\n emit('update:modelValue', `${dateStr}T${timeStr}`)\n close()\n}\n\nfunction goToToday() {\n const today = new Date()\n setCurrentMonthFromDate(today)\n const dateStr = formatDateValue(today)\n const timeStr = selectedTimeStr.value || '09:00'\n emit('update:modelValue', `${dateStr}T${timeStr}`)\n}\n\nfunction clear() {\n emit('update:modelValue', undefined)\n close()\n}\n\nfunction toggleDropdown() {\n if (props.disabled) return\n toggle()\n}\n\nfunction handleTriggerKeydown(event: KeyboardEvent) {\n if (props.disabled || isOpen.value) return\n if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {\n event.preventDefault()\n open()\n }\n}\n\n/** Moves focus into the calendar grid: selected day, else today, else the first day. */\nfunction focusCalendar() {\n const panel = dropdownRef.value\n if (!panel) return\n const target =\n panel.querySelector<HTMLElement>('.mint-date-picker__day--selected') ??\n panel.querySelector<HTMLElement>('.mint-date-picker__day--today') ??\n panel.querySelector<HTMLElement>('.mint-date-picker__day:not([disabled])')\n target?.focus()\n}\n\nwatch(isOpen, async (opened) => {\n if (!opened) return\n if (selectedDate.value) {\n setCurrentMonthFromDate(selectedDate.value)\n }\n await nextTick()\n if (selectedDate.value) {\n // Scroll time grid to selected time\n const grid = dropdownRef.value?.querySelector('.mint-datetime-picker__time-grid')\n const active = dropdownRef.value?.querySelector('.mint-datetime-picker__time-chip--active')\n if (grid && active) {\n active.scrollIntoView({ block: 'center' })\n }\n }\n // A second tick so this focus lands after the focus trap's initial focus.\n await nextTick()\n focusCalendar()\n})\n</script>\n\n<template>\n <div ref=\"root\" class=\"mint-datetime-picker\">\n <div class=\"mint-datetime-picker__input-wrapper\">\n <div class=\"mint-datetime-picker__icon-calendar\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M8 2v4\" /><path d=\"M16 2v4\" /><rect width=\"18\" height=\"18\" x=\"3\" y=\"4\" rx=\"2\" /><path d=\"M3 10h18\" />\n </svg>\n </div>\n <input\n ref=\"trigger\"\n type=\"text\"\n readonly\n aria-haspopup=\"dialog\"\n :aria-expanded=\"isOpen\"\n :value=\"displayValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :class=\"[\n 'mint-datetime-picker__input',\n `mint-datetime-picker__input--${size}`,\n invalid ? 'mint-datetime-picker__input--error' : '',\n disabled ? 'mint-datetime-picker__input--disabled' : '',\n ]\"\n aria-label=\"Select date and time\"\n @click=\"toggleDropdown\"\n @keydown=\"handleTriggerKeydown\"\n />\n </div>\n\n <Transition\n enter-active-class=\"mint-datetime-picker__dropdown-enter-active\"\n enter-from-class=\"mint-datetime-picker__dropdown-enter-from\"\n enter-to-class=\"mint-datetime-picker__dropdown-enter-to\"\n leave-active-class=\"mint-datetime-picker__dropdown-leave-active\"\n leave-from-class=\"mint-datetime-picker__dropdown-leave-from\"\n leave-to-class=\"mint-datetime-picker__dropdown-leave-to\"\n >\n <div\n v-if=\"isOpen\"\n ref=\"dropdownRef\"\n class=\"mint-popover mint-datetime-picker__dropdown\"\n :style=\"dropdownStyle\"\n role=\"dialog\"\n aria-label=\"Date and time picker\"\n @keydown.escape.prevent=\"close(true)\"\n >\n <!-- Calendar section -->\n <div class=\"mint-datetime-picker__calendar-section\">\n <CalendarGridPanelInternal\n :week-days=\"weekDays\"\n :month-year=\"monthYear\"\n :days=\"calendarDays\"\n :selected-date=\"selectedDate\"\n :locale=\"locale\"\n @previous-month=\"prevMonth\"\n @next-month=\"nextMonth\"\n @select=\"selectDate\"\n />\n </div>\n\n <!-- Divider -->\n <div class=\"mint-datetime-picker__divider\" />\n\n <!-- Time section -->\n <div class=\"mint-datetime-picker__time-section\">\n <div class=\"mint-datetime-picker__time-label\">Time</div>\n <div class=\"mint-datetime-picker__time-grid\">\n <button\n v-for=\"time in timeSlots\"\n :key=\"time\"\n type=\"button\"\n :disabled=\"isTimeDisabled(time)\"\n :class=\"[\n 'mint-datetime-picker__time-chip',\n selectedTimeStr === time ? 'mint-datetime-picker__time-chip--active' : '',\n isTimeDisabled(time) ? 'mint-datetime-picker__time-chip--disabled' : '',\n ]\"\n @click=\"selectTime(time)\"\n >\n {{ formatTimeSlot(time, timeFormat) }}\n </button>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"mint-datetime-picker__footer\">\n <div>\n <button type=\"button\" class=\"mint-datetime-picker__footer-btn\" @click=\"goToToday\">Today</button>\n <button type=\"button\" class=\"mint-datetime-picker__footer-btn\" @click=\"goToNow\">Now</button>\n </div>\n <button\n v-if=\"clearable && modelValue\"\n type=\"button\"\n class=\"mint-datetime-picker__footer-btn mint-datetime-picker__footer-btn--muted\"\n @click=\"clear\"\n >\n Clear\n </button>\n </div>\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** DatePicker type=\"datetime\": combined date and time picker with a dropdown calendar, time slots, and 12/24 h format support. */\nimport { computed, ref, watch, nextTick, useTemplateRef } from 'vue'\nimport { useCalendarGrid, type CalendarGridDay } from '../../composables/useCalendarGrid'\nimport { useDropdownState } from '../../composables/useDropdownState'\nimport { useFloatingPanel } from '../../composables/useFloatingPanel'\nimport { useFocusTrap } from '../../composables/useFocusTrap'\nimport { formatTime, formatTimeSlot, generateTimeSlots, toMinutes } from '../../utils/time'\nimport CalendarGridPanelInternal from './CalendarGridPanelInternal.vue'\n\ninterface Props {\n modelValue?: string\n placeholder?: string\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n min?: string\n max?: string\n timeStep?: number\n timeFormat?: '12h' | '24h'\n clearable?: boolean\n locale?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n placeholder: 'Select date & time',\n disabled: false,\n invalid: false,\n size: 'md',\n timeStep: 15,\n timeFormat: '24h',\n clearable: false,\n locale: 'en-US',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | undefined]\n}>()\n\nconst dropdownRef = ref<HTMLElement | null>(null)\nconst rootRef = useTemplateRef<HTMLElement>('root')\nconst { isOpen, open, close, toggle } = useDropdownState({\n rootRef,\n triggerRef: useTemplateRef<HTMLElement>('trigger'),\n})\n// Fixed below the field, so a modal body's overflow does not clip it; it stays in place in the DOM.\nconst { style: dropdownStyle } = useFloatingPanel(rootRef, dropdownRef, isOpen)\n// Non-modal dialog: Tab stays inside the panel, focus returns to the input on close.\nuseFocusTrap({ containerRef: dropdownRef, active: isOpen })\nconst {\n weekDays,\n monthYear,\n calendarDays,\n formatDateValue,\n setCurrentMonthFromDate,\n prevMonth,\n nextMonth,\n} = useCalendarGrid({\n min: () => props.min,\n max: () => props.max,\n locale: () => props.locale,\n})\n\n// Parse model value into date and time parts\nconst selectedDate = computed(() => {\n if (!props.modelValue) return null\n const d = new Date(props.modelValue)\n return isNaN(d.getTime()) ? null : d\n})\n\nconst selectedDateStr = computed(() => {\n if (!selectedDate.value) return null\n return formatDateValue(selectedDate.value)\n})\n\nconst selectedTimeStr = computed(() => {\n if (!selectedDate.value) return null\n return formatTime(selectedDate.value.getHours(), selectedDate.value.getMinutes())\n})\n\nconst displayValue = computed(() => {\n if (!selectedDate.value) return ''\n const timeStr = formatTime(\n selectedDate.value.getHours(),\n selectedDate.value.getMinutes(),\n props.timeFormat,\n )\n return `${formatDateValue(selectedDate.value)} ${timeStr}`\n})\n\n// Time slots\nconst timeSlots = computed(() => {\n return generateTimeSlots('00:00', '23:59', props.timeStep)\n})\n\nfunction isTimeDisabled(time: string): boolean {\n if (!selectedDateStr.value) return false\n const timeMinutes = toMinutes(time)\n\n if (props.min) {\n const minDate = new Date(props.min)\n if (selectedDateStr.value === formatDateValue(minDate)) {\n const minMin = minDate.getHours() * 60 + minDate.getMinutes()\n if (timeMinutes < minMin) return true\n }\n }\n if (props.max) {\n const maxDate = new Date(props.max)\n if (selectedDateStr.value === formatDateValue(maxDate)) {\n const maxMin = maxDate.getHours() * 60 + maxDate.getMinutes()\n if (timeMinutes > maxMin) return true\n }\n }\n return false\n}\n\nfunction selectDate(day: CalendarGridDay) {\n if (day.isDisabled || props.disabled) return\n const dateStr = formatDateValue(day.date)\n const timeStr = selectedTimeStr.value || '09:00'\n emit('update:modelValue', `${dateStr}T${timeStr}`)\n}\n\nfunction selectTime(time: string) {\n if (isTimeDisabled(time)) return\n const dateStr = selectedDateStr.value || formatDateValue(new Date())\n emit('update:modelValue', `${dateStr}T${time}`)\n}\n\nfunction goToNow() {\n const now = new Date()\n setCurrentMonthFromDate(now)\n const dateStr = formatDateValue(now)\n const timeStr = formatTime(now.getHours(), now.getMinutes())\n emit('update:modelValue', `${dateStr}T${timeStr}`)\n close()\n}\n\nfunction goToToday() {\n const today = new Date()\n setCurrentMonthFromDate(today)\n const dateStr = formatDateValue(today)\n const timeStr = selectedTimeStr.value || '09:00'\n emit('update:modelValue', `${dateStr}T${timeStr}`)\n}\n\nfunction clear() {\n emit('update:modelValue', undefined)\n close()\n}\n\nfunction toggleDropdown() {\n if (props.disabled) return\n toggle()\n}\n\nfunction handleTriggerKeydown(event: KeyboardEvent) {\n if (props.disabled || isOpen.value) return\n if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {\n event.preventDefault()\n open()\n }\n}\n\n/** Moves focus into the calendar grid: selected day, else today, else the first day. */\nfunction focusCalendar() {\n const panel = dropdownRef.value\n if (!panel) return\n const target =\n panel.querySelector<HTMLElement>('.mint-date-picker__day--selected') ??\n panel.querySelector<HTMLElement>('.mint-date-picker__day--today') ??\n panel.querySelector<HTMLElement>('.mint-date-picker__day:not([disabled])')\n target?.focus()\n}\n\nwatch(isOpen, async (opened) => {\n if (!opened) return\n if (selectedDate.value) {\n setCurrentMonthFromDate(selectedDate.value)\n }\n await nextTick()\n if (selectedDate.value) {\n // Scroll time grid to selected time\n const grid = dropdownRef.value?.querySelector('.mint-datetime-picker__time-grid')\n const active = dropdownRef.value?.querySelector('.mint-datetime-picker__time-chip--active')\n if (grid && active) {\n active.scrollIntoView({ block: 'center' })\n }\n }\n // A second tick so this focus lands after the focus trap's initial focus.\n await nextTick()\n focusCalendar()\n})\n</script>\n\n<template>\n <div ref=\"root\" class=\"mint-datetime-picker\">\n <div class=\"mint-datetime-picker__input-wrapper\">\n <div class=\"mint-datetime-picker__icon-calendar\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M8 2v4\" /><path d=\"M16 2v4\" /><rect width=\"18\" height=\"18\" x=\"3\" y=\"4\" rx=\"2\" /><path d=\"M3 10h18\" />\n </svg>\n </div>\n <input\n ref=\"trigger\"\n type=\"text\"\n readonly\n aria-haspopup=\"dialog\"\n :aria-expanded=\"isOpen\"\n :value=\"displayValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :class=\"[\n 'mint-datetime-picker__input',\n `mint-datetime-picker__input--${size}`,\n invalid ? 'mint-datetime-picker__input--error' : '',\n disabled ? 'mint-datetime-picker__input--disabled' : '',\n ]\"\n aria-label=\"Select date and time\"\n @click=\"toggleDropdown\"\n @keydown=\"handleTriggerKeydown\"\n />\n </div>\n\n <Transition\n enter-active-class=\"mint-datetime-picker__dropdown-enter-active\"\n enter-from-class=\"mint-datetime-picker__dropdown-enter-from\"\n enter-to-class=\"mint-datetime-picker__dropdown-enter-to\"\n leave-active-class=\"mint-datetime-picker__dropdown-leave-active\"\n leave-from-class=\"mint-datetime-picker__dropdown-leave-from\"\n leave-to-class=\"mint-datetime-picker__dropdown-leave-to\"\n >\n <div\n v-if=\"isOpen\"\n ref=\"dropdownRef\"\n class=\"mint-popover mint-datetime-picker__dropdown\"\n :style=\"dropdownStyle\"\n role=\"dialog\"\n aria-label=\"Date and time picker\"\n @keydown.escape.prevent=\"close(true)\"\n >\n <!-- Calendar section -->\n <div class=\"mint-datetime-picker__calendar-section\">\n <CalendarGridPanelInternal\n :week-days=\"weekDays\"\n :month-year=\"monthYear\"\n :days=\"calendarDays\"\n :selected-date=\"selectedDate\"\n :locale=\"locale\"\n @previous-month=\"prevMonth\"\n @next-month=\"nextMonth\"\n @select=\"selectDate\"\n />\n </div>\n\n <!-- Divider -->\n <div class=\"mint-datetime-picker__divider\" />\n\n <!-- Time section -->\n <div class=\"mint-datetime-picker__time-section\">\n <div class=\"mint-datetime-picker__time-label\">Time</div>\n <div class=\"mint-datetime-picker__time-grid\">\n <button\n v-for=\"time in timeSlots\"\n :key=\"time\"\n type=\"button\"\n :disabled=\"isTimeDisabled(time)\"\n :class=\"[\n 'mint-datetime-picker__time-chip',\n selectedTimeStr === time ? 'mint-datetime-picker__time-chip--active' : '',\n isTimeDisabled(time) ? 'mint-datetime-picker__time-chip--disabled' : '',\n ]\"\n @click=\"selectTime(time)\"\n >\n {{ formatTimeSlot(time, timeFormat) }}\n </button>\n </div>\n </div>\n\n <!-- Footer -->\n <div class=\"mint-datetime-picker__footer\">\n <div>\n <button type=\"button\" class=\"mint-datetime-picker__footer-btn\" @click=\"goToToday\">Today</button>\n <button type=\"button\" class=\"mint-datetime-picker__footer-btn\" @click=\"goToNow\">Now</button>\n </div>\n <button\n v-if=\"clearable && modelValue\"\n type=\"button\"\n class=\"mint-datetime-picker__footer-btn mint-datetime-picker__footer-btn--muted\"\n @click=\"clear\"\n >\n Clear\n </button>\n </div>\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** DatePicker type=\"time\": input that opens a scrollable time-slot dropdown in 12h or 24h format, with min/max range and optional unavailable slots. */\nimport { ref, computed, watch, nextTick, useId, useTemplateRef } from 'vue'\nimport type { TimeSlotDisabledFn } from '../../types'\nimport { useDropdownState } from '../../composables/useDropdownState'\nimport { useFloatingPanel } from '../../composables/useFloatingPanel'\nimport {\n compareTime,\n findNearestTimeSlotIndex,\n formatTimeSlot,\n generateTimeSlots,\n} from '../../utils/time'\n\ninterface Props {\n modelValue?: string\n placeholder?: string\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n min?: string\n max?: string\n step?: number\n format?: '12h' | '24h'\n clearable?: boolean\n /** Marks slots unavailable; a returned string is shown inline as the reason. */\n disabledSlots?: TimeSlotDisabledFn\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n placeholder: 'Select time',\n disabled: false,\n invalid: false,\n size: 'md',\n step: 15,\n format: '24h',\n clearable: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | undefined]\n}>()\n\nconst listRef = ref<HTMLUListElement | null>(null)\nconst dropdownRef = ref<HTMLElement | null>(null)\nconst highlightedIndex = ref(-1)\nconst listboxId = `mint-time-picker-listbox-${useId().replace(/:/g, '')}`\nconst rootRef = useTemplateRef<HTMLElement>('root')\nconst { isOpen, open, close, toggle } = useDropdownState({\n rootRef,\n triggerRef: useTemplateRef<HTMLElement>('trigger'),\n})\n// Fixed below the field and at least as wide, so a modal body's overflow does not clip it; it stays in place in the DOM.\nconst { style: dropdownStyle } = useFloatingPanel(rootRef, dropdownRef, isOpen, { matchWidth: true })\n\n/** Id of the highlighted slot, published to the input as `aria-activedescendant`. */\nconst activeSlotId = computed(() =>\n isOpen.value && highlightedIndex.value >= 0\n ? `${listboxId}-option-${highlightedIndex.value}`\n : undefined,\n)\n\nconst slots = computed(() => {\n const start = props.min ?? '00:00'\n const end = props.max ?? '23:59'\n return generateTimeSlots(start, end, props.step)\n})\n\nconst displayValue = computed(() => {\n if (!props.modelValue) return ''\n return formatTimeSlot(props.modelValue, props.format)\n})\n\nfunction isSlotDisabled(slot: string): boolean {\n if (props.min && compareTime(slot, props.min) < 0) return true\n if (props.max && compareTime(slot, props.max) > 0) return true\n return !!props.disabledSlots?.(slot)\n}\n\nfunction slotReason(slot: string): string | undefined {\n const result = props.disabledSlots?.(slot)\n return typeof result === 'string' && result ? result : undefined\n}\n\nfunction selectSlot(slot: string) {\n if (isSlotDisabled(slot) || props.disabled) return\n emit('update:modelValue', slot)\n close()\n}\n\nfunction clear() {\n emit('update:modelValue', undefined)\n close()\n}\n\nfunction toggleDropdown() {\n if (props.disabled) return\n toggle()\n}\n\nfunction scrollToActiveSlot() {\n nextTick(() => {\n if (!listRef.value) return\n const activeEl = listRef.value.querySelector('.mint-time-picker__slot--active') as HTMLElement | null\n if (activeEl) {\n activeEl.scrollIntoView({ block: 'center' })\n } else if (props.modelValue) {\n // Scroll to nearest slot\n const nearestIndex = findNearestSlotIndex(props.modelValue)\n if (nearestIndex >= 0) {\n const children = listRef.value.children\n if (children[nearestIndex]) {\n (children[nearestIndex] as HTMLElement).scrollIntoView({ block: 'center' })\n }\n }\n }\n })\n}\n\nfunction findNearestSlotIndex(time: string): number {\n return findNearestTimeSlotIndex(time, slots.value)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!isOpen.value) {\n if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {\n event.preventDefault()\n open()\n return\n }\n return\n }\n\n switch (event.key) {\n case 'ArrowDown': {\n event.preventDefault()\n let next = highlightedIndex.value + 1\n while (next < slots.value.length && isSlotDisabled(slots.value[next])) next++\n if (next < slots.value.length) {\n highlightedIndex.value = next\n scrollToHighlighted()\n }\n break\n }\n case 'ArrowUp': {\n event.preventDefault()\n let prev = highlightedIndex.value - 1\n while (prev >= 0 && isSlotDisabled(slots.value[prev])) prev--\n if (prev >= 0) {\n highlightedIndex.value = prev\n scrollToHighlighted()\n }\n break\n }\n case 'Enter': {\n event.preventDefault()\n if (highlightedIndex.value >= 0 && highlightedIndex.value < slots.value.length) {\n selectSlot(slots.value[highlightedIndex.value])\n }\n break\n }\n case 'Home': {\n event.preventDefault()\n const first = slots.value.findIndex(slot => !isSlotDisabled(slot))\n if (first >= 0) {\n highlightedIndex.value = first\n scrollToHighlighted()\n }\n break\n }\n case 'End': {\n event.preventDefault()\n for (let index = slots.value.length - 1; index >= 0; index--) {\n if (!isSlotDisabled(slots.value[index])) {\n highlightedIndex.value = index\n scrollToHighlighted()\n break\n }\n }\n break\n }\n case 'Escape': {\n event.preventDefault()\n close(true)\n break\n }\n }\n}\n\nfunction scrollToHighlighted() {\n nextTick(() => {\n if (!listRef.value) return\n const children = listRef.value.children\n if (children[highlightedIndex.value]) {\n (children[highlightedIndex.value] as HTMLElement).scrollIntoView({ block: 'nearest' })\n }\n })\n}\n\nwatch(isOpen, (open) => {\n if (open) {\n // Set highlighted index to current value or nearest slot\n if (props.modelValue) {\n const idx = slots.value.indexOf(props.modelValue)\n highlightedIndex.value = idx >= 0 ? idx : findNearestSlotIndex(props.modelValue)\n } else {\n highlightedIndex.value = 0\n }\n scrollToActiveSlot()\n } else {\n highlightedIndex.value = -1\n }\n})\n</script>\n\n<template>\n <div ref=\"root\" class=\"mint-time-picker\" @keydown=\"handleKeydown\">\n <div class=\"mint-time-picker__input-wrapper\">\n <div class=\"mint-time-picker__icon\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 6v6l4 2\" />\n </svg>\n </div>\n <input\n ref=\"trigger\"\n type=\"text\"\n readonly\n :value=\"displayValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :class=\"[\n 'mint-time-picker__input',\n `mint-time-picker__input--${size}`,\n invalid ? 'mint-time-picker__input--error' : '',\n disabled ? 'mint-time-picker__input--disabled' : '',\n ]\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n :aria-expanded=\"isOpen\"\n :aria-controls=\"isOpen ? listboxId : undefined\"\n :aria-activedescendant=\"activeSlotId\"\n @click=\"toggleDropdown\"\n />\n <button\n v-if=\"clearable && modelValue\"\n type=\"button\"\n class=\"mint-time-picker__clear-btn\"\n aria-label=\"Clear time\"\n @click.stop=\"clear\"\n >\n <svg 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 <Transition\n enter-active-class=\"mint-time-picker__dropdown-enter-active\"\n enter-from-class=\"mint-time-picker__dropdown-enter-from\"\n enter-to-class=\"mint-time-picker__dropdown-enter-to\"\n leave-active-class=\"mint-time-picker__dropdown-leave-active\"\n leave-from-class=\"mint-time-picker__dropdown-leave-from\"\n leave-to-class=\"mint-time-picker__dropdown-leave-to\"\n >\n <div\n v-if=\"isOpen\"\n ref=\"dropdownRef\"\n class=\"mint-popover mint-time-picker__dropdown\"\n :style=\"dropdownStyle\"\n >\n <ul\n :id=\"listboxId\"\n ref=\"listRef\"\n class=\"mint-time-picker__list\"\n role=\"listbox\"\n aria-label=\"Available times\"\n >\n <li\n v-for=\"(slot, index) in slots\"\n :id=\"`${listboxId}-option-${index}`\"\n :key=\"slot\"\n role=\"option\"\n :aria-selected=\"slot === modelValue\"\n :aria-disabled=\"isSlotDisabled(slot)\"\n :class=\"[\n 'mint-menu-item mint-time-picker__slot',\n slot === modelValue ? 'mint-menu-item--selected mint-time-picker__slot--active' : '',\n isSlotDisabled(slot) ? 'mint-time-picker__slot--disabled' : '',\n index === highlightedIndex && !isSlotDisabled(slot) ? 'mint-menu-item--active mint-time-picker__slot--highlighted' : '',\n ]\"\n @click=\"selectSlot(slot)\"\n >\n <span class=\"mint-time-picker__slot-time\">{{ formatTimeSlot(slot, format) }}</span>\n <span v-if=\"slotReason(slot)\" class=\"mint-time-picker__slot-reason\">{{ slotReason(slot) }}</span>\n </li>\n </ul>\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** DatePicker type=\"time\": input that opens a scrollable time-slot dropdown in 12h or 24h format, with min/max range and optional unavailable slots. */\nimport { ref, computed, watch, nextTick, useId, useTemplateRef } from 'vue'\nimport type { TimeSlotDisabledFn } from '../../types'\nimport { useDropdownState } from '../../composables/useDropdownState'\nimport { useFloatingPanel } from '../../composables/useFloatingPanel'\nimport {\n compareTime,\n findNearestTimeSlotIndex,\n formatTimeSlot,\n generateTimeSlots,\n} from '../../utils/time'\n\ninterface Props {\n modelValue?: string\n placeholder?: string\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n min?: string\n max?: string\n step?: number\n format?: '12h' | '24h'\n clearable?: boolean\n /** Marks slots unavailable; a returned string is shown inline as the reason. */\n disabledSlots?: TimeSlotDisabledFn\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n placeholder: 'Select time',\n disabled: false,\n invalid: false,\n size: 'md',\n step: 15,\n format: '24h',\n clearable: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | undefined]\n}>()\n\nconst listRef = ref<HTMLUListElement | null>(null)\nconst dropdownRef = ref<HTMLElement | null>(null)\nconst highlightedIndex = ref(-1)\nconst listboxId = `mint-time-picker-listbox-${useId().replace(/:/g, '')}`\nconst rootRef = useTemplateRef<HTMLElement>('root')\nconst { isOpen, open, close, toggle } = useDropdownState({\n rootRef,\n triggerRef: useTemplateRef<HTMLElement>('trigger'),\n})\n// Fixed below the field and at least as wide, so a modal body's overflow does not clip it; it stays in place in the DOM.\nconst { style: dropdownStyle } = useFloatingPanel(rootRef, dropdownRef, isOpen, { matchWidth: true })\n\n/** Id of the highlighted slot, published to the input as `aria-activedescendant`. */\nconst activeSlotId = computed(() =>\n isOpen.value && highlightedIndex.value >= 0\n ? `${listboxId}-option-${highlightedIndex.value}`\n : undefined,\n)\n\nconst slots = computed(() => {\n const start = props.min ?? '00:00'\n const end = props.max ?? '23:59'\n return generateTimeSlots(start, end, props.step)\n})\n\nconst displayValue = computed(() => {\n if (!props.modelValue) return ''\n return formatTimeSlot(props.modelValue, props.format)\n})\n\nfunction isSlotDisabled(slot: string): boolean {\n if (props.min && compareTime(slot, props.min) < 0) return true\n if (props.max && compareTime(slot, props.max) > 0) return true\n return !!props.disabledSlots?.(slot)\n}\n\nfunction slotReason(slot: string): string | undefined {\n const result = props.disabledSlots?.(slot)\n return typeof result === 'string' && result ? result : undefined\n}\n\nfunction selectSlot(slot: string) {\n if (isSlotDisabled(slot) || props.disabled) return\n emit('update:modelValue', slot)\n close()\n}\n\nfunction clear() {\n emit('update:modelValue', undefined)\n close()\n}\n\nfunction toggleDropdown() {\n if (props.disabled) return\n toggle()\n}\n\nfunction scrollToActiveSlot() {\n nextTick(() => {\n if (!listRef.value) return\n const activeEl = listRef.value.querySelector('.mint-time-picker__slot--active') as HTMLElement | null\n if (activeEl) {\n activeEl.scrollIntoView({ block: 'center' })\n } else if (props.modelValue) {\n // Scroll to nearest slot\n const nearestIndex = findNearestSlotIndex(props.modelValue)\n if (nearestIndex >= 0) {\n const children = listRef.value.children\n if (children[nearestIndex]) {\n (children[nearestIndex] as HTMLElement).scrollIntoView({ block: 'center' })\n }\n }\n }\n })\n}\n\nfunction findNearestSlotIndex(time: string): number {\n return findNearestTimeSlotIndex(time, slots.value)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!isOpen.value) {\n if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {\n event.preventDefault()\n open()\n return\n }\n return\n }\n\n switch (event.key) {\n case 'ArrowDown': {\n event.preventDefault()\n let next = highlightedIndex.value + 1\n while (next < slots.value.length && isSlotDisabled(slots.value[next])) next++\n if (next < slots.value.length) {\n highlightedIndex.value = next\n scrollToHighlighted()\n }\n break\n }\n case 'ArrowUp': {\n event.preventDefault()\n let prev = highlightedIndex.value - 1\n while (prev >= 0 && isSlotDisabled(slots.value[prev])) prev--\n if (prev >= 0) {\n highlightedIndex.value = prev\n scrollToHighlighted()\n }\n break\n }\n case 'Enter': {\n event.preventDefault()\n if (highlightedIndex.value >= 0 && highlightedIndex.value < slots.value.length) {\n selectSlot(slots.value[highlightedIndex.value])\n }\n break\n }\n case 'Home': {\n event.preventDefault()\n const first = slots.value.findIndex(slot => !isSlotDisabled(slot))\n if (first >= 0) {\n highlightedIndex.value = first\n scrollToHighlighted()\n }\n break\n }\n case 'End': {\n event.preventDefault()\n for (let index = slots.value.length - 1; index >= 0; index--) {\n if (!isSlotDisabled(slots.value[index])) {\n highlightedIndex.value = index\n scrollToHighlighted()\n break\n }\n }\n break\n }\n case 'Escape': {\n event.preventDefault()\n close(true)\n break\n }\n }\n}\n\nfunction scrollToHighlighted() {\n nextTick(() => {\n if (!listRef.value) return\n const children = listRef.value.children\n if (children[highlightedIndex.value]) {\n (children[highlightedIndex.value] as HTMLElement).scrollIntoView({ block: 'nearest' })\n }\n })\n}\n\nwatch(isOpen, (open) => {\n if (open) {\n // Set highlighted index to current value or nearest slot\n if (props.modelValue) {\n const idx = slots.value.indexOf(props.modelValue)\n highlightedIndex.value = idx >= 0 ? idx : findNearestSlotIndex(props.modelValue)\n } else {\n highlightedIndex.value = 0\n }\n scrollToActiveSlot()\n } else {\n highlightedIndex.value = -1\n }\n})\n</script>\n\n<template>\n <div ref=\"root\" class=\"mint-time-picker\" @keydown=\"handleKeydown\">\n <div class=\"mint-time-picker__input-wrapper\">\n <div class=\"mint-time-picker__icon\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" /><path d=\"M12 6v6l4 2\" />\n </svg>\n </div>\n <input\n ref=\"trigger\"\n type=\"text\"\n readonly\n :value=\"displayValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :class=\"[\n 'mint-time-picker__input',\n `mint-time-picker__input--${size}`,\n invalid ? 'mint-time-picker__input--error' : '',\n disabled ? 'mint-time-picker__input--disabled' : '',\n ]\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n :aria-expanded=\"isOpen\"\n :aria-controls=\"isOpen ? listboxId : undefined\"\n :aria-activedescendant=\"activeSlotId\"\n @click=\"toggleDropdown\"\n />\n <button\n v-if=\"clearable && modelValue\"\n type=\"button\"\n class=\"mint-time-picker__clear-btn\"\n aria-label=\"Clear time\"\n @click.stop=\"clear\"\n >\n <svg 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 <Transition\n enter-active-class=\"mint-time-picker__dropdown-enter-active\"\n enter-from-class=\"mint-time-picker__dropdown-enter-from\"\n enter-to-class=\"mint-time-picker__dropdown-enter-to\"\n leave-active-class=\"mint-time-picker__dropdown-leave-active\"\n leave-from-class=\"mint-time-picker__dropdown-leave-from\"\n leave-to-class=\"mint-time-picker__dropdown-leave-to\"\n >\n <div\n v-if=\"isOpen\"\n ref=\"dropdownRef\"\n class=\"mint-popover mint-time-picker__dropdown\"\n :style=\"dropdownStyle\"\n >\n <ul\n :id=\"listboxId\"\n ref=\"listRef\"\n class=\"mint-time-picker__list\"\n role=\"listbox\"\n aria-label=\"Available times\"\n >\n <li\n v-for=\"(slot, index) in slots\"\n :id=\"`${listboxId}-option-${index}`\"\n :key=\"slot\"\n role=\"option\"\n :aria-selected=\"slot === modelValue\"\n :aria-disabled=\"isSlotDisabled(slot)\"\n :class=\"[\n 'mint-menu-item mint-time-picker__slot',\n slot === modelValue ? 'mint-menu-item--selected mint-time-picker__slot--active' : '',\n isSlotDisabled(slot) ? 'mint-time-picker__slot--disabled' : '',\n index === highlightedIndex && !isSlotDisabled(slot) ? 'mint-menu-item--active mint-time-picker__slot--highlighted' : '',\n ]\"\n @click=\"selectSlot(slot)\"\n >\n <span class=\"mint-time-picker__slot-time\">{{ formatTimeSlot(slot, format) }}</span>\n <span v-if=\"slotReason(slot)\" class=\"mint-time-picker__slot-reason\">{{ slotReason(slot) }}</span>\n </li>\n </ul>\n </div>\n </Transition>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Date, time or date-and-time input with a dropdown picker.\n * `type=\"date\"` (default) binds `YYYY-MM-DD`, `type=\"time\"` binds `HH:mm`, `type=\"datetime\"` binds `YYYY-MM-DDTHH:mm`.\n *\n * @example\n * ```vue\n * <DatePicker v-model=\"startDate\" />\n * <DatePicker v-model=\"startTime\" type=\"time\" :step=\"30\" format=\"12h\" />\n * <DatePicker v-model=\"startsAt\" type=\"datetime\" />\n * ```\n */\nimport type { TimeSlotDisabledFn } from '../types'\nimport DatePickerDateInternal from './internal/DatePickerDateInternal.vue'\nimport DatePickerDateTimeInternal from './internal/DatePickerDateTimeInternal.vue'\nimport DatePickerTimeInternal from './internal/DatePickerTimeInternal.vue'\n\ninterface Props {\n modelValue?: string\n type?: 'date' | 'time' | 'datetime'\n placeholder?: string\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n /** Earliest value, in the format of the bound value. */\n min?: string\n /** Latest value, in the format of the bound value. */\n max?: string\n /** Show a clear button. Default: true for `date`, false otherwise. */\n clearable?: boolean\n /** Minutes between time slots (`time`, `datetime`). */\n step?: number\n /** Clock format of the time slots (`time`, `datetime`). */\n format?: '12h' | '24h'\n /** Locale of the calendar (`datetime`). */\n locale?: string\n /** Marks time slots unavailable; a returned string is shown inline as the reason (`time`). */\n disabledSlots?: TimeSlotDisabledFn\n}\n\nwithDefaults(defineProps<Props>(), {\n modelValue: undefined,\n type: 'date',\n placeholder: undefined,\n disabled: false,\n invalid: false,\n size: 'md',\n min: undefined,\n max: undefined,\n clearable: undefined,\n step: undefined,\n format: undefined,\n locale: undefined,\n disabledSlots: undefined,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | undefined]\n}>()\n</script>\n\n<template>\n <DatePickerTimeInternal\n v-if=\"type === 'time'\"\n :model-value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n :size=\"size\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :format=\"format\"\n :clearable=\"clearable\"\n :disabled-slots=\"disabledSlots\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n <DatePickerDateTimeInternal\n v-else-if=\"type === 'datetime'\"\n :model-value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n :size=\"size\"\n :min=\"min\"\n :max=\"max\"\n :time-step=\"step\"\n :time-format=\"format\"\n :clearable=\"clearable\"\n :locale=\"locale\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n <DatePickerDateInternal\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n :size=\"size\"\n :min=\"min\"\n :max=\"max\"\n :clearable=\"clearable\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n</template>\n","<script setup lang=\"ts\">\n/**\n * Date, time or date-and-time input with a dropdown picker.\n * `type=\"date\"` (default) binds `YYYY-MM-DD`, `type=\"time\"` binds `HH:mm`, `type=\"datetime\"` binds `YYYY-MM-DDTHH:mm`.\n *\n * @example\n * ```vue\n * <DatePicker v-model=\"startDate\" />\n * <DatePicker v-model=\"startTime\" type=\"time\" :step=\"30\" format=\"12h\" />\n * <DatePicker v-model=\"startsAt\" type=\"datetime\" />\n * ```\n */\nimport type { TimeSlotDisabledFn } from '../types'\nimport DatePickerDateInternal from './internal/DatePickerDateInternal.vue'\nimport DatePickerDateTimeInternal from './internal/DatePickerDateTimeInternal.vue'\nimport DatePickerTimeInternal from './internal/DatePickerTimeInternal.vue'\n\ninterface Props {\n modelValue?: string\n type?: 'date' | 'time' | 'datetime'\n placeholder?: string\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n /** Earliest value, in the format of the bound value. */\n min?: string\n /** Latest value, in the format of the bound value. */\n max?: string\n /** Show a clear button. Default: true for `date`, false otherwise. */\n clearable?: boolean\n /** Minutes between time slots (`time`, `datetime`). */\n step?: number\n /** Clock format of the time slots (`time`, `datetime`). */\n format?: '12h' | '24h'\n /** Locale of the calendar (`datetime`). */\n locale?: string\n /** Marks time slots unavailable; a returned string is shown inline as the reason (`time`). */\n disabledSlots?: TimeSlotDisabledFn\n}\n\nwithDefaults(defineProps<Props>(), {\n modelValue: undefined,\n type: 'date',\n placeholder: undefined,\n disabled: false,\n invalid: false,\n size: 'md',\n min: undefined,\n max: undefined,\n clearable: undefined,\n step: undefined,\n format: undefined,\n locale: undefined,\n disabledSlots: undefined,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | undefined]\n}>()\n</script>\n\n<template>\n <DatePickerTimeInternal\n v-if=\"type === 'time'\"\n :model-value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n :size=\"size\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :format=\"format\"\n :clearable=\"clearable\"\n :disabled-slots=\"disabledSlots\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n <DatePickerDateTimeInternal\n v-else-if=\"type === 'datetime'\"\n :model-value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n :size=\"size\"\n :min=\"min\"\n :max=\"max\"\n :time-step=\"step\"\n :time-format=\"format\"\n :clearable=\"clearable\"\n :locale=\"locale\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n <DatePickerDateInternal\n v-else\n :model-value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n :size=\"size\"\n :min=\"min\"\n :max=\"max\"\n :clearable=\"clearable\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n />\n</template>\n","<script setup lang=\"ts\">\n/**\n * NumberInput with `units`: a numeric input with a unit segment. Several units open a menu that\n * previews the value in each unit; units with a `factor` also get a hint in the neighbour unit\n * nearest to 1–1000.\n */\nimport { computed, nextTick, ref, useTemplateRef } from 'vue'\nimport { useDropdownState } from '../../composables/useDropdownState'\nimport { useFloatingPanel } from '../../composables/useFloatingPanel'\nimport { useMenuKeyboard } from '../../composables/useMenuKeyboard'\nimport type { UnitOption } from '../../types'\n\ninterface Props {\n modelValue?: number | null\n unit?: string\n units: UnitOption[]\n precision?: number\n min?: number\n max?: number\n step?: number\n placeholder?: string\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n convertOnUnitChange?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n precision: undefined,\n step: undefined,\n placeholder: 'Enter value',\n disabled: false,\n invalid: false,\n size: 'md',\n convertOnUnitChange: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number | null]\n 'update:unit': [unit: string]\n}>()\n\nconst currentUnit = computed(() => props.unit || (props.units.length > 0 ? props.units[0].value : ''))\nconst currentOption = computed(() => findUnit(currentUnit.value))\nconst selectable = computed(() => props.units.length > 1)\n\n/** Menu rows: ungrouped units first, then one labelled block per group. */\nconst menuSections = computed(() => {\n const sections = new Map<string, UnitOption[]>()\n for (const u of props.units) {\n const key = u.group ?? ''\n sections.set(key, [...(sections.get(key) ?? []), u])\n }\n return [...sections].map(([label, units]) => ({ label, units }))\n .sort((a, b) => Number(a.label !== '') - Number(b.label !== ''))\n})\n\nfunction findUnit(value: string): UnitOption | undefined {\n return props.units.find(u => u.value === value)\n}\n\nfunction roundToPrecision(value: number): number {\n if (props.precision === undefined) return value\n const factor = Math.pow(10, props.precision)\n return Math.round(value * factor) / factor\n}\n\nfunction clamp(value: number): number {\n let result = value\n if (props.min !== undefined && result < props.min) result = props.min\n if (props.max !== undefined && result > props.max) result = props.max\n return result\n}\n\n/** 4 significant digits; exponent form outside 1e-3 … 1e6. */\nfunction formatNumber(value: number): string {\n const abs = Math.abs(value)\n if (abs !== 0 && (abs >= 1e6 || abs < 1e-3)) return value.toExponential(2)\n return String(parseFloat(value.toPrecision(4)))\n}\n\n/** The current value in `target`; null without factors or across unit groups (M vs mg/mL). */\nfunction convertTo(target: UnitOption): number | null {\n const current = currentOption.value\n if (props.modelValue == null || current?.factor === undefined || !target.factor) return null\n if (target.group !== current.group) return null\n return props.modelValue * current.factor / target.factor\n}\n\nfunction preview(target: UnitOption): string {\n const converted = convertTo(target)\n return converted === null ? '' : formatNumber(converted)\n}\n\n/** Log distance of |value| from the 1–1000 window (0 inside it). */\nfunction distanceFromReadable(value: number): number {\n const exp = Math.log10(Math.abs(value))\n return exp < 0 ? -exp : Math.max(0, exp - 3)\n}\n\nconst conversionHint = computed(() => {\n const current = currentOption.value\n if (!props.modelValue || current?.factor === undefined) return null\n const scale = props.units\n .filter(u => u.factor !== undefined && u.group === current.group)\n .sort((a, b) => a.factor! - b.factor!)\n const index = scale.indexOf(current)\n let best: { unit: UnitOption; value: number } | null = null\n for (const neighbour of [scale[index + 1], scale[index - 1]]) {\n const value = neighbour && convertTo(neighbour)\n if (!value) continue\n if (!best || distanceFromReadable(value) < distanceFromReadable(best.value)) best = { unit: neighbour, value }\n }\n return best && `${formatNumber(best.value)} ${best.unit.label}`\n})\n\n// Typing emits the raw value so a prefix below `min` (the \"1\" of \"120\") stays editable;\n// the change event (blur or Enter) clamps it.\nfunction handleValueInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = target.value === '' ? NaN : Number(target.value)\n emit('update:modelValue', isNaN(value) ? null : value)\n}\n\nfunction handleValueChange(event: Event) {\n const target = event.target as HTMLInputElement\n if (target.value === '') return\n const value = clamp(Number(target.value))\n target.value = String(value)\n emit('update:modelValue', value)\n}\n\nfunction selectUnit(newUnitValue: string) {\n close(true)\n if (newUnitValue === currentUnit.value) return\n\n const value = props.modelValue\n if (props.convertOnUnitChange && value != null) {\n const oldUnit = currentOption.value\n const newUnit = findUnit(newUnitValue)\n if (oldUnit?.factor !== undefined && newUnit?.factor !== undefined && newUnit.factor !== 0) {\n emit('update:modelValue', clamp(roundToPrecision(value * (oldUnit.factor / newUnit.factor))))\n }\n }\n\n emit('update:unit', newUnitValue)\n}\n\n// ---- Unit menu (disclosure + WAI-ARIA keyboard, shared with ActionMenu) ----\nconst menuRef = ref<HTMLElement | null>(null)\nconst { isOpen, rootRef, open, close } = useDropdownState({\n triggerRef: useTemplateRef<HTMLElement>('trigger'),\n insideRefs: [menuRef],\n})\n\n/** Teleported + fixed like ActionMenu, so overflow:hidden/auto ancestors never clip the menu; it follows the field on scroll. */\nconst { style: menuStyle } = useFloatingPanel(rootRef, menuRef, isOpen, { placement: 'bottom-end' })\n\nfunction menuItems(): HTMLElement[] {\n return menuRef.value ? Array.from(menuRef.value.querySelectorAll<HTMLElement>('[role=\"option\"]')) : []\n}\n\nfunction openMenu(focusSelected = true) {\n if (props.disabled || !selectable.value) return\n open()\n if (focusSelected) {\n void nextTick(() => (menuItems().find(el => el.getAttribute('aria-selected') === 'true') ?? menuItems()[0])?.focus())\n }\n}\n\nfunction toggleMenu() {\n if (isOpen.value) close()\n else openMenu()\n}\n\nconst { handleTriggerKeydown, handlePanelKeydown } = useMenuKeyboard({\n items: menuItems,\n isOpen: () => isOpen.value,\n open: () => openMenu(false),\n close,\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-unit-input',\n invalid ? 'mint-unit-input--error' : '',\n disabled ? 'mint-unit-input--disabled' : '',\n ]\"\n >\n <div ref=\"rootRef\" class=\"mint-unit-input__field\">\n <div :class=\"['mint-unit-input__controls', `mint-unit-input__controls--${size}`]\">\n <input\n type=\"number\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :aria-invalid=\"invalid || undefined\"\n :class=\"[\n 'mint-unit-input__value',\n `mint-unit-input__value--${size}`,\n disabled ? 'mint-unit-input__value--disabled' : '',\n ]\"\n aria-label=\"Value\"\n @input=\"handleValueInput\"\n @change=\"handleValueChange\"\n />\n\n <button\n v-if=\"selectable\"\n ref=\"trigger\"\n type=\"button\"\n :class=\"['mint-unit-input__unit', { 'mint-unit-input__unit--open': isOpen }]\"\n :disabled=\"disabled\"\n aria-haspopup=\"listbox\"\n :aria-expanded=\"isOpen\"\n :aria-label=\"`Unit: ${currentOption?.label ?? currentUnit}`\"\n @click=\"toggleMenu\"\n @keydown=\"handleTriggerKeydown\"\n >\n {{ currentOption?.label ?? currentUnit }}\n <svg class=\"mint-unit-input__chevron\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" aria-hidden=\"true\">\n <path d=\"M4 6l4 4 4-4\" />\n </svg>\n </button>\n <span v-else class=\"mint-unit-input__unit\">{{ currentOption?.label ?? currentUnit }}</span>\n </div>\n\n </div>\n\n <Teleport to=\"body\">\n <div\n v-if=\"isOpen\"\n ref=\"menuRef\"\n class=\"mint-popover mint-unit-input__menu\"\n :style=\"menuStyle\"\n role=\"listbox\"\n aria-label=\"Unit\"\n @keydown=\"handlePanelKeydown\"\n >\n <template v-for=\"section in menuSections\" :key=\"section.label\">\n <div v-if=\"section.label\" class=\"mint-unit-input__menu-group\" role=\"presentation\">{{ section.label }}</div>\n <button\n v-for=\"u in section.units\"\n :key=\"u.value\"\n type=\"button\"\n role=\"option\"\n :aria-selected=\"u.value === currentUnit\"\n :class=\"['mint-menu-item', { 'mint-menu-item--selected': u.value === currentUnit }]\"\n @click=\"selectUnit(u.value)\"\n >\n <span class=\"mint-menu-item__text\"><span class=\"mint-menu-item__label\">{{ u.label }}</span></span>\n <span v-if=\"preview(u)\" class=\"mint-menu-item__meta\">{{ preview(u) }}</span>\n </button>\n </template>\n </div>\n </Teleport>\n\n <div v-if=\"conversionHint\" class=\"mint-unit-input__hint\">≈ {{ conversionHint }}</div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * NumberInput with `units`: a numeric input with a unit segment. Several units open a menu that\n * previews the value in each unit; units with a `factor` also get a hint in the neighbour unit\n * nearest to 1–1000.\n */\nimport { computed, nextTick, ref, useTemplateRef } from 'vue'\nimport { useDropdownState } from '../../composables/useDropdownState'\nimport { useFloatingPanel } from '../../composables/useFloatingPanel'\nimport { useMenuKeyboard } from '../../composables/useMenuKeyboard'\nimport type { UnitOption } from '../../types'\n\ninterface Props {\n modelValue?: number | null\n unit?: string\n units: UnitOption[]\n precision?: number\n min?: number\n max?: number\n step?: number\n placeholder?: string\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n convertOnUnitChange?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n precision: undefined,\n step: undefined,\n placeholder: 'Enter value',\n disabled: false,\n invalid: false,\n size: 'md',\n convertOnUnitChange: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number | null]\n 'update:unit': [unit: string]\n}>()\n\nconst currentUnit = computed(() => props.unit || (props.units.length > 0 ? props.units[0].value : ''))\nconst currentOption = computed(() => findUnit(currentUnit.value))\nconst selectable = computed(() => props.units.length > 1)\n\n/** Menu rows: ungrouped units first, then one labelled block per group. */\nconst menuSections = computed(() => {\n const sections = new Map<string, UnitOption[]>()\n for (const u of props.units) {\n const key = u.group ?? ''\n sections.set(key, [...(sections.get(key) ?? []), u])\n }\n return [...sections].map(([label, units]) => ({ label, units }))\n .sort((a, b) => Number(a.label !== '') - Number(b.label !== ''))\n})\n\nfunction findUnit(value: string): UnitOption | undefined {\n return props.units.find(u => u.value === value)\n}\n\nfunction roundToPrecision(value: number): number {\n if (props.precision === undefined) return value\n const factor = Math.pow(10, props.precision)\n return Math.round(value * factor) / factor\n}\n\nfunction clamp(value: number): number {\n let result = value\n if (props.min !== undefined && result < props.min) result = props.min\n if (props.max !== undefined && result > props.max) result = props.max\n return result\n}\n\n/** 4 significant digits; exponent form outside 1e-3 … 1e6. */\nfunction formatNumber(value: number): string {\n const abs = Math.abs(value)\n if (abs !== 0 && (abs >= 1e6 || abs < 1e-3)) return value.toExponential(2)\n return String(parseFloat(value.toPrecision(4)))\n}\n\n/** The current value in `target`; null without factors or across unit groups (M vs mg/mL). */\nfunction convertTo(target: UnitOption): number | null {\n const current = currentOption.value\n if (props.modelValue == null || current?.factor === undefined || !target.factor) return null\n if (target.group !== current.group) return null\n return props.modelValue * current.factor / target.factor\n}\n\nfunction preview(target: UnitOption): string {\n const converted = convertTo(target)\n return converted === null ? '' : formatNumber(converted)\n}\n\n/** Log distance of |value| from the 1–1000 window (0 inside it). */\nfunction distanceFromReadable(value: number): number {\n const exp = Math.log10(Math.abs(value))\n return exp < 0 ? -exp : Math.max(0, exp - 3)\n}\n\nconst conversionHint = computed(() => {\n const current = currentOption.value\n if (!props.modelValue || current?.factor === undefined) return null\n const scale = props.units\n .filter(u => u.factor !== undefined && u.group === current.group)\n .sort((a, b) => a.factor! - b.factor!)\n const index = scale.indexOf(current)\n let best: { unit: UnitOption; value: number } | null = null\n for (const neighbour of [scale[index + 1], scale[index - 1]]) {\n const value = neighbour && convertTo(neighbour)\n if (!value) continue\n if (!best || distanceFromReadable(value) < distanceFromReadable(best.value)) best = { unit: neighbour, value }\n }\n return best && `${formatNumber(best.value)} ${best.unit.label}`\n})\n\n// Typing emits the raw value so a prefix below `min` (the \"1\" of \"120\") stays editable;\n// the change event (blur or Enter) clamps it.\nfunction handleValueInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = target.value === '' ? NaN : Number(target.value)\n emit('update:modelValue', isNaN(value) ? null : value)\n}\n\nfunction handleValueChange(event: Event) {\n const target = event.target as HTMLInputElement\n if (target.value === '') return\n const value = clamp(Number(target.value))\n target.value = String(value)\n emit('update:modelValue', value)\n}\n\nfunction selectUnit(newUnitValue: string) {\n close(true)\n if (newUnitValue === currentUnit.value) return\n\n const value = props.modelValue\n if (props.convertOnUnitChange && value != null) {\n const oldUnit = currentOption.value\n const newUnit = findUnit(newUnitValue)\n if (oldUnit?.factor !== undefined && newUnit?.factor !== undefined && newUnit.factor !== 0) {\n emit('update:modelValue', clamp(roundToPrecision(value * (oldUnit.factor / newUnit.factor))))\n }\n }\n\n emit('update:unit', newUnitValue)\n}\n\n// ---- Unit menu (disclosure + WAI-ARIA keyboard, shared with ActionMenu) ----\nconst menuRef = ref<HTMLElement | null>(null)\nconst { isOpen, rootRef, open, close } = useDropdownState({\n triggerRef: useTemplateRef<HTMLElement>('trigger'),\n insideRefs: [menuRef],\n})\n\n/** Teleported + fixed like ActionMenu, so overflow:hidden/auto ancestors never clip the menu; it follows the field on scroll. */\nconst { style: menuStyle } = useFloatingPanel(rootRef, menuRef, isOpen, { placement: 'bottom-end' })\n\nfunction menuItems(): HTMLElement[] {\n return menuRef.value ? Array.from(menuRef.value.querySelectorAll<HTMLElement>('[role=\"option\"]')) : []\n}\n\nfunction openMenu(focusSelected = true) {\n if (props.disabled || !selectable.value) return\n open()\n if (focusSelected) {\n void nextTick(() => (menuItems().find(el => el.getAttribute('aria-selected') === 'true') ?? menuItems()[0])?.focus())\n }\n}\n\nfunction toggleMenu() {\n if (isOpen.value) close()\n else openMenu()\n}\n\nconst { handleTriggerKeydown, handlePanelKeydown } = useMenuKeyboard({\n items: menuItems,\n isOpen: () => isOpen.value,\n open: () => openMenu(false),\n close,\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-unit-input',\n invalid ? 'mint-unit-input--error' : '',\n disabled ? 'mint-unit-input--disabled' : '',\n ]\"\n >\n <div ref=\"rootRef\" class=\"mint-unit-input__field\">\n <div :class=\"['mint-unit-input__controls', `mint-unit-input__controls--${size}`]\">\n <input\n type=\"number\"\n :value=\"modelValue\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :aria-invalid=\"invalid || undefined\"\n :class=\"[\n 'mint-unit-input__value',\n `mint-unit-input__value--${size}`,\n disabled ? 'mint-unit-input__value--disabled' : '',\n ]\"\n aria-label=\"Value\"\n @input=\"handleValueInput\"\n @change=\"handleValueChange\"\n />\n\n <button\n v-if=\"selectable\"\n ref=\"trigger\"\n type=\"button\"\n :class=\"['mint-unit-input__unit', { 'mint-unit-input__unit--open': isOpen }]\"\n :disabled=\"disabled\"\n aria-haspopup=\"listbox\"\n :aria-expanded=\"isOpen\"\n :aria-label=\"`Unit: ${currentOption?.label ?? currentUnit}`\"\n @click=\"toggleMenu\"\n @keydown=\"handleTriggerKeydown\"\n >\n {{ currentOption?.label ?? currentUnit }}\n <svg class=\"mint-unit-input__chevron\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" aria-hidden=\"true\">\n <path d=\"M4 6l4 4 4-4\" />\n </svg>\n </button>\n <span v-else class=\"mint-unit-input__unit\">{{ currentOption?.label ?? currentUnit }}</span>\n </div>\n\n </div>\n\n <Teleport to=\"body\">\n <div\n v-if=\"isOpen\"\n ref=\"menuRef\"\n class=\"mint-popover mint-unit-input__menu\"\n :style=\"menuStyle\"\n role=\"listbox\"\n aria-label=\"Unit\"\n @keydown=\"handlePanelKeydown\"\n >\n <template v-for=\"section in menuSections\" :key=\"section.label\">\n <div v-if=\"section.label\" class=\"mint-unit-input__menu-group\" role=\"presentation\">{{ section.label }}</div>\n <button\n v-for=\"u in section.units\"\n :key=\"u.value\"\n type=\"button\"\n role=\"option\"\n :aria-selected=\"u.value === currentUnit\"\n :class=\"['mint-menu-item', { 'mint-menu-item--selected': u.value === currentUnit }]\"\n @click=\"selectUnit(u.value)\"\n >\n <span class=\"mint-menu-item__text\"><span class=\"mint-menu-item__label\">{{ u.label }}</span></span>\n <span v-if=\"preview(u)\" class=\"mint-menu-item__meta\">{{ preview(u) }}</span>\n </button>\n </template>\n </div>\n </Teleport>\n\n <div v-if=\"conversionHint\" class=\"mint-unit-input__hint\">≈ {{ conversionHint }}</div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Numeric input with always-visible steppers, optional unit label, and (with min+max) an in-field drag-to-scrub range fill.\n * With `units` it renders a unit segment instead: `v-model:unit` holds the chosen unit, and several units open a unit menu.\n *\n * @example\n * ```vue\n * <NumberInput v-model=\"dose\" unit=\"µM\" />\n * <NumberInput v-model=\"volume\" v-model:unit=\"volumeUnit\" :units=\"volumeUnits\" />\n * ```\n */\nimport { computed, onBeforeUnmount, useAttrs, type StyleValue } from 'vue'\nimport { ChevronDown, ChevronUp } from 'lucide-vue-next'\nimport type { UnitOption } from '../types'\nimport NumberUnitInputInternal from './internal/NumberUnitInputInternal.vue'\n\n// `class` and `style` style the field; every other attribute (id, name, aria-*) goes on the number input.\ndefineOptions({ inheritAttrs: false })\n\ninterface Props {\n modelValue?: number | null\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n placeholder?: string\n /** Unit label; with `units`, the selected unit value (`v-model:unit`). */\n unit?: string\n /** Selectable units; renders the unit segment instead of the steppers. */\n units?: UnitOption[]\n /** Decimal places kept when `convertOnUnitChange` converts the value. */\n precision?: number\n /** Convert the value by the unit factors when the unit changes (needs `units` with `factor`). */\n convertOnUnitChange?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: null,\n step: 1,\n disabled: false,\n invalid: false,\n size: 'md',\n units: undefined,\n precision: undefined,\n convertOnUnitChange: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number | null]\n 'update:unit': [unit: string]\n}>()\n\nconst attrs = useAttrs()\nconst inputAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs\n return rest\n})\n\nconst isSliderMode = computed(() => props.min !== undefined && props.max !== undefined)\n\nconst sliderPercent = computed(() => {\n const min = props.min\n const max = props.max\n if (min === undefined || max === undefined || props.modelValue == null) return 0\n const range = max - min\n if (range === 0) return 0\n return Math.min(100, Math.max(0, ((props.modelValue - min) / range) * 100))\n})\n\nconst canDecrement = computed(() => {\n if (props.modelValue == null) return true\n if (props.min === undefined) return true\n return props.modelValue > props.min\n})\n\nconst canIncrement = computed(() => {\n if (props.modelValue == null) return true\n if (props.max === undefined) return true\n return props.modelValue < props.max\n})\n\nfunction clamp(value: number): number {\n let result = value\n if (props.min !== undefined && result < props.min) result = props.min\n if (props.max !== undefined && result > props.max) result = props.max\n return result\n}\n\n/** Clamp and trim float noise (0.1 + 0.2 → 0.3) without losing real precision. */\nfunction settle(value: number): number {\n return Number(clamp(value).toFixed(10))\n}\n\n/** Snap to the step grid anchored at `min`, for pointer scrubbing. */\nfunction snap(value: number): number {\n const origin = props.min ?? 0\n return settle(origin + Math.round((value - origin) / props.step) * props.step)\n}\n\n// Typing emits the raw value so a prefix below `min` (the \"1\" of \"120\") stays editable;\n// the change event (blur or Enter) clamps it.\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = target.value === '' ? NaN : Number(target.value)\n emit('update:modelValue', isNaN(value) ? null : value)\n}\n\nfunction handleChange(event: Event) {\n const target = event.target as HTMLInputElement\n if (target.value === '') return\n const value = clamp(Number(target.value))\n target.value = String(value)\n emit('update:modelValue', value)\n}\n\nfunction nudge(delta: number) {\n if (props.disabled) return\n if (delta < 0 && !canDecrement.value) return\n if (delta > 0 && !canIncrement.value) return\n const current = props.modelValue ?? (delta < 0 ? props.max ?? 0 : props.min ?? 0)\n emit('update:modelValue', settle(current + delta))\n}\n\nfunction decrement() {\n nudge(-props.step)\n}\n\nfunction increment() {\n nudge(props.step)\n}\n\n// Home/End only apply on the slider surface: inside the text input they must\n// keep moving the caret.\nfunction handleKeydown(event: KeyboardEvent, allowJump = false) {\n if (props.disabled) return\n switch (event.key) {\n case 'ArrowUp': increment(); break\n case 'ArrowDown': decrement(); break\n case 'PageUp': nudge(props.step * 10); break\n case 'PageDown': nudge(-props.step * 10); break\n case 'Home':\n if (!allowJump || props.min === undefined) return\n emit('update:modelValue', props.min)\n break\n case 'End':\n if (!allowJump || props.max === undefined) return\n emit('update:modelValue', props.max)\n break\n default:\n return\n }\n event.preventDefault()\n}\n\n// The surrounding range uses absolute position; dragging the number uses a\n// relative offset so grabbing it never jumps away from the current value.\nfunction scrubTo(clientX: number, area: HTMLElement) {\n const rect = area.getBoundingClientRect()\n if (rect.width === 0 || props.min === undefined || props.max === undefined) return\n const ratio = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width))\n emit('update:modelValue', snap(props.min + ratio * (props.max - props.min)))\n}\n\nlet stopScrubbing: (() => void) | undefined\nonBeforeUnmount(() => stopScrubbing?.())\n\nfunction handleScrubStart(event: PointerEvent) {\n const target = event.target as HTMLElement\n const onNumber = target.tagName === 'INPUT'\n if (props.disabled || event.button !== 0 || (!onNumber && !isSliderMode.value)) return\n stopScrubbing?.()\n const area = event.currentTarget as HTMLElement\n const startValue = props.modelValue ?? props.min ?? 0\n const width = area.getBoundingClientRect().width\n const unitsPerPixel = isSliderMode.value && width > 0\n ? (props.max! - props.min!) / width\n : props.step / 8\n // Preserve ordinary clicks and double-clicks for caret placement and typing.\n if (!onNumber) {\n event.preventDefault()\n scrubTo(event.clientX, area)\n }\n target.setPointerCapture?.(event.pointerId)\n let dragging = false\n const move = (e: PointerEvent) => {\n if (e.pointerId !== event.pointerId) return\n if (props.disabled) { stop(); return }\n if (!onNumber) { scrubTo(e.clientX, area); return }\n const delta = e.clientX - event.clientX\n if (!dragging && Math.abs(delta) < 4) return\n dragging = true\n e.preventDefault()\n emit('update:modelValue', settle(startValue + Math.round(delta * unitsPerPixel / props.step) * props.step))\n }\n const stop = () => {\n area.removeEventListener('pointermove', move)\n area.removeEventListener('pointerup', stop)\n area.removeEventListener('pointercancel', stop)\n area.removeEventListener('lostpointercapture', stop)\n if (target.hasPointerCapture?.(event.pointerId)) target.releasePointerCapture(event.pointerId)\n stopScrubbing = undefined\n }\n stopScrubbing = stop\n area.addEventListener('pointermove', move)\n area.addEventListener('pointerup', stop)\n area.addEventListener('pointercancel', stop)\n area.addEventListener('lostpointercapture', stop)\n}\n</script>\n\n<template>\n <NumberUnitInputInternal\n v-if=\"units\"\n v-bind=\"attrs\"\n :model-value=\"modelValue\"\n :unit=\"unit\"\n :units=\"units\"\n :precision=\"precision\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n :size=\"size\"\n :convert-on-unit-change=\"convertOnUnitChange\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @update:unit=\"emit('update:unit', $event)\"\n />\n <div\n v-else\n :class=\"[\n 'mint-number-input',\n `mint-number-input--${size}`,\n isSliderMode ? 'mint-number-input--slider' : 'mint-number-input--stepper',\n invalid ? 'mint-number-input--error' : '',\n disabled ? 'mint-number-input--disabled' : '',\n attrs.class,\n ]\"\n :style=\"attrs.style as StyleValue\"\n >\n <div class=\"mint-number-input__field\">\n <div\n class=\"mint-number-input__scrub\"\n :style=\"isSliderMode ? { '--mint-number-input-fill': `${sliderPercent}%` } : undefined\"\n :role=\"isSliderMode ? 'slider' : undefined\"\n :tabindex=\"isSliderMode && !disabled ? 0 : undefined\"\n :aria-valuenow=\"isSliderMode ? modelValue ?? undefined : undefined\"\n :aria-valuemin=\"isSliderMode ? min : undefined\"\n :aria-valuemax=\"isSliderMode ? max : undefined\"\n :aria-orientation=\"isSliderMode ? 'horizontal' : undefined\"\n @pointerdown=\"handleScrubStart\"\n @keydown.self=\"isSliderMode && handleKeydown($event, true)\"\n >\n <template v-if=\"isSliderMode\">\n <span class=\"mint-number-input__wash\" aria-hidden=\"true\" />\n <span class=\"mint-number-input__rule\" aria-hidden=\"true\" />\n </template>\n <input\n v-bind=\"inputAttrs\"\n type=\"number\"\n :value=\"modelValue ?? undefined\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :aria-invalid=\"invalid || undefined\"\n :class=\"[\n 'mint-number-input__input',\n `mint-number-input__input--${size}`,\n ]\"\n @input=\"handleInput\"\n @change=\"handleChange\"\n @keydown=\"handleKeydown\"\n />\n <span v-if=\"unit\" class=\"mint-number-input__unit\">{{ unit }}</span>\n </div>\n\n <div class=\"mint-number-input__steppers\" aria-hidden=\"true\">\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"Decrease value\"\n :disabled=\"disabled || !canDecrement\"\n class=\"mint-number-input__stepper mint-number-input__stepper--down\"\n @click=\"decrement\"\n >\n <ChevronDown :size=\"12\" :stroke-width=\"2.25\" />\n </button>\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"Increase value\"\n :disabled=\"disabled || !canIncrement\"\n class=\"mint-number-input__stepper mint-number-input__stepper--up\"\n @click=\"increment\"\n >\n <ChevronUp :size=\"12\" :stroke-width=\"2.25\" />\n </button>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Numeric input with always-visible steppers, optional unit label, and (with min+max) an in-field drag-to-scrub range fill.\n * With `units` it renders a unit segment instead: `v-model:unit` holds the chosen unit, and several units open a unit menu.\n *\n * @example\n * ```vue\n * <NumberInput v-model=\"dose\" unit=\"µM\" />\n * <NumberInput v-model=\"volume\" v-model:unit=\"volumeUnit\" :units=\"volumeUnits\" />\n * ```\n */\nimport { computed, onBeforeUnmount, useAttrs, type StyleValue } from 'vue'\nimport { ChevronDown, ChevronUp } from 'lucide-vue-next'\nimport type { UnitOption } from '../types'\nimport NumberUnitInputInternal from './internal/NumberUnitInputInternal.vue'\n\n// `class` and `style` style the field; every other attribute (id, name, aria-*) goes on the number input.\ndefineOptions({ inheritAttrs: false })\n\ninterface Props {\n modelValue?: number | null\n min?: number\n max?: number\n step?: number\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n placeholder?: string\n /** Unit label; with `units`, the selected unit value (`v-model:unit`). */\n unit?: string\n /** Selectable units; renders the unit segment instead of the steppers. */\n units?: UnitOption[]\n /** Decimal places kept when `convertOnUnitChange` converts the value. */\n precision?: number\n /** Convert the value by the unit factors when the unit changes (needs `units` with `factor`). */\n convertOnUnitChange?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: null,\n step: 1,\n disabled: false,\n invalid: false,\n size: 'md',\n units: undefined,\n precision: undefined,\n convertOnUnitChange: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number | null]\n 'update:unit': [unit: string]\n}>()\n\nconst attrs = useAttrs()\nconst inputAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs\n return rest\n})\n\nconst isSliderMode = computed(() => props.min !== undefined && props.max !== undefined)\n\nconst sliderPercent = computed(() => {\n const min = props.min\n const max = props.max\n if (min === undefined || max === undefined || props.modelValue == null) return 0\n const range = max - min\n if (range === 0) return 0\n return Math.min(100, Math.max(0, ((props.modelValue - min) / range) * 100))\n})\n\nconst canDecrement = computed(() => {\n if (props.modelValue == null) return true\n if (props.min === undefined) return true\n return props.modelValue > props.min\n})\n\nconst canIncrement = computed(() => {\n if (props.modelValue == null) return true\n if (props.max === undefined) return true\n return props.modelValue < props.max\n})\n\nfunction clamp(value: number): number {\n let result = value\n if (props.min !== undefined && result < props.min) result = props.min\n if (props.max !== undefined && result > props.max) result = props.max\n return result\n}\n\n/** Clamp and trim float noise (0.1 + 0.2 → 0.3) without losing real precision. */\nfunction settle(value: number): number {\n return Number(clamp(value).toFixed(10))\n}\n\n/** Snap to the step grid anchored at `min`, for pointer scrubbing. */\nfunction snap(value: number): number {\n const origin = props.min ?? 0\n return settle(origin + Math.round((value - origin) / props.step) * props.step)\n}\n\n// Typing emits the raw value so a prefix below `min` (the \"1\" of \"120\") stays editable;\n// the change event (blur or Enter) clamps it.\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = target.value === '' ? NaN : Number(target.value)\n emit('update:modelValue', isNaN(value) ? null : value)\n}\n\nfunction handleChange(event: Event) {\n const target = event.target as HTMLInputElement\n if (target.value === '') return\n const value = clamp(Number(target.value))\n target.value = String(value)\n emit('update:modelValue', value)\n}\n\nfunction nudge(delta: number) {\n if (props.disabled) return\n if (delta < 0 && !canDecrement.value) return\n if (delta > 0 && !canIncrement.value) return\n const current = props.modelValue ?? (delta < 0 ? props.max ?? 0 : props.min ?? 0)\n emit('update:modelValue', settle(current + delta))\n}\n\nfunction decrement() {\n nudge(-props.step)\n}\n\nfunction increment() {\n nudge(props.step)\n}\n\n// Home/End only apply on the slider surface: inside the text input they must\n// keep moving the caret.\nfunction handleKeydown(event: KeyboardEvent, allowJump = false) {\n if (props.disabled) return\n switch (event.key) {\n case 'ArrowUp': increment(); break\n case 'ArrowDown': decrement(); break\n case 'PageUp': nudge(props.step * 10); break\n case 'PageDown': nudge(-props.step * 10); break\n case 'Home':\n if (!allowJump || props.min === undefined) return\n emit('update:modelValue', props.min)\n break\n case 'End':\n if (!allowJump || props.max === undefined) return\n emit('update:modelValue', props.max)\n break\n default:\n return\n }\n event.preventDefault()\n}\n\n// The surrounding range uses absolute position; dragging the number uses a\n// relative offset so grabbing it never jumps away from the current value.\nfunction scrubTo(clientX: number, area: HTMLElement) {\n const rect = area.getBoundingClientRect()\n if (rect.width === 0 || props.min === undefined || props.max === undefined) return\n const ratio = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width))\n emit('update:modelValue', snap(props.min + ratio * (props.max - props.min)))\n}\n\nlet stopScrubbing: (() => void) | undefined\nonBeforeUnmount(() => stopScrubbing?.())\n\nfunction handleScrubStart(event: PointerEvent) {\n const target = event.target as HTMLElement\n const onNumber = target.tagName === 'INPUT'\n if (props.disabled || event.button !== 0 || (!onNumber && !isSliderMode.value)) return\n stopScrubbing?.()\n const area = event.currentTarget as HTMLElement\n const startValue = props.modelValue ?? props.min ?? 0\n const width = area.getBoundingClientRect().width\n const unitsPerPixel = isSliderMode.value && width > 0\n ? (props.max! - props.min!) / width\n : props.step / 8\n // Preserve ordinary clicks and double-clicks for caret placement and typing.\n if (!onNumber) {\n event.preventDefault()\n scrubTo(event.clientX, area)\n }\n target.setPointerCapture?.(event.pointerId)\n let dragging = false\n const move = (e: PointerEvent) => {\n if (e.pointerId !== event.pointerId) return\n if (props.disabled) { stop(); return }\n if (!onNumber) { scrubTo(e.clientX, area); return }\n const delta = e.clientX - event.clientX\n if (!dragging && Math.abs(delta) < 4) return\n dragging = true\n e.preventDefault()\n emit('update:modelValue', settle(startValue + Math.round(delta * unitsPerPixel / props.step) * props.step))\n }\n const stop = () => {\n area.removeEventListener('pointermove', move)\n area.removeEventListener('pointerup', stop)\n area.removeEventListener('pointercancel', stop)\n area.removeEventListener('lostpointercapture', stop)\n if (target.hasPointerCapture?.(event.pointerId)) target.releasePointerCapture(event.pointerId)\n stopScrubbing = undefined\n }\n stopScrubbing = stop\n area.addEventListener('pointermove', move)\n area.addEventListener('pointerup', stop)\n area.addEventListener('pointercancel', stop)\n area.addEventListener('lostpointercapture', stop)\n}\n</script>\n\n<template>\n <NumberUnitInputInternal\n v-if=\"units\"\n v-bind=\"attrs\"\n :model-value=\"modelValue\"\n :unit=\"unit\"\n :units=\"units\"\n :precision=\"precision\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n :size=\"size\"\n :convert-on-unit-change=\"convertOnUnitChange\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n @update:unit=\"emit('update:unit', $event)\"\n />\n <div\n v-else\n :class=\"[\n 'mint-number-input',\n `mint-number-input--${size}`,\n isSliderMode ? 'mint-number-input--slider' : 'mint-number-input--stepper',\n invalid ? 'mint-number-input--error' : '',\n disabled ? 'mint-number-input--disabled' : '',\n attrs.class,\n ]\"\n :style=\"attrs.style as StyleValue\"\n >\n <div class=\"mint-number-input__field\">\n <div\n class=\"mint-number-input__scrub\"\n :style=\"isSliderMode ? { '--mint-number-input-fill': `${sliderPercent}%` } : undefined\"\n :role=\"isSliderMode ? 'slider' : undefined\"\n :tabindex=\"isSliderMode && !disabled ? 0 : undefined\"\n :aria-valuenow=\"isSliderMode ? modelValue ?? undefined : undefined\"\n :aria-valuemin=\"isSliderMode ? min : undefined\"\n :aria-valuemax=\"isSliderMode ? max : undefined\"\n :aria-orientation=\"isSliderMode ? 'horizontal' : undefined\"\n @pointerdown=\"handleScrubStart\"\n @keydown.self=\"isSliderMode && handleKeydown($event, true)\"\n >\n <template v-if=\"isSliderMode\">\n <span class=\"mint-number-input__wash\" aria-hidden=\"true\" />\n <span class=\"mint-number-input__rule\" aria-hidden=\"true\" />\n </template>\n <input\n v-bind=\"inputAttrs\"\n type=\"number\"\n :value=\"modelValue ?? undefined\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :aria-invalid=\"invalid || undefined\"\n :class=\"[\n 'mint-number-input__input',\n `mint-number-input__input--${size}`,\n ]\"\n @input=\"handleInput\"\n @change=\"handleChange\"\n @keydown=\"handleKeydown\"\n />\n <span v-if=\"unit\" class=\"mint-number-input__unit\">{{ unit }}</span>\n </div>\n\n <div class=\"mint-number-input__steppers\" aria-hidden=\"true\">\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"Decrease value\"\n :disabled=\"disabled || !canDecrement\"\n class=\"mint-number-input__stepper mint-number-input__stepper--down\"\n @click=\"decrement\"\n >\n <ChevronDown :size=\"12\" :stroke-width=\"2.25\" />\n </button>\n <button\n type=\"button\"\n tabindex=\"-1\"\n aria-label=\"Increase value\"\n :disabled=\"disabled || !canIncrement\"\n class=\"mint-number-input__stepper mint-number-input__stepper--up\"\n @click=\"increment\"\n >\n <ChevronUp :size=\"12\" :stroke-width=\"2.25\" />\n </button>\n </div>\n </div>\n </div>\n</template>\n","import { ref, computed } from 'vue'\nimport { getRoleInfo, hasAllPermissions, isAdminUser } from '../permissions'\nimport type { Access, PlatformContext, ThemeMode } from '../types'\nimport type { PlatformFrontendContext } from '../types/generated/platformContext'\n\nconst DEFAULT_CONTEXT: PlatformContext = {\n isIntegrated: false,\n theme: 'system',\n}\n\nconst platformContext = ref<PlatformContext>({ ...DEFAULT_CONTEXT })\n\nfunction storedTheme(): ThemeMode {\n try {\n const s = localStorage.getItem('mint-settings')\n if (s) return (JSON.parse(s).theme as ThemeMode) || 'system'\n } catch { /* ignore */ }\n return 'system'\n}\n\nfunction detectPlatform(): void {\n // The platform injects its context when it serves the plugin page. It sends no theme.\n const platformData = (window as unknown as { __MINT_PLATFORM__?: PlatformFrontendContext }).__MINT_PLATFORM__\n\n if (platformData) {\n platformContext.value = {\n ...platformData,\n isIntegrated: true,\n theme: storedTheme(),\n }\n return\n }\n\n const urlParams = new URLSearchParams(window.location.search)\n platformContext.value = {\n isIntegrated: urlParams.has('mint-plugin'),\n theme: storedTheme(),\n }\n}\n\nconst ACCESS_PERMISSIONS: Record<Exclude<Access, 'admin'>, readonly string[]> = {\n use: ['plugins.use'],\n configure: ['plugins.use', 'plugins.configure'],\n}\n\n/**\n * Read the platform context the MINT platform injects into a plugin page.\n *\n * `can(access)` answers with the same three levels as the Python `access`\n * option. A page with no signed-in user passes every check when it runs\n * standalone or the platform reports `authRequired: false`; the platform\n * still checks every request.\n *\n * @example\n * ```typescript\n * const platform = usePlatform()\n * const canConfigure = computed(() => platform.can('configure'))\n * ```\n */\nexport function usePlatform() {\n // Read the context on every call, so a router guard or a setup-time check\n // sees the injected user before any component has mounted.\n detectPlatform()\n\n const integrated = computed(() => platformContext.value.isIntegrated)\n const plugin = computed(() => platformContext.value.plugin)\n const user = computed(() => platformContext.value.user)\n const theme = computed(() => platformContext.value.theme)\n const features = computed(() => platformContext.value.features)\n\n function can(access: Access): boolean {\n const current = user.value\n // With auth disabled every caller is the implicit administrator.\n if (!current) return !integrated.value || platformContext.value.authRequired === false\n if (isAdminUser(current)) return true\n if (access === 'admin' || !hasAllPermissions(current, ACCESS_PERMISSIONS[access])) return false\n const visible = getRoleInfo(current)?.plugin_access\n if (!visible || visible === 'all') return true\n // The platform matches a role's list by registry name or by route prefix.\n const names = [plugin.value?.id, plugin.value?.route_prefix?.replace(/^\\/+|\\/+$/g, '')]\n return Array.isArray(visible) && names.some((name) => !!name && visible.includes(name))\n }\n\n const isAdmin = computed(() => can('admin'))\n\n return {\n integrated,\n plugin,\n user,\n theme,\n features,\n isAdmin,\n can,\n }\n}\n","import {\n getCurrentInstance,\n getCurrentScope,\n onMounted,\n onScopeDispose,\n ref,\n shallowRef,\n type Ref,\n} from 'vue'\nimport { tryUseSettingsStore } from '../stores/settings'\n\nexport type EventStreamTransport = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>\n\nexport interface EventStreamMessage<TData = string> {\n event: string\n data: TData\n id?: string\n retry?: number\n raw: string\n}\n\nexport interface CreateEventStreamOptions<TData = string> {\n transport?: EventStreamTransport\n headers?: HeadersInit\n credentials?: RequestCredentials\n immediate?: boolean\n /**\n * Reconnect after a dropped stream. Left unset, it follows the platform's\n * `wsAutoReconnect` setting, defaulting to `true` without a settings store.\n */\n reconnect?: boolean\n /**\n * Delay before the first reconnect attempt. Left unset, it follows the\n * platform's `wsReconnectInterval` setting, defaulting to 2000 ms without a\n * settings store. A server-sent `retry:` field still overrides it.\n */\n reconnectDelayMs?: number\n parseJson?: boolean\n shouldReconnect?: (cause?: unknown) => boolean\n onOpen?: () => void\n onMessage?: (message: EventStreamMessage<TData>) => void\n onError?: (cause: unknown) => void\n onClose?: (cause?: unknown) => void\n errorLabel?: string\n}\n\nexport interface UseEventStreamReturn<TData = string> {\n isConnected: Ref<boolean>\n isConnecting: Ref<boolean>\n error: Ref<string | null>\n lastMessage: Ref<EventStreamMessage<TData> | null>\n lastEventId: Ref<string | null>\n start: () => void\n stop: () => void\n}\n\ninterface ParsedEventStreamBlock<TData> {\n message?: EventStreamMessage<TData>\n id?: string\n retry?: number\n}\n\nclass EventStreamResponseError extends Error {\n constructor(readonly response: Response, label: string) {\n super(`${label} failed with HTTP ${response.status}`)\n this.name = 'EventStreamResponseError'\n }\n}\n\n/** Parse one complete SSE block without owning connection policy. */\nexport function parseEventStreamBlock<TData>(\n block: string,\n parseJson: boolean,\n): ParsedEventStreamBlock<TData> | null {\n let event = 'message'\n let id: string | undefined\n let retry: number | undefined\n const dataLines: string[] = []\n\n for (const line of block.split(/\\r\\n|\\r|\\n/)) {\n if (!line || line.startsWith(':')) continue\n const separator = line.indexOf(':')\n const field = separator === -1 ? line : line.slice(0, separator)\n const value = separator === -1\n ? ''\n : line.slice(separator + 1).replace(/^ /, '')\n\n if (field === 'event') event = value || 'message'\n if (field === 'data') dataLines.push(value)\n if (field === 'id' && !value.includes('\\0')) id = value\n if (field === 'retry' && /^\\d+$/.test(value)) retry = Number(value)\n }\n\n if (!dataLines.length && id === undefined && retry === undefined) return null\n const data = dataLines.join('\\n')\n return {\n message: dataLines.length\n ? {\n event,\n data: (parseJson && data ? JSON.parse(data) : data) as TData,\n id,\n retry,\n raw: block,\n }\n : undefined,\n id,\n retry,\n }\n}\n\n/** Shared fetch-based SSE lifecycle with cancellation and reconnect safety. */\nexport function createEventStream<TData = string>(\n url: string | (() => string),\n options: CreateEventStreamOptions<TData> = {},\n): UseEventStreamReturn<TData> {\n const isConnected = ref(false)\n const isConnecting = ref(false)\n const error = ref<string | null>(null)\n const lastMessage = shallowRef<EventStreamMessage<TData> | null>(null)\n const lastEventId = ref<string | null>(null)\n let controller: AbortController | null = null\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null\n const settings = tryUseSettingsStore()\n const reconnectEnabled = options.reconnect ?? settings?.wsAutoReconnect ?? true\n let reconnectDelayMs = options.reconnectDelayMs ?? settings?.wsReconnectInterval ?? 2_000\n let stopped = true\n let generation = 0\n\n function clearReconnectTimer(): void {\n if (reconnectTimer === null) return\n clearTimeout(reconnectTimer)\n reconnectTimer = null\n }\n\n function ownsConnection(\n connectionGeneration: number,\n connectionController: AbortController,\n ): boolean {\n return !stopped\n && generation === connectionGeneration\n && controller === connectionController\n }\n\n function scheduleReconnect(connectionGeneration: number, cause?: unknown): void {\n if (stopped || generation !== connectionGeneration) return\n if (!reconnectEnabled || options.shouldReconnect?.(cause) === false) {\n stopped = true\n return\n }\n clearReconnectTimer()\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null\n if (stopped || generation !== connectionGeneration) return\n void connect(connectionGeneration)\n }, reconnectDelayMs)\n }\n\n function dispatchBlock(\n block: string,\n connectionGeneration: number,\n connectionController: AbortController,\n ): void {\n const parsed = parseEventStreamBlock<TData>(block, options.parseJson ?? false)\n if (!parsed || !ownsConnection(connectionGeneration, connectionController)) return\n if (parsed.id !== undefined) {\n lastEventId.value = parsed.id\n if (!ownsConnection(connectionGeneration, connectionController)) return\n }\n if (parsed.retry !== undefined) reconnectDelayMs = parsed.retry\n const message = parsed.message\n if (!message || !ownsConnection(connectionGeneration, connectionController)) return\n lastMessage.value = message\n if (!ownsConnection(connectionGeneration, connectionController)) return\n options.onMessage?.(message)\n }\n\n async function consume(\n body: ReadableStream<Uint8Array>,\n connectionGeneration: number,\n connectionController: AbortController,\n ): Promise<void> {\n const connectionReader = body.getReader()\n reader = connectionReader\n const decoder = new TextDecoder()\n let buffer = ''\n try {\n for (;;) {\n const { value, done } = await connectionReader.read()\n if (!ownsConnection(connectionGeneration, connectionController)) return\n buffer += decoder.decode(value, { stream: !done })\n\n let boundary = /(?:\\r\\n|\\r|\\n){2}/.exec(buffer)\n while (boundary?.index !== undefined) {\n const block = buffer.slice(0, boundary.index)\n buffer = buffer.slice(boundary.index + boundary[0].length)\n dispatchBlock(block, connectionGeneration, connectionController)\n if (!ownsConnection(connectionGeneration, connectionController)) return\n boundary = /(?:\\r\\n|\\r|\\n){2}/.exec(buffer)\n }\n\n if (!done) continue\n if (buffer) dispatchBlock(buffer, connectionGeneration, connectionController)\n return\n }\n } finally {\n if (reader === connectionReader) reader = null\n connectionReader.releaseLock()\n }\n }\n\n async function connect(connectionGeneration: number): Promise<void> {\n if (stopped || generation !== connectionGeneration) return\n controller?.abort()\n const connectionController = new AbortController()\n controller = connectionController\n isConnecting.value = true\n if (!ownsConnection(connectionGeneration, connectionController)) return\n error.value = null\n if (!ownsConnection(connectionGeneration, connectionController)) return\n\n let cause: unknown\n try {\n const headers = new Headers(options.headers)\n headers.set('Accept', 'text/event-stream')\n if (lastEventId.value && !headers.has('Last-Event-ID')) {\n headers.set('Last-Event-ID', lastEventId.value)\n }\n const transport = options.transport ?? globalThis.fetch?.bind(globalThis)\n if (!transport) throw new Error('[MINT SDK] fetch is required for event streams.')\n const response = await transport(typeof url === 'function' ? url() : url, {\n method: 'GET',\n headers,\n credentials: options.credentials ?? 'same-origin',\n signal: connectionController.signal,\n })\n if (!ownsConnection(connectionGeneration, connectionController)) return\n if (!response.ok) {\n throw new EventStreamResponseError(response, options.errorLabel ?? 'Event stream')\n }\n if (!response.body) {\n throw new Error(`${options.errorLabel ?? 'Event stream'} response did not include a readable body.`)\n }\n\n isConnected.value = true\n if (!ownsConnection(connectionGeneration, connectionController)) return\n isConnecting.value = false\n if (!ownsConnection(connectionGeneration, connectionController)) return\n options.onOpen?.()\n if (!ownsConnection(connectionGeneration, connectionController)) return\n await consume(response.body, connectionGeneration, connectionController)\n } catch (errorCause) {\n cause = errorCause\n if (\n ownsConnection(connectionGeneration, connectionController)\n && !isAbortError(errorCause)\n ) {\n error.value = errorCause instanceof Error\n ? errorCause.message\n : `${options.errorLabel ?? 'Event stream'} failed`\n if (ownsConnection(connectionGeneration, connectionController)) {\n options.onError?.(errorCause)\n }\n }\n } finally {\n if (ownsConnection(connectionGeneration, connectionController)) {\n isConnecting.value = false\n if (!ownsConnection(connectionGeneration, connectionController)) return\n isConnected.value = false\n if (!ownsConnection(connectionGeneration, connectionController)) return\n controller = null\n options.onClose?.(cause)\n if (!stopped && generation === connectionGeneration) {\n scheduleReconnect(connectionGeneration, cause)\n }\n }\n }\n }\n\n function start(): void {\n if (!stopped) return\n stopped = false\n clearReconnectTimer()\n generation += 1\n void connect(generation)\n }\n\n function stop(): void {\n if (stopped) return\n stopped = true\n generation += 1\n clearReconnectTimer()\n controller?.abort()\n controller = null\n if (reader) void reader.cancel().catch(() => undefined)\n reader = null\n isConnecting.value = false\n isConnected.value = false\n }\n\n const currentInstance = getCurrentInstance()\n if (currentInstance && options.immediate !== false && typeof window === 'undefined') {\n onMounted(start)\n }\n if (getCurrentScope()) onScopeDispose(stop)\n if (options.immediate !== false && (typeof window !== 'undefined' || !currentInstance)) {\n start()\n }\n\n return {\n isConnected,\n isConnecting,\n error,\n lastMessage,\n lastEventId,\n start,\n stop,\n }\n}\n\nfunction isAbortError(cause: unknown): boolean {\n return cause instanceof DOMException && cause.name === 'AbortError'\n}\n","import {\n computed,\n onScopeDispose,\n ref,\n shallowRef,\n type ComputedRef,\n type Ref,\n type ShallowRef,\n} from 'vue'\n\nimport type { PluginJobCenterSource } from './useJobCenter'\nimport { createEventStream, type EventStreamMessage } from './eventStream'\nimport { isActiveJobStatus, normalizeJobState } from '../jobs'\nimport { MintApiError, mintApiErrorFromResponse } from '../apiErrors'\nimport { AUTH_TOKEN_STORAGE_KEY } from '../stores/auth'\nimport type {\n GeneratedAnalysisJob,\n GeneratedAnalysisJobInput,\n GeneratedAnalysisResult,\n GeneratedSession,\n GeneratedAnalysisManifest,\n PluginJobArtifactPathInput,\n PluginJobPathInput,\n PluginJobPathUpload,\n PluginJobStagedPartInput,\n PluginJobStagedPartUploadResult,\n PluginJobStaging,\n PluginJobStagingPagination,\n} from '../types/generated-ui'\n\nexport type GeneratedAnalysisTransport = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>\n\nexport interface GeneratedAnalysisTransportOptions {\n fetcher?: typeof fetch\n getAccessToken?: () => string | null | undefined\n}\n\nexport interface UseJobsEndpointOptions {\n baseUrl: string\n transport?: GeneratedAnalysisTransport\n storage?: Storage\n createId?: () => string\n /** Milliseconds between job list polls while no event stream is open. Default 750. */\n poll?: number\n stream?: boolean\n streamReconnectDelay?: number\n /** List the existing jobs at once instead of on the first `submit()` or `refresh()`. */\n immediate?: boolean\n /** Load the generated UI manifest (`/generated/manifest`) before the jobs. */\n manifest?: boolean\n}\n\nexport interface PluginJobWaitOptions {\n signal?: AbortSignal\n rejectOnFailure?: boolean\n}\n\nexport interface PluginJobRequestOptions {\n signal?: AbortSignal\n}\n\ninterface GeneratedSessionStorage {\n getItem: (key: string) => string | null\n setItem: (key: string, value: string) => void\n}\n\nexport interface UseJobsEndpointReturn extends PluginJobCenterSource {\n manifest: ShallowRef<GeneratedAnalysisManifest | null>\n session: ShallowRef<GeneratedSession | null>\n jobs: ShallowRef<GeneratedAnalysisJob[]>\n latestResult: ComputedRef<GeneratedAnalysisResult | null>\n isLoading: Ref<boolean>\n isSubmitting: Ref<boolean>\n error: ShallowRef<string | null>\n status: ShallowRef<'idle' | 'initializing' | 'ready' | 'paused' | 'error' | 'disposed'>\n uploadPath: (\n input: PluginJobPathInput,\n definitionId?: string,\n ) => Promise<PluginJobPathUpload>\n uploadStagedPart: <Metadata>(\n input: PluginJobStagedPartInput<Metadata>,\n ) => Promise<PluginJobStagedPartUploadResult<Metadata>>\n getJobStaging: <Metadata = unknown>(\n job: GeneratedAnalysisJob | string,\n pagination?: PluginJobStagingPagination,\n ) => Promise<PluginJobStaging<Metadata>>\n sealJob: (job: GeneratedAnalysisJob | string) => Promise<GeneratedAnalysisJob>\n /** Submit a job. The first call opens the job session. */\n submit: (definitionId: string, inputs: Record<string, unknown>) => Promise<GeneratedAnalysisJob>\n result: (job: GeneratedAnalysisJob) => Promise<GeneratedAnalysisResult | null>\n resultForJob: (jobId: string) => GeneratedAnalysisResult | null\n /** List the jobs again. The first call opens the job session. */\n refresh: () => Promise<GeneratedAnalysisJob[]>\n get: (jobId: string, options?: PluginJobRequestOptions) => Promise<GeneratedAnalysisJob>\n wait: (\n job: GeneratedAnalysisJob | string,\n options?: PluginJobWaitOptions,\n ) => Promise<GeneratedAnalysisJob>\n jobsFor: (definitionId: string) => GeneratedAnalysisJob[]\n cancel: (job: GeneratedAnalysisJob | string) => Promise<GeneratedAnalysisJob>\n delete: (job: GeneratedAnalysisJob | string) => Promise<boolean>\n clear: (definitionId?: string) => Promise<number>\n pause: () => void\n resume: () => void\n dispose: () => void\n}\n\nconst SESSION_NOT_FOUND_DETAIL = 'Session not found'\nconst MAX_RESULT_CACHE_ENTRIES = 32\nconst MAX_RESULT_RETRIES = 3\nconst DOWNLOAD_URL_REFRESH_SKEW_MS = 30_000\n\ninterface PluginJobWaiter {\n resolve: (job: GeneratedAnalysisJob) => void\n reject: (cause: unknown) => void\n rejectOnFailure: boolean\n signal?: AbortSignal\n abort?: () => void\n}\n\ntype PluginJobStreamData = GeneratedAnalysisJobInput | {\n jobs: GeneratedAnalysisJobInput[]\n}\n\nexport function createGeneratedAnalysisTransport(\n options: GeneratedAnalysisTransportOptions = {},\n): GeneratedAnalysisTransport {\n const fetcher = options.fetcher ?? globalThis.fetch.bind(globalThis)\n const getAccessToken = options.getAccessToken ?? readStoredAccessToken\n\n return async (input, init = {}) => {\n const headers = new Headers(init.headers)\n const accessToken = getAccessToken()\n if (accessToken && !headers.has('Authorization')) {\n headers.set('Authorization', `Bearer ${accessToken}`)\n }\n return await fetcher(input, {\n credentials: 'same-origin',\n ...init,\n headers,\n })\n }\n}\n\n/**\n * Connect a frontend to the SDK-owned job lifecycle of one plugin API prefix.\n * Generated clients use it through `useJobs()`.\n *\n * @example\n * ```ts\n * const jobs = useJobsEndpoint({ baseUrl: '/api/leaf', immediate: true })\n * const job = await jobs.submit('fit-plate', inputs)\n * ```\n */\nexport function useJobsEndpoint(\n options: UseJobsEndpointOptions,\n): UseJobsEndpointReturn {\n const manifestPath = options.manifest ? '/generated/manifest' : undefined\n const baseUrl = options.baseUrl.replace(/\\/$/, '')\n const transport = options.transport ?? createGeneratedAnalysisTransport()\n const storage = createGeneratedSessionStorage(options.storage)\n const createId = options.createId ?? (() => globalThis.crypto.randomUUID())\n const pollInterval = options.poll ?? 750\n const streamEnabled = options.stream ?? true\n const streamReconnectDelay = options.streamReconnectDelay ?? 1_500\n const tabKey = `mint:generated:${baseUrl}:tab`\n const sessionKey = `mint:generated:${baseUrl}:session`\n const manifest = shallowRef<GeneratedAnalysisManifest | null>(null)\n const session = shallowRef<GeneratedSession | null>(null)\n const jobs = shallowRef<GeneratedAnalysisJob[]>([])\n const jobRevisions = new Map<string, number>()\n const pendingJobOperations = new Map<string, number>()\n const resultCache = shallowRef<Record<string, GeneratedAnalysisResult>>({})\n const isLoading = ref(false)\n const isSubmitting = ref(false)\n const error = shallowRef<string | null>(null)\n const status = shallowRef<'idle' | 'initializing' | 'ready' | 'paused' | 'error' | 'disposed'>('idle')\n let pollTimer: ReturnType<typeof setTimeout> | undefined\n let disposed = false\n let paused = false\n let initialized = false\n let initializePromise: Promise<void> | undefined\n let refreshPromise: Promise<GeneratedAnalysisJob[]> | undefined\n let initializeRefreshPromise: Promise<GeneratedAnalysisJob[]> | undefined\n let jobsRevision = 0\n let resultCacheOrder: string[] = []\n const resultRequests = new Map<string, Promise<GeneratedAnalysisResult | null>>()\n const jobWaiters = new Map<string, Set<PluginJobWaiter>>()\n const resultRetryAttempts = new Map<string, number>()\n const resultRetryTimers = new Map<string, ReturnType<typeof setTimeout>>()\n const downloadRefreshTimers = new Map<string, ReturnType<typeof setTimeout>>()\n const jobStream = createEventStream<PluginJobStreamData>(`${baseUrl}/jobs/events`, {\n transport,\n immediate: false,\n reconnectDelayMs: streamReconnectDelay,\n parseJson: true,\n errorLabel: 'Job stream',\n onOpen: clearPoll,\n onMessage: applyJobStreamMessage,\n onError: schedulePollForActiveJobs,\n onClose: schedulePollForActiveJobs,\n })\n\n const latestResult = computed<GeneratedAnalysisResult | null>(() => {\n const job = currentCompletedJob()\n return job ? resultForJob(job.job_id) : null\n })\n\n function initialize(): Promise<void> {\n if (disposed) return Promise.reject(new Error('Plugin jobs runtime is disposed'))\n if (initialized) return Promise.resolve()\n if (initializePromise) return initializePromise\n isLoading.value = true\n error.value = null\n status.value = 'initializing'\n const pending = (async () => {\n if (manifestPath) {\n const loadedManifest = await request<GeneratedAnalysisManifest>(manifestPath, { method: 'GET' })\n if (!disposed) manifest.value = loadedManifest\n }\n const resolvedSession = await resolveSession()\n if (disposed) return\n session.value = resolvedSession\n await refreshJobs()\n if (disposed) return\n initialized = true\n status.value = paused ? 'paused' : 'ready'\n if (streamEnabled && !paused) jobStream.start()\n })().catch((cause: unknown) => {\n if (!disposed) {\n error.value = errorMessage(cause, 'Failed to initialize generated analysis')\n status.value = 'error'\n }\n throw cause\n }).finally(() => {\n if (initializePromise === pending) initializePromise = undefined\n if (!disposed) isLoading.value = false\n })\n initializePromise = pending\n return pending\n }\n\n // Runtimes of one plugin on one page share a session: a concurrent first\n // resolve joins the request in flight instead of creating a second session.\n function resolveSession(): Promise<GeneratedSession> {\n const pending = pendingSessions.get(sessionKey)\n if (pending) return pending\n const resolving = resolveStoredSession().finally(() => pendingSessions.delete(sessionKey))\n pendingSessions.set(sessionKey, resolving)\n return resolving\n }\n\n async function resolveStoredSession(): Promise<GeneratedSession> {\n let tabId = storage.getItem(tabKey)\n if (!tabId) {\n tabId = createId()\n storage.setItem(tabKey, tabId)\n }\n const storedSessionId = storage.getItem(sessionKey)\n const payload = await request<{ sessions: GeneratedSession[] }>('/jobs/sessions', { method: 'GET' })\n const existing = payload.sessions.find(candidate => (\n candidate.id === storedSessionId && candidate.tab_id === tabId\n ))\n if (existing) return existing\n const created = await request<GeneratedSession>('/jobs/sessions', {\n method: 'POST',\n body: JSON.stringify({ tab_id: tabId }),\n })\n storage.setItem(sessionKey, created.id)\n return created\n }\n\n async function submit(\n definitionId: string,\n inputs: Record<string, unknown>,\n ): Promise<GeneratedAnalysisJob> {\n await initialize()\n if (!session.value) throw new Error('Plugin jobs runtime is disposed')\n const clientRequestId = createId()\n isSubmitting.value = true\n error.value = null\n try {\n let jobInput: GeneratedAnalysisJobInput\n try {\n jobInput = await submitToSession(\n session.value.id,\n clientRequestId,\n await stagePathInputs(inputs, session.value.id, definitionId),\n )\n } catch (cause) {\n if (!isSessionNotFoundError(cause)) throw cause\n session.value = await resolveSession()\n jobInput = await submitToSession(\n session.value.id,\n clientRequestId,\n await stagePathInputs(inputs, session.value.id, definitionId),\n )\n }\n const job = upsertJob(jobInput, definitionId)\n if (!jobStream.isConnected.value) schedulePoll()\n return job\n } catch (cause) {\n error.value = errorMessage(cause, 'Failed to submit analysis')\n throw cause\n } finally {\n isSubmitting.value = false\n }\n\n function submitToSession(\n sessionId: string,\n requestId: string,\n stagedInputs: Record<string, unknown>,\n ): Promise<GeneratedAnalysisJobInput> {\n return request<GeneratedAnalysisJobInput>('/jobs', {\n method: 'POST',\n body: JSON.stringify({\n definition_id: definitionId,\n inputs: stagedInputs,\n session_id: sessionId,\n client_request_id: requestId,\n }),\n })\n }\n }\n\n async function uploadPath(\n input: PluginJobPathInput,\n definitionId?: string,\n ): Promise<PluginJobPathUpload> {\n await initialize()\n if (!session.value) throw new Error('Plugin jobs runtime is disposed')\n return uploadPathForSession(input, session.value.id, definitionId)\n }\n\n async function uploadStagedPart<Metadata>(\n input: PluginJobStagedPartInput<Metadata>,\n ): Promise<PluginJobStagedPartUploadResult<Metadata>> {\n const jobId = jobIdFromReference(input.job)\n const body = new FormData()\n body.append('file', input.file)\n body.append('metadata', JSON.stringify(input.metadata))\n if (input.expectedSize !== undefined) {\n body.append('expected_size', String(input.expectedSize))\n }\n if (input.expectedSha256 !== undefined) {\n body.append('expected_sha256', input.expectedSha256)\n }\n return request<PluginJobStagedPartUploadResult<Metadata>>(\n `/jobs/${encodeURIComponent(jobId)}/parts/${encodeStagedPartKey(input.key)}`,\n { method: 'PUT', body, signal: input.signal },\n )\n }\n\n function getJobStaging<Metadata = unknown>(\n job: GeneratedAnalysisJob | string,\n pagination: PluginJobStagingPagination = {},\n ): Promise<PluginJobStaging<Metadata>> {\n const jobId = jobIdFromReference(job)\n const query = new URLSearchParams()\n if (pagination.offset !== undefined) query.set('offset', String(pagination.offset))\n if (pagination.limit !== undefined) query.set('limit', String(pagination.limit))\n const suffix = query.size > 0 ? `?${query}` : ''\n return request<PluginJobStaging<Metadata>>(\n `/jobs/${encodeURIComponent(jobId)}/staging${suffix}`,\n { method: 'GET' },\n )\n }\n\n async function sealJob(\n job: GeneratedAnalysisJob | string,\n ): Promise<GeneratedAnalysisJob> {\n const jobId = jobIdFromReference(job)\n const revision = beginJobOperation(jobId)\n try {\n const updated = await request<GeneratedAnalysisJobInput>(\n `/jobs/${encodeURIComponent(jobId)}/seal`,\n { method: 'POST' },\n )\n return mergeJobResponse(updated, revision)\n } finally {\n finishJobOperation(jobId)\n }\n }\n\n async function uploadPathForSession(\n input: PluginJobPathInput,\n sessionId: string,\n definitionId?: string,\n ): Promise<PluginJobPathUpload> {\n if (!input.files.length || input.files.length !== input.relativePaths.length) {\n throw new Error('Path upload requires one relative path for each file')\n }\n const body = new FormData()\n body.append('session_id', sessionId)\n if (input.rootName) body.append('root_name', input.rootName)\n for (let index = 0; index < input.files.length; index += 1) {\n body.append('files', input.files[index]!)\n body.append('relative_paths', input.relativePaths[index]!)\n }\n const headers: Record<string, string> = {\n 'X-Mint-Job-Session': sessionId,\n }\n if (definitionId) headers['X-Mint-Job-Definition'] = definitionId\n return request<PluginJobPathUpload>('/jobs/uploads', {\n method: 'POST',\n body,\n signal: input.signal,\n headers,\n })\n }\n\n async function stageArtifactPathForSession(\n input: PluginJobArtifactPathInput,\n sessionId: string,\n definitionId: string,\n ): Promise<PluginJobPathUpload> {\n return request<PluginJobPathUpload>('/jobs/artifacts', {\n method: 'POST',\n body: JSON.stringify({\n experiment_id: input.experimentId,\n artifact_id: input.artifactId,\n session_id: sessionId,\n }),\n signal: input.signal,\n headers: {\n 'X-Mint-Job-Session': sessionId,\n 'X-Mint-Job-Definition': definitionId,\n },\n })\n }\n\n async function stagePathInputs(\n inputs: Record<string, unknown>,\n sessionId: string,\n definitionId: string,\n ): Promise<Record<string, unknown>> {\n return await stagePathValue(inputs, sessionId, definitionId) as Record<string, unknown>\n }\n\n async function stagePathValue(\n value: unknown,\n sessionId: string,\n definitionId: string,\n ): Promise<unknown> {\n if (typeof File !== 'undefined' && value instanceof File) {\n const upload = await uploadPathForSession({\n kind: 'path',\n files: [value],\n relativePaths: [value.name],\n }, sessionId, definitionId)\n return { token: upload.token }\n }\n if (isPluginJobPathInput(value)) {\n const upload = await uploadPathForSession(value, sessionId, definitionId)\n return { token: upload.token }\n }\n if (isPluginJobArtifactPathInput(value)) {\n const upload = await stageArtifactPathForSession(value, sessionId, definitionId)\n return { token: upload.token }\n }\n if (Array.isArray(value)) {\n return await Promise.all(value.map(\n item => stagePathValue(item, sessionId, definitionId),\n ))\n }\n if (value && typeof value === 'object') {\n const entries = await Promise.all(Object.entries(value).map(async ([key, child]) => (\n [key, await stagePathValue(child, sessionId, definitionId)] as const\n )))\n return Object.fromEntries(entries)\n }\n return value\n }\n\n function refreshJobs(): Promise<GeneratedAnalysisJob[]> {\n if (disposed) return Promise.resolve(jobs.value)\n if (refreshPromise) return refreshPromise\n const revision = jobsRevision\n const pending = request<{ jobs: GeneratedAnalysisJobInput[] }>('/jobs', { method: 'GET' })\n .then((payload) => {\n if (disposed) return jobs.value\n if (revision !== jobsRevision) {\n if (jobs.value.some(job => isActiveJobStatus(job.status))) schedulePoll()\n return jobs.value\n }\n replaceJobs(payload.jobs)\n error.value = null\n if (!paused && !jobStream.isConnected.value && jobs.value.some(job => isActiveJobStatus(job.status))) {\n schedulePoll()\n }\n return jobs.value\n }).finally(() => {\n if (refreshPromise === pending) refreshPromise = undefined\n })\n refreshPromise = pending\n return pending\n }\n\n function refresh(): Promise<GeneratedAnalysisJob[]> {\n if (disposed) return Promise.resolve(jobs.value)\n if (initialized) return refreshJobs()\n if (initializeRefreshPromise) return initializeRefreshPromise\n const pending = initialize()\n .then(() => jobs.value)\n .finally(() => {\n if (initializeRefreshPromise === pending) initializeRefreshPromise = undefined\n })\n initializeRefreshPromise = pending\n return pending\n }\n\n async function getJob(\n jobId: string,\n options: PluginJobRequestOptions = {},\n ): Promise<GeneratedAnalysisJob> {\n const revision = beginJobOperation(jobId)\n try {\n const job = await request<GeneratedAnalysisJobInput>(\n `/jobs/${encodeURIComponent(jobId)}`,\n { method: 'GET', signal: options.signal },\n )\n return mergeJobResponse(job, revision)\n } finally {\n finishJobOperation(jobId)\n }\n }\n\n function waitForJob(\n job: GeneratedAnalysisJob | string,\n options: PluginJobWaitOptions = {},\n ): Promise<GeneratedAnalysisJob> {\n if (disposed) return Promise.reject(new Error('Plugin jobs runtime is disposed'))\n if (options.signal?.aborted) {\n return Promise.reject(pluginJobWaitAbort(options.signal))\n }\n const jobId = jobIdFromReference(job)\n const current = typeof job === 'string'\n ? jobs.value.find(candidate => candidate.job_id === jobId)\n : jobs.value.find(candidate => candidate.job_id === jobId) ?? job\n if (current && !isActiveJobStatus(current.status)) {\n return terminalWaitResult(current, options.rejectOnFailure === true)\n }\n if (paused) return Promise.reject(new Error('Plugin jobs runtime is paused'))\n return new Promise<GeneratedAnalysisJob>((resolve, reject) => {\n const waiter: PluginJobWaiter = {\n resolve,\n reject,\n rejectOnFailure: options.rejectOnFailure === true,\n signal: options.signal,\n }\n if (options.signal) {\n waiter.abort = () => {\n removeJobWaiter(jobId, waiter)\n reject(pluginJobWaitAbort(options.signal!))\n }\n options.signal.addEventListener('abort', waiter.abort, { once: true })\n }\n const waiting = jobWaiters.get(jobId) ?? new Set<PluginJobWaiter>()\n waiting.add(waiter)\n jobWaiters.set(jobId, waiting)\n if (!paused) {\n if (streamEnabled) jobStream.start()\n if (!jobStream.isConnected.value) schedulePoll()\n }\n void getJob(jobId, { signal: options.signal }).catch((cause: unknown) => {\n if (current && isRetryableJobLookupError(cause)) {\n if (!jobStream.isConnected.value) schedulePoll()\n return\n }\n removeJobWaiter(jobId, waiter)\n reject(cause)\n })\n })\n }\n\n function settleJobWaiters(job: GeneratedAnalysisJob): void {\n if (isActiveJobStatus(job.status)) return\n const waiting = jobWaiters.get(job.job_id)\n if (!waiting) return\n jobWaiters.delete(job.job_id)\n for (const waiter of waiting) {\n if (waiter.abort && waiter.signal) {\n waiter.signal.removeEventListener('abort', waiter.abort)\n }\n void terminalWaitResult(job, waiter.rejectOnFailure).then(\n waiter.resolve,\n waiter.reject,\n )\n }\n }\n\n function rejectJobWaiters(jobId: string, cause: unknown): void {\n const waiting = jobWaiters.get(jobId)\n if (!waiting) return\n jobWaiters.delete(jobId)\n for (const waiter of waiting) {\n if (waiter.abort && waiter.signal) {\n waiter.signal.removeEventListener('abort', waiter.abort)\n }\n waiter.reject(cause)\n }\n }\n\n function removeJobWaiter(jobId: string, waiter: PluginJobWaiter): void {\n const waiting = jobWaiters.get(jobId)\n if (!waiting) return\n waiting.delete(waiter)\n if (waiting.size === 0) jobWaiters.delete(jobId)\n if (waiter.abort && waiter.signal) {\n waiter.signal.removeEventListener('abort', waiter.abort)\n }\n }\n\n function jobsFor(definitionId: string): GeneratedAnalysisJob[] {\n return jobs.value.filter(job => job.definition_id === definitionId)\n }\n\n function canonicalJob(\n input: GeneratedAnalysisJobInput,\n fallbackDefinitionId?: string,\n ): GeneratedAnalysisJob {\n const normalized = normalizeJobState(input)\n const definitionId = normalized.definition_id ?? fallbackDefinitionId ?? null\n return {\n ...normalized,\n definition_id: definitionId,\n metadata: definitionId\n ? { ...normalized.metadata, definition_id: definitionId }\n : normalized.metadata,\n }\n }\n\n function recordJobsMutation(jobIds: Iterable<string>): void {\n jobsRevision += 1\n for (const jobId of jobIds) {\n jobRevisions.set(jobId, (jobRevisions.get(jobId) ?? 0) + 1)\n }\n }\n\n function beginJobOperation(jobId: string): number {\n pendingJobOperations.set(jobId, (pendingJobOperations.get(jobId) ?? 0) + 1)\n return jobRevisions.get(jobId) ?? 0\n }\n\n function finishJobOperation(jobId: string): void {\n const remaining = (pendingJobOperations.get(jobId) ?? 1) - 1\n if (remaining > 0) pendingJobOperations.set(jobId, remaining)\n else pendingJobOperations.delete(jobId)\n pruneJobRevisions()\n }\n\n function pruneJobRevisions(): void {\n const retained = new Set([\n ...jobs.value.map(job => job.job_id),\n ...pendingJobOperations.keys(),\n ])\n for (const jobId of jobRevisions.keys()) {\n if (!retained.has(jobId)) jobRevisions.delete(jobId)\n }\n }\n\n function replaceJobs(inputs: readonly GeneratedAnalysisJobInput[]): GeneratedAnalysisJob[] {\n const previousJobs = new Map(jobs.value.map(job => [job.job_id, job]))\n const previousJobIds = new Set(jobs.value.map(job => job.job_id))\n const nextJobs = inputs.map((input) => {\n const incoming = canonicalJob(input)\n const current = previousJobs.get(incoming.job_id)\n return current && shouldPreserveJobLifecycle(current, incoming) ? current : incoming\n })\n const nextJobIds = new Set(nextJobs.map(job => job.job_id))\n recordJobsMutation(new Set([\n ...previousJobIds,\n ...nextJobIds,\n ]))\n jobs.value = nextJobs\n for (const job of nextJobs) settleJobWaiters(job)\n for (const jobId of previousJobIds) {\n if (!nextJobIds.has(jobId)) {\n rejectJobWaiters(jobId, new Error(`Job ${jobId} is no longer available`))\n }\n }\n pruneResultState()\n pruneJobRevisions()\n return nextJobs\n }\n\n function upsertJob(\n input: GeneratedAnalysisJobInput,\n fallbackDefinitionId?: string,\n ): GeneratedAnalysisJob {\n const incoming = canonicalJob(input, fallbackDefinitionId)\n const current = jobs.value.find(candidate => candidate.job_id === incoming.job_id)\n const job = current && shouldPreserveJobLifecycle(current, incoming)\n ? current\n : incoming\n if (job === current) {\n settleJobWaiters(job)\n pruneResultState()\n pruneJobRevisions()\n return job\n }\n jobs.value = [job, ...jobs.value.filter(candidate => candidate.job_id !== job.job_id)]\n recordJobsMutation([job.job_id])\n settleJobWaiters(job)\n pruneResultState()\n pruneJobRevisions()\n return job\n }\n\n function mergeJobResponse(\n input: GeneratedAnalysisJobInput,\n expectedRevision: number,\n ): GeneratedAnalysisJob {\n const incoming = canonicalJob(input)\n const current = jobs.value.find(candidate => candidate.job_id === incoming.job_id)\n if (disposed) return current ?? incoming\n if (current && shouldPreserveJobLifecycle(current, incoming)) return current\n if (expectedRevision === (jobRevisions.get(incoming.job_id) ?? 0)) {\n return upsertJob(incoming)\n }\n if (current && jobLifecycleRank(incoming.status) > jobLifecycleRank(current.status)) {\n return upsertJob(incoming)\n }\n return current ?? incoming\n }\n\n function currentCompletedJob(): GeneratedAnalysisJob | undefined {\n const activeSessionId = session.value?.id\n return jobs.value.find(candidate => (\n candidate.session_id === activeSessionId\n && candidate.status === 'completed'\n && candidate.result_status === 'available'\n ))\n }\n\n function resultForJob(jobId: string): GeneratedAnalysisResult | null {\n return resultCache.value[jobId] ?? null\n }\n\n function loadResult(job: GeneratedAnalysisJob): Promise<GeneratedAnalysisResult | null> {\n return loadResultInternal(job)\n }\n\n function loadResultInternal(\n job: GeneratedAnalysisJob,\n forceRefresh = false,\n ): Promise<GeneratedAnalysisResult | null> {\n if (\n disposed\n || !isLoadableResultJob(job)\n ) return Promise.resolve(null)\n\n const cached = resultForJob(job.job_id)\n if (cached && !forceRefresh && !managedDownloadNeedsRefresh(cached)) {\n return Promise.resolve(cached)\n }\n const pending = resultRequests.get(job.job_id)\n if (pending) return pending\n\n const requestResult = request<GeneratedAnalysisResult>(\n `/jobs/${encodeURIComponent(job.job_id)}/result`,\n { method: 'GET' },\n ).then((result) => {\n if (!disposed && isCurrentResultJob(job.job_id)) {\n cacheResult(job.job_id, result)\n clearResultRetry(job.job_id)\n error.value = null\n }\n return result\n }).catch((cause: unknown) => {\n if (!disposed && isCurrentResultJob(job.job_id)) {\n error.value = errorMessage(cause, 'Failed to load analysis result')\n if (cached && isManagedDownloadResult(cached)) {\n handleManagedDownloadRefreshFailure(job, cause)\n } else {\n scheduleResultRetry(job, cause)\n }\n }\n return null\n }).finally(() => {\n resultRequests.delete(job.job_id)\n })\n resultRequests.set(job.job_id, requestResult)\n return requestResult\n }\n\n function cacheResult(jobId: string, result: GeneratedAnalysisResult): void {\n resultCacheOrder = [jobId, ...resultCacheOrder.filter(candidate => candidate !== jobId)]\n .slice(0, MAX_RESULT_CACHE_ENTRIES)\n const retained = new Set(resultCacheOrder)\n resultCache.value = Object.fromEntries([\n ...Object.entries(resultCache.value)\n .filter(([candidate]) => retained.has(candidate) && candidate !== jobId),\n [jobId, result],\n ])\n for (const candidate of downloadRefreshTimers.keys()) {\n if (!retained.has(candidate)) clearManagedDownloadRefresh(candidate)\n }\n scheduleManagedDownloadRefresh(jobId, managedDownloadRefreshDelay(result))\n }\n\n function pruneResultState(): void {\n const loadableJobIds = new Set(jobs.value\n .filter(job => isLoadableResultJob(job))\n .map(job => job.job_id))\n resultCacheOrder = resultCacheOrder\n .filter(jobId => loadableJobIds.has(jobId))\n .slice(0, MAX_RESULT_CACHE_ENTRIES)\n const retained = new Set(resultCacheOrder)\n resultCache.value = Object.fromEntries(\n Object.entries(resultCache.value).filter(([jobId]) => retained.has(jobId)),\n )\n for (const jobId of resultRetryTimers.keys()) {\n if (!loadableJobIds.has(jobId)) clearResultRetry(jobId)\n }\n for (const jobId of resultRetryAttempts.keys()) {\n if (!loadableJobIds.has(jobId)) clearResultRetry(jobId)\n }\n for (const jobId of downloadRefreshTimers.keys()) {\n if (!loadableJobIds.has(jobId) || !retained.has(jobId)) {\n clearManagedDownloadRefresh(jobId)\n }\n }\n }\n\n function scheduleManagedDownloadRefresh(jobId: string, delay: number | null): void {\n clearManagedDownloadRefresh(jobId)\n if (delay === null || disposed || paused) return\n downloadRefreshTimers.set(jobId, setTimeout(() => {\n downloadRefreshTimers.delete(jobId)\n if (disposed || paused) return\n const current = jobs.value.find(candidate => candidate.job_id === jobId)\n if (current && isLoadableResultJob(current)) {\n void loadResultInternal(current, true)\n }\n }, Math.max(0, delay)))\n }\n\n function clearManagedDownloadRefresh(jobId: string): void {\n const timer = downloadRefreshTimers.get(jobId)\n if (timer !== undefined) clearTimeout(timer)\n downloadRefreshTimers.delete(jobId)\n }\n\n function handleManagedDownloadRefreshFailure(\n job: GeneratedAnalysisJob,\n cause: unknown,\n ): void {\n if (cause instanceof MintApiError) {\n if (cause.status === 404 || cause.status === 410) {\n discardCachedResult(job.job_id)\n jobs.value = jobs.value.map(candidate => (\n candidate.job_id === job.job_id\n ? { ...candidate, result_status: 'expired' }\n : candidate\n ))\n recordJobsMutation([job.job_id])\n return\n }\n if (cause.status === 401 || cause.status === 403) {\n discardCachedResult(job.job_id)\n return\n }\n }\n scheduleResultRetry(job, cause, true)\n }\n\n function discardCachedResult(jobId: string): void {\n clearResultRetry(jobId)\n clearManagedDownloadRefresh(jobId)\n resultCacheOrder = resultCacheOrder.filter(candidate => candidate !== jobId)\n const retainedResults = { ...resultCache.value }\n delete retainedResults[jobId]\n resultCache.value = retainedResults\n }\n\n function isLoadableResultJob(job: GeneratedAnalysisJob): boolean {\n return job.status === 'completed'\n && job.result_status === 'available'\n }\n\n function isCurrentResultJob(jobId: string): boolean {\n const job = jobs.value.find(candidate => candidate.job_id === jobId)\n return job !== undefined && isLoadableResultJob(job)\n }\n\n function scheduleResultRetry(\n job: GeneratedAnalysisJob,\n cause: unknown,\n forceRefresh = false,\n ): void {\n if (\n paused\n || !isRetryableResultError(cause)\n || resultRetryTimers.has(job.job_id)\n ) return\n const attempt = resultRetryAttempts.get(job.job_id) ?? 0\n if (attempt >= MAX_RESULT_RETRIES) return\n resultRetryAttempts.set(job.job_id, attempt + 1)\n resultRetryTimers.set(job.job_id, setTimeout(() => {\n resultRetryTimers.delete(job.job_id)\n const current = jobs.value.find(candidate => candidate.job_id === job.job_id)\n if (!paused && current && isLoadableResultJob(current)) {\n void loadResultInternal(current, forceRefresh)\n }\n }, pollInterval * (2 ** attempt)))\n }\n\n function clearResultRetry(jobId: string): void {\n const timer = resultRetryTimers.get(jobId)\n if (timer !== undefined) clearTimeout(timer)\n resultRetryTimers.delete(jobId)\n resultRetryAttempts.delete(jobId)\n }\n\n function schedulePoll(): void {\n if (disposed || paused || jobStream.isConnected.value || pollTimer !== undefined) return\n pollTimer = setTimeout(async () => {\n pollTimer = undefined\n if (disposed) return\n try {\n await refreshJobs()\n } catch (cause) {\n error.value = errorMessage(cause, 'Failed to refresh analysis jobs')\n if (jobs.value.some(job => isActiveJobStatus(job.status))) schedulePoll()\n }\n }, pollInterval)\n }\n\n function clearPoll(): void {\n if (pollTimer === undefined) return\n clearTimeout(pollTimer)\n pollTimer = undefined\n }\n\n function schedulePollForActiveJobs(): void {\n if (jobs.value.some(job => isActiveJobStatus(job.status))) schedulePoll()\n }\n\n function applyJobStreamMessage(message: EventStreamMessage<PluginJobStreamData>): void {\n if (message.event === 'snapshot' && 'jobs' in message.data) {\n const payload = message.data\n replaceJobs(payload.jobs)\n } else if (message.event === 'job' || message.event === 'terminal') {\n upsertJob(message.data as GeneratedAnalysisJobInput)\n }\n error.value = null\n }\n\n async function cancelJob(job: GeneratedAnalysisJob | string): Promise<GeneratedAnalysisJob> {\n const jobId = typeof job === 'string' ? job : job.job_id\n const revision = beginJobOperation(jobId)\n try {\n const updated = await request<GeneratedAnalysisJobInput>(\n `/jobs/${encodeURIComponent(jobId)}/cancel`,\n { method: 'POST' },\n )\n return mergeJobResponse(updated, revision)\n } finally {\n finishJobOperation(jobId)\n }\n }\n\n async function deleteJob(job: GeneratedAnalysisJob | string): Promise<boolean> {\n const jobId = typeof job === 'string' ? job : job.job_id\n await request<{ status: 'deleted' }>(\n `/jobs/${encodeURIComponent(jobId)}`,\n { method: 'DELETE' },\n )\n jobs.value = jobs.value.filter(candidate => candidate.job_id !== jobId)\n recordJobsMutation([jobId])\n rejectJobWaiters(jobId, new Error(`Job ${jobId} was deleted`))\n discardCachedResult(jobId)\n pruneJobRevisions()\n return true\n }\n\n async function clearFinishedJobs(definitionId?: string): Promise<number> {\n const query = definitionId === undefined\n ? ''\n : `?definition_id=${encodeURIComponent(definitionId)}`\n const payload = await request<{ deleted: number }>(`/jobs${query}`, { method: 'DELETE' })\n if (payload.deleted > 0) await refreshJobs()\n return payload.deleted\n }\n\n function pause(): void {\n if (disposed || paused) return\n paused = true\n status.value = 'paused'\n clearPoll()\n for (const jobId of resultRetryTimers.keys()) clearResultRetry(jobId)\n for (const jobId of downloadRefreshTimers.keys()) {\n clearManagedDownloadRefresh(jobId)\n }\n jobStream.stop()\n }\n\n function resume(): void {\n if (disposed || !paused) return\n paused = false\n status.value = initialized ? 'ready' : 'idle'\n if (!initialized) return\n for (const [jobId, result] of Object.entries(resultCache.value)) {\n scheduleManagedDownloadRefresh(jobId, managedDownloadRefreshDelay(result))\n }\n if (streamEnabled) jobStream.start()\n else if (jobs.value.some(job => isActiveJobStatus(job.status))) schedulePoll()\n }\n\n function dispose(): void {\n if (disposed) return\n disposed = true\n paused = true\n status.value = 'disposed'\n clearPoll()\n for (const timer of resultRetryTimers.values()) clearTimeout(timer)\n resultRetryTimers.clear()\n resultRetryAttempts.clear()\n for (const timer of downloadRefreshTimers.values()) clearTimeout(timer)\n downloadRefreshTimers.clear()\n const cause = new Error('Plugin jobs runtime is disposed')\n for (const jobId of jobWaiters.keys()) rejectJobWaiters(jobId, cause)\n jobRevisions.clear()\n pendingJobOperations.clear()\n jobStream.stop()\n }\n\n async function request<T>(path: string, init: RequestInit): Promise<T> {\n const response = await transport(`${baseUrl}${path}`, {\n ...init,\n headers: {\n Accept: 'application/json',\n ...(init.body && !(init.body instanceof FormData)\n ? { 'Content-Type': 'application/json' }\n : {}),\n ...init.headers,\n },\n })\n if (!response.ok) {\n const payload: unknown = await response.json().catch(() => null)\n throw mintApiErrorFromResponse(response.status, payload, {\n requestId: response.headers.get('X-Request-ID'),\n })\n }\n return await response.json() as T\n }\n\n onScopeDispose(dispose)\n\n if (options.immediate) void initialize().catch(() => undefined)\n\n return {\n manifest,\n session,\n jobs,\n latestResult,\n isLoading,\n isSubmitting,\n error,\n status,\n uploadPath,\n uploadStagedPart,\n getJobStaging,\n sealJob,\n submit,\n result: loadResult,\n resultForJob,\n refresh,\n get: getJob,\n wait: waitForJob,\n jobsFor,\n cancel: cancelJob,\n delete: deleteJob,\n clear: clearFinishedJobs,\n pause,\n resume,\n dispose,\n }\n}\n\nconst pendingSessions = new Map<string, Promise<GeneratedSession>>()\n\nfunction jobIdFromReference(job: GeneratedAnalysisJob | string): string {\n return typeof job === 'string' ? job : job.job_id\n}\n\nfunction encodeStagedPartKey(key: string): string {\n return key.split('/').map(segment => encodeURIComponent(segment)).join('/')\n}\n\nfunction jobLifecycleRank(status: GeneratedAnalysisJob['status']): number {\n if (status === 'awaiting_input') return 0\n if (status === 'queued') return 1\n if (status === 'running') return 2\n return 3\n}\n\nfunction shouldPreserveJobLifecycle(\n current: GeneratedAnalysisJob,\n incoming: GeneratedAnalysisJob,\n): boolean {\n const currentRank = jobLifecycleRank(current.status)\n const incomingRank = jobLifecycleRank(incoming.status)\n return incomingRank < currentRank\n || (currentRank === 3 && incomingRank === 3 && current.status !== incoming.status)\n}\n\nfunction terminalWaitResult(\n job: GeneratedAnalysisJob,\n rejectOnFailure: boolean,\n): Promise<GeneratedAnalysisJob> {\n if (rejectOnFailure && (job.status === 'failed' || job.status === 'cancelled')) {\n return Promise.reject(new Error(job.error ?? `Job ${job.job_id} ${job.status}`))\n }\n return Promise.resolve(job)\n}\n\nfunction pluginJobWaitAbort(signal: AbortSignal): unknown {\n if (signal.reason !== undefined) return signal.reason\n const cause = new Error('Waiting for job was aborted')\n cause.name = 'AbortError'\n return cause\n}\n\nfunction isPluginJobArtifactPathInput(value: unknown): value is PluginJobArtifactPathInput {\n return Boolean(\n value\n && typeof value === 'object'\n && 'kind' in value\n && value.kind === 'artifact-path'\n && 'experimentId' in value\n && Number.isInteger(value.experimentId)\n && 'artifactId' in value\n && Number.isInteger(value.artifactId),\n )\n}\n\nfunction isPluginJobPathInput(value: unknown): value is PluginJobPathInput {\n return Boolean(\n value\n && typeof value === 'object'\n && 'kind' in value\n && value.kind === 'path'\n && 'files' in value\n && Array.isArray(value.files)\n && 'relativePaths' in value\n && Array.isArray(value.relativePaths),\n )\n}\n\nfunction readStoredAccessToken(): string | null {\n try {\n return globalThis.localStorage?.getItem(AUTH_TOKEN_STORAGE_KEY) ?? null\n } catch {\n return null\n }\n}\n\nfunction createGeneratedSessionStorage(candidate?: Storage): GeneratedSessionStorage {\n const fallback = new Map<string, string>()\n let delegate: Storage | undefined\n let available = true\n try {\n delegate = candidate ?? globalThis.sessionStorage\n } catch {\n available = false\n }\n return {\n getItem(key) {\n if (available && delegate) {\n try {\n return delegate.getItem(key) ?? fallback.get(key) ?? null\n } catch {\n available = false\n }\n }\n return fallback.get(key) ?? null\n },\n setItem(key, value) {\n fallback.set(key, value)\n if (available && delegate) {\n try {\n delegate.setItem(key, value)\n } catch {\n available = false\n }\n }\n },\n }\n}\n\nfunction isSessionNotFoundError(error: unknown): boolean {\n return error instanceof MintApiError\n && error.status === 404\n && (error.detail === SESSION_NOT_FOUND_DETAIL || error.message === SESSION_NOT_FOUND_DETAIL)\n}\n\nfunction isRetryableResultError(error: unknown): boolean {\n return !(error instanceof MintApiError)\n || error.status === 409\n || error.status === 429\n || error.status >= 500\n}\n\nfunction isRetryableJobLookupError(error: unknown): boolean {\n return !(error instanceof MintApiError)\n || error.status === 408\n || error.status === 429\n || error.status >= 500\n}\n\nfunction isManagedDownloadResult(\n result: GeneratedAnalysisResult,\n): result is Extract<GeneratedAnalysisResult, { kind: 'artifact' }> {\n return result.kind === 'artifact' && result.managed === true\n}\n\nfunction managedDownloadNeedsRefresh(result: GeneratedAnalysisResult): boolean {\n if (!isManagedDownloadResult(result)) return false\n if (!result.download_url || !result.download_expires_at) return true\n const expiresAt = Date.parse(result.download_expires_at)\n return !Number.isFinite(expiresAt)\n || expiresAt <= Date.now() + DOWNLOAD_URL_REFRESH_SKEW_MS\n}\n\nfunction managedDownloadRefreshDelay(result: GeneratedAnalysisResult): number | null {\n if (!isManagedDownloadResult(result)) return null\n if (!result.download_url || !result.download_expires_at) return null\n const expiresAt = Date.parse(result.download_expires_at)\n if (!Number.isFinite(expiresAt)) return null\n const remaining = expiresAt - Date.now()\n return remaining <= DOWNLOAD_URL_REFRESH_SKEW_MS\n ? remaining\n : remaining - DOWNLOAD_URL_REFRESH_SKEW_MS\n}\n\nfunction errorMessage(error: unknown, fallback: string): string {\n return error instanceof Error ? error.message : fallback\n}\n","import type { InjectionKey } from 'vue'\n\nimport type { GeneratedAnalysisTransport } from '../../composables/useJobsEndpoint'\n\n/** The plugin jobs API a generated workspace talks to, for fields that need it. */\nexport interface GeneratedJobsApi {\n baseUrl: string\n /** Defaults to the authenticated SDK transport. */\n transport?: GeneratedAnalysisTransport\n}\n\nexport const GENERATED_JOBS_API_KEY: InjectionKey<GeneratedJobsApi> = Symbol('mint-generated-jobs-api')\n","<script setup lang=\"ts\">\nimport { computed, inject, ref, watch } from 'vue'\n\nimport { mintApiErrorFromResponse } from '../apiErrors'\nimport { formatExperimentDate } from '../composables/experiment-utils'\nimport { createGeneratedAnalysisTransport } from '../composables/useJobsEndpoint'\nimport { getInjectedPlatformContext } from '../composables/platformContextHelpers'\nimport { useExperimentStore } from '../stores/experiment'\nimport type { AnalysisArtifactSummary } from '../types/analysisArtifactTypes'\nimport type {\n PluginJobArtifactPathInput,\n PluginJobPathInput,\n PluginJobPathSelection,\n} from '../types/generated-ui'\nimport BaseButton from './BaseButton.vue'\nimport BaseModal from './BaseModal.vue'\nimport FileUploader from './FileUploader.vue'\nimport { GENERATED_JOBS_API_KEY } from './internal/generatedJobsApi'\nimport ResourcePickerListInternal from './internal/ResourcePickerListInternal.vue'\n\n/** A file artifact the plugin's jobs API says this Path input may use. */\ninterface ReadableArtifact extends AnalysisArtifactSummary {\n filename: string\n}\n\nconst props = defineProps<{\n modelValue?: PluginJobPathSelection | PluginJobPathSelection[] | null\n disabled?: boolean\n multiple?: boolean\n}>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: PluginJobPathSelection | PluginJobPathSelection[] | null]\n}>()\n\nconst mode = ref<'file' | 'folder'>('file')\nconst selections = computed(() => Array.isArray(props.modelValue)\n ? props.modelValue\n : props.modelValue ? [props.modelValue] : [])\nconst uploads = computed(() => selections.value.filter(\n (selection): selection is PluginJobPathInput => selection.kind === 'path',\n))\nconst artifacts = computed(() => selections.value.filter(\n (selection): selection is PluginJobArtifactPathInput => selection.kind === 'artifact-path',\n))\nconst selectedFiles = computed(() => uploads.value.flatMap(selection => selection.files))\nwatch(uploads, (current) => {\n if (current.length) mode.value = current.every(selection => selection.rootName) ? 'folder' : 'file'\n}, { immediate: true })\nconst artifactOpen = ref(false)\nconst artifactSearch = ref('')\nconst artifactRows = ref<ReadableArtifact[]>([])\nconst artifactLoading = ref(false)\nconst artifactError = ref<string | null>(null)\nconst platform = getInjectedPlatformContext()\nconst experimentStore = useExperimentStore()\nconst jobsApi = inject(GENERATED_JOBS_API_KEY, null)\nconst canSelectArtifact = computed(() => Boolean(platform?.isIntegrated && experimentStore.currentId !== null && jobsApi))\nconst visibleArtifactRows = computed(() => {\n const query = artifactSearch.value.trim().toLowerCase()\n return query\n ? artifactRows.value.filter(row => [row.display_name, row.artifact_key, row.filename, row.plugin_id]\n .some(text => text.toLowerCase().includes(query)))\n : artifactRows.value\n})\n\nfunction selectMode(nextMode: 'file' | 'folder'): void {\n mode.value = nextMode\n emit('update:modelValue', null)\n}\n\nfunction selectFiles(\n files: File[],\n rootName?: string,\n relativePaths?: string[],\n): void {\n const paths = relativePaths ?? files.map(file => file.name)\n const added: PluginJobPathInput[] = props.multiple && !rootName\n ? files.map((file, index) => ({\n kind: 'path',\n files: [file],\n relativePaths: [paths[index] ?? file.name],\n }))\n : [{\n kind: 'path',\n files,\n relativePaths: paths,\n ...(rootName ? { rootName } : {}),\n }]\n emitSelections(props.multiple ? [...selections.value, ...added] : added)\n}\n\nfunction removeFiles(files: File[]): void {\n const removed = new Set(files)\n emitSelections(selections.value.flatMap((selection): PluginJobPathSelection[] => {\n if (selection.kind !== 'path') return [selection]\n const indexes = selection.files\n .map((file, index) => removed.has(file) ? -1 : index)\n .filter(index => index >= 0)\n if (!indexes.length) return []\n return [{\n ...selection,\n files: indexes.map(index => selection.files[index]!),\n relativePaths: indexes.map(index => selection.relativePaths[index]!),\n }]\n }))\n}\n\nfunction removeArtifact(artifact: PluginJobArtifactPathInput): void {\n emitSelections(selections.value.filter(selection => selection !== artifact))\n}\n\nfunction emitSelections(next: PluginJobPathSelection[]): void {\n emit('update:modelValue', props.multiple ? next : next[0] ?? null)\n}\n\n// The plugin lists exactly the artifacts its staging route accepts (same\n// readers and visibility rule), so a pick never fails later with a 404.\nlet artifactRequest = 0\n\nasync function openArtifactPicker(): Promise<void> {\n const experimentId = experimentStore.currentId\n if (!jobsApi || experimentId === null) return\n const request = ++artifactRequest\n artifactOpen.value = true\n artifactSearch.value = ''\n artifactLoading.value = true\n artifactError.value = null\n let rows: ReadableArtifact[] = []\n let error: string | null = null\n try {\n const transport = jobsApi.transport ?? createGeneratedAnalysisTransport()\n const baseUrl = jobsApi.baseUrl.replace(/\\/$/, '')\n const response = await transport(\n `${baseUrl}/jobs/artifacts?experiment_id=${encodeURIComponent(experimentId)}`,\n { headers: { Accept: 'application/json' } },\n )\n const payload: unknown = await response.json().catch(() => null)\n if (!response.ok) throw mintApiErrorFromResponse(response.status, payload)\n rows = (payload as { artifacts?: ReadableArtifact[] } | null)?.artifacts ?? []\n } catch (cause) {\n error = cause instanceof Error ? cause.message : 'Failed to load artifacts'\n }\n // A reopened picker owns the list; an earlier, slower answer is dropped.\n if (request !== artifactRequest) return\n artifactRows.value = rows\n artifactError.value = error\n artifactLoading.value = false\n}\n\nfunction describeArtifact(row: ReadableArtifact) {\n const date = row.updated_at ?? row.created_at\n return {\n code: row.artifact_key,\n title: row.display_name,\n meta: [row.filename, row.plugin_id],\n date: date ? formatExperimentDate(date) : '',\n }\n}\n\n// Only the database ids are kept: the plugin re-authorizes and copies the\n// file when the job is submitted, so the browser never downloads it.\nfunction selectArtifact(row: ReadableArtifact): void {\n const selection: PluginJobArtifactPathInput = {\n kind: 'artifact-path',\n experimentId: row.experiment_id,\n artifactId: row.id,\n name: row.filename,\n }\n emitSelections(props.multiple ? [...selections.value, selection] : [selection])\n artifactOpen.value = false\n}\n</script>\n\n<template>\n <div class=\"mint-generated-path-input\">\n <div class=\"mint-generated-path-input__modes\" role=\"group\" aria-label=\"Path type\">\n <BaseButton\n size=\"sm\"\n :variant=\"mode === 'file' ? 'primary' : 'secondary'\"\n :disabled=\"disabled\"\n @click=\"selectMode('file')\"\n >\n File\n </BaseButton>\n <BaseButton\n size=\"sm\"\n :variant=\"mode === 'folder' ? 'primary' : 'secondary'\"\n :disabled=\"disabled\"\n @click=\"selectMode('folder')\"\n >\n Folder\n </BaseButton>\n <BaseButton\n v-if=\"canSelectArtifact\"\n size=\"sm\"\n variant=\"secondary\"\n :disabled=\"disabled\"\n @click=\"openArtifactPicker\"\n >\n Existing artifact\n </BaseButton>\n </div>\n\n <FileUploader\n :key=\"mode\"\n :mode=\"mode\"\n :files=\"selectedFiles\"\n :root-name=\"uploads[0]?.rootName\"\n :multiple=\"multiple && mode === 'file'\"\n :disabled=\"disabled\"\n @upload=\"selectFiles\"\n @remove=\"removeFiles\"\n />\n\n <ul v-if=\"artifacts.length\" class=\"mint-file-uploader__list\">\n <li\n v-for=\"artifact in artifacts\"\n :key=\"`${artifact.experimentId}:${artifact.artifactId}`\"\n class=\"mint-file-uploader__file mint-generated-path-input__artifact\"\n >\n <div class=\"mint-file-uploader__file-info\">\n <span class=\"mint-file-uploader__file-badge mint-generated-path-input__artifact-badge\">ART</span>\n <div class=\"mint-file-uploader__file-meta\">\n <span class=\"mint-file-uploader__file-name\">{{ artifact.name }}</span>\n <span class=\"mint-file-uploader__file-size\">Existing artifact · experiment {{ artifact.experimentId }}</span>\n </div>\n </div>\n <button\n type=\"button\"\n :aria-label=\"`Remove ${artifact.name}`\"\n class=\"mint-file-uploader__remove-btn\"\n :disabled=\"disabled\"\n @click.stop=\"removeArtifact(artifact)\"\n >\n <svg class=\"mint-file-uploader__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 </li>\n </ul>\n\n <BaseModal\n v-if=\"canSelectArtifact\"\n v-model:open=\"artifactOpen\"\n title=\"Select a file artifact\"\n size=\"lg\"\n >\n <ResourcePickerListInternal\n v-model:search=\"artifactSearch\"\n label=\"File artifacts\"\n placeholder=\"Search file artifacts…\"\n :items=\"visibleArtifactRows\"\n :item-key=\"(row) => row.id\"\n :describe=\"describeArtifact\"\n :loading=\"artifactLoading\"\n :error=\"artifactError\"\n :empty-title=\"artifactRows.length ? 'No matching file artifacts' : 'No file artifacts this plugin can read'\"\n :empty-description=\"artifactRows.length\n ? 'Try a different search.'\n : 'Only active file artifacts saved by this plugin, or by the plugins it declares as readers, can be used as a path.'\"\n @select=\"selectArtifact\"\n />\n </BaseModal>\n </div>\n</template>\n\n<style scoped>\n.mint-generated-path-input {\n display: grid;\n gap: 0.75rem;\n}\n\n.mint-generated-path-input__modes {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.mint-generated-path-input__artifact-badge {\n --badge-color: var(--color-primary);\n}\n\n.mint-generated-path-input__artifact .mint-file-uploader__remove-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n color: var(--text-muted);\n background: transparent;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, inject, ref, watch } from 'vue'\n\nimport { mintApiErrorFromResponse } from '../apiErrors'\nimport { formatExperimentDate } from '../composables/experiment-utils'\nimport { createGeneratedAnalysisTransport } from '../composables/useJobsEndpoint'\nimport { getInjectedPlatformContext } from '../composables/platformContextHelpers'\nimport { useExperimentStore } from '../stores/experiment'\nimport type { AnalysisArtifactSummary } from '../types/analysisArtifactTypes'\nimport type {\n PluginJobArtifactPathInput,\n PluginJobPathInput,\n PluginJobPathSelection,\n} from '../types/generated-ui'\nimport BaseButton from './BaseButton.vue'\nimport BaseModal from './BaseModal.vue'\nimport FileUploader from './FileUploader.vue'\nimport { GENERATED_JOBS_API_KEY } from './internal/generatedJobsApi'\nimport ResourcePickerListInternal from './internal/ResourcePickerListInternal.vue'\n\n/** A file artifact the plugin's jobs API says this Path input may use. */\ninterface ReadableArtifact extends AnalysisArtifactSummary {\n filename: string\n}\n\nconst props = defineProps<{\n modelValue?: PluginJobPathSelection | PluginJobPathSelection[] | null\n disabled?: boolean\n multiple?: boolean\n}>()\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: PluginJobPathSelection | PluginJobPathSelection[] | null]\n}>()\n\nconst mode = ref<'file' | 'folder'>('file')\nconst selections = computed(() => Array.isArray(props.modelValue)\n ? props.modelValue\n : props.modelValue ? [props.modelValue] : [])\nconst uploads = computed(() => selections.value.filter(\n (selection): selection is PluginJobPathInput => selection.kind === 'path',\n))\nconst artifacts = computed(() => selections.value.filter(\n (selection): selection is PluginJobArtifactPathInput => selection.kind === 'artifact-path',\n))\nconst selectedFiles = computed(() => uploads.value.flatMap(selection => selection.files))\nwatch(uploads, (current) => {\n if (current.length) mode.value = current.every(selection => selection.rootName) ? 'folder' : 'file'\n}, { immediate: true })\nconst artifactOpen = ref(false)\nconst artifactSearch = ref('')\nconst artifactRows = ref<ReadableArtifact[]>([])\nconst artifactLoading = ref(false)\nconst artifactError = ref<string | null>(null)\nconst platform = getInjectedPlatformContext()\nconst experimentStore = useExperimentStore()\nconst jobsApi = inject(GENERATED_JOBS_API_KEY, null)\nconst canSelectArtifact = computed(() => Boolean(platform?.isIntegrated && experimentStore.currentId !== null && jobsApi))\nconst visibleArtifactRows = computed(() => {\n const query = artifactSearch.value.trim().toLowerCase()\n return query\n ? artifactRows.value.filter(row => [row.display_name, row.artifact_key, row.filename, row.plugin_id]\n .some(text => text.toLowerCase().includes(query)))\n : artifactRows.value\n})\n\nfunction selectMode(nextMode: 'file' | 'folder'): void {\n mode.value = nextMode\n emit('update:modelValue', null)\n}\n\nfunction selectFiles(\n files: File[],\n rootName?: string,\n relativePaths?: string[],\n): void {\n const paths = relativePaths ?? files.map(file => file.name)\n const added: PluginJobPathInput[] = props.multiple && !rootName\n ? files.map((file, index) => ({\n kind: 'path',\n files: [file],\n relativePaths: [paths[index] ?? file.name],\n }))\n : [{\n kind: 'path',\n files,\n relativePaths: paths,\n ...(rootName ? { rootName } : {}),\n }]\n emitSelections(props.multiple ? [...selections.value, ...added] : added)\n}\n\nfunction removeFiles(files: File[]): void {\n const removed = new Set(files)\n emitSelections(selections.value.flatMap((selection): PluginJobPathSelection[] => {\n if (selection.kind !== 'path') return [selection]\n const indexes = selection.files\n .map((file, index) => removed.has(file) ? -1 : index)\n .filter(index => index >= 0)\n if (!indexes.length) return []\n return [{\n ...selection,\n files: indexes.map(index => selection.files[index]!),\n relativePaths: indexes.map(index => selection.relativePaths[index]!),\n }]\n }))\n}\n\nfunction removeArtifact(artifact: PluginJobArtifactPathInput): void {\n emitSelections(selections.value.filter(selection => selection !== artifact))\n}\n\nfunction emitSelections(next: PluginJobPathSelection[]): void {\n emit('update:modelValue', props.multiple ? next : next[0] ?? null)\n}\n\n// The plugin lists exactly the artifacts its staging route accepts (same\n// readers and visibility rule), so a pick never fails later with a 404.\nlet artifactRequest = 0\n\nasync function openArtifactPicker(): Promise<void> {\n const experimentId = experimentStore.currentId\n if (!jobsApi || experimentId === null) return\n const request = ++artifactRequest\n artifactOpen.value = true\n artifactSearch.value = ''\n artifactLoading.value = true\n artifactError.value = null\n let rows: ReadableArtifact[] = []\n let error: string | null = null\n try {\n const transport = jobsApi.transport ?? createGeneratedAnalysisTransport()\n const baseUrl = jobsApi.baseUrl.replace(/\\/$/, '')\n const response = await transport(\n `${baseUrl}/jobs/artifacts?experiment_id=${encodeURIComponent(experimentId)}`,\n { headers: { Accept: 'application/json' } },\n )\n const payload: unknown = await response.json().catch(() => null)\n if (!response.ok) throw mintApiErrorFromResponse(response.status, payload)\n rows = (payload as { artifacts?: ReadableArtifact[] } | null)?.artifacts ?? []\n } catch (cause) {\n error = cause instanceof Error ? cause.message : 'Failed to load artifacts'\n }\n // A reopened picker owns the list; an earlier, slower answer is dropped.\n if (request !== artifactRequest) return\n artifactRows.value = rows\n artifactError.value = error\n artifactLoading.value = false\n}\n\nfunction describeArtifact(row: ReadableArtifact) {\n const date = row.updated_at ?? row.created_at\n return {\n code: row.artifact_key,\n title: row.display_name,\n meta: [row.filename, row.plugin_id],\n date: date ? formatExperimentDate(date) : '',\n }\n}\n\n// Only the database ids are kept: the plugin re-authorizes and copies the\n// file when the job is submitted, so the browser never downloads it.\nfunction selectArtifact(row: ReadableArtifact): void {\n const selection: PluginJobArtifactPathInput = {\n kind: 'artifact-path',\n experimentId: row.experiment_id,\n artifactId: row.id,\n name: row.filename,\n }\n emitSelections(props.multiple ? [...selections.value, selection] : [selection])\n artifactOpen.value = false\n}\n</script>\n\n<template>\n <div class=\"mint-generated-path-input\">\n <div class=\"mint-generated-path-input__modes\" role=\"group\" aria-label=\"Path type\">\n <BaseButton\n size=\"sm\"\n :variant=\"mode === 'file' ? 'primary' : 'secondary'\"\n :disabled=\"disabled\"\n @click=\"selectMode('file')\"\n >\n File\n </BaseButton>\n <BaseButton\n size=\"sm\"\n :variant=\"mode === 'folder' ? 'primary' : 'secondary'\"\n :disabled=\"disabled\"\n @click=\"selectMode('folder')\"\n >\n Folder\n </BaseButton>\n <BaseButton\n v-if=\"canSelectArtifact\"\n size=\"sm\"\n variant=\"secondary\"\n :disabled=\"disabled\"\n @click=\"openArtifactPicker\"\n >\n Existing artifact\n </BaseButton>\n </div>\n\n <FileUploader\n :key=\"mode\"\n :mode=\"mode\"\n :files=\"selectedFiles\"\n :root-name=\"uploads[0]?.rootName\"\n :multiple=\"multiple && mode === 'file'\"\n :disabled=\"disabled\"\n @upload=\"selectFiles\"\n @remove=\"removeFiles\"\n />\n\n <ul v-if=\"artifacts.length\" class=\"mint-file-uploader__list\">\n <li\n v-for=\"artifact in artifacts\"\n :key=\"`${artifact.experimentId}:${artifact.artifactId}`\"\n class=\"mint-file-uploader__file mint-generated-path-input__artifact\"\n >\n <div class=\"mint-file-uploader__file-info\">\n <span class=\"mint-file-uploader__file-badge mint-generated-path-input__artifact-badge\">ART</span>\n <div class=\"mint-file-uploader__file-meta\">\n <span class=\"mint-file-uploader__file-name\">{{ artifact.name }}</span>\n <span class=\"mint-file-uploader__file-size\">Existing artifact · experiment {{ artifact.experimentId }}</span>\n </div>\n </div>\n <button\n type=\"button\"\n :aria-label=\"`Remove ${artifact.name}`\"\n class=\"mint-file-uploader__remove-btn\"\n :disabled=\"disabled\"\n @click.stop=\"removeArtifact(artifact)\"\n >\n <svg class=\"mint-file-uploader__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 </li>\n </ul>\n\n <BaseModal\n v-if=\"canSelectArtifact\"\n v-model:open=\"artifactOpen\"\n title=\"Select a file artifact\"\n size=\"lg\"\n >\n <ResourcePickerListInternal\n v-model:search=\"artifactSearch\"\n label=\"File artifacts\"\n placeholder=\"Search file artifacts…\"\n :items=\"visibleArtifactRows\"\n :item-key=\"(row) => row.id\"\n :describe=\"describeArtifact\"\n :loading=\"artifactLoading\"\n :error=\"artifactError\"\n :empty-title=\"artifactRows.length ? 'No matching file artifacts' : 'No file artifacts this plugin can read'\"\n :empty-description=\"artifactRows.length\n ? 'Try a different search.'\n : 'Only active file artifacts saved by this plugin, or by the plugins it declares as readers, can be used as a path.'\"\n @select=\"selectArtifact\"\n />\n </BaseModal>\n </div>\n</template>\n\n<style scoped>\n.mint-generated-path-input {\n display: grid;\n gap: 0.75rem;\n}\n\n.mint-generated-path-input__modes {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.mint-generated-path-input__artifact-badge {\n --badge-color: var(--color-primary);\n}\n\n.mint-generated-path-input__artifact .mint-file-uploader__remove-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n color: var(--text-muted);\n background: transparent;\n}\n</style>\n","// Types\nexport interface ParsedElement {\n symbol: string\n count: number\n}\n\nexport interface FormulaParseResult {\n elements: Record<string, number>\n valid: boolean\n error?: string\n}\n\n/** `coefficient` is a full-size multiplier at the start of a hydrate segment (the 5 in CuSO4·5H2O). */\nexport type FormulaPartType = 'element' | 'subscript' | 'superscript' | 'paren' | 'dot' | 'charge' | 'coefficient'\n\nexport interface FormulaPart {\n type: FormulaPartType\n text: string\n}\n\n// Every element with its standard atomic weight (mass number of the most stable isotope when none is defined)\nexport const ATOMIC_WEIGHTS: Record<string, number> = {\n H: 1.008, He: 4.003, Li: 6.941, Be: 9.012, B: 10.81,\n C: 12.011, N: 14.007, O: 15.999, F: 18.998, Ne: 20.180,\n Na: 22.990, Mg: 24.305, Al: 26.982, Si: 28.086, P: 30.974,\n S: 32.065, Cl: 35.453, Ar: 39.948, K: 39.098, Ca: 40.078,\n Sc: 44.956, Ti: 47.867, V: 50.942, Cr: 51.996,\n Mn: 54.938, Fe: 55.845, Co: 58.933, Ni: 58.693, Cu: 63.546,\n Zn: 65.38, Ga: 69.723, Ge: 72.630, As: 74.922, Se: 78.971,\n Br: 79.904, Kr: 83.798, Rb: 85.468, Sr: 87.62, Y: 88.906,\n Zr: 91.224, Nb: 92.906, Mo: 95.95, Tc: 98, Ru: 101.07,\n Rh: 102.906, Pd: 106.42, Ag: 107.868, Cd: 112.414, In: 114.818,\n Sn: 118.710, Sb: 121.760, Te: 127.60, I: 126.904, Xe: 131.293,\n Cs: 132.905, Ba: 137.327, La: 138.905, Ce: 140.116, Pr: 140.908,\n Nd: 144.242, Pm: 145, Sm: 150.36, Eu: 151.964, Gd: 157.25,\n Tb: 158.925, Dy: 162.500, Ho: 164.930, Er: 167.259, Tm: 168.934,\n Yb: 173.045, Lu: 174.967, Hf: 178.49, Ta: 180.948, W: 183.84,\n Re: 186.207, Os: 190.23, Ir: 192.217, Pt: 195.084, Au: 196.967,\n Hg: 200.592, Tl: 204.38, Pb: 207.2, Bi: 208.980, Po: 209,\n At: 210, Rn: 222, Fr: 223, Ra: 226, Ac: 227,\n Th: 232.038, Pa: 231.036, U: 238.029, Np: 237, Pu: 244,\n Am: 243, Cm: 247, Bk: 247, Cf: 251, Es: 252,\n Fm: 257, Md: 258, No: 259, Lr: 266, Rf: 267,\n Db: 268, Sg: 269, Bh: 270, Hs: 277, Mt: 278,\n Ds: 281, Rg: 282, Cn: 285, Nh: 286, Fl: 289,\n Mc: 290, Lv: 293, Ts: 294, Og: 294,\n}\n\nfunction parseFormulaSegment(formula: string): FormulaParseResult {\n const elements: Record<string, number> = {}\n const stack: Record<string, number>[] = [{}]\n let i = 0\n\n while (i < formula.length) {\n const ch = formula[i]\n\n if (ch === '(') {\n stack.push({})\n i++\n } else if (ch === ')') {\n if (stack.length < 2) {\n return { elements: {}, valid: false, error: 'Unmatched closing parenthesis' }\n }\n i++\n // Read multiplier after closing paren\n let numStr = ''\n while (i < formula.length && /\\d/.test(formula[i])) {\n numStr += formula[i]\n i++\n }\n const multiplier = numStr ? parseInt(numStr, 10) : 1\n const top = stack.pop()!\n const current = stack[stack.length - 1]\n for (const [el, count] of Object.entries(top)) {\n current[el] = (current[el] || 0) + count * multiplier\n }\n } else if (/[A-Z]/.test(ch)) {\n // Element symbol: uppercase letter optionally followed by lowercase\n let symbol = ch\n i++\n while (i < formula.length && /[a-z]/.test(formula[i])) {\n symbol += formula[i]\n i++\n }\n // Validate element\n if (!(symbol in ATOMIC_WEIGHTS)) {\n return { elements: {}, valid: false, error: `Unknown element: ${symbol}` }\n }\n // Read count\n let numStr = ''\n while (i < formula.length && /\\d/.test(formula[i])) {\n numStr += formula[i]\n i++\n }\n const count = numStr ? parseInt(numStr, 10) : 1\n const current = stack[stack.length - 1]\n current[symbol] = (current[symbol] || 0) + count\n } else {\n return { elements: {}, valid: false, error: `Unexpected character: ${ch}` }\n }\n }\n\n if (stack.length !== 1) {\n return { elements: {}, valid: false, error: 'Unmatched opening parenthesis' }\n }\n\n Object.assign(elements, stack[0])\n return { elements, valid: true }\n}\n\nexport function parseFormula(formula: string): FormulaParseResult {\n const trimmed = formula.trim()\n if (!trimmed) {\n return { elements: {}, valid: false, error: 'Empty formula' }\n }\n\n // A trailing charge (SO4^2-, Fe2+, NH4+) does not change the composition. Like renderFormulaParts,\n // digits before a bare sign are the charge only on a lone element (Fe2+); otherwise they are a\n // subscript and the sign a unit charge (NH4+). Write Hg2^2+ for any other multiple charge.\n const body = trimmed.replace(/^([A-Z][a-z]?)\\d*[+-]$/, '$1').replace(/(\\^\\d*)?[+-]$/, '')\n if (!body) {\n return { elements: {}, valid: false, error: 'Empty formula' }\n }\n // Split on hydrate separator (·, . or *)\n const parts = body.split(/[·.*]/)\n const combined: Record<string, number> = {}\n\n for (const part of parts) {\n const segment = part.trim()\n if (!segment) continue\n\n // Check for leading coefficient (e.g., 5H2O)\n let coefficient = 1\n let formulaStr = segment\n const coeffMatch = segment.match(/^(\\d+)([A-Z].*)$/)\n if (coeffMatch) {\n coefficient = parseInt(coeffMatch[1], 10)\n formulaStr = coeffMatch[2]\n }\n\n const result = parseFormulaSegment(formulaStr)\n if (!result.valid) {\n return result\n }\n\n for (const [el, count] of Object.entries(result.elements)) {\n combined[el] = (combined[el] || 0) + count * coefficient\n }\n }\n\n return { elements: combined, valid: true }\n}\n\nexport function calculateMW(elements: Record<string, number>): number {\n let mw = 0\n for (const [el, count] of Object.entries(elements)) {\n const weight = ATOMIC_WEIGHTS[el]\n if (weight !== undefined) {\n mw += weight * count\n }\n }\n return Math.round(mw * 1000) / 1000\n}\n\nexport function renderFormulaParts(formula: string): FormulaPart[] {\n const parts: FormulaPart[] = []\n const trimmed = formula.trim()\n let i = 0\n\n while (i < trimmed.length) {\n const ch = trimmed[i]\n\n // Hydrate dot separator\n if (ch === '·' || (ch === '.' && i > 0 && /\\d/.test(trimmed[i - 1]) && i + 1 < trimmed.length && /\\d/.test(trimmed[i + 1]))) {\n // Disambiguate: '.' as hydrate separator only if followed by a digit then uppercase\n // For simplicity, treat '·' always as dot\n if (ch === '·') {\n parts.push({ type: 'dot', text: '·' })\n i++\n continue\n }\n }\n\n // Parentheses\n if (ch === '(' || ch === ')') {\n parts.push({ type: 'paren', text: ch })\n i++\n // After ')' check for subscript number\n if (ch === ')' && i < trimmed.length && /\\d/.test(trimmed[i])) {\n let numStr = ''\n while (i < trimmed.length && /\\d/.test(trimmed[i])) {\n numStr += trimmed[i]\n i++\n }\n parts.push({ type: 'subscript', text: numStr })\n }\n continue\n }\n\n // Charge notation: ^2+, ^2-, 2+, 2- at end, or +, -\n if (ch === '^') {\n i++\n let chargeStr = ''\n while (i < trimmed.length && /[\\d+\\-]/.test(trimmed[i])) {\n chargeStr += trimmed[i]\n i++\n }\n if (chargeStr) {\n parts.push({ type: 'charge', text: chargeStr })\n }\n continue\n }\n\n // Element symbol: uppercase followed by optional lowercase\n if (/[A-Z]/.test(ch)) {\n let symbol = ch\n i++\n while (i < trimmed.length && /[a-z]/.test(trimmed[i])) {\n symbol += trimmed[i]\n i++\n }\n parts.push({ type: 'element', text: symbol })\n\n // Check for subscript number after element\n if (i < trimmed.length && /\\d/.test(trimmed[i])) {\n let numStr = ''\n while (i < trimmed.length && /\\d/.test(trimmed[i])) {\n numStr += trimmed[i]\n i++\n }\n parts.push({ type: 'subscript', text: numStr })\n\n // On a lone element the digits before a trailing sign are the charge (Fe2+);\n // elsewhere they stay a subscript and the sign renders as a unit charge (NH4+).\n if (parts.length === 2 && i === trimmed.length - 1 && (trimmed[i] === '+' || trimmed[i] === '-')) {\n parts.push({ type: 'charge', text: numStr + trimmed[i] })\n parts.splice(parts.length - 2, 1) // Remove the subscript\n i++\n continue\n }\n }\n continue\n }\n\n // Leading digit (hydrate coefficient like 5H2O)\n if (/\\d/.test(ch)) {\n let numStr = ''\n while (i < trimmed.length && /\\d/.test(trimmed[i])) {\n numStr += trimmed[i]\n i++\n }\n // Could be a subscript or coefficient depending on context\n // If next char is uppercase, it's a coefficient (render as element-like text)\n // If at end or followed by +/-, it's a charge component\n const prev = parts[parts.length - 1]\n if (i < trimmed.length && (trimmed[i] === '+' || trimmed[i] === '-')) {\n parts.push({ type: 'charge', text: numStr + trimmed[i] })\n i++\n } else if (!prev || prev.type === 'dot') {\n parts.push({ type: 'coefficient', text: numStr })\n } else {\n parts.push({ type: 'subscript', text: numStr })\n }\n continue\n }\n\n // Standalone + or - (charge)\n if (ch === '+' || ch === '-') {\n parts.push({ type: 'charge', text: ch })\n i++\n continue\n }\n\n // Dot or asterisk as hydrate separator\n if (ch === '.' || ch === '*') {\n parts.push({ type: 'dot', text: '·' })\n i++\n continue\n }\n\n // Skip whitespace\n if (/\\s/.test(ch)) {\n i++\n continue\n }\n\n // Unknown character, skip\n i++\n }\n\n return parts\n}\n","<script setup lang=\"ts\">\n/**\n * Text input for chemical formulas (e.g. Ca(OH)2, CuSO4·5H2O). Blurred, the field shows the rendered\n * formula; focused, the raw text. A fixed segment shows the molecular weight; an unknown symbol puts\n * the field in the error state and names the symbol.\n */\nimport { computed, ref } from 'vue'\nimport { calculateMW, parseFormula, renderFormulaParts } from '../utils/formula'\n\ninterface Props {\n modelValue?: string\n showPreview?: boolean\n showMW?: boolean\n placeholder?: string\n invalid?: boolean\n disabled?: boolean\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: '',\n showPreview: true,\n showMW: true,\n placeholder: 'e.g. Ca(OH)2',\n invalid: false,\n disabled: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n 'mw': [mw: number | null]\n}>()\n\n\nconst focused = ref(false)\n\nfunction analyse(value: string) {\n if (!value.trim()) return { mw: null, error: null }\n const result = parseFormula(value)\n if (!result.valid) return { mw: null, error: result.error || 'Invalid formula' }\n return { mw: calculateMW(result.elements), error: null }\n}\n\nconst analysis = computed(() => analyse(props.modelValue))\nconst parts = computed(() => renderFormulaParts(props.modelValue))\nconst showRendered = computed(() =>\n props.showPreview && !focused.value && Boolean(props.modelValue.trim()) && !analysis.value.error,\n)\nconst mwText = computed(() => (analysis.value.mw === null ? '—' : analysis.value.mw.toFixed(2)))\n\nfunction handleInput(event: Event) {\n const value = (event.target as HTMLInputElement).value\n emit('update:modelValue', value)\n emit('mw', analyse(value).mw)\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-formula-input',\n invalid || analysis.error ? 'mint-formula-input--error' : '',\n disabled ? 'mint-formula-input--disabled' : '',\n ]\"\n >\n <div :class=\"['mint-formula-input__field', `mint-formula-input__field--${size}`]\">\n <div class=\"mint-formula-input__entry\">\n <input\n type=\"text\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :aria-invalid=\"invalid || Boolean(analysis.error) || undefined\"\n spellcheck=\"false\"\n autocomplete=\"off\"\n :class=\"['mint-formula-input__input', showRendered ? 'mint-formula-input__input--masked' : '']\"\n aria-label=\"Chemical formula\"\n @input=\"handleInput\"\n @focus=\"focused = true\"\n @blur=\"focused = false\"\n />\n <span v-if=\"showRendered\" class=\"mint-formula-input__rendered\" aria-hidden=\"true\">\n <template v-for=\"(part, i) in parts\" :key=\"i\">\n <span v-if=\"part.type === 'subscript'\" class=\"mint-formula-input__sub\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'superscript' || part.type === 'charge'\" class=\"mint-formula-input__sup\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'dot'\" class=\"mint-formula-input__dot\">{{ part.text }}</span>\n <span v-else>{{ part.text }}</span>\n </template>\n </span>\n </div>\n <span v-if=\"showMW\" class=\"mint-formula-input__mw\"><span class=\"mint-formula-input__mw-label\">MW</span> <span class=\"mint-formula-input__mw-value\">{{ mwText }}</span> <span class=\"mint-formula-input__mw-unit\">g/mol</span></span>\n </div>\n\n <div v-if=\"analysis.error\" class=\"mint-formula-input__error-text\">\n {{ analysis.error }}\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * Text input for chemical formulas (e.g. Ca(OH)2, CuSO4·5H2O). Blurred, the field shows the rendered\n * formula; focused, the raw text. A fixed segment shows the molecular weight; an unknown symbol puts\n * the field in the error state and names the symbol.\n */\nimport { computed, ref } from 'vue'\nimport { calculateMW, parseFormula, renderFormulaParts } from '../utils/formula'\n\ninterface Props {\n modelValue?: string\n showPreview?: boolean\n showMW?: boolean\n placeholder?: string\n invalid?: boolean\n disabled?: boolean\n size?: 'sm' | 'md' | 'lg'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: '',\n showPreview: true,\n showMW: true,\n placeholder: 'e.g. Ca(OH)2',\n invalid: false,\n disabled: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n 'mw': [mw: number | null]\n}>()\n\n\nconst focused = ref(false)\n\nfunction analyse(value: string) {\n if (!value.trim()) return { mw: null, error: null }\n const result = parseFormula(value)\n if (!result.valid) return { mw: null, error: result.error || 'Invalid formula' }\n return { mw: calculateMW(result.elements), error: null }\n}\n\nconst analysis = computed(() => analyse(props.modelValue))\nconst parts = computed(() => renderFormulaParts(props.modelValue))\nconst showRendered = computed(() =>\n props.showPreview && !focused.value && Boolean(props.modelValue.trim()) && !analysis.value.error,\n)\nconst mwText = computed(() => (analysis.value.mw === null ? '—' : analysis.value.mw.toFixed(2)))\n\nfunction handleInput(event: Event) {\n const value = (event.target as HTMLInputElement).value\n emit('update:modelValue', value)\n emit('mw', analyse(value).mw)\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-formula-input',\n invalid || analysis.error ? 'mint-formula-input--error' : '',\n disabled ? 'mint-formula-input--disabled' : '',\n ]\"\n >\n <div :class=\"['mint-formula-input__field', `mint-formula-input__field--${size}`]\">\n <div class=\"mint-formula-input__entry\">\n <input\n type=\"text\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :aria-invalid=\"invalid || Boolean(analysis.error) || undefined\"\n spellcheck=\"false\"\n autocomplete=\"off\"\n :class=\"['mint-formula-input__input', showRendered ? 'mint-formula-input__input--masked' : '']\"\n aria-label=\"Chemical formula\"\n @input=\"handleInput\"\n @focus=\"focused = true\"\n @blur=\"focused = false\"\n />\n <span v-if=\"showRendered\" class=\"mint-formula-input__rendered\" aria-hidden=\"true\">\n <template v-for=\"(part, i) in parts\" :key=\"i\">\n <span v-if=\"part.type === 'subscript'\" class=\"mint-formula-input__sub\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'superscript' || part.type === 'charge'\" class=\"mint-formula-input__sup\">{{ part.text }}</span>\n <span v-else-if=\"part.type === 'dot'\" class=\"mint-formula-input__dot\">{{ part.text }}</span>\n <span v-else>{{ part.text }}</span>\n </template>\n </span>\n </div>\n <span v-if=\"showMW\" class=\"mint-formula-input__mw\"><span class=\"mint-formula-input__mw-label\">MW</span> <span class=\"mint-formula-input__mw-value\">{{ mwText }}</span> <span class=\"mint-formula-input__mw-unit\">g/mol</span></span>\n </div>\n\n <div v-if=\"analysis.error\" class=\"mint-formula-input__error-text\">\n {{ analysis.error }}\n </div>\n </div>\n</template>\n","export type SequenceType = 'dna' | 'rna' | 'protein' | 'auto'\n\nexport interface SequenceStats {\n length: number\n gcPercent?: number\n molecularWeight?: number\n}\n\n/** What `validateSequence` would strip or flag, so a field can report it instead. */\nexport interface SequenceProblems {\n /** Ambiguous `N` bases (DNA/RNA only; `N` is asparagine in protein). */\n ambiguousBases: number\n /** Unique characters outside the alphabet, upper-cased, in order of appearance. Whitespace is ignored. */\n invalidCharacters: string[]\n}\n\nconst DNA_CHARS = /[^ATCGNatcgn]/g\nconst RNA_CHARS = /[^AUCGNaucgn]/g\nconst PROTEIN_CHARS = /[^ACDEFGHIKLMNPQRSTVWYacdefghiklmnpqrstvwy]/g\n\nconst DNA_COMPLEMENT: Record<string, string> = {\n A: 'T', T: 'A', C: 'G', G: 'C', N: 'N',\n a: 't', t: 'a', c: 'g', g: 'c', n: 'n',\n}\n\nconst RNA_COMPLEMENT: Record<string, string> = {\n A: 'U', U: 'A', C: 'G', G: 'C', N: 'N',\n a: 'u', u: 'a', c: 'g', g: 'c', n: 'n',\n}\n\n// Average molecular weights per residue\nconst MW_DNA_NT = 330 // Da per nucleotide (average)\nconst MW_RNA_NT = 340 // Da per nucleotide (average)\nconst MW_PROTEIN_AA = 110 // Da per amino acid (average)\n\n/** Detects, validates, cleans, and computes stats (GC%, MW) for DNA, RNA, and protein sequences. */\nexport function useSequenceUtils() {\n function detectSequenceType(seq: string): 'dna' | 'rna' | 'protein' {\n const upper = seq.toUpperCase()\n\n // If contains U but not T, it's RNA\n if (upper.includes('U') && !upper.includes('T')) {\n return 'rna'\n }\n\n // If its letters are only ATCGN, it's DNA (line breaks, spaces and digits don't count)\n if (/^[ATCGN]+$/i.test(seq.replace(/[^a-z]/gi, ''))) {\n return 'dna'\n }\n\n return 'protein'\n }\n\n function validateSequence(seq: string, type: SequenceType): string {\n const effectiveType = type === 'auto' ? detectSequenceType(seq) : type\n\n switch (effectiveType) {\n case 'dna':\n return seq.replace(DNA_CHARS, '')\n case 'rna':\n return seq.replace(RNA_CHARS, '')\n case 'protein':\n return seq.replace(PROTEIN_CHARS, '')\n default:\n return seq\n }\n }\n\n function findSequenceProblems(seq: string, type: 'dna' | 'rna' | 'protein'): SequenceProblems {\n const alphabet = { dna: DNA_CHARS, rna: RNA_CHARS, protein: PROTEIN_CHARS }[type]\n const invalid = seq.replace(/\\s/g, '').match(alphabet) ?? []\n return {\n ambiguousBases: type === 'protein' ? 0 : (seq.match(/n/gi) ?? []).length,\n invalidCharacters: [...new Set(invalid.map(ch => ch.toUpperCase()))],\n }\n }\n\n function reverseComplement(seq: string, type: 'dna' | 'rna'): string {\n const complementMap = type === 'dna' ? DNA_COMPLEMENT : RNA_COMPLEMENT\n\n return seq\n .split('')\n .reverse()\n .map(ch => complementMap[ch] ?? ch)\n .join('')\n }\n\n function calculateStats(seq: string, type: 'dna' | 'rna' | 'protein'): SequenceStats {\n const length = seq.length\n const stats: SequenceStats = { length }\n\n if (type === 'dna' || type === 'rna') {\n const upper = seq.toUpperCase()\n let gcCount = 0\n for (const ch of upper) {\n if (ch === 'G' || ch === 'C') gcCount++\n }\n stats.gcPercent = length > 0 ? Math.round((gcCount / length) * 10000) / 100 : 0\n stats.molecularWeight = length * (type === 'dna' ? MW_DNA_NT : MW_RNA_NT)\n } else {\n stats.molecularWeight = length * MW_PROTEIN_AA\n }\n\n return stats\n }\n\n function formatFasta(seq: string, lineWidth: number = 60): string {\n const lines: string[] = []\n for (let i = 0; i < seq.length; i += lineWidth) {\n lines.push(seq.slice(i, i + lineWidth))\n }\n return lines.join('\\n')\n }\n\n return {\n detectSequenceType,\n validateSequence,\n findSequenceProblems,\n reverseComplement,\n calculateStats,\n formatFasta,\n }\n}\n","<script setup lang=\"ts\">\n/** Textarea for DNA/RNA/protein sequences with auto-detection, validation, GC-content stats, reverse-complement, and FASTA formatting tools. */\nimport { computed, ref, watch } from 'vue'\nimport { useSequenceUtils, type SequenceType, type SequenceStats } from '../composables/useSequenceUtils'\n\ninterface Props {\n modelValue?: string\n type?: SequenceType\n readonly?: boolean\n showStats?: boolean\n showTools?: boolean\n maxLength?: number\n placeholder?: string\n rows?: number\n invalid?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: '',\n type: 'auto',\n readonly: false,\n showStats: true,\n showTools: true,\n rows: 6,\n placeholder: 'Paste or type sequence...',\n invalid: false,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n}>()\n\ndefineSlots<{\n tools?(props: { sequence: string; type: 'dna' | 'rna' | 'protein'; stats: SequenceStats }): unknown\n}>()\n\nconst { detectSequenceType, validateSequence, findSequenceProblems, reverseComplement, calculateStats, formatFasta } = useSequenceUtils()\n\n// The field shows what was typed; `modelValue` stays the sanitised sequence and\n// the stripped characters are reported below the field instead of vanishing (9c).\nconst draft = ref(props.modelValue)\nlet lastEmitted = props.modelValue\nwatch(() => props.modelValue, (value) => {\n if (value === lastEmitted) return\n draft.value = value\n lastEmitted = value\n})\n\n// Detected from what was typed, so residues the sanitised value would drop still count.\nconst resolvedType = computed<'dna' | 'rna' | 'protein'>(() => {\n if (props.type !== 'auto') return props.type as 'dna' | 'rna' | 'protein'\n if (!draft.value) return 'dna'\n return detectSequenceType(draft.value)\n})\n\nconst TYPE_LABELS = { dna: 'DNA', rna: 'RNA', protein: 'Protein' } as const\n\nconst stats = computed<SequenceStats>(() => {\n if (!props.modelValue) return { length: 0 }\n return calculateStats(props.modelValue, resolvedType.value)\n})\n\nconst problems = computed(() => findSequenceProblems(draft.value, resolvedType.value))\nconst hasInvalid = computed(() => problems.value.invalidCharacters.length > 0)\n\nconst viewerLines = computed(() => {\n if (!props.modelValue) return []\n const formatted = formatFasta(props.modelValue, 60)\n return formatted.split('\\n').filter(l => l.length > 0)\n})\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLTextAreaElement\n draft.value = target.value\n // maxLength counts sequence characters, so the cut falls after the last one that fits.\n const value = capToMaxLength(target.value, resolvedType.value)\n if (value !== target.value) {\n draft.value = value\n target.value = value\n }\n lastEmitted = validateSequence(value, resolvedType.value)\n emit('update:modelValue', lastEmitted)\n}\n\nfunction capToMaxLength(text: string, type: 'dna' | 'rna' | 'protein'): string {\n if (!props.maxLength) return text\n let kept = 0\n for (let i = 0; i < text.length; i++) {\n kept += validateSequence(text[i], type).length\n if (kept > props.maxLength) return text.slice(0, i)\n }\n return text\n}\n\nfunction doReverseComplement() {\n if (!props.modelValue || resolvedType.value === 'protein') return\n emit('update:modelValue', reverseComplement(props.modelValue, resolvedType.value as 'dna' | 'rna'))\n}\n\nfunction doUppercase() {\n if (!props.modelValue) return\n emit('update:modelValue', props.modelValue.toUpperCase())\n}\n\nfunction doClear() {\n draft.value = ''\n emit('update:modelValue', '')\n}\n\nfunction doCopy() {\n if (!props.modelValue) return\n navigator.clipboard.writeText(props.modelValue)\n}\n\nfunction getBaseClass(char: string): string {\n const c = char.toLowerCase()\n if (c === 'a') return 'mint-sequence-input__base--a'\n if (c === 't') return 'mint-sequence-input__base--t'\n if (c === 'u') return 'mint-sequence-input__base--u'\n if (c === 'g') return 'mint-sequence-input__base--g'\n if (c === 'c') return 'mint-sequence-input__base--c'\n if (c === 'n') return 'mint-sequence-input__base--n'\n return ''\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-sequence-input',\n invalid || hasInvalid ? 'mint-sequence-input--error' : '',\n disabled ? 'mint-sequence-input--disabled' : '',\n ]\"\n >\n <!-- Toolbar -->\n <div v-if=\"showTools && !readonly\" class=\"mint-sequence-input__toolbar\">\n <span class=\"mint-sequence-input__type-badge\">{{ TYPE_LABELS[resolvedType] }}</span>\n <div class=\"mint-sequence-input__tool-sep\" />\n <slot name=\"tools\" :sequence=\"modelValue || ''\" :type=\"resolvedType\" :stats=\"stats\">\n <button\n v-if=\"resolvedType !== 'protein'\"\n type=\"button\"\n class=\"mint-sequence-input__tool-btn\"\n :disabled=\"!modelValue || disabled\"\n @click=\"doReverseComplement\"\n >\n Rev Comp\n </button>\n <button\n type=\"button\"\n class=\"mint-sequence-input__tool-btn\"\n :disabled=\"!modelValue || disabled\"\n @click=\"doUppercase\"\n >\n Uppercase\n </button>\n <div class=\"mint-sequence-input__tool-sep\" />\n <button\n type=\"button\"\n class=\"mint-sequence-input__tool-btn\"\n :disabled=\"!modelValue || disabled\"\n @click=\"doCopy\"\n >\n Copy\n </button>\n <button\n type=\"button\"\n class=\"mint-sequence-input__tool-btn\"\n :disabled=\"!draft || disabled\"\n @click=\"doClear\"\n >\n Clear\n </button>\n </slot>\n </div>\n\n <!-- Editor (input mode) -->\n <div v-if=\"!readonly\" class=\"mint-sequence-input__editor\">\n <textarea\n :value=\"draft\"\n :rows=\"rows\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n class=\"mint-sequence-input__textarea\"\n spellcheck=\"false\"\n autocomplete=\"off\"\n @input=\"handleInput\"\n />\n </div>\n\n <!-- Viewer (readonly mode) -->\n <div v-else class=\"mint-sequence-input__viewer\">\n <div\n v-for=\"(line, lineIdx) in viewerLines\"\n :key=\"lineIdx\"\n class=\"mint-sequence-input__viewer-line\"\n >\n <span class=\"mint-sequence-input__line-number\">{{ lineIdx * 60 + 1 }}</span>\n <span class=\"mint-sequence-input__line-content\">\n <span\n v-for=\"(char, charIdx) in line.split('')\"\n :key=\"charIdx\"\n :class=\"getBaseClass(char)\"\n >{{ char }}</span>\n </span>\n </div>\n <div v-if=\"!modelValue\" class=\"mint-sequence-input__viewer-line\">\n <span class=\"mint-sequence-input__line-number\" />\n <span class=\"mint-sequence-input__line-content\" style=\"color: var(--text-muted);\">No sequence</span>\n </div>\n </div>\n\n <!-- Stats and problems -->\n <div\n v-if=\"(showStats && modelValue) || problems.ambiguousBases || hasInvalid\"\n class=\"mint-sequence-input__stats\"\n >\n <template v-if=\"showStats && modelValue\">\n <span>\n <span class=\"mint-sequence-input__stat-label\">Length:</span>\n <span class=\"mint-sequence-input__stat-value\">{{ stats.length }}</span>\n </span>\n <span v-if=\"stats.gcPercent !== undefined\">\n <span class=\"mint-sequence-input__stat-label\">GC:</span>\n <span class=\"mint-sequence-input__stat-value\">{{ stats.gcPercent.toFixed(1) }}%</span>\n </span>\n <span v-if=\"stats.molecularWeight !== undefined\">\n <span class=\"mint-sequence-input__stat-label\">MW:</span>\n <span class=\"mint-sequence-input__stat-value\">~{{ (stats.molecularWeight / 1000).toFixed(1) }} kDa</span>\n </span>\n </template>\n <span v-if=\"problems.ambiguousBases\" class=\"mint-sequence-input__problem mint-sequence-input__problem--warning\">\n <svg class=\"mint-sequence-input__problem-icon\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path class=\"mint-sequence-input__problem-shape\" d=\"M8 1.5l7 12.5H1z\" />\n <path class=\"mint-sequence-input__problem-mark\" d=\"M8 6v3.5M8 11.5v.01\" />\n </svg>\n {{ problems.ambiguousBases }} ambiguous bases (N)\n </span>\n <span v-if=\"hasInvalid\" role=\"alert\" class=\"mint-sequence-input__problem mint-sequence-input__problem--error\">\n <svg class=\"mint-sequence-input__problem-icon\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <circle class=\"mint-sequence-input__problem-shape\" cx=\"8\" cy=\"8\" r=\"7\" />\n <path class=\"mint-sequence-input__problem-mark\" d=\"M8 4.5v4M8 11.5v.01\" />\n </svg>\n Invalid characters: {{ problems.invalidCharacters.join(' ') }}\n </span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Textarea for DNA/RNA/protein sequences with auto-detection, validation, GC-content stats, reverse-complement, and FASTA formatting tools. */\nimport { computed, ref, watch } from 'vue'\nimport { useSequenceUtils, type SequenceType, type SequenceStats } from '../composables/useSequenceUtils'\n\ninterface Props {\n modelValue?: string\n type?: SequenceType\n readonly?: boolean\n showStats?: boolean\n showTools?: boolean\n maxLength?: number\n placeholder?: string\n rows?: number\n invalid?: boolean\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: '',\n type: 'auto',\n readonly: false,\n showStats: true,\n showTools: true,\n rows: 6,\n placeholder: 'Paste or type sequence...',\n invalid: false,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string]\n}>()\n\ndefineSlots<{\n tools?(props: { sequence: string; type: 'dna' | 'rna' | 'protein'; stats: SequenceStats }): unknown\n}>()\n\nconst { detectSequenceType, validateSequence, findSequenceProblems, reverseComplement, calculateStats, formatFasta } = useSequenceUtils()\n\n// The field shows what was typed; `modelValue` stays the sanitised sequence and\n// the stripped characters are reported below the field instead of vanishing (9c).\nconst draft = ref(props.modelValue)\nlet lastEmitted = props.modelValue\nwatch(() => props.modelValue, (value) => {\n if (value === lastEmitted) return\n draft.value = value\n lastEmitted = value\n})\n\n// Detected from what was typed, so residues the sanitised value would drop still count.\nconst resolvedType = computed<'dna' | 'rna' | 'protein'>(() => {\n if (props.type !== 'auto') return props.type as 'dna' | 'rna' | 'protein'\n if (!draft.value) return 'dna'\n return detectSequenceType(draft.value)\n})\n\nconst TYPE_LABELS = { dna: 'DNA', rna: 'RNA', protein: 'Protein' } as const\n\nconst stats = computed<SequenceStats>(() => {\n if (!props.modelValue) return { length: 0 }\n return calculateStats(props.modelValue, resolvedType.value)\n})\n\nconst problems = computed(() => findSequenceProblems(draft.value, resolvedType.value))\nconst hasInvalid = computed(() => problems.value.invalidCharacters.length > 0)\n\nconst viewerLines = computed(() => {\n if (!props.modelValue) return []\n const formatted = formatFasta(props.modelValue, 60)\n return formatted.split('\\n').filter(l => l.length > 0)\n})\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLTextAreaElement\n draft.value = target.value\n // maxLength counts sequence characters, so the cut falls after the last one that fits.\n const value = capToMaxLength(target.value, resolvedType.value)\n if (value !== target.value) {\n draft.value = value\n target.value = value\n }\n lastEmitted = validateSequence(value, resolvedType.value)\n emit('update:modelValue', lastEmitted)\n}\n\nfunction capToMaxLength(text: string, type: 'dna' | 'rna' | 'protein'): string {\n if (!props.maxLength) return text\n let kept = 0\n for (let i = 0; i < text.length; i++) {\n kept += validateSequence(text[i], type).length\n if (kept > props.maxLength) return text.slice(0, i)\n }\n return text\n}\n\nfunction doReverseComplement() {\n if (!props.modelValue || resolvedType.value === 'protein') return\n emit('update:modelValue', reverseComplement(props.modelValue, resolvedType.value as 'dna' | 'rna'))\n}\n\nfunction doUppercase() {\n if (!props.modelValue) return\n emit('update:modelValue', props.modelValue.toUpperCase())\n}\n\nfunction doClear() {\n draft.value = ''\n emit('update:modelValue', '')\n}\n\nfunction doCopy() {\n if (!props.modelValue) return\n navigator.clipboard.writeText(props.modelValue)\n}\n\nfunction getBaseClass(char: string): string {\n const c = char.toLowerCase()\n if (c === 'a') return 'mint-sequence-input__base--a'\n if (c === 't') return 'mint-sequence-input__base--t'\n if (c === 'u') return 'mint-sequence-input__base--u'\n if (c === 'g') return 'mint-sequence-input__base--g'\n if (c === 'c') return 'mint-sequence-input__base--c'\n if (c === 'n') return 'mint-sequence-input__base--n'\n return ''\n}\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-sequence-input',\n invalid || hasInvalid ? 'mint-sequence-input--error' : '',\n disabled ? 'mint-sequence-input--disabled' : '',\n ]\"\n >\n <!-- Toolbar -->\n <div v-if=\"showTools && !readonly\" class=\"mint-sequence-input__toolbar\">\n <span class=\"mint-sequence-input__type-badge\">{{ TYPE_LABELS[resolvedType] }}</span>\n <div class=\"mint-sequence-input__tool-sep\" />\n <slot name=\"tools\" :sequence=\"modelValue || ''\" :type=\"resolvedType\" :stats=\"stats\">\n <button\n v-if=\"resolvedType !== 'protein'\"\n type=\"button\"\n class=\"mint-sequence-input__tool-btn\"\n :disabled=\"!modelValue || disabled\"\n @click=\"doReverseComplement\"\n >\n Rev Comp\n </button>\n <button\n type=\"button\"\n class=\"mint-sequence-input__tool-btn\"\n :disabled=\"!modelValue || disabled\"\n @click=\"doUppercase\"\n >\n Uppercase\n </button>\n <div class=\"mint-sequence-input__tool-sep\" />\n <button\n type=\"button\"\n class=\"mint-sequence-input__tool-btn\"\n :disabled=\"!modelValue || disabled\"\n @click=\"doCopy\"\n >\n Copy\n </button>\n <button\n type=\"button\"\n class=\"mint-sequence-input__tool-btn\"\n :disabled=\"!draft || disabled\"\n @click=\"doClear\"\n >\n Clear\n </button>\n </slot>\n </div>\n\n <!-- Editor (input mode) -->\n <div v-if=\"!readonly\" class=\"mint-sequence-input__editor\">\n <textarea\n :value=\"draft\"\n :rows=\"rows\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n class=\"mint-sequence-input__textarea\"\n spellcheck=\"false\"\n autocomplete=\"off\"\n @input=\"handleInput\"\n />\n </div>\n\n <!-- Viewer (readonly mode) -->\n <div v-else class=\"mint-sequence-input__viewer\">\n <div\n v-for=\"(line, lineIdx) in viewerLines\"\n :key=\"lineIdx\"\n class=\"mint-sequence-input__viewer-line\"\n >\n <span class=\"mint-sequence-input__line-number\">{{ lineIdx * 60 + 1 }}</span>\n <span class=\"mint-sequence-input__line-content\">\n <span\n v-for=\"(char, charIdx) in line.split('')\"\n :key=\"charIdx\"\n :class=\"getBaseClass(char)\"\n >{{ char }}</span>\n </span>\n </div>\n <div v-if=\"!modelValue\" class=\"mint-sequence-input__viewer-line\">\n <span class=\"mint-sequence-input__line-number\" />\n <span class=\"mint-sequence-input__line-content\" style=\"color: var(--text-muted);\">No sequence</span>\n </div>\n </div>\n\n <!-- Stats and problems -->\n <div\n v-if=\"(showStats && modelValue) || problems.ambiguousBases || hasInvalid\"\n class=\"mint-sequence-input__stats\"\n >\n <template v-if=\"showStats && modelValue\">\n <span>\n <span class=\"mint-sequence-input__stat-label\">Length:</span>\n <span class=\"mint-sequence-input__stat-value\">{{ stats.length }}</span>\n </span>\n <span v-if=\"stats.gcPercent !== undefined\">\n <span class=\"mint-sequence-input__stat-label\">GC:</span>\n <span class=\"mint-sequence-input__stat-value\">{{ stats.gcPercent.toFixed(1) }}%</span>\n </span>\n <span v-if=\"stats.molecularWeight !== undefined\">\n <span class=\"mint-sequence-input__stat-label\">MW:</span>\n <span class=\"mint-sequence-input__stat-value\">~{{ (stats.molecularWeight / 1000).toFixed(1) }} kDa</span>\n </span>\n </template>\n <span v-if=\"problems.ambiguousBases\" class=\"mint-sequence-input__problem mint-sequence-input__problem--warning\">\n <svg class=\"mint-sequence-input__problem-icon\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path class=\"mint-sequence-input__problem-shape\" d=\"M8 1.5l7 12.5H1z\" />\n <path class=\"mint-sequence-input__problem-mark\" d=\"M8 6v3.5M8 11.5v.01\" />\n </svg>\n {{ problems.ambiguousBases }} ambiguous bases (N)\n </span>\n <span v-if=\"hasInvalid\" role=\"alert\" class=\"mint-sequence-input__problem mint-sequence-input__problem--error\">\n <svg class=\"mint-sequence-input__problem-icon\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <circle class=\"mint-sequence-input__problem-shape\" cx=\"8\" cy=\"8\" r=\"7\" />\n <path class=\"mint-sequence-input__problem-mark\" d=\"M8 4.5v4M8 11.5v.01\" />\n </svg>\n Invalid characters: {{ problems.invalidCharacters.join(' ') }}\n </span>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Interactive chemical structure editor powered by JSME, emitting SMILES strings for compound registration in experiment design. */\nimport { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'\nimport type { MoleculeData } from '../types'\n\ninterface Props {\n modelValue?: MoleculeData\n disabled?: boolean\n readonly?: boolean\n height?: number\n showSmiles?: boolean\n placeholder?: string\n invalid?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n readonly: false,\n height: 300,\n showSmiles: true,\n placeholder: 'Draw a chemical structure',\n invalid: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [data: MoleculeData | undefined]\n 'error': [message: string]\n}>()\n\n// State\nconst containerRef = ref<HTMLDivElement | null>(null)\nconst jsmeInstance = ref<unknown>(null)\nconst isLoading = ref(true)\nconst loadError = ref<string | null>(null)\nconst debounceTimer = ref<ReturnType<typeof setTimeout> | null>(null)\n\n// Computed\nconst hasStructure = computed(() => {\n return props.modelValue?.smiles && props.modelValue.smiles.length > 0\n})\n\n// Use window-level state to persist across component instances and hot-reloads\ninterface JSMEGlobalState {\n __jsmeCallbacks__?: Array<() => void>\n __jsmeLoading__?: boolean\n __jsmeLoaded__?: boolean\n __jsmeLoadPromise__?: Promise<void>\n JSApplet?: { JSME?: new (id: string, width: string, height: string, options?: object) => unknown }\n jsmeOnLoad?: () => void\n}\n\nfunction getJSMEState(): JSMEGlobalState {\n const win = window as unknown as JSMEGlobalState\n if (!win.__jsmeCallbacks__) {\n win.__jsmeCallbacks__ = []\n }\n return win\n}\n\nfunction waitForJSME(): Promise<void> {\n const win = getJSMEState()\n\n // Already loaded\n if (win.JSApplet?.JSME) {\n return Promise.resolve()\n }\n\n // Reuse existing promise if loading\n if (win.__jsmeLoadPromise__) {\n return win.__jsmeLoadPromise__\n }\n\n win.__jsmeLoadPromise__ = new Promise((resolve, reject) => {\n // Double-check after promise creation\n if (win.JSApplet?.JSME) {\n resolve()\n return\n }\n\n // Set up global callback FIRST (before checking for existing script)\n const originalOnLoad = win.jsmeOnLoad\n win.jsmeOnLoad = () => {\n win.__jsmeLoaded__ = true\n originalOnLoad?.()\n win.__jsmeCallbacks__?.forEach(cb => cb())\n win.__jsmeCallbacks__ = []\n resolve()\n }\n\n // Add to callback queue\n win.__jsmeCallbacks__?.push(resolve)\n\n // Drop a failed script and the cached promise so a later mount retries from scratch.\n const fail = (script: Element, message: string) => {\n if (!script.isConnected) return // already failed (timeout, then a late error)\n win.__jsmeLoading__ = false\n win.__jsmeLoadPromise__ = undefined\n script.remove()\n reject(new Error(message))\n }\n\n // Check if script already exists\n const existingScript = document.querySelector('script[data-jsme]')\n if (existingScript) {\n // Script exists, poll for JSApplet.JSME (in case jsmeOnLoad already fired)\n const checkReady = setInterval(() => {\n if (win.JSApplet?.JSME) {\n clearInterval(checkReady)\n win.__jsmeLoaded__ = true\n win.__jsmeCallbacks__?.forEach(cb => cb())\n win.__jsmeCallbacks__ = []\n resolve()\n }\n }, 100)\n\n setTimeout(() => {\n clearInterval(checkReady)\n if (!win.__jsmeLoaded__ && !win.JSApplet?.JSME) {\n fail(existingScript, 'JSME initialization timeout')\n }\n }, 15000)\n return\n }\n\n // Load the script\n win.__jsmeLoading__ = true\n const script = document.createElement('script')\n script.src = 'https://jsme-editor.github.io/dist/jsme/jsme.nocache.js'\n script.integrity = 'sha384-l6tNzsc/eAJ7uql0dGAcHYI5ANVEV7DrJYjzXp3t13L+3OzLnfpzJO0Uio7mUSjY'\n script.crossOrigin = 'anonymous'\n script.async = true\n script.setAttribute('data-jsme', 'true')\n\n script.onerror = () => fail(script, 'Failed to load JSME script')\n\n // Timeout\n setTimeout(() => {\n if (!win.__jsmeLoaded__ && !win.JSApplet?.JSME) {\n fail(script, 'JSME initialization timeout')\n }\n }, 15000)\n\n document.head.appendChild(script)\n })\n\n return win.__jsmeLoadPromise__\n}\n\n// Wait for the browser to complete a paint cycle\nfunction waitForPaint(): Promise<void> {\n return new Promise(resolve => {\n requestAnimationFrame(() => requestAnimationFrame(() => resolve()))\n })\n}\n\n// JSME initialization — two phases:\n// 1. Load the script (no DOM needed)\n// 2. Reveal the editor div, wait for paint, then mount JSME with explicit px dimensions\nasync function initJSME() {\n if (props.readonly) return\n\n try {\n isLoading.value = true\n loadError.value = null\n\n // Phase 1: load the JSME script (independent of DOM)\n await waitForJSME()\n\n const win = getJSMEState()\n if (!win.JSApplet?.JSME) {\n throw new Error('JSME library not available after loading')\n }\n\n // Phase 2: reveal the editor div by clearing the loading state\n isLoading.value = false\n await nextTick()\n\n if (!containerRef.value) return\n\n // Wait for a full paint cycle so GWT can measure the container\n await waitForPaint()\n\n if (!containerRef.value) return\n\n // Use explicit pixel width — GWT cannot resolve percentage widths reliably\n const rect = containerRef.value.getBoundingClientRect()\n const width = Math.floor(rect.width) || 400\n\n // Mount JSME into the now-painted container\n const editorId = `jsme-${Date.now()}`\n containerRef.value.id = editorId\n\n const instance = new win.JSApplet.JSME(\n editorId,\n `${width}px`,\n `${props.height}px`,\n {\n options: 'query,hydrogens,paste',\n }\n )\n\n jsmeInstance.value = instance\n\n // Set initial value if provided\n if (props.modelValue?.molfile) {\n (instance as { readMolFile: (mol: string) => void }).readMolFile(props.modelValue.molfile)\n }\n\n // Set up change callback\n (instance as { setCallBack: (event: string, callback: () => void) => void }).setCallBack('AfterStructureModified', handleStructureChange)\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to load molecule editor'\n loadError.value = message\n emit('error', message)\n isLoading.value = false\n }\n}\n\nfunction handleStructureChange() {\n // Debounce the change event\n if (debounceTimer.value) {\n clearTimeout(debounceTimer.value)\n }\n\n debounceTimer.value = setTimeout(() => {\n if (!jsmeInstance.value) return\n\n const instance = jsmeInstance.value as {\n smiles: () => string\n molFile: () => string\n }\n\n const smiles = instance.smiles()\n const molfile = instance.molFile()\n\n if (!smiles || smiles.length === 0) {\n emit('update:modelValue', undefined)\n } else {\n emit('update:modelValue', { smiles, molfile })\n }\n }, 300)\n}\n\nfunction clearStructure() {\n if (jsmeInstance.value) {\n (jsmeInstance.value as { reset: () => void }).reset()\n }\n emit('update:modelValue', undefined)\n}\n\n// Watch for external value changes\nwatch(() => props.modelValue, (newValue) => {\n if (!jsmeInstance.value) return\n\n const instance = jsmeInstance.value as {\n smiles: () => string\n readMolFile: (mol: string) => void\n reset: () => void\n }\n\n // Only update if the external value differs from current\n const currentSmiles = instance.smiles()\n if (newValue?.smiles !== currentSmiles) {\n if (newValue?.molfile) {\n instance.readMolFile(newValue.molfile)\n } else {\n instance.reset()\n }\n }\n})\n\n// Lifecycle\nonMounted(() => {\n if (!props.readonly) {\n initJSME()\n } else {\n isLoading.value = false\n }\n})\n\nonUnmounted(() => {\n if (debounceTimer.value) {\n clearTimeout(debounceTimer.value)\n }\n jsmeInstance.value = null\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-molecule-input',\n disabled ? 'mint-molecule-input--disabled' : '',\n readonly ? 'mint-molecule-input--readonly' : '',\n invalid ? 'mint-molecule-input--error' : '',\n ]\"\n >\n <!-- Loading state -->\n <div\n v-if=\"isLoading && !readonly\"\n class=\"mint-molecule-input__skeleton\"\n :style=\"{ height: `${height}px` }\"\n >\n <svg\n class=\"mint-molecule-input__skeleton-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\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 <span class=\"mint-molecule-input__skeleton-text\">Loading molecule editor...</span>\n </div>\n\n <!-- Error state -->\n <div\n v-else-if=\"loadError\"\n class=\"mint-molecule-input__error\"\n >\n <svg\n class=\"mint-molecule-input__error-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n 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 />\n </svg>\n {{ loadError }}\n </div>\n\n <!-- Readonly mode - show empty or placeholder -->\n <template v-else-if=\"readonly\">\n <div\n v-if=\"hasStructure\"\n class=\"mint-molecule-input__readonly\"\n :style=\"{ height: `${height}px` }\"\n >\n <!-- In readonly mode, we just display a placeholder since we don't have SVG rendering -->\n <div class=\"mint-molecule-input__empty\">\n <svg\n class=\"mint-molecule-input__empty-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\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 <span class=\"mint-molecule-input__empty-text\">Structure defined (readonly)</span>\n </div>\n </div>\n <div\n v-else\n class=\"mint-molecule-input__empty\"\n :style=\"{ height: `${height}px` }\"\n >\n <svg\n class=\"mint-molecule-input__empty-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\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 <span class=\"mint-molecule-input__empty-text\">No structure</span>\n </div>\n </template>\n\n <!-- Editor mode -->\n <template v-else>\n <div\n ref=\"containerRef\"\n class=\"mint-molecule-input__editor\"\n :style=\"{ height: `${height}px` }\"\n role=\"application\"\n aria-label=\"Molecule structure editor\"\n />\n\n <!-- Actions toolbar -->\n <div class=\"mint-molecule-input__actions\">\n <button\n type=\"button\"\n class=\"mint-molecule-input__action-btn\"\n :disabled=\"!hasStructure || disabled\"\n aria-label=\"Clear structure\"\n @click=\"clearStructure\"\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 </div>\n </template>\n\n <!-- SMILES display -->\n <div\n v-if=\"showSmiles && hasStructure && !loadError\"\n class=\"mint-molecule-input__smiles\"\n >\n <span class=\"mint-molecule-input__smiles-label\">SMILES:</span>\n {{ modelValue?.smiles }}\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Interactive chemical structure editor powered by JSME, emitting SMILES strings for compound registration in experiment design. */\nimport { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'\nimport type { MoleculeData } from '../types'\n\ninterface Props {\n modelValue?: MoleculeData\n disabled?: boolean\n readonly?: boolean\n height?: number\n showSmiles?: boolean\n placeholder?: string\n invalid?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n readonly: false,\n height: 300,\n showSmiles: true,\n placeholder: 'Draw a chemical structure',\n invalid: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [data: MoleculeData | undefined]\n 'error': [message: string]\n}>()\n\n// State\nconst containerRef = ref<HTMLDivElement | null>(null)\nconst jsmeInstance = ref<unknown>(null)\nconst isLoading = ref(true)\nconst loadError = ref<string | null>(null)\nconst debounceTimer = ref<ReturnType<typeof setTimeout> | null>(null)\n\n// Computed\nconst hasStructure = computed(() => {\n return props.modelValue?.smiles && props.modelValue.smiles.length > 0\n})\n\n// Use window-level state to persist across component instances and hot-reloads\ninterface JSMEGlobalState {\n __jsmeCallbacks__?: Array<() => void>\n __jsmeLoading__?: boolean\n __jsmeLoaded__?: boolean\n __jsmeLoadPromise__?: Promise<void>\n JSApplet?: { JSME?: new (id: string, width: string, height: string, options?: object) => unknown }\n jsmeOnLoad?: () => void\n}\n\nfunction getJSMEState(): JSMEGlobalState {\n const win = window as unknown as JSMEGlobalState\n if (!win.__jsmeCallbacks__) {\n win.__jsmeCallbacks__ = []\n }\n return win\n}\n\nfunction waitForJSME(): Promise<void> {\n const win = getJSMEState()\n\n // Already loaded\n if (win.JSApplet?.JSME) {\n return Promise.resolve()\n }\n\n // Reuse existing promise if loading\n if (win.__jsmeLoadPromise__) {\n return win.__jsmeLoadPromise__\n }\n\n win.__jsmeLoadPromise__ = new Promise((resolve, reject) => {\n // Double-check after promise creation\n if (win.JSApplet?.JSME) {\n resolve()\n return\n }\n\n // Set up global callback FIRST (before checking for existing script)\n const originalOnLoad = win.jsmeOnLoad\n win.jsmeOnLoad = () => {\n win.__jsmeLoaded__ = true\n originalOnLoad?.()\n win.__jsmeCallbacks__?.forEach(cb => cb())\n win.__jsmeCallbacks__ = []\n resolve()\n }\n\n // Add to callback queue\n win.__jsmeCallbacks__?.push(resolve)\n\n // Drop a failed script and the cached promise so a later mount retries from scratch.\n const fail = (script: Element, message: string) => {\n if (!script.isConnected) return // already failed (timeout, then a late error)\n win.__jsmeLoading__ = false\n win.__jsmeLoadPromise__ = undefined\n script.remove()\n reject(new Error(message))\n }\n\n // Check if script already exists\n const existingScript = document.querySelector('script[data-jsme]')\n if (existingScript) {\n // Script exists, poll for JSApplet.JSME (in case jsmeOnLoad already fired)\n const checkReady = setInterval(() => {\n if (win.JSApplet?.JSME) {\n clearInterval(checkReady)\n win.__jsmeLoaded__ = true\n win.__jsmeCallbacks__?.forEach(cb => cb())\n win.__jsmeCallbacks__ = []\n resolve()\n }\n }, 100)\n\n setTimeout(() => {\n clearInterval(checkReady)\n if (!win.__jsmeLoaded__ && !win.JSApplet?.JSME) {\n fail(existingScript, 'JSME initialization timeout')\n }\n }, 15000)\n return\n }\n\n // Load the script\n win.__jsmeLoading__ = true\n const script = document.createElement('script')\n script.src = 'https://jsme-editor.github.io/dist/jsme/jsme.nocache.js'\n script.integrity = 'sha384-l6tNzsc/eAJ7uql0dGAcHYI5ANVEV7DrJYjzXp3t13L+3OzLnfpzJO0Uio7mUSjY'\n script.crossOrigin = 'anonymous'\n script.async = true\n script.setAttribute('data-jsme', 'true')\n\n script.onerror = () => fail(script, 'Failed to load JSME script')\n\n // Timeout\n setTimeout(() => {\n if (!win.__jsmeLoaded__ && !win.JSApplet?.JSME) {\n fail(script, 'JSME initialization timeout')\n }\n }, 15000)\n\n document.head.appendChild(script)\n })\n\n return win.__jsmeLoadPromise__\n}\n\n// Wait for the browser to complete a paint cycle\nfunction waitForPaint(): Promise<void> {\n return new Promise(resolve => {\n requestAnimationFrame(() => requestAnimationFrame(() => resolve()))\n })\n}\n\n// JSME initialization — two phases:\n// 1. Load the script (no DOM needed)\n// 2. Reveal the editor div, wait for paint, then mount JSME with explicit px dimensions\nasync function initJSME() {\n if (props.readonly) return\n\n try {\n isLoading.value = true\n loadError.value = null\n\n // Phase 1: load the JSME script (independent of DOM)\n await waitForJSME()\n\n const win = getJSMEState()\n if (!win.JSApplet?.JSME) {\n throw new Error('JSME library not available after loading')\n }\n\n // Phase 2: reveal the editor div by clearing the loading state\n isLoading.value = false\n await nextTick()\n\n if (!containerRef.value) return\n\n // Wait for a full paint cycle so GWT can measure the container\n await waitForPaint()\n\n if (!containerRef.value) return\n\n // Use explicit pixel width — GWT cannot resolve percentage widths reliably\n const rect = containerRef.value.getBoundingClientRect()\n const width = Math.floor(rect.width) || 400\n\n // Mount JSME into the now-painted container\n const editorId = `jsme-${Date.now()}`\n containerRef.value.id = editorId\n\n const instance = new win.JSApplet.JSME(\n editorId,\n `${width}px`,\n `${props.height}px`,\n {\n options: 'query,hydrogens,paste',\n }\n )\n\n jsmeInstance.value = instance\n\n // Set initial value if provided\n if (props.modelValue?.molfile) {\n (instance as { readMolFile: (mol: string) => void }).readMolFile(props.modelValue.molfile)\n }\n\n // Set up change callback\n (instance as { setCallBack: (event: string, callback: () => void) => void }).setCallBack('AfterStructureModified', handleStructureChange)\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to load molecule editor'\n loadError.value = message\n emit('error', message)\n isLoading.value = false\n }\n}\n\nfunction handleStructureChange() {\n // Debounce the change event\n if (debounceTimer.value) {\n clearTimeout(debounceTimer.value)\n }\n\n debounceTimer.value = setTimeout(() => {\n if (!jsmeInstance.value) return\n\n const instance = jsmeInstance.value as {\n smiles: () => string\n molFile: () => string\n }\n\n const smiles = instance.smiles()\n const molfile = instance.molFile()\n\n if (!smiles || smiles.length === 0) {\n emit('update:modelValue', undefined)\n } else {\n emit('update:modelValue', { smiles, molfile })\n }\n }, 300)\n}\n\nfunction clearStructure() {\n if (jsmeInstance.value) {\n (jsmeInstance.value as { reset: () => void }).reset()\n }\n emit('update:modelValue', undefined)\n}\n\n// Watch for external value changes\nwatch(() => props.modelValue, (newValue) => {\n if (!jsmeInstance.value) return\n\n const instance = jsmeInstance.value as {\n smiles: () => string\n readMolFile: (mol: string) => void\n reset: () => void\n }\n\n // Only update if the external value differs from current\n const currentSmiles = instance.smiles()\n if (newValue?.smiles !== currentSmiles) {\n if (newValue?.molfile) {\n instance.readMolFile(newValue.molfile)\n } else {\n instance.reset()\n }\n }\n})\n\n// Lifecycle\nonMounted(() => {\n if (!props.readonly) {\n initJSME()\n } else {\n isLoading.value = false\n }\n})\n\nonUnmounted(() => {\n if (debounceTimer.value) {\n clearTimeout(debounceTimer.value)\n }\n jsmeInstance.value = null\n})\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-molecule-input',\n disabled ? 'mint-molecule-input--disabled' : '',\n readonly ? 'mint-molecule-input--readonly' : '',\n invalid ? 'mint-molecule-input--error' : '',\n ]\"\n >\n <!-- Loading state -->\n <div\n v-if=\"isLoading && !readonly\"\n class=\"mint-molecule-input__skeleton\"\n :style=\"{ height: `${height}px` }\"\n >\n <svg\n class=\"mint-molecule-input__skeleton-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\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 <span class=\"mint-molecule-input__skeleton-text\">Loading molecule editor...</span>\n </div>\n\n <!-- Error state -->\n <div\n v-else-if=\"loadError\"\n class=\"mint-molecule-input__error\"\n >\n <svg\n class=\"mint-molecule-input__error-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n 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 />\n </svg>\n {{ loadError }}\n </div>\n\n <!-- Readonly mode - show empty or placeholder -->\n <template v-else-if=\"readonly\">\n <div\n v-if=\"hasStructure\"\n class=\"mint-molecule-input__readonly\"\n :style=\"{ height: `${height}px` }\"\n >\n <!-- In readonly mode, we just display a placeholder since we don't have SVG rendering -->\n <div class=\"mint-molecule-input__empty\">\n <svg\n class=\"mint-molecule-input__empty-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\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 <span class=\"mint-molecule-input__empty-text\">Structure defined (readonly)</span>\n </div>\n </div>\n <div\n v-else\n class=\"mint-molecule-input__empty\"\n :style=\"{ height: `${height}px` }\"\n >\n <svg\n class=\"mint-molecule-input__empty-icon\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\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 <span class=\"mint-molecule-input__empty-text\">No structure</span>\n </div>\n </template>\n\n <!-- Editor mode -->\n <template v-else>\n <div\n ref=\"containerRef\"\n class=\"mint-molecule-input__editor\"\n :style=\"{ height: `${height}px` }\"\n role=\"application\"\n aria-label=\"Molecule structure editor\"\n />\n\n <!-- Actions toolbar -->\n <div class=\"mint-molecule-input__actions\">\n <button\n type=\"button\"\n class=\"mint-molecule-input__action-btn\"\n :disabled=\"!hasStructure || disabled\"\n aria-label=\"Clear structure\"\n @click=\"clearStructure\"\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 </div>\n </template>\n\n <!-- SMILES display -->\n <div\n v-if=\"showSmiles && hasStructure && !loadError\"\n class=\"mint-molecule-input__smiles\"\n >\n <span class=\"mint-molecule-input__smiles-label\">SMILES:</span>\n {{ modelValue?.smiles }}\n </div>\n </div>\n</template>\n","import { computed, type ComputedRef } from 'vue'\n\n// Concentration unit types\nexport type MolarityUnit = 'pM' | 'nM' | 'µM' | 'mM' | 'M'\nexport type MassVolumeUnit = 'pg/mL' | 'ng/mL' | 'µg/mL' | 'mg/mL' | 'g/mL'\nexport type PercentageUnit = '% v/v' | '% w/v' | '% w/w'\nexport type ConcentrationUnit = MolarityUnit | MassVolumeUnit | PercentageUnit | string\n\nexport interface ConcentrationValue {\n value: number\n unit: ConcentrationUnit\n}\n\nexport interface UnitCategory {\n label: string\n units: ConcentrationUnit[]\n}\n\nexport interface UseConcentrationUnitsReturn {\n unitCategories: ComputedRef<UnitCategory[]>\n molarityUnits: MolarityUnit[]\n massVolumeUnits: MassVolumeUnit[]\n percentageUnits: PercentageUnit[]\n convert: (value: number, from: ConcentrationUnit, to: ConcentrationUnit, mw?: number) => number | null\n formatWithUnit: (concentration: ConcentrationValue, precision?: number) => string\n parseConcentration: (input: string) => ConcentrationValue | null\n getBaseUnit: (unit: ConcentrationUnit) => ConcentrationUnit\n getConversionHint: (concentration: ConcentrationValue) => string | null\n isMolarity: (unit: ConcentrationUnit) => boolean\n isMassVolume: (unit: ConcentrationUnit) => boolean\n isPercentage: (unit: ConcentrationUnit) => boolean\n}\n\n// Conversion factors to base unit (M for molarity, g/mL for mass/volume)\nconst MOLARITY_FACTORS: Record<MolarityUnit, number> = {\n 'pM': 1e-12,\n 'nM': 1e-9,\n 'µM': 1e-6,\n 'mM': 1e-3,\n 'M': 1,\n}\n\nconst MASS_VOLUME_FACTORS: Record<MassVolumeUnit, number> = {\n 'pg/mL': 1e-12,\n 'ng/mL': 1e-9,\n 'µg/mL': 1e-6,\n 'mg/mL': 1e-3,\n 'g/mL': 1,\n}\n\n// Percentage conversion factors (for potential future use)\n// const PERCENTAGE_FACTORS: Record<PercentageUnit, number> = {\n// '% v/v': 0.01,\n// '% w/v': 0.01,\n// '% w/w': 0.01,\n// }\n\nconst MOLARITY_UNITS: MolarityUnit[] = ['pM', 'nM', 'µM', 'mM', 'M']\nconst MASS_VOLUME_UNITS: MassVolumeUnit[] = ['pg/mL', 'ng/mL', 'µg/mL', 'mg/mL', 'g/mL']\nconst PERCENTAGE_UNITS: PercentageUnit[] = ['% v/v', '% w/v', '% w/w']\n\n/** Converts between molarity, mass/volume, and percentage concentration units with formatting helpers. */\nexport function useConcentrationUnits(): UseConcentrationUnitsReturn {\n const unitCategories = computed<UnitCategory[]>(() => [\n { label: 'Molarity', units: MOLARITY_UNITS },\n { label: 'Mass/Volume', units: MASS_VOLUME_UNITS },\n { label: 'Percentage', units: PERCENTAGE_UNITS },\n ])\n\n function isMolarity(unit: ConcentrationUnit): unit is MolarityUnit {\n return MOLARITY_UNITS.includes(unit as MolarityUnit)\n }\n\n function isMassVolume(unit: ConcentrationUnit): unit is MassVolumeUnit {\n return MASS_VOLUME_UNITS.includes(unit as MassVolumeUnit)\n }\n\n function isPercentage(unit: ConcentrationUnit): unit is PercentageUnit {\n return PERCENTAGE_UNITS.includes(unit as PercentageUnit)\n }\n\n function getBaseUnit(unit: ConcentrationUnit): ConcentrationUnit {\n if (isMolarity(unit)) return 'M'\n if (isMassVolume(unit)) return 'g/mL'\n if (isPercentage(unit)) return '% w/v'\n return unit\n }\n\n function convert(\n value: number,\n from: ConcentrationUnit,\n to: ConcentrationUnit,\n mw?: number\n ): number | null {\n if (from === to) return value\n if (value === 0) return 0\n\n // Same category conversion\n if (isMolarity(from) && isMolarity(to)) {\n const baseValue = value * MOLARITY_FACTORS[from]\n return baseValue / MOLARITY_FACTORS[to]\n }\n\n if (isMassVolume(from) && isMassVolume(to)) {\n const baseValue = value * MASS_VOLUME_FACTORS[from]\n return baseValue / MASS_VOLUME_FACTORS[to]\n }\n\n if (isPercentage(from) && isPercentage(to)) {\n // Percentage conversions within same category are direct\n return value\n }\n\n // Cross-category conversion (requires molecular weight)\n if (mw && mw > 0) {\n // Molarity to Mass/Volume\n if (isMolarity(from) && isMassVolume(to)) {\n // Convert to M, then to g/mL using MW, then to target unit\n const molarConc = value * MOLARITY_FACTORS[from]\n const massConc = molarConc * mw // g/mL (MW in g/mol, concentration in mol/L = g/L)\n return massConc / MASS_VOLUME_FACTORS[to]\n }\n\n // Mass/Volume to Molarity\n if (isMassVolume(from) && isMolarity(to)) {\n // Convert to g/mL, then to M using MW, then to target unit\n const massConc = value * MASS_VOLUME_FACTORS[from]\n const molarConc = massConc / mw // M (g/mL / g/mol = mol/L)\n return molarConc / MOLARITY_FACTORS[to]\n }\n }\n\n return null // Cannot convert without MW or between incompatible units\n }\n\n function formatWithUnit(concentration: ConcentrationValue, precision: number = 3): string {\n const { value, unit } = concentration\n if (value === 0) return `0 ${unit}`\n\n // Use appropriate precision based on value magnitude\n let formattedValue: string\n if (Math.abs(value) >= 1000) {\n formattedValue = value.toExponential(precision - 1)\n } else if (Math.abs(value) < 0.001) {\n formattedValue = value.toExponential(precision - 1)\n } else {\n formattedValue = value.toPrecision(precision)\n }\n\n // Remove trailing zeros after decimal point\n formattedValue = formattedValue.replace(/\\.?0+$/, '')\n formattedValue = formattedValue.replace(/\\.?0+e/, 'e')\n\n return `${formattedValue} ${unit}`\n }\n\n function parseConcentration(input: string): ConcentrationValue | null {\n const trimmed = input.trim()\n if (!trimmed) return null\n\n // Match number followed by optional unit\n const match = trimmed.match(/^([-+]?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)\\s*(.*)$/)\n if (!match) return null\n\n const value = parseFloat(match[1])\n if (isNaN(value)) return null\n\n let unit = match[2].trim()\n if (!unit) {\n return null // Unit is required\n }\n\n // Normalize common variations\n unit = unit\n .replace(/uM/i, 'µM')\n .replace(/um/i, 'µM')\n .replace(/ug\\/ml/i, 'µg/mL')\n .replace(/ng\\/ml/i, 'ng/mL')\n .replace(/mg\\/ml/i, 'mg/mL')\n .replace(/pg\\/ml/i, 'pg/mL')\n .replace(/g\\/ml/i, 'g/mL')\n\n return { value, unit }\n }\n\n function getConversionHint(concentration: ConcentrationValue): string | null {\n const { value, unit } = concentration\n if (value === 0) return null\n\n // Find adjacent unit for conversion hint\n if (isMolarity(unit)) {\n const index = MOLARITY_UNITS.indexOf(unit)\n // If value is large, suggest next higher unit\n if (value >= 1000 && index < MOLARITY_UNITS.length - 1) {\n const nextUnit = MOLARITY_UNITS[index + 1]\n const converted = convert(value, unit, nextUnit)\n if (converted !== null) {\n return formatWithUnit({ value: converted, unit: nextUnit })\n }\n }\n // If value is small, suggest next lower unit\n if (value < 1 && index > 0) {\n const prevUnit = MOLARITY_UNITS[index - 1]\n const converted = convert(value, unit, prevUnit)\n if (converted !== null) {\n return formatWithUnit({ value: converted, unit: prevUnit })\n }\n }\n }\n\n if (isMassVolume(unit)) {\n const index = MASS_VOLUME_UNITS.indexOf(unit)\n if (value >= 1000 && index < MASS_VOLUME_UNITS.length - 1) {\n const nextUnit = MASS_VOLUME_UNITS[index + 1]\n const converted = convert(value, unit, nextUnit)\n if (converted !== null) {\n return formatWithUnit({ value: converted, unit: nextUnit })\n }\n }\n if (value < 1 && index > 0) {\n const prevUnit = MASS_VOLUME_UNITS[index - 1]\n const converted = convert(value, unit, prevUnit)\n if (converted !== null) {\n return formatWithUnit({ value: converted, unit: prevUnit })\n }\n }\n }\n\n return null\n }\n\n return {\n unitCategories,\n molarityUnits: MOLARITY_UNITS,\n massVolumeUnits: MASS_VOLUME_UNITS,\n percentageUnits: PERCENTAGE_UNITS,\n convert,\n formatWithUnit,\n parseConcentration,\n getBaseUnit,\n getConversionHint,\n isMolarity,\n isMassVolume,\n isPercentage,\n }\n}\n","<script setup lang=\"ts\">\n/** Paired numeric + unit input for reagent concentrations (pM–M, mg/mL, etc.) with optional molar-mass conversion hint. */\nimport { computed, watch } from 'vue'\nimport {\n useConcentrationUnits,\n type ConcentrationValue,\n type ConcentrationUnit,\n} from '../composables/useConcentrationUnits'\nimport NumberInput from './NumberInput.vue'\n\ninterface Props {\n modelValue?: ConcentrationValue\n allowedUnits?: ConcentrationUnit[]\n showConversion?: boolean\n molecularWeight?: number\n min?: number\n max?: number\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n placeholder?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showConversion: true,\n disabled: false,\n invalid: false,\n size: 'md',\n placeholder: 'Enter value',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: ConcentrationValue | undefined]\n}>()\n\nconst { unitCategories, convert, getBaseUnit, isMolarity, isMassVolume } = useConcentrationUnits()\n\n/** Scale factor to the category base unit; NumberInput uses it for menu previews and the nearest-unit hint. */\nfunction factorOf(unit: ConcentrationUnit): number | undefined {\n if (!props.showConversion || !(isMolarity(unit) || isMassVolume(unit))) return undefined\n return convert(1, unit, getBaseUnit(unit)) ?? undefined\n}\n\n// Filter categories based on allowedUnits\nconst filteredCategories = computed(() => {\n const allowedUnits = props.allowedUnits\n if (!allowedUnits || allowedUnits.length === 0) {\n return unitCategories.value\n }\n return unitCategories.value\n .map(cat => ({\n label: cat.label,\n units: cat.units.filter(u => allowedUnits.includes(u)),\n }))\n .filter(cat => cat.units.length > 0)\n})\n\n// Flatten all available units\nconst availableUnits = computed(() => {\n return filteredCategories.value.flatMap(cat => cat.units)\n})\n\nconst unitOptions = computed(() =>\n filteredCategories.value.flatMap(category =>\n category.units.map(unit => ({\n value: unit,\n label: unit,\n group: filteredCategories.value.length > 1 ? category.label : undefined,\n factor: factorOf(unit),\n }))\n )\n)\n\n// Current value and unit\nconst currentValue = computed(() => props.modelValue?.value)\nconst currentUnit = computed(() => props.modelValue?.unit || availableUnits.value[0] || 'µM')\n\nfunction handleValueUpdate(value: number | null) {\n if (value === null) {\n emit('update:modelValue', undefined)\n return\n }\n\n // Apply min/max constraints\n let clampedValue = value\n if (props.min !== undefined && clampedValue < props.min) {\n clampedValue = props.min\n }\n if (props.max !== undefined && clampedValue > props.max) {\n clampedValue = props.max\n }\n\n emit('update:modelValue', {\n value: clampedValue,\n unit: currentUnit.value,\n })\n}\n\nfunction handleUnitUpdate(unit: string) {\n emit('update:modelValue', {\n value: currentValue.value ?? 0,\n unit: unit as ConcentrationUnit,\n })\n}\n\n// Ensure modelValue always has a valid unit\nwatch(availableUnits, (units) => {\n if (props.modelValue && !units.includes(props.modelValue.unit)) {\n emit('update:modelValue', {\n value: props.modelValue.value,\n unit: units[0] || 'µM',\n })\n }\n}, { immediate: true })\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-concentration-input',\n invalid ? 'mint-concentration-input--error' : '',\n disabled ? 'mint-concentration-input--disabled' : '',\n ]\"\n >\n <NumberInput\n :model-value=\"currentValue\"\n :unit=\"currentUnit\"\n :units=\"unitOptions\"\n :min=\"min\"\n :max=\"max\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n :size=\"size\"\n :placeholder=\"placeholder\"\n @update:model-value=\"handleValueUpdate\"\n @update:unit=\"handleUnitUpdate\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Paired numeric + unit input for reagent concentrations (pM–M, mg/mL, etc.) with optional molar-mass conversion hint. */\nimport { computed, watch } from 'vue'\nimport {\n useConcentrationUnits,\n type ConcentrationValue,\n type ConcentrationUnit,\n} from '../composables/useConcentrationUnits'\nimport NumberInput from './NumberInput.vue'\n\ninterface Props {\n modelValue?: ConcentrationValue\n allowedUnits?: ConcentrationUnit[]\n showConversion?: boolean\n molecularWeight?: number\n min?: number\n max?: number\n disabled?: boolean\n invalid?: boolean\n size?: 'sm' | 'md' | 'lg'\n placeholder?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showConversion: true,\n disabled: false,\n invalid: false,\n size: 'md',\n placeholder: 'Enter value',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: ConcentrationValue | undefined]\n}>()\n\nconst { unitCategories, convert, getBaseUnit, isMolarity, isMassVolume } = useConcentrationUnits()\n\n/** Scale factor to the category base unit; NumberInput uses it for menu previews and the nearest-unit hint. */\nfunction factorOf(unit: ConcentrationUnit): number | undefined {\n if (!props.showConversion || !(isMolarity(unit) || isMassVolume(unit))) return undefined\n return convert(1, unit, getBaseUnit(unit)) ?? undefined\n}\n\n// Filter categories based on allowedUnits\nconst filteredCategories = computed(() => {\n const allowedUnits = props.allowedUnits\n if (!allowedUnits || allowedUnits.length === 0) {\n return unitCategories.value\n }\n return unitCategories.value\n .map(cat => ({\n label: cat.label,\n units: cat.units.filter(u => allowedUnits.includes(u)),\n }))\n .filter(cat => cat.units.length > 0)\n})\n\n// Flatten all available units\nconst availableUnits = computed(() => {\n return filteredCategories.value.flatMap(cat => cat.units)\n})\n\nconst unitOptions = computed(() =>\n filteredCategories.value.flatMap(category =>\n category.units.map(unit => ({\n value: unit,\n label: unit,\n group: filteredCategories.value.length > 1 ? category.label : undefined,\n factor: factorOf(unit),\n }))\n )\n)\n\n// Current value and unit\nconst currentValue = computed(() => props.modelValue?.value)\nconst currentUnit = computed(() => props.modelValue?.unit || availableUnits.value[0] || 'µM')\n\nfunction handleValueUpdate(value: number | null) {\n if (value === null) {\n emit('update:modelValue', undefined)\n return\n }\n\n // Apply min/max constraints\n let clampedValue = value\n if (props.min !== undefined && clampedValue < props.min) {\n clampedValue = props.min\n }\n if (props.max !== undefined && clampedValue > props.max) {\n clampedValue = props.max\n }\n\n emit('update:modelValue', {\n value: clampedValue,\n unit: currentUnit.value,\n })\n}\n\nfunction handleUnitUpdate(unit: string) {\n emit('update:modelValue', {\n value: currentValue.value ?? 0,\n unit: unit as ConcentrationUnit,\n })\n}\n\n// Ensure modelValue always has a valid unit\nwatch(availableUnits, (units) => {\n if (props.modelValue && !units.includes(props.modelValue.unit)) {\n emit('update:modelValue', {\n value: props.modelValue.value,\n unit: units[0] || 'µM',\n })\n }\n}, { immediate: true })\n</script>\n\n<template>\n <div\n :class=\"[\n 'mint-concentration-input',\n invalid ? 'mint-concentration-input--error' : '',\n disabled ? 'mint-concentration-input--disabled' : '',\n ]\"\n >\n <NumberInput\n :model-value=\"currentValue\"\n :unit=\"currentUnit\"\n :units=\"unitOptions\"\n :min=\"min\"\n :max=\"max\"\n :disabled=\"disabled\"\n :invalid=\"invalid\"\n :size=\"size\"\n :placeholder=\"placeholder\"\n @update:model-value=\"handleValueUpdate\"\n @update:unit=\"handleUnitUpdate\"\n />\n </div>\n</template>\n","import type { Component } from 'vue'\nimport type { FormFieldType } from '../types/form-builder'\n\nimport BaseInput from '../components/BaseInput.vue'\nimport SecretInput from '../components/SecretInput.vue'\nimport BaseSelect from '../components/BaseSelect.vue'\nimport BaseCheckbox from '../components/BaseCheckbox.vue'\nimport BaseRadioGroup from '../components/BaseRadioGroup.vue'\nimport BaseSlider from '../components/BaseSlider.vue'\nimport TagsInput from '../components/TagsInput.vue'\nimport NumberInput from '../components/NumberInput.vue'\nimport DatePicker from '../components/DatePicker.vue'\nimport FileUploader from '../components/FileUploader.vue'\nimport GeneratedPathInput from '../components/GeneratedPathInput.vue'\nimport FormulaInput from '../components/FormulaInput.vue'\nimport SequenceInput from '../components/SequenceInput.vue'\nimport MoleculeInput from '../components/MoleculeInput.vue'\nimport ConcentrationInput from '../components/ConcentrationInput.vue'\n\nexport interface RegistryEntry {\n component: Component\n /** Default props applied to the component. */\n defaults: Record<string, unknown>\n /** Whether the component uses v-model (false for event-only components like FileUploader). */\n vModel: boolean\n}\n\nconst registry: Record<FormFieldType, RegistryEntry> = {\n text: { component: BaseInput, defaults: { type: 'text' }, vModel: true },\n email: { component: BaseInput, defaults: { type: 'email' }, vModel: true },\n password: { component: BaseInput, defaults: { type: 'password' }, vModel: true },\n secret: { component: SecretInput, defaults: {}, vModel: true },\n tel: { component: BaseInput, defaults: { type: 'tel' }, vModel: true },\n url: { component: BaseInput, defaults: { type: 'url' }, vModel: true },\n search: { component: BaseInput, defaults: { type: 'search' }, vModel: true },\n number: { component: NumberInput, defaults: {}, vModel: true },\n textarea: { component: BaseInput, defaults: { type: 'textarea' }, vModel: true },\n select: { component: BaseSelect, defaults: {}, vModel: true },\n multiselect: { component: BaseSelect, defaults: { multiple: true }, vModel: true },\n checkbox: { component: BaseCheckbox, defaults: {}, vModel: true },\n toggle: { component: BaseCheckbox, defaults: { type: 'switch' }, vModel: true },\n radio: { component: BaseRadioGroup, defaults: {}, vModel: true },\n slider: { component: BaseSlider, defaults: {}, vModel: true },\n tags: { component: TagsInput, defaults: {}, vModel: true },\n date: { component: DatePicker, defaults: {}, vModel: true },\n time: { component: DatePicker, defaults: { type: 'time' }, vModel: true },\n datetime: { component: DatePicker, defaults: { type: 'datetime' }, vModel: true },\n file: { component: FileUploader, defaults: {}, vModel: false },\n path: { component: GeneratedPathInput, defaults: {}, vModel: true },\n formula: { component: FormulaInput, defaults: {}, vModel: true },\n sequence: { component: SequenceInput, defaults: {}, vModel: true },\n molecule: { component: MoleculeInput, defaults: {}, vModel: true },\n concentration: { component: ConcentrationInput, defaults: {}, vModel: true },\n unit: { component: NumberInput, defaults: {}, vModel: true },\n}\n\n/** Return the registry entry for a given field type. Throws if the type is unregistered. */\nexport function getFieldRegistryEntry(type: FormFieldType): RegistryEntry {\n return registry[type]\n}\n\n/** Get the default empty value for a given field type. */\nexport function getTypeDefault(type: FormFieldType): unknown {\n switch (type) {\n case 'checkbox':\n case 'toggle':\n return false\n case 'number':\n case 'slider':\n return undefined\n case 'multiselect':\n case 'tags':\n return []\n case 'secret':\n // A secret field has no empty string state: `null` means \"not stored\".\n return null\n default:\n return ''\n }\n}\n","export function recordValue(value: unknown): Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? value as Record<string, unknown>\n : {}\n}\n\nexport function numericValue(value: number | { value: number; message: string }): number {\n return typeof value === 'number' ? value : value.value\n}\n\nexport function orderedUnique(values: string[]): string[] {\n return [...new Set(values)]\n}\n\nexport function replaceRecord(target: Record<string, unknown>, values: Record<string, unknown>) {\n for (const key of Object.keys(target)) {\n delete target[key]\n }\n Object.assign(target, values)\n}\n\nexport function omitUndefined<T extends object>(record: T): T {\n const next: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(record as Record<string, unknown>)) {\n if (value !== undefined) next[key] = value\n }\n return next as T\n}\n\nexport function humanize(value: string): string {\n return value\n .replace(/[_-]+/g, ' ')\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .trim()\n .replace(/\\w\\S*/g, word => word.charAt(0).toUpperCase() + word.slice(1))\n}\n","import type {\n SelectOption,\n} from '../types'\nimport type {\n FormFieldType,\n} from '../types/form-builder'\nimport {\n getTypeDefault,\n} from './formBuilderRegistry'\nimport {\n humanize,\n} from './controlSchemaUtils'\nimport type {\n ControlDefinition,\n ControlInput,\n ControlOption,\n ControlOptionValue,\n ControlSchema,\n ControlSchemaOptions,\n} from './controlSchemaTypes'\n\nexport interface NormalizedControl {\n name: string\n definition: ControlDefinition\n type: FormFieldType\n sectionId: string\n viewId: string\n order: number\n}\n\nexport function normalizeControls(\n controls: ControlSchema,\n options: ControlSchemaOptions = {},\n): NormalizedControl[] {\n return Object.entries(controls)\n .map(([name, input], index): NormalizedControl => {\n const definition = normalizeControlDefinition(input)\n const type = definition.type ?? inferControlType(definition)\n return {\n name,\n definition,\n type,\n sectionId: definition.section ?? options.section ?? 'controls',\n viewId: definition.view ?? options.view ?? 'default',\n order: definition.order ?? index,\n }\n })\n .sort((a, b) => a.order - b.order)\n}\n\nexport function normalizeControlDefinition(input: ControlInput): ControlDefinition {\n if (isControlOptionArray(input)) {\n if (input.length === 0) return { type: 'tags', default: [] }\n return {\n default: optionValue(input[0]),\n options: input,\n }\n }\n\n if (typeof input === 'string' || typeof input === 'number' || typeof input === 'boolean') {\n return { default: input }\n }\n\n return input\n}\n\nexport function defaultValueForControl(definition: ControlDefinition, type: FormFieldType): unknown {\n if (definition.default !== undefined) return definition.default\n if (definition.defaultValue !== undefined) return definition.defaultValue\n return getTypeDefault(type)\n}\n\nexport function normalizeOption(option: ControlOption): SelectOption<unknown> {\n if (typeof option === 'object' && option !== null && 'label' in option) {\n return option\n }\n return {\n value: option,\n label: humanize(String(option)),\n }\n}\n\nfunction isControlOptionArray(input: ControlInput): input is readonly ControlOption[] {\n return Array.isArray(input)\n}\n\nfunction inferControlType(definition: ControlDefinition): FormFieldType {\n const value = definition.default ?? definition.defaultValue\n if (definition.options?.length) return Array.isArray(value) ? 'multiselect' : 'select'\n if (typeof value === 'boolean') return 'toggle'\n if (typeof value === 'number') return 'number'\n if (Array.isArray(value)) return 'tags'\n return 'text'\n}\n\nfunction optionValue(option: ControlOption): ControlOptionValue | unknown {\n if (typeof option === 'object' && option !== null && 'value' in option) {\n return option.value\n }\n return option\n}\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'\nimport BaseButton from './BaseButton.vue'\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 /** `vertical` stacks the steps in a rail beside the step content. */\n orientation?: 'horizontal' | 'vertical'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: 0,\n linear: true,\n showProgress: true,\n showStepNumbers: true,\n size: 'md',\n orientation: 'horizontal',\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=\"[\n 'mint-wizard',\n `mint-wizard--${props.size}`,\n { 'mint-wizard--vertical': props.orientation === 'vertical' },\n ]\"\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 <span class=\"mint-wizard__counter\">{{ props.modelValue + 1 }} / {{ props.steps.length }}</span>\n <BaseButton class=\"mint-wizard__cancel\" variant=\"ghost\" :size=\"props.size\" @click=\"cancel\">\n Cancel\n </BaseButton>\n <BaseButton\n variant=\"secondary\"\n :size=\"props.size\"\n :disabled=\"props.modelValue === 0\"\n @click=\"goBack\"\n >\n Back\n </BaseButton>\n <BaseButton\n v-if=\"props.modelValue < props.steps.length - 1\"\n variant=\"primary\"\n :size=\"props.size\"\n :disabled=\"stepValidity[props.modelValue] === false\"\n @click=\"goNext\"\n >\n Next\n </BaseButton>\n <BaseButton\n v-else\n variant=\"primary\"\n :size=\"props.size\"\n :disabled=\"stepValidity[props.modelValue] === false\"\n @click=\"finish\"\n >\n Finish\n </BaseButton>\n </slot>\n </div>\n </div>\n</template>\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'\nimport BaseButton from './BaseButton.vue'\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 /** `vertical` stacks the steps in a rail beside the step content. */\n orientation?: 'horizontal' | 'vertical'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n modelValue: 0,\n linear: true,\n showProgress: true,\n showStepNumbers: true,\n size: 'md',\n orientation: 'horizontal',\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=\"[\n 'mint-wizard',\n `mint-wizard--${props.size}`,\n { 'mint-wizard--vertical': props.orientation === 'vertical' },\n ]\"\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 <span class=\"mint-wizard__counter\">{{ props.modelValue + 1 }} / {{ props.steps.length }}</span>\n <BaseButton class=\"mint-wizard__cancel\" variant=\"ghost\" :size=\"props.size\" @click=\"cancel\">\n Cancel\n </BaseButton>\n <BaseButton\n variant=\"secondary\"\n :size=\"props.size\"\n :disabled=\"props.modelValue === 0\"\n @click=\"goBack\"\n >\n Back\n </BaseButton>\n <BaseButton\n v-if=\"props.modelValue < props.steps.length - 1\"\n variant=\"primary\"\n :size=\"props.size\"\n :disabled=\"stepValidity[props.modelValue] === false\"\n @click=\"goNext\"\n >\n Next\n </BaseButton>\n <BaseButton\n v-else\n variant=\"primary\"\n :size=\"props.size\"\n :disabled=\"stepValidity[props.modelValue] === false\"\n @click=\"finish\"\n >\n Finish\n </BaseButton>\n </slot>\n </div>\n </div>\n</template>\n","import { nextTick, ref, type Ref } from 'vue'\n\n/** Inline style for a visually hidden `aria-live` region (no global utility class in the SDK). */\nexport const REORDER_ANNOUNCER_STYLE = Object.freeze({\n position: 'absolute',\n width: '1px',\n height: '1px',\n padding: '0',\n margin: '-1px',\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: '0',\n})\n\nexport interface UseReorderAnnouncerReturn {\n /** Bind to a polite live region's text content. */\n announcement: Ref<string>\n /** Publishes a message to the live region. */\n announce: (message: string) => void\n /** Bind to the live region's `style` so it stays visually hidden. */\n announcerStyle: typeof REORDER_ANNOUNCER_STYLE\n}\n\n/**\n * Screen-reader announcements for drag/keyboard move interactions. Bind\n * `announcement` to a `role=\"status\" aria-live=\"polite\"` element styled with\n * `announcerStyle`.\n */\nexport function useReorderAnnouncer(): UseReorderAnnouncerReturn {\n const announcement = ref('')\n\n function announce(message: string) {\n // Re-announce identical consecutive messages by clearing first.\n if (announcement.value === message) announcement.value = ''\n void nextTick(() => {\n announcement.value = message\n })\n }\n\n return { announcement, announce, announcerStyle: REORDER_ANNOUNCER_STYLE }\n}\n\nexport interface ListReorderMove<T> {\n /** Index the item moved from, within `items()`. */\n fromIndex: number\n /** Index the item moved to, within `items()`. */\n toIndex: number\n /** The reordered array (a copy — `items()` is never mutated). */\n items: T[]\n /** Whether the move came from the keyboard or a pointer drag. */\n source: 'keyboard' | 'drag'\n}\n\nexport interface UseListReorderOptions<T> {\n /** The list being reordered, in its canonical (not merely rendered) order. */\n items: () => T[]\n /** Stable id for an item; falls back to the render index when ids repeat. */\n getId: (item: T, index: number) => string\n /** Applies the move. The composable never mutates the source array. */\n onReorder: (move: ListReorderMove<T>) => void\n /** Human-readable name used in announcements. Defaults to the item's id. */\n getLabel?: (item: T, index: number) => string\n /** Blocks every reorder while it returns `true` (readonly / non-sortable lists). */\n disabled?: () => boolean\n /** Resolves the focusable element for an id so keyboard moves keep focus on the item. */\n getElement?: (id: string) => HTMLElement | null | undefined\n /** Noun used in announcements, e.g. `'Reagent'`. Default `'Item'`. */\n itemNoun?: string\n}\n\nexport interface UseListReorderReturn<T> {\n /** Id of the item currently being dragged, or `null`. */\n draggingId: Ref<string | null>\n /** Id of the item the pointer is hovering as a drop target, or `null`. */\n dragOverId: Ref<string | null>\n isDragging: (id: string) => boolean\n isDragOver: (id: string) => boolean\n announcement: Ref<string>\n announcerStyle: typeof REORDER_ANNOUNCER_STYLE\n announce: (message: string) => void\n /** Bind to the draggable element's `@dragstart`. */\n onDragStart: (event: DragEvent, item: T, index: number) => void\n /** Bind to `@dragover` — calls `preventDefault()` so the drop is accepted. */\n onDragOver: (event: DragEvent, item: T, index: number) => void\n /** Bind to `@dragleave`. */\n onDragLeave: () => void\n /** Bind to `@drop`. */\n onDrop: (event: DragEvent, item: T, index: number) => void\n /** Bind to `@dragend`. */\n onDragEnd: () => void\n /**\n * Bind to the item's (or its drag handle's) `@keydown`: Alt+ArrowUp / Alt+ArrowLeft\n * moves the item one position earlier, Alt+ArrowDown / Alt+ArrowRight one later.\n */\n onKeydown: (event: KeyboardEvent, item: T, index: number) => void\n /** Moves an item between two indices of `items()` programmatically. */\n move: (fromIndex: number, toIndex: number, source?: 'keyboard' | 'drag') => void\n}\n\n/** Pure helper: a copy of `items` with the entry at `from` moved to `to`. */\nexport function reorderList<T>(items: T[], from: number, to: number): T[] {\n const next = [...items]\n const [moved] = next.splice(from, 1)\n next.splice(to, 0, moved)\n return next\n}\n\n/**\n * Keyboard-accessible list reordering shared by the SDK's drag-to-reorder lists.\n * It owns the HTML5 drag state, the drop-index maths, and an Alt+Arrow keyboard\n * alternative that announces every move through a polite live region — so a\n * reorder never depends on a pointer.\n */\nexport function useListReorder<T>(options: UseListReorderOptions<T>): UseListReorderReturn<T> {\n const draggingId = ref<string | null>(null)\n const dragOverId = ref<string | null>(null)\n const { announcement, announce, announcerStyle } = useReorderAnnouncer()\n const noun = options.itemNoun ?? 'Item'\n\n function isDisabled(): boolean {\n return options.disabled?.() === true\n }\n\n function idFor(item: T, index: number): string {\n return options.getId(item, index)\n }\n\n /** Index within `items()`, resolved by id so filtered/sorted renders still map back. */\n function indexOf(item: T, renderIndex: number): number {\n const id = idFor(item, renderIndex)\n const found = options.items().findIndex((candidate, index) => idFor(candidate, index) === id)\n return found === -1 ? renderIndex : found\n }\n\n function labelFor(index: number): string {\n const item = options.items()[index]\n if (item === undefined) return noun\n return options.getLabel?.(item, index) ?? idFor(item, index)\n }\n\n function clearDragState() {\n draggingId.value = null\n dragOverId.value = null\n }\n\n function move(fromIndex: number, toIndex: number, source: 'keyboard' | 'drag' = 'keyboard') {\n if (isDisabled()) return\n const items = options.items()\n if (\n fromIndex === toIndex ||\n fromIndex < 0 ||\n toIndex < 0 ||\n fromIndex >= items.length ||\n toIndex >= items.length\n ) return\n\n const movedId = idFor(items[fromIndex], fromIndex)\n const label = labelFor(fromIndex)\n options.onReorder({\n fromIndex,\n toIndex,\n items: reorderList(items, fromIndex, toIndex),\n source,\n })\n announce(`${label} moved to position ${toIndex + 1} of ${items.length}.`)\n void nextTick(() => options.getElement?.(movedId)?.focus())\n }\n\n function onDragStart(event: DragEvent, item: T, index: number) {\n if (isDisabled()) return\n const id = idFor(item, index)\n draggingId.value = id\n if (event.dataTransfer) {\n event.dataTransfer.effectAllowed = 'move'\n event.dataTransfer.setData('text/plain', id)\n }\n }\n\n function onDragOver(event: DragEvent, item: T, index: number) {\n if (isDisabled() || draggingId.value === null) return\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n const id = idFor(item, index)\n dragOverId.value = id === draggingId.value ? null : id\n }\n\n function onDragLeave() {\n dragOverId.value = null\n }\n\n function onDrop(event: DragEvent, item: T, index: number) {\n if (isDisabled() || draggingId.value === null) {\n clearDragState()\n return\n }\n event.preventDefault()\n\n const items = options.items()\n const fromIndex = items.findIndex((candidate, i) => idFor(candidate, i) === draggingId.value)\n const toIndex = indexOf(item, index)\n clearDragState()\n if (fromIndex === -1) return\n move(fromIndex, toIndex, 'drag')\n }\n\n function onDragEnd() {\n clearDragState()\n }\n\n function onKeydown(event: KeyboardEvent, item: T, index: number) {\n if (isDisabled() || !event.altKey) return\n const direction =\n event.key === 'ArrowUp' || event.key === 'ArrowLeft'\n ? -1\n : event.key === 'ArrowDown' || event.key === 'ArrowRight'\n ? 1\n : 0\n if (direction === 0) return\n\n event.preventDefault()\n const fromIndex = indexOf(item, index)\n move(fromIndex, fromIndex + direction, 'keyboard')\n }\n\n return {\n draggingId,\n dragOverId,\n isDragging: (id: string) => draggingId.value === id,\n isDragOver: (id: string) => dragOverId.value === id,\n announcement,\n announcerStyle,\n announce,\n onDragStart,\n onDragOver,\n onDragLeave,\n onDrop,\n onDragEnd,\n onKeydown,\n move,\n }\n}\n"],"mappings":"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"}