@morscherlab/mint-sdk 1.5.0-beta.1 → 1.5.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 (174) hide show
  1. package/dist/{AlertBox-CIhqsWbN.js → AlertBox-Cai0w7gF.js} +3 -3
  2. package/dist/{AlertBox-CIhqsWbN.js.map → AlertBox-Cai0w7gF.js.map} +1 -1
  3. package/dist/{AppSidebar-BDrd7Kwn.js → AppSidebar-C-Vq5x4I.js} +410 -127
  4. package/dist/AppSidebar-C-Vq5x4I.js.map +1 -0
  5. package/dist/{ConfirmDialog-CjWoxAD1.js → ConfirmDialog-BjFsOpBe.js} +3 -3
  6. package/dist/ConfirmDialog-BjFsOpBe.js.map +1 -0
  7. package/dist/{ExperimentPopover-BM92LlrQ.js → ExperimentPopover-DYy_s0kI.js} +25 -37
  8. package/dist/ExperimentPopover-DYy_s0kI.js.map +1 -0
  9. package/dist/{ExperimentSelectorModal-LsB1ABmh.js → ExperimentSelectorModal-Bfh5bdkv.js} +37 -8
  10. package/dist/ExperimentSelectorModal-Bfh5bdkv.js.map +1 -0
  11. package/dist/{SegmentedControl-Dz41vAFM.js → SegmentedControl-B8KgyOjd.js} +2 -2
  12. package/dist/{SegmentedControl-Dz41vAFM.js.map → SegmentedControl-B8KgyOjd.js.map} +1 -1
  13. package/dist/{SettingsModal-C-MSR-bk.js → SettingsModal-DwfrD1ER.js} +7 -7
  14. package/dist/{SettingsModal-C-MSR-bk.js.map → SettingsModal-DwfrD1ER.js.map} +1 -1
  15. package/dist/__tests__/components/AppTopBarCompact.test.d.ts +1 -0
  16. package/dist/__tests__/components/AppTopBarCompactReset.test.d.ts +1 -0
  17. package/dist/__tests__/components/AutoGroupModal.experimentLoad.test.d.ts +1 -0
  18. package/dist/components/AppTopBar.compact.d.ts +5 -0
  19. package/dist/components/AppTopBar.vue.d.ts +17 -17
  20. package/dist/components/FormBuilder.vue.d.ts +1 -1
  21. package/dist/components/PluginIcon.vue.d.ts +2 -2
  22. package/dist/components/PluginWorkspaceView.vue.d.ts +4 -0
  23. package/dist/components/SectionCard.vue.d.ts +13 -1
  24. package/dist/components/SettingsModal.vue.d.ts +1 -1
  25. package/dist/components/internal/AppTopBarPageSelectorInternal.vue.d.ts +2 -2
  26. package/dist/components/internal/FormSchemaInternal.vue.d.ts +9 -9
  27. package/dist/components/internal/FormSectionRenderer.vue.d.ts +4 -4
  28. package/dist/components/internal/LucideIconInternal.vue.d.ts +17 -0
  29. package/dist/composables/appLayoutSidebar.d.ts +9 -0
  30. package/dist/composables/useExperimentSelector.d.ts +2 -0
  31. package/dist/composables/useOnboarding.d.ts +1 -1
  32. package/dist/composables/usePluginWorkspace.d.ts +2 -0
  33. package/dist/composables/useVersionedSave.d.ts +5 -2
  34. package/dist/{controlSchemaModel-BbXbMgc6.js → controlSchemaModel-BtFNZj8t.js} +2 -2
  35. package/dist/{controlSchemaModel-BbXbMgc6.js.map → controlSchemaModel-BtFNZj8t.js.map} +1 -1
  36. package/dist/{data-LzdkEZJ-.js → data-CJztFns8.js} +31 -15
  37. package/dist/{data-LzdkEZJ-.js.map → data-CJztFns8.js.map} +1 -1
  38. package/dist/data.js +8 -8
  39. package/dist/{experimentStatus-DCn2QKG7.js → experimentStatus-Deokk7Br.js} +2 -2
  40. package/dist/{experimentStatus-DCn2QKG7.js.map → experimentStatus-Deokk7Br.js.map} +1 -1
  41. package/dist/index.d.ts +0 -1
  42. package/dist/index.js +15 -20
  43. package/dist/index.js.map +1 -1
  44. package/dist/{install-UTqrAgHe.js → install-Bk02tJ5M.js} +15 -40
  45. package/dist/install-Bk02tJ5M.js.map +1 -0
  46. package/dist/install.js +1 -1
  47. package/dist/{lab-CbdDLAVr.js → lab-BYhb_cJI.js} +47 -117
  48. package/dist/lab-BYhb_cJI.js.map +1 -0
  49. package/dist/lab.js +4 -4
  50. package/dist/{records-CWRztE5M.js → records-GtiVMlTQ.js} +207 -171
  51. package/dist/records-GtiVMlTQ.js.map +1 -0
  52. package/dist/{runtime-DrL6lKAb.js → runtime-aZPUPxoB.js} +112 -54
  53. package/dist/runtime-aZPUPxoB.js.map +1 -0
  54. package/dist/runtime.js +6 -6
  55. package/dist/{shell-Ch7cJnjo.js → shell-Bk9mD4Lc.js} +5 -5
  56. package/dist/{shell-Ch7cJnjo.js.map → shell-Bk9mD4Lc.js.map} +1 -1
  57. package/dist/shell.js +5 -5
  58. package/dist/styles.css +315 -134
  59. package/dist/styles.d.ts +1 -0
  60. package/dist/types/components.d.ts +22 -2
  61. package/dist/types/generated-ui.d.ts +2 -0
  62. package/dist/types/index.d.ts +2 -2
  63. package/dist/types/instrument.d.ts +5 -17
  64. package/dist/{useDropdownState-uFIz6Yv8.js → useDropdownState-CcBzUYZ6.js} +14 -5
  65. package/dist/useDropdownState-CcBzUYZ6.js.map +1 -0
  66. package/dist/{useExperimentSamples-Cz2v3V_q.js → useExperimentSamples-CpNXvpUn.js} +3 -3
  67. package/dist/{useExperimentSamples-Cz2v3V_q.js.map → useExperimentSamples-CpNXvpUn.js.map} +1 -1
  68. package/dist/{useListReorder-QqJc1dpX.js → useListReorder-Bp4EAI2w.js} +16 -4
  69. package/dist/useListReorder-Bp4EAI2w.js.map +1 -0
  70. package/dist/{useRackEditor-BHGM1Ngz.js → useRackEditor-i2bdPUFf.js} +156 -27
  71. package/dist/useRackEditor-i2bdPUFf.js.map +1 -0
  72. package/dist/{useRealtime-CNQbrS01.js → useRealtime-BLQEjbuq.js} +3 -3
  73. package/dist/{useRealtime-CNQbrS01.js.map → useRealtime-BLQEjbuq.js.map} +1 -1
  74. package/dist/useRequest-CXyPw3eh.js +76 -0
  75. package/dist/useRequest-CXyPw3eh.js.map +1 -0
  76. package/dist/utils/deprecation.d.ts +2 -0
  77. package/dist/utils/lucideIcon.d.ts +4 -0
  78. package/package.json +7 -4
  79. package/src/__tests__/components/AppTopBar.test.ts +102 -20
  80. package/src/__tests__/components/AppTopBarCompact.test.ts +65 -0
  81. package/src/__tests__/components/AppTopBarCompactReset.test.ts +24 -0
  82. package/src/__tests__/components/AppTopBarOnboarding.test.ts +10 -0
  83. package/src/__tests__/components/AutoGroupModal.experimentLoad.test.ts +58 -0
  84. package/src/__tests__/components/BaseModal.test.ts +33 -2
  85. package/src/__tests__/components/ConfirmDialog.test.ts +2 -2
  86. package/src/__tests__/components/ExperimentPopover.test.ts +32 -0
  87. package/src/__tests__/components/ExperimentSelectorModal.test.ts +16 -0
  88. package/src/__tests__/components/PluginWorkspaceView.test.ts +20 -1
  89. package/src/__tests__/components/SectionCard.test.ts +33 -0
  90. package/src/__tests__/components/deprecationTags.test.ts +4 -18
  91. package/src/__tests__/composables/useJobCenter.test.ts +35 -0
  92. package/src/__tests__/composables/useJobsEndpoint.test.ts +50 -0
  93. package/src/__tests__/composables/useOnboarding.test.ts +2 -2
  94. package/src/__tests__/composables/useVersionedSave.test.ts +71 -6
  95. package/src/__tests__/docs/frontendDocsCatalog.test.ts +3 -3
  96. package/src/__tests__/styles/menusShellRefresh.test.ts +10 -0
  97. package/src/components/AppLayout.story.vue +5 -5
  98. package/src/components/AppLayout.vue +13 -1
  99. package/src/components/AppSidebar.vue +2 -2
  100. package/src/components/AppTopBar.compact.ts +60 -0
  101. package/src/components/AppTopBar.story.vue +39 -14
  102. package/src/components/AppTopBar.vue +222 -89
  103. package/src/components/BaseModal.vue +16 -5
  104. package/src/components/CollapsibleCard.vue +1 -1
  105. package/src/components/ConfirmDialog.vue +1 -1
  106. package/src/components/ExperimentPopover.story.vue +2 -2
  107. package/src/components/ExperimentPopover.vue +6 -22
  108. package/src/components/ExperimentSelectorModal.vue +35 -0
  109. package/src/components/FileUploader.vue +3 -35
  110. package/src/components/GeneratedPluginView.vue +34 -81
  111. package/src/components/GeneratedResultRenderer.vue +2 -1
  112. package/src/components/InstrumentStatusPanels.vue +3 -3
  113. package/src/components/PlotlyChart.vue +1 -1
  114. package/src/components/PluginIcon.story.vue +2 -2
  115. package/src/components/PluginIcon.vue +2 -2
  116. package/src/components/PluginWorkspaceView.vue +10 -1
  117. package/src/components/SectionCard.vue +42 -6
  118. package/src/components/internal/AppTopBarOnboardingInternal.vue +30 -14
  119. package/src/components/internal/AppTopBarPageSelectorInternal.vue +12 -0
  120. package/src/components/internal/AutoGroupModalInternal.vue +14 -3
  121. package/src/components/internal/LucideIconInternal.vue +23 -0
  122. package/src/composables/appLayoutSidebar.ts +11 -0
  123. package/src/composables/index.ts +1 -2
  124. package/src/composables/useExperimentSelector.ts +4 -0
  125. package/src/composables/useJobCenter.ts +21 -1
  126. package/src/composables/useJobsEndpoint.ts +15 -4
  127. package/src/composables/useOnboarding.ts +3 -3
  128. package/src/composables/usePluginWorkspace.ts +15 -4
  129. package/src/composables/useVersionedSave.ts +32 -7
  130. package/src/generated-ui/main.ts +3 -1
  131. package/src/index.ts +0 -3
  132. package/src/styles/components/alert-box.css +1 -1
  133. package/src/styles/components/app-layout.css +5 -0
  134. package/src/styles/components/app-sidebar.css +5 -5
  135. package/src/styles/components/app-top-bar.css +130 -9
  136. package/src/styles/components/button.css +3 -3
  137. package/src/styles/components/checkbox.css +1 -1
  138. package/src/styles/components/collapsible-card.css +1 -1
  139. package/src/styles/components/confirm-dialog.css +2 -2
  140. package/src/styles/components/file-uploader.css +3 -3
  141. package/src/styles/components/icon-button.css +3 -3
  142. package/src/styles/components/loading-spinner.css +1 -1
  143. package/src/styles/components/onboarding.css +39 -0
  144. package/src/styles/components/pill.css +1 -1
  145. package/src/styles/components/plugin-icon.css +2 -2
  146. package/src/styles/components/presence-avatars.css +4 -4
  147. package/src/styles/components/progress-bar.css +1 -1
  148. package/src/styles/components/section-card.css +65 -6
  149. package/src/styles/components/sequence-input.css +1 -1
  150. package/src/styles/components/slider.css +1 -1
  151. package/src/styles/components/toggle.css +1 -1
  152. package/src/styles/variables.css +10 -4
  153. package/src/types/components.ts +23 -2
  154. package/src/types/generated-ui.ts +2 -0
  155. package/src/types/index.ts +1 -1
  156. package/src/types/instrument.ts +5 -18
  157. package/src/utils/deprecation.ts +8 -0
  158. package/src/utils/lucideIcon.ts +25 -0
  159. package/src/utils/lucideIconIndex.d.ts +2 -0
  160. package/dist/AppSidebar-BDrd7Kwn.js.map +0 -1
  161. package/dist/ConfirmDialog-CjWoxAD1.js.map +0 -1
  162. package/dist/ExperimentPopover-BM92LlrQ.js.map +0 -1
  163. package/dist/ExperimentSelectorModal-LsB1ABmh.js.map +0 -1
  164. package/dist/deprecatedRootExports.d.ts +0 -191
  165. package/dist/install-UTqrAgHe.js.map +0 -1
  166. package/dist/lab-CbdDLAVr.js.map +0 -1
  167. package/dist/records-CWRztE5M.js.map +0 -1
  168. package/dist/runtime-DrL6lKAb.js.map +0 -1
  169. package/dist/useDropdownState-uFIz6Yv8.js.map +0 -1
  170. package/dist/useListReorder-QqJc1dpX.js.map +0 -1
  171. package/dist/useRackEditor-BHGM1Ngz.js.map +0 -1
  172. package/dist/useRequest-Bfsh7gIm.js +0 -215
  173. package/dist/useRequest-Bfsh7gIm.js.map +0 -1
  174. package/src/deprecatedRootExports.ts +0 -226
@@ -470,9 +470,9 @@ describe('frontend docs catalog coverage', () => {
470
470
  'BaseButton',
471
471
  'PluginWorkspaceView',
472
472
  'DataFrame',
473
- 'useForm',
474
- 'useReagentSeries',
475
- 'ConcentrationValue',
473
+ 'useExperiment',
474
+ 'useRequest',
475
+ 'UseExperimentReturn',
476
476
  'FormSectionSchema',
477
477
  ]))
478
478
  })
@@ -21,6 +21,11 @@ describe('menus and app shell styles', () => {
21
21
  expect(css('step-wizard.css')).not.toMatch(/\.mint-wizard__nav-btn/)
22
22
  })
23
23
 
24
+ // A max-content pill column made the grid wider than a phone, pushing theme and settings off the bar.
25
+ it('should let the pill column shrink when the two-row top bar is narrower than the pills', () => {
26
+ expect(css('app-top-bar.css')).toMatch(/\.mint-topbar__container--stackable\s*\{[^}]*grid-template-columns:\s*minmax\(0, max-content\) minmax\(0, 1fr\) auto;/)
27
+ })
28
+
24
29
  it('should pad the top bar to the 1.2 height (62px)', () => {
25
30
  expect(css('app-top-bar.css')).toMatch(/\.mint-topbar\s*\{[^}]*padding:\s*0\.75rem 1rem;/)
26
31
  })
@@ -40,6 +45,11 @@ describe('menus and app shell styles', () => {
40
45
  expect(css('app-page-selector.css')).toMatch(new RegExp(`\\.mint-page-selector__${part}\\s*\\{[^}]*box-sizing:\\s*border-box;`))
41
46
  })
42
47
 
48
+ // Beside a nav slot, a title without a subtitle broke at its hyphen ("LEAF-" over "cal").
49
+ it('should keep a title-only top-bar label on one line', () => {
50
+ expect(css('app-top-bar.css')).toMatch(/\.mint-topbar__title-only[^{]*\{[^}]*white-space:\s*nowrap;/)
51
+ })
52
+
43
53
  // Shrinking the tile beside the identity also shrank structured icons inside their solid chip, drawing a ring.
44
54
  it('should keep the plugin tile beside the static plugin identity (5a)', () => {
45
55
  expect(css('app-top-bar.css')).not.toMatch(/:has\(\+ \.mint-topbar__plugin-identity\)/)
@@ -52,7 +52,7 @@ const logScale = ref(false)
52
52
  >
53
53
  <template #topbar>
54
54
  <AppTopBar
55
- :spec="{ pills: pillNav, themeToggle: true, homePath: '' }"
55
+ :spec="{ pills: pillNav, homePath: '' }"
56
56
  title="My Plugin"
57
57
  subtitle="Laboratory Analysis"
58
58
  :pill="activeView"
@@ -113,7 +113,7 @@ const logScale = ref(false)
113
113
  >
114
114
  <template #topbar>
115
115
  <AppTopBar
116
- :spec="{ pills: pillNav, themeToggle: true, homePath: '' }"
116
+ :spec="{ pills: pillNav, homePath: '' }"
117
117
  title="Plate Analyzer"
118
118
  subtitle="Dashboard"
119
119
  :pill="activeView"
@@ -170,7 +170,7 @@ const logScale = ref(false)
170
170
  <div style="height: 400px;">
171
171
  <AppLayout>
172
172
  <template #topbar>
173
- <AppTopBar :spec="{ themeToggle: true, homePath: '' }" title="Simple Page" />
173
+ <AppTopBar :spec="{ homePath: '' }" title="Simple Page" />
174
174
  </template>
175
175
  <div style="padding: 2rem;">
176
176
  <p style="color: var(--text-muted, #94a3b8);">Layout without a sidebar, just topbar and content.</p>
@@ -254,7 +254,7 @@ const logScale = ref(false)
254
254
  <AppLayout :floating="true" sidebar-width="280px">
255
255
  <template #topbar>
256
256
  <AppTopBar
257
- :spec="{ pills: pillNav, themeToggle: true, homePath: '' }"
257
+ :spec="{ pills: pillNav, homePath: '' }"
258
258
  title="IC50 Calculator"
259
259
  subtitle="Analysis"
260
260
  :pill="activeView"
@@ -302,7 +302,7 @@ const logScale = ref(false)
302
302
  <AppLayout :floating="true">
303
303
  <template #topbar>
304
304
  <AppTopBar
305
- :spec="{ themeToggle: true, homePath: '' }"
305
+ :spec="{ homePath: '' }"
306
306
  title="Plate Analyzer"
307
307
  subtitle="Dashboard"
308
308
  />
@@ -21,7 +21,8 @@
21
21
  * </AppLayout>
22
22
  * ```
23
23
  */
24
- import { computed, ref } from 'vue'
24
+ import { computed, provide, ref, useSlots } from 'vue'
25
+ import { APP_LAYOUT_SIDEBAR_KEY } from '../composables/appLayoutSidebar'
25
26
 
26
27
  interface Props {
27
28
  /** Position of sidebar (left or right side of screen) */
@@ -57,6 +58,7 @@ const emit = defineEmits<{
57
58
  'update:sidebarOpen': [value: boolean]
58
59
  }>()
59
60
 
61
+ const slots = useSlots()
60
62
  const internalSidebarOpen = ref(props.defaultSidebarOpen)
61
63
  const sidebarOpenModel = computed({
62
64
  get: () => props.sidebarOpen ?? internalSidebarOpen.value,
@@ -82,6 +84,16 @@ function toggleSidebar() {
82
84
  sidebarOpenModel.value = !sidebarOpenModel.value
83
85
  }
84
86
 
87
+ // An AppTopBar in the topbar slot takes the drawer button; the floating one then hides (CSS :has).
88
+ if (props.responsiveSidebar && slots.sidebar) {
89
+ provide(APP_LAYOUT_SIDEBAR_KEY, {
90
+ open: computed(() => sidebarOpenModel.value),
91
+ toggle: toggleSidebar,
92
+ openLabel: computed(() => props.sidebarToggleLabel),
93
+ closeLabel: computed(() => props.sidebarCloseLabel),
94
+ })
95
+ }
96
+
85
97
  function closeSidebar() {
86
98
  sidebarOpenModel.value = false
87
99
  }
@@ -208,11 +208,11 @@ function railIconStyle(section: SidebarToolSection): Record<string, string> {
208
208
  }
209
209
 
210
210
  function badgeToneClass(section: SidebarToolSection): string {
211
- return `mint-sidebar__rail-badge--${section.badgeTone ?? 'cta'}`
211
+ return `mint-sidebar__rail-badge--${section.badgeTone ?? 'primary'}`
212
212
  }
213
213
 
214
214
  function tagToneClass(section: SidebarToolSection): string {
215
- return `mint-sidebar__tag--${section.badgeTone ?? 'cta'}`
215
+ return `mint-sidebar__tag--${section.badgeTone ?? 'primary'}`
216
216
  }
217
217
 
218
218
  function actionClasses(action: SidebarToolSectionAction): string[] {
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Decides when the top bar trades the Save and Jobs labels for icons.
3
+ *
4
+ * Labels go when the content no longer fits: the experiment chip is squeezed below
5
+ * `CHIP_MIN_PX` with its name cut, the plugin or page name is cut, or the pills or the context group overflow. They come back once the
6
+ * bar has grown by at least the width the labels took, so the bar never flips back and
7
+ * forth at one width. `reset()` re-measures after the content changes.
8
+ */
9
+ import type { Ref } from 'vue'
10
+
11
+ const CHIP_MIN_PX = 192
12
+ const LABELS = '.mint-experiment-popover__save-label, .mint-topbar__job-label'
13
+ /** Identity text that truncates when the left side is squeezed. */
14
+ const IDENTITY_TEXT = '.mint-page-selector__plugin, .mint-page-selector__label, .mint-topbar__plugin-name, .mint-topbar-title, .mint-topbar__title-only'
15
+
16
+ function overflows(el: Element | null): boolean {
17
+ return !!el && el.scrollWidth > el.clientWidth + 1
18
+ }
19
+
20
+ export function trackTopBarCompact(header: HTMLElement, compact: Ref<boolean>): { reset: () => void; stop: () => void } {
21
+ if (typeof ResizeObserver === 'undefined') return { reset: () => {}, stop: () => {} }
22
+ let expandAt = 0
23
+
24
+ function check(): void {
25
+ const width = header.clientWidth
26
+ if (compact.value) {
27
+ if (width >= expandAt) compact.value = false
28
+ return
29
+ }
30
+ // A short name leaves the chip narrow by itself: squeezed means narrow and truncated.
31
+ const chip = header.querySelector('.mint-experiment-popover__trigger')
32
+ const squeezed = (chip && chip.getBoundingClientRect().width < CHIP_MIN_PX
33
+ && overflows(chip.querySelector('.mint-experiment-popover__trigger-text')))
34
+ || [...header.querySelectorAll(IDENTITY_TEXT)].some(overflows)
35
+ || overflows(header.querySelector('.mint-topbar__center'))
36
+ || overflows(header.querySelector('.mint-topbar__context'))
37
+ if (!squeezed) return
38
+ const labelsWidth = [...header.querySelectorAll(LABELS)].reduce((sum, el) => sum + el.getBoundingClientRect().width, 0)
39
+ if (labelsWidth === 0) return
40
+ expandAt = width + labelsWidth
41
+ compact.value = true
42
+ }
43
+
44
+ const observer = new ResizeObserver(check)
45
+ // The groups mount and unmount with their content, so each reset observes them again.
46
+ function observe(): void {
47
+ observer.disconnect()
48
+ observer.observe(header)
49
+ for (const el of header.querySelectorAll('.mint-topbar__center, .mint-topbar__context')) observer.observe(el)
50
+ }
51
+ observe()
52
+ return {
53
+ reset: () => {
54
+ compact.value = false
55
+ expandAt = 0
56
+ requestAnimationFrame(observe)
57
+ },
58
+ stop: () => observer.disconnect(),
59
+ }
60
+ }
@@ -10,6 +10,7 @@ import type {
10
10
  PillNavItem,
11
11
  PageSelectorItem,
12
12
  PluginInfo,
13
+ TopBarAction,
13
14
  TopBarJob,
14
15
  } from '../types'
15
16
 
@@ -87,6 +88,17 @@ const samplePluginWithSubtitle: PluginInfo = {
87
88
  api_prefix: '/api/ms-planner',
88
89
  }
89
90
 
91
+ // Page icons are Lucide names; plugin buttons come from spec.actions (two show, the rest in More).
92
+ const sampleIconPages: PageSelectorItem[] = [
93
+ { id: 'monitor', label: 'Monitor', icon: 'activity' },
94
+ { id: 'planner', label: 'Planner', icon: 'calendar-days' },
95
+ { id: 'queue', label: 'Queue', icon: 'list-ordered' },
96
+ ]
97
+ const sampleActions: TopBarAction[] = [
98
+ { id: 'status', label: 'Database ready', icon: 'database', dot: 'success', onSelect: () => {} },
99
+ { id: 'export', label: 'Export', icon: 'download', onSelect: () => {} },
100
+ { id: 'reindex', label: 'Reindex', onSelect: () => {} },
101
+ ]
90
102
  // The seed is module state: clear it when the variant unmounts.
91
103
  function seedIdentity({ app }: { app: App }) {
92
104
  providePluginIdentity({
@@ -109,7 +121,6 @@ function seedIdentity({ app }: { app: App }) {
109
121
  <AppTopBar
110
122
  :spec="{
111
123
  pills: samplePillNav.slice(1, 3),
112
- themeToggle: true,
113
124
  homePath: '',
114
125
  standaloneLabel: false,
115
126
  }"
@@ -127,7 +138,7 @@ function seedIdentity({ app }: { app: App }) {
127
138
  <Variant title="Refresh · Workspace">
128
139
  <div style="padding: 2rem; background: var(--bg-primary); min-height: 22rem;">
129
140
  <AppTopBar
130
- :spec="{ pills: samplePillNav, themeToggle: true, homePath: '' }"
141
+ :spec="{ pills: samplePillNav, homePath: '' }"
131
142
  :page-selector="samplePageSelector"
132
143
  current-page-selector-id="workspace"
133
144
  pill="workspace"
@@ -139,7 +150,7 @@ function seedIdentity({ app }: { app: App }) {
139
150
  <div style="padding: 2rem; background: var(--bg-primary); min-height: 12rem;">
140
151
  <ExperimentProvider>
141
152
  <AppTopBar
142
- :spec="{ pills: samplePillNav, themeToggle: true, homePath: '' }"
153
+ :spec="{ pills: samplePillNav, homePath: '' }"
143
154
  :page-selector="samplePageSelector"
144
155
  current-page-selector-id="experiments"
145
156
  pill="experiments"
@@ -152,7 +163,7 @@ function seedIdentity({ app }: { app: App }) {
152
163
  <div style="padding: 2rem; background: var(--bg-primary); min-height: 12rem;">
153
164
  <ExperimentProvider :plugin="samplePlugin">
154
165
  <AppTopBar
155
- :spec="{ pills: samplePillNav, themeToggle: true, homePath: '' }"
166
+ :spec="{ pills: samplePillNav, homePath: '' }"
156
167
  :page-selector="samplePluginPages"
157
168
  current-page-selector-id="peaks"
158
169
  pill="experiments"
@@ -168,7 +179,7 @@ function seedIdentity({ app }: { app: App }) {
168
179
  <AppTopBar
169
180
  v-for="row in runSlotJobs"
170
181
  :key="row.label"
171
- :spec="{ themeToggle: true, homePath: '' }"
182
+ :spec="{ homePath: '' }"
172
183
  :page-selector="samplePluginPages"
173
184
  current-page-selector-id="peaks"
174
185
  :job="row.job"
@@ -193,7 +204,7 @@ function seedIdentity({ app }: { app: App }) {
193
204
  </div>
194
205
  <ExperimentProvider :plugin="samplePlugin" :save="false">
195
206
  <AppTopBar
196
- :spec="{ themeToggle: true, homePath: '' }"
207
+ :spec="{ homePath: '' }"
197
208
  :page-selector="samplePluginPages"
198
209
  current-page-selector-id="peaks"
199
210
  :job="runSlotJobs[liveRow].job"
@@ -205,7 +216,7 @@ function seedIdentity({ app }: { app: App }) {
205
216
  <Variant title="Refresh · Admin">
206
217
  <div style="padding: 2rem; background: var(--bg-primary); min-height: 12rem;">
207
218
  <AppTopBar
208
- :spec="{ pills: samplePillNav, themeToggle: true, homePath: '' }"
219
+ :spec="{ pills: samplePillNav, homePath: '' }"
209
220
  :page-selector="samplePageSelector"
210
221
  current-page-selector-id="admin"
211
222
  pill="admin"
@@ -217,7 +228,7 @@ function seedIdentity({ app }: { app: App }) {
217
228
  <div style="padding: 2rem; background: var(--bg-primary); min-height: 12rem;">
218
229
  <ExperimentProvider :preselect="false">
219
230
  <AppTopBar
220
- :spec="{ pills: samplePillNav, themeToggle: true, homePath: '' }"
231
+ :spec="{ pills: samplePillNav, homePath: '' }"
221
232
  :page-selector="samplePageSelector"
222
233
  current-page-selector-id="workspace"
223
234
  pill="workspace"
@@ -232,7 +243,6 @@ function seedIdentity({ app }: { app: App }) {
232
243
  <AppTopBar
233
244
  :spec="{
234
245
  pills: samplePillNav.slice(1, 3),
235
- themeToggle: true,
236
246
  homePath: '',
237
247
  standaloneLabel: false,
238
248
  }"
@@ -256,7 +266,6 @@ function seedIdentity({ app }: { app: App }) {
256
266
  :spec="{
257
267
  variant: state.variant,
258
268
  logo: state.showLogo,
259
- themeToggle: state.showThemeToggle,
260
269
  homePath: state.homePath,
261
270
  }"
262
271
  :title="state.title"
@@ -274,7 +283,6 @@ function seedIdentity({ app }: { app: App }) {
274
283
  :options="variants.map(v => ({ label: v, value: v }))"
275
284
  />
276
285
  <HstCheckbox v-model="state.showLogo" title="Show Logo" />
277
- <HstCheckbox v-model="state.showThemeToggle" title="Show Theme Toggle" />
278
286
  <HstText v-model="state.homePath" title="Home Path" />
279
287
  </template>
280
288
  </Variant>
@@ -282,7 +290,7 @@ function seedIdentity({ app }: { app: App }) {
282
290
  <Variant title="Base · Title + Subtitle">
283
291
  <div style="padding: 2rem;">
284
292
  <AppTopBar
285
- :spec="{ themeToggle: true }"
293
+ :spec="{}"
286
294
  title="Dose-Response Analysis"
287
295
  subtitle="Experiment EXP-2024-001"
288
296
  />
@@ -306,7 +314,7 @@ function seedIdentity({ app }: { app: App }) {
306
314
  <Variant title="Navigation · Page selector">
307
315
  <div style="padding: 2rem;">
308
316
  <AppTopBar
309
- :spec="{ themeToggle: true, homePath: '' }"
317
+ :spec="{ homePath: '' }"
310
318
  title="Results"
311
319
  :page-selector="samplePageSelector"
312
320
  current-page-selector-id="plugins"
@@ -354,11 +362,28 @@ function seedIdentity({ app }: { app: App }) {
354
362
  <div style="padding: 2rem;">
355
363
  <ExperimentProvider>
356
364
  <AppTopBar
357
- :spec="{ themeToggle: true, homePath: '' }"
365
+ :spec="{ homePath: '' }"
358
366
  title="Analysis"
359
367
  />
360
368
  </ExperimentProvider>
361
369
  </div>
362
370
  </Variant>
371
+
372
+ <Variant title="Layout · Fixed tail and two rows">
373
+ <!-- The theme and settings buttons always close the bar. Below 1024px of bar width the
374
+ pills and the context group (experiment, plugin buttons) move to a second row. -->
375
+ <div style="padding: 2rem; background: var(--bg-primary); display: grid; gap: 1rem;">
376
+ <ExperimentProvider v-for="width in ['1360px', '1180px', '900px', '768px']" :key="width" :plugin="samplePlugin">
377
+ <div :style="{ width, maxWidth: '100%' }">
378
+ <AppTopBar
379
+ :spec="{ pills: samplePillNav.slice(0, 3), homePath: '', actions: sampleActions }"
380
+ :page-selector="sampleIconPages"
381
+ current-page-selector-id="monitor"
382
+ pill="workspace"
383
+ />
384
+ </div>
385
+ </ExperimentProvider>
386
+ </div>
387
+ </Variant>
363
388
  </Story>
364
389
  </template>