@morscherlab/mint-sdk 1.0.0-beta.3 → 1.0.0-beta.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 (181) hide show
  1. package/README.md +9 -2
  2. package/dist/__tests__/composables/experiment-utils.test.d.ts +1 -0
  3. package/dist/__tests__/composables/useApi.test.d.ts +1 -0
  4. package/dist/components/AppContainer.vue.d.ts +1 -1
  5. package/dist/components/AppLayout.vue.d.ts +20 -1
  6. package/dist/components/AppSidebar.vue.d.ts +57 -5
  7. package/dist/components/AppTopBar.vue.d.ts +7 -25
  8. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +3 -1
  9. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -0
  10. package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +5 -0
  11. package/dist/components/ComponentBindingRenderer.vue.d.ts +44 -0
  12. package/dist/components/ControlWorkspaceView.vue.d.ts +24 -7
  13. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +149 -0
  14. package/dist/components/ExperimentTimeline.vue.d.ts +1 -1
  15. package/dist/components/FormBuilder.vue.d.ts +9 -9
  16. package/dist/components/PlateMapEditor.vue.d.ts +1 -1
  17. package/dist/components/PluginWorkspaceView.vue.d.ts +310 -0
  18. package/dist/components/SettingsModal.vue.d.ts +1 -1
  19. package/dist/components/WellPlate.vue.d.ts +2 -2
  20. package/dist/components/index.d.ts +3 -12
  21. package/dist/components/index.js +3 -3
  22. package/dist/components/{AppPageSelector.vue.d.ts → internal/AppTopBarPageSelectorInternal.vue.d.ts} +1 -1
  23. package/dist/components/{AppPillNav.vue.d.ts → internal/AppTopBarPillNavInternal.vue.d.ts} +3 -1
  24. package/dist/components/{CalendarGridPanel.vue.d.ts → internal/CalendarGridPanelInternal.vue.d.ts} +1 -1
  25. package/dist/components/internal/FormSectionRenderer.vue.d.ts +4 -4
  26. package/dist/components/{WellEditPopup.vue.d.ts → internal/WellEditPopupInternal.vue.d.ts} +1 -1
  27. package/dist/{components-D_Sr0adg.js → components-DihbSJjU.js} +5932 -5408
  28. package/dist/components-DihbSJjU.js.map +1 -0
  29. package/dist/composables/experiment-utils.d.ts +8 -0
  30. package/dist/composables/index.d.ts +5 -7
  31. package/dist/composables/index.js +4 -4
  32. package/dist/composables/useAppExperiment.d.ts +31 -2
  33. package/dist/composables/useBioTemplateComponents.d.ts +5 -3
  34. package/dist/composables/useBioTemplatePackWorkspace.d.ts +3 -2
  35. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +6 -5
  36. package/dist/composables/useBioTemplateWorkspace.d.ts +5 -4
  37. package/dist/composables/useControlSchema.d.ts +43 -21
  38. package/dist/composables/usePluginClient.d.ts +5 -2
  39. package/dist/{composables-C3dpXQN5.js → composables-BcgZ6diz.js} +40 -28
  40. package/dist/composables-BcgZ6diz.js.map +1 -0
  41. package/dist/index.d.ts +5 -12
  42. package/dist/index.js +5 -5
  43. package/dist/install.js +2 -2
  44. package/dist/styles.css +5637 -5663
  45. package/dist/templates/adapters.d.ts +7 -1
  46. package/dist/templates/catalog.d.ts +5 -5
  47. package/dist/templates/componentBindings.d.ts +13 -0
  48. package/dist/templates/index.d.ts +5 -5
  49. package/dist/templates/index.js +2 -2
  50. package/dist/templates/presets.d.ts +4 -4
  51. package/dist/templates/types.d.ts +4 -1
  52. package/dist/{templates-50NPjaxL.js → templates-Cyt0Suwf.js} +322 -73
  53. package/dist/templates-Cyt0Suwf.js.map +1 -0
  54. package/dist/types/components.d.ts +6 -25
  55. package/dist/types/index.d.ts +1 -1
  56. package/dist/{useScheduleDrag-D4oWdh41.js → useExperimentData-CM6Y0u5L.js} +400 -357
  57. package/dist/useExperimentData-CM6Y0u5L.js.map +1 -0
  58. package/package.json +1 -1
  59. package/src/__tests__/components/ActionItem.test.ts +6 -6
  60. package/src/__tests__/components/AppLayout.test.ts +44 -0
  61. package/src/__tests__/components/AppSidebar.test.ts +130 -2
  62. package/src/__tests__/components/AppToastContainer.test.ts +0 -11
  63. package/src/__tests__/components/AppTopBar.test.ts +189 -120
  64. package/src/__tests__/components/{AppPageSelector.test.ts → AppTopBarPageSelector.test.ts} +8 -8
  65. package/src/__tests__/components/{AppPillNav.test.ts → AppTopBarPillNav.test.ts} +53 -6
  66. package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +7 -1
  67. package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +32 -1
  68. package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +48 -1
  69. package/src/__tests__/components/BioTemplateRenderer.test.ts +25 -0
  70. package/src/__tests__/components/CalendarGridPanel.test.ts +3 -3
  71. package/src/__tests__/components/ComponentBindingRenderer.test.ts +278 -0
  72. package/src/__tests__/components/ControlWorkspaceView.test.ts +134 -63
  73. package/src/__tests__/components/DateTimePicker.test.ts +2 -2
  74. package/src/__tests__/components/DoseDesignWorkspaceView.test.ts +185 -0
  75. package/src/__tests__/components/PluginWorkspaceView.test.ts +548 -0
  76. package/src/__tests__/composables/experiment-utils.test.ts +30 -0
  77. package/src/__tests__/composables/useApi.test.ts +30 -0
  78. package/src/__tests__/composables/useAppExperiment.test.ts +100 -1
  79. package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +7 -4
  80. package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +7 -7
  81. package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +6 -1
  82. package/src/__tests__/composables/useControlSchema.test.ts +151 -37
  83. package/src/__tests__/composables/usePluginClient.test.ts +99 -2
  84. package/src/__tests__/docs/frontendDocsCatalog.test.ts +120 -25
  85. package/src/__tests__/templates/templates.test.ts +56 -0
  86. package/src/components/AppAvatarMenu.vue +3 -3
  87. package/src/components/AppLayout.story.vue +39 -0
  88. package/src/components/AppLayout.vue +83 -2
  89. package/src/components/AppPluginSwitcher.vue +5 -5
  90. package/src/components/AppSidebar.story.vue +113 -5
  91. package/src/components/AppSidebar.vue +147 -27
  92. package/src/components/AppTopBar.story.vue +2 -5
  93. package/src/components/AppTopBar.vue +35 -425
  94. package/src/components/BioTemplateExperimentWorkspaceView.story.vue +2 -2
  95. package/src/components/BioTemplateExperimentWorkspaceView.vue +6 -0
  96. package/src/components/BioTemplatePackWorkspaceView.story.vue +4 -4
  97. package/src/components/BioTemplatePackWorkspaceView.vue +1 -0
  98. package/src/components/BioTemplatePresetWorkspaceView.story.vue +14 -2
  99. package/src/components/BioTemplatePresetWorkspaceView.vue +12 -3
  100. package/src/components/BioTemplateRenderer.story.vue +2 -2
  101. package/src/components/BioTemplateRenderer.vue +15 -227
  102. package/src/components/ComponentBindingRenderer.story.vue +87 -0
  103. package/src/components/ComponentBindingRenderer.vue +317 -0
  104. package/src/components/ControlWorkspaceView.story.vue +20 -9
  105. package/src/components/ControlWorkspaceView.vue +43 -12
  106. package/src/components/DatePicker.vue +2 -2
  107. package/src/components/DateTimePicker.vue +2 -2
  108. package/src/components/DoseDesignWorkspaceView.story.vue +77 -0
  109. package/src/components/DoseDesignWorkspaceView.vue +255 -0
  110. package/src/components/ExperimentPopover.story.vue +2 -2
  111. package/src/components/ExperimentPopover.vue +2 -6
  112. package/src/components/ExperimentSelectorModal.vue +6 -5
  113. package/src/components/FormBuilder.story.vue +190 -0
  114. package/src/components/PluginWorkspaceView.story.vue +334 -0
  115. package/src/components/PluginWorkspaceView.vue +708 -0
  116. package/src/components/SettingsModal.story.vue +87 -0
  117. package/src/components/WellPlate.vue +2 -2
  118. package/src/components/index.ts +3 -12
  119. package/src/components/{AppPageSelector.vue → internal/AppTopBarPageSelectorInternal.vue} +9 -9
  120. package/src/components/internal/AppTopBarPillNavInternal.vue +194 -0
  121. package/src/components/{CalendarGridPanel.vue → internal/CalendarGridPanelInternal.vue} +1 -1
  122. package/src/components/{WellEditPopup.vue → internal/WellEditPopupInternal.vue} +3 -3
  123. package/src/composables/experiment-utils.ts +26 -0
  124. package/src/composables/index.ts +21 -7
  125. package/src/composables/useApi.ts +9 -2
  126. package/src/composables/useAppExperiment.ts +85 -13
  127. package/src/composables/useBioTemplateComponents.ts +12 -0
  128. package/src/composables/useBioTemplatePackWorkspace.ts +6 -2
  129. package/src/composables/useBioTemplatePresetWorkspace.ts +10 -21
  130. package/src/composables/useBioTemplateWorkspace.ts +6 -4
  131. package/src/composables/useControlSchema.ts +157 -69
  132. package/src/composables/usePluginClient.ts +50 -9
  133. package/src/index.ts +6 -563
  134. package/src/styles/components/app-layout.css +82 -0
  135. package/src/styles/components/app-page-selector.css +1 -1
  136. package/src/styles/components/app-pill-nav.css +71 -1
  137. package/src/styles/components/app-sidebar.css +119 -0
  138. package/src/styles/components/app-top-bar.css +0 -235
  139. package/src/styles/components/experiment-popover.css +2 -2
  140. package/src/styles/index.css +0 -1
  141. package/src/templates/adapters.ts +193 -0
  142. package/src/templates/catalog.ts +5 -5
  143. package/src/templates/componentBindings.ts +90 -3
  144. package/src/templates/index.ts +10 -0
  145. package/src/templates/packs.ts +10 -1
  146. package/src/templates/presets.ts +14 -4
  147. package/src/templates/types.ts +4 -0
  148. package/src/types/components.ts +6 -31
  149. package/src/types/index.ts +2 -6
  150. package/dist/__tests__/composables/usePluginApi.test.d.ts +0 -13
  151. package/dist/components/FormFieldRenderer.vue.d.ts +0 -28
  152. package/dist/components/FormSection.vue.d.ts +0 -30
  153. package/dist/components/GroupingModal.vue.d.ts +0 -12
  154. package/dist/components/SettingsButton.vue.d.ts +0 -30
  155. package/dist/components/ToastNotification.vue.d.ts +0 -2
  156. package/dist/components-D_Sr0adg.js.map +0 -1
  157. package/dist/composables/usePluginApi.d.ts +0 -22
  158. package/dist/composables-C3dpXQN5.js.map +0 -1
  159. package/dist/templates-50NPjaxL.js.map +0 -1
  160. package/dist/useScheduleDrag-D4oWdh41.js.map +0 -1
  161. package/src/__tests__/components/FormCompatibility.test.ts +0 -94
  162. package/src/__tests__/components/GroupingModal.test.ts +0 -73
  163. package/src/__tests__/components/SettingsButton.test.ts +0 -44
  164. package/src/__tests__/composables/usePluginApi.test.ts +0 -81
  165. package/src/components/AppPillNav.vue +0 -71
  166. package/src/components/FormFieldRenderer.vue +0 -35
  167. package/src/components/FormSection.vue +0 -37
  168. package/src/components/GroupingModal.story.vue +0 -52
  169. package/src/components/GroupingModal.vue +0 -61
  170. package/src/components/SettingsButton.story.vue +0 -58
  171. package/src/components/SettingsButton.vue +0 -64
  172. package/src/components/ToastNotification.vue +0 -9
  173. package/src/composables/usePluginApi.ts +0 -32
  174. package/src/styles/components/settings-button.css +0 -31
  175. /package/dist/__tests__/components/{AppPageSelector.test.d.ts → AppTopBarPageSelector.test.d.ts} +0 -0
  176. /package/dist/__tests__/components/{AppPillNav.test.d.ts → AppTopBarPillNav.test.d.ts} +0 -0
  177. /package/dist/__tests__/components/{FormCompatibility.test.d.ts → ComponentBindingRenderer.test.d.ts} +0 -0
  178. /package/dist/__tests__/components/{GroupingModal.test.d.ts → DoseDesignWorkspaceView.test.d.ts} +0 -0
  179. /package/dist/__tests__/components/{SettingsButton.test.d.ts → PluginWorkspaceView.test.d.ts} +0 -0
  180. /package/dist/components/{ActionItem.vue.d.ts → internal/ActionItemInternal.vue.d.ts} +0 -0
  181. /package/src/components/{ActionItem.vue → internal/ActionItemInternal.vue} +0 -0
@@ -2,6 +2,11 @@ import type {
2
2
  DataFrameColumn,
3
3
  PlateCondition,
4
4
  PlateMapEditorState,
5
+ ProtocolStep,
6
+ ProtocolStepStatus,
7
+ ProtocolStepType,
8
+ ScheduleEvent,
9
+ ScheduleEventStatus,
5
10
  SelectOption,
6
11
  Well,
7
12
  } from '../types'
@@ -24,6 +29,7 @@ import type {
24
29
  AssayMatrixColumnsAdapterResult,
25
30
  AssayMatrixDataFrameAdapterResult,
26
31
  AssayMatrixRowsAdapterResult,
32
+ AssayMatrixSampleOptionsAdapterResult,
27
33
  AssayMatrixTemplate,
28
34
  AssayMatrixTemplateData,
29
35
  BioTemplateEnvelope,
@@ -44,8 +50,13 @@ import type {
44
50
  InstrumentRunColumnsAdapterResult,
45
51
  InstrumentRunDataFrameAdapterResult,
46
52
  InstrumentRunRowsAdapterResult,
53
+ InstrumentRunScheduleEventsAdapterResult,
54
+ InstrumentRunStepsAdapterResult,
47
55
  InstrumentRunTemplate,
48
56
  InstrumentRunTemplateData,
57
+ InstrumentRunItem,
58
+ InstrumentRunItemKind,
59
+ InstrumentRunStatus,
49
60
  PlateMapEditorAdapterResult,
50
61
  PlateMapTemplate,
51
62
  PlateMapTemplateData,
@@ -478,6 +489,19 @@ export function toAssayMatrixDataFrame(
478
489
  }
479
490
  }
480
491
 
492
+ /** Convert an assay-matrix template into SampleSelector options derived from assay samples. */
493
+ export function toAssayMatrixSampleOptions(
494
+ template: AssayMatrixTemplate | AssayMatrixTemplateData
495
+ ): AssayMatrixSampleOptionsAdapterResult {
496
+ const data = getTemplateData(template, 'assay-matrix')
497
+ validateAssayMatrixData(data)
498
+ return data.samples.map((sample): SelectOption<string> => ({
499
+ value: sample.sampleId,
500
+ label: sample.name ?? sample.sampleId,
501
+ description: sample.group,
502
+ }))
503
+ }
504
+
481
505
  export function toReagentListItems(
482
506
  template: ReagentListTemplate | ReagentListTemplateData
483
507
  ): ReagentListItemsAdapterResult {
@@ -649,6 +673,91 @@ export function toInstrumentRunDataFrame(
649
673
  }
650
674
  }
651
675
 
676
+ /** Convert an instrument-run template into ExperimentTimeline protocol steps for acquisition queue previews. */
677
+ export function toInstrumentRunSteps(
678
+ template: InstrumentRunTemplate | InstrumentRunTemplateData
679
+ ): InstrumentRunStepsAdapterResult {
680
+ const data = getTemplateData(template, 'instrument-run')
681
+ validateInstrumentRunData(data)
682
+ const methods = new Map(data.methods.map(method => [method.id, method]))
683
+ return [...data.items]
684
+ .sort((a, b) => a.order - b.order)
685
+ .map((item): ProtocolStep => {
686
+ const method = methods.get(item.methodId)
687
+ const name = item.name ?? item.sampleId ?? item.kind
688
+ return {
689
+ id: item.id,
690
+ type: instrumentRunStepType(item.kind),
691
+ name,
692
+ description: [
693
+ item.kind,
694
+ method?.name,
695
+ item.vial ? `Vial ${item.vial}` : undefined,
696
+ item.wellId ? `Well ${item.wellId}` : undefined,
697
+ ]
698
+ .filter(Boolean)
699
+ .join(' / '),
700
+ duration: item.expectedDurationMin,
701
+ status: instrumentRunStepStatus(item.status),
702
+ parameters: {
703
+ kind: item.kind,
704
+ sampleId: item.sampleId,
705
+ methodId: item.methodId,
706
+ method: method?.name,
707
+ vial: item.vial,
708
+ plateId: item.plateId,
709
+ wellId: item.wellId,
710
+ injectionVolume: item.injectionVolume,
711
+ instrument: data.instrument ?? method?.instrument,
712
+ },
713
+ order: item.order,
714
+ }
715
+ })
716
+ }
717
+
718
+ /** Convert an instrument-run template into readonly ScheduleCalendar events ordered by run queue timing. */
719
+ export function toInstrumentRunScheduleEvents(
720
+ template: InstrumentRunTemplate | InstrumentRunTemplateData
721
+ ): InstrumentRunScheduleEventsAdapterResult {
722
+ const data = getTemplateData(template, 'instrument-run')
723
+ validateInstrumentRunData(data)
724
+ const methods = new Map(data.methods.map(method => [method.id, method]))
725
+ let cursor = dateFromMetadata(data.metadata, ['scheduledStart', 'runStart', 'start', 'startedAt'])
726
+ ?? new Date('2024-01-01T08:00:00.000Z')
727
+
728
+ return [...data.items]
729
+ .sort((a, b) => a.order - b.order)
730
+ .map((item): ScheduleEvent => {
731
+ const method = methods.get(item.methodId)
732
+ const start = dateFromMetadata(item.metadata, ['scheduledStart', 'start', 'startedAt']) ?? cursor
733
+ const end = dateFromMetadata(item.metadata, ['scheduledEnd', 'end', 'completedAt'])
734
+ ?? addMinutes(start, Math.max(item.expectedDurationMin ?? 10, 1))
735
+ cursor = end
736
+
737
+ return {
738
+ id: item.id,
739
+ title: item.name ?? item.sampleId ?? item.kind,
740
+ start: start.toISOString(),
741
+ end: end.toISOString(),
742
+ color: instrumentRunScheduleColor(item.kind),
743
+ status: instrumentRunScheduleStatus(item.status),
744
+ draggable: false,
745
+ resizable: false,
746
+ metadata: {
747
+ kind: item.kind,
748
+ sampleId: item.sampleId,
749
+ methodId: item.methodId,
750
+ method: method?.name,
751
+ vial: item.vial,
752
+ plateId: item.plateId,
753
+ wellId: item.wellId,
754
+ injectionVolume: item.injectionVolume,
755
+ instrument: data.instrument ?? method?.instrument,
756
+ },
757
+ }
758
+ })
759
+ }
760
+
652
761
  export function toQpcrRows(
653
762
  template: QpcrPlateTemplate | QpcrPlateTemplateData
654
763
  ): QpcrRowsAdapterResult {
@@ -783,3 +892,87 @@ function sampleColumnKey(columnId: string): string {
783
892
  }
784
893
  return aliases[columnId] ?? columnId
785
894
  }
895
+
896
+ function instrumentRunStepType(kind: InstrumentRunItemKind): ProtocolStepType {
897
+ switch (kind) {
898
+ case 'wash':
899
+ return 'wash'
900
+ case 'blank':
901
+ case 'qc':
902
+ case 'standard':
903
+ case 'calibration':
904
+ case 'sample':
905
+ return 'measurement'
906
+ default:
907
+ return 'custom'
908
+ }
909
+ }
910
+
911
+ function instrumentRunStepStatus(status: InstrumentRunStatus): ProtocolStepStatus {
912
+ switch (status) {
913
+ case 'running':
914
+ return 'in_progress'
915
+ case 'completed':
916
+ return 'completed'
917
+ case 'failed':
918
+ return 'failed'
919
+ case 'skipped':
920
+ return 'skipped'
921
+ case 'planned':
922
+ case 'queued':
923
+ default:
924
+ return 'pending'
925
+ }
926
+ }
927
+
928
+ function instrumentRunScheduleStatus(status: InstrumentRunStatus): ScheduleEventStatus {
929
+ switch (status) {
930
+ case 'running':
931
+ return 'in-progress'
932
+ case 'completed':
933
+ return 'confirmed'
934
+ case 'failed':
935
+ case 'skipped':
936
+ return 'cancelled'
937
+ case 'planned':
938
+ case 'queued':
939
+ default:
940
+ return 'pending'
941
+ }
942
+ }
943
+
944
+ function instrumentRunScheduleColor(kind: InstrumentRunItem['kind']): string {
945
+ switch (kind) {
946
+ case 'blank':
947
+ return '#64748b'
948
+ case 'qc':
949
+ return '#8b5cf6'
950
+ case 'standard':
951
+ case 'calibration':
952
+ return '#f59e0b'
953
+ case 'wash':
954
+ return '#06b6d4'
955
+ case 'sample':
956
+ return '#2563eb'
957
+ default:
958
+ return '#475569'
959
+ }
960
+ }
961
+
962
+ function dateFromMetadata(
963
+ metadata: Record<string, unknown> | undefined,
964
+ keys: readonly string[]
965
+ ): Date | undefined {
966
+ if (!metadata) return undefined
967
+ for (const key of keys) {
968
+ const value = metadata[key]
969
+ if (!(typeof value === 'string' || value instanceof Date)) continue
970
+ const date = new Date(value)
971
+ if (!Number.isNaN(date.getTime())) return date
972
+ }
973
+ return undefined
974
+ }
975
+
976
+ function addMinutes(date: Date, minutes: number): Date {
977
+ return new Date(date.getTime() + minutes * 60_000)
978
+ }
@@ -142,8 +142,8 @@ export const bioTemplateCatalog = [
142
142
  aliases: ['omics matrix', 'measurement matrix', 'readout matrix', 'feature table'],
143
143
  python_import: 'from mint_sdk.templates import AssayMatrixTemplate, save_template',
144
144
  python_example: 'AssayMatrixTemplate.create(samples=["S001", "S002"], features=["Lactate", "Glucose"])',
145
- frontend_import: "import { toTemplateDataFrame } from '@morscherlab/mint-sdk/templates'",
146
- frontend_adapters: ['toTemplateDataFrame', 'toAssayMatrixDataFrame', 'toAssayMatrixRows', 'toAssayMatrixColumns'],
145
+ frontend_import: "import { toAssayMatrixSampleOptions, toTemplateDataFrame } from '@morscherlab/mint-sdk/templates'",
146
+ frontend_adapters: ['toTemplateDataFrame', 'toAssayMatrixDataFrame', 'toAssayMatrixRows', 'toAssayMatrixColumns', 'toAssayMatrixSampleOptions'],
147
147
  components: ['DataFrame', 'ChartContainer', 'SampleSelector'],
148
148
  add_command: 'mint add data-template assay-matrix --page',
149
149
  },
@@ -187,9 +187,9 @@ export const bioTemplateCatalog = [
187
187
  aliases: ['run queue', 'sequence table', 'sample queue', 'lcms run', 'instrument sequence', 'batch run'],
188
188
  python_import: 'from mint_sdk.templates import InstrumentRunTemplate, save_template',
189
189
  python_example: 'InstrumentRunTemplate.create(["S001", "S002"], instrument="LC-MS")',
190
- frontend_import: "import { toInstrumentRunDataFrame, toTemplateDataFrame } from '@morscherlab/mint-sdk/templates'",
191
- frontend_adapters: ['toTemplateDataFrame', 'toInstrumentRunDataFrame', 'toInstrumentRunRows', 'toInstrumentRunColumns'],
192
- components: ['DataFrame', 'ScheduleCalendar', 'SampleSelector'],
190
+ frontend_import: "import { toInstrumentRunDataFrame, toInstrumentRunScheduleEvents, toInstrumentRunSteps, toTemplateDataFrame } from '@morscherlab/mint-sdk/templates'",
191
+ frontend_adapters: ['toTemplateDataFrame', 'toInstrumentRunDataFrame', 'toInstrumentRunRows', 'toInstrumentRunColumns', 'toInstrumentRunSteps', 'toInstrumentRunScheduleEvents'],
192
+ components: ['DataFrame', 'ExperimentTimeline', 'ScheduleCalendar', 'SampleSelector'],
193
193
  add_command: 'mint add data-template instrument-run --page',
194
194
  },
195
195
  {
@@ -4,8 +4,11 @@ import {
4
4
  getTemplateData,
5
5
  } from './builders'
6
6
  import {
7
+ toAssayMatrixSampleOptions,
7
8
  toDoseConditions,
8
9
  toDoseLayoutState,
10
+ toInstrumentRunScheduleEvents,
11
+ toInstrumentRunSteps,
9
12
  toPlateMapEditorState,
10
13
  toProtocolSteps,
11
14
  toQpcrWellPlateWells,
@@ -26,8 +29,10 @@ import {
26
29
  } from './packs'
27
30
  import type {
28
31
  BioTemplateEnvelope,
32
+ AssayMatrixTemplate,
29
33
  DataFrameTemplate,
30
34
  DoseResponseTemplate,
35
+ InstrumentRunTemplate,
31
36
  PlateMapTemplate,
32
37
  QpcrPlateTemplate,
33
38
  ReagentListTemplate,
@@ -62,6 +67,16 @@ export interface BioTemplateComponentPropsBinding extends BioTemplateComponentBi
62
67
  propsObject: Record<string, unknown>
63
68
  }
64
69
 
70
+ export interface BioTemplateResolvedComponentBinding extends Omit<BioTemplateComponentBinding, 'props'> {
71
+ /** Prop names advertised by the static template/component compatibility catalog. */
72
+ propNames: readonly string[]
73
+ /** Concrete props ready for direct `v-bind="componentBindingsById[id].props"` use. */
74
+ props: Record<string, unknown>
75
+ /** Backward-compatible alias for callers that already consume componentProps entries. */
76
+ propsObject: Record<string, unknown>
77
+ }
78
+
79
+ export type BioTemplateComponentBindingsById = Record<string, BioTemplateResolvedComponentBinding>
65
80
  export type BioTemplateComponentPropsById = Record<string, Record<string, unknown>>
66
81
  export type BioTemplateComponentPropsByComponent = Record<string, Record<string, unknown>[]>
67
82
 
@@ -130,6 +145,7 @@ const templateComponentBindings = {
130
145
  ],
131
146
  'assay-matrix': [
132
147
  binding('assay-matrix', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render sample-by-feature measurements.'),
148
+ binding('assay-matrix', 'SampleSelector', ['toAssayMatrixSampleOptions'], ['samples', 'modelValue'], 'Select samples from assay-matrix records.'),
133
149
  ],
134
150
  'reagent-list': [
135
151
  binding('reagent-list', 'ReagentList', ['toReagentListItems'], ['modelValue'], 'Render reagents with lot, storage, and stock metadata.'),
@@ -140,6 +156,20 @@ const templateComponentBindings = {
140
156
  ],
141
157
  'instrument-run': [
142
158
  binding('instrument-run', 'DataFrame', ['toTemplateDataFrame'], ['data', 'columns', 'rowKey'], 'Render acquisition queues, methods, QC, and run status.'),
159
+ binding(
160
+ 'instrument-run',
161
+ 'ScheduleCalendar',
162
+ ['toInstrumentRunScheduleEvents'],
163
+ ['modelValue', 'events', 'view', 'readonly', 'showNavigation', 'showViewToggle'],
164
+ 'Render acquisition queues as a scheduled run calendar.',
165
+ ),
166
+ binding(
167
+ 'instrument-run',
168
+ 'ExperimentTimeline',
169
+ ['toInstrumentRunSteps'],
170
+ ['modelValue', 'orientation', 'showDuration', 'editable'],
171
+ 'Render acquisition queues as an ordered run timeline.',
172
+ ),
143
173
  ],
144
174
  'qpcr-plate': [
145
175
  binding('qpcr-plate', 'WellPlate', ['toQpcrWellPlateWells'], ['format', 'wells', 'sampleColors', 'legendItems'], 'Render qPCR reactions on a well plate.'),
@@ -169,6 +199,22 @@ export function toBioTemplateComponentProps(
169
199
  return Object.values(collection).flatMap(template => propsForTemplate(template))
170
200
  }
171
201
 
202
+ /** Return resolved component bindings with concrete props for direct slot rendering. */
203
+ export function toBioTemplateComponentBindings(
204
+ target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope,
205
+ ): BioTemplateResolvedComponentBinding[] {
206
+ return toBioTemplateComponentProps(target).map(toResolvedComponentBinding)
207
+ }
208
+
209
+ /** Return resolved component bindings keyed by stable template component id. */
210
+ export function toBioTemplateComponentBindingsById(
211
+ target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope,
212
+ ): BioTemplateComponentBindingsById {
213
+ return Object.fromEntries(
214
+ toBioTemplateComponentBindings(target).map(binding => [binding.id, binding]),
215
+ )
216
+ }
217
+
172
218
  /** Return component props keyed by stable binding id for direct `v-bind="componentPropsById[id]"` use. */
173
219
  export function toBioTemplateComponentPropsById(
174
220
  target: BioTemplateEnvelope<unknown> | TemplateCollectionEnvelope,
@@ -205,6 +251,18 @@ export function getBioTemplateComponentProps(
205
251
  })?.propsObject
206
252
  }
207
253
 
254
+ function toResolvedComponentBinding(
255
+ binding: BioTemplateComponentPropsBinding,
256
+ ): BioTemplateResolvedComponentBinding {
257
+ const { props: propNames, propsObject, ...rest } = binding
258
+ return {
259
+ ...rest,
260
+ propNames,
261
+ props: propsObject,
262
+ propsObject,
263
+ }
264
+ }
265
+
208
266
  export function toBioTemplateComponentImports(
209
267
  target: BioTemplateComponentTarget
210
268
  ): BioTemplateComponentImport[] {
@@ -291,10 +349,19 @@ function propsForTemplate(template: BioTemplateEnvelope<unknown>): BioTemplateCo
291
349
  ]
292
350
  case 'sample-prep':
293
351
  case 'calibration-curve':
294
- case 'assay-matrix':
295
352
  case 'flow-cytometry-panel':
296
- case 'instrument-run':
297
353
  return [dataFrameProps(template as DataFrameTemplate)]
354
+ case 'assay-matrix':
355
+ return [
356
+ dataFrameProps(template as DataFrameTemplate),
357
+ assayMatrixSampleSelectorProps(template as AssayMatrixTemplate),
358
+ ]
359
+ case 'instrument-run':
360
+ return [
361
+ dataFrameProps(template as DataFrameTemplate),
362
+ scheduleCalendarProps(template as InstrumentRunTemplate),
363
+ timelineProps(template as InstrumentRunTemplate, toInstrumentRunSteps(template as InstrumentRunTemplate)),
364
+ ]
298
365
  case 'dose-response':
299
366
  return doseResponseProps(template as DoseResponseTemplate)
300
367
  case 'time-course':
@@ -408,8 +475,16 @@ function sampleSelectorProps(template: SampleSheetTemplate): BioTemplateComponen
408
475
  })
409
476
  }
410
477
 
478
+ function assayMatrixSampleSelectorProps(template: AssayMatrixTemplate): BioTemplateComponentPropsBinding {
479
+ const options = toAssayMatrixSampleOptions(template)
480
+ return propsBinding('assay-matrix', 'SampleSelector', {
481
+ samples: options.map(option => option.label),
482
+ modelValue: [],
483
+ })
484
+ }
485
+
411
486
  function timelineProps(
412
- template: TimeCourseTemplate | ProtocolStepsTemplate,
487
+ template: TimeCourseTemplate | ProtocolStepsTemplate | InstrumentRunTemplate,
413
488
  steps: unknown[],
414
489
  editable = false
415
490
  ): BioTemplateComponentPropsBinding {
@@ -420,6 +495,18 @@ function timelineProps(
420
495
  })
421
496
  }
422
497
 
498
+ function scheduleCalendarProps(template: InstrumentRunTemplate): BioTemplateComponentPropsBinding {
499
+ const events = toInstrumentRunScheduleEvents(template)
500
+ return propsBinding('instrument-run', 'ScheduleCalendar', {
501
+ modelValue: events[0]?.start,
502
+ events,
503
+ view: 'day',
504
+ readonly: true,
505
+ showNavigation: false,
506
+ showViewToggle: false,
507
+ })
508
+ }
509
+
423
510
  function reagentListProps(template: ReagentListTemplate): BioTemplateComponentPropsBinding {
424
511
  return propsBinding('reagent-list', 'ReagentList', {
425
512
  modelValue: toReagentListItems(template),
@@ -71,6 +71,8 @@ export {
71
71
  getBioTemplateComponentProps,
72
72
  getBioTemplateComponentBindings,
73
73
  listBioTemplateComponentBindings,
74
+ toBioTemplateComponentBindings,
75
+ toBioTemplateComponentBindingsById,
74
76
  toBioTemplateComponentImports,
75
77
  toBioTemplateComponentProps,
76
78
  toBioTemplateComponentPropsByComponent,
@@ -80,6 +82,7 @@ export {
80
82
  } from './componentBindings'
81
83
 
82
84
  export type {
85
+ BioTemplateComponentBindingsById,
83
86
  BioTemplateComponentPropsByComponent,
84
87
  BioTemplateComponentPropsById,
85
88
  BioTemplateComponentPropsLookupOptions,
@@ -103,6 +106,7 @@ export {
103
106
  toAssayMatrixColumns,
104
107
  toAssayMatrixDataFrame,
105
108
  toAssayMatrixRows,
109
+ toAssayMatrixSampleOptions,
106
110
  toCalibrationCurveColumns,
107
111
  toCalibrationCurveDataFrame,
108
112
  toCalibrationCurveRows,
@@ -114,6 +118,8 @@ export {
114
118
  toInstrumentRunColumns,
115
119
  toInstrumentRunDataFrame,
116
120
  toInstrumentRunRows,
121
+ toInstrumentRunScheduleEvents,
122
+ toInstrumentRunSteps,
117
123
  toPlateMapEditorState,
118
124
  toProtocolColumns,
119
125
  toProtocolDataFrame,
@@ -149,6 +155,7 @@ export type {
149
155
  AssayMatrixColumnsAdapterResult,
150
156
  AssayMatrixDataFrameAdapterResult,
151
157
  AssayMatrixRowsAdapterResult,
158
+ AssayMatrixSampleOptionsAdapterResult,
152
159
  AssayMatrixTemplate,
153
160
  AssayMatrixTemplateData,
154
161
  AssayMeasurement,
@@ -208,6 +215,8 @@ export type {
208
215
  InstrumentRunItemInput,
209
216
  InstrumentRunItemKind,
210
217
  InstrumentRunRowsAdapterResult,
218
+ InstrumentRunScheduleEventsAdapterResult,
219
+ InstrumentRunStepsAdapterResult,
211
220
  InstrumentRunStatus,
212
221
  InstrumentRunTemplate,
213
222
  InstrumentRunTemplateData,
@@ -293,6 +302,7 @@ export type {
293
302
  BioTemplateComponentBinding,
294
303
  BioTemplateComponentImport,
295
304
  BioTemplateComponentPropsBinding,
305
+ BioTemplateResolvedComponentBinding,
296
306
  BioTemplateComponentSnippet,
297
307
  BioTemplateComponentSnippetOptions,
298
308
  BioTemplateComponentTarget,
@@ -89,7 +89,16 @@ const bioTemplatePackDefinitions = [
89
89
  'calibration-curve',
90
90
  'assay-matrix',
91
91
  ],
92
- aliases: ['omics', 'metabolomics', 'proteomics', 'assay matrix', 'lcms', 'standard curve'],
92
+ aliases: [
93
+ 'omics',
94
+ 'metabolomics',
95
+ 'metabolism',
96
+ 'metabolite profiling',
97
+ 'proteomics',
98
+ 'assay matrix',
99
+ 'lcms',
100
+ 'standard curve',
101
+ ],
93
102
  },
94
103
  {
95
104
  name: 'longitudinal-study',
@@ -57,12 +57,22 @@ export const bioTemplatePresets = [
57
57
  category: 'bio-template-preset',
58
58
  purpose: 'Sample metadata, instrument queue, and assay matrix payloads for omics acquisition batches.',
59
59
  templates: ['sample-sheet', 'instrument-run', 'assay-matrix'],
60
- aliases: ['lcms', 'lc-ms', 'mass spec batch', 'omics batch', 'run queue', 'sequence table'],
60
+ aliases: [
61
+ 'lcms',
62
+ 'lc-ms',
63
+ 'mass spec batch',
64
+ 'omics batch',
65
+ 'metabolomics',
66
+ 'metabolism',
67
+ 'metabolite profiling',
68
+ 'run queue',
69
+ 'sequence table',
70
+ ],
61
71
  python_import: 'from mint_sdk.templates import create_lcms_batch_collection, save_template_collection',
62
72
  python_example: 'create_lcms_batch_collection(samples=["S001", "S002"], features=["Glucose", "Lactate"], instrument="LC-MS")',
63
- frontend_import: "import { createLcmsBatchCollection, toInstrumentRunDataFrame, toTemplateDataFrame } from '@morscherlab/mint-sdk/templates'",
64
- frontend_adapters: ['toTemplateDataFrame', 'toInstrumentRunDataFrame', 'toAssayMatrixDataFrame'],
65
- components: ['DataFrame', 'ScheduleCalendar', 'ChartContainer', 'SampleSelector'],
73
+ frontend_import: "import { createLcmsBatchCollection, toAssayMatrixSampleOptions, toInstrumentRunDataFrame, toInstrumentRunScheduleEvents, toInstrumentRunSteps, toTemplateDataFrame } from '@morscherlab/mint-sdk/templates'",
74
+ frontend_adapters: ['toTemplateDataFrame', 'toInstrumentRunDataFrame', 'toInstrumentRunSteps', 'toInstrumentRunScheduleEvents', 'toAssayMatrixDataFrame', 'toAssayMatrixSampleOptions'],
75
+ components: ['DataFrame', 'ExperimentTimeline', 'ScheduleCalendar', 'ChartContainer', 'SampleSelector'],
66
76
  },
67
77
  {
68
78
  name: 'elisa-assay',
@@ -5,6 +5,7 @@ import type {
5
5
  PlateMapEditorState,
6
6
  ProtocolStep,
7
7
  Reagent,
8
+ ScheduleEvent,
8
9
  SampleType,
9
10
  SelectOption,
10
11
  Well,
@@ -651,6 +652,7 @@ export type ProtocolDataFrameAdapterResult = DataFrameAdapterResult
651
652
  export type AssayMatrixRowsAdapterResult = Array<Record<string, unknown>>
652
653
  export type AssayMatrixColumnsAdapterResult = DataFrameColumn<Record<string, unknown>>[]
653
654
  export type AssayMatrixDataFrameAdapterResult = DataFrameAdapterResult
655
+ export type AssayMatrixSampleOptionsAdapterResult = SelectOption<string>[]
654
656
  export type ReagentListItemsAdapterResult = Reagent[]
655
657
  export type ReagentRowsAdapterResult = Array<Record<string, unknown>>
656
658
  export type ReagentColumnsAdapterResult = DataFrameColumn<Record<string, unknown>>[]
@@ -661,6 +663,8 @@ export type FlowPanelDataFrameAdapterResult = DataFrameAdapterResult
661
663
  export type InstrumentRunRowsAdapterResult = Array<Record<string, unknown>>
662
664
  export type InstrumentRunColumnsAdapterResult = DataFrameColumn<Record<string, unknown>>[]
663
665
  export type InstrumentRunDataFrameAdapterResult = DataFrameAdapterResult
666
+ export type InstrumentRunStepsAdapterResult = ProtocolStep[]
667
+ export type InstrumentRunScheduleEventsAdapterResult = ScheduleEvent[]
664
668
  export type QpcrRowsAdapterResult = Array<Record<string, unknown>>
665
669
  export type QpcrColumnsAdapterResult = DataFrameColumn<Record<string, unknown>>[]
666
670
  export type QpcrDataFrameAdapterResult = DataFrameAdapterResult
@@ -89,31 +89,7 @@ export interface CollapsibleState {
89
89
  // TopBar types
90
90
  export type TopBarVariant = 'card' | 'default'
91
91
 
92
- // TopBar page navigation types
93
- export interface TopBarPage {
94
- id: string
95
- label: string
96
- to?: string // vue-router path
97
- href?: string // External link
98
- icon?: string // Optional icon
99
- description?: string // Optional subtitle in dropdown
100
- disabled?: boolean
101
- }
102
-
103
- export type TopBarPageInput = TopBarPage | string
104
-
105
- // TopBar tab types (displayed in middle of topbar)
106
- export interface TopBarTab {
107
- id: string
108
- label: string
109
- to?: string // vue-router path
110
- href?: string // External link
111
- icon?: string // Optional icon
112
- disabled?: boolean
113
- children?: TopBarTabOption[] // Dropdown options
114
- }
115
-
116
- export interface TopBarTabOption {
92
+ export interface PillNavOption {
117
93
  id: string
118
94
  label: string
119
95
  to?: string
@@ -122,11 +98,7 @@ export interface TopBarTabOption {
122
98
  disabled?: boolean
123
99
  }
124
100
 
125
- export type TopBarTabOptionInput = TopBarTabOption | string
126
-
127
- export type TopBarTabInput = (Omit<TopBarTab, 'children'> & {
128
- children?: TopBarTabOptionInput[]
129
- }) | string
101
+ export type PillNavOptionInput = PillNavOption | string
130
102
 
131
103
  // TopBar settings config (pass-through for built-in SettingsModal)
132
104
  export interface TopBarSettingsConfig {
@@ -159,9 +131,12 @@ export interface PillNavItem {
159
131
  to?: string
160
132
  href?: string
161
133
  disabled?: boolean
134
+ children?: PillNavOption[]
162
135
  }
163
136
 
164
- export type PillNavItemInput = PillNavItem | string
137
+ export type PillNavItemInput = (Omit<PillNavItem, 'children'> & {
138
+ children?: PillNavOptionInput[]
139
+ }) | string
165
140
 
166
141
  // TopBar page selector items (left-side "go to page" dropdown)
167
142
  export interface PageSelectorItem {
@@ -19,13 +19,9 @@ export type {
19
19
  SidebarToolSection,
20
20
  CollapsibleState,
21
21
  TopBarVariant,
22
- TopBarPage,
23
- TopBarPageInput,
24
- TopBarTab,
25
- TopBarTabInput,
26
- TopBarTabOption,
27
- TopBarTabOptionInput,
28
22
  TopBarSettingsConfig,
23
+ PillNavOption,
24
+ PillNavOptionInput,
29
25
  PillNavItem,
30
26
  PillNavItemInput,
31
27
  PageSelectorItem,
@@ -1,13 +0,0 @@
1
- /**
2
- * Tests for usePluginApi.
3
- *
4
- * Exercises the baseUrl resolution priority that every plugin relies on:
5
- * 1. VITE_API_PREFIX (build-time env override)
6
- * 2. fallbackPrefix option (plugin's route prefix)
7
- * 3. '/api' default
8
- *
9
- * The SDK-wide axios client is a singleton, so these tests stub axios
10
- * with a MockAdapter-style approach via vi.spyOn to observe the
11
- * baseURL actually used at request time.
12
- */
13
- export {};
@@ -1,28 +0,0 @@
1
- import { FormFieldSchema } from '../types/form-builder';
2
- import { UseFormReturn } from '../composables/useForm';
3
- interface Props {
4
- field: FormFieldSchema;
5
- resolvedProps: Record<string, unknown>;
6
- form: UseFormReturn<Record<string, unknown>>;
7
- }
8
- declare function __VLS_template(): {
9
- attrs: Partial<{}>;
10
- slots: Readonly<{
11
- [name: string]: ((props: Record<string, unknown>) => unknown) | undefined;
12
- '`field:${field.name}`'?: (props: Record<string, unknown>) => unknown;
13
- }> & {
14
- [name: string]: ((props: Record<string, unknown>) => unknown) | undefined;
15
- '`field:${field.name}`'?: (props: Record<string, unknown>) => unknown;
16
- };
17
- refs: {};
18
- rootEl: HTMLDivElement;
19
- };
20
- type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
21
- declare const __VLS_component: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
22
- declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
23
- export default _default;
24
- type __VLS_WithTemplateSlots<T, S> = T & {
25
- new (): {
26
- $slots: S;
27
- };
28
- };