@morscherlab/mint-sdk 1.5.0-beta.11 → 1.5.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.
package/dist/styles.css CHANGED
@@ -122,12 +122,7 @@
122
122
  overflow-wrap: anywhere;
123
123
  }
124
124
 
125
- .mint-generated-workspace[data-v-7ba9e342] {
126
- min-width: 0;
127
- min-height: 0;
128
- overflow-y: auto;
129
- }
130
- .mint-generated-workspace__loading[data-v-7ba9e342] {
125
+ .mint-generated-workspace__loading[data-v-57d6f544] {
131
126
  display: flex;
132
127
  flex: 1;
133
128
  align-items: center;
@@ -135,12 +130,12 @@
135
130
  gap: 0.75rem;
136
131
  color: var(--text-secondary);
137
132
  }
138
- .mint-generated-workspace__resources[data-v-7ba9e342] {
133
+ .mint-generated-workspace__resources[data-v-57d6f544] {
139
134
  color: var(--text-muted);
140
135
  font-family: var(--font-mono);
141
136
  font-size: 0.75rem;
142
137
  }
143
- .mint-generated-workspace__description[data-v-7ba9e342] {
138
+ .mint-generated-workspace__description[data-v-57d6f544] {
144
139
  margin: 0 0 var(--mint-gutter);
145
140
  color: var(--text-secondary);
146
141
  }
@@ -1711,6 +1706,23 @@ button, input, select, textarea {
1711
1706
  .mint-layout--responsive-sidebar:has(.mint-topbar__sidebar-toggle) .mint-layout__sidebar-toggle {
1712
1707
  display: none;
1713
1708
  }
1709
+
1710
+ /* PluginWorkspaceView main card: one scrolling card that stacks the views' section cards. */
1711
+ .mint-container.mint-workspace-main {
1712
+ display: flex;
1713
+ flex-direction: column;
1714
+ gap: var(--mint-gutter);
1715
+ }
1716
+
1717
+ /*
1718
+ * Children start at their content height and never shrink, so the card scrolls instead of
1719
+ * squeezing them. Their own flex-grow stays: a `flex: 1` child (fill SectionCard, loading
1720
+ * state) still fills the free height when the content is short.
1721
+ */
1722
+ .mint-plugin-workspace-view .mint-container.mint-workspace-main > * {
1723
+ flex-basis: auto;
1724
+ flex-shrink: 0;
1725
+ }
1714
1726
  /* AppSidebar Component Styles */
1715
1727
 
1716
1728
  .mint-sidebar {
@@ -268,6 +268,12 @@ export interface PluginWorkspaceViewSpec {
268
268
  subtitle?: string;
269
269
  /** Floating layout style. Default true. */
270
270
  floating?: boolean;
271
+ /**
272
+ * Main pane surface. `'card'` puts the default slot in one scrolling card that stacks its
273
+ * children (`SectionCard`s) with the gutter; `'plain'` renders the slot as is, for full-bleed
274
+ * views. Default `'card'` when `floating`, else `'plain'`.
275
+ */
276
+ main?: 'card' | 'plain';
271
277
  /** Render the toast container. Default true; set false when the page renders its own. */
272
278
  toasts?: boolean;
273
279
  /** Show the experiment chip (popover, selector, save) in the top bar. Read once at setup, like `save`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morscherlab/mint-sdk",
3
- "version": "1.5.0-beta.11",
3
+ "version": "1.5.0-beta.13",
4
4
  "description": "MINT Platform SDK — Vue 3 components, composables, and types for plugin development. MINT = Mass-spec Infrastructure for Next-generation Toolkits.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -228,6 +228,23 @@ describe('PluginWorkspaceView', () => {
228
228
  expect(wrapper.findComponent(AppTopBar).exists()).toBe(false)
229
229
  })
230
230
 
231
+ it.each([
232
+ { spec: {}, card: true },
233
+ { spec: { main: 'plain' as const }, card: false },
234
+ { spec: { floating: false }, card: false },
235
+ { spec: { floating: false, main: 'card' as const }, card: true },
236
+ ])('should render the main card: $card when spec is $spec', ({ spec, card }) => {
237
+ const wrapper = mount(PluginWorkspaceView, {
238
+ props: { spec },
239
+ slots: { default: () => h('p', { class: 'view' }, 'View') },
240
+ global: globalOptions,
241
+ })
242
+
243
+ const main = wrapper.find('.mint-workspace-main')
244
+ expect(main.exists()).toBe(card)
245
+ expect(wrapper.find(card ? '.mint-workspace-main > .view' : '.mint-layout__main > .view').exists()).toBe(true)
246
+ })
247
+
231
248
  it.each([
232
249
  { toasts: undefined, rendered: true },
233
250
  { toasts: false, rendered: false },
@@ -22,7 +22,6 @@ import {
22
22
  type GeneratedAnalysisTransport,
23
23
  } from '../composables/useJobsEndpoint'
24
24
  import AlertBox from './AlertBox.vue'
25
- import AppContainer from './AppContainer.vue'
26
25
  import EmptyState from './EmptyState.vue'
27
26
  import GeneratedResultRenderer from './GeneratedResultRenderer.vue'
28
27
  import LoadingSpinner from './LoadingSpinner.vue'
@@ -181,47 +180,39 @@ onMounted(() => {
181
180
  @form-submit="submitJob"
182
181
  @job-select="selectJob"
183
182
  >
184
- <AppContainer direction="column" gap="var(--mint-gutter)" class="mint-generated-workspace">
185
- <div v-if="generated.isLoading.value" class="mint-generated-workspace__loading">
186
- <LoadingSpinner size="lg" />
187
- <span>Loading analysis workspace</span>
188
- </div>
189
-
190
- <template v-else>
191
- <AlertBox v-if="generated.error.value" tone="error">{{ generated.error.value }}</AlertBox>
192
-
193
- <SectionCard :title="activeDefinition?.title ?? 'Result'">
194
- <template v-if="activeDefinition" #actions>
195
- <span class="mint-generated-workspace__resources" aria-label="Job resource requirements">
196
- {{ resourceSummary(activeDefinition) }}
197
- </span>
198
- </template>
199
- <p v-if="activeDefinition?.description" class="mint-generated-workspace__description">
200
- {{ activeDefinition.description }}
201
- </p>
202
- <GeneratedResultRenderer
203
- v-if="currentResult"
204
- :result="currentResult"
205
- />
206
- <EmptyState
207
- v-else
208
- title="No result yet"
209
- description="Set the parameters and run the analysis. Jobs continue independently if you switch tabs."
210
- size="md"
211
- />
212
- </SectionCard>
213
- </template>
214
- </AppContainer>
183
+ <div v-if="generated.isLoading.value" class="mint-generated-workspace__loading">
184
+ <LoadingSpinner size="lg" />
185
+ <span>Loading analysis workspace</span>
186
+ </div>
187
+
188
+ <template v-else>
189
+ <AlertBox v-if="generated.error.value" tone="error">{{ generated.error.value }}</AlertBox>
190
+
191
+ <SectionCard :title="activeDefinition?.title ?? 'Result'">
192
+ <template v-if="activeDefinition" #actions>
193
+ <span class="mint-generated-workspace__resources" aria-label="Job resource requirements">
194
+ {{ resourceSummary(activeDefinition) }}
195
+ </span>
196
+ </template>
197
+ <p v-if="activeDefinition?.description" class="mint-generated-workspace__description">
198
+ {{ activeDefinition.description }}
199
+ </p>
200
+ <GeneratedResultRenderer
201
+ v-if="currentResult"
202
+ :result="currentResult"
203
+ />
204
+ <EmptyState
205
+ v-else
206
+ title="No result yet"
207
+ description="Set the parameters and run the analysis. Jobs continue independently if you switch tabs."
208
+ size="md"
209
+ />
210
+ </SectionCard>
211
+ </template>
215
212
  </PluginWorkspaceView>
216
213
  </template>
217
214
 
218
215
  <style scoped>
219
- .mint-generated-workspace {
220
- min-width: 0;
221
- min-height: 0;
222
- overflow-y: auto;
223
- }
224
-
225
216
  .mint-generated-workspace__loading {
226
217
  display: flex;
227
218
  flex: 1;
@@ -6,7 +6,9 @@
6
6
  * top bar, sidebar (a `defineControlModel()` result generates its sections),
7
7
  * the experiment chip and its save action. The active pill, view, sidebar
8
8
  * values and collapse state are `v-model`s. It renders the toast container
9
- * unless `spec.toasts` is false.
9
+ * unless `spec.toasts` is false. The default slot sits in one scrolling main
10
+ * card that stacks its children (`spec.main`, default `'card'` when floating);
11
+ * views put `SectionCard`s straight into the slot.
10
12
  *
11
13
  * @example Recommended
12
14
  * ```vue
@@ -25,6 +27,7 @@ import { useAppExperiment } from '../composables/useAppExperiment'
25
27
  import { useJobCenter } from '../composables/useJobCenter'
26
28
  import { usePluginWorkspace } from '../composables/usePluginWorkspace'
27
29
  import { warnDeprecatedOnce } from '../utils/deprecation'
30
+ import AppContainer from './AppContainer.vue'
28
31
  import AppToastContainer from './AppToastContainer.vue'
29
32
  import JobsStatusTray from './JobsStatusTray.vue'
30
33
  import WorkspaceControlsInternal from './internal/WorkspaceControlsInternal.vue'
@@ -177,7 +180,15 @@ function handleFormCancel(sectionId: string) {
177
180
  </template>
178
181
 
179
182
  <template #default>
180
- <WorkspaceControlsInternal :controls="controls">
183
+ <!-- The main pane is one card; the views' section cards stack inside it. -->
184
+ <AppContainer v-if="shell.mainCard" scrollable class="mint-workspace-main">
185
+ <WorkspaceControlsInternal :controls="controls">
186
+ <template #default="controlsScope">
187
+ <slot v-bind="controlsScope" />
188
+ </template>
189
+ </WorkspaceControlsInternal>
190
+ </AppContainer>
191
+ <WorkspaceControlsInternal v-else :controls="controls">
181
192
  <template #default="controlsScope">
182
193
  <slot v-bind="controlsScope" />
183
194
  </template>
@@ -42,6 +42,8 @@ export interface PluginWorkspaceShellModel {
42
42
  sidebarWidth?: string
43
43
  /** AppLayout floating style. */
44
44
  floating: boolean
45
+ /** Wrap the default slot in the SDK main card. */
46
+ mainCard: boolean
45
47
  /** Convert the sidebar into an SDK-owned mobile overlay below the AppLayout breakpoint. */
46
48
  responsiveSidebar: boolean
47
49
  /** Accessible label for the mobile sidebar toggle. */
@@ -126,6 +126,7 @@ export function usePluginWorkspace(options: UsePluginWorkspaceOptions): UsePlugi
126
126
  sidebarPosition: sidebar.position ?? 'left',
127
127
  sidebarWidth: sidebarCollapsed.value ? (sidebar.collapsedWidth ?? '3rem') : sidebar.width,
128
128
  floating: props.spec?.floating ?? true,
129
+ mainCard: (props.spec?.main ?? ((props.spec?.floating ?? true) ? 'card' : 'plain')) === 'card',
129
130
  responsiveSidebar: sidebar.responsive ?? true,
130
131
  sidebarToggleLabel: sidebar.toggleLabel ?? 'Open workspace sidebar',
131
132
  sidebarCloseLabel: sidebar.closeLabel ?? 'Close workspace sidebar',
@@ -187,3 +187,20 @@
187
187
  .mint-layout--responsive-sidebar:has(.mint-topbar__sidebar-toggle) .mint-layout__sidebar-toggle {
188
188
  display: none;
189
189
  }
190
+
191
+ /* PluginWorkspaceView main card: one scrolling card that stacks the views' section cards. */
192
+ .mint-container.mint-workspace-main {
193
+ display: flex;
194
+ flex-direction: column;
195
+ gap: var(--mint-gutter);
196
+ }
197
+
198
+ /*
199
+ * Children start at their content height and never shrink, so the card scrolls instead of
200
+ * squeezing them. Their own flex-grow stays: a `flex: 1` child (fill SectionCard, loading
201
+ * state) still fills the free height when the content is short.
202
+ */
203
+ .mint-plugin-workspace-view .mint-container.mint-workspace-main > * {
204
+ flex-basis: auto;
205
+ flex-shrink: 0;
206
+ }
@@ -324,6 +324,12 @@ export interface PluginWorkspaceViewSpec {
324
324
  subtitle?: string
325
325
  /** Floating layout style. Default true. */
326
326
  floating?: boolean
327
+ /**
328
+ * Main pane surface. `'card'` puts the default slot in one scrolling card that stacks its
329
+ * children (`SectionCard`s) with the gutter; `'plain'` renders the slot as is, for full-bleed
330
+ * views. Default `'card'` when `floating`, else `'plain'`.
331
+ */
332
+ main?: 'card' | 'plain'
327
333
  /** Render the toast container. Default true; set false when the page renders its own. */
328
334
  toasts?: boolean
329
335
  /** Show the experiment chip (popover, selector, save) in the top bar. Read once at setup, like `save`. */