@morscherlab/mint-sdk 1.2.6 → 1.2.8

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.
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { a as useAuthStore, c as paletteCssVariables, d as useSettingsStore, f a
2
2
  import { a as SORT_OPTIONS, c as formatExperimentDate, d as resolveExperimentCode, f as BaseModal_default, h as BaseButton_default, i as EXPERIMENT_STATUS_VARIANT_MAP, l as formatExperimentStatus, m as useEventListener, n as EXPERIMENT_STATUS_LABELS, o as datePresetToISO, p as useFocusTrap, r as EXPERIMENT_STATUS_OPTIONS, s as experimentIdFromRoute, t as DATE_PRESET_OPTIONS, u as getExperimentStatusVariant } from "./experiment-utils-CV2Wza3z.js";
3
3
  import { a as BaseSelect_default, i as BasePill_default, n as useRequestSyncState, r as EmptyState_default, s as BaseInput_default, t as Skeleton_default } from "./Skeleton-jhF9wUkU.js";
4
4
  import { $ as resolveJobCapabilities, A as useConcentrationUnits, At as formatTimeSlot, Bt as MultiSelect_default, C as controlsToViewIds, Ct as addMinutes, D as getTypeDefault, Dt as findNearestTimeSlotIndex, E as getFieldRegistryEntry, Et as findAvailableSlots, F as ATOMIC_WEIGHTS, Ft as rangesOverlap, G as createGeneratedAnalysisTransport, Gt as BaseToggle_default, Ht as useSelectionLimit, I as useChemicalFormula, It as snapToSlot, J as isActiveJobStatus, Jt as BaseTextarea_default, K as useGeneratedAnalysis, Kt as BaseCheckbox_default, L as GeneratedPathInput_default, Lt as toMinutes, M as SequenceInput_default, Mt as generateTimeSlots, N as useSequenceUtils, Nt as isTimeInRange, O as ConcentrationInput_default, Ot as formatDuration, P as FormulaInput_default, Pt as parseTime, Q as normalizeJobState, R as ArtifactSelectorModal_default, Rt as useTimeUtils, S as controlsToTopBarSettingsConfig, St as TimePicker_default, T as getDefaultControlView, Tt as durationMinutes, Ut as BaseSlider_default, Vt as useListSelection, W as resolveCurrentPluginId, Wt as BaseRadioGroup_default, X as jobStatusLabel, Y as isTerminalJobStatus, Z as normalizeJobPercent, _ as controlsToFormSchema, _t as pluginSecretLabel, a as defineDoseCalculatorControlProps, at as AlertBox_default, b as controlsToSettingsSchema, bt as usesPluginSecretReferences, c as defineWellPlateDoseComponentBindings, ct as SecretInput_default, d as defineControlModel, dt as PLUGIN_SECRET_REF_KEY, ft as clearPluginSecret, gt as pluginSecretId, h as controlValuesToComponentProps, ht as keepPluginSecret, i as useControlSchema, j as MoleculeInput_default, jt as fromMinutes, k as UnitInput_default, kt as formatTime, l as defineWellPlateDoseControlProps, lt as PLUGIN_SECRET_FORMAT_KEY, m as controlValuesToComponentBindingsById, mt as isPluginSecretRef, n as defineControls, o as defineDoseDesignControlModel, ot as FileUploader_default, p as controlValuesToComponentBindings, pt as isPluginSecretLocked, q as usePluginJobs, qt as Tooltip_default, r as getControlDefaults, s as defineWellPlateControlProps, st as NumberInput_default, t as defineControlComponentBindings, tt as DateTimePicker_default, u as useControlWorkspace, ut as PLUGIN_SECRET_FORMAT_REF, v as controlsToSectionFormSchema, vt as pluginSecretState, w as controlsToViewItems, wt as compareTime, x as controlsToSidebarPanels, xt as TagsInput_default, y as controlsToSectionFormSchemas, yt as setPluginSecret, z as useAnalysisArtifacts, zt as DatePicker_default } from "./useControlSchema-vLiRPUmj.js";
5
- import { $ as ScheduleCalendar_default, A as ChemicalFormula_default, At as CollapsibleCard_default, B as BioTemplateExperimentWorkspaceView_default, Bt as SegmentedControl_default, C as SequenceProgressBar_default, Ct as FormBuilder_default, D as formatSequenceRemaining, Dt as AppPluginSwitcher_default, E as formatSequenceEta, Et as AppTopBar_default, F as SmartGroupModal_default, Ft as AppToastContainer_default, G as ProgressBar_default, H as AdductText_default, Ht as ColorSlider_default, I as GroupAssigner_default, It as DataFrame_default, J as LayoutResizeHandle_default, K as StatusIndicator_default, L as RackEditor_default, Lt as LoadingSpinner_default, M as ProtocolStepEditor_default, Mt as ActionMenu_default, N as SampleHierarchyTree_default, Nt as IconButton_default, O as sequenceProgressPercent, Ot as AppAvatarMenu_default, P as ReagentEditor_default, Pt as RuntimeUpdateNotice_default, Q as ComponentBindingRenderer_default, R as BioTemplatePresetWorkspaceView_default, Rt as Calendar_default, S as InstrumentAlertLog_default, St as AppSidebar_default, T as estimateSequenceRemainingSeconds, Tt as StepWizard_default, U as Breadcrumb_default, Ut as SearchableSelect_default, V as BioTemplateRenderer_default, Vt as BaseTabs_default, W as Avatar_default, X as DoseDesignWorkspaceView_default, Y as AppContainer_default, Z as ControlWorkspaceView_default, _ as inferLcmsPlateTypeFromWellIds, _t as generatedJobFormSchema, a as FileBrowserModal_default, at as PlateMapEditor_default, b as InstrumentStatusCard_default, bt as MobileSupportGate_default, c as TimeRangeInput_default, ct as ExperimentTimeline_default, d as AuditTrail_default, dt as JobsStatusTray_default, et as SampleSelector_default, f as LcmsSequenceTable_default, ft as GeneratedResultRenderer_default, g as extractLcmsSampleName, gt as generatedJobDefaults, h as extractLcmsCommonPrefix, ht as ChartContainer_default, i as ArtifactSaveDialog_default, it as ReagentList_default, j as ScientificNumber_default, jt as ThemeToggle_default, k as sequenceSamplesRemaining, kt as PluginIcon_default, l as ExperimentDataViewer_default, lt as DoseCalculator_default, m as basenameFromWindowsPath, mt as PlotlyChart_default, n as FilePicker_default, nt as SmartGroupManual_default, o as SectionCard_default, ot as SampleLegend_default, p as DEFAULT_LCMS_SEQUENCE_COLUMNS, pt as GeneratedPlotlyResult_default, q as Divider_default, r as FitPanel_default, rt as SmartGroupFieldRecipe_default, s as ResourceCard_default, st as WellPlate_default, tt as AutoGroupModal_default, u as BatchProgressList_default, ut as GeneratedPluginView_default, v as lcmsWellIdFromPosition, vt as normalizeGeneratedJobInput, w as estimateSequenceFinishDate, wt as FormActions_default, x as InstrumentStateBadge_default, xt as AppLayout_default, y as reconstructLcmsPlateCellsFromSequenceItems, yt as PluginWorkspaceView_default, z as BioTemplatePackWorkspaceView_default, zt as DropdownButton_default } from "./components-CZzBCakZ.js";
5
+ import { $ as ScheduleCalendar_default, A as ChemicalFormula_default, At as CollapsibleCard_default, B as BioTemplateExperimentWorkspaceView_default, Bt as SegmentedControl_default, C as SequenceProgressBar_default, Ct as FormBuilder_default, D as formatSequenceRemaining, Dt as AppPluginSwitcher_default, E as formatSequenceEta, Et as AppTopBar_default, F as SmartGroupModal_default, Ft as AppToastContainer_default, G as ProgressBar_default, H as AdductText_default, Ht as ColorSlider_default, I as GroupAssigner_default, It as DataFrame_default, J as LayoutResizeHandle_default, K as StatusIndicator_default, L as RackEditor_default, Lt as LoadingSpinner_default, M as ProtocolStepEditor_default, Mt as ActionMenu_default, N as SampleHierarchyTree_default, Nt as IconButton_default, O as sequenceProgressPercent, Ot as AppAvatarMenu_default, P as ReagentEditor_default, Pt as RuntimeUpdateNotice_default, Q as ComponentBindingRenderer_default, R as BioTemplatePresetWorkspaceView_default, Rt as Calendar_default, S as InstrumentAlertLog_default, St as AppSidebar_default, T as estimateSequenceRemainingSeconds, Tt as StepWizard_default, U as Breadcrumb_default, Ut as SearchableSelect_default, V as BioTemplateRenderer_default, Vt as BaseTabs_default, W as Avatar_default, X as DoseDesignWorkspaceView_default, Y as AppContainer_default, Z as ControlWorkspaceView_default, _ as inferLcmsPlateTypeFromWellIds, _t as generatedJobFormSchema, a as FileBrowserModal_default, at as PlateMapEditor_default, b as InstrumentStatusCard_default, bt as MobileSupportGate_default, c as TimeRangeInput_default, ct as ExperimentTimeline_default, d as AuditTrail_default, dt as JobsStatusTray_default, et as SampleSelector_default, f as LcmsSequenceTable_default, ft as GeneratedResultRenderer_default, g as extractLcmsSampleName, gt as generatedJobDefaults, h as extractLcmsCommonPrefix, ht as ChartContainer_default, i as ArtifactSaveDialog_default, it as ReagentList_default, j as ScientificNumber_default, jt as ThemeToggle_default, k as sequenceSamplesRemaining, kt as PluginIcon_default, l as ExperimentDataViewer_default, lt as DoseCalculator_default, m as basenameFromWindowsPath, mt as PlotlyChart_default, n as FilePicker_default, nt as SmartGroupManual_default, o as SectionCard_default, ot as SampleLegend_default, p as DEFAULT_LCMS_SEQUENCE_COLUMNS, pt as GeneratedPlotlyResult_default, q as Divider_default, r as FitPanel_default, rt as SmartGroupFieldRecipe_default, s as ResourceCard_default, st as WellPlate_default, tt as AutoGroupModal_default, u as BatchProgressList_default, ut as GeneratedPluginView_default, v as lcmsWellIdFromPosition, vt as normalizeGeneratedJobInput, w as estimateSequenceFinishDate, wt as FormActions_default, x as InstrumentStateBadge_default, xt as AppLayout_default, y as reconstructLcmsPlateCellsFromSequenceItems, yt as PluginWorkspaceView_default, z as BioTemplatePackWorkspaceView_default, zt as DropdownButton_default } from "./components-Bbg_gtzF.js";
6
6
  import { s as FormField_default, t as SettingsModal_default } from "./SettingsModal-DELMkUFR.js";
7
7
  import { $ as getInjectedFrontendRevision, A as useFileImport, B as REORDER_ANNOUNCER_STYLE, C as useExpansionSet, D as detectDelimiter, F as extractSampleNamesFromDesignData, G as combinePluginJobSources, H as useListReorder, I as extractSampleOptionsFromDesignData, J as usePluginWorkspace, K as useJobsStatusTray, L as unwrapExperimentDesignData, M as extractSamplesFromDesignData, N as useAutoGroup, O as parseDelimitedText, Q as useAppExperiment, R as DEFAULT_COLORS, S as useExperimentData, U as useReorderAnnouncer, V as reorderList, W as useDoseCalculator, X as useMobileSupportGate, Y as DEFAULT_MOBILE_VIEWPORT_QUERY, Z as APP_EXPERIMENT_KEY, _ as useBioTemplateControls, a as DEFAULT_PRESETS, at as candidateMatchesSearch, b as useScheduleDrag, c as useReagentSeries, ct as compareSortValues, d as useBioTemplatePresetWorkspace, dt as useRovingFocus, et as useRuntimeAlignment, f as useBioTemplatePackWorkspace, g as useBioTemplateComponents, h as toBioTemplateComponentPropsByComponent, i as useProtocolTemplates, it as useToast, j as validateImportFile, k as readFileAsText, l as useGroupAssignment, lt as useSortedItems, m as getBioTemplateComponentProps, nt as usePresenceHeartbeat, o as DEFAULT_UNITS, ot as normalizeSearchQuery, p as useBioTemplateWorkspace, q as usePluginJobCenter, rt as useTheme, s as generateDilutionSeries, st as useTextSearch, tt as getPresenceClientId, u as useRackEditor, ut as useMenuKeyboard, v as useTemplateCollection, w as useSampleGroups, x as useExperimentSamples, y as useExperimentSave, z as useWellPlateEditor } from "./validation-ChMyixAf.js";
8
8
  import { i as usePlatformContext, n as evaluateCondition, r as useForm, t as useFormBuilder } from "./useFormBuilder-D37ajgS8.js";
package/dist/install.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./useControlSchema-vLiRPUmj.js";
2
- import { t as components_exports } from "./components-CZzBCakZ.js";
2
+ import { t as components_exports } from "./components-Bbg_gtzF.js";
3
3
  //#region src/install.ts
4
4
  /**
5
5
  * Vue plugin that registers all MINT SDK components globally.
package/dist/styles.css CHANGED
@@ -7510,7 +7510,8 @@ html.dark .mint-tab--active .mint-tab__badge {
7510
7510
  /* ToastNotification Component Styles — neutral surface, accent only on icon + TTL bar */
7511
7511
  .mint-toast__container {
7512
7512
  position: fixed;
7513
- top: 1rem;
7513
+ /* AppTopBar publishes its bottom edge + 1rem so toasts clear its controls. */
7514
+ top: var(--mint-toast-offset-top, 1rem);
7514
7515
  right: 1rem;
7515
7516
  z-index: 9999;
7516
7517
  display: flex;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morscherlab/mint-sdk",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "description": "MINT Platform SDK — Vue 3 components, composables, and types for plugin development. MINT = Mass-spec INtegrated Toolkit.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -83,7 +83,7 @@
83
83
  },
84
84
  "dependencies": {
85
85
  "@floating-ui/dom": "^1.7.6",
86
- "axios": "1.13.6",
86
+ "axios": "1.20.0",
87
87
  "lucide-vue-next": "1.0.0",
88
88
  "plotly.js-dist-min": "^3.7.0"
89
89
  },
@@ -1,3 +1,5 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
1
3
  import { mount } from '@vue/test-utils'
2
4
  import { afterEach, describe, expect, it, vi } from 'vitest'
3
5
  import { nextTick } from 'vue'
@@ -88,4 +90,10 @@ describe('AppToastContainer', () => {
88
90
  expect(wrapper.find('.mint-toast__count').text()).toBe('×4')
89
91
  expect(wrapper.find('.mint-toast__item--stacked').exists()).toBe(true)
90
92
  })
93
+
94
+ it('should place toasts 1rem from the top when no top bar offset is set', () => {
95
+ const styles = readFileSync(resolve(process.cwd(), 'src/styles/components/toast.css'), 'utf8')
96
+
97
+ expect(styles).toMatch(/\.mint-toast__container\s*\{[^}]*top:\s*var\(--mint-toast-offset-top,\s*1rem\);/s)
98
+ })
91
99
  })
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
2
- import { flushPromises, mount, type VueWrapper } from '@vue/test-utils'
2
+ import { enableAutoUnmount, flushPromises, mount, type VueWrapper } from '@vue/test-utils'
3
3
  import { computed, ref } from 'vue'
4
4
  import { createPinia } from 'pinia'
5
5
  import AppTopBar from '../../components/AppTopBar.vue'
@@ -75,6 +75,8 @@ async function openSettingsModal(wrapper: VueWrapper) {
75
75
  return wrapper.findComponent(SettingsModal)
76
76
  }
77
77
 
78
+ enableAutoUnmount(afterEach)
79
+
78
80
  describe('AppTopBar', () => {
79
81
  beforeEach(() => {
80
82
  vi.clearAllMocks()
@@ -1253,4 +1255,99 @@ describe('AppTopBar', () => {
1253
1255
  })
1254
1256
 
1255
1257
  })
1258
+
1259
+ describe('toast offset', () => {
1260
+ const property = '--mint-toast-offset-top'
1261
+ let rects: Record<string, { top: number; bottom: number }> = {}
1262
+
1263
+ function toastOffset() {
1264
+ return document.documentElement.style.getPropertyValue(property)
1265
+ }
1266
+
1267
+ beforeEach(() => {
1268
+ rects = {}
1269
+ vi.spyOn(HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(function (this: HTMLElement) {
1270
+ const title = this.querySelector('.mint-topbar__title-only')?.textContent ?? ''
1271
+ return (rects[title] ?? { top: 16, bottom: 72 }) as DOMRect
1272
+ })
1273
+ })
1274
+
1275
+ afterEach(() => {
1276
+ vi.restoreAllMocks()
1277
+ vi.unstubAllGlobals()
1278
+ })
1279
+
1280
+ it('should set the toast offset below the top bar when mounted', () => {
1281
+ createWrapper({ title: 'Test App' })
1282
+
1283
+ expect(toastOffset()).toBe('calc(72px + 1rem)')
1284
+ })
1285
+
1286
+ it('should remove the toast offset when the top bar unmounts', () => {
1287
+ createWrapper({ title: 'Test App' }).unmount()
1288
+
1289
+ expect(toastOffset()).toBe('')
1290
+ })
1291
+
1292
+ it('should keep the toast offset when another top bar is still mounted', () => {
1293
+ createWrapper({ title: 'Remaining' })
1294
+ createWrapper({ title: 'Removed' }).unmount()
1295
+
1296
+ expect(toastOffset()).toBe('calc(72px + 1rem)')
1297
+ })
1298
+
1299
+ it('should clamp the toast offset at 0 when the top bar is scrolled above the viewport', () => {
1300
+ rects['Test App'] = { top: -96, bottom: -40 }
1301
+ createWrapper({ title: 'Test App' })
1302
+
1303
+ expect(toastOffset()).toBe('calc(0px + 1rem)')
1304
+ })
1305
+
1306
+ it('should follow the top-most top bar when several are mounted', () => {
1307
+ rects.Header = { top: 16, bottom: 72 }
1308
+ rects.Lower = { top: 200, bottom: 260 }
1309
+ createWrapper({ title: 'Lower' })
1310
+ createWrapper({ title: 'Header' })
1311
+
1312
+ expect(toastOffset()).toBe('calc(72px + 1rem)')
1313
+ })
1314
+
1315
+ it('should update the toast offset when the page scrolls', () => {
1316
+ createWrapper({ title: 'Test App' })
1317
+ rects['Test App'] = { top: -26, bottom: 30 }
1318
+ window.dispatchEvent(new Event('scroll'))
1319
+
1320
+ expect(toastOffset()).toBe('calc(30px + 1rem)')
1321
+ })
1322
+
1323
+ it('should update the toast offset when the window resizes', () => {
1324
+ createWrapper({ title: 'Test App' })
1325
+ rects['Test App'] = { top: 16, bottom: 96 }
1326
+ window.dispatchEvent(new Event('resize'))
1327
+
1328
+ expect(toastOffset()).toBe('calc(96px + 1rem)')
1329
+ })
1330
+
1331
+ it('should update the toast offset when the top bar resizes', () => {
1332
+ let onResize: (() => void) | undefined
1333
+ vi.stubGlobal('ResizeObserver', class {
1334
+ constructor(callback: () => void) { onResize = callback }
1335
+ observe() {}
1336
+ unobserve() {}
1337
+ disconnect() {}
1338
+ })
1339
+ createWrapper({ title: 'Test App' })
1340
+ rects['Test App'] = { top: 16, bottom: 120 }
1341
+ onResize?.()
1342
+
1343
+ expect(toastOffset()).toBe('calc(120px + 1rem)')
1344
+ })
1345
+
1346
+ it('should stop listening for scroll when the last top bar unmounts', () => {
1347
+ const removeListener = vi.spyOn(window, 'removeEventListener')
1348
+ createWrapper({ title: 'Test App' }).unmount()
1349
+
1350
+ expect(removeListener).toHaveBeenCalledWith('scroll', expect.any(Function), expect.objectContaining({ capture: true }))
1351
+ })
1352
+ })
1256
1353
  })
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Publishes the top-most mounted AppTopBar's bottom edge as `--mint-toast-offset-top`
3
+ * on `<html>`, so the teleported toast container starts below the top bar.
4
+ */
5
+ const TOAST_OFFSET_PROPERTY = '--mint-toast-offset-top'
6
+ const LISTENER_OPTIONS = { capture: true, passive: true } as const
7
+ const topBars = new Set<HTMLElement>()
8
+ let observer: ResizeObserver | undefined
9
+
10
+ function syncToastOffset(): void {
11
+ const style = document.documentElement.style
12
+ if (topBars.size === 0) {
13
+ style.removeProperty(TOAST_OFFSET_PROPERTY)
14
+ return
15
+ }
16
+ const header = [...topBars]
17
+ .map((el) => el.getBoundingClientRect())
18
+ .reduce((top, rect) => (rect.top < top.top ? rect : top))
19
+ // Clamp so a top bar scrolled out of view leaves toasts at the plain 1rem gap.
20
+ style.setProperty(TOAST_OFFSET_PROPERTY, `calc(${Math.max(0, Math.round(header.bottom))}px + 1rem)`)
21
+ }
22
+
23
+ /** Track a top bar element; returns the cleanup to call on unmount. */
24
+ export function trackTopBarForToasts(el: HTMLElement): () => void {
25
+ if (topBars.size === 0) {
26
+ // Capture so scrolling any container that holds the top bar also moves the toasts.
27
+ window.addEventListener('scroll', syncToastOffset, LISTENER_OPTIONS)
28
+ window.addEventListener('resize', syncToastOffset, LISTENER_OPTIONS)
29
+ if (typeof ResizeObserver !== 'undefined') observer = new ResizeObserver(syncToastOffset)
30
+ }
31
+ topBars.add(el)
32
+ observer?.observe(el)
33
+ syncToastOffset()
34
+ return () => {
35
+ topBars.delete(el)
36
+ observer?.unobserve(el)
37
+ if (topBars.size === 0) {
38
+ window.removeEventListener('scroll', syncToastOffset, LISTENER_OPTIONS)
39
+ window.removeEventListener('resize', syncToastOffset, LISTENER_OPTIONS)
40
+ observer?.disconnect()
41
+ observer = undefined
42
+ }
43
+ syncToastOffset()
44
+ }
45
+ }
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  /** Full application top bar with brand logo, page selector or plugin switcher, centered pill nav, experiment popover, and avatar menu. */
3
- import { ref, computed, inject, defineAsyncComponent } from 'vue'
3
+ import { ref, computed, inject, defineAsyncComponent, onMounted, onBeforeUnmount } from 'vue'
4
4
  import type {
5
5
  PillNavOption,
6
6
  TopBarSettingsConfig,
@@ -33,6 +33,7 @@ import {
33
33
  currentItemIdFromLocation,
34
34
  pluginNavItemToPageSelectorItem,
35
35
  } from './AppTopBar.navigation'
36
+ import { trackTopBarForToasts } from './AppTopBar.toastOffset'
36
37
 
37
38
  interface Props {
38
39
  /** App or plugin title shown in the left title group when no page selector is present. */
@@ -119,6 +120,12 @@ const emit = defineEmits<{
119
120
  }>()
120
121
 
121
122
  const settingsOpen = ref(false)
123
+ const headerEl = ref<HTMLElement | null>(null)
124
+ let untrackToastOffset: (() => void) | undefined
125
+ onMounted(() => {
126
+ if (headerEl.value) untrackToastOffset = trackTopBarForToasts(headerEl.value)
127
+ })
128
+ onBeforeUnmount(() => untrackToastOffset?.())
122
129
  const { isIntegrated, plugin } = usePlatformContext()
123
130
  const isStandalone = computed(() => !isIntegrated.value)
124
131
  const presenceScope = computed(() => plugin.value?.id || plugin.value?.name || '')
@@ -194,6 +201,7 @@ const effectiveCurrentPageSelectorId = computed(() =>
194
201
 
195
202
  <template>
196
203
  <header
204
+ ref="headerEl"
197
205
  :class="[
198
206
  'mint-topbar',
199
207
  `mint-topbar--${props.variant}`,
@@ -2,7 +2,8 @@
2
2
 
3
3
  .mint-toast__container {
4
4
  position: fixed;
5
- top: 1rem;
5
+ /* AppTopBar publishes its bottom edge + 1rem so toasts clear its controls. */
6
+ top: var(--mint-toast-offset-top, 1rem);
6
7
  right: 1rem;
7
8
  z-index: 9999;
8
9
  display: flex;