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

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 (84) hide show
  1. package/dist/{AlertBox-B2Bgr3ei.js → AlertBox-BdoZqANv.js} +2 -2
  2. package/dist/{AlertBox-B2Bgr3ei.js.map → AlertBox-BdoZqANv.js.map} +1 -1
  3. package/dist/{AppSidebar-mvm--GxN.js → AppSidebar-CIXJBzDv.js} +237 -70
  4. package/dist/{AppSidebar-mvm--GxN.js.map → AppSidebar-CIXJBzDv.js.map} +1 -1
  5. package/dist/{ExperimentPopover-5T1fAq2L.js → ExperimentPopover-CbD7BR_s.js} +2 -2
  6. package/dist/{ExperimentPopover-5T1fAq2L.js.map → ExperimentPopover-CbD7BR_s.js.map} +1 -1
  7. package/dist/{ExperimentSelectorModal-65peG4Up.js → ExperimentSelectorModal-BbFtr1Fr.js} +112 -96
  8. package/dist/ExperimentSelectorModal-BbFtr1Fr.js.map +1 -0
  9. package/dist/{SegmentedControl-BtnnjWzs.js → SegmentedControl-BkwYOKpG.js} +2 -2
  10. package/dist/{SegmentedControl-BtnnjWzs.js.map → SegmentedControl-BkwYOKpG.js.map} +1 -1
  11. package/dist/{SettingsModal-DO3IZ7qz.js → SettingsModal-CdhFMi8j.js} +25 -24
  12. package/dist/SettingsModal-CdhFMi8j.js.map +1 -0
  13. package/dist/__stories__/experiment-helpers.d.ts +10 -0
  14. package/dist/__tests__/styles/paletteTokens.test.d.ts +1 -0
  15. package/dist/components/AppTopBar.vue.d.ts +13 -5
  16. package/dist/components/ExperimentSelectorModal.vue.d.ts +15 -1
  17. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +1 -1
  18. package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -1
  19. package/dist/{controlSchemaModel-B8CTXagP.js → controlSchemaModel-BF5g4wRC.js} +2 -2
  20. package/dist/{controlSchemaModel-B8CTXagP.js.map → controlSchemaModel-BF5g4wRC.js.map} +1 -1
  21. package/dist/{data-CgILxDLW.js → data-DUt0TIp5.js} +10 -10
  22. package/dist/{data-CgILxDLW.js.map → data-DUt0TIp5.js.map} +1 -1
  23. package/dist/data.js +7 -7
  24. package/dist/{experiment-43OJtH7R.js → experiment-DyO54FQB.js} +25 -61
  25. package/dist/experiment-DyO54FQB.js.map +1 -0
  26. package/dist/index.js +18 -18
  27. package/dist/{install-mB8-IyvO.js → install-Czoe1rA8.js} +13 -13
  28. package/dist/{install-mB8-IyvO.js.map → install-Czoe1rA8.js.map} +1 -1
  29. package/dist/install.js +1 -1
  30. package/dist/{lab-D5bIX426.js → lab-B-pLwc5U.js} +10 -10
  31. package/dist/{lab-D5bIX426.js.map → lab-B-pLwc5U.js.map} +1 -1
  32. package/dist/lab.js +4 -4
  33. package/dist/{records-B6gpM_3e.js → records-DnK_1Q_S.js} +2 -2
  34. package/dist/{records-B6gpM_3e.js.map → records-DnK_1Q_S.js.map} +1 -1
  35. package/dist/{runtime-D7vKpfbF.js → runtime-fPb_FEZr.js} +9 -9
  36. package/dist/{runtime-D7vKpfbF.js.map → runtime-fPb_FEZr.js.map} +1 -1
  37. package/dist/runtime.js +7 -7
  38. package/dist/{shell-CywENwBT.js → shell-lDf_EY-x.js} +4 -4
  39. package/dist/{shell-CywENwBT.js.map → shell-lDf_EY-x.js.map} +1 -1
  40. package/dist/shell.js +4 -4
  41. package/dist/stores/index.d.ts +1 -1
  42. package/dist/stores/index.js +2 -2
  43. package/dist/stores/settings.d.ts +4 -12
  44. package/dist/styles.css +409 -60
  45. package/dist/types/index.d.ts +1 -1
  46. package/dist/types/jobs.d.ts +9 -0
  47. package/dist/types/platform.d.ts +1 -1
  48. package/dist/{useExperimentSamples-DHnxKFAF.js → useExperimentSamples-BGN1dmcR.js} +4 -4
  49. package/dist/{useExperimentSamples-DHnxKFAF.js.map → useExperimentSamples-BGN1dmcR.js.map} +1 -1
  50. package/dist/{useListReorder-BitdUhxn.js → useListReorder-DPynGrCj.js} +3 -3
  51. package/dist/{useListReorder-BitdUhxn.js.map → useListReorder-DPynGrCj.js.map} +1 -1
  52. package/dist/{useRackEditor-Q8xO-cCy.js → useRackEditor-B2FrQA0p.js} +7 -7
  53. package/dist/{useRackEditor-Q8xO-cCy.js.map → useRackEditor-B2FrQA0p.js.map} +1 -1
  54. package/dist/{useRealtime-C3iy9QkU.js → useRealtime-yWjTTnc9.js} +2 -2
  55. package/dist/{useRealtime-C3iy9QkU.js.map → useRealtime-yWjTTnc9.js.map} +1 -1
  56. package/dist/{useRequest-D506eHi8.js → useRequest-Dqw12k2s.js} +3 -3
  57. package/dist/{useRequest-D506eHi8.js.map → useRequest-Dqw12k2s.js.map} +1 -1
  58. package/package.json +1 -1
  59. package/src/__stories__/experiment-helpers.ts +3 -3
  60. package/src/__tests__/components/AppTopBar.test.ts +175 -1
  61. package/src/__tests__/components/AppTopBarPageSelector.test.ts +125 -9
  62. package/src/__tests__/components/ExperimentSelectorModal.test.ts +34 -1
  63. package/src/__tests__/components/SettingsModal.test.ts +9 -2
  64. package/src/__tests__/stores/settings.test.ts +21 -21
  65. package/src/__tests__/styles/menusShellRefresh.test.ts +8 -2
  66. package/src/__tests__/styles/paletteTokens.test.ts +44 -0
  67. package/src/components/AppTopBar.story.vue +59 -4
  68. package/src/components/AppTopBar.vue +107 -8
  69. package/src/components/ExperimentSelectorModal.story.vue +38 -0
  70. package/src/components/ExperimentSelectorModal.vue +20 -1
  71. package/src/components/SettingsModal.vue +7 -12
  72. package/src/components/internal/AppTopBarPageSelectorInternal.vue +140 -72
  73. package/src/stores/index.ts +0 -2
  74. package/src/stores/settings.ts +20 -50
  75. package/src/styles/components/app-page-selector.css +120 -45
  76. package/src/styles/components/app-top-bar.css +155 -0
  77. package/src/styles/components/experiment-selector-modal.css +13 -0
  78. package/src/styles/variables.css +121 -15
  79. package/src/types/index.ts +1 -0
  80. package/src/types/jobs.ts +10 -0
  81. package/src/types/platform.ts +1 -1
  82. package/dist/ExperimentSelectorModal-65peG4Up.js.map +0 -1
  83. package/dist/SettingsModal-DO3IZ7qz.js.map +0 -1
  84. package/dist/experiment-43OJtH7R.js.map +0 -1
@@ -55,39 +55,24 @@ const defaultSettings: SettingsState = {
55
55
  tableDensity: 'normal',
56
56
  }
57
57
 
58
- export const colorPalettes: Record<ColorPalette, { name: string; hues: [number, number] }> = {
59
- default: { name: 'Default (Cyan-Pink)', hues: [180, 320] },
60
- colorblind: { name: 'Colorblind-friendly', hues: [45, 260] },
61
- viridis: { name: 'Viridis', hues: [280, 80] },
62
- pastel: { name: 'Pastel', hues: [200, 340] },
63
- }
64
-
65
58
  /**
66
- * Custom properties a non-default palette overrides on `<html>`.
67
- *
68
- * The saturation/lightness pairs are lifted from the original indigo/orange
69
- * tokens in `styles/variables.css` so only the hue moves; the `default`
70
- * palette writes nothing at all and the stylesheet values stand.
59
+ * Accent palettes. `styles/variables.css` holds each palette's light and dark
60
+ * tokens under `[data-palette]`; the store only writes the attribute.
71
61
  */
72
- export const PALETTE_CSS_VARIABLES = [
73
- '--color-primary',
74
- '--color-primary-hover',
75
- '--color-primary-soft',
76
- '--color-cta',
77
- '--color-cta-hover',
78
- ] as const
79
-
80
- /** Build the `<html>` custom-property overrides for a palette. */
81
- export function paletteCssVariables(palette: ColorPalette): Record<string, string> {
82
- if (palette === 'default') return {}
83
- const [primaryHue, accentHue] = colorPalettes[palette].hues
84
- return {
85
- '--color-primary': `hsl(${primaryHue} 84% 67%)`,
86
- '--color-primary-hover': `hsl(${primaryHue} 75% 59%)`,
87
- '--color-primary-soft': `hsl(${primaryHue} 84% 67% / 0.12)`,
88
- '--color-cta': `hsl(${accentHue} 95% 53%)`,
89
- '--color-cta-hover': `hsl(${accentHue} 90% 48%)`,
90
- }
62
+ export const colorPalettes: Record<ColorPalette, { name: string }> = {
63
+ default: { name: 'Teal' },
64
+ colorblind: { name: 'Colour-blind safe' },
65
+ purple: { name: 'Purple' },
66
+ ocean: { name: 'Ocean' },
67
+ forest: { name: 'Forest' },
68
+ }
69
+
70
+ /** Pre-v2 palette ids and the palette that replaces each one. */
71
+ const RETIRED_PALETTES: Record<string, ColorPalette> = { viridis: 'purple', pastel: 'default' }
72
+
73
+ function knownPalette(value: unknown): ColorPalette {
74
+ if (typeof value === 'string' && value in colorPalettes) return value as ColorPalette
75
+ return (typeof value === 'string' && RETIRED_PALETTES[value]) || 'default'
91
76
  }
92
77
 
93
78
  /** `DataFrame`-style size that each table density maps onto. */
@@ -107,13 +92,13 @@ function loadSettings(): SettingsState {
107
92
  const stored = localStorage.getItem(STORAGE_KEY)
108
93
  if (stored) {
109
94
  const parsed = JSON.parse(stored)
110
- return { ...defaultSettings, ...parsed }
95
+ return { ...defaultSettings, ...parsed, colorPalette: knownPalette(parsed.colorPalette) }
111
96
  }
112
97
 
113
98
  const legacyStored = localStorage.getItem(LEGACY_STORAGE_KEY)
114
99
  if (legacyStored) {
115
100
  const parsed = JSON.parse(legacyStored)
116
- const migrated = { ...defaultSettings, ...parsed }
101
+ const migrated = { ...defaultSettings, ...parsed, colorPalette: knownPalette(parsed.colorPalette) }
117
102
  localStorage.setItem(STORAGE_KEY, JSON.stringify(migrated))
118
103
  localStorage.removeItem(LEGACY_STORAGE_KEY)
119
104
  return migrated
@@ -202,20 +187,10 @@ export const useSettingsStore = defineStore('mint-settings', () => {
202
187
  document.documentElement.classList.toggle('dark', dark)
203
188
  }
204
189
 
205
- /**
206
- * Write the selected palette's hues onto `<html>` as custom-property
207
- * overrides. The `default` palette clears them so `styles/variables.css`
208
- * keeps ownership of the brand colors.
209
- */
190
+ /** Mirror the palette onto `<html data-palette>`; `styles/variables.css` maps it to tokens. */
210
191
  function applyPalette() {
211
192
  if (typeof document === 'undefined') return
212
- const style = document.documentElement.style
213
- const overrides = paletteCssVariables(colorPalette.value)
214
- for (const name of PALETTE_CSS_VARIABLES) {
215
- const value = overrides[name]
216
- if (value) style.setProperty(name, value)
217
- else style.removeProperty(name)
218
- }
193
+ document.documentElement.dataset.palette = colorPalette.value
219
194
  }
220
195
 
221
196
  /** Mirror the table density onto `<html data-density>` for CSS consumers. */
@@ -269,10 +244,6 @@ export const useSettingsStore = defineStore('mint-settings', () => {
269
244
  tableDensity.value = defaultSettings.tableDensity
270
245
  }
271
246
 
272
- function getPaletteHues(): [number, number] {
273
- return colorPalettes[colorPalette.value].hues
274
- }
275
-
276
247
  /** The `size` the SDK table components default to for the current density. */
277
248
  function getTableSize(): 'sm' | 'md' | 'lg' {
278
249
  return tableDensityToSize(tableDensity.value)
@@ -298,7 +269,6 @@ export const useSettingsStore = defineStore('mint-settings', () => {
298
269
  applyDensity,
299
270
  persistSettings,
300
271
  resetToDefaults,
301
- getPaletteHues,
302
272
  getTableSize,
303
273
  isDark,
304
274
  getApiBaseUrl,
@@ -1,51 +1,76 @@
1
- /* AppTopBar page selector — left-side route/page dropdown.
2
- Trigger speaks the field language (5a): 36px (the identity tile height), hover changes the background
3
- only, open = field focus, inline 12px chevron. The menu keeps the 1.2 panel
4
- (own card, tinted active row) rather than .mint-popover + .mint-menu-item. */
1
+ /* AppTopBar page selector — design v2 option C: "<plugin> / <page>" as one switcher.
2
+ The crumb holds the plugin name, a slash and the tinted 32px trigger. The menu keeps its own
3
+ card (not .mint-popover): a "<plugin> pages" title, then one row per page with its hint and number key. */
5
4
 
6
- /* Shrinks down to its inline min-width (11.25rem) so a crowded bar truncates the label instead of overlapping the nav. */
5
+ /* Shrinks with the bar: the plugin name and the page label truncate instead of overlapping the nav. */
6
+ .mint-page-selector__crumb {
7
+ display: inline-flex;
8
+ align-items: center;
9
+ gap: 0.375rem;
10
+ min-width: 0;
11
+ flex-shrink: 1;
12
+ }
13
+
14
+ .mint-page-selector__plugin {
15
+ min-width: 0;
16
+ font-size: 0.875rem;
17
+ font-weight: 600;
18
+ color: var(--text-primary);
19
+ white-space: nowrap;
20
+ overflow: hidden;
21
+ text-overflow: ellipsis;
22
+ }
23
+
24
+ .mint-page-selector__slash {
25
+ flex-shrink: 0;
26
+ font-size: 1.125rem;
27
+ font-weight: 300;
28
+ line-height: 1;
29
+ color: var(--text-muted);
30
+ }
31
+
32
+ /* Anchors the menu under the trigger, not under the plugin name. */
7
33
  .mint-page-selector {
8
34
  position: relative;
9
- flex-shrink: 1;
35
+ display: flex;
36
+ min-width: 0;
10
37
  }
11
38
 
12
39
  .mint-page-selector__trigger {
13
40
  display: inline-flex;
14
41
  align-items: center;
15
- gap: 0.5rem;
16
- width: 100%;
17
- height: 2.25rem;
18
- padding: 0 0.5rem 0 0.4375rem;
19
- border: 1px solid var(--border-color);
20
- background: var(--bg-card);
42
+ gap: 0.375rem;
43
+ min-width: 0;
44
+ max-width: 100%;
45
+ height: 2rem;
46
+ padding: 0 0.5rem 0 0.375rem;
47
+ border: 0;
48
+ background: var(--color-primary-soft);
21
49
  color: var(--text-primary);
22
50
  border-radius: var(--radius-md);
23
51
  font-size: 0.8125rem;
24
- font-weight: 500;
52
+ font-weight: 600;
25
53
  font-family: inherit;
26
54
  cursor: pointer;
27
55
  transition:
28
56
  background-color var(--mint-transition),
29
- border-color var(--mint-transition),
30
57
  box-shadow var(--mint-transition);
31
58
  }
32
59
 
33
- .mint-page-selector__trigger:hover:not(.mint-page-selector__trigger--open) {
34
- background: var(--bg-hover);
60
+ .mint-page-selector__trigger:hover {
61
+ background: color-mix(in srgb, var(--color-primary) 16%, transparent);
35
62
  }
36
63
 
37
- .mint-page-selector__trigger:focus-visible,
38
- .mint-page-selector__trigger--open {
64
+ .mint-page-selector__trigger:focus-visible {
39
65
  outline: none;
40
- border-color: var(--color-primary);
41
- box-shadow: var(--focus-ring-soft);
66
+ box-shadow: var(--focus-ring-offset);
42
67
  }
43
68
 
44
69
  /* Page icon tile is neutral; the plugin carries the color. */
45
70
  .mint-page-selector__icon {
46
71
  width: 1.25rem;
47
72
  height: 1.25rem;
48
- border-radius: var(--radius-sm);
73
+ border-radius: var(--radius);
49
74
  background: var(--bg-tertiary);
50
75
  color: var(--text-secondary-strong);
51
76
  display: grid;
@@ -66,7 +91,7 @@
66
91
  .mint-page-selector__trigger-metadata-icon.mint-plugin-icon--sm {
67
92
  width: 1.25rem;
68
93
  height: 1.25rem;
69
- border-radius: var(--radius-sm);
94
+ border-radius: var(--radius);
70
95
  }
71
96
 
72
97
  .mint-page-selector__icon-fallback {
@@ -76,7 +101,6 @@
76
101
  }
77
102
 
78
103
  .mint-page-selector__label {
79
- flex: 1;
80
104
  min-width: 0;
81
105
  text-align: left;
82
106
  white-space: nowrap;
@@ -84,7 +108,6 @@
84
108
  text-overflow: ellipsis;
85
109
  }
86
110
 
87
- /* The chevron sits inline; the wrap no longer draws a tile. */
88
111
  .mint-page-selector__chevron-wrap {
89
112
  display: inline-flex;
90
113
  color: var(--text-secondary);
@@ -101,38 +124,46 @@
101
124
  transform: rotate(180deg);
102
125
  }
103
126
 
127
+ /* max-height: 8 two-line hint rows (8 x 68px) + title (~25px) + padding (8px) = ~577px; a 9th row scrolls. A short window caps it. */
104
128
  .mint-page-selector__menu {
105
129
  position: absolute;
130
+ box-sizing: border-box;
106
131
  top: calc(100% + 0.375rem);
107
132
  left: 0;
108
- width: 16.25rem;
133
+ width: 18.75rem;
134
+ max-height: min(36.25rem, calc(100vh - 6rem));
135
+ overflow-y: auto;
109
136
  background: var(--bg-card);
110
137
  border: 1px solid var(--border-color);
111
138
  border-radius: var(--radius-lg);
112
139
  box-shadow: var(--shadow-md);
113
- padding: 0.375rem;
140
+ padding: 0.25rem;
114
141
  z-index: 50;
115
142
  }
116
143
 
144
+ /* The mockup's 10px uppercase eyebrow breaks rules 2a (no uppercase) and 8d (UI text >= 11px): 11px, sentence case. */
117
145
  .mint-page-selector__menu-title {
118
- padding: 0.375rem 0.625rem 0.5rem;
119
- font-size: 0.75rem;
146
+ padding: 0.375rem 0.5rem 0.25rem;
147
+ font-family: var(--font-mono);
148
+ font-size: 0.6875rem;
120
149
  font-weight: 500;
121
- color: var(--text-secondary);
150
+ letter-spacing: 0.06em;
151
+ color: var(--text-muted);
122
152
  }
123
153
 
124
154
  .mint-page-selector__item {
125
155
  display: flex;
126
- align-items: center;
156
+ box-sizing: border-box;
157
+ align-items: flex-start;
127
158
  gap: 0.625rem;
128
159
  width: 100%;
129
- padding: 0.5rem 0.625rem;
160
+ padding: 0.5rem;
130
161
  border: 0;
131
162
  background: transparent;
132
- border-radius: 0.4375rem;
163
+ border-radius: var(--radius);
133
164
  color: var(--text-primary);
134
165
  font-size: 0.8125rem;
135
- font-weight: 500;
166
+ line-height: 1.4;
136
167
  font-family: inherit;
137
168
  text-align: left;
138
169
  text-decoration: none;
@@ -140,13 +171,22 @@
140
171
  transition: background-color var(--mint-transition);
141
172
  }
142
173
 
143
- .mint-page-selector__item:hover:not(.mint-page-selector__item--disabled) {
174
+ /* A row without a hint has one line: centre it on the icon tile. */
175
+ .mint-page-selector__item:not(:has(.mint-page-selector__item-hint)) {
176
+ align-items: center;
177
+ }
178
+
179
+ .mint-page-selector__item:hover:not(.mint-page-selector__item--disabled):not(.mint-page-selector__item--active) {
144
180
  background: var(--bg-hover);
145
181
  }
146
182
 
183
+ .mint-page-selector__item:focus-visible {
184
+ outline: 2px solid var(--color-primary);
185
+ outline-offset: -2px;
186
+ }
187
+
147
188
  .mint-page-selector__item--active {
148
- background: var(--bg-tertiary);
149
- font-weight: 600;
189
+ background: var(--color-primary-soft);
150
190
  }
151
191
 
152
192
  .mint-page-selector__item--disabled {
@@ -155,9 +195,9 @@
155
195
  }
156
196
 
157
197
  .mint-page-selector__item-icon {
158
- width: 1.375rem;
159
- height: 1.375rem;
160
- border-radius: 0.3125rem;
198
+ width: 1.75rem;
199
+ height: 1.75rem;
200
+ border-radius: var(--radius-md);
161
201
  background: var(--bg-tertiary);
162
202
  color: var(--text-secondary);
163
203
  display: grid;
@@ -165,6 +205,10 @@
165
205
  flex-shrink: 0;
166
206
  }
167
207
 
208
+ .mint-page-selector__item-icon .mint-page-selector__icon-fallback {
209
+ font-size: 0.8125rem;
210
+ }
211
+
168
212
  .mint-page-selector__item--active .mint-page-selector__item-icon {
169
213
  background: var(--color-primary-soft);
170
214
  color: var(--color-primary);
@@ -177,24 +221,55 @@
177
221
  }
178
222
 
179
223
  .mint-page-selector__item-icon svg {
180
- width: 0.8125rem;
181
- height: 0.8125rem;
224
+ width: 0.9375rem;
225
+ height: 0.9375rem;
182
226
  }
183
227
 
184
228
  .mint-page-selector__metadata-icon.mint-plugin-icon--sm {
185
- width: 1.375rem;
186
- height: 1.375rem;
187
- border-radius: 0.3125rem;
229
+ width: 1.75rem;
230
+ height: 1.75rem;
231
+ border-radius: var(--radius-md);
188
232
  }
189
233
 
190
- .mint-page-selector__item-label {
234
+ .mint-page-selector__item-text {
191
235
  flex: 1;
192
236
  min-width: 0;
237
+ display: grid;
238
+ gap: 1px;
239
+ }
240
+
241
+ .mint-page-selector__item-label {
242
+ font-weight: 600;
193
243
  overflow: hidden;
194
244
  text-overflow: ellipsis;
195
245
  white-space: nowrap;
196
246
  }
197
247
 
248
+ /* --_ink-primary is --color-primary-hover in light mode and stays legible in dark mode. */
249
+ .mint-page-selector__item--active .mint-page-selector__item-label {
250
+ color: var(--_ink-primary);
251
+ }
252
+
253
+ .mint-page-selector__item-hint {
254
+ font-size: 0.75rem;
255
+ line-height: 1.35;
256
+ color: var(--text-secondary);
257
+ }
258
+
259
+ /* 11px, the rule 8d floor (the mockup has 10.5px). */
260
+ .mint-page-selector__item-key {
261
+ flex-shrink: 0;
262
+ margin-top: 0.125rem;
263
+ padding: 0 0.25rem;
264
+ border: 1px solid var(--border-color);
265
+ border-radius: var(--radius);
266
+ font-family: var(--font-mono);
267
+ font-size: 0.6875rem;
268
+ font-weight: 500;
269
+ line-height: 1rem;
270
+ color: var(--text-muted);
271
+ }
272
+
198
273
  @media (prefers-reduced-motion: reduce) {
199
274
  .mint-page-selector__trigger,
200
275
  .mint-page-selector__chevron {
@@ -231,3 +231,158 @@
231
231
  white-space: nowrap;
232
232
  user-select: none;
233
233
  }
234
+
235
+ /* ─── Run slot: [ experiment | job ] ───
236
+ While a job shows, one border wraps the experiment chip and the job
237
+ segment, as the experiment popover does for Save. The wrapper does not
238
+ clip: the popover panel and the focus rings reach outside it, so each
239
+ segment rounds its own outer corners. The idle Jobs button stands apart
240
+ with its own border. */
241
+ .mint-topbar__run-slot {
242
+ display: inline-flex;
243
+ align-items: center;
244
+ gap: 0.5rem;
245
+ min-width: 0;
246
+ }
247
+
248
+ .mint-topbar__run-slot--joined {
249
+ gap: 0;
250
+ border: 1px solid var(--border-color);
251
+ border-radius: var(--radius-md);
252
+ background: var(--bg-card);
253
+ }
254
+
255
+ .mint-topbar__run-slot--joined .mint-experiment-popover,
256
+ .mint-topbar__run-slot--joined .mint-experiment-popover__trigger {
257
+ border: 0;
258
+ border-radius: calc(var(--radius-md) - 1px) 0 0 calc(var(--radius-md) - 1px);
259
+ }
260
+
261
+ /* The job takes the name's place: the name folds away while the job grows in,
262
+ and the code badge stays. The name stays in the accessible name. A trigger
263
+ with no code, the empty placeholder and the error text keep their text. */
264
+ .mint-topbar__run-slot .mint-experiment-popover__trigger-text {
265
+ transition:
266
+ max-width 200ms ease,
267
+ opacity 150ms ease,
268
+ margin 200ms ease;
269
+ }
270
+
271
+ .mint-topbar__run-slot--joined
272
+ .mint-experiment-popover__trigger:has(.mint-exp-code)
273
+ .mint-experiment-popover__trigger-text {
274
+ max-width: 0 !important; /* beats the inline maxNameWidth */
275
+ margin-inline-start: -0.5rem; /* takes back the trigger gap */
276
+ opacity: 0;
277
+ }
278
+
279
+ .mint-topbar__job {
280
+ display: inline-flex;
281
+ max-width: 6rem; /* close to the content (icon + bar, or "Jobs"), so the fold is even */
282
+ overflow: hidden;
283
+ align-items: center;
284
+ gap: 0.375rem;
285
+ flex-shrink: 0;
286
+ height: 30px;
287
+ padding: 0 0.625rem;
288
+ border: 0;
289
+ background: transparent;
290
+ color: var(--text-secondary);
291
+ font: inherit;
292
+ font-size: 0.75rem;
293
+ font-weight: 500;
294
+ white-space: nowrap;
295
+ cursor: pointer;
296
+ transition: background-color var(--mint-transition);
297
+ }
298
+
299
+ .mint-topbar__run-slot--joined .mint-topbar__job {
300
+ border-radius: calc(var(--radius-md) - 1px);
301
+ }
302
+
303
+ .mint-topbar__run-slot--joined .mint-experiment-popover + .mint-topbar__job {
304
+ border-left: 1px solid var(--border-color);
305
+ border-radius: 0 calc(var(--radius-md) - 1px) calc(var(--radius-md) - 1px) 0;
306
+ }
307
+
308
+ .mint-topbar__job--idle {
309
+ height: 32px;
310
+ border: 1px solid var(--border-color);
311
+ border-radius: var(--radius-md);
312
+ background: var(--bg-card);
313
+ }
314
+
315
+ .mint-topbar__job--idle:hover,
316
+ .mint-topbar__job--neutral:hover {
317
+ background: var(--bg-hover);
318
+ }
319
+
320
+ .mint-topbar__job:focus-visible {
321
+ outline: none;
322
+ box-shadow: inset var(--focus-ring);
323
+ }
324
+
325
+ .mint-topbar__job--primary { background: var(--color-primary-soft); color: var(--_ink-primary); }
326
+ .mint-topbar__job--warning { background: var(--mint-warning-bg); color: var(--mint-warning-text); }
327
+ .mint-topbar__job--success { background: var(--mint-success-bg); color: color-mix(in srgb, var(--mint-success) 50%, var(--text-primary)); }
328
+ .mint-topbar__job--error { background: var(--mint-error-bg); color: var(--_ink-error); }
329
+
330
+ .mint-topbar__job--primary:hover,
331
+ .mint-topbar__job--warning:hover,
332
+ .mint-topbar__job--success:hover,
333
+ .mint-topbar__job--error:hover {
334
+ filter: brightness(0.97);
335
+ }
336
+
337
+ .mint-topbar__job-icon {
338
+ flex-shrink: 0;
339
+ }
340
+
341
+ .mint-topbar__job-icon--spin {
342
+ animation: mint-topbar-job-spin 1s linear infinite;
343
+ }
344
+
345
+ @keyframes mint-topbar-job-spin {
346
+ to { transform: rotate(360deg); }
347
+ }
348
+
349
+ .mint-topbar__job-bar {
350
+ width: 2rem;
351
+ height: 4px;
352
+ border-radius: 2px;
353
+ background: var(--border-color);
354
+ overflow: hidden;
355
+ }
356
+
357
+ .mint-topbar__job-bar > span {
358
+ display: block;
359
+ height: 100%;
360
+ background: var(--color-primary);
361
+ transition: width var(--mint-transition);
362
+ }
363
+
364
+ /* Enter and leave: the job grows in from zero width and folds back out. */
365
+ .mint-topbar-job-enter-active,
366
+ .mint-topbar-job-leave-active {
367
+ transition:
368
+ max-width 200ms ease,
369
+ padding 200ms ease,
370
+ opacity 150ms ease;
371
+ }
372
+
373
+ .mint-topbar-job-enter-from,
374
+ .mint-topbar-job-leave-to {
375
+ max-width: 0;
376
+ padding-inline: 0;
377
+ opacity: 0;
378
+ }
379
+
380
+ /* Screen-reader only: announces status changes of the shown job. */
381
+ .mint-topbar__job-status {
382
+ position: absolute;
383
+ width: 1px;
384
+ height: 1px;
385
+ overflow: hidden;
386
+ clip-path: inset(50%);
387
+ white-space: nowrap;
388
+ }
@@ -112,13 +112,26 @@
112
112
  }
113
113
 
114
114
  /* Footer with clear selection */
115
+ .mint-experiment-selector__description {
116
+ padding: 0 1.25rem 0.75rem;
117
+ font-size: 0.8125rem;
118
+ color: var(--text-secondary);
119
+ }
120
+
115
121
  .mint-experiment-selector__footer {
116
122
  display: flex;
123
+ align-items: center;
124
+ gap: 0.75rem;
117
125
  justify-content: flex-start;
118
126
  padding: 0.5rem 1.25rem 0.75rem;
119
127
  border-top: 1px solid var(--border-light);
120
128
  }
121
129
 
130
+ .mint-experiment-selector__footer-slot {
131
+ flex: 1;
132
+ min-width: 0;
133
+ }
134
+
122
135
  .mint-experiment-selector__clear-btn {
123
136
  padding: 0.25rem 0;
124
137
  border: none;