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

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 (266) 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/__tests__/styles/v2AuditRules.test.d.ts +1 -0
  16. package/dist/components/AppSidebar.vue.d.ts +1 -1
  17. package/dist/components/ArtifactSaveDialog.vue.d.ts +9 -1
  18. package/dist/components/AuditTrail.vue.d.ts +1 -1
  19. package/dist/components/BaseSelect.vue.d.ts +2 -2
  20. package/dist/components/ChartContainer.vue.d.ts +2 -0
  21. package/dist/components/DatePicker.vue.d.ts +1 -1
  22. package/dist/components/ExperimentPopover.vue.d.ts +18 -0
  23. package/dist/components/ExperimentSelectorModal.vue.d.ts +0 -4
  24. package/dist/components/FilePicker.vue.d.ts +1 -1
  25. package/dist/components/FileUploader.vue.d.ts +2 -2
  26. package/dist/components/PluginIcon.vue.d.ts +2 -2
  27. package/dist/components/SampleSelector.vue.d.ts +6 -6
  28. package/dist/components/SequenceProgressBar.vue.d.ts +3 -0
  29. package/dist/components/StepWizard.vue.d.ts +3 -0
  30. package/dist/components/internal/AutoGroupModalInternal.vue.d.ts +1 -1
  31. package/dist/components/internal/DatePickerTimeInternal.vue.d.ts +1 -1
  32. package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +2 -7
  33. package/dist/components/internal/SelectMultipleInternal.vue.d.ts +4 -0
  34. package/dist/composables/createClient.d.ts +2 -0
  35. package/dist/composables/index.d.ts +1 -0
  36. package/dist/composables/pluginIdentity.d.ts +24 -0
  37. package/dist/composables/pluginWorkspaceModel.d.ts +2 -2
  38. package/dist/composables/useAppExperiment.d.ts +8 -0
  39. package/dist/composables/useCalendarGrid.d.ts +1 -1
  40. package/dist/composables/useExperimentSelector.d.ts +6 -2
  41. package/dist/composables/usePlatform.d.ts +1 -0
  42. package/dist/{controlSchemaModel-CP5-_nAj.js → controlSchemaModel-B8CTXagP.js} +2 -2
  43. package/dist/{controlSchemaModel-CP5-_nAj.js.map → controlSchemaModel-B8CTXagP.js.map} +1 -1
  44. package/dist/{data-C2PwI_dn.js → data-CgILxDLW.js} +178 -124
  45. package/dist/data-CgILxDLW.js.map +1 -0
  46. package/dist/data.js +8 -8
  47. package/dist/{experimentStatus-BZmU4czV.js → experimentStatus-Cqp6qNrR.js} +3 -3
  48. package/dist/{experimentStatus-BZmU4czV.js.map → experimentStatus-Cqp6qNrR.js.map} +1 -1
  49. package/dist/index.js +19 -19
  50. package/dist/{install-DAID6c2-.js → install-mB8-IyvO.js} +15 -14
  51. package/dist/{install-DAID6c2-.js.map → install-mB8-IyvO.js.map} +1 -1
  52. package/dist/install.js +1 -1
  53. package/dist/{lab-DLwRl_Ql.js → lab-D5bIX426.js} +128 -147
  54. package/dist/lab-D5bIX426.js.map +1 -0
  55. package/dist/lab.js +4 -3
  56. package/dist/{records-BzTcxekG.js → records-B6gpM_3e.js} +472 -274
  57. package/dist/records-B6gpM_3e.js.map +1 -0
  58. package/dist/{runtime-D6gVat6r.js → runtime-D7vKpfbF.js} +27 -31
  59. package/dist/runtime-D7vKpfbF.js.map +1 -0
  60. package/dist/runtime.js +6 -6
  61. package/dist/{shell-5ju97nOS.js → shell-CywENwBT.js} +5 -5
  62. package/dist/{shell-5ju97nOS.js.map → shell-CywENwBT.js.map} +1 -1
  63. package/dist/shell.js +5 -5
  64. package/dist/styles.css +1644 -1688
  65. package/dist/types/componentWorkflowTypes.d.ts +2 -0
  66. package/dist/types/generated/platformContext.d.ts +1 -0
  67. package/dist/types/generated-ui.d.ts +4 -0
  68. package/dist/{BaseModal-DfRIuVoj.js → useDropdownState-8yWMu-7_.js} +60 -2
  69. package/dist/useDropdownState-8yWMu-7_.js.map +1 -0
  70. package/dist/{useExperimentSamples-CxcnxZ8h.js → useExperimentSamples-DHnxKFAF.js} +3 -3
  71. package/dist/{useExperimentSamples-CxcnxZ8h.js.map → useExperimentSamples-DHnxKFAF.js.map} +1 -1
  72. package/dist/{useListReorder-CcbEYxX-.js → useListReorder-BitdUhxn.js} +109 -277
  73. package/dist/useListReorder-BitdUhxn.js.map +1 -0
  74. package/dist/{useRackEditor-BD5NIo5s.js → useRackEditor-Q8xO-cCy.js} +72 -58
  75. package/dist/useRackEditor-Q8xO-cCy.js.map +1 -0
  76. package/dist/{useRealtime-PalKue84.js → useRealtime-C3iy9QkU.js} +7 -4
  77. package/dist/useRealtime-C3iy9QkU.js.map +1 -0
  78. package/dist/useRequest-D506eHi8.js +244 -0
  79. package/dist/useRequest-D506eHi8.js.map +1 -0
  80. package/dist/utils/time.d.ts +7 -0
  81. package/package.json +1 -1
  82. package/src/__tests__/components/AppTopBar.test.ts +51 -2
  83. package/src/__tests__/components/AppTopBarPillNav.test.ts +35 -0
  84. package/src/__tests__/components/ArtifactSaveDialog.test.ts +78 -0
  85. package/src/__tests__/components/BaseSelect.multiple.test.ts +87 -32
  86. package/src/__tests__/components/BaseSelect.test.ts +2 -1
  87. package/src/__tests__/components/BatchProgressList.test.ts +16 -0
  88. package/src/__tests__/components/CalendarGridPanel.test.ts +2 -2
  89. package/src/__tests__/components/ChartContainer.test.ts +11 -0
  90. package/src/__tests__/components/ChemicalFormula.test.ts +6 -3
  91. package/src/__tests__/components/DatePicker.datetime.test.ts +6 -0
  92. package/src/__tests__/components/DatePicker.test.ts +21 -0
  93. package/src/__tests__/components/DatePicker.time.test.ts +2 -2
  94. package/src/__tests__/components/EmptyState.test.ts +1 -1
  95. package/src/__tests__/components/ExperimentPopover.test.ts +125 -16
  96. package/src/__tests__/components/ExperimentSelectorModal.test.ts +119 -13
  97. package/src/__tests__/components/GeneratedPluginView.test.ts +22 -1
  98. package/src/__tests__/components/InstrumentStateBadge.test.ts +4 -4
  99. package/src/__tests__/components/PluginWorkspaceView.test.ts +7 -0
  100. package/src/__tests__/components/ResourcePickerListInternal.test.ts +0 -20
  101. package/src/__tests__/components/SampleSelector.test.ts +13 -8
  102. package/src/__tests__/components/SequenceProgressBar.test.ts +12 -0
  103. package/src/__tests__/components/StepWizard.test.ts +11 -0
  104. package/src/__tests__/components/WellPlate.decoration.test.ts +11 -4
  105. package/src/__tests__/components/deprecationTags.test.ts +1 -1
  106. package/src/__tests__/composables/useAppExperiment.test.ts +27 -1
  107. package/src/__tests__/composables/useCalendarGrid.test.ts +4 -2
  108. package/src/__tests__/composables/useExperimentSelector.test.ts +111 -0
  109. package/src/__tests__/styles/baseControlsRules.test.ts +6 -2
  110. package/src/__tests__/styles/controlRefreshRules.test.ts +0 -6
  111. package/src/__tests__/styles/cssTokens.test.ts +2 -2
  112. package/src/__tests__/styles/menusShellRefresh.test.ts +14 -10
  113. package/src/__tests__/styles/v2AuditRules.test.ts +96 -0
  114. package/src/__tests__/utils/plotlyTemplate.test.ts +4 -4
  115. package/src/__tests__/utils/time.test.ts +18 -1
  116. package/src/components/AdductText.story.vue +7 -0
  117. package/src/components/AdductText.vue +1 -1
  118. package/src/components/AlertBox.story.vue +2 -2
  119. package/src/components/AlertBox.vue +18 -16
  120. package/src/components/AppSidebar.story.vue +0 -28
  121. package/src/components/AppTopBar.story.vue +47 -12
  122. package/src/components/AppTopBar.vue +118 -114
  123. package/src/components/ArtifactSaveDialog.story.vue +23 -0
  124. package/src/components/ArtifactSaveDialog.vue +102 -12
  125. package/src/components/AuditTrail.story.vue +2 -1
  126. package/src/components/AuditTrail.vue +2 -21
  127. package/src/components/Avatar.vue +10 -14
  128. package/src/components/BaseSelect.multiple.story.vue +13 -2
  129. package/src/components/BaseSelect.vue +4 -3
  130. package/src/components/BatchProgressList.vue +35 -25
  131. package/src/components/ChartContainer.story.vue +2 -1
  132. package/src/components/ChartContainer.vue +8 -6
  133. package/src/components/ConfirmDialog.story.vue +1 -1
  134. package/src/components/EmptyState.vue +1 -1
  135. package/src/components/ExperimentPopover.story.vue +165 -53
  136. package/src/components/ExperimentPopover.vue +169 -187
  137. package/src/components/ExperimentSelectorModal.story.vue +40 -36
  138. package/src/components/ExperimentSelectorModal.vue +124 -110
  139. package/src/components/FormField.story.vue +8 -48
  140. package/src/components/GeneratedPluginView.vue +7 -1
  141. package/src/components/GeneratedResultRenderer.vue +3 -4
  142. package/src/components/InstrumentStateBadge.vue +1 -1
  143. package/src/components/JobsStatusTray.vue +1 -1
  144. package/src/components/LoadingSpinner.vue +1 -1
  145. package/src/components/NumberInput.vue +3 -6
  146. package/src/components/PluginIcon.story.vue +2 -10
  147. package/src/components/PluginIcon.vue +2 -2
  148. package/src/components/PluginWorkspaceView.story.vue +1 -1
  149. package/src/components/RuntimeUpdateNotice.vue +1 -1
  150. package/src/components/SampleSelector.story.vue +1 -1
  151. package/src/components/SampleSelector.vue +27 -37
  152. package/src/components/SampleSelectorGroupNode.vue +1 -1
  153. package/src/components/SectionCard.story.vue +2 -2
  154. package/src/components/SequenceProgressBar.story.vue +9 -0
  155. package/src/components/SequenceProgressBar.vue +6 -1
  156. package/src/components/SettingsModal.schema.ts +1 -1
  157. package/src/components/SettingsModal.vue +2 -2
  158. package/src/components/SmartGroupFieldRecipe.story.vue +1 -1
  159. package/src/components/SmartGroupManual.story.vue +1 -1
  160. package/src/components/StepWizard.story.vue +33 -0
  161. package/src/components/StepWizard.vue +8 -1
  162. package/src/components/WellPlate.story.vue +3 -1
  163. package/src/components/WellPlate.vue +7 -0
  164. package/src/components/internal/AppTopBarPillNavInternal.vue +33 -2
  165. package/src/components/internal/DatePickerDateInternal.vue +16 -40
  166. package/src/components/internal/DatePickerDateTimeInternal.vue +3 -13
  167. package/src/components/internal/DatePickerTimeInternal.vue +5 -8
  168. package/src/components/internal/ResourcePickerListInternal.vue +45 -85
  169. package/src/components/internal/SelectMultipleInternal.vue +182 -65
  170. package/src/components/internal/SelectSearchableInternal.vue +2 -3
  171. package/src/composables/createClient.ts +2 -0
  172. package/src/composables/index.ts +1 -0
  173. package/src/composables/pluginIdentity.ts +31 -0
  174. package/src/composables/pluginWorkspaceModel.ts +2 -2
  175. package/src/composables/useAppExperiment.ts +20 -0
  176. package/src/composables/useCalendarGrid.ts +4 -3
  177. package/src/composables/useExperimentSelector.ts +77 -31
  178. package/src/composables/usePluginWorkspace.ts +1 -1
  179. package/src/styles/components/adduct-text.css +13 -5
  180. package/src/styles/components/alert-box.css +39 -58
  181. package/src/styles/components/app-layout.css +7 -5
  182. package/src/styles/components/app-page-selector.css +3 -1
  183. package/src/styles/components/app-pill-nav.css +11 -12
  184. package/src/styles/components/app-sidebar.css +9 -18
  185. package/src/styles/components/app-top-bar.css +57 -55
  186. package/src/styles/components/artifact-save-dialog.css +153 -2
  187. package/src/styles/components/audit-trail.css +16 -19
  188. package/src/styles/components/avatar.css +6 -4
  189. package/src/styles/components/batch-progress-list.css +38 -36
  190. package/src/styles/components/button.css +28 -59
  191. package/src/styles/components/calendar.css +12 -12
  192. package/src/styles/components/chart-container.css +34 -23
  193. package/src/styles/components/checkbox.css +2 -12
  194. package/src/styles/components/collapsible-card.css +27 -29
  195. package/src/styles/components/confirm-dialog.css +13 -14
  196. package/src/styles/components/dataframe.css +13 -5
  197. package/src/styles/components/date-picker.css +80 -92
  198. package/src/styles/components/datetime-picker.css +31 -43
  199. package/src/styles/components/divider.css +20 -20
  200. package/src/styles/components/dose-calculator.css +3 -3
  201. package/src/styles/components/empty-state.css +20 -17
  202. package/src/styles/components/experiment-code-badge.css +2 -0
  203. package/src/styles/components/experiment-popover.css +174 -329
  204. package/src/styles/components/experiment-selector-modal.css +75 -56
  205. package/src/styles/components/file-picker.css +3 -3
  206. package/src/styles/components/file-uploader.css +38 -49
  207. package/src/styles/components/form-builder.css +3 -3
  208. package/src/styles/components/formula-input.css +1 -1
  209. package/src/styles/components/icon-button.css +18 -24
  210. package/src/styles/components/input.css +5 -4
  211. package/src/styles/components/instrument-monitor.css +13 -15
  212. package/src/styles/components/jobs-status-tray.css +31 -25
  213. package/src/styles/components/modal.css +4 -6
  214. package/src/styles/components/molecule-input.css +2 -2
  215. package/src/styles/components/multi-select.css +65 -117
  216. package/src/styles/components/number-input.css +26 -19
  217. package/src/styles/components/pill.css +3 -2
  218. package/src/styles/components/plate-editor.css +0 -1
  219. package/src/styles/components/plugin-icon.css +26 -23
  220. package/src/styles/components/popover.css +2 -2
  221. package/src/styles/components/presence-avatars.css +1 -1
  222. package/src/styles/components/progress-bar.css +3 -3
  223. package/src/styles/components/protocol-step-editor.css +1 -3
  224. package/src/styles/components/resource-card.css +2 -2
  225. package/src/styles/components/resource-picker-list.css +6 -57
  226. package/src/styles/components/sample-hierarchy-tree.css +4 -4
  227. package/src/styles/components/sample-selector.css +95 -79
  228. package/src/styles/components/schedule-calendar.css +8 -8
  229. package/src/styles/components/searchable-select.css +12 -12
  230. package/src/styles/components/section-card.css +30 -47
  231. package/src/styles/components/segmented-control.css +18 -15
  232. package/src/styles/components/select.css +3 -3
  233. package/src/styles/components/sequence-input.css +2 -2
  234. package/src/styles/components/sequence-progress-bar.css +17 -8
  235. package/src/styles/components/settings-modal.css +7 -2
  236. package/src/styles/components/smart-group.css +18 -18
  237. package/src/styles/components/step-wizard.css +52 -16
  238. package/src/styles/components/tabs.css +2 -2
  239. package/src/styles/components/textarea.css +3 -3
  240. package/src/styles/components/time-picker.css +36 -21
  241. package/src/styles/components/toast.css +12 -10
  242. package/src/styles/components/toggle.css +1 -11
  243. package/src/styles/components/tooltip.css +10 -9
  244. package/src/styles/components/unit-input.css +8 -7
  245. package/src/styles/components/well-plate.css +9 -2
  246. package/src/styles/variables.css +118 -77
  247. package/src/types/componentWorkflowTypes.ts +2 -0
  248. package/src/types/generated/platformContext.ts +1 -0
  249. package/src/types/generated-ui.ts +4 -0
  250. package/src/utils/time.ts +21 -0
  251. package/dist/ActionMenu-CScINahU.js.map +0 -1
  252. package/dist/AppSidebar-BmiiPfz9.js.map +0 -1
  253. package/dist/BaseModal-DfRIuVoj.js.map +0 -1
  254. package/dist/ExperimentPopover-C4sGw-is.js +0 -359
  255. package/dist/ExperimentPopover-C4sGw-is.js.map +0 -1
  256. package/dist/ExperimentSelectorModal-D-9z7cNx.js.map +0 -1
  257. package/dist/SettingsModal-CSLE31yg.js.map +0 -1
  258. package/dist/data-C2PwI_dn.js.map +0 -1
  259. package/dist/lab-DLwRl_Ql.js.map +0 -1
  260. package/dist/records-BzTcxekG.js.map +0 -1
  261. package/dist/runtime-D6gVat6r.js.map +0 -1
  262. package/dist/useListReorder-CcbEYxX-.js.map +0 -1
  263. package/dist/useRackEditor-BD5NIo5s.js.map +0 -1
  264. package/dist/useRealtime-PalKue84.js.map +0 -1
  265. package/dist/useRequest-D5JDfsgZ.js +0 -76
  266. package/dist/useRequest-D5JDfsgZ.js.map +0 -1
@@ -26,9 +26,9 @@ export interface UseCalendarGridReturn {
26
26
  nextMonth: () => void
27
27
  }
28
28
 
29
- const WEEK_DAYS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] as const
29
+ const WEEK_DAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'] as const
30
30
 
31
- /** Shared calendar grid state for date and datetime pickers. */
31
+ /** Shared Monday-first calendar grid state for date and datetime pickers. */
32
32
  export function useCalendarGrid(options: UseCalendarGridOptions = {}): UseCalendarGridReturn {
33
33
  const currentMonth = ref(new Date())
34
34
 
@@ -46,7 +46,8 @@ export function useCalendarGrid(options: UseCalendarGridOptions = {}): UseCalend
46
46
  const lastDay = new Date(year, month + 1, 0)
47
47
  const days: CalendarGridDay[] = []
48
48
 
49
- const startPadding = firstDay.getDay()
49
+ // Weeks start on Monday.
50
+ const startPadding = (firstDay.getDay() + 6) % 7
50
51
  for (let i = startPadding - 1; i >= 0; i--) {
51
52
  const date = new Date(year, month, -i)
52
53
  days.push({ date, isCurrentMonth: false, isDisabled: isDateDisabled(date) })
@@ -28,6 +28,8 @@ export interface UseExperimentSelectorOptions {
28
28
  apiBaseUrl?: string
29
29
  limit?: number
30
30
  immediate?: boolean
31
+ /** Keep `statusCounts` in step with the non-status filters (one stats request per change). */
32
+ withStatusCounts?: boolean
31
33
  }
32
34
 
33
35
  export interface UseExperimentSelectorReturn {
@@ -57,9 +59,8 @@ export interface UseExperimentSelectorReturn {
57
59
  experimentTypes: Ref<ExperimentTypeOption[]>
58
60
  /** Available project options for filter UI. */
59
61
  projects: Ref<SelectOption<string>[]>
60
- // Grouped view
61
- /** Fetched experiments grouped by project name. */
62
- groupedByProject: ComputedRef<[string, ExperimentSummary[]][]>
62
+ /** Experiment count per status under every filter except status itself. */
63
+ statusCounts: Ref<Record<string, number>>
63
64
  // Methods
64
65
  /** Fetch the current page with the current filters. */
65
66
  fetch: () => Promise<void>
@@ -73,6 +74,8 @@ export interface UseExperimentSelectorReturn {
73
74
  clear: () => void
74
75
  /** Fetch experiment type and project filter options. */
75
76
  fetchFilterOptions: () => Promise<void>
77
+ /** Fetch `statusCounts` from `GET /experiments/stats`. */
78
+ fetchStatusCounts: () => Promise<void>
76
79
  }
77
80
 
78
81
  /** Fetches a paginated, filtered experiment list from the platform API for picker and selector UIs. */
@@ -85,6 +88,7 @@ export function useExperimentSelector(
85
88
  experimentType,
86
89
  allowedExperimentTypes: optionAllowedExperimentTypes,
87
90
  apiBaseUrl,
91
+ withStatusCounts = false,
88
92
  } = options
89
93
  const platformBase = apiBaseUrl ?? getPlatformApiUrl()
90
94
  const api = useApi()
@@ -107,6 +111,10 @@ export function useExperimentSelector(
107
111
  const projects = ref<SelectOption<string>[]>([])
108
112
  let filterOptionsFetched = false
109
113
 
114
+ const statusCounts = ref<Record<string, number>>({})
115
+ let statsGeneration = 0
116
+ let countedScope: string | null = null
117
+
110
118
  const hasMore = computed(() => experiments.value.length < total.value)
111
119
 
112
120
  const filters: ExperimentFilters = reactive({
@@ -115,6 +123,7 @@ export function useExperimentSelector(
115
123
  project: undefined,
116
124
  experimentType: undefined,
117
125
  datePreset: undefined,
126
+ mine: undefined,
118
127
  })
119
128
 
120
129
  function normalizeExperimentTypes(values: string[] | null | undefined): string[] | null {
@@ -163,21 +172,27 @@ export function useExperimentSelector(
163
172
  return { kind: 'all' }
164
173
  }
165
174
 
166
- function buildExperimentListUrl(typeFilter: string | undefined, skip: number, pageLimit: number): string {
175
+ /** Query params shared by the list and the stats route; status, sort and paging are list-only. */
176
+ function scopeParams(typeFilter: string | undefined): URLSearchParams {
167
177
  const params = new URLSearchParams()
168
178
  if (typeFilter) params.set('experiment_type', typeFilter)
169
- if (filters.status) params.set('status', filters.status)
170
179
  if (filters.search) params.set('search', filters.search)
171
180
  if (filters.project) params.set('project', filters.project)
181
+ if (filters.mine) params.set('mine', 'true')
182
+ if (filters.datePreset) {
183
+ params.set('created_after', datePresetToISO(filters.datePreset))
184
+ }
185
+ return params
186
+ }
187
+
188
+ function buildExperimentListUrl(typeFilter: string | undefined, skip: number, pageLimit: number): string {
189
+ const params = scopeParams(typeFilter)
190
+ if (filters.status) params.set('status', filters.status)
172
191
 
173
192
  const { sortBy, sortOrder } = parseSortKey()
174
193
  params.set('sort_by', sortBy)
175
194
  params.set('sort_order', sortOrder)
176
195
 
177
- if (filters.datePreset) {
178
- params.set('created_after', datePresetToISO(filters.datePreset))
179
- }
180
-
181
196
  params.set('limit', String(pageLimit))
182
197
  params.set('skip', String(skip))
183
198
 
@@ -298,6 +313,51 @@ export function useExperimentSelector(
298
313
  }
299
314
  }
300
315
 
316
+ /** The filters the stats route reads; status, sort and paging do not change the counts. */
317
+ function countScope(): string {
318
+ return JSON.stringify([filters.search, filters.project, filters.experimentType, filters.datePreset, filters.mine])
319
+ }
320
+
321
+ function refreshStatusCountsOnScopeChange(): void {
322
+ if (withStatusCounts && countScope() !== countedScope) fetchStatusCounts()
323
+ }
324
+
325
+ async function fetchStatusCounts(): Promise<void> {
326
+ const generation = ++statsGeneration
327
+ countedScope = countScope()
328
+ const scope = resolveExperimentTypeScope()
329
+ if (scope.kind === 'blocked') {
330
+ statusCounts.value = {}
331
+ return
332
+ }
333
+ const types = scope.kind === 'multi'
334
+ ? scope.experimentTypes
335
+ : [scope.kind === 'single' ? scope.experimentType : undefined]
336
+ const base = platformBase ?? ''
337
+ try {
338
+ const responses = await Promise.all(
339
+ types.map(type => api.get<{ total: number; by_status: Record<string, number> }>(
340
+ `${base}/experiments/stats?${scopeParams(type)}`,
341
+ )),
342
+ )
343
+ if (generation !== statsGeneration) return
344
+ const counts: Record<string, number> = {}
345
+ for (const response of responses) {
346
+ for (const [status, count] of Object.entries(response.by_status)) {
347
+ counts[status] = (counts[status] ?? 0) + count
348
+ }
349
+ }
350
+ statusCounts.value = counts
351
+ } catch {
352
+ // Counts only decorate the status menu; without them it shows bare labels.
353
+ // Forget the scope so the next status or sort change retries the request.
354
+ if (generation === statsGeneration) {
355
+ statusCounts.value = {}
356
+ countedScope = null
357
+ }
358
+ }
359
+ }
360
+
301
361
  async function loadMore(): Promise<void> {
302
362
  if (!hasMore.value || isLoading.value) return
303
363
  page.value++
@@ -323,31 +383,12 @@ export function useExperimentSelector(
323
383
  filters.project = undefined
324
384
  filters.experimentType = undefined
325
385
  filters.datePreset = undefined
386
+ filters.mine = undefined
326
387
  sortKey.value = 'created_at:desc'
327
388
  page.value = 0
328
389
  request.clearError()
329
390
  }
330
391
 
331
- // Group experiments by project (client-side)
332
- const groupedByProject = computed<[string, ExperimentSummary[]][]>(() => {
333
- const groups = new Map<string, ExperimentSummary[]>()
334
- for (const exp of experiments.value) {
335
- const key = exp.project_name ?? exp.project ?? 'No project'
336
- const list = groups.get(key)
337
- if (list) {
338
- list.push(exp)
339
- } else {
340
- groups.set(key, [exp])
341
- }
342
- }
343
- // Sort alphabetically, "No project" last
344
- return [...groups.entries()].sort(([a], [b]) => {
345
- if (a === 'No project') return 1
346
- if (b === 'No project') return -1
347
- return a.localeCompare(b)
348
- })
349
- })
350
-
351
392
  watch(
352
393
  () => [
353
394
  filters.search,
@@ -355,6 +396,7 @@ export function useExperimentSelector(
355
396
  filters.project,
356
397
  filters.experimentType,
357
398
  filters.datePreset,
399
+ filters.mine,
358
400
  sortKey.value,
359
401
  ],
360
402
  () => invalidateFetch(),
@@ -366,17 +408,20 @@ export function useExperimentSelector(
366
408
  () => {
367
409
  page.value = 0
368
410
  fetchExperiments()
411
+ refreshStatusCountsOnScopeChange()
369
412
  },
370
413
  { delay: 300 },
371
414
  )
372
415
 
373
416
  // Immediate watch on discrete filters and sort (cancel any pending search debounce)
374
417
  watch(
375
- () => [filters.status, filters.project, filters.experimentType, filters.datePreset, sortKey.value],
418
+ () => [filters.status, filters.project, filters.experimentType, filters.datePreset, filters.mine, sortKey.value],
376
419
  () => {
377
420
  searchWatch.cancel()
378
421
  page.value = 0
379
422
  fetchExperiments()
423
+ // Also checked on a status or sort change: the cancelled debounce may hold a new search.
424
+ refreshStatusCountsOnScopeChange()
380
425
  },
381
426
  )
382
427
 
@@ -397,12 +442,13 @@ export function useExperimentSelector(
397
442
  sortKey,
398
443
  experimentTypes,
399
444
  projects,
400
- groupedByProject,
445
+ statusCounts,
401
446
  fetch: fetchExperiments,
402
447
  loadMore,
403
448
  reset,
404
449
  select,
405
450
  clear,
406
451
  fetchFilterOptions,
452
+ fetchStatusCounts,
407
453
  }
408
454
  }
@@ -120,7 +120,7 @@ export function usePluginWorkspace(options: UsePluginWorkspaceOptions): UsePlugi
120
120
  sidebarToggleLabel: sidebar.toggleLabel ?? 'Open workspace sidebar',
121
121
  sidebarCloseLabel: sidebar.closeLabel ?? 'Close workspace sidebar',
122
122
  hasSidebarSurface: hasSidebarSurface.value,
123
- title: props.spec?.title ?? 'Workspace',
123
+ title: props.spec?.title,
124
124
  subtitle: props.spec?.subtitle,
125
125
  topBar: topBarSpec.value === false
126
126
  ? false
@@ -6,14 +6,14 @@
6
6
  overflow: hidden;
7
7
  color: var(--text-muted);
8
8
  font-variant-numeric: tabular-nums;
9
- font-weight: 600;
9
+ font-weight: 400;
10
10
  letter-spacing: 0;
11
11
  text-overflow: ellipsis;
12
12
  white-space: nowrap;
13
13
  }
14
14
 
15
15
  .mint-adduct-text--mono { font-family: var(--font-mono); font-feature-settings: 'liga' 0, 'calt' 0; }
16
- .mint-adduct-text--sans { font-family: var(--font-sans); font-feature-settings: 'liga' 1, 'calt' 1; font-weight: 650; }
16
+ .mint-adduct-text--sans { font-family: var(--font-sans); font-feature-settings: 'liga' 1, 'calt' 1; }
17
17
 
18
18
  .mint-adduct-text--xs {
19
19
  font-size: 0.6875rem;
@@ -26,9 +26,16 @@
26
26
  }
27
27
 
28
28
  .mint-adduct-text--badge {
29
- padding: 0.125rem 0.25rem;
29
+ height: 1.25rem;
30
+ padding: 0 0.375rem;
30
31
  border-radius: var(--radius-sm);
31
32
  background: var(--bg-tertiary);
33
+ color: var(--text-secondary-strong);
34
+ }
35
+
36
+ .mint-adduct-text--badge.mint-adduct-text--xs {
37
+ font-size: 0.75rem;
38
+ line-height: 1.25rem;
32
39
  }
33
40
 
34
41
  /* On a filled surface the text inherits that surface's ink (9c: the old
@@ -44,8 +51,9 @@
44
51
  /* Charge as a superscript by relative offset, so the line box keeps its height. */
45
52
  .mint-adduct-text__charge {
46
53
  position: relative;
47
- top: -0.5em;
48
- font-size: 0.72em;
54
+ top: -0.45em;
55
+ margin-left: 1px;
56
+ font-size: 0.78em;
49
57
  line-height: 0;
50
58
  vertical-align: baseline;
51
59
  color: inherit;
@@ -1,84 +1,58 @@
1
- /* AlertBox Component Styles */
1
+ /* AlertBox Component Styles — neutral card; tone lives only in the 22px icon tile (v2 callout). */
2
2
 
3
3
  .mint-alert {
4
4
  display: flex;
5
5
  align-items: flex-start;
6
6
  gap: 0.625rem;
7
- padding: 0.75rem 0.875rem;
8
- border: 1px solid;
7
+ padding: 0.625rem 0.75rem;
8
+ background-color: var(--bg-card);
9
+ border: 1px solid var(--border-color);
9
10
  border-radius: var(--radius-md);
10
11
  }
11
12
 
12
- .mint-alert--success {
13
- background-color: var(--mint-success-bg);
14
- border-color: var(--mint-success-border);
15
- }
16
-
17
- .mint-alert--error {
18
- background-color: var(--mint-error-bg);
19
- border-color: var(--mint-error-border);
20
- }
21
-
22
- .mint-alert--warning {
23
- background-color: var(--mint-warning-bg);
24
- border-color: var(--mint-warning-border);
25
- }
26
-
27
- .mint-alert--info {
28
- background-color: var(--mint-info-bg);
29
- border-color: var(--mint-info-border);
30
- }
31
-
32
- .mint-alert--neutral {
33
- background-color: var(--bg-tertiary);
34
- border-color: var(--border-color);
35
- }
36
-
37
- .mint-alert--primary {
38
- background-color: color-mix(in srgb, var(--color-primary) 10%, transparent);
39
- border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
40
- }
41
-
42
- .mint-alert--cta {
43
- background-color: color-mix(in srgb, var(--color-cta) 10%, transparent);
44
- border-color: color-mix(in srgb, var(--color-cta) 30%, transparent);
45
- }
46
-
47
- /* Semantic color stays in the surface and the icon only (1h, same rule as Toast). */
48
13
  .mint-alert__icon {
49
- width: 1rem;
50
- height: 1rem;
51
- margin-top: 0.125rem; /* centers on the first 13px/1.5 text line */
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ width: 1.375rem;
18
+ height: 1.375rem;
52
19
  flex-shrink: 0;
20
+ border-radius: var(--radius);
53
21
  }
54
22
 
55
- .mint-alert__icon--success { color: var(--mint-success-hover); }
56
- .mint-alert__icon--error { color: var(--_ink-error); }
57
- .mint-alert__icon--warning { color: var(--mint-warning-text); }
58
- .mint-alert__icon--info { color: var(--mint-info); }
59
- .mint-alert__icon--neutral { color: var(--text-secondary); }
60
- .mint-alert__icon--primary { color: var(--color-primary); }
61
- .mint-alert__icon--cta { color: var(--color-cta-hover); }
23
+ .mint-alert__icon svg {
24
+ width: 0.875rem;
25
+ height: 0.875rem;
26
+ }
27
+
28
+ .mint-alert__icon--success { background-color: var(--mint-success-bg); color: var(--mint-success-hover); }
29
+ .mint-alert__icon--error { background-color: var(--mint-error-bg); color: var(--_ink-error); }
30
+ .mint-alert__icon--warning { background-color: var(--mint-warning-bg); color: var(--mint-warning-text); }
31
+ .mint-alert__icon--info { background-color: var(--mint-info-bg); color: var(--mint-info); }
32
+ .mint-alert__icon--neutral { background-color: var(--bg-tertiary); color: var(--text-secondary); }
33
+ .mint-alert__icon--primary { background-color: var(--color-primary-soft); color: var(--color-primary); }
34
+ .mint-alert__icon--cta { background-color: var(--color-primary-soft); color: var(--color-cta-hover); }
62
35
 
63
36
  .mint-alert__content {
64
37
  flex: 1;
65
38
  min-width: 0;
39
+ padding-top: 0.125rem;
66
40
  }
67
41
 
68
42
  .mint-alert__title {
69
43
  font-size: 0.8125rem;
70
44
  font-weight: 600;
71
45
  color: var(--text-primary);
72
- line-height: 1.5;
46
+ line-height: 1.35;
73
47
  margin-top: 0 !important;
74
- margin-bottom: 0.125rem !important;
48
+ margin-bottom: 0.0625rem !important;
75
49
  padding: 0 !important;
76
50
  }
77
51
 
78
52
  .mint-alert__message {
79
53
  font-size: 0.8125rem;
80
- line-height: 1.5;
81
- color: var(--text-secondary-strong);
54
+ line-height: 1.45;
55
+ color: var(--text-secondary);
82
56
  margin-top: 0 !important;
83
57
  margin-bottom: 0 !important;
84
58
  padding: 0 !important;
@@ -86,24 +60,31 @@
86
60
 
87
61
  .mint-alert__actions {
88
62
  display: flex;
63
+ align-self: center;
89
64
  align-items: center;
90
- gap: 0.5rem;
65
+ gap: 0.375rem;
91
66
  flex-shrink: 0;
92
67
  }
93
68
 
94
69
  .mint-alert__dismiss {
95
- padding: 0.25rem;
96
- border-radius: var(--radius-sm);
70
+ display: flex;
71
+ align-items: center;
72
+ justify-content: center;
73
+ width: 1.375rem;
74
+ height: 1.375rem;
75
+ flex-shrink: 0;
76
+ padding: 0;
77
+ border: 0;
78
+ border-radius: var(--radius);
97
79
  color: var(--text-muted);
98
80
  background: transparent;
99
- border: none;
100
81
  cursor: pointer;
101
82
  transition: color 0.15s ease, background-color 0.15s ease;
102
83
  }
103
84
 
104
85
  .mint-alert__dismiss:hover {
105
86
  color: var(--text-primary);
106
- background-color: var(--bg-tertiary);
87
+ background-color: var(--bg-hover);
107
88
  }
108
89
 
109
90
  .mint-alert__dismiss:focus-visible {
@@ -59,11 +59,13 @@
59
59
  border-bottom: 1px solid var(--border-color);
60
60
  }
61
61
 
62
- /* Floating variant: all sections appear as separate cards on a background */
62
+ /* Floating variant: all sections appear as separate cards on a background,
63
+ 16px from the window edge and spaced by the density gutter (8px, 12px
64
+ comfortable). */
63
65
  .mint-layout--floating {
64
66
  background-color: var(--bg-primary);
65
- padding: 1rem;
66
- gap: 1rem;
67
+ padding: var(--mint-page-inset);
68
+ gap: var(--mint-gutter);
67
69
  }
68
70
 
69
71
  .mint-layout--floating .mint-layout__topbar {
@@ -73,7 +75,7 @@
73
75
  }
74
76
 
75
77
  .mint-layout--floating .mint-layout__body {
76
- gap: 1rem;
78
+ gap: var(--mint-gutter);
77
79
  }
78
80
 
79
81
  .mint-layout--floating .mint-layout__sidebar {
@@ -85,7 +87,7 @@
85
87
  }
86
88
 
87
89
  .mint-layout--floating .mint-layout__main {
88
- gap: 1rem;
90
+ gap: var(--mint-gutter);
89
91
  }
90
92
 
91
93
  /* In floating mode, inner components don't need borders since the wrapper provides them */
@@ -3,9 +3,10 @@
3
3
  only, open = field focus, inline 12px chevron. The menu keeps the 1.2 panel
4
4
  (own card, tinted active row) rather than .mint-popover + .mint-menu-item. */
5
5
 
6
+ /* Shrinks down to its inline min-width (11.25rem) so a crowded bar truncates the label instead of overlapping the nav. */
6
7
  .mint-page-selector {
7
8
  position: relative;
8
- flex-shrink: 0;
9
+ flex-shrink: 1;
9
10
  }
10
11
 
11
12
  .mint-page-selector__trigger {
@@ -76,6 +77,7 @@
76
77
 
77
78
  .mint-page-selector__label {
78
79
  flex: 1;
80
+ min-width: 0;
79
81
  text-align: left;
80
82
  white-space: nowrap;
81
83
  overflow: hidden;
@@ -1,14 +1,14 @@
1
- /* AppTopBar pillNav — centered top-level navigation pills.
2
- Keeps the pill shape (the only pill in the bar, 5a); items are 26px to fit
3
- the 52px bar. Dropdown = .mint-popover + .mint-menu-item (2d). */
1
+ /* AppTopBar pillNav — centred top-level navigation as a segmented track:
2
+ 26px items + 2px track padding + 1px border = 32px.
3
+ Dropdown = .mint-popover + .mint-menu-item (2d). */
4
4
 
5
5
  .mint-pill-nav {
6
6
  display: inline-flex;
7
- gap: 2px;
8
- padding: 0.1875rem;
9
- border-radius: 9999px;
7
+ gap: 0;
8
+ padding: 2px;
9
+ border-radius: var(--radius-lg);
10
10
  background: var(--bg-tertiary);
11
- border: 1px solid var(--border-light);
11
+ border: 1px solid transparent;
12
12
  flex-shrink: 0;
13
13
  }
14
14
 
@@ -21,10 +21,10 @@
21
21
  display: inline-flex;
22
22
  align-items: center;
23
23
  gap: 0.375rem;
24
- height: 1.75rem;
24
+ height: 1.625rem;
25
25
  border: 0;
26
- padding: 0 0.875rem;
27
- border-radius: 9999px;
26
+ padding: 0 0.75rem;
27
+ border-radius: var(--radius-md);
28
28
  font-size: 0.8125rem;
29
29
  font-weight: 500;
30
30
  font-family: inherit;
@@ -52,8 +52,7 @@
52
52
  .mint-pill-nav__item--active {
53
53
  color: var(--text-primary);
54
54
  background: var(--bg-card);
55
- box-shadow: var(--shadow-sm);
56
- font-weight: 600;
55
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06), 0 0 0 1px var(--border-color);
57
56
  }
58
57
 
59
58
  .mint-pill-nav__item--disabled {
@@ -96,7 +96,7 @@
96
96
  flex-shrink: 0;
97
97
  min-width: 1.375rem;
98
98
  padding: 0.125rem 0.375rem;
99
- border-radius: 9999px;
99
+ border-radius: var(--radius);
100
100
  background: color-mix(in srgb, var(--color-cta) 14%, transparent);
101
101
  color: var(--color-cta);
102
102
  font-size: 0.6875rem;
@@ -277,7 +277,7 @@
277
277
  min-width: 1.125rem;
278
278
  height: 1rem;
279
279
  padding: 0 0.25rem;
280
- border-radius: 0.1875rem;
280
+ border-radius: var(--radius-sm);
281
281
  background: color-mix(in srgb, var(--mint-sidebar-tone) 14%, transparent);
282
282
  color: var(--mint-sidebar-tone);
283
283
  font-family: var(--font-mono);
@@ -428,14 +428,11 @@
428
428
  color: var(--text-secondary);
429
429
  background: transparent;
430
430
  cursor: pointer;
431
- transition:
432
- background-color 150ms ease,
433
- transform 150ms ease;
431
+ transition: background-color var(--mint-transition);
434
432
  }
435
433
 
436
434
  .mint-sidebar__rail-item:hover {
437
435
  background: var(--bg-tertiary);
438
- transform: translateY(-1px);
439
436
  }
440
437
 
441
438
  .mint-sidebar__rail-item:focus-visible {
@@ -450,11 +447,6 @@
450
447
  width: 1.75rem;
451
448
  height: 1.75rem;
452
449
  border-radius: var(--radius-md);
453
- transition: transform 150ms ease;
454
- }
455
-
456
- .mint-sidebar__rail-item:hover .mint-sidebar__rail-icon {
457
- transform: scale(1.04);
458
450
  }
459
451
 
460
452
  .mint-sidebar__rail-svg {
@@ -475,7 +467,7 @@
475
467
  min-width: 1rem;
476
468
  height: 1rem;
477
469
  padding: 0 0.25rem;
478
- border-radius: 9999px;
470
+ border-radius: var(--radius);
479
471
  background: var(--mint-sidebar-tone);
480
472
  color: white;
481
473
  font-size: 0.6875rem;
@@ -485,6 +477,11 @@
485
477
  font-variant-numeric: tabular-nums;
486
478
  }
487
479
 
480
+ .mint-sidebar__rail-badge--primary,
481
+ .mint-sidebar__rail-badge--cta {
482
+ color: var(--text-on-accent);
483
+ }
484
+
488
485
  /* Semantic tones shared by the rail badge, the inline tag and section actions. */
489
486
  .mint-sidebar__rail-badge--primary,
490
487
  .mint-sidebar__tag--primary,
@@ -539,15 +536,9 @@
539
536
  @media (prefers-reduced-motion: reduce) {
540
537
  .mint-sidebar,
541
538
  .mint-sidebar__rail-item,
542
- .mint-sidebar__rail-icon,
543
539
  .mint-sidebar__chevron,
544
540
  .mint-sidebar__switch,
545
541
  .mint-sidebar__switch-knob {
546
542
  transition: none;
547
543
  }
548
-
549
- .mint-sidebar__rail-item:hover,
550
- .mint-sidebar__rail-item:hover .mint-sidebar__rail-icon {
551
- transform: none;
552
- }
553
544
  }