@morscherlab/mint-sdk 1.4.0-beta.1 → 1.4.0-beta.10

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 (261) hide show
  1. package/dist/{ActionMenu-CScINahU.js → AlertBox-B2Bgr3ei.js} +40 -209
  2. package/dist/AlertBox-B2Bgr3ei.js.map +1 -0
  3. package/dist/{AppSidebar-BmiiPfz9.js → AppSidebar-mvm--GxN.js} +161 -110
  4. package/dist/AppSidebar-mvm--GxN.js.map +1 -0
  5. package/dist/{ConfirmDialog-CJ7hqYUP.js → ConfirmDialog-nbJj51is.js} +2 -2
  6. package/dist/{ConfirmDialog-CJ7hqYUP.js.map → ConfirmDialog-nbJj51is.js.map} +1 -1
  7. package/dist/ExperimentPopover-5T1fAq2L.js +337 -0
  8. package/dist/ExperimentPopover-5T1fAq2L.js.map +1 -0
  9. package/dist/{ExperimentSelectorModal-D-9z7cNx.js → ExperimentSelectorModal-65peG4Up.js} +198 -181
  10. package/dist/ExperimentSelectorModal-65peG4Up.js.map +1 -0
  11. package/dist/{SegmentedControl-DERBN-Xd.js → SegmentedControl-BtnnjWzs.js} +2 -2
  12. package/dist/{SegmentedControl-DERBN-Xd.js.map → SegmentedControl-BtnnjWzs.js.map} +1 -1
  13. package/dist/{SettingsModal-CSLE31yg.js → SettingsModal-DO3IZ7qz.js} +9 -9
  14. package/dist/SettingsModal-DO3IZ7qz.js.map +1 -0
  15. package/dist/components/AppSidebar.vue.d.ts +1 -1
  16. package/dist/components/ArtifactSaveDialog.vue.d.ts +9 -1
  17. package/dist/components/AuditTrail.vue.d.ts +1 -1
  18. package/dist/components/BaseSelect.vue.d.ts +2 -2
  19. package/dist/components/ChartContainer.vue.d.ts +2 -0
  20. package/dist/components/DatePicker.vue.d.ts +1 -1
  21. package/dist/components/ExperimentPopover.vue.d.ts +18 -0
  22. package/dist/components/ExperimentSelectorModal.vue.d.ts +0 -4
  23. package/dist/components/FilePicker.vue.d.ts +1 -1
  24. package/dist/components/FileUploader.vue.d.ts +2 -2
  25. package/dist/components/PluginIcon.vue.d.ts +2 -2
  26. package/dist/components/SampleSelector.vue.d.ts +6 -6
  27. package/dist/components/SequenceProgressBar.vue.d.ts +3 -0
  28. package/dist/components/StepWizard.vue.d.ts +3 -0
  29. package/dist/components/internal/AutoGroupModalInternal.vue.d.ts +1 -1
  30. package/dist/components/internal/DatePickerTimeInternal.vue.d.ts +1 -1
  31. package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +2 -7
  32. package/dist/components/internal/SelectMultipleInternal.vue.d.ts +4 -0
  33. package/dist/composables/createClient.d.ts +2 -0
  34. package/dist/composables/index.d.ts +1 -0
  35. package/dist/composables/pluginIdentity.d.ts +24 -0
  36. package/dist/composables/pluginWorkspaceModel.d.ts +2 -2
  37. package/dist/composables/useAppExperiment.d.ts +8 -0
  38. package/dist/composables/useCalendarGrid.d.ts +1 -1
  39. package/dist/composables/useExperimentSelector.d.ts +6 -2
  40. package/dist/composables/usePlatform.d.ts +1 -0
  41. package/dist/{controlSchemaModel-CP5-_nAj.js → controlSchemaModel-B8CTXagP.js} +2 -2
  42. package/dist/{controlSchemaModel-CP5-_nAj.js.map → controlSchemaModel-B8CTXagP.js.map} +1 -1
  43. package/dist/{data-C2PwI_dn.js → data-CgILxDLW.js} +178 -124
  44. package/dist/data-CgILxDLW.js.map +1 -0
  45. package/dist/data.js +8 -8
  46. package/dist/{experimentStatus-BZmU4czV.js → experimentStatus-Cqp6qNrR.js} +3 -3
  47. package/dist/{experimentStatus-BZmU4czV.js.map → experimentStatus-Cqp6qNrR.js.map} +1 -1
  48. package/dist/index.js +19 -19
  49. package/dist/{install-DAID6c2-.js → install-CsPFJSx3.js} +15 -14
  50. package/dist/{install-DAID6c2-.js.map → install-CsPFJSx3.js.map} +1 -1
  51. package/dist/install.js +1 -1
  52. package/dist/{lab-DLwRl_Ql.js → lab-MHzp-_J8.js} +128 -147
  53. package/dist/lab-MHzp-_J8.js.map +1 -0
  54. package/dist/lab.js +4 -3
  55. package/dist/{records-BzTcxekG.js → records-B6gpM_3e.js} +472 -274
  56. package/dist/records-B6gpM_3e.js.map +1 -0
  57. package/dist/{runtime-D6gVat6r.js → runtime-8bs8EogX.js} +27 -31
  58. package/dist/runtime-8bs8EogX.js.map +1 -0
  59. package/dist/runtime.js +6 -6
  60. package/dist/{shell-5ju97nOS.js → shell-CywENwBT.js} +5 -5
  61. package/dist/{shell-5ju97nOS.js.map → shell-CywENwBT.js.map} +1 -1
  62. package/dist/shell.js +5 -5
  63. package/dist/styles.css +1606 -1666
  64. package/dist/types/componentWorkflowTypes.d.ts +2 -0
  65. package/dist/types/generated/platformContext.d.ts +1 -0
  66. package/dist/types/generated-ui.d.ts +4 -0
  67. package/dist/{BaseModal-DfRIuVoj.js → useDropdownState-8yWMu-7_.js} +60 -2
  68. package/dist/useDropdownState-8yWMu-7_.js.map +1 -0
  69. package/dist/{useExperimentSamples-CxcnxZ8h.js → useExperimentSamples-DHnxKFAF.js} +3 -3
  70. package/dist/{useExperimentSamples-CxcnxZ8h.js.map → useExperimentSamples-DHnxKFAF.js.map} +1 -1
  71. package/dist/{useListReorder-CcbEYxX-.js → useListReorder-BitdUhxn.js} +109 -277
  72. package/dist/useListReorder-BitdUhxn.js.map +1 -0
  73. package/dist/{useRackEditor-BD5NIo5s.js → useRackEditor-DiwCt77M.js} +71 -57
  74. package/dist/useRackEditor-DiwCt77M.js.map +1 -0
  75. package/dist/{useRealtime-PalKue84.js → useRealtime-C3iy9QkU.js} +7 -4
  76. package/dist/useRealtime-C3iy9QkU.js.map +1 -0
  77. package/dist/useRequest-D506eHi8.js +244 -0
  78. package/dist/useRequest-D506eHi8.js.map +1 -0
  79. package/dist/utils/time.d.ts +7 -0
  80. package/package.json +1 -1
  81. package/src/__tests__/components/AppTopBar.test.ts +51 -2
  82. package/src/__tests__/components/AppTopBarPillNav.test.ts +35 -0
  83. package/src/__tests__/components/ArtifactSaveDialog.test.ts +78 -0
  84. package/src/__tests__/components/BaseSelect.multiple.test.ts +87 -32
  85. package/src/__tests__/components/BaseSelect.test.ts +2 -1
  86. package/src/__tests__/components/BatchProgressList.test.ts +16 -0
  87. package/src/__tests__/components/CalendarGridPanel.test.ts +2 -2
  88. package/src/__tests__/components/ChartContainer.test.ts +11 -0
  89. package/src/__tests__/components/ChemicalFormula.test.ts +6 -3
  90. package/src/__tests__/components/DatePicker.datetime.test.ts +6 -0
  91. package/src/__tests__/components/DatePicker.test.ts +21 -0
  92. package/src/__tests__/components/DatePicker.time.test.ts +2 -2
  93. package/src/__tests__/components/EmptyState.test.ts +1 -1
  94. package/src/__tests__/components/ExperimentPopover.test.ts +125 -16
  95. package/src/__tests__/components/ExperimentSelectorModal.test.ts +119 -13
  96. package/src/__tests__/components/GeneratedPluginView.test.ts +22 -1
  97. package/src/__tests__/components/InstrumentStateBadge.test.ts +4 -4
  98. package/src/__tests__/components/PluginWorkspaceView.test.ts +7 -0
  99. package/src/__tests__/components/ResourcePickerListInternal.test.ts +0 -20
  100. package/src/__tests__/components/SampleSelector.test.ts +13 -8
  101. package/src/__tests__/components/SequenceProgressBar.test.ts +12 -0
  102. package/src/__tests__/components/StepWizard.test.ts +11 -0
  103. package/src/__tests__/components/WellPlate.decoration.test.ts +11 -4
  104. package/src/__tests__/components/deprecationTags.test.ts +1 -1
  105. package/src/__tests__/composables/useAppExperiment.test.ts +27 -1
  106. package/src/__tests__/composables/useCalendarGrid.test.ts +4 -2
  107. package/src/__tests__/composables/useExperimentSelector.test.ts +111 -0
  108. package/src/__tests__/styles/baseControlsRules.test.ts +6 -2
  109. package/src/__tests__/styles/controlRefreshRules.test.ts +0 -6
  110. package/src/__tests__/styles/cssTokens.test.ts +2 -2
  111. package/src/__tests__/styles/menusShellRefresh.test.ts +14 -10
  112. package/src/__tests__/utils/plotlyTemplate.test.ts +4 -4
  113. package/src/__tests__/utils/time.test.ts +18 -1
  114. package/src/components/AdductText.story.vue +7 -0
  115. package/src/components/AdductText.vue +1 -1
  116. package/src/components/AlertBox.story.vue +2 -2
  117. package/src/components/AlertBox.vue +18 -16
  118. package/src/components/AppSidebar.story.vue +0 -28
  119. package/src/components/AppTopBar.story.vue +47 -12
  120. package/src/components/AppTopBar.vue +118 -114
  121. package/src/components/ArtifactSaveDialog.story.vue +23 -0
  122. package/src/components/ArtifactSaveDialog.vue +102 -12
  123. package/src/components/AuditTrail.story.vue +2 -1
  124. package/src/components/AuditTrail.vue +2 -21
  125. package/src/components/Avatar.vue +10 -14
  126. package/src/components/BaseSelect.multiple.story.vue +13 -2
  127. package/src/components/BaseSelect.vue +4 -3
  128. package/src/components/BatchProgressList.vue +35 -25
  129. package/src/components/ChartContainer.story.vue +2 -1
  130. package/src/components/ChartContainer.vue +8 -6
  131. package/src/components/ConfirmDialog.story.vue +1 -1
  132. package/src/components/EmptyState.vue +1 -1
  133. package/src/components/ExperimentPopover.story.vue +165 -53
  134. package/src/components/ExperimentPopover.vue +169 -187
  135. package/src/components/ExperimentSelectorModal.story.vue +40 -36
  136. package/src/components/ExperimentSelectorModal.vue +124 -110
  137. package/src/components/FormField.story.vue +8 -48
  138. package/src/components/GeneratedPluginView.vue +7 -1
  139. package/src/components/InstrumentStateBadge.vue +1 -1
  140. package/src/components/JobsStatusTray.vue +1 -1
  141. package/src/components/LoadingSpinner.vue +1 -1
  142. package/src/components/NumberInput.vue +3 -6
  143. package/src/components/PluginIcon.story.vue +2 -10
  144. package/src/components/PluginIcon.vue +2 -2
  145. package/src/components/PluginWorkspaceView.story.vue +1 -1
  146. package/src/components/RuntimeUpdateNotice.vue +1 -1
  147. package/src/components/SampleSelector.story.vue +1 -1
  148. package/src/components/SampleSelector.vue +27 -37
  149. package/src/components/SampleSelectorGroupNode.vue +1 -1
  150. package/src/components/SectionCard.story.vue +2 -2
  151. package/src/components/SequenceProgressBar.story.vue +9 -0
  152. package/src/components/SequenceProgressBar.vue +6 -1
  153. package/src/components/SettingsModal.schema.ts +1 -1
  154. package/src/components/SettingsModal.vue +2 -2
  155. package/src/components/SmartGroupFieldRecipe.story.vue +1 -1
  156. package/src/components/SmartGroupManual.story.vue +1 -1
  157. package/src/components/StepWizard.story.vue +33 -0
  158. package/src/components/StepWizard.vue +8 -1
  159. package/src/components/WellPlate.story.vue +3 -1
  160. package/src/components/WellPlate.vue +7 -0
  161. package/src/components/internal/AppTopBarPillNavInternal.vue +33 -2
  162. package/src/components/internal/DatePickerDateInternal.vue +16 -40
  163. package/src/components/internal/DatePickerDateTimeInternal.vue +3 -13
  164. package/src/components/internal/DatePickerTimeInternal.vue +5 -8
  165. package/src/components/internal/ResourcePickerListInternal.vue +45 -85
  166. package/src/components/internal/SelectMultipleInternal.vue +182 -65
  167. package/src/components/internal/SelectSearchableInternal.vue +2 -3
  168. package/src/composables/createClient.ts +2 -0
  169. package/src/composables/index.ts +1 -0
  170. package/src/composables/pluginIdentity.ts +31 -0
  171. package/src/composables/pluginWorkspaceModel.ts +2 -2
  172. package/src/composables/useAppExperiment.ts +20 -0
  173. package/src/composables/useCalendarGrid.ts +4 -3
  174. package/src/composables/useExperimentSelector.ts +77 -31
  175. package/src/composables/usePluginWorkspace.ts +1 -1
  176. package/src/styles/components/adduct-text.css +13 -5
  177. package/src/styles/components/alert-box.css +39 -58
  178. package/src/styles/components/app-layout.css +6 -5
  179. package/src/styles/components/app-page-selector.css +3 -1
  180. package/src/styles/components/app-pill-nav.css +11 -12
  181. package/src/styles/components/app-sidebar.css +9 -18
  182. package/src/styles/components/app-top-bar.css +57 -55
  183. package/src/styles/components/artifact-save-dialog.css +153 -2
  184. package/src/styles/components/audit-trail.css +16 -19
  185. package/src/styles/components/avatar.css +6 -4
  186. package/src/styles/components/batch-progress-list.css +38 -36
  187. package/src/styles/components/button.css +28 -59
  188. package/src/styles/components/calendar.css +12 -12
  189. package/src/styles/components/chart-container.css +34 -23
  190. package/src/styles/components/checkbox.css +2 -12
  191. package/src/styles/components/collapsible-card.css +27 -29
  192. package/src/styles/components/confirm-dialog.css +13 -14
  193. package/src/styles/components/dataframe.css +12 -4
  194. package/src/styles/components/date-picker.css +80 -92
  195. package/src/styles/components/datetime-picker.css +31 -43
  196. package/src/styles/components/divider.css +20 -20
  197. package/src/styles/components/dose-calculator.css +3 -3
  198. package/src/styles/components/empty-state.css +20 -17
  199. package/src/styles/components/experiment-code-badge.css +2 -0
  200. package/src/styles/components/experiment-popover.css +174 -329
  201. package/src/styles/components/experiment-selector-modal.css +75 -56
  202. package/src/styles/components/file-picker.css +3 -3
  203. package/src/styles/components/file-uploader.css +38 -49
  204. package/src/styles/components/form-builder.css +3 -3
  205. package/src/styles/components/formula-input.css +1 -1
  206. package/src/styles/components/icon-button.css +18 -24
  207. package/src/styles/components/input.css +5 -4
  208. package/src/styles/components/instrument-monitor.css +13 -15
  209. package/src/styles/components/jobs-status-tray.css +31 -25
  210. package/src/styles/components/modal.css +2 -4
  211. package/src/styles/components/molecule-input.css +2 -2
  212. package/src/styles/components/multi-select.css +65 -117
  213. package/src/styles/components/number-input.css +26 -19
  214. package/src/styles/components/pill.css +3 -2
  215. package/src/styles/components/plate-editor.css +0 -1
  216. package/src/styles/components/plugin-icon.css +26 -23
  217. package/src/styles/components/popover.css +2 -2
  218. package/src/styles/components/presence-avatars.css +1 -1
  219. package/src/styles/components/protocol-step-editor.css +1 -3
  220. package/src/styles/components/resource-card.css +2 -2
  221. package/src/styles/components/resource-picker-list.css +6 -57
  222. package/src/styles/components/sample-hierarchy-tree.css +4 -4
  223. package/src/styles/components/sample-selector.css +95 -79
  224. package/src/styles/components/schedule-calendar.css +8 -8
  225. package/src/styles/components/searchable-select.css +12 -12
  226. package/src/styles/components/section-card.css +28 -47
  227. package/src/styles/components/segmented-control.css +18 -15
  228. package/src/styles/components/select.css +3 -3
  229. package/src/styles/components/sequence-input.css +2 -2
  230. package/src/styles/components/sequence-progress-bar.css +17 -8
  231. package/src/styles/components/smart-group.css +18 -18
  232. package/src/styles/components/step-wizard.css +52 -16
  233. package/src/styles/components/tabs.css +2 -2
  234. package/src/styles/components/textarea.css +3 -3
  235. package/src/styles/components/time-picker.css +36 -21
  236. package/src/styles/components/toast.css +12 -10
  237. package/src/styles/components/toggle.css +1 -11
  238. package/src/styles/components/tooltip.css +10 -9
  239. package/src/styles/components/unit-input.css +8 -7
  240. package/src/styles/components/well-plate.css +9 -2
  241. package/src/styles/variables.css +106 -75
  242. package/src/types/componentWorkflowTypes.ts +2 -0
  243. package/src/types/generated/platformContext.ts +1 -0
  244. package/src/types/generated-ui.ts +4 -0
  245. package/src/utils/time.ts +21 -0
  246. package/dist/ActionMenu-CScINahU.js.map +0 -1
  247. package/dist/AppSidebar-BmiiPfz9.js.map +0 -1
  248. package/dist/BaseModal-DfRIuVoj.js.map +0 -1
  249. package/dist/ExperimentPopover-C4sGw-is.js +0 -359
  250. package/dist/ExperimentPopover-C4sGw-is.js.map +0 -1
  251. package/dist/ExperimentSelectorModal-D-9z7cNx.js.map +0 -1
  252. package/dist/SettingsModal-CSLE31yg.js.map +0 -1
  253. package/dist/data-C2PwI_dn.js.map +0 -1
  254. package/dist/lab-DLwRl_Ql.js.map +0 -1
  255. package/dist/records-BzTcxekG.js.map +0 -1
  256. package/dist/runtime-D6gVat6r.js.map +0 -1
  257. package/dist/useListReorder-CcbEYxX-.js.map +0 -1
  258. package/dist/useRackEditor-BD5NIo5s.js.map +0 -1
  259. package/dist/useRealtime-PalKue84.js.map +0 -1
  260. package/dist/useRequest-D5JDfsgZ.js +0 -76
  261. package/dist/useRequest-D5JDfsgZ.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  /** Interactive sample list with drag-and-drop group assignment, color labeling, and smart-group support. */
3
3
  import { ref, computed } from 'vue'
4
+ import { Layers, RotateCcw, WandSparkles } from 'lucide-vue-next'
4
5
  import BaseButton from './BaseButton.vue'
5
6
  import BaseCheckbox from './BaseCheckbox.vue'
6
7
  import BaseInput from './BaseInput.vue'
@@ -268,6 +269,31 @@ defineExpose({ handleSmartGroupApply })
268
269
 
269
270
  <template>
270
271
  <div class="mint-sample-selector">
272
+ <div class="mint-sample-selector__header">
273
+ <span class="mint-sample-selector__title">Samples</span>
274
+ <span class="mint-sample-selector__selected">{{ selectedCount }} selected</span>
275
+ <div v-if="!isPlaceholderState && enableGrouping" class="mint-sample-selector__header-actions">
276
+ <BaseButton
277
+ variant="ghost"
278
+ size="sm"
279
+ :icon="enableSmartGroup ? WandSparkles : Layers"
280
+ class="mint-sample-selector__action-btn"
281
+ @click="openGroupingModal(groupingActionMode)"
282
+ >
283
+ {{ enableSmartGroup ? 'Smart group' : 'Group' }}
284
+ </BaseButton>
285
+ <BaseButton
286
+ v-if="internalGroups.length > 0"
287
+ variant="ghost"
288
+ size="sm"
289
+ :icon="RotateCcw"
290
+ label="Clear all groups"
291
+ class="mint-sample-selector__action-btn"
292
+ @click="clearGroups"
293
+ />
294
+ </div>
295
+ </div>
296
+
271
297
  <div v-if="error" class="mint-sample-selector__state">
272
298
  <slot name="error" :error="error">
273
299
  <DataStateInternal state="error" size="sm" :title="error" />
@@ -302,43 +328,7 @@ defineExpose({ handleSmartGroupApply })
302
328
  @update:model-value="toggleSelectAll"
303
329
  />
304
330
  <span class="mint-sample-selector__select-all-label">All samples</span>
305
- <span class="mint-sample-selector__select-all-count">{{ selectedCount }} / {{ resolvedSamples.length }}</span>
306
- </div>
307
-
308
- <!-- Action Buttons Row -->
309
- <div v-if="!isPlaceholderState && enableGrouping" class="mint-sample-selector__actions">
310
- <div class="mint-sample-selector__actions-row">
311
- <!-- Grouping Button -->
312
- <BaseButton
313
- :variant="enableSmartGroup && groupingEnabled ? 'primary' : 'secondary'"
314
- size="sm"
315
- :disabled="resolvedSamples.length === 0"
316
- class="mint-sample-selector__action-btn"
317
- @click="openGroupingModal(groupingActionMode)"
318
- >
319
- <svg v-if="enableSmartGroup" class="mint-sample-selector__action-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
320
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
321
- </svg>
322
- <svg v-else class="mint-sample-selector__action-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
323
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3 3 7.5l9 4.5 9-4.5L12 3ZM3 12l9 4.5 9-4.5M3 16.5l9 4.5 9-4.5" />
324
- </svg>
325
- <span class="mint-sample-selector__action-label">Grouping</span>
326
- </BaseButton>
327
-
328
- <!-- Reset Button -->
329
- <BaseButton
330
- variant="ghost"
331
- size="sm"
332
- :disabled="internalGroups.length === 0"
333
- class="mint-sample-selector__action-btn mint-sample-selector__action-btn--reset"
334
- @click="clearGroups"
335
- title="Clear all groups"
336
- >
337
- <svg class="mint-sample-selector__action-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
338
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
339
- </svg>
340
- </BaseButton>
341
- </div>
331
+ <span class="mint-sample-selector__select-all-count">{{ selectedCount }}/{{ resolvedSamples.length }}</span>
342
332
  </div>
343
333
 
344
334
  <!-- Grouped View -->
@@ -153,7 +153,7 @@ function onHeaderKeydown(event: KeyboardEvent) {
153
153
  :title="node.group?.name ?? node.label"
154
154
  >{{ node.label }}</span>
155
155
 
156
- <span class="mint-sample-selector__count-badge">{{ selectedCount }} / {{ node.allSamples.length }}</span>
156
+ <span class="mint-sample-selector__count-badge">{{ selectedCount }}/{{ node.allSamples.length }}</span>
157
157
 
158
158
  <button
159
159
  type="button"
@@ -46,7 +46,7 @@ function initPlayground() {
46
46
  >
47
47
  <span style="flex: 1; min-width: 0">
48
48
  <span style="display: block; font-size: 0.8125rem; font-weight: 500; color: var(--text-primary)">{{ e.name }}</span>
49
- <span style="display: block; font-family: 'Fira Code', monospace; font-size: 0.65625rem; color: var(--text-muted)">{{ e.code }}</span>
49
+ <span style="display: block; font-family: var(--font-mono); font-size: 0.65625rem; color: var(--text-muted)">{{ e.code }}</span>
50
50
  </span>
51
51
  <span style="flex-shrink: 0; font-size: 0.71875rem; color: var(--text-muted)">{{ e.updated }}</span>
52
52
  </a>
@@ -139,7 +139,7 @@ function initPlayground() {
139
139
  <div class="mint-section-card__rows">
140
140
  <div v-for="n in 20" :key="n" class="mint-section-card__row">
141
141
  <span style="flex: 1; font-size: 0.8125rem; color: var(--text-primary)">Row {{ n }}</span>
142
- <span style="font-family: 'Fira Code', monospace; font-size: 0.71875rem; color: var(--text-muted)">MM-2026-{{ String(n).padStart(3, '0') }}</span>
142
+ <span style="font-family: var(--font-mono); font-size: 0.71875rem; color: var(--text-muted)">MM-2026-{{ String(n).padStart(3, '0') }}</span>
143
143
  </div>
144
144
  </div>
145
145
  <template #footer>
@@ -40,6 +40,7 @@ function initState() {
40
40
  showEta: true,
41
41
  showRemaining: true,
42
42
  label: 'samples',
43
+ failed: 0,
43
44
  }
44
45
  }
45
46
  </script>
@@ -60,6 +61,7 @@ function initState() {
60
61
  :label="state.label"
61
62
  :show-eta="state.showEta"
62
63
  :show-remaining="state.showRemaining"
64
+ :failed="state.failed"
63
65
  />
64
66
  </div>
65
67
  </template>
@@ -69,6 +71,7 @@ function initState() {
69
71
  <HstNumber v-model="state.total" title="Total samples" />
70
72
  <HstNumber v-model="state.remaining" title="Remaining (s)" />
71
73
  <HstText v-model="state.label" title="Unit label" />
74
+ <HstNumber v-model="state.failed" title="Failed" />
72
75
  <HstCheckbox v-model="state.compact" title="Compact" />
73
76
  <HstCheckbox v-model="state.showEta" title="Show ETA" />
74
77
  <HstCheckbox v-model="state.showRemaining" title="Show remaining" />
@@ -81,6 +84,12 @@ function initState() {
81
84
  </div>
82
85
  </Variant>
83
86
 
87
+ <Variant title="With failures">
88
+ <div style="padding: 2rem; max-width: 600px;">
89
+ <SequenceProgressBar :progress="running" :failed="1" />
90
+ </div>
91
+ </Variant>
92
+
84
93
  <Variant title="Compact (no footer)">
85
94
  <!-- Compact drops the remaining/ETA row for dense instrument tables. -->
86
95
  <div style="padding: 2rem; max-width: 600px;">
@@ -15,6 +15,8 @@ interface Props {
15
15
  label?: string
16
16
  showEta?: boolean
17
17
  showRemaining?: boolean
18
+ /** Failed injections so far; shown as "N failed" in the head when above 0. */
19
+ failed?: number
18
20
  }
19
21
 
20
22
  const props = withDefaults(defineProps<Props>(), {
@@ -22,6 +24,7 @@ const props = withDefaults(defineProps<Props>(), {
22
24
  label: 'samples',
23
25
  showEta: true,
24
26
  showRemaining: true,
27
+ failed: 0,
25
28
  })
26
29
 
27
30
  const percent = computed(() => sequenceProgressPercent(props.progress))
@@ -37,8 +40,10 @@ const hasFooter = computed(() => {
37
40
  <div class="mint-sequence-progress">
38
41
  <div class="mint-sequence-progress__header">
39
42
  <span class="mint-sequence-progress__label">
40
- {{ progress.current_sample }} / {{ progress.total_samples }} {{ label }}
43
+ <span class="mint-sequence-progress__count">{{ progress.current_sample }} / {{ progress.total_samples }}</span>
44
+ {{ label }}
41
45
  </span>
46
+ <span v-if="failed > 0" class="mint-sequence-progress__failed">{{ failed }} failed</span>
42
47
  <span class="mint-sequence-progress__percent">{{ percent }}%</span>
43
48
  </div>
44
49
 
@@ -17,7 +17,7 @@ export const APPEARANCE_TAB: SettingsTab = {
17
17
  // Heroicons "swatch" — the built-in tab sits beside plugin tabs that all
18
18
  // carry icons, so it needs one of its own to keep the rail consistent.
19
19
  icon: '<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.098 19.902a3.75 3.75 0 0 0 5.304 0l6.401-6.402M6.75 21A3.75 3.75 0 0 1 3 17.25V4.125C3 3.504 3.504 3 4.125 3h5.25c.621 0 1.125.504 1.125 1.125v4.072M6.75 21a3.75 3.75 0 0 0 3.75-3.75V8.197M6.75 21h13.125c.621 0 1.125-.504 1.125-1.125v-5.25c0-.621-.504-1.125-1.125-1.125h-4.072M10.5 8.197l2.88-2.88c.438-.439 1.15-.439 1.59 0l3.712 3.713c.44.44.44 1.152 0 1.59l-2.879 2.88M6.75 17.25h.008v.008H6.75v-.008Z"></path></svg>',
20
- description: 'Theme, color palette, and table density',
20
+ description: 'Theme, color palette, and density',
21
21
  }
22
22
 
23
23
  // Map settings groups onto the form-builder's flat-section shape.
@@ -591,8 +591,8 @@ function canShowForCurrentUser(item: AccessControlled): boolean {
591
591
 
592
592
  <div class="mint-settings-modal__setting mint-settings-modal__setting--inline">
593
593
  <div class="mint-settings-modal__setting-text">
594
- <span class="mint-settings-modal__setting-label">Table density</span>
595
- <span class="mint-settings-modal__setting-hint">Adjusts row height in data tables</span>
594
+ <span class="mint-settings-modal__setting-label">Density</span>
595
+ <span class="mint-settings-modal__setting-hint">Adjusts control and table row height</span>
596
596
  </div>
597
597
  <SegmentedControl
598
598
  :model-value="settings.tableDensity"
@@ -51,7 +51,7 @@ import type { FieldRecipeApplyPayload } from './SmartGroup.types'
51
51
  }
52
52
  .sg-story__log {
53
53
  margin: 0;
54
- font-family: 'Fira Code', monospace;
54
+ font-family: var(--font-mono);
55
55
  font-size: 0.8125rem;
56
56
  color: var(--text-secondary, #64748b);
57
57
  }
@@ -48,7 +48,7 @@ import type { ManualDonePayload } from './SmartGroup.types'
48
48
  }
49
49
  .sg-story__log {
50
50
  margin: 0;
51
- font-family: 'Fira Code', monospace;
51
+ font-family: var(--font-mono);
52
52
  font-size: 0.8125rem;
53
53
  color: var(--text-secondary, #64748b);
54
54
  }
@@ -23,6 +23,7 @@ function initState() {
23
23
  showProgress: true,
24
24
  showStepNumbers: true,
25
25
  size: 'md' as const,
26
+ orientation: 'horizontal' as const,
26
27
  }
27
28
  }
28
29
  </script>
@@ -39,6 +40,7 @@ function initState() {
39
40
  :show-progress="state.showProgress"
40
41
  :show-step-numbers="state.showStepNumbers"
41
42
  :size="state.size"
43
+ :orientation="state.orientation"
42
44
  >
43
45
  <template #step-setup>
44
46
  <div style="padding: 1.5rem;">
@@ -72,6 +74,14 @@ function initState() {
72
74
  <HstCheckbox v-model="state.linear" title="Linear" />
73
75
  <HstCheckbox v-model="state.showProgress" title="Show Progress" />
74
76
  <HstCheckbox v-model="state.showStepNumbers" title="Show Step Numbers" />
77
+ <HstSelect
78
+ v-model="state.orientation"
79
+ title="Orientation"
80
+ :options="[
81
+ { value: 'horizontal', label: 'Horizontal' },
82
+ { value: 'vertical', label: 'Vertical' },
83
+ ]"
84
+ />
75
85
  <HstSelect
76
86
  v-model="state.size"
77
87
  title="Size"
@@ -129,6 +139,29 @@ function initState() {
129
139
  </div>
130
140
  </Variant>
131
141
 
142
+ <Variant title="Vertical">
143
+ <div style="padding: 2rem; max-width: 700px;">
144
+ <StepWizard
145
+ :steps="mockSteps"
146
+ :model-value="2"
147
+ orientation="vertical"
148
+ >
149
+ <template #step-setup>
150
+ <div style="padding: 1.5rem;">Setup</div>
151
+ </template>
152
+ <template #step-samples>
153
+ <div style="padding: 1.5rem;">Samples</div>
154
+ </template>
155
+ <template #step-protocol>
156
+ <div style="padding: 1.5rem;">Protocol (active)</div>
157
+ </template>
158
+ <template #step-review>
159
+ <div style="padding: 1.5rem;">Review</div>
160
+ </template>
161
+ </StepWizard>
162
+ </div>
163
+ </Variant>
164
+
132
165
  <Variant title="Small">
133
166
  <div style="padding: 2rem; max-width: 500px;">
134
167
  <StepWizard
@@ -11,6 +11,8 @@ export interface Props {
11
11
  showProgress?: boolean
12
12
  showStepNumbers?: boolean
13
13
  size?: 'sm' | 'md' | 'lg'
14
+ /** `vertical` stacks the steps in a rail beside the step content. */
15
+ orientation?: 'horizontal' | 'vertical'
14
16
  }
15
17
 
16
18
  const props = withDefaults(defineProps<Props>(), {
@@ -19,6 +21,7 @@ const props = withDefaults(defineProps<Props>(), {
19
21
  showProgress: true,
20
22
  showStepNumbers: true,
21
23
  size: 'md',
24
+ orientation: 'horizontal',
22
25
  })
23
26
 
24
27
  const emit = defineEmits<{
@@ -114,7 +117,11 @@ defineExpose({ setStepValid })
114
117
 
115
118
  <template>
116
119
  <div
117
- :class="['mint-wizard', `mint-wizard--${props.size}`]"
120
+ :class="[
121
+ 'mint-wizard',
122
+ `mint-wizard--${props.size}`,
123
+ { 'mint-wizard--vertical': props.orientation === 'vertical' },
124
+ ]"
118
125
  tabindex="0"
119
126
  @keydown="handleKeydown"
120
127
  >
@@ -64,13 +64,14 @@ function generateHeatmapWells(format: WellPlateFormat): Record<string, Partial<W
64
64
  const mockWells96 = generateMockWells(96)
65
65
  const heatmapWells = generateHeatmapWells(96)
66
66
  const mockWells6 = generateMockWells(6)
67
- // One well per marker type; the Q / i glyph moves to a corner when an ID or label takes the centre.
67
+ // One well per marker type; a QC well shows a mono Q and an iQC well an i when no ID or label takes the centre.
68
68
  const markerWells: Record<string, Partial<Well>> = {
69
69
  A1: { id: 'A1', row: 0, col: 0, state: 'filled', sampleType: 'sample', metadata: { label: 'S-01' } },
70
70
  A2: { id: 'A2', row: 0, col: 1, state: 'filled', sampleType: 'blank', metadata: { label: 'Blank' } },
71
71
  A3: { id: 'A3', row: 0, col: 2, state: 'filled', sampleType: 'qc', metadata: { label: 'Pool' } },
72
72
  B1: { id: 'B1', row: 1, col: 0, state: 'filled', sampleType: 'iqc', metadata: { label: 'Std 1' } },
73
73
  B2: { id: 'B2', row: 1, col: 1, state: 'filled', sampleType: 'iqc' },
74
+ B3: { id: 'B3', row: 1, col: 2, state: 'filled', sampleType: 'qc' },
74
75
  }
75
76
 
76
77
  const rectSelected = ref<string[]>([])
@@ -287,6 +288,7 @@ function initState() {
287
288
  <div style="padding: 2rem; display: flex; gap: 2rem;">
288
289
  <WellPlate :spec="{ format: 6, selectionMode: 'none', showWellIds: true, size: 'lg' }" :wells="markerWells" />
289
290
  <WellPlate :spec="{ format: 6, selectionMode: 'none', showWellLabels: true, size: 'lg' }" :wells="markerWells" />
291
+ <WellPlate :spec="{ format: 6, selectionMode: 'none', size: 'lg' }" :wells="markerWells" />
290
292
  </div>
291
293
  </Variant>
292
294
 
@@ -449,6 +449,13 @@ const tableStyle = computed(() => ({
449
449
  >
450
450
  {{ well.id }}
451
451
  </span>
452
+ <!-- QC / iQC marker glyph when the centre is free -->
453
+ <span
454
+ v-else-if="well.sampleType === 'qc' || well.sampleType === 'iqc'"
455
+ class="mint-well-plate__indicator"
456
+ >
457
+ {{ well.sampleType === 'qc' ? 'Q' : 'i' }}
458
+ </span>
452
459
 
453
460
  <!-- Badge (injection count or custom method) -->
454
461
  <span
@@ -21,7 +21,26 @@ const emit = defineEmits<{
21
21
  const openItemId = ref<string | null>(null)
22
22
  const itemRefs = ref<Map<string, HTMLElement>>(new Map())
23
23
 
24
- const normalizedItems = computed<PillNavItem[]>(() => props.items.map(normalizePillNavItem))
24
+ /** The bar shows this many items; the rest fold into a More dropdown. */
25
+ const MAX_VISIBLE_ITEMS = 4
26
+ const MORE_ITEM_ID = '__mint-pill-nav-more'
27
+
28
+ const allItems = computed<PillNavItem[]>(() => props.items.map(normalizePillNavItem))
29
+
30
+ const normalizedItems = computed<PillNavItem[]>(() => {
31
+ if (allItems.value.length <= MAX_VISIBLE_ITEMS) return allItems.value
32
+ const folded = allItems.value.slice(MAX_VISIBLE_ITEMS)
33
+ return [
34
+ ...allItems.value.slice(0, MAX_VISIBLE_ITEMS),
35
+ {
36
+ id: MORE_ITEM_ID,
37
+ label: 'More',
38
+ dot: folded.some(item => item.dot),
39
+ // A folded group contributes its options; a plain item becomes one option.
40
+ children: folded.flatMap(item => item.children?.length ? item.children : [item]),
41
+ },
42
+ ]
43
+ })
25
44
 
26
45
  function handleClick(item: PillNavItem) {
27
46
  if (item.disabled) return
@@ -35,8 +54,20 @@ function handleClick(item: PillNavItem) {
35
54
 
36
55
  function handleOptionClick(option: PillNavOption, item: PillNavItem) {
37
56
  if (option.disabled) return
38
- if (!option.href && !option.to) emit('option-select', option, item)
39
57
  openItemId.value = null
58
+ if (option.href || option.to) return
59
+ if (item.id !== MORE_ITEM_ID) {
60
+ emit('option-select', option, item)
61
+ return
62
+ }
63
+ // A folded entry reports as it would from its own pill.
64
+ const folded = allItems.value.find(candidate => candidate.id === option.id && !candidate.children?.length)
65
+ if (folded) {
66
+ emit('select', folded)
67
+ return
68
+ }
69
+ const group = allItems.value.find(candidate => candidate.children?.some(child => child.id === option.id))
70
+ if (group) emit('option-select', option, group)
40
71
  }
41
72
 
42
73
  function normalizePillNavItem(item: PillNavItemInput): PillNavItem {
@@ -42,7 +42,6 @@ const {
42
42
  weekDays,
43
43
  monthYear,
44
44
  calendarDays,
45
- isDateDisabled,
46
45
  formatDateValue,
47
46
  setCurrentMonthFromDate,
48
47
  prevMonth,
@@ -58,14 +57,7 @@ const selectedDate = computed(() => {
58
57
  return isNaN(date.getTime()) ? null : date
59
58
  })
60
59
 
61
- const displayValue = computed(() => {
62
- if (!selectedDate.value) return ''
63
- return selectedDate.value.toLocaleDateString('en-US', {
64
- year: 'numeric',
65
- month: 'short',
66
- day: 'numeric',
67
- })
68
- })
60
+ const displayValue = computed(() => (selectedDate.value ? formatDateValue(selectedDate.value) : ''))
69
61
 
70
62
  function selectDate(day: CalendarGridDay) {
71
63
  if (day.isDisabled || props.disabled) return
@@ -73,15 +65,6 @@ function selectDate(day: CalendarGridDay) {
73
65
  close()
74
66
  }
75
67
 
76
- function goToToday() {
77
- const today = new Date()
78
- setCurrentMonthFromDate(today)
79
- if (!isDateDisabled(today)) {
80
- emit('update:modelValue', formatDateValue(today))
81
- close()
82
- }
83
- }
84
-
85
68
  function clear() {
86
69
  emit('update:modelValue', undefined)
87
70
  close()
@@ -167,6 +150,11 @@ onBeforeUnmount(stopPositioning)
167
150
  <template>
168
151
  <div ref="container" class="mint-date-picker">
169
152
  <div class="mint-date-picker__input-wrapper">
153
+ <div class="mint-date-picker__icon">
154
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
155
+ <path d="M8 2v4" /><path d="M16 2v4" /><rect width="18" height="18" x="3" y="4" rx="2" /><path d="M3 10h18" />
156
+ </svg>
157
+ </div>
170
158
  <input
171
159
  ref="triggerRef"
172
160
  type="text"
@@ -185,11 +173,17 @@ onBeforeUnmount(stopPositioning)
185
173
  @click="toggleCalendar"
186
174
  @keydown="handleTriggerKeydown"
187
175
  />
188
- <div class="mint-date-picker__icon">
189
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
190
- <path d="M8 2v4" /><path d="M16 2v4" /><rect width="18" height="18" x="3" y="4" rx="2" /><path d="M3 10h18" />
176
+ <button
177
+ v-if="clearable && modelValue && !disabled"
178
+ type="button"
179
+ class="mint-date-picker__clear-btn"
180
+ aria-label="Clear date"
181
+ @click.stop="clear"
182
+ >
183
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
184
+ <path d="M18 6 6 18" /><path d="m6 6 12 12" />
191
185
  </svg>
192
- </div>
186
+ </button>
193
187
  </div>
194
188
 
195
189
  <Teleport to="body">
@@ -218,24 +212,6 @@ onBeforeUnmount(stopPositioning)
218
212
  @next-month="nextMonth"
219
213
  @select="selectDate"
220
214
  />
221
-
222
- <div class="mint-date-picker__footer">
223
- <button
224
- type="button"
225
- class="mint-date-picker__footer-btn mint-date-picker__today-btn"
226
- @click="goToToday"
227
- >
228
- Today
229
- </button>
230
- <button
231
- v-if="clearable && modelValue"
232
- type="button"
233
- class="mint-date-picker__footer-btn mint-date-picker__clear-btn"
234
- @click="clear"
235
- >
236
- Clear
237
- </button>
238
- </div>
239
215
  </div>
240
216
  </Transition>
241
217
  </Teleport>
@@ -76,17 +76,12 @@ const selectedTimeStr = computed(() => {
76
76
 
77
77
  const displayValue = computed(() => {
78
78
  if (!selectedDate.value) return ''
79
- const dateStr = selectedDate.value.toLocaleDateString(props.locale, {
80
- year: 'numeric',
81
- month: 'short',
82
- day: 'numeric',
83
- })
84
79
  const timeStr = formatTime(
85
80
  selectedDate.value.getHours(),
86
81
  selectedDate.value.getMinutes(),
87
82
  props.timeFormat,
88
83
  )
89
- return `${dateStr} ${timeStr}`
84
+ return `${formatDateValue(selectedDate.value)} ${timeStr}`
90
85
  })
91
86
 
92
87
  // Time slots
@@ -198,8 +193,8 @@ watch(isOpen, async (opened) => {
198
193
  <div ref="root" class="mint-datetime-picker">
199
194
  <div class="mint-datetime-picker__input-wrapper">
200
195
  <div class="mint-datetime-picker__icon-calendar">
201
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
202
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
196
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
197
+ <path d="M8 2v4" /><path d="M16 2v4" /><rect width="18" height="18" x="3" y="4" rx="2" /><path d="M3 10h18" />
203
198
  </svg>
204
199
  </div>
205
200
  <input
@@ -221,11 +216,6 @@ watch(isOpen, async (opened) => {
221
216
  @click="toggleDropdown"
222
217
  @keydown="handleTriggerKeydown"
223
218
  />
224
- <div class="mint-datetime-picker__icon-clock">
225
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
226
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
227
- </svg>
228
- </div>
229
219
  </div>
230
220
 
231
221
  <Transition
@@ -210,6 +210,11 @@ watch(isOpen, (open) => {
210
210
  <template>
211
211
  <div ref="root" class="mint-time-picker" @keydown="handleKeydown">
212
212
  <div class="mint-time-picker__input-wrapper">
213
+ <div class="mint-time-picker__icon">
214
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
215
+ <circle cx="12" cy="12" r="10" /><path d="M12 6v6l4 2" />
216
+ </svg>
217
+ </div>
213
218
  <input
214
219
  ref="trigger"
215
220
  type="text"
@@ -241,11 +246,6 @@ watch(isOpen, (open) => {
241
246
  <path d="M18 6 6 18" /><path d="m6 6 12 12" />
242
247
  </svg>
243
248
  </button>
244
- <div class="mint-time-picker__icon">
245
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
246
- <circle cx="12" cy="12" r="10" /><path d="M12 6v6l4 2" />
247
- </svg>
248
- </div>
249
249
  </div>
250
250
 
251
251
  <Transition
@@ -284,9 +284,6 @@ watch(isOpen, (open) => {
284
284
  >
285
285
  <span class="mint-time-picker__slot-time">{{ formatTimeSlot(slot, format) }}</span>
286
286
  <span v-if="slotReason(slot)" class="mint-time-picker__slot-reason">{{ slotReason(slot) }}</span>
287
- <svg class="mint-menu-item__check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
288
- <path d="M3.5 8.5l3 3 6-7" />
289
- </svg>
290
287
  </li>
291
288
  </ul>
292
289
  </div>