@morscherlab/mint-sdk 1.0.0-rc.2 → 1.0.0-rc.5

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 (443) hide show
  1. package/dist/__tests__/components/AppTopBar.navigation.test.d.ts +1 -0
  2. package/dist/__tests__/components/DoseCalculatorVolumeField.test.d.ts +1 -0
  3. package/dist/__tests__/components/PlateMapEditorToolbarInternal.test.d.ts +1 -0
  4. package/dist/__tests__/components/PluginWorkspaceView.controls.test.d.ts +1 -0
  5. package/dist/__tests__/components/PluginWorkspaceView.navigation.test.d.ts +1 -0
  6. package/dist/__tests__/components/PluginWorkspaceView.shell.test.d.ts +1 -0
  7. package/dist/__tests__/components/ProtocolStep.presentation.test.d.ts +1 -0
  8. package/dist/__tests__/components/ProtocolStepEditor.state.test.d.ts +1 -0
  9. package/dist/__tests__/components/ProtocolStepParameterField.test.d.ts +1 -0
  10. package/dist/__tests__/components/ReagentList.presentation.test.d.ts +1 -0
  11. package/dist/__tests__/components/SampleSelector.colors.test.d.ts +1 -0
  12. package/dist/__tests__/components/SampleSelector.drag.test.d.ts +1 -0
  13. package/dist/__tests__/components/SampleSelector.groups.test.d.ts +1 -0
  14. package/dist/__tests__/components/SampleSelector.selection.test.d.ts +1 -0
  15. package/dist/__tests__/components/SampleSelectorSampleRow.test.d.ts +1 -0
  16. package/dist/__tests__/components/ScheduleCalendar.test.d.ts +1 -0
  17. package/dist/__tests__/components/SettingsModal.schema.test.d.ts +1 -0
  18. package/dist/__tests__/components/WellPlate.colors.test.d.ts +1 -0
  19. package/dist/__tests__/components/WellPlate.conditions.test.d.ts +1 -0
  20. package/dist/__tests__/components/WellPlate.geometry.test.d.ts +1 -0
  21. package/dist/__tests__/components/WellPlate.interaction.test.d.ts +1 -0
  22. package/dist/__tests__/components/WellPlate.legend.test.d.ts +1 -0
  23. package/dist/__tests__/components/WellPlate.rendering.test.d.ts +1 -0
  24. package/dist/__tests__/components/WellPlate.sampleDrop.test.d.ts +1 -0
  25. package/dist/__tests__/composables/autoGroup/classify.test.d.ts +1 -0
  26. package/dist/__tests__/composables/autoGroup/columns.test.d.ts +1 -0
  27. package/dist/__tests__/composables/autoGroup/compose.test.d.ts +1 -0
  28. package/dist/__tests__/composables/autoGroup/cooccurrence.test.d.ts +1 -0
  29. package/dist/__tests__/composables/autoGroup/fingerprint.test.d.ts +1 -0
  30. package/dist/__tests__/composables/autoGroup/integration.test.d.ts +1 -0
  31. package/dist/__tests__/composables/autoGroup/template.test.d.ts +1 -0
  32. package/dist/__tests__/composables/autoGroup/tokenize.test.d.ts +1 -0
  33. package/dist/__tests__/composables/useAutoGroupInputSources.test.d.ts +1 -0
  34. package/dist/__tests__/composables/useScheduleCalendarLayout.test.d.ts +1 -0
  35. package/dist/__tests__/docs/extractDocsComponents.test.d.ts +1 -0
  36. package/dist/__tests__/docs/extractDocsExports.test.d.ts +1 -0
  37. package/dist/__tests__/docs/extractDocsParsing.test.d.ts +1 -0
  38. package/dist/__tests__/docs/extractDocsTemplates.test.d.ts +1 -0
  39. package/dist/__tests__/docs/extractDocsTheme.test.d.ts +1 -0
  40. package/dist/components/AppTopBar.navigation.d.ts +11 -0
  41. package/dist/components/BaseButton.vue.d.ts +1 -1
  42. package/dist/components/BaseCheckbox.vue.d.ts +1 -1
  43. package/dist/components/BaseInput.vue.d.ts +2 -2
  44. package/dist/components/BasePill.vue.d.ts +1 -1
  45. package/dist/components/BaseRadioGroup.vue.d.ts +1 -1
  46. package/dist/components/BaseSelect.vue.d.ts +1 -1
  47. package/dist/components/BaseSlider.vue.d.ts +2 -2
  48. package/dist/components/BaseTextarea.vue.d.ts +2 -2
  49. package/dist/components/BaseToggle.vue.d.ts +1 -1
  50. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
  51. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -1
  52. package/dist/components/ColorSlider.vue.d.ts +2 -2
  53. package/dist/components/ConcentrationInput.vue.d.ts +2 -2
  54. package/dist/components/DatePicker.vue.d.ts +1 -1
  55. package/dist/components/DateTimePicker.vue.d.ts +2 -2
  56. package/dist/components/DoseCalculatorVolumeField.vue.d.ts +15 -0
  57. package/dist/components/DropdownButton.vue.d.ts +1 -1
  58. package/dist/components/FileUploader.vue.d.ts +2 -2
  59. package/dist/components/FormulaInput.vue.d.ts +2 -2
  60. package/dist/components/IconButton.vue.d.ts +1 -1
  61. package/dist/components/LoadingSpinner.vue.d.ts +1 -1
  62. package/dist/components/MoleculeInput.vue.d.ts +2 -2
  63. package/dist/components/MultiSelect.vue.d.ts +1 -1
  64. package/dist/components/NumberInput.vue.d.ts +1 -1
  65. package/dist/components/PlateMapEditor.vue.d.ts +6 -6
  66. package/dist/components/PluginWorkspaceView.controls.d.ts +28 -0
  67. package/dist/components/PluginWorkspaceView.navigation.d.ts +29 -0
  68. package/dist/components/PluginWorkspaceView.props.d.ts +151 -0
  69. package/dist/components/PluginWorkspaceView.shell.d.ts +19 -0
  70. package/dist/components/PluginWorkspaceView.vue.d.ts +46 -195
  71. package/dist/components/ProgressBar.vue.d.ts +1 -1
  72. package/dist/components/ProtocolStep.presentation.d.ts +4 -0
  73. package/dist/components/ProtocolStepEditor.state.d.ts +18 -0
  74. package/dist/components/ProtocolStepParameterField.vue.d.ts +12 -0
  75. package/dist/components/ReagentList.presentation.d.ts +16 -0
  76. package/dist/components/ResourceCard.vue.d.ts +1 -1
  77. package/dist/components/SampleSelector.colors.d.ts +13 -0
  78. package/dist/components/SampleSelector.drag.d.ts +24 -0
  79. package/dist/components/SampleSelector.groups.d.ts +15 -0
  80. package/dist/components/SampleSelector.selection.d.ts +26 -0
  81. package/dist/components/SampleSelector.vue.d.ts +4 -1
  82. package/dist/components/SampleSelectorSampleRow.vue.d.ts +21 -0
  83. package/dist/components/SegmentedControl.vue.d.ts +1 -1
  84. package/dist/components/SequenceInput.vue.d.ts +2 -2
  85. package/dist/components/SequenceProgressBar.vue.d.ts +1 -1
  86. package/dist/components/SettingsModal.schema.d.ts +9 -0
  87. package/dist/components/StatusIndicator.vue.d.ts +1 -1
  88. package/dist/components/TagsInput.vue.d.ts +2 -2
  89. package/dist/components/TimePicker.vue.d.ts +2 -2
  90. package/dist/components/TimeRangeInput.vue.d.ts +1 -1
  91. package/dist/components/UnitInput.vue.d.ts +2 -2
  92. package/dist/components/WellPlate.colors.d.ts +9 -0
  93. package/dist/components/WellPlate.conditions.d.ts +26 -0
  94. package/dist/components/WellPlate.geometry.d.ts +23 -0
  95. package/dist/components/WellPlate.interaction.d.ts +71 -0
  96. package/dist/components/WellPlate.legend.d.ts +2 -0
  97. package/dist/components/WellPlate.rendering.d.ts +24 -0
  98. package/dist/components/WellPlate.sampleDrop.d.ts +8 -0
  99. package/dist/components/WellPlate.vue.d.ts +1 -1
  100. package/dist/components/index.js +2 -2
  101. package/dist/components/internal/ActionItemInternal.vue.d.ts +1 -1
  102. package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +28 -0
  103. package/dist/{components-BhK-dW99.js → components-DtHA2bgp.js} +3754 -2991
  104. package/dist/components-DtHA2bgp.js.map +1 -0
  105. package/dist/composables/autoGroup/classKey.d.ts +4 -0
  106. package/dist/composables/autoGroup/classify.d.ts +28 -0
  107. package/dist/composables/autoGroup/colors.d.ts +2 -0
  108. package/dist/composables/autoGroup/columns.d.ts +10 -0
  109. package/dist/composables/autoGroup/compose.d.ts +8 -0
  110. package/dist/composables/autoGroup/cooccurrence.d.ts +2 -0
  111. package/dist/composables/autoGroup/csv-shim.d.ts +2 -0
  112. package/dist/composables/autoGroup/fingerprint.d.ts +3 -0
  113. package/dist/composables/autoGroup/index.d.ts +16 -0
  114. package/dist/composables/autoGroup/replicatePreGroup.d.ts +38 -0
  115. package/dist/composables/autoGroup/template.d.ts +15 -0
  116. package/dist/composables/autoGroup/tokenize.d.ts +8 -0
  117. package/dist/composables/autoGroupConstants.d.ts +1 -0
  118. package/dist/composables/autoGroupGrouping.d.ts +3 -0
  119. package/dist/composables/controlComponentBindings.d.ts +7 -0
  120. package/dist/composables/controlSchemaAdapters.d.ts +20 -0
  121. package/dist/composables/controlSchemaDoseDesign.d.ts +11 -0
  122. package/dist/composables/controlSchemaFormFields.d.ts +3 -0
  123. package/dist/composables/controlSchemaLayout.d.ts +7 -0
  124. package/dist/composables/controlSchemaModel.d.ts +5 -0
  125. package/dist/composables/controlSchemaNormalize.d.ts +15 -0
  126. package/dist/composables/controlSchemaTypes.d.ts +305 -0
  127. package/dist/composables/controlSchemaUtils.d.ts +9 -0
  128. package/dist/composables/controlWorkspaceOptions.d.ts +2 -0
  129. package/dist/composables/formBuilderSchema.d.ts +18 -0
  130. package/dist/composables/index.js +3 -3
  131. package/dist/composables/pluginEndpointBuilder.d.ts +13 -0
  132. package/dist/composables/protocolTemplateCatalog.d.ts +26 -0
  133. package/dist/composables/useAutoGroup.d.ts +61 -74
  134. package/dist/composables/useAutoGroupInputSources.d.ts +32 -0
  135. package/dist/composables/useBioTemplateControls.d.ts +1 -1
  136. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +1 -1
  137. package/dist/composables/useBioTemplateWorkspace.d.ts +1 -1
  138. package/dist/composables/useControlSchema.d.ts +8 -346
  139. package/dist/composables/useControlWorkspace.d.ts +5 -0
  140. package/dist/composables/useForm.d.ts +2 -33
  141. package/dist/composables/useFormBuilder.d.ts +2 -9
  142. package/dist/composables/useFormValidation.d.ts +34 -0
  143. package/dist/composables/usePluginClient.d.ts +1 -4
  144. package/dist/composables/useProtocolTemplates.d.ts +2 -24
  145. package/dist/composables/useScheduleCalendarLayout.d.ts +49 -0
  146. package/dist/{composables-Bg7CFuNz.js → composables-Dlg8jenH.js} +33 -31
  147. package/dist/composables-Dlg8jenH.js.map +1 -0
  148. package/dist/index.js +4 -4
  149. package/dist/install.js +2 -2
  150. package/dist/styles.css +547 -516
  151. package/dist/templates/adapters.d.ts +14 -47
  152. package/dist/templates/assayLookups.d.ts +3 -0
  153. package/dist/templates/assayMatrixAdapters.d.ts +6 -0
  154. package/dist/templates/assayMatrixBuilder.d.ts +2 -0
  155. package/dist/templates/assayNormalizers.d.ts +4 -0
  156. package/dist/templates/builderDefaults.d.ts +1 -0
  157. package/dist/templates/builderIdUtils.d.ts +4 -0
  158. package/dist/templates/builderPresetControls.d.ts +10 -0
  159. package/dist/templates/builderReadUtils.d.ts +8 -0
  160. package/dist/templates/builders.d.ts +26 -67
  161. package/dist/templates/calibrationCurveAdapters.d.ts +4 -0
  162. package/dist/templates/calibrationCurveBuilder.d.ts +2 -0
  163. package/dist/templates/calibrationNormalizers.d.ts +5 -0
  164. package/dist/templates/componentBindingCatalog.d.ts +25 -0
  165. package/dist/templates/componentBindingHelpers.d.ts +17 -0
  166. package/dist/templates/componentDoseResponseProps.d.ts +3 -0
  167. package/dist/templates/componentGenericProps.d.ts +8 -0
  168. package/dist/templates/componentPlateHelpers.d.ts +5 -0
  169. package/dist/templates/componentPlateMapProps.d.ts +3 -0
  170. package/dist/templates/componentPropsFactory.d.ts +3 -0
  171. package/dist/templates/componentQpcrPlateProps.d.ts +3 -0
  172. package/dist/templates/componentTargetResolvers.d.ts +5 -0
  173. package/dist/templates/componentTemplateProps.d.ts +3 -0
  174. package/dist/templates/controlSchemaClone.d.ts +4 -0
  175. package/dist/templates/controlSchemaConstants.d.ts +10 -0
  176. package/dist/templates/controlSchemaMerge.d.ts +3 -0
  177. package/dist/templates/controlSchemaTargets.d.ts +17 -0
  178. package/dist/templates/controlSchemaTypes.d.ts +4 -0
  179. package/dist/templates/controlSchemas.d.ts +4 -4
  180. package/dist/templates/defaultBioTemplateBuilder.d.ts +2 -0
  181. package/dist/templates/doseResponseAdapters.d.ts +4 -0
  182. package/dist/templates/doseResponseBuilder.d.ts +2 -0
  183. package/dist/templates/elisaAssayCollectionBuilder.d.ts +2 -0
  184. package/dist/templates/flowCytometryAssayCollectionBuilder.d.ts +2 -0
  185. package/dist/templates/flowCytometryPanelBuilder.d.ts +2 -0
  186. package/dist/templates/flowNormalizers.d.ts +8 -0
  187. package/dist/templates/flowPanelAdapters.d.ts +4 -0
  188. package/dist/templates/index.js +1 -1
  189. package/dist/templates/instrumentRunAdapterHelpers.d.ts +8 -0
  190. package/dist/templates/instrumentRunAdapters.d.ts +8 -0
  191. package/dist/templates/instrumentRunBuilder.d.ts +2 -0
  192. package/dist/templates/lcmsBatchCollectionBuilder.d.ts +2 -0
  193. package/dist/templates/plateGeometry.d.ts +4 -0
  194. package/dist/templates/plateMapAdapters.d.ts +3 -0
  195. package/dist/templates/plateMapBuilder.d.ts +2 -0
  196. package/dist/templates/presetControlSchemas.d.ts +534 -0
  197. package/dist/templates/protocolAdapters.d.ts +5 -0
  198. package/dist/templates/protocolNormalizers.d.ts +6 -0
  199. package/dist/templates/protocolStepsBuilder.d.ts +2 -0
  200. package/dist/templates/qpcrAdapters.d.ts +5 -0
  201. package/dist/templates/qpcrExpressionCollectionBuilder.d.ts +2 -0
  202. package/dist/templates/qpcrPlateBuilder.d.ts +2 -0
  203. package/dist/templates/reagentAdapters.d.ts +5 -0
  204. package/dist/templates/reagentListBuilder.d.ts +2 -0
  205. package/dist/templates/runNormalizers.d.ts +10 -0
  206. package/dist/templates/sampleNormalizers.d.ts +4 -0
  207. package/dist/templates/samplePrepAdapters.d.ts +4 -0
  208. package/dist/templates/samplePrepBuilder.d.ts +2 -0
  209. package/dist/templates/sampleSheetAdapters.d.ts +5 -0
  210. package/dist/templates/sampleSheetBuilder.d.ts +2 -0
  211. package/dist/templates/targetedMetabolomicsCollectionBuilder.d.ts +2 -0
  212. package/dist/templates/targetedMetabolomicsHelpers.d.ts +5 -0
  213. package/dist/templates/templateAdapterTypes.d.ts +48 -0
  214. package/dist/templates/templateControlSchemas.d.ts +400 -0
  215. package/dist/templates/templateCreateOptions.d.ts +165 -0
  216. package/dist/templates/templateEnvelopes.d.ts +9 -0
  217. package/dist/templates/templatePackCollectionBuilder.d.ts +2 -0
  218. package/dist/templates/templatePresetCollectionBuilder.d.ts +18 -0
  219. package/dist/templates/templateQpcrTypes.d.ts +42 -0
  220. package/dist/templates/templateValidators.d.ts +13 -0
  221. package/dist/templates/timeCourseAdapters.d.ts +5 -0
  222. package/dist/templates/timeCourseBuilder.d.ts +2 -0
  223. package/dist/templates/types.d.ts +5 -250
  224. package/dist/templates/wellPlateScreenCollectionBuilder.d.ts +2 -0
  225. package/dist/templates/westernBlotAssayCollectionBuilder.d.ts +2 -0
  226. package/dist/{templates-BorLR_7p.js → templates-DtdUvJ4c.js} +3565 -3411
  227. package/dist/templates-DtdUvJ4c.js.map +1 -0
  228. package/dist/types/auto-group.d.ts +79 -9
  229. package/dist/types/componentLabTypes.d.ts +161 -0
  230. package/dist/types/componentWorkflowTypes.d.ts +150 -0
  231. package/dist/types/components.d.ts +2 -311
  232. package/dist/{useProtocolTemplates-n6AJqSqv.js → useProtocolTemplates-Bm5vyH4_.js} +1220 -454
  233. package/dist/useProtocolTemplates-Bm5vyH4_.js.map +1 -0
  234. package/package.json +1 -1
  235. package/src/__tests__/components/AppTopBar.navigation.test.ts +70 -0
  236. package/src/__tests__/components/DoseCalculatorVolumeField.test.ts +53 -0
  237. package/src/__tests__/components/PlateMapEditorToolbarInternal.test.ts +54 -0
  238. package/src/__tests__/components/PluginWorkspaceView.controls.test.ts +156 -0
  239. package/src/__tests__/components/PluginWorkspaceView.navigation.test.ts +102 -0
  240. package/src/__tests__/components/PluginWorkspaceView.shell.test.ts +41 -0
  241. package/src/__tests__/components/ProtocolStep.presentation.test.ts +31 -0
  242. package/src/__tests__/components/ProtocolStepEditor.state.test.ts +165 -0
  243. package/src/__tests__/components/ProtocolStepParameterField.test.ts +44 -0
  244. package/src/__tests__/components/ReagentList.presentation.test.ts +68 -0
  245. package/src/__tests__/components/SampleSelector.colors.test.ts +49 -0
  246. package/src/__tests__/components/SampleSelector.drag.test.ts +100 -0
  247. package/src/__tests__/components/SampleSelector.groups.test.ts +81 -0
  248. package/src/__tests__/components/SampleSelector.selection.test.ts +70 -0
  249. package/src/__tests__/components/SampleSelector.test.ts +32 -0
  250. package/src/__tests__/components/SampleSelectorSampleRow.test.ts +37 -0
  251. package/src/__tests__/components/ScheduleCalendar.test.ts +44 -0
  252. package/src/__tests__/components/SettingsModal.schema.test.ts +97 -0
  253. package/src/__tests__/components/WellPlate.colors.test.ts +28 -0
  254. package/src/__tests__/components/WellPlate.conditions.test.ts +68 -0
  255. package/src/__tests__/components/WellPlate.geometry.test.ts +54 -0
  256. package/src/__tests__/components/WellPlate.interaction.test.ts +171 -0
  257. package/src/__tests__/components/WellPlate.legend.test.ts +13 -0
  258. package/src/__tests__/components/WellPlate.rendering.test.ts +122 -0
  259. package/src/__tests__/components/WellPlate.sampleDrop.test.ts +70 -0
  260. package/src/__tests__/composables/autoGroup/classify.test.ts +107 -0
  261. package/src/__tests__/composables/autoGroup/columns.test.ts +135 -0
  262. package/src/__tests__/composables/autoGroup/compose.test.ts +227 -0
  263. package/src/__tests__/composables/autoGroup/cooccurrence.test.ts +91 -0
  264. package/src/__tests__/composables/autoGroup/fingerprint.test.ts +50 -0
  265. package/src/__tests__/composables/autoGroup/integration.test.ts +79 -0
  266. package/src/__tests__/composables/autoGroup/template.test.ts +70 -0
  267. package/src/__tests__/composables/autoGroup/tokenize.test.ts +33 -0
  268. package/src/__tests__/composables/useAutoGroup.test.ts +129 -625
  269. package/src/__tests__/composables/useAutoGroupInputSources.test.ts +107 -0
  270. package/src/__tests__/composables/useControlSchema.test.ts +23 -0
  271. package/src/__tests__/composables/useScheduleCalendarLayout.test.ts +89 -0
  272. package/src/__tests__/docs/extractDocsComponents.test.ts +142 -0
  273. package/src/__tests__/docs/extractDocsExports.test.ts +77 -0
  274. package/src/__tests__/docs/extractDocsParsing.test.ts +69 -0
  275. package/src/__tests__/docs/extractDocsTemplates.test.ts +54 -0
  276. package/src/__tests__/docs/extractDocsTheme.test.ts +89 -0
  277. package/src/__tests__/docs/frontendDocsCatalog.test.ts +1 -1
  278. package/src/__tests__/fixtures/auto-group/mixed-lc-ms-batch.txt +187 -0
  279. package/src/components/AppSidebar.vue +2 -6
  280. package/src/components/AppTopBar.navigation.ts +62 -0
  281. package/src/components/AppTopBar.vue +17 -44
  282. package/src/components/AutoGroupModal.story.vue +50 -0
  283. package/src/components/AutoGroupModal.vue +441 -158
  284. package/src/components/ControlWorkspaceView.vue +2 -6
  285. package/src/components/DoseCalculator.vue +13 -73
  286. package/src/components/DoseCalculatorVolumeField.vue +61 -0
  287. package/src/components/ExperimentTimeline.vue +6 -31
  288. package/src/components/FormBuilder.vue +2 -7
  289. package/src/components/PlateMapEditor.vue +32 -106
  290. package/src/components/PluginWorkspaceView.controls.ts +182 -0
  291. package/src/components/PluginWorkspaceView.navigation.ts +106 -0
  292. package/src/components/PluginWorkspaceView.props.ts +174 -0
  293. package/src/components/PluginWorkspaceView.shell.ts +66 -0
  294. package/src/components/PluginWorkspaceView.vue +85 -404
  295. package/src/components/ProtocolStep.presentation.ts +31 -0
  296. package/src/components/ProtocolStepEditor.state.ts +104 -0
  297. package/src/components/ProtocolStepEditor.vue +48 -179
  298. package/src/components/ProtocolStepParameterField.vue +134 -0
  299. package/src/components/ReagentList.presentation.ts +105 -0
  300. package/src/components/ReagentList.vue +16 -79
  301. package/src/components/SampleSelector.colors.ts +43 -0
  302. package/src/components/SampleSelector.drag.ts +164 -0
  303. package/src/components/SampleSelector.groups.ts +109 -0
  304. package/src/components/SampleSelector.selection.ts +103 -0
  305. package/src/components/SampleSelector.vue +82 -349
  306. package/src/components/SampleSelectorSampleRow.vue +64 -0
  307. package/src/components/ScheduleCalendar.vue +44 -199
  308. package/src/components/SettingsModal.schema.ts +71 -0
  309. package/src/components/SettingsModal.vue +16 -46
  310. package/src/components/WellPlate.colors.ts +56 -0
  311. package/src/components/WellPlate.conditions.ts +100 -0
  312. package/src/components/WellPlate.geometry.ts +91 -0
  313. package/src/components/WellPlate.interaction.ts +272 -0
  314. package/src/components/WellPlate.legend.ts +8 -0
  315. package/src/components/WellPlate.rendering.ts +105 -0
  316. package/src/components/WellPlate.sampleDrop.ts +73 -0
  317. package/src/components/WellPlate.vue +102 -550
  318. package/src/components/internal/PlateMapEditorToolbarInternal.vue +128 -0
  319. package/src/composables/autoGroup/classKey.ts +5 -0
  320. package/src/composables/autoGroup/classify.ts +205 -0
  321. package/src/composables/autoGroup/colors.ts +6 -0
  322. package/src/composables/autoGroup/columns.ts +226 -0
  323. package/src/composables/autoGroup/compose.ts +156 -0
  324. package/src/composables/autoGroup/cooccurrence.ts +46 -0
  325. package/src/composables/autoGroup/csv-shim.ts +44 -0
  326. package/src/composables/autoGroup/fingerprint.ts +49 -0
  327. package/src/composables/autoGroup/index.ts +20 -0
  328. package/src/composables/autoGroup/replicatePreGroup.ts +90 -0
  329. package/src/composables/autoGroup/template.ts +126 -0
  330. package/src/composables/autoGroup/tokenize.ts +41 -0
  331. package/src/composables/autoGroup/vocab.json +67 -0
  332. package/src/composables/autoGroupConstants.ts +4 -0
  333. package/src/composables/autoGroupGrouping.ts +148 -0
  334. package/src/composables/controlComponentBindings.ts +80 -0
  335. package/src/composables/controlSchemaAdapters.ts +196 -0
  336. package/src/composables/controlSchemaDoseDesign.ts +215 -0
  337. package/src/composables/controlSchemaFormFields.ts +61 -0
  338. package/src/composables/controlSchemaLayout.ts +59 -0
  339. package/src/composables/controlSchemaModel.ts +163 -0
  340. package/src/composables/controlSchemaNormalize.ts +101 -0
  341. package/src/composables/controlSchemaTypes.ts +364 -0
  342. package/src/composables/controlSchemaUtils.ts +36 -0
  343. package/src/composables/controlWorkspaceOptions.ts +21 -0
  344. package/src/composables/formBuilderSchema.ts +153 -0
  345. package/src/composables/pluginEndpointBuilder.ts +203 -0
  346. package/src/composables/protocolTemplateCatalog.ts +325 -0
  347. package/src/composables/useAutoGroup.ts +395 -549
  348. package/src/composables/useAutoGroupInputSources.ts +147 -0
  349. package/src/composables/useBioTemplateControls.ts +1 -1
  350. package/src/composables/useBioTemplatePresetWorkspace.ts +1 -1
  351. package/src/composables/useBioTemplateWorkspace.ts +1 -1
  352. package/src/composables/useControlSchema.ts +64 -1312
  353. package/src/composables/useControlWorkspace.ts +201 -0
  354. package/src/composables/useForm.ts +5 -187
  355. package/src/composables/useFormBuilder.ts +11 -153
  356. package/src/composables/useFormValidation.ts +154 -0
  357. package/src/composables/usePluginClient.ts +10 -193
  358. package/src/composables/useProtocolTemplates.ts +10 -328
  359. package/src/composables/useScheduleCalendarLayout.ts +287 -0
  360. package/src/styles/components/auto-group-modal.css +248 -310
  361. package/src/templates/adapters.ts +89 -930
  362. package/src/templates/assayLookups.ts +33 -0
  363. package/src/templates/assayMatrixAdapters.ts +78 -0
  364. package/src/templates/assayMatrixBuilder.ts +59 -0
  365. package/src/templates/assayNormalizers.ts +34 -0
  366. package/src/templates/builderDefaults.ts +11 -0
  367. package/src/templates/builderIdUtils.ts +20 -0
  368. package/src/templates/builderPresetControls.ts +165 -0
  369. package/src/templates/builderReadUtils.ts +57 -0
  370. package/src/templates/builders.ts +122 -2350
  371. package/src/templates/calibrationCurveAdapters.ts +59 -0
  372. package/src/templates/calibrationCurveBuilder.ts +99 -0
  373. package/src/templates/calibrationNormalizers.ts +60 -0
  374. package/src/templates/componentBindingCatalog.ts +90 -0
  375. package/src/templates/componentBindingHelpers.ts +93 -0
  376. package/src/templates/componentBindings.ts +12 -461
  377. package/src/templates/componentDoseResponseProps.ts +42 -0
  378. package/src/templates/componentGenericProps.ts +77 -0
  379. package/src/templates/componentPlateHelpers.ts +29 -0
  380. package/src/templates/componentPlateMapProps.ts +32 -0
  381. package/src/templates/componentPropsFactory.ts +21 -0
  382. package/src/templates/componentQpcrPlateProps.ts +28 -0
  383. package/src/templates/componentTargetResolvers.ts +69 -0
  384. package/src/templates/componentTemplateProps.ts +78 -0
  385. package/src/templates/controlSchemaClone.ts +32 -0
  386. package/src/templates/controlSchemaConstants.ts +11 -0
  387. package/src/templates/controlSchemaMerge.ts +40 -0
  388. package/src/templates/controlSchemaTargets.ts +87 -0
  389. package/src/templates/controlSchemaTypes.ts +20 -0
  390. package/src/templates/controlSchemas.ts +22 -704
  391. package/src/templates/defaultBioTemplateBuilder.ts +124 -0
  392. package/src/templates/doseResponseAdapters.ts +45 -0
  393. package/src/templates/doseResponseBuilder.ts +44 -0
  394. package/src/templates/elisaAssayCollectionBuilder.ts +62 -0
  395. package/src/templates/flowCytometryAssayCollectionBuilder.ts +41 -0
  396. package/src/templates/flowCytometryPanelBuilder.ts +53 -0
  397. package/src/templates/flowNormalizers.ts +58 -0
  398. package/src/templates/flowPanelAdapters.ts +58 -0
  399. package/src/templates/instrumentRunAdapterHelpers.ts +94 -0
  400. package/src/templates/instrumentRunAdapters.ts +163 -0
  401. package/src/templates/instrumentRunBuilder.ts +97 -0
  402. package/src/templates/lcmsBatchCollectionBuilder.ts +38 -0
  403. package/src/templates/plateGeometry.ts +62 -0
  404. package/src/templates/plateMapAdapters.ts +36 -0
  405. package/src/templates/plateMapBuilder.ts +43 -0
  406. package/src/templates/presetControlSchemas.ts +258 -0
  407. package/src/templates/protocolAdapters.ts +69 -0
  408. package/src/templates/protocolNormalizers.ts +37 -0
  409. package/src/templates/protocolStepsBuilder.ts +36 -0
  410. package/src/templates/qpcrAdapters.ts +104 -0
  411. package/src/templates/qpcrExpressionCollectionBuilder.ts +33 -0
  412. package/src/templates/qpcrPlateBuilder.ts +96 -0
  413. package/src/templates/reagentAdapters.ts +77 -0
  414. package/src/templates/reagentListBuilder.ts +30 -0
  415. package/src/templates/runNormalizers.ts +63 -0
  416. package/src/templates/sampleNormalizers.ts +58 -0
  417. package/src/templates/samplePrepAdapters.ts +63 -0
  418. package/src/templates/samplePrepBuilder.ts +51 -0
  419. package/src/templates/sampleSheetAdapters.ts +75 -0
  420. package/src/templates/sampleSheetBuilder.ts +23 -0
  421. package/src/templates/targetedMetabolomicsCollectionBuilder.ts +79 -0
  422. package/src/templates/targetedMetabolomicsHelpers.ts +102 -0
  423. package/src/templates/templateAdapterTypes.ts +58 -0
  424. package/src/templates/templateControlSchemas.ts +320 -0
  425. package/src/templates/templateCreateOptions.ts +208 -0
  426. package/src/templates/templateEnvelopes.ts +137 -0
  427. package/src/templates/templatePackCollectionBuilder.ts +23 -0
  428. package/src/templates/templatePresetCollectionBuilder.ts +139 -0
  429. package/src/templates/templateQpcrTypes.ts +48 -0
  430. package/src/templates/templateValidators.ts +414 -0
  431. package/src/templates/timeCourseAdapters.ts +73 -0
  432. package/src/templates/timeCourseBuilder.ts +64 -0
  433. package/src/templates/types.ts +79 -275
  434. package/src/templates/wellPlateScreenCollectionBuilder.ts +36 -0
  435. package/src/templates/westernBlotAssayCollectionBuilder.ts +68 -0
  436. package/src/types/auto-group.ts +107 -9
  437. package/src/types/componentLabTypes.ts +235 -0
  438. package/src/types/componentWorkflowTypes.ts +190 -0
  439. package/src/types/components.ts +74 -424
  440. package/dist/components-BhK-dW99.js.map +0 -1
  441. package/dist/composables-Bg7CFuNz.js.map +0 -1
  442. package/dist/templates-BorLR_7p.js.map +0 -1
  443. package/dist/useProtocolTemplates-n6AJqSqv.js.map +0 -1
@@ -8,9 +8,10 @@ import StepWizard from './StepWizard.vue'
8
8
  import LoadingSpinner from './LoadingSpinner.vue'
9
9
  import AlertBox from './AlertBox.vue'
10
10
  import { useAutoGroup, parseCSV } from '../composables/useAutoGroup'
11
+ import { classKey } from '../composables/autoGroup'
11
12
  import { useApi } from '../composables/useApi'
12
13
  import type { WizardStep } from '../types/components'
13
- import type { AutoGroupResult } from '../types/auto-group'
14
+ import type { AutoGroupResult, MergeSuggestion, ColumnRole } from '../types/auto-group'
14
15
 
15
16
  export interface Props {
16
17
  modelValue: boolean
@@ -41,7 +42,162 @@ const experimentLoading = ref(false)
41
42
  const experimentError = ref<string | null>(null)
42
43
  const csvError = ref<string | null>(null)
43
44
 
44
- // Pre-fill from props
45
+ // Workspace step refs
46
+ const openPopoverIdx = ref<number | null>(null)
47
+ const qcAsOverlay = ref(true)
48
+ const excludeQc = ref(false)
49
+
50
+ function roleLabel(role?: ColumnRole): string {
51
+ if (!role) return 'Factor'
52
+ const labels: Record<ColumnRole, string> = {
53
+ 'constant': 'Constant',
54
+ 'factor': 'Factor',
55
+ 'replicate': 'Replicate',
56
+ 'run-order': 'Run',
57
+ 'numeric': 'Numeric',
58
+ 'class-tag': 'Class tag',
59
+ 'ignore': 'Ignore',
60
+ }
61
+ return labels[role]
62
+ }
63
+
64
+ const activeSchemaLabel = computed(() => {
65
+ const cls = autoGroup.classes.value.find(c => classKey(c) === autoGroup.activeClassKey.value)
66
+ return cls?.label ?? '—'
67
+ })
68
+
69
+ const activeMemberCount = computed(() => {
70
+ const cls = autoGroup.classes.value.find(c => classKey(c) === autoGroup.activeClassKey.value)
71
+ return cls?.members.length ?? 0
72
+ })
73
+
74
+ const activeColumnCount = computed(() => autoGroup.activeSchema.value?.columns.length ?? 0)
75
+
76
+ const groupKeyNames = computed<string[]>(() => {
77
+ const schema = autoGroup.activeSchema.value
78
+ if (!schema) return []
79
+ return schema.groupBy
80
+ .map(idx => {
81
+ const col = schema.columns.find(c => c.index === idx)
82
+ return col?.displayName ?? col?.name ?? ''
83
+ })
84
+ .filter((s): s is string => s.length > 0)
85
+ })
86
+
87
+ const totalGrouped = computed(() =>
88
+ (autoGroup.result.value.experimentalGroups ?? []).reduce((acc, g) => acc + g.samples.length, 0),
89
+ )
90
+ const totalQc = computed(() =>
91
+ (autoGroup.qcGroups.value ?? []).reduce((acc, g) => acc + g.samples.length, 0),
92
+ )
93
+
94
+ function acceptSuggestion(s: MergeSuggestion) {
95
+ autoGroup.mergeColumns(autoGroup.activeClassKey.value, s.columnIndices)
96
+ }
97
+
98
+ function dismissSuggestion(_: MergeSuggestion) {
99
+ // Suggestions are derived; "dismiss" is a no-op in MVP (it will re-appear on schema reset).
100
+ }
101
+
102
+ // QC routing: a 3-way state replaces the two former booleans. Internally we
103
+ // still store qcAsOverlay + excludeQc (so downstream watchers stay stable),
104
+ // but the UI presents the choice as one radio group: overlay / exclude / mix.
105
+ type QcRouting = 'overlay' | 'exclude' | 'mix'
106
+ const qcRouting = computed<QcRouting>(() => {
107
+ if (excludeQc.value) return 'exclude'
108
+ if (qcAsOverlay.value) return 'overlay'
109
+ return 'mix'
110
+ })
111
+
112
+ function setQcRouting(value: QcRouting) {
113
+ qcAsOverlay.value = value === 'overlay'
114
+ excludeQc.value = value === 'exclude'
115
+ autoGroup.setClassDispositions(
116
+ c => c.kind !== 'biological' && c.kind !== 'unknown',
117
+ value === 'exclude' ? 'exclude' : value === 'overlay' ? 'overlay' : 'group',
118
+ )
119
+ }
120
+
121
+ // Group statistics for the 3-stat strip — scoped to the ACTIVE class so the
122
+ // numbers correspond to what the user sees in the schema panel below.
123
+ // (The Preview step shows global totals across all classes.)
124
+ const groupStats = computed(() => {
125
+ const cls = autoGroup.classes.value.find(c => classKey(c) === autoGroup.activeClassKey.value)
126
+ if (!cls) return { count: 0, samples: 0, avg: 0, min: 0, max: 0 }
127
+ const schema = autoGroup.activeSchema.value
128
+ const total = cls.members.length
129
+ const buckets = new Map<string, number>()
130
+ if (!schema || schema.groupBy.length === 0) {
131
+ // No factor columns selected → one group containing everyone.
132
+ if (total > 0) buckets.set('__all__', total)
133
+ } else {
134
+ for (const m of cls.members) {
135
+ const tokens = autoGroup.tokenized.value[m]
136
+ const key = schema.groupBy.map(idx => tokens[idx] ?? '').join(' / ')
137
+ buckets.set(key, (buckets.get(key) ?? 0) + 1)
138
+ }
139
+ }
140
+ const sizes = [...buckets.values()]
141
+ return {
142
+ count: sizes.length,
143
+ samples: total,
144
+ avg: sizes.length > 0 ? Math.round((total / sizes.length) * 10) / 10 : 0,
145
+ min: sizes.length > 0 ? Math.min(...sizes) : 0,
146
+ max: sizes.length > 0 ? Math.max(...sizes) : 0,
147
+ }
148
+ })
149
+
150
+ const activeColumn = computed(() => {
151
+ if (openPopoverIdx.value === null) return null
152
+ return autoGroup.activeSchema.value?.columns.find(c => c.index === openPopoverIdx.value) ?? null
153
+ })
154
+
155
+ function renameActive(name: string) {
156
+ const col = activeColumn.value
157
+ if (!col) return
158
+ autoGroup.setColumnName(autoGroup.activeClassKey.value, col.index, name)
159
+ }
160
+
161
+ function setAlias(src: string, dst: string) {
162
+ const col = activeColumn.value
163
+ if (!col) return
164
+ const next = { ...(col.ops?.alias ?? {}) }
165
+ if (dst === src) delete next[src]
166
+ else next[src] = dst
167
+ autoGroup.setValueOps(autoGroup.activeClassKey.value, col.index, {
168
+ ...col.ops,
169
+ alias: next,
170
+ })
171
+ }
172
+
173
+ function toggleExclude(value: string) {
174
+ const col = activeColumn.value
175
+ if (!col) return
176
+ const list = new Set(col.ops?.exclude ?? [])
177
+ if (list.has(value)) list.delete(value)
178
+ else list.add(value)
179
+ autoGroup.setValueOps(autoGroup.activeClassKey.value, col.index, {
180
+ ...col.ops,
181
+ exclude: [...list],
182
+ })
183
+ }
184
+
185
+ function mergeWithNext() {
186
+ const col = activeColumn.value
187
+ if (!col) return
188
+ const schema = autoGroup.activeSchema.value
189
+ const next = schema?.columns.find(c => c.index === col.index + 1)
190
+ if (!next) return
191
+ autoGroup.mergeColumns(autoGroup.activeClassKey.value, [col.index, next.index])
192
+ openPopoverIdx.value = null
193
+ }
194
+
195
+ // Pre-fill from props.
196
+ // When samples or experiment data are already known we auto-skip the Input
197
+ // step entirely and land on Workspace — there's nothing for the user to
198
+ // paste or upload. The wizard becomes a clean 2-step (Workspace → Preview)
199
+ // in the common case. Input still exists for cold opens (no samples, no
200
+ // experiment) where users paste their own sample list or upload a CSV.
45
201
  watch(() => props.modelValue, (open) => {
46
202
  if (open) {
47
203
  autoGroup.reset()
@@ -51,26 +207,28 @@ watch(() => props.modelValue, (open) => {
51
207
  csvError.value = null
52
208
  if (props.experimentId || props.designData) {
53
209
  fetchExperimentData()
210
+ currentStep.value = 1
54
211
  } else if (props.samples.length > 0) {
55
212
  autoGroup.rawText.value = props.samples.join('\n')
213
+ autoGroup.inputMode.value = 'paste'
214
+ autoGroup.parseInput()
215
+ currentStep.value = 1
216
+ } else {
217
+ // Cold open: default to Paste so users can type their own sample
218
+ // list immediately. CSV upload is one click away in the mode toggle.
219
+ autoGroup.inputMode.value = 'paste'
56
220
  }
57
221
  }
58
222
  }, { immediate: true })
59
223
 
60
- // Dynamic steps: skip outlier step when no outliers
224
+ // Wizard steps: three steps, unconditional
61
225
  const allSteps: WizardStep[] = [
62
226
  { id: 'input', label: 'Input' },
63
- { id: 'outliers', label: 'Outliers' },
64
- { id: 'fields', label: 'Fields' },
227
+ { id: 'workspace', label: 'Workspace' },
65
228
  { id: 'preview', label: 'Preview' },
66
229
  ]
67
230
 
68
- const dynamicSteps = computed<WizardStep[]>(() => {
69
- if (autoGroup.hasOutliers.value) {
70
- return allSteps
71
- }
72
- return allSteps.filter(s => s.id !== 'outliers')
73
- })
231
+ const dynamicSteps = computed<WizardStep[]>(() => allSteps)
74
232
 
75
233
  // Map current step index to step ID
76
234
  const currentStepId = computed(() => {
@@ -87,17 +245,16 @@ watch(() => dynamicSteps.value.length, (newLen) => {
87
245
 
88
246
  // Step validity
89
247
  const inputValid = computed(() => autoGroup.samples.value.length > 0)
90
- const fieldsValid = computed(() => autoGroup.enabledFields.value.size > 0)
248
+ const workspaceValid = computed(() => true) // empty groupBy is allowed; warning is shown in Preview.
91
249
 
92
- watch([inputValid, fieldsValid, dynamicSteps], () => {
250
+ watch([inputValid, workspaceValid, dynamicSteps], () => {
93
251
  if (!wizardRef.value) return
94
252
  const steps = dynamicSteps.value
95
253
  for (let i = 0; i < steps.length; i++) {
96
254
  const id = steps[i].id
97
255
  if (id === 'input') wizardRef.value.setStepValid(i, inputValid.value)
98
- else if (id === 'outliers') wizardRef.value.setStepValid(i, true)
99
- else if (id === 'fields') wizardRef.value.setStepValid(i, fieldsValid.value)
100
- else if (id === 'preview') wizardRef.value.setStepValid(i, true)
256
+ else if (id === 'workspace') wizardRef.value.setStepValid(i, workspaceValid.value)
257
+ else wizardRef.value.setStepValid(i, true)
101
258
  }
102
259
  }, { immediate: true })
103
260
 
@@ -149,11 +306,11 @@ async function fetchExperimentData() {
149
306
  return
150
307
  }
151
308
 
152
- // Auto-advance to Fields step
309
+ // Auto-advance to Workspace step
153
310
  await nextTick()
154
- const fieldsIdx = dynamicSteps.value.findIndex(s => s.id === 'fields')
155
- if (fieldsIdx >= 0) {
156
- currentStep.value = fieldsIdx
311
+ const workspaceIdx = dynamicSteps.value.findIndex(s => s.id === 'workspace')
312
+ if (workspaceIdx >= 0) {
313
+ currentStep.value = workspaceIdx
157
314
  }
158
315
  } catch (e) {
159
316
  experimentError.value = e instanceof Error ? e.message : 'Failed to fetch experiment data'
@@ -196,8 +353,18 @@ async function processFile(file: File) {
196
353
 
197
354
  function clearCsvFile() {
198
355
  autoGroup.csvData.value = null
356
+ // Stay in CSV mode after a clear so the user can pick another file
357
+ // without re-clicking the CSV tab. They can switch to Paste manually.
358
+ autoGroup.inputMode.value = 'csv'
359
+ csvFileName.value = ''
360
+ }
361
+
362
+ function switchToPasteMode() {
199
363
  autoGroup.inputMode.value = 'paste'
364
+ autoGroup.csvData.value = null
365
+ autoGroup.sampleTypeHints.value = []
200
366
  csvFileName.value = ''
367
+ csvError.value = null
201
368
  }
202
369
 
203
370
  // Experiment metadata field names (for display in experiment mode)
@@ -215,6 +382,8 @@ const isFirstStep = computed(() => currentStep.value === 0)
215
382
  :model-value="modelValue"
216
383
  title="Smart Group"
217
384
  size="lg"
385
+ :close-on-overlay="false"
386
+ :close-on-escape="false"
218
387
  @update:model-value="emit('update:modelValue', $event)"
219
388
  @close="handleCancel"
220
389
  >
@@ -229,7 +398,10 @@ const isFirstStep = computed(() => currentStep.value === 0)
229
398
  <!-- Step: Input -->
230
399
  <template #step-input>
231
400
  <div class="mint-auto-group__input-step">
232
- <!-- Mode toggle -->
401
+ <!-- Mode toggle. Paste is the self-designed path (type your
402
+ own sample list); CSV is the structured-upload path; the
403
+ Experiment tab only appears when the parent component
404
+ owns an experimentId / designData. -->
233
405
  <div class="mint-auto-group__mode-toggle">
234
406
  <BaseButton
235
407
  v-if="experimentId || designData"
@@ -243,7 +415,7 @@ const isFirstStep = computed(() => currentStep.value === 0)
243
415
  <BaseButton
244
416
  :variant="autoGroup.inputMode.value === 'paste' ? 'primary' : 'secondary'"
245
417
  size="sm"
246
- @click="autoGroup.inputMode.value = 'paste'"
418
+ @click="switchToPasteMode"
247
419
  >
248
420
  Paste
249
421
  </BaseButton>
@@ -265,7 +437,7 @@ const isFirstStep = computed(() => currentStep.value === 0)
265
437
  <div v-else-if="experimentError" class="mint-auto-group__experiment-error">
266
438
  <p>{{ experimentError }}</p>
267
439
  <p>
268
- Switch to <button type="button" class="mint-auto-group__link-btn" @click="autoGroup.inputMode.value = 'paste'">Paste</button>
440
+ Switch to <button type="button" class="mint-auto-group__link-btn" @click="switchToPasteMode">Paste</button>
269
441
  or <button type="button" class="mint-auto-group__link-btn" @click="autoGroup.inputMode.value = 'csv'">CSV</button> mode instead.
270
442
  </p>
271
443
  </div>
@@ -288,7 +460,8 @@ const isFirstStep = computed(() => currentStep.value === 0)
288
460
  </div>
289
461
  </div>
290
462
 
291
- <!-- Paste mode -->
463
+ <!-- Paste mode: free-form textarea for users to type or paste
464
+ their own sample list (the "self-designed data" path). -->
292
465
  <div v-if="autoGroup.inputMode.value === 'paste'" class="mint-auto-group__paste">
293
466
  <textarea
294
467
  v-model="autoGroup.rawText.value"
@@ -347,134 +520,240 @@ const isFirstStep = computed(() => currentStep.value === 0)
347
520
  </button>
348
521
  </div>
349
522
  </div>
350
- </div>
351
- </template>
352
523
 
353
- <!-- Step: Outliers -->
354
- <template #step-outliers>
355
- <div class="mint-auto-group__outlier-step">
356
- <div class="mint-auto-group__outlier-banner">
357
- {{ autoGroup.outliers.value.length }} of {{ autoGroup.samples.value.length }}
358
- samples have irregular structure
359
- (fewer than {{ autoGroup.dominantFieldCount.value }} fields, delimiter
360
- <code>{{ autoGroup.delimiter.value }}</code>)
361
- </div>
362
-
363
- <div class="mint-auto-group__outlier-batch">
364
- <BaseButton size="sm" variant="secondary" @click="autoGroup.setAllOutlierActions('include')">
365
- Include All
366
- </BaseButton>
367
- <BaseButton size="sm" variant="secondary" @click="autoGroup.setAllOutlierActions('exclude')">
368
- Exclude All
369
- </BaseButton>
370
- </div>
371
-
372
- <div class="mint-auto-group__outlier-list">
373
- <div
374
- v-for="outlier in autoGroup.outliers.value"
375
- :key="outlier.index"
376
- class="mint-auto-group__outlier-item"
377
- >
378
- <div class="mint-auto-group__outlier-info">
379
- <code class="mint-auto-group__outlier-name">{{ outlier.sample }}</code>
380
- <span class="mint-auto-group__outlier-badge">{{ outlier.fieldCount }} fields</span>
381
- </div>
382
- <div class="mint-auto-group__outlier-actions">
383
- <button
384
- type="button"
385
- :class="['mint-auto-group__action-btn', outlier.action === 'include' ? 'mint-auto-group__action-btn--active' : '']"
386
- @click="autoGroup.setOutlierAction(outlier.index, 'include')"
387
- >Include</button>
388
- <button
389
- type="button"
390
- :class="['mint-auto-group__action-btn', outlier.action === 'exclude' ? 'mint-auto-group__action-btn--active mint-auto-group__action-btn--exclude' : '']"
391
- @click="autoGroup.setOutlierAction(outlier.index, 'exclude')"
392
- >Exclude</button>
393
- <button
394
- type="button"
395
- :class="['mint-auto-group__action-btn', outlier.action === 'qc' ? 'mint-auto-group__action-btn--active mint-auto-group__action-btn--qc' : '']"
396
- @click="autoGroup.setOutlierAction(outlier.index, 'qc')"
397
- >QC</button>
398
- </div>
524
+ <div class="mint-auto-group__template" v-if="autoGroup.canDownloadTemplate.value">
525
+ <div class="mint-auto-group__template-label">Or hand-edit in Excel:</div>
526
+ <div class="mint-auto-group__template-actions">
527
+ <BaseButton size="sm" variant="ghost" @click="autoGroup.downloadTemplate('prefilled', 'csv')">
528
+ Download pre-filled CSV
529
+ </BaseButton>
530
+ <BaseButton size="sm" variant="ghost" @click="autoGroup.downloadTemplate('blank', 'csv')">
531
+ Download blank CSV
532
+ </BaseButton>
399
533
  </div>
400
534
  </div>
401
535
  </div>
402
536
  </template>
403
537
 
404
- <!-- Step: Fields -->
405
- <template #step-fields>
406
- <div class="mint-auto-group__field-step">
407
- <div class="mint-auto-group__field-grid">
408
- <div
409
- v-for="col in autoGroup.effectiveColumns.value"
410
- :key="col.index"
538
+ <!-- Step: Workspace -->
539
+ <template #step-workspace>
540
+ <div class="mint-auto-group__workspace">
541
+ <aside class="mint-auto-group__classes" aria-label="Detected sample classes">
542
+ <h3>Detected classes</h3>
543
+ <button
544
+ v-for="cls in autoGroup.classes.value"
545
+ :key="classKey(cls)"
546
+ type="button"
411
547
  :class="[
412
- 'mint-auto-group__field-card',
413
- autoGroup.enabledFields.value.has(col.index) ? 'mint-auto-group__field-card--enabled' : '',
548
+ 'mint-auto-group__class',
549
+ autoGroup.activeClassKey.value === classKey(cls) ? 'mint-auto-group__class--active' : '',
550
+ cls.kind !== 'biological' && cls.kind !== 'unknown' ? 'mint-auto-group__class--qc' : '',
414
551
  ]"
552
+ @click="autoGroup.activeClassKey.value = classKey(cls)"
415
553
  >
416
- <div class="mint-auto-group__field-header">
417
- <label class="mint-auto-group__field-toggle">
554
+ <span class="mint-auto-group__class-dot" />
555
+ <span class="mint-auto-group__class-label">{{ cls.label }}</span>
556
+ <span class="mint-auto-group__class-count">{{ cls.members.length }}</span>
557
+ </button>
558
+
559
+ <div class="mint-auto-group__routing">
560
+ <h4 class="mint-auto-group__routing-head">QC routing</h4>
561
+ <label class="mint-auto-group__routing-radio">
562
+ <input
563
+ type="radio"
564
+ name="qc-routing"
565
+ :checked="qcRouting === 'overlay'"
566
+ @change="setQcRouting('overlay')"
567
+ />
568
+ <span>Overlay <small>preview only</small></span>
569
+ </label>
570
+ <label class="mint-auto-group__routing-radio">
571
+ <input
572
+ type="radio"
573
+ name="qc-routing"
574
+ :checked="qcRouting === 'exclude'"
575
+ @change="setQcRouting('exclude')"
576
+ />
577
+ <span>Exclude entirely</span>
578
+ </label>
579
+ <label class="mint-auto-group__routing-radio">
580
+ <input
581
+ type="radio"
582
+ name="qc-routing"
583
+ :checked="qcRouting === 'mix'"
584
+ @change="setQcRouting('mix')"
585
+ />
586
+ <span>Mix into groups</span>
587
+ </label>
588
+ </div>
589
+ </aside>
590
+
591
+ <section class="mint-auto-group__schema" aria-label="Schema editor">
592
+ <div class="mint-auto-group__stats">
593
+ <div class="mint-auto-group__stat mint-auto-group__stat--primary">
594
+ <div class="mint-auto-group__stat-v">{{ groupStats.count }}</div>
595
+ <div class="mint-auto-group__stat-l">Groups</div>
596
+ <div class="mint-auto-group__stat-sub">
597
+ <span v-if="groupKeyNames.length === 0" class="mint-auto-group__stat-empty">Toggle <strong>Use</strong> on a column to start splitting</span>
598
+ <template v-else>
599
+ <span
600
+ v-for="name in groupKeyNames"
601
+ :key="name"
602
+ class="mint-auto-group__group-chip"
603
+ >{{ name }}</span>
604
+ </template>
605
+ </div>
606
+ </div>
607
+ <div class="mint-auto-group__stat">
608
+ <div class="mint-auto-group__stat-v">{{ activeMemberCount }}</div>
609
+ <div class="mint-auto-group__stat-l">Samples</div>
610
+ <div class="mint-auto-group__stat-sub mint-auto-group__stat-sub--muted">{{ activeSchemaLabel }} · {{ activeColumnCount }} token positions</div>
611
+ </div>
612
+ <div class="mint-auto-group__stat">
613
+ <div class="mint-auto-group__stat-v">{{ groupStats.avg }}</div>
614
+ <div class="mint-auto-group__stat-l">Avg / group</div>
615
+ <div class="mint-auto-group__stat-sub mint-auto-group__stat-sub--muted">
616
+ <template v-if="groupStats.count > 0">Min {{ groupStats.min }} · Max {{ groupStats.max }}</template>
617
+ <template v-else>—</template>
618
+ </div>
619
+ </div>
620
+ </div>
621
+
622
+ <div v-if="autoGroup.suggestions.value.length > 0" class="mint-auto-group__suggest">
623
+ <strong>Suggested merge:</strong>
624
+ columns {{ autoGroup.suggestions.value[0].columnIndices.map((i: number) => i + 1).join(' + ') }}
625
+ always co-occur — combine into <code>{{ autoGroup.suggestions.value[0].proposedName }}</code>?
626
+ <button type="button" class="mint-auto-group__suggest-btn" @click="acceptSuggestion(autoGroup.suggestions.value[0])">
627
+ Accept · merge
628
+ </button>
629
+ <button type="button" class="mint-auto-group__suggest-btn mint-auto-group__suggest-btn--ghost" @click="dismissSuggestion(autoGroup.suggestions.value[0])">
630
+ Dismiss
631
+ </button>
632
+ </div>
633
+
634
+ <div class="mint-auto-group__token-table" role="table" aria-label="Token positions">
635
+ <div class="mint-auto-group__token-row mint-auto-group__token-row--head" role="row">
636
+ <span role="columnheader" aria-label="Token number">#</span>
637
+ <span role="columnheader">Name</span>
638
+ <span role="columnheader">Role</span>
639
+ <span role="columnheader" class="mint-auto-group__token-num" aria-label="Unique value count">Unique</span>
640
+ <span role="columnheader">Preview</span>
641
+ <span role="columnheader" class="mint-auto-group__token-use-head">Use</span>
642
+ <span role="columnheader" aria-label="More actions" />
643
+ </div>
644
+ <button
645
+ v-for="col in (autoGroup.activeSchema.value?.columns ?? [])"
646
+ :key="col.index"
647
+ type="button"
648
+ role="row"
649
+ :class="[
650
+ 'mint-auto-group__token-row',
651
+ openPopoverIdx === col.index ? 'mint-auto-group__token-row--active' : '',
652
+ ]"
653
+ @click="openPopoverIdx = col.index"
654
+ >
655
+ <span class="mint-auto-group__token-ix" role="cell">{{ col.index + 1 }}</span>
656
+ <span class="mint-auto-group__token-nm" role="cell">{{ col.displayName ?? col.name }}</span>
657
+ <span
658
+ :class="['mint-auto-group__role', `mint-auto-group__role--${col.role}`]"
659
+ role="cell"
660
+ >{{ roleLabel(col.role) }}</span>
661
+ <span class="mint-auto-group__token-unique" role="cell">{{ col.cardinality }}</span>
662
+ <span class="mint-auto-group__token-preview" role="cell">
663
+ {{ col.uniqueValues.slice(0, 4).join(', ') }}<span
664
+ v-if="col.uniqueValues.length > 4"
665
+ class="mint-auto-group__token-more"
666
+ > +{{ col.uniqueValues.length - 4 }} more</span>
667
+ </span>
668
+ <label
669
+ class="mint-auto-group__token-use"
670
+ role="cell"
671
+ @click.stop
672
+ >
418
673
  <input
419
674
  type="checkbox"
420
- :checked="autoGroup.enabledFields.value.has(col.index)"
421
- @change="autoGroup.toggleField(col.index)"
675
+ :checked="(autoGroup.activeSchema.value?.groupBy ?? []).includes(col.index)"
676
+ @change="autoGroup.toggleGroupBy(autoGroup.activeClassKey.value, col.index)"
422
677
  />
423
- <span class="mint-auto-group__field-toggle-label">Group by</span>
424
678
  </label>
425
- <span class="mint-auto-group__field-cardinality">{{ col.cardinality }} unique</span>
679
+ <span class="mint-auto-group__token-trigger" role="cell" aria-hidden="true">⋯</span>
680
+ </button>
681
+ </div>
682
+ </section>
683
+
684
+ <div
685
+ v-if="openPopoverIdx !== null && activeColumn"
686
+ class="mint-auto-group__popover"
687
+ @click.stop
688
+ >
689
+ <div class="mint-auto-group__popover-head">
690
+ <strong>{{ activeColumn.displayName ?? activeColumn.name }}</strong>
691
+ <span class="mono">{{ activeColumn.cardinality }} unique</span>
692
+ <button type="button" class="mint-auto-group__popover-close" @click="openPopoverIdx = null">×</button>
693
+ </div>
694
+
695
+ <div class="mint-auto-group__popover-section">
696
+ <div class="mint-auto-group__popover-label">Role</div>
697
+ <div class="mint-auto-group__pills">
698
+ <button
699
+ v-for="r in (['factor','replicate','run-order','numeric','constant','ignore'] as ColumnRole[])"
700
+ :key="r"
701
+ type="button"
702
+ :class="['mint-auto-group__pill-btn', activeColumn.role === r ? 'mint-auto-group__pill-btn--on' : '']"
703
+ @click="autoGroup.setRole(autoGroup.activeClassKey.value, activeColumn.index, r)"
704
+ >{{ roleLabel(r) }}</button>
426
705
  </div>
706
+ </div>
427
707
 
708
+ <div class="mint-auto-group__popover-section">
709
+ <div class="mint-auto-group__popover-label">Rename</div>
428
710
  <BaseInput
429
- :model-value="col.name"
430
- placeholder="Field name..."
431
- class="mint-auto-group__field-name-input"
432
- @update:model-value="autoGroup.renameField(col.index, String($event ?? ''))"
711
+ :model-value="activeColumn.displayName ?? activeColumn.name"
712
+ @update:model-value="renameActive(String($event ?? ''))"
433
713
  />
714
+ </div>
434
715
 
435
- <div class="mint-auto-group__field-values">
436
- <span
437
- v-for="val in col.uniqueValues.slice(0, 5)"
438
- :key="val"
439
- class="mint-auto-group__field-value"
440
- >{{ val }}</span>
441
- <span
442
- v-if="col.uniqueValues.length > 5"
443
- class="mint-auto-group__field-more"
444
- >+{{ col.uniqueValues.length - 5 }} more</span>
716
+ <div class="mint-auto-group__popover-section">
717
+ <div class="mint-auto-group__popover-label">Values ({{ activeColumn.uniqueValues.length }})</div>
718
+ <div class="mint-auto-group__values-list">
719
+ <div
720
+ v-for="v in activeColumn.uniqueValues"
721
+ :key="v"
722
+ class="mint-auto-group__value-row"
723
+ >
724
+ <span class="mint-auto-group__value-src">{{ v }} →</span>
725
+ <input
726
+ class="mint-auto-group__value-input"
727
+ :value="activeColumn.ops?.alias?.[v] ?? v"
728
+ @change="setAlias(v, ($event.target as HTMLInputElement).value)"
729
+ />
730
+ <label class="mint-auto-group__value-excl">
731
+ <input
732
+ type="checkbox"
733
+ :checked="activeColumn.ops?.exclude?.includes(v) ?? false"
734
+ @change="toggleExclude(v)"
735
+ />
736
+ excl
737
+ </label>
738
+ </div>
445
739
  </div>
446
740
  </div>
447
- </div>
448
-
449
- <div v-if="autoGroup.enabledFields.value.size > 0" class="mint-auto-group__field-summary">
450
- Grouping by:
451
- <strong>{{
452
- autoGroup.effectiveColumns.value
453
- .filter(c => autoGroup.enabledFields.value.has(c.index))
454
- .map(c => c.name)
455
- .join(' / ')
456
- }}</strong>
457
- </div>
458
741
 
459
- <div v-if="autoGroup.enabledFields.value.size === 0" class="mint-auto-group__field-warning">
460
- Select at least one field for grouping
742
+ <div class="mint-auto-group__popover-section">
743
+ <button type="button" class="mint-auto-group__link" @click="mergeWithNext">▸ Merge with next column</button>
744
+ </div>
461
745
  </div>
462
746
  </div>
463
747
  </template>
464
748
 
465
749
  <!-- Step: Preview -->
466
750
  <template #step-preview>
467
- <div class="mint-auto-group__preview-step">
751
+ <div class="mint-auto-group__preview">
468
752
  <div class="mint-auto-group__preview-summary">
469
- <span class="mint-auto-group__preview-stat">
470
- <strong>{{ autoGroup.groups.value.length }}</strong> groups
471
- </span>
472
- <span class="mint-auto-group__preview-stat">
473
- <strong>{{ autoGroup.groups.value.reduce((sum, g) => sum + g.samples.length, 0) }}</strong> samples
474
- </span>
475
- <span v-if="autoGroup.excludedSamples.value.length > 0" class="mint-auto-group__preview-stat mint-auto-group__preview-stat--excluded">
476
- <strong>{{ autoGroup.excludedSamples.value.length }}</strong> excluded
477
- </span>
753
+ <strong>{{ autoGroup.groups.value.length }}</strong> groups ·
754
+ <strong>{{ totalGrouped }}</strong> samples ·
755
+ <strong>{{ totalQc }}</strong> QC kept aside ·
756
+ <strong>{{ autoGroup.excludedSamples.value.length }}</strong> excluded
478
757
  </div>
479
758
 
480
759
  <AlertBox
@@ -482,44 +761,48 @@ const isFirstStep = computed(() => currentStep.value === 0)
482
761
  type="warning"
483
762
  title="Each group has only one sample"
484
763
  >
485
- Your enabled fields produce a unique key for every row, so no aggregation is happening. Go back to <em>Fields</em> and disable any column whose values are unique per sample.
764
+ Group key produced a unique key per row. Go back and disable any column whose
765
+ values are unique per sample.
486
766
  </AlertBox>
487
767
 
488
- <div class="mint-auto-group__preview-groups">
489
- <details
490
- v-for="group in autoGroup.groups.value"
491
- :key="group.name"
492
- class="mint-auto-group__preview-group"
493
- >
494
- <summary class="mint-auto-group__preview-group-header">
495
- <span
496
- class="mint-auto-group__preview-dot"
497
- :style="{ backgroundColor: group.color }"
498
- />
499
- <span class="mint-auto-group__preview-group-name">{{ group.name }}</span>
500
- <span
501
- class="mint-auto-group__preview-group-count"
502
- :style="{ backgroundColor: group.color + '20', color: group.color }"
503
- >{{ group.samples.length }}</span>
504
- </summary>
505
- <div class="mint-auto-group__preview-samples">
768
+ <div class="mint-auto-group__preview-grid">
769
+ <div class="mint-auto-group__preview-panel">
770
+ <h4>Experimental groups</h4>
771
+ <details
772
+ v-for="group in (autoGroup.result.value.experimentalGroups ?? [])"
773
+ :key="group.name"
774
+ class="mint-auto-group__preview-group"
775
+ >
776
+ <summary>
777
+ <span class="mint-auto-group__preview-dot" :style="{ background: group.color }" />
778
+ <span>{{ group.name }}</span>
779
+ <span class="mint-auto-group__preview-count">{{ group.samples.length }}</span>
780
+ </summary>
781
+ <div class="mint-auto-group__preview-samples">
782
+ <span
783
+ v-for="s in group.samples"
784
+ :key="s"
785
+ class="mint-auto-group__preview-sample"
786
+ >{{ s }}</span>
787
+ </div>
788
+ </details>
789
+ </div>
790
+
791
+ <div class="mint-auto-group__preview-panel">
792
+ <h4>QC kept aside (overlay)</h4>
793
+ <div class="mint-auto-group__qc-chips">
506
794
  <span
507
- v-for="sample in group.samples"
508
- :key="sample"
509
- class="mint-auto-group__preview-sample"
510
- >{{ sample }}</span>
795
+ v-for="g in (autoGroup.qcGroups.value ?? [])"
796
+ :key="g.name"
797
+ class="mint-auto-group__qc-chip"
798
+ >
799
+ {{ g.name }} <code>×{{ g.samples.length }}</code>
800
+ </span>
511
801
  </div>
512
- </details>
513
- </div>
514
-
515
- <div v-if="autoGroup.excludedSamples.value.length > 0" class="mint-auto-group__preview-excluded">
516
- <div class="mint-auto-group__preview-excluded-title">Excluded Samples</div>
517
- <div class="mint-auto-group__preview-excluded-list">
518
- <span
519
- v-for="sample in autoGroup.excludedSamples.value"
520
- :key="sample"
521
- class="mint-auto-group__preview-sample mint-auto-group__preview-sample--excluded"
522
- >{{ sample }}</span>
802
+ <details class="mint-auto-group__fingerprint">
803
+ <summary>Schema fingerprint</summary>
804
+ <pre>{{ JSON.stringify(autoGroup.fingerprint.value, null, 2) }}</pre>
805
+ </details>
523
806
  </div>
524
807
  </div>
525
808
  </div>