@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
@@ -0,0 +1,128 @@
1
+ <script setup lang="ts">
2
+ /** Toolbar for PlateMapEditor plate tabs and import/export history actions. */
3
+ import type { PlateMap } from '../../types'
4
+
5
+ interface Props {
6
+ plates: PlateMap[]
7
+ activePlateId?: string
8
+ plateWellCounts: Record<string, number>
9
+ canAddPlate: boolean
10
+ canUndo: boolean
11
+ canRedo: boolean
12
+ slotColorForPlate: (plateId: string, plateIndex: number) => string
13
+ }
14
+
15
+ const props = defineProps<Props>()
16
+
17
+ const emit = defineEmits<{
18
+ 'select-plate': [plateId: string]
19
+ 'remove-plate': [plateId: string]
20
+ 'add-plate': []
21
+ undo: []
22
+ redo: []
23
+ import: []
24
+ 'export-json': []
25
+ }>()
26
+ </script>
27
+
28
+ <template>
29
+ <div class="mint-plate-editor__toolbar">
30
+ <div class="mint-plate-editor__tabs">
31
+ <button
32
+ v-for="(plate, index) in plates"
33
+ :key="plate.id"
34
+ type="button"
35
+ :class="['mint-plate-editor__tab', { 'mint-plate-editor__tab--active': plate.id === activePlateId }]"
36
+ @click="emit('select-plate', plate.id)"
37
+ >
38
+ <span
39
+ class="mint-plate-editor__tab-slot"
40
+ :style="{ backgroundColor: slotColorForPlate(plate.id, index) }"
41
+ />
42
+ <span class="mint-plate-editor__tab-name">{{ plate.name }}</span>
43
+ <span
44
+ v-if="plateWellCounts[plate.id] > 0"
45
+ class="mint-plate-editor__tab-count"
46
+ >
47
+ {{ plateWellCounts[plate.id] }}
48
+ </span>
49
+ <button
50
+ v-if="props.plates.length > 1"
51
+ type="button"
52
+ class="mint-plate-editor__tab-remove"
53
+ :aria-label="`Remove ${plate.name}`"
54
+ @click.stop="emit('remove-plate', plate.id)"
55
+ >
56
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
57
+ <path d="M18 6 6 18" /><path d="m6 6 12 12" />
58
+ </svg>
59
+ </button>
60
+ </button>
61
+
62
+ <button
63
+ v-if="canAddPlate"
64
+ type="button"
65
+ class="mint-plate-editor__add-plate"
66
+ aria-label="Add plate"
67
+ @click="emit('add-plate')"
68
+ >
69
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
70
+ <path d="M5 12h14" /><path d="M12 5v14" />
71
+ </svg>
72
+ <span>Add</span>
73
+ </button>
74
+ </div>
75
+
76
+ <div class="mint-plate-editor__spacer" />
77
+
78
+ <div class="mint-plate-editor__actions">
79
+ <button
80
+ type="button"
81
+ :disabled="!canUndo"
82
+ class="mint-plate-editor__action-btn"
83
+ title="Undo (Ctrl+Z)"
84
+ @click="emit('undo')"
85
+ >
86
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
87
+ <path d="M9 14 4 9l5-5" /><path d="M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11" />
88
+ </svg>
89
+ </button>
90
+
91
+ <button
92
+ type="button"
93
+ :disabled="!canRedo"
94
+ class="mint-plate-editor__action-btn"
95
+ title="Redo (Ctrl+Shift+Z)"
96
+ @click="emit('redo')"
97
+ >
98
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
99
+ <path d="m15 14 5-5-5-5" /><path d="M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13" />
100
+ </svg>
101
+ </button>
102
+
103
+ <div class="mint-plate-editor__divider" />
104
+
105
+ <button
106
+ type="button"
107
+ class="mint-plate-editor__action-btn"
108
+ title="Import"
109
+ @click="emit('import')"
110
+ >
111
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
112
+ <path d="M12 3v12" /><path d="m17 8-5-5-5 5" /><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
113
+ </svg>
114
+ </button>
115
+
116
+ <button
117
+ type="button"
118
+ class="mint-plate-editor__action-btn"
119
+ title="Export JSON"
120
+ @click="emit('export-json')"
121
+ >
122
+ <svg fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
123
+ <path d="M12 15V3" /><path d="m7 10 5 5 5-5" /><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
124
+ </svg>
125
+ </button>
126
+ </div>
127
+ </div>
128
+ </template>
@@ -0,0 +1,5 @@
1
+ import type { SampleClass } from '../../types/auto-group'
2
+
3
+ export function classKey(c: Pick<SampleClass, 'kind'> & { subKind?: string }): string {
4
+ return c.subKind ? `${c.kind}:${c.subKind}` : c.kind
5
+ }
@@ -0,0 +1,205 @@
1
+ import type { SampleClass, SampleClassKind, ClassDisposition } from '../../types/auto-group'
2
+ import { classKey } from './classKey'
3
+ import vocabData from './vocab.json'
4
+
5
+ export interface ClassVocab {
6
+ blankPattern: RegExp
7
+ iqcPattern: RegExp
8
+ eqcPattern: RegExp
9
+ standardPattern: RegExp
10
+ standardAllow: Set<string>
11
+ spikeInPattern: RegExp
12
+ stockPattern: RegExp
13
+ /**
14
+ * Matrix lookup: lowercase token → canonical display name. Matching is
15
+ * case-insensitive, and several aliases can collapse to one display name
16
+ * (cell → Cells, medium → Medium).
17
+ */
18
+ matrixVocab: Map<string, string>
19
+ /** Lowercase polarity tokens → POS / NEG. */
20
+ ionizationModes: Map<string, 'POS' | 'NEG'>
21
+ /** Lowercase tokens that flag the next token as the organ subKind. */
22
+ tissueParentTokens: Set<string>
23
+ }
24
+
25
+ type VocabFile = typeof vocabData
26
+ type IonMode = 'POS' | 'NEG'
27
+
28
+ function compileVocab(data: VocabFile): ClassVocab {
29
+ return {
30
+ blankPattern: new RegExp(data.patterns.blank, 'i'),
31
+ iqcPattern: new RegExp(data.patterns.iqc, 'i'),
32
+ eqcPattern: new RegExp(data.patterns.eqc, 'i'),
33
+ standardPattern: new RegExp(data.patterns.standard, 'i'),
34
+ spikeInPattern: new RegExp(data.patterns.spikeIn, 'i'),
35
+ stockPattern: new RegExp(data.patterns.stock, 'i'),
36
+ standardAllow: new Set(data.standardAllow),
37
+ matrixVocab: new Map(Object.entries(data.matrixVocab)),
38
+ ionizationModes: new Map(
39
+ Object.entries(data.ionizationModes).map(([k, v]) => [k, v as IonMode]),
40
+ ),
41
+ tissueParentTokens: new Set(data.tissueParentTokens),
42
+ }
43
+ }
44
+
45
+ export const classVocab: ClassVocab = compileVocab(vocabData)
46
+
47
+ function matrixCanonical(token: string, vocab: ClassVocab): string | undefined {
48
+ return vocab.matrixVocab.get(token.toLowerCase())
49
+ }
50
+
51
+ function isTissueParentToken(token: string, vocab: ClassVocab): boolean {
52
+ return vocab.tissueParentTokens.has(token.toLowerCase())
53
+ }
54
+
55
+ export interface DetectClassOptions {
56
+ vocab?: Partial<ClassVocab>
57
+ /** Per-sample explicit `sample_type` hint (e.g. from experiment design data). */
58
+ sampleTypeHints?: (string | undefined)[]
59
+ }
60
+
61
+ const DEFAULT_DISPOSITION: Record<SampleClassKind, ClassDisposition> = {
62
+ biological: 'group',
63
+ iqc: 'overlay',
64
+ eqc: 'overlay',
65
+ standard: 'overlay',
66
+ 'spike-in': 'overlay',
67
+ stock: 'overlay',
68
+ blank: 'overlay',
69
+ unknown: 'group',
70
+ }
71
+
72
+ function classifyTokens(
73
+ tokens: string[],
74
+ vocab: ClassVocab,
75
+ ): { kind: SampleClassKind; subKind?: string; tagPositions: number[] } {
76
+ // First-match-wins ordering: Blank → IQC → EQC → Standard → SpikeIn → Stock → Biological → Unknown.
77
+ for (let i = 0; i < tokens.length; i++) {
78
+ if (vocab.blankPattern.test(tokens[i])) return { kind: 'blank', tagPositions: [i] }
79
+ }
80
+ for (let i = 0; i < tokens.length; i++) {
81
+ if (vocab.iqcPattern.test(tokens[i])) return { kind: 'iqc', tagPositions: [i] }
82
+ }
83
+ for (let i = 0; i < tokens.length; i++) {
84
+ if (vocab.eqcPattern.test(tokens[i])) return { kind: 'eqc', tagPositions: [i] }
85
+ }
86
+ for (let i = 0; i < tokens.length; i++) {
87
+ if (vocab.standardPattern.test(tokens[i]) || vocab.standardAllow.has(tokens[i])) {
88
+ return { kind: 'standard', tagPositions: [i] }
89
+ }
90
+ }
91
+ for (let i = 0; i < tokens.length; i++) {
92
+ if (vocab.spikeInPattern.test(tokens[i])) return { kind: 'spike-in', tagPositions: [i] }
93
+ }
94
+ for (let i = 0; i < tokens.length; i++) {
95
+ if (vocab.stockPattern.test(tokens[i])) return { kind: 'stock', tagPositions: [i] }
96
+ }
97
+ for (let i = 0; i < tokens.length; i++) {
98
+ const canonical = matrixCanonical(tokens[i], vocab)
99
+ if (canonical) {
100
+ const isTissueParent = isTissueParentToken(tokens[i], vocab)
101
+ const tagPositions = [i]
102
+ if (isTissueParent) {
103
+ // "Tissues"/"Tissue" is the parent group; tag the next token (organ name) as well.
104
+ if (i + 1 < tokens.length) tagPositions.push(i + 1)
105
+ return { kind: 'biological', subKind: 'Tissues', tagPositions }
106
+ }
107
+ // Non-parent matrix token: tag any immediately preceding "Tissues"/"Tissue" parent.
108
+ if (i > 0 && isTissueParentToken(tokens[i - 1], vocab)) {
109
+ tagPositions.unshift(i - 1)
110
+ return { kind: 'biological', subKind: 'Tissues', tagPositions }
111
+ }
112
+ return { kind: 'biological', subKind: canonical, tagPositions }
113
+ }
114
+ }
115
+ return { kind: 'unknown', tagPositions: [] }
116
+ }
117
+
118
+ /**
119
+ * LC-MS batches run positive- and negative-ion-mode acquisitions separately,
120
+ * and the two produce different feature tables that should never be grouped
121
+ * together. When a POS / NEG / positive / negative token is present we suffix
122
+ * it to the class label so the schema builder treats each mode as its own class.
123
+ */
124
+ function ionizationModeFor(
125
+ tokens: string[],
126
+ vocab: ClassVocab,
127
+ ): { mode: IonMode; position: number } | undefined {
128
+ for (let i = 0; i < tokens.length; i++) {
129
+ const mode = vocab.ionizationModes.get(tokens[i].toLowerCase())
130
+ if (mode) return { mode, position: i }
131
+ }
132
+ return undefined
133
+ }
134
+
135
+ function labelFor(kind: SampleClassKind, subKind?: string): string {
136
+ const base: Record<SampleClassKind, string> = {
137
+ biological: 'Biological',
138
+ iqc: 'IQC',
139
+ eqc: 'EQC',
140
+ standard: 'Standard',
141
+ 'spike-in': 'SpikeIn',
142
+ stock: 'Stock',
143
+ blank: 'Blank',
144
+ unknown: 'Unknown',
145
+ }
146
+ return subKind ? `${base[kind]} / ${subKind}` : base[kind]
147
+ }
148
+
149
+ /** Classify each tokenized sample into a SampleClass. */
150
+ export function detectClass(
151
+ tokenizedSamples: string[][],
152
+ options: DetectClassOptions,
153
+ ): SampleClass[] {
154
+ const vocab = { ...classVocab, ...(options.vocab ?? {}) }
155
+ const hints = options.sampleTypeHints ?? []
156
+ const buckets = new Map<string, SampleClass>()
157
+
158
+ for (let i = 0; i < tokenizedSamples.length; i++) {
159
+ const hint = hints[i]?.toLowerCase()
160
+ let kind: SampleClassKind
161
+ let subKind: string | undefined
162
+ let tagPositions: number[]
163
+
164
+ if (hint === 'qc') {
165
+ kind = 'iqc'
166
+ tagPositions = []
167
+ } else if (hint === 'blank') {
168
+ kind = 'blank'
169
+ tagPositions = []
170
+ } else {
171
+ const r = classifyTokens(tokenizedSamples[i], vocab)
172
+ kind = r.kind
173
+ subKind = r.subKind
174
+ tagPositions = r.tagPositions
175
+ }
176
+
177
+ // Split classes by ionization mode (POS / NEG) so the two acquisitions get
178
+ // independent schemas even when they share a matrix and sample-type vocab.
179
+ const mode = ionizationModeFor(tokenizedSamples[i], vocab)
180
+ if (mode) {
181
+ subKind = subKind ? `${subKind} / ${mode.mode}` : mode.mode
182
+ if (!tagPositions.includes(mode.position)) tagPositions.push(mode.position)
183
+ }
184
+
185
+ const key = classKey({ kind, subKind })
186
+ const bucket = buckets.get(key)
187
+ if (bucket) {
188
+ bucket.members.push(i)
189
+ for (const p of tagPositions) {
190
+ if (!bucket.classTagPositions.includes(p)) bucket.classTagPositions.push(p)
191
+ }
192
+ } else {
193
+ buckets.set(key, {
194
+ kind,
195
+ subKind,
196
+ label: labelFor(kind, subKind),
197
+ members: [i],
198
+ classTagPositions: [...tagPositions],
199
+ disposition: DEFAULT_DISPOSITION[kind],
200
+ })
201
+ }
202
+ }
203
+
204
+ return [...buckets.values()]
205
+ }
@@ -0,0 +1,6 @@
1
+ export const DEFAULT_COLORS = [
2
+ '#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6',
3
+ '#EC4899', '#06B6D4', '#84CC16', '#F97316', '#6366F1',
4
+ ]
5
+
6
+ export const QC_OVERLAY_COLOR = '#6B7280'
@@ -0,0 +1,226 @@
1
+ import type {
2
+ ColumnInfo,
3
+ ColumnRole,
4
+ ClassSchema,
5
+ NumericParse,
6
+ SampleClassKind,
7
+ } from '../../types/auto-group'
8
+
9
+ const NUMERIC_VALUE_UNIT = /^(\d+(?:dot\d+)?)([A-Za-z%/μ]+)$/
10
+ const RATIO = /^(\d+)to(\d+)$/
11
+ const RUN_ORDER_PATTERN = /^\d{2,4}[A-Z]?$/
12
+ // Explicit replicate-token vocabulary: rep1 / Rep2 / REP3 / replicate1 / R1.
13
+ // These tokens almost always identify a within-group replicate dimension that
14
+ // users do NOT want in the default group key — e.g. ctrl/rep1, ctrl/rep2
15
+ // should land in one Ctrl group with two replicates, not two singleton groups.
16
+ const REPLICATE_TOKEN_PATTERN = /^(?:rep(?:licate)?|r)\d+$/i
17
+
18
+ // Real-world LC-MS batches often have 30+ distinct conditions (subject IDs,
19
+ // calibration levels, treatment combinations) in a single class. The earlier
20
+ // cutoff of 20 routed everything above it to `ignore`, leaving such columns
21
+ // out of the default groupBy — meaning a single giant group with no useful
22
+ // split. 50 covers the common cases without making singleton-heavy columns
23
+ // (sample IDs, timestamps) into factors.
24
+ const DEFAULT_FACTOR_CARDINALITY_MAX = 50
25
+ // Numerics with only a handful of distinct levels (e.g. `5min` / `10min`,
26
+ // `1nM` / `5nM` / `25nM`) read as factor levels by default. Users opt into
27
+ // numeric binning explicitly via the column popover when they want it.
28
+ const LOW_CARDINALITY_NUMERIC_AS_FACTOR_MAX = 5
29
+
30
+ export function parseNumeric(token: string): NumericParse | null {
31
+ const ratio = RATIO.exec(token)
32
+ if (ratio) {
33
+ const num = Number(ratio[1])
34
+ const den = Number(ratio[2])
35
+ if (den === 0) return null
36
+ return { value: num / den, unit: 'ratio', raw: token }
37
+ }
38
+ const m = NUMERIC_VALUE_UNIT.exec(token)
39
+ if (!m) return null
40
+ const valueStr = m[1].replace(/dot/g, '.')
41
+ const value = Number(valueStr)
42
+ if (Number.isNaN(value)) return null
43
+ return { value, unit: m[2], raw: token }
44
+ }
45
+
46
+ export interface InferRoleInput {
47
+ values: string[]
48
+ position: number
49
+ totalPositions: number
50
+ classSize: number
51
+ }
52
+
53
+ export function inferRole(input: InferRoleInput): ColumnRole {
54
+ const { values, position, totalPositions, classSize } = input
55
+ const unique = new Set(values)
56
+ const cardinality = unique.size
57
+
58
+ if (cardinality === 1) return 'constant'
59
+
60
+ // run-order: trailing position, all values match \d{2,4}[A-Z]? and cardinality is high.
61
+ if (position === totalPositions - 1) {
62
+ const allRunLike = values.every(v => RUN_ORDER_PATTERN.test(v))
63
+ if (allRunLike && cardinality >= classSize * 0.5) return 'run-order'
64
+ }
65
+
66
+ // numeric: every value parses as value+unit (or ratio).
67
+ const allNumeric = values.every(v => parseNumeric(v) !== null)
68
+ if (allNumeric) {
69
+ // A column like 5min / 10min reads as a factor in practice — users want to
70
+ // group on the timepoint, not bin it. Numeric binning UI is opt-in via the
71
+ // column popover for columns the user explicitly wants binned.
72
+ if (cardinality <= LOW_CARDINALITY_NUMERIC_AS_FACTOR_MAX) return 'factor'
73
+ return 'numeric'
74
+ }
75
+
76
+ // replicate by explicit name (rep1, Rep2, replicate3, R1) — regardless of
77
+ // cardinality. The cardinality-based ID_PATTERN check that used to live
78
+ // here (allId && cardinality >= classSize * 0.5) was removed: with the
79
+ // replicate pre-grouping pass running upstream, real replicates have
80
+ // already been collapsed by name, so a column like C1/C2/M1/M2 reaching
81
+ // role inference is almost certainly a distinct-condition factor, not a
82
+ // replicate. Users who want a residual replicate column can still mark
83
+ // it via the Workspace popover.
84
+ const allReplicateNamed = values.every(v => REPLICATE_TOKEN_PATTERN.test(v))
85
+ if (allReplicateNamed) return 'replicate'
86
+
87
+ if (cardinality <= DEFAULT_FACTOR_CARDINALITY_MAX) return 'factor'
88
+
89
+ return 'ignore'
90
+ }
91
+
92
+ /**
93
+ * Auto-detect a friendlier column name from the column's values + role.
94
+ * Returns null when nothing matches, so callers can fall back to "Token N".
95
+ *
96
+ * Heuristics are content-derived and deterministic. They cover the common
97
+ * LC-MS / proteomics naming conventions; users can override anything via
98
+ * the column popover (rename input).
99
+ */
100
+ function inferColumnName(
101
+ values: string[],
102
+ uniqueValues: string[],
103
+ role: ColumnRole,
104
+ ): string | null {
105
+ if (uniqueValues.length === 0) return null
106
+
107
+ // Numeric+unit columns name themselves by the dominant unit category.
108
+ // We tally all values so a mixed-unit column (`1uL`, `1nM`) picks the
109
+ // majority rather than the first sample's unit.
110
+ const allNumeric = (role === 'numeric' || role === 'factor') && values.every(v => parseNumeric(v) !== null)
111
+ if (allNumeric) {
112
+ const categoryFor = (u: string): string | null => {
113
+ const ul = u.toLowerCase()
114
+ if (['min', 'sec', 's', 'hr', 'h', 'd', 'day', 'days', 'wk', 'week', 'hour', 'hours'].includes(ul)) return 'Timepoint'
115
+ if (['nm', 'um', 'mm', 'm', 'molar', 'mol'].includes(ul)) return 'Concentration'
116
+ if (['mg', 'g', 'kg', 'ng', 'ug', 'pg'].includes(ul)) return 'Mass'
117
+ if (['ml', 'l', 'ul', 'nl', 'pl'].includes(ul)) return 'Volume'
118
+ if (ul === '%') return 'Percentage'
119
+ if (['c', 'k', 'celsius', 'kelvin'].includes(ul)) return 'Temperature'
120
+ return null
121
+ }
122
+ const tally = new Map<string, number>()
123
+ for (const v of values) {
124
+ const p = parseNumeric(v)
125
+ const cat = p ? categoryFor(p.unit) : null
126
+ if (cat) tally.set(cat, (tally.get(cat) ?? 0) + 1)
127
+ }
128
+ let best: { cat: string; count: number } | null = null
129
+ for (const [cat, count] of tally) {
130
+ if (!best || count > best.count) best = { cat, count }
131
+ }
132
+ if (best && best.count >= values.length / 2) return best.cat
133
+ }
134
+
135
+ // Date-like all-digits tokens (YYMMDD or YYYYMMDD).
136
+ if (values.every(v => /^\d{6}$/.test(v) || /^\d{8}$/.test(v))) return 'Date'
137
+
138
+ // Run-order column already has the right semantic role.
139
+ if (role === 'run-order') return 'Run Order'
140
+
141
+ // Polarity tokens (POS/NEG/positive/negative).
142
+ if (values.every(v => /^(pos|neg|positive|negative)$/i.test(v))) return 'Polarity'
143
+
144
+ // Common biological matrices — kept in sync with classify.ts matrixVocab.
145
+ if (values.every(v => /^(plasma|serum|tissue|tissues|kidney|liver|tumor|urine|brain|muscle|adipose|csf|feces|faeces|cells?|media|medium|extract)$/i.test(v))) {
146
+ return 'Matrix'
147
+ }
148
+
149
+ // Experiment / study identifiers (exp268, study42, expt7).
150
+ if (values.every(v => /^(exp|expt|study)\d+$/i.test(v))) return 'Experiment'
151
+
152
+ // Operator / Username (2-3 uppercase letters per value, low cardinality).
153
+ // Constant or small-set 2-3-letter all-caps columns are almost always
154
+ // operator initials (LCK, JD, AB). Larger 2-4-letter columns could be
155
+ // project codes — disambiguation happens after the loop via the
156
+ // `Username → Project` post-pass.
157
+ if (uniqueValues.length <= 4 && values.every(v => /^[A-Z]{2,3}$/.test(v))) return 'Username'
158
+
159
+ return null
160
+ }
161
+
162
+ export function buildClassSchema(
163
+ tokenizedMembers: string[][],
164
+ classKind: SampleClassKind,
165
+ subKind: string | undefined,
166
+ classTagPositions: number[],
167
+ ): ClassSchema {
168
+ const totalPositions = tokenizedMembers[0]?.length ?? 0
169
+ const classSize = tokenizedMembers.length
170
+
171
+ const columns: ColumnInfo[] = []
172
+ const tagSet = new Set(classTagPositions)
173
+
174
+ for (let pos = 0; pos < totalPositions; pos++) {
175
+ const values = tokenizedMembers.map(t => t[pos] ?? '')
176
+ const uniqueValues = [...new Set(values)]
177
+
178
+ let role: ColumnRole
179
+ if (tagSet.has(pos) && uniqueValues.length === 1) {
180
+ // Only constant tag positions become class-tag; multi-valued positions (e.g. organ
181
+ // type within a Tissues class) fall through to normal role inference so they can be
182
+ // used as factor columns for grouping.
183
+ role = 'class-tag'
184
+ } else {
185
+ role = inferRole({ values, position: pos, totalPositions, classSize })
186
+ }
187
+
188
+ const numeric =
189
+ role === 'numeric'
190
+ ? {
191
+ byValue: Object.fromEntries(
192
+ uniqueValues
193
+ .map(v => [v, parseNumeric(v)] as const)
194
+ .filter((e): e is readonly [string, NumericParse] => e[1] !== null),
195
+ ),
196
+ }
197
+ : undefined
198
+
199
+ const inferredName = inferColumnName(values, uniqueValues, role)
200
+ columns.push({
201
+ index: pos,
202
+ name: inferredName ?? `Token ${pos + 1}`,
203
+ sourceIndices: [pos],
204
+ uniqueValues,
205
+ cardinality: uniqueValues.length,
206
+ role,
207
+ numeric,
208
+ // Legacy field
209
+ type: pos === 0 ? 'prefix' : 'suffix',
210
+ })
211
+ }
212
+
213
+ // Disambiguate Username when there are multiple constant 2-4 letter columns:
214
+ // first hit stays "Username", subsequent ones become "Project".
215
+ let usernameAssigned = false
216
+ for (const c of columns) {
217
+ if (c.name === 'Username') {
218
+ if (usernameAssigned) c.name = 'Project'
219
+ else usernameAssigned = true
220
+ }
221
+ }
222
+
223
+ const groupBy = columns.filter(c => c.role === 'factor').map(c => c.index)
224
+
225
+ return { classKind, subKind, columns, groupBy }
226
+ }