@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.
- package/dist/__tests__/components/AppTopBar.navigation.test.d.ts +1 -0
- package/dist/__tests__/components/DoseCalculatorVolumeField.test.d.ts +1 -0
- package/dist/__tests__/components/PlateMapEditorToolbarInternal.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.controls.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.navigation.test.d.ts +1 -0
- package/dist/__tests__/components/PluginWorkspaceView.shell.test.d.ts +1 -0
- package/dist/__tests__/components/ProtocolStep.presentation.test.d.ts +1 -0
- package/dist/__tests__/components/ProtocolStepEditor.state.test.d.ts +1 -0
- package/dist/__tests__/components/ProtocolStepParameterField.test.d.ts +1 -0
- package/dist/__tests__/components/ReagentList.presentation.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.colors.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.drag.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.groups.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelector.selection.test.d.ts +1 -0
- package/dist/__tests__/components/SampleSelectorSampleRow.test.d.ts +1 -0
- package/dist/__tests__/components/ScheduleCalendar.test.d.ts +1 -0
- package/dist/__tests__/components/SettingsModal.schema.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.colors.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.conditions.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.geometry.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.interaction.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.legend.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.rendering.test.d.ts +1 -0
- package/dist/__tests__/components/WellPlate.sampleDrop.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/classify.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/columns.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/compose.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/cooccurrence.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/fingerprint.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/integration.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/template.test.d.ts +1 -0
- package/dist/__tests__/composables/autoGroup/tokenize.test.d.ts +1 -0
- package/dist/__tests__/composables/useAutoGroupInputSources.test.d.ts +1 -0
- package/dist/__tests__/composables/useScheduleCalendarLayout.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsComponents.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsExports.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsParsing.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsTemplates.test.d.ts +1 -0
- package/dist/__tests__/docs/extractDocsTheme.test.d.ts +1 -0
- package/dist/components/AppTopBar.navigation.d.ts +11 -0
- package/dist/components/BaseButton.vue.d.ts +1 -1
- package/dist/components/BaseCheckbox.vue.d.ts +1 -1
- package/dist/components/BaseInput.vue.d.ts +2 -2
- package/dist/components/BasePill.vue.d.ts +1 -1
- package/dist/components/BaseRadioGroup.vue.d.ts +1 -1
- package/dist/components/BaseSelect.vue.d.ts +1 -1
- package/dist/components/BaseSlider.vue.d.ts +2 -2
- package/dist/components/BaseTextarea.vue.d.ts +2 -2
- package/dist/components/BaseToggle.vue.d.ts +1 -1
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
- package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -1
- package/dist/components/ColorSlider.vue.d.ts +2 -2
- package/dist/components/ConcentrationInput.vue.d.ts +2 -2
- package/dist/components/DatePicker.vue.d.ts +1 -1
- package/dist/components/DateTimePicker.vue.d.ts +2 -2
- package/dist/components/DoseCalculatorVolumeField.vue.d.ts +15 -0
- package/dist/components/DropdownButton.vue.d.ts +1 -1
- package/dist/components/FileUploader.vue.d.ts +2 -2
- package/dist/components/FormulaInput.vue.d.ts +2 -2
- package/dist/components/IconButton.vue.d.ts +1 -1
- package/dist/components/LoadingSpinner.vue.d.ts +1 -1
- package/dist/components/MoleculeInput.vue.d.ts +2 -2
- package/dist/components/MultiSelect.vue.d.ts +1 -1
- package/dist/components/NumberInput.vue.d.ts +1 -1
- package/dist/components/PlateMapEditor.vue.d.ts +6 -6
- package/dist/components/PluginWorkspaceView.controls.d.ts +28 -0
- package/dist/components/PluginWorkspaceView.navigation.d.ts +29 -0
- package/dist/components/PluginWorkspaceView.props.d.ts +151 -0
- package/dist/components/PluginWorkspaceView.shell.d.ts +19 -0
- package/dist/components/PluginWorkspaceView.vue.d.ts +46 -195
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ProtocolStep.presentation.d.ts +4 -0
- package/dist/components/ProtocolStepEditor.state.d.ts +18 -0
- package/dist/components/ProtocolStepParameterField.vue.d.ts +12 -0
- package/dist/components/ReagentList.presentation.d.ts +16 -0
- package/dist/components/ResourceCard.vue.d.ts +1 -1
- package/dist/components/SampleSelector.colors.d.ts +13 -0
- package/dist/components/SampleSelector.drag.d.ts +24 -0
- package/dist/components/SampleSelector.groups.d.ts +15 -0
- package/dist/components/SampleSelector.selection.d.ts +26 -0
- package/dist/components/SampleSelector.vue.d.ts +4 -1
- package/dist/components/SampleSelectorSampleRow.vue.d.ts +21 -0
- package/dist/components/SegmentedControl.vue.d.ts +1 -1
- package/dist/components/SequenceInput.vue.d.ts +2 -2
- package/dist/components/SequenceProgressBar.vue.d.ts +1 -1
- package/dist/components/SettingsModal.schema.d.ts +9 -0
- package/dist/components/StatusIndicator.vue.d.ts +1 -1
- package/dist/components/TagsInput.vue.d.ts +2 -2
- package/dist/components/TimePicker.vue.d.ts +2 -2
- package/dist/components/TimeRangeInput.vue.d.ts +1 -1
- package/dist/components/UnitInput.vue.d.ts +2 -2
- package/dist/components/WellPlate.colors.d.ts +9 -0
- package/dist/components/WellPlate.conditions.d.ts +26 -0
- package/dist/components/WellPlate.geometry.d.ts +23 -0
- package/dist/components/WellPlate.interaction.d.ts +71 -0
- package/dist/components/WellPlate.legend.d.ts +2 -0
- package/dist/components/WellPlate.rendering.d.ts +24 -0
- package/dist/components/WellPlate.sampleDrop.d.ts +8 -0
- package/dist/components/WellPlate.vue.d.ts +1 -1
- package/dist/components/index.js +2 -2
- package/dist/components/internal/ActionItemInternal.vue.d.ts +1 -1
- package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +28 -0
- package/dist/{components-BhK-dW99.js → components-DtHA2bgp.js} +3754 -2991
- package/dist/components-DtHA2bgp.js.map +1 -0
- package/dist/composables/autoGroup/classKey.d.ts +4 -0
- package/dist/composables/autoGroup/classify.d.ts +28 -0
- package/dist/composables/autoGroup/colors.d.ts +2 -0
- package/dist/composables/autoGroup/columns.d.ts +10 -0
- package/dist/composables/autoGroup/compose.d.ts +8 -0
- package/dist/composables/autoGroup/cooccurrence.d.ts +2 -0
- package/dist/composables/autoGroup/csv-shim.d.ts +2 -0
- package/dist/composables/autoGroup/fingerprint.d.ts +3 -0
- package/dist/composables/autoGroup/index.d.ts +16 -0
- package/dist/composables/autoGroup/replicatePreGroup.d.ts +38 -0
- package/dist/composables/autoGroup/template.d.ts +15 -0
- package/dist/composables/autoGroup/tokenize.d.ts +8 -0
- package/dist/composables/autoGroupConstants.d.ts +1 -0
- package/dist/composables/autoGroupGrouping.d.ts +3 -0
- package/dist/composables/controlComponentBindings.d.ts +7 -0
- package/dist/composables/controlSchemaAdapters.d.ts +20 -0
- package/dist/composables/controlSchemaDoseDesign.d.ts +11 -0
- package/dist/composables/controlSchemaFormFields.d.ts +3 -0
- package/dist/composables/controlSchemaLayout.d.ts +7 -0
- package/dist/composables/controlSchemaModel.d.ts +5 -0
- package/dist/composables/controlSchemaNormalize.d.ts +15 -0
- package/dist/composables/controlSchemaTypes.d.ts +305 -0
- package/dist/composables/controlSchemaUtils.d.ts +9 -0
- package/dist/composables/controlWorkspaceOptions.d.ts +2 -0
- package/dist/composables/formBuilderSchema.d.ts +18 -0
- package/dist/composables/index.js +3 -3
- package/dist/composables/pluginEndpointBuilder.d.ts +13 -0
- package/dist/composables/protocolTemplateCatalog.d.ts +26 -0
- package/dist/composables/useAutoGroup.d.ts +61 -74
- package/dist/composables/useAutoGroupInputSources.d.ts +32 -0
- package/dist/composables/useBioTemplateControls.d.ts +1 -1
- package/dist/composables/useBioTemplatePresetWorkspace.d.ts +1 -1
- package/dist/composables/useBioTemplateWorkspace.d.ts +1 -1
- package/dist/composables/useControlSchema.d.ts +8 -346
- package/dist/composables/useControlWorkspace.d.ts +5 -0
- package/dist/composables/useForm.d.ts +2 -33
- package/dist/composables/useFormBuilder.d.ts +2 -9
- package/dist/composables/useFormValidation.d.ts +34 -0
- package/dist/composables/usePluginClient.d.ts +1 -4
- package/dist/composables/useProtocolTemplates.d.ts +2 -24
- package/dist/composables/useScheduleCalendarLayout.d.ts +49 -0
- package/dist/{composables-Bg7CFuNz.js → composables-Dlg8jenH.js} +33 -31
- package/dist/composables-Dlg8jenH.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/install.js +2 -2
- package/dist/styles.css +547 -516
- package/dist/templates/adapters.d.ts +14 -47
- package/dist/templates/assayLookups.d.ts +3 -0
- package/dist/templates/assayMatrixAdapters.d.ts +6 -0
- package/dist/templates/assayMatrixBuilder.d.ts +2 -0
- package/dist/templates/assayNormalizers.d.ts +4 -0
- package/dist/templates/builderDefaults.d.ts +1 -0
- package/dist/templates/builderIdUtils.d.ts +4 -0
- package/dist/templates/builderPresetControls.d.ts +10 -0
- package/dist/templates/builderReadUtils.d.ts +8 -0
- package/dist/templates/builders.d.ts +26 -67
- package/dist/templates/calibrationCurveAdapters.d.ts +4 -0
- package/dist/templates/calibrationCurveBuilder.d.ts +2 -0
- package/dist/templates/calibrationNormalizers.d.ts +5 -0
- package/dist/templates/componentBindingCatalog.d.ts +25 -0
- package/dist/templates/componentBindingHelpers.d.ts +17 -0
- package/dist/templates/componentDoseResponseProps.d.ts +3 -0
- package/dist/templates/componentGenericProps.d.ts +8 -0
- package/dist/templates/componentPlateHelpers.d.ts +5 -0
- package/dist/templates/componentPlateMapProps.d.ts +3 -0
- package/dist/templates/componentPropsFactory.d.ts +3 -0
- package/dist/templates/componentQpcrPlateProps.d.ts +3 -0
- package/dist/templates/componentTargetResolvers.d.ts +5 -0
- package/dist/templates/componentTemplateProps.d.ts +3 -0
- package/dist/templates/controlSchemaClone.d.ts +4 -0
- package/dist/templates/controlSchemaConstants.d.ts +10 -0
- package/dist/templates/controlSchemaMerge.d.ts +3 -0
- package/dist/templates/controlSchemaTargets.d.ts +17 -0
- package/dist/templates/controlSchemaTypes.d.ts +4 -0
- package/dist/templates/controlSchemas.d.ts +4 -4
- package/dist/templates/defaultBioTemplateBuilder.d.ts +2 -0
- package/dist/templates/doseResponseAdapters.d.ts +4 -0
- package/dist/templates/doseResponseBuilder.d.ts +2 -0
- package/dist/templates/elisaAssayCollectionBuilder.d.ts +2 -0
- package/dist/templates/flowCytometryAssayCollectionBuilder.d.ts +2 -0
- package/dist/templates/flowCytometryPanelBuilder.d.ts +2 -0
- package/dist/templates/flowNormalizers.d.ts +8 -0
- package/dist/templates/flowPanelAdapters.d.ts +4 -0
- package/dist/templates/index.js +1 -1
- package/dist/templates/instrumentRunAdapterHelpers.d.ts +8 -0
- package/dist/templates/instrumentRunAdapters.d.ts +8 -0
- package/dist/templates/instrumentRunBuilder.d.ts +2 -0
- package/dist/templates/lcmsBatchCollectionBuilder.d.ts +2 -0
- package/dist/templates/plateGeometry.d.ts +4 -0
- package/dist/templates/plateMapAdapters.d.ts +3 -0
- package/dist/templates/plateMapBuilder.d.ts +2 -0
- package/dist/templates/presetControlSchemas.d.ts +534 -0
- package/dist/templates/protocolAdapters.d.ts +5 -0
- package/dist/templates/protocolNormalizers.d.ts +6 -0
- package/dist/templates/protocolStepsBuilder.d.ts +2 -0
- package/dist/templates/qpcrAdapters.d.ts +5 -0
- package/dist/templates/qpcrExpressionCollectionBuilder.d.ts +2 -0
- package/dist/templates/qpcrPlateBuilder.d.ts +2 -0
- package/dist/templates/reagentAdapters.d.ts +5 -0
- package/dist/templates/reagentListBuilder.d.ts +2 -0
- package/dist/templates/runNormalizers.d.ts +10 -0
- package/dist/templates/sampleNormalizers.d.ts +4 -0
- package/dist/templates/samplePrepAdapters.d.ts +4 -0
- package/dist/templates/samplePrepBuilder.d.ts +2 -0
- package/dist/templates/sampleSheetAdapters.d.ts +5 -0
- package/dist/templates/sampleSheetBuilder.d.ts +2 -0
- package/dist/templates/targetedMetabolomicsCollectionBuilder.d.ts +2 -0
- package/dist/templates/targetedMetabolomicsHelpers.d.ts +5 -0
- package/dist/templates/templateAdapterTypes.d.ts +48 -0
- package/dist/templates/templateControlSchemas.d.ts +400 -0
- package/dist/templates/templateCreateOptions.d.ts +165 -0
- package/dist/templates/templateEnvelopes.d.ts +9 -0
- package/dist/templates/templatePackCollectionBuilder.d.ts +2 -0
- package/dist/templates/templatePresetCollectionBuilder.d.ts +18 -0
- package/dist/templates/templateQpcrTypes.d.ts +42 -0
- package/dist/templates/templateValidators.d.ts +13 -0
- package/dist/templates/timeCourseAdapters.d.ts +5 -0
- package/dist/templates/timeCourseBuilder.d.ts +2 -0
- package/dist/templates/types.d.ts +5 -250
- package/dist/templates/wellPlateScreenCollectionBuilder.d.ts +2 -0
- package/dist/templates/westernBlotAssayCollectionBuilder.d.ts +2 -0
- package/dist/{templates-BorLR_7p.js → templates-DtdUvJ4c.js} +3565 -3411
- package/dist/templates-DtdUvJ4c.js.map +1 -0
- package/dist/types/auto-group.d.ts +79 -9
- package/dist/types/componentLabTypes.d.ts +161 -0
- package/dist/types/componentWorkflowTypes.d.ts +150 -0
- package/dist/types/components.d.ts +2 -311
- package/dist/{useProtocolTemplates-n6AJqSqv.js → useProtocolTemplates-Bm5vyH4_.js} +1220 -454
- package/dist/useProtocolTemplates-Bm5vyH4_.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/components/AppTopBar.navigation.test.ts +70 -0
- package/src/__tests__/components/DoseCalculatorVolumeField.test.ts +53 -0
- package/src/__tests__/components/PlateMapEditorToolbarInternal.test.ts +54 -0
- package/src/__tests__/components/PluginWorkspaceView.controls.test.ts +156 -0
- package/src/__tests__/components/PluginWorkspaceView.navigation.test.ts +102 -0
- package/src/__tests__/components/PluginWorkspaceView.shell.test.ts +41 -0
- package/src/__tests__/components/ProtocolStep.presentation.test.ts +31 -0
- package/src/__tests__/components/ProtocolStepEditor.state.test.ts +165 -0
- package/src/__tests__/components/ProtocolStepParameterField.test.ts +44 -0
- package/src/__tests__/components/ReagentList.presentation.test.ts +68 -0
- package/src/__tests__/components/SampleSelector.colors.test.ts +49 -0
- package/src/__tests__/components/SampleSelector.drag.test.ts +100 -0
- package/src/__tests__/components/SampleSelector.groups.test.ts +81 -0
- package/src/__tests__/components/SampleSelector.selection.test.ts +70 -0
- package/src/__tests__/components/SampleSelector.test.ts +32 -0
- package/src/__tests__/components/SampleSelectorSampleRow.test.ts +37 -0
- package/src/__tests__/components/ScheduleCalendar.test.ts +44 -0
- package/src/__tests__/components/SettingsModal.schema.test.ts +97 -0
- package/src/__tests__/components/WellPlate.colors.test.ts +28 -0
- package/src/__tests__/components/WellPlate.conditions.test.ts +68 -0
- package/src/__tests__/components/WellPlate.geometry.test.ts +54 -0
- package/src/__tests__/components/WellPlate.interaction.test.ts +171 -0
- package/src/__tests__/components/WellPlate.legend.test.ts +13 -0
- package/src/__tests__/components/WellPlate.rendering.test.ts +122 -0
- package/src/__tests__/components/WellPlate.sampleDrop.test.ts +70 -0
- package/src/__tests__/composables/autoGroup/classify.test.ts +107 -0
- package/src/__tests__/composables/autoGroup/columns.test.ts +135 -0
- package/src/__tests__/composables/autoGroup/compose.test.ts +227 -0
- package/src/__tests__/composables/autoGroup/cooccurrence.test.ts +91 -0
- package/src/__tests__/composables/autoGroup/fingerprint.test.ts +50 -0
- package/src/__tests__/composables/autoGroup/integration.test.ts +79 -0
- package/src/__tests__/composables/autoGroup/template.test.ts +70 -0
- package/src/__tests__/composables/autoGroup/tokenize.test.ts +33 -0
- package/src/__tests__/composables/useAutoGroup.test.ts +129 -625
- package/src/__tests__/composables/useAutoGroupInputSources.test.ts +107 -0
- package/src/__tests__/composables/useControlSchema.test.ts +23 -0
- package/src/__tests__/composables/useScheduleCalendarLayout.test.ts +89 -0
- package/src/__tests__/docs/extractDocsComponents.test.ts +142 -0
- package/src/__tests__/docs/extractDocsExports.test.ts +77 -0
- package/src/__tests__/docs/extractDocsParsing.test.ts +69 -0
- package/src/__tests__/docs/extractDocsTemplates.test.ts +54 -0
- package/src/__tests__/docs/extractDocsTheme.test.ts +89 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +1 -1
- package/src/__tests__/fixtures/auto-group/mixed-lc-ms-batch.txt +187 -0
- package/src/components/AppSidebar.vue +2 -6
- package/src/components/AppTopBar.navigation.ts +62 -0
- package/src/components/AppTopBar.vue +17 -44
- package/src/components/AutoGroupModal.story.vue +50 -0
- package/src/components/AutoGroupModal.vue +441 -158
- package/src/components/ControlWorkspaceView.vue +2 -6
- package/src/components/DoseCalculator.vue +13 -73
- package/src/components/DoseCalculatorVolumeField.vue +61 -0
- package/src/components/ExperimentTimeline.vue +6 -31
- package/src/components/FormBuilder.vue +2 -7
- package/src/components/PlateMapEditor.vue +32 -106
- package/src/components/PluginWorkspaceView.controls.ts +182 -0
- package/src/components/PluginWorkspaceView.navigation.ts +106 -0
- package/src/components/PluginWorkspaceView.props.ts +174 -0
- package/src/components/PluginWorkspaceView.shell.ts +66 -0
- package/src/components/PluginWorkspaceView.vue +85 -404
- package/src/components/ProtocolStep.presentation.ts +31 -0
- package/src/components/ProtocolStepEditor.state.ts +104 -0
- package/src/components/ProtocolStepEditor.vue +48 -179
- package/src/components/ProtocolStepParameterField.vue +134 -0
- package/src/components/ReagentList.presentation.ts +105 -0
- package/src/components/ReagentList.vue +16 -79
- package/src/components/SampleSelector.colors.ts +43 -0
- package/src/components/SampleSelector.drag.ts +164 -0
- package/src/components/SampleSelector.groups.ts +109 -0
- package/src/components/SampleSelector.selection.ts +103 -0
- package/src/components/SampleSelector.vue +82 -349
- package/src/components/SampleSelectorSampleRow.vue +64 -0
- package/src/components/ScheduleCalendar.vue +44 -199
- package/src/components/SettingsModal.schema.ts +71 -0
- package/src/components/SettingsModal.vue +16 -46
- package/src/components/WellPlate.colors.ts +56 -0
- package/src/components/WellPlate.conditions.ts +100 -0
- package/src/components/WellPlate.geometry.ts +91 -0
- package/src/components/WellPlate.interaction.ts +272 -0
- package/src/components/WellPlate.legend.ts +8 -0
- package/src/components/WellPlate.rendering.ts +105 -0
- package/src/components/WellPlate.sampleDrop.ts +73 -0
- package/src/components/WellPlate.vue +102 -550
- package/src/components/internal/PlateMapEditorToolbarInternal.vue +128 -0
- package/src/composables/autoGroup/classKey.ts +5 -0
- package/src/composables/autoGroup/classify.ts +205 -0
- package/src/composables/autoGroup/colors.ts +6 -0
- package/src/composables/autoGroup/columns.ts +226 -0
- package/src/composables/autoGroup/compose.ts +156 -0
- package/src/composables/autoGroup/cooccurrence.ts +46 -0
- package/src/composables/autoGroup/csv-shim.ts +44 -0
- package/src/composables/autoGroup/fingerprint.ts +49 -0
- package/src/composables/autoGroup/index.ts +20 -0
- package/src/composables/autoGroup/replicatePreGroup.ts +90 -0
- package/src/composables/autoGroup/template.ts +126 -0
- package/src/composables/autoGroup/tokenize.ts +41 -0
- package/src/composables/autoGroup/vocab.json +67 -0
- package/src/composables/autoGroupConstants.ts +4 -0
- package/src/composables/autoGroupGrouping.ts +148 -0
- package/src/composables/controlComponentBindings.ts +80 -0
- package/src/composables/controlSchemaAdapters.ts +196 -0
- package/src/composables/controlSchemaDoseDesign.ts +215 -0
- package/src/composables/controlSchemaFormFields.ts +61 -0
- package/src/composables/controlSchemaLayout.ts +59 -0
- package/src/composables/controlSchemaModel.ts +163 -0
- package/src/composables/controlSchemaNormalize.ts +101 -0
- package/src/composables/controlSchemaTypes.ts +364 -0
- package/src/composables/controlSchemaUtils.ts +36 -0
- package/src/composables/controlWorkspaceOptions.ts +21 -0
- package/src/composables/formBuilderSchema.ts +153 -0
- package/src/composables/pluginEndpointBuilder.ts +203 -0
- package/src/composables/protocolTemplateCatalog.ts +325 -0
- package/src/composables/useAutoGroup.ts +395 -549
- package/src/composables/useAutoGroupInputSources.ts +147 -0
- package/src/composables/useBioTemplateControls.ts +1 -1
- package/src/composables/useBioTemplatePresetWorkspace.ts +1 -1
- package/src/composables/useBioTemplateWorkspace.ts +1 -1
- package/src/composables/useControlSchema.ts +64 -1312
- package/src/composables/useControlWorkspace.ts +201 -0
- package/src/composables/useForm.ts +5 -187
- package/src/composables/useFormBuilder.ts +11 -153
- package/src/composables/useFormValidation.ts +154 -0
- package/src/composables/usePluginClient.ts +10 -193
- package/src/composables/useProtocolTemplates.ts +10 -328
- package/src/composables/useScheduleCalendarLayout.ts +287 -0
- package/src/styles/components/auto-group-modal.css +248 -310
- package/src/templates/adapters.ts +89 -930
- package/src/templates/assayLookups.ts +33 -0
- package/src/templates/assayMatrixAdapters.ts +78 -0
- package/src/templates/assayMatrixBuilder.ts +59 -0
- package/src/templates/assayNormalizers.ts +34 -0
- package/src/templates/builderDefaults.ts +11 -0
- package/src/templates/builderIdUtils.ts +20 -0
- package/src/templates/builderPresetControls.ts +165 -0
- package/src/templates/builderReadUtils.ts +57 -0
- package/src/templates/builders.ts +122 -2350
- package/src/templates/calibrationCurveAdapters.ts +59 -0
- package/src/templates/calibrationCurveBuilder.ts +99 -0
- package/src/templates/calibrationNormalizers.ts +60 -0
- package/src/templates/componentBindingCatalog.ts +90 -0
- package/src/templates/componentBindingHelpers.ts +93 -0
- package/src/templates/componentBindings.ts +12 -461
- package/src/templates/componentDoseResponseProps.ts +42 -0
- package/src/templates/componentGenericProps.ts +77 -0
- package/src/templates/componentPlateHelpers.ts +29 -0
- package/src/templates/componentPlateMapProps.ts +32 -0
- package/src/templates/componentPropsFactory.ts +21 -0
- package/src/templates/componentQpcrPlateProps.ts +28 -0
- package/src/templates/componentTargetResolvers.ts +69 -0
- package/src/templates/componentTemplateProps.ts +78 -0
- package/src/templates/controlSchemaClone.ts +32 -0
- package/src/templates/controlSchemaConstants.ts +11 -0
- package/src/templates/controlSchemaMerge.ts +40 -0
- package/src/templates/controlSchemaTargets.ts +87 -0
- package/src/templates/controlSchemaTypes.ts +20 -0
- package/src/templates/controlSchemas.ts +22 -704
- package/src/templates/defaultBioTemplateBuilder.ts +124 -0
- package/src/templates/doseResponseAdapters.ts +45 -0
- package/src/templates/doseResponseBuilder.ts +44 -0
- package/src/templates/elisaAssayCollectionBuilder.ts +62 -0
- package/src/templates/flowCytometryAssayCollectionBuilder.ts +41 -0
- package/src/templates/flowCytometryPanelBuilder.ts +53 -0
- package/src/templates/flowNormalizers.ts +58 -0
- package/src/templates/flowPanelAdapters.ts +58 -0
- package/src/templates/instrumentRunAdapterHelpers.ts +94 -0
- package/src/templates/instrumentRunAdapters.ts +163 -0
- package/src/templates/instrumentRunBuilder.ts +97 -0
- package/src/templates/lcmsBatchCollectionBuilder.ts +38 -0
- package/src/templates/plateGeometry.ts +62 -0
- package/src/templates/plateMapAdapters.ts +36 -0
- package/src/templates/plateMapBuilder.ts +43 -0
- package/src/templates/presetControlSchemas.ts +258 -0
- package/src/templates/protocolAdapters.ts +69 -0
- package/src/templates/protocolNormalizers.ts +37 -0
- package/src/templates/protocolStepsBuilder.ts +36 -0
- package/src/templates/qpcrAdapters.ts +104 -0
- package/src/templates/qpcrExpressionCollectionBuilder.ts +33 -0
- package/src/templates/qpcrPlateBuilder.ts +96 -0
- package/src/templates/reagentAdapters.ts +77 -0
- package/src/templates/reagentListBuilder.ts +30 -0
- package/src/templates/runNormalizers.ts +63 -0
- package/src/templates/sampleNormalizers.ts +58 -0
- package/src/templates/samplePrepAdapters.ts +63 -0
- package/src/templates/samplePrepBuilder.ts +51 -0
- package/src/templates/sampleSheetAdapters.ts +75 -0
- package/src/templates/sampleSheetBuilder.ts +23 -0
- package/src/templates/targetedMetabolomicsCollectionBuilder.ts +79 -0
- package/src/templates/targetedMetabolomicsHelpers.ts +102 -0
- package/src/templates/templateAdapterTypes.ts +58 -0
- package/src/templates/templateControlSchemas.ts +320 -0
- package/src/templates/templateCreateOptions.ts +208 -0
- package/src/templates/templateEnvelopes.ts +137 -0
- package/src/templates/templatePackCollectionBuilder.ts +23 -0
- package/src/templates/templatePresetCollectionBuilder.ts +139 -0
- package/src/templates/templateQpcrTypes.ts +48 -0
- package/src/templates/templateValidators.ts +414 -0
- package/src/templates/timeCourseAdapters.ts +73 -0
- package/src/templates/timeCourseBuilder.ts +64 -0
- package/src/templates/types.ts +79 -275
- package/src/templates/wellPlateScreenCollectionBuilder.ts +36 -0
- package/src/templates/westernBlotAssayCollectionBuilder.ts +68 -0
- package/src/types/auto-group.ts +107 -9
- package/src/types/componentLabTypes.ts +235 -0
- package/src/types/componentWorkflowTypes.ts +190 -0
- package/src/types/components.ts +74 -424
- package/dist/components-BhK-dW99.js.map +0 -1
- package/dist/composables-Bg7CFuNz.js.map +0 -1
- package/dist/templates-BorLR_7p.js.map +0 -1
- 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,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,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
|
+
}
|