@morscherlab/mint-sdk 1.1.4 → 1.1.6
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/{EmptyState-LMPS3keb.js → EmptyState-p90quq8Z.js} +2 -2
- package/dist/{EmptyState-LMPS3keb.js.map → EmptyState-p90quq8Z.js.map} +1 -1
- package/dist/{ExperimentPopover-D6_RGzTY.js → ExperimentPopover-tLwJ7XF4.js} +3 -3
- package/dist/{ExperimentPopover-D6_RGzTY.js.map → ExperimentPopover-tLwJ7XF4.js.map} +1 -1
- package/dist/{ExperimentSelectorModal-DcQ38zua.js → ExperimentSelectorModal-B6P2msT4.js} +12 -5
- package/dist/ExperimentSelectorModal-B6P2msT4.js.map +1 -0
- package/dist/{SettingsModal-Ccq2z-Ms.js → SettingsModal-D7LFUokT.js} +100 -156
- package/dist/SettingsModal-D7LFUokT.js.map +1 -0
- package/dist/__tests__/components/ActionMenu.test.d.ts +1 -0
- package/dist/__tests__/components/BaseModal.test.d.ts +1 -0
- package/dist/components/ActionMenu.vue.d.ts +21 -0
- package/dist/components/BaseModal.vue.d.ts +17 -1
- package/dist/components/GeneratedPluginView.vue.d.ts +3 -3
- package/dist/components/GeneratedResultRenderer.vue.d.ts +3 -3
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +8 -8
- package/dist/{components-CV5zuWa8.js → components-Dra_ac_5.js} +350 -71
- package/dist/components-Dra_ac_5.js.map +1 -0
- package/dist/composables/controlSchemaDoseDesign.d.ts +8 -1
- package/dist/composables/controlSchemaModel.d.ts +10 -1
- package/dist/composables/index.js +8 -8
- package/dist/composables/useAnalysisArtifacts.d.ts +9 -1
- package/dist/composables/useAppExperiment.d.ts +7 -0
- package/dist/composables/useBioTemplatePackWorkspace.d.ts +9 -1
- package/dist/composables/useControlWorkspace.d.ts +9 -1
- package/dist/composables/useGeneratedAnalysis.d.ts +9 -0
- package/dist/{composables-CW8NRXLB.js → composables-CRR_JxQD.js} +4 -4
- package/dist/{composables-CW8NRXLB.js.map → composables-CRR_JxQD.js.map} +1 -1
- package/dist/{experiment-utils-7Yw-h0tD.js → experiment-utils-CLnzPPq5.js} +170 -13
- package/dist/experiment-utils-CLnzPPq5.js.map +1 -0
- package/dist/index.js +13 -13
- package/dist/install.js +7 -7
- package/dist/styles.css +764 -610
- package/dist/templates/index.js +4 -4
- package/dist/{templates-CJRFiWUX.js → templates-Cr-48-u2.js} +2 -2
- package/dist/{templates-CJRFiWUX.js.map → templates-Cr-48-u2.js.map} +1 -1
- package/dist/types/components.d.ts +32 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/{useControlSchema-BEAgLyyx.js → useControlSchema-BMVb2XIQ.js} +58 -10
- package/dist/useControlSchema-BMVb2XIQ.js.map +1 -0
- package/dist/{useDropdownState-Be-ug_ci.js → useDropdownState-DSb3BC3N.js} +2 -2
- package/dist/{useDropdownState-Be-ug_ci.js.map → useDropdownState-DSb3BC3N.js.map} +1 -1
- package/dist/{useExperimentSelector-DFg_iEHI.js → useExperimentSelector-D0pjj9WH.js} +3 -3
- package/dist/{useExperimentSelector-DFg_iEHI.js.map → useExperimentSelector-D0pjj9WH.js.map} +1 -1
- package/dist/{useFormBuilder-CR9pBApW.js → useFormBuilder-DG3HOFGA.js} +2 -2
- package/dist/{useFormBuilder-CR9pBApW.js.map → useFormBuilder-DG3HOFGA.js.map} +1 -1
- package/dist/{useProtocolTemplates-DXeIHMwV.js → useProtocolTemplates-BMt0LlXf.js} +21 -6
- package/dist/useProtocolTemplates-BMt0LlXf.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/components/ActionMenu.test.ts +99 -0
- package/src/__tests__/components/BaseModal.test.ts +133 -0
- package/src/__tests__/components/SettingsModal.test.ts +9 -7
- package/src/__tests__/docs/extractDocsComponents.test.ts +15 -1
- package/src/__tests__/docs/extractDocsParsing.test.ts +22 -0
- package/src/__tests__/docs/frontendDocsCatalog.test.ts +80 -0
- package/src/components/ActionMenu.story.vue +26 -0
- package/src/components/ActionMenu.vue +212 -0
- package/src/components/AppSidebar.vue +2 -13
- package/src/components/ArtifactSelectorModal.vue +8 -1
- package/src/components/BaseModal.story.vue +107 -1
- package/src/components/BaseModal.vue +152 -4
- package/src/components/BioTemplateExperimentWorkspaceView.vue +8 -1
- package/src/components/BioTemplatePackWorkspaceView.vue +8 -1
- package/src/components/BioTemplatePresetWorkspaceView.vue +8 -1
- package/src/components/BioTemplateRenderer.vue +8 -1
- package/src/components/ComponentBindingRenderer.vue +8 -1
- package/src/components/ControlWorkspaceView.vue +8 -1
- package/src/components/DoseDesignWorkspaceView.vue +8 -1
- package/src/components/ExperimentSelectorModal.vue +8 -1
- package/src/components/FormBuilder.vue +5 -0
- package/src/components/GeneratedPluginView.vue +12 -2
- package/src/components/GeneratedResultRenderer.vue +12 -2
- package/src/components/JobsStatusTray.vue +8 -1
- package/src/components/PluginWorkspaceView.vue +10 -1
- package/src/components/SettingsModal.vue +102 -149
- package/src/components/index.ts +1 -0
- package/src/composables/controlSchemaDoseDesign.ts +8 -1
- package/src/composables/controlSchemaModel.ts +10 -1
- package/src/composables/useAnalysisArtifacts.ts +9 -1
- package/src/composables/useAppExperiment.ts +7 -0
- package/src/composables/useBioTemplatePackWorkspace.ts +9 -1
- package/src/composables/useControlWorkspace.ts +9 -1
- package/src/composables/useGeneratedAnalysis.ts +9 -0
- package/src/styles/components/action-menu.css +59 -0
- package/src/styles/components/icon-button.css +7 -3
- package/src/styles/components/modal.css +293 -0
- package/src/styles/components/settings-modal.css +3 -273
- package/src/styles/index.css +1 -0
- package/src/types/components.ts +38 -0
- package/src/types/index.ts +3 -0
- package/dist/ExperimentSelectorModal-DcQ38zua.js.map +0 -1
- package/dist/SettingsModal-Ccq2z-Ms.js.map +0 -1
- package/dist/components-CV5zuWa8.js.map +0 -1
- package/dist/experiment-utils-7Yw-h0tD.js.map +0 -1
- package/dist/useControlSchema-BEAgLyyx.js.map +0 -1
- package/dist/useProtocolTemplates-DXeIHMwV.js.map +0 -1
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Complete ControlWorkspaceView page shell for WellPlate + DoseCalculator dose design.
|
|
4
|
+
*
|
|
5
|
+
* @example Recommended
|
|
6
|
+
* ```vue
|
|
7
|
+
* <DoseDesignWorkspaceView v-model="values" title="Dose Design" />
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
3
10
|
import { computed } from 'vue'
|
|
4
11
|
import type { TopBarVariant } from '../types'
|
|
5
12
|
import type { FormEnhancements } from '../types/form-builder'
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Modal for searching and selecting an experiment from the platform list with filters and keyboard nav.
|
|
4
|
+
*
|
|
5
|
+
* @example Recommended
|
|
6
|
+
* ```vue
|
|
7
|
+
* <ExperimentSelectorModal v-model="open" :allowed-experiment-types="['mass-spectrometry']" />
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
3
10
|
import { ref, reactive, computed, watch, nextTick } from 'vue'
|
|
4
11
|
import type { ModalSize, ExperimentSummary, ExperimentFilters } from '../types'
|
|
5
12
|
import { useExperimentSelector } from '../composables/useExperimentSelector'
|
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
* - `section:<id>` — replace an entire section body
|
|
22
22
|
* - `section:<id>:after` — inject content after a section
|
|
23
23
|
* - `actions` — replace the default FormActions bar when `showActions` is true
|
|
24
|
+
*
|
|
25
|
+
* @example Recommended
|
|
26
|
+
* ```vue
|
|
27
|
+
* <FormBuilder v-model="values" :model="formModel" />
|
|
28
|
+
* ```
|
|
24
29
|
*/
|
|
25
30
|
import { ref, computed, watch } from 'vue'
|
|
26
31
|
import type { FormSchema, FormEnhancements, UseFormBuilderReturn } from '../types/form-builder'
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Standard generated workspace for a plugin that exposes an SDK job manifest.
|
|
4
|
+
*
|
|
5
|
+
* @example Recommended
|
|
6
|
+
* ```vue
|
|
7
|
+
* <GeneratedPluginView base-url="/api/peak-qc" />
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
2
10
|
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
|
3
11
|
|
|
4
12
|
import { generatedJobDefaults, generatedJobFormSchema, normalizeGeneratedJobInput } from '../generated/schema'
|
|
@@ -20,11 +28,13 @@ import JobsStatusTray from './JobsStatusTray.vue'
|
|
|
20
28
|
import LoadingSpinner from './LoadingSpinner.vue'
|
|
21
29
|
import PluginWorkspaceView from './PluginWorkspaceView.vue'
|
|
22
30
|
|
|
23
|
-
|
|
31
|
+
interface Props {
|
|
24
32
|
baseUrl: string
|
|
25
33
|
showStandaloneLabel?: boolean
|
|
26
34
|
transport?: GeneratedAnalysisTransport
|
|
27
|
-
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
28
38
|
showStandaloneLabel: false,
|
|
29
39
|
})
|
|
30
40
|
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Render standardized text, table, image, Plotly, JSON, and artifact job results.
|
|
4
|
+
*
|
|
5
|
+
* @example Recommended
|
|
6
|
+
* ```vue
|
|
7
|
+
* <GeneratedResultRenderer :result="result" />
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
2
10
|
import { computed } from 'vue'
|
|
3
11
|
|
|
4
12
|
import type { DataFrameColumn } from '../types'
|
|
@@ -7,9 +15,11 @@ import ChartContainer from './ChartContainer.vue'
|
|
|
7
15
|
import DataFrame from './DataFrame.vue'
|
|
8
16
|
import GeneratedPlotlyResult from './GeneratedPlotlyResult.vue'
|
|
9
17
|
|
|
10
|
-
|
|
18
|
+
interface Props {
|
|
11
19
|
result: GeneratedAnalysisResult
|
|
12
|
-
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const props = defineProps<Props>()
|
|
13
23
|
|
|
14
24
|
const tableColumns = computed<DataFrameColumn[]>(() => {
|
|
15
25
|
if (props.result.kind !== 'table') return []
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Floating, source-driven job status tray for plugin and platform workflows.
|
|
4
|
+
*
|
|
5
|
+
* @example Recommended
|
|
6
|
+
* ```vue
|
|
7
|
+
* <JobsStatusTray :source="jobs" />
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
3
10
|
import { computed, nextTick, ref, toRef, useId } from 'vue'
|
|
4
11
|
|
|
5
12
|
import {
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Standard plugin workspace shell that combines AppLayout, AppTopBar, and AppSidebar with LEAF-style defaults.
|
|
4
|
+
*
|
|
5
|
+
* @example Recommended
|
|
6
|
+
* ```vue
|
|
7
|
+
* <PluginWorkspaceView title="Analysis" experiment-shell>
|
|
8
|
+
* <router-view />
|
|
9
|
+
* </PluginWorkspaceView>
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
3
12
|
import { computed, useSlots } from 'vue'
|
|
4
13
|
import type {
|
|
5
14
|
AccountMenuItem,
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Layout: `horizontal` (underline tabs, default) or `vertical` (sidebar rail
|
|
19
19
|
* with optional icons + descriptions, recommended for 5+ groups).
|
|
20
|
+
*
|
|
21
|
+
* @example Recommended
|
|
22
|
+
* ```vue
|
|
23
|
+
* <SettingsModal v-model="open" v-model:values="values" :model="settingsModel" />
|
|
24
|
+
* ```
|
|
20
25
|
*/
|
|
21
26
|
import { ref, computed, watch } from 'vue'
|
|
22
27
|
import BaseModal from './BaseModal.vue'
|
|
@@ -45,6 +50,8 @@ import type {
|
|
|
45
50
|
ThemeMode,
|
|
46
51
|
ColorPalette,
|
|
47
52
|
TableDensity,
|
|
53
|
+
ModalLayout,
|
|
54
|
+
ModalTab,
|
|
48
55
|
SettingsTab,
|
|
49
56
|
SettingsTabInput,
|
|
50
57
|
SettingsModalLayout,
|
|
@@ -107,11 +114,6 @@ const { user: platformUser } = usePlatformContext()
|
|
|
107
114
|
|
|
108
115
|
const isVertical = computed(() => props.layout === 'vertical')
|
|
109
116
|
|
|
110
|
-
// Per-instance prefix for ARIA tab/tabpanel id wiring. Stable across renders.
|
|
111
|
-
const uid = `mint-settings-${Math.random().toString(36).slice(2, 9)}`
|
|
112
|
-
const tabId = (id: string) => `${uid}-tab-${id}`
|
|
113
|
-
const panelId = (id: string) => `${uid}-panel-${id}`
|
|
114
|
-
|
|
115
117
|
const resolvedModel = computed<ControlModelBinding | undefined>(() => {
|
|
116
118
|
return resolveControlModel(props.model)
|
|
117
119
|
})
|
|
@@ -223,8 +225,21 @@ const allTabs = computed<SettingsTab[]>(() => {
|
|
|
223
225
|
|
|
224
226
|
const activeTab = ref(allTabs.value[0]?.id || APPEARANCE_TAB_ID)
|
|
225
227
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
+
// BaseModal owns the tab chrome: 'vertical' keeps its public name and maps to
|
|
229
|
+
// the shell rail; horizontal maps to the shell strip (plain when single-tab).
|
|
230
|
+
const baseLayout = computed<ModalLayout>(() => {
|
|
231
|
+
if (isVertical.value) return 'rail'
|
|
232
|
+
return allTabs.value.length > 1 ? 'tabs' : 'plain'
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
const modalTabs = computed<ModalTab[]>(() =>
|
|
236
|
+
allTabs.value.map((tab) => ({
|
|
237
|
+
id: tab.id,
|
|
238
|
+
label: tab.label,
|
|
239
|
+
icon: tab.icon,
|
|
240
|
+
description: tab.description,
|
|
241
|
+
meta: tab.meta,
|
|
242
|
+
})),
|
|
228
243
|
)
|
|
229
244
|
|
|
230
245
|
// If the active tab vanishes (group hidden by condition), drop back to the first available.
|
|
@@ -278,166 +293,104 @@ function canShowForCurrentUser(item: AccessControlled): boolean {
|
|
|
278
293
|
:model-value="modelValue"
|
|
279
294
|
:title="title"
|
|
280
295
|
:size="size"
|
|
296
|
+
:layout="baseLayout"
|
|
297
|
+
:tabs="modalTabs"
|
|
298
|
+
:active-tab="activeTab"
|
|
299
|
+
@update:active-tab="activeTab = $event"
|
|
281
300
|
@update:model-value="emit('update:modelValue', $event)"
|
|
282
301
|
@close="handleClose"
|
|
283
302
|
>
|
|
284
|
-
|
|
303
|
+
<!-- Body: the active pane only — tab strip / rail / footer are BaseModal's shell. -->
|
|
304
|
+
<div class="mint-settings-modal">
|
|
285
305
|
<div
|
|
286
|
-
v-if="
|
|
287
|
-
class="mint-settings-
|
|
306
|
+
v-if="isSchemaDriven && activeGroup"
|
|
307
|
+
class="mint-settings-modal__group-grid"
|
|
308
|
+
:style="{ '--mint-settings-cols': activeGroup.columns ?? 1 }"
|
|
288
309
|
>
|
|
289
|
-
<
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
310
|
+
<template v-for="field in activeGroupVisibleFields" :key="field.name">
|
|
311
|
+
<div :style="field.colSpan ? { gridColumn: `span ${field.colSpan}` } : undefined">
|
|
312
|
+
<slot
|
|
313
|
+
:name="`field:${field.name}`"
|
|
314
|
+
:field="field"
|
|
315
|
+
:form="builder.form"
|
|
316
|
+
:field-props="builder.form.getFieldProps(field.name)"
|
|
317
|
+
>
|
|
318
|
+
<FormFieldRendererInternal
|
|
319
|
+
:field="field"
|
|
320
|
+
:resolved-props="builder.getResolvedFieldProps(field)"
|
|
321
|
+
:form="builder.form"
|
|
322
|
+
/>
|
|
323
|
+
</slot>
|
|
324
|
+
</div>
|
|
325
|
+
</template>
|
|
298
326
|
</div>
|
|
299
327
|
|
|
300
|
-
<
|
|
301
|
-
v-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
>
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
</
|
|
322
|
-
|
|
323
|
-
<span class="mint-settings-modal__rail-item-label">{{ tab.label }}</span>
|
|
324
|
-
<span v-if="tab.description" class="mint-settings-modal__rail-item-description">
|
|
325
|
-
{{ tab.description }}
|
|
326
|
-
</span>
|
|
327
|
-
</span>
|
|
328
|
-
</button>
|
|
329
|
-
</div>
|
|
328
|
+
<template v-else-if="!isSchemaDriven">
|
|
329
|
+
<template v-for="tab in manualTabs" :key="tab.id">
|
|
330
|
+
<div v-show="activeTab === tab.id">
|
|
331
|
+
<slot :name="`tab-${tab.id}`" />
|
|
332
|
+
</div>
|
|
333
|
+
</template>
|
|
334
|
+
</template>
|
|
335
|
+
|
|
336
|
+
<div v-if="showAppearance" v-show="activeTab === APPEARANCE_TAB_ID">
|
|
337
|
+
<div class="mint-settings-modal__section">
|
|
338
|
+
<div class="mint-settings-modal__section-label">Theme</div>
|
|
339
|
+
<div class="mint-settings-modal__option-group">
|
|
340
|
+
<button
|
|
341
|
+
v-for="opt in themeOptions"
|
|
342
|
+
:key="opt.value"
|
|
343
|
+
type="button"
|
|
344
|
+
:class="['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.theme === opt.value }]"
|
|
345
|
+
@click="settings.theme = opt.value"
|
|
346
|
+
>
|
|
347
|
+
{{ opt.label }}
|
|
348
|
+
</button>
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
330
351
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
>
|
|
344
|
-
<h4 class="mint-settings-modal__pane-title">{{ activeTabMeta.label }}</h4>
|
|
345
|
-
<p v-if="activeTabMeta.description" class="mint-settings-modal__pane-subtitle">
|
|
346
|
-
{{ activeTabMeta.description }}
|
|
347
|
-
</p>
|
|
348
|
-
</header>
|
|
349
|
-
|
|
350
|
-
<div :class="isVertical ? 'mint-settings-modal__pane-body' : null">
|
|
351
|
-
<div
|
|
352
|
-
v-if="isSchemaDriven && activeGroup"
|
|
353
|
-
class="mint-settings-modal__group-grid"
|
|
354
|
-
:style="{ '--mint-settings-cols': activeGroup.columns ?? 1 }"
|
|
355
|
-
>
|
|
356
|
-
<template v-for="field in activeGroupVisibleFields" :key="field.name">
|
|
357
|
-
<div :style="field.colSpan ? { gridColumn: `span ${field.colSpan}` } : undefined">
|
|
358
|
-
<slot
|
|
359
|
-
:name="`field:${field.name}`"
|
|
360
|
-
:field="field"
|
|
361
|
-
:form="builder.form"
|
|
362
|
-
:field-props="builder.form.getFieldProps(field.name)"
|
|
363
|
-
>
|
|
364
|
-
<FormFieldRendererInternal
|
|
365
|
-
:field="field"
|
|
366
|
-
:resolved-props="builder.getResolvedFieldProps(field)"
|
|
367
|
-
:form="builder.form"
|
|
368
|
-
/>
|
|
369
|
-
</slot>
|
|
370
|
-
</div>
|
|
371
|
-
</template>
|
|
352
|
+
<div class="mint-settings-modal__section">
|
|
353
|
+
<div class="mint-settings-modal__section-label">Color Palette</div>
|
|
354
|
+
<div class="mint-settings-modal__option-group">
|
|
355
|
+
<button
|
|
356
|
+
v-for="(palette, key) in colorPalettes"
|
|
357
|
+
:key="key"
|
|
358
|
+
type="button"
|
|
359
|
+
:class="['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.colorPalette === key }]"
|
|
360
|
+
@click="settings.colorPalette = key as ColorPalette"
|
|
361
|
+
>
|
|
362
|
+
{{ palette.name }}
|
|
363
|
+
</button>
|
|
372
364
|
</div>
|
|
365
|
+
</div>
|
|
373
366
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
<button
|
|
387
|
-
v-for="opt in themeOptions"
|
|
388
|
-
:key="opt.value"
|
|
389
|
-
type="button"
|
|
390
|
-
:class="['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.theme === opt.value }]"
|
|
391
|
-
@click="settings.theme = opt.value"
|
|
392
|
-
>
|
|
393
|
-
{{ opt.label }}
|
|
394
|
-
</button>
|
|
395
|
-
</div>
|
|
396
|
-
</div>
|
|
397
|
-
|
|
398
|
-
<div class="mint-settings-modal__section">
|
|
399
|
-
<div class="mint-settings-modal__section-label">Color Palette</div>
|
|
400
|
-
<div class="mint-settings-modal__option-group">
|
|
401
|
-
<button
|
|
402
|
-
v-for="(palette, key) in colorPalettes"
|
|
403
|
-
:key="key"
|
|
404
|
-
type="button"
|
|
405
|
-
:class="['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.colorPalette === key }]"
|
|
406
|
-
@click="settings.colorPalette = key as ColorPalette"
|
|
407
|
-
>
|
|
408
|
-
{{ palette.name }}
|
|
409
|
-
</button>
|
|
410
|
-
</div>
|
|
411
|
-
</div>
|
|
412
|
-
|
|
413
|
-
<div class="mint-settings-modal__section">
|
|
414
|
-
<div class="mint-settings-modal__section-label">Table Density</div>
|
|
415
|
-
<div class="mint-settings-modal__option-group">
|
|
416
|
-
<button
|
|
417
|
-
v-for="opt in densityOptions"
|
|
418
|
-
:key="opt.value"
|
|
419
|
-
type="button"
|
|
420
|
-
:class="['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.tableDensity === opt.value }]"
|
|
421
|
-
@click="settings.tableDensity = opt.value"
|
|
422
|
-
>
|
|
423
|
-
{{ opt.label }}
|
|
424
|
-
</button>
|
|
425
|
-
</div>
|
|
426
|
-
<p class="mint-settings-modal__note">Adjusts row height in data tables.</p>
|
|
427
|
-
</div>
|
|
428
|
-
|
|
429
|
-
<slot name="appearance" />
|
|
367
|
+
<div class="mint-settings-modal__section">
|
|
368
|
+
<div class="mint-settings-modal__section-label">Table Density</div>
|
|
369
|
+
<div class="mint-settings-modal__option-group">
|
|
370
|
+
<button
|
|
371
|
+
v-for="opt in densityOptions"
|
|
372
|
+
:key="opt.value"
|
|
373
|
+
type="button"
|
|
374
|
+
:class="['mint-settings-modal__option-btn', { 'mint-settings-modal__option-btn--active': settings.tableDensity === opt.value }]"
|
|
375
|
+
@click="settings.tableDensity = opt.value"
|
|
376
|
+
>
|
|
377
|
+
{{ opt.label }}
|
|
378
|
+
</button>
|
|
430
379
|
</div>
|
|
380
|
+
<p class="mint-settings-modal__note">Adjusts row height in data tables.</p>
|
|
431
381
|
</div>
|
|
432
|
-
|
|
382
|
+
|
|
383
|
+
<slot name="appearance" />
|
|
384
|
+
</div>
|
|
433
385
|
</div>
|
|
434
|
-
|
|
386
|
+
|
|
387
|
+
<template v-if="$slots.footer" #footer>
|
|
435
388
|
<slot
|
|
436
389
|
name="footer"
|
|
437
390
|
:values="builder.form.data"
|
|
438
391
|
:close="handleClose"
|
|
439
392
|
/>
|
|
440
|
-
</
|
|
393
|
+
</template>
|
|
441
394
|
</BaseModal>
|
|
442
395
|
</template>
|
|
443
396
|
|
package/src/components/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ export { default as AlertBox } from './AlertBox.vue'
|
|
|
30
30
|
export { default as AppToastContainer } from './AppToastContainer.vue'
|
|
31
31
|
|
|
32
32
|
// Action components
|
|
33
|
+
export { default as ActionMenu } from './ActionMenu.vue'
|
|
33
34
|
export { default as IconButton } from './IconButton.vue'
|
|
34
35
|
export { default as ThemeToggle } from './ThemeToggle.vue'
|
|
35
36
|
|
|
@@ -86,7 +86,14 @@ export function defineWellPlateDoseComponentBindings<TValues extends Record<stri
|
|
|
86
86
|
]
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
/**
|
|
89
|
+
/**
|
|
90
|
+
* Return a complete ControlWorkspaceView model for WellPlate + DoseCalculator dose design.
|
|
91
|
+
*
|
|
92
|
+
* @example Recommended
|
|
93
|
+
* ```ts
|
|
94
|
+
* const workspaceModel = defineDoseDesignControlModel({ selectedWells: ['A1', 'A2'] })
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
90
97
|
export function defineDoseDesignControlModel(
|
|
91
98
|
options: DoseDesignControlModelOptions = {},
|
|
92
99
|
): ControlModelBinding {
|
|
@@ -33,7 +33,16 @@ export function resolveControlModel(
|
|
|
33
33
|
return isControlModelBinding(model) ? model : defineControlModel(model)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Create a complete workspace component binding from a simple controls data model for ControlWorkspaceView, generated forms, and sidebars.
|
|
38
|
+
*
|
|
39
|
+
* @example Recommended
|
|
40
|
+
* ```ts
|
|
41
|
+
* const workspaceModel = defineControlModel({
|
|
42
|
+
* controls: { threshold: { type: 'number', default: 0.05 } },
|
|
43
|
+
* })
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
37
46
|
export function defineControlModel(model: ControlModel): ControlModelBinding {
|
|
38
47
|
const {
|
|
39
48
|
controls: rootControls,
|
|
@@ -85,7 +85,15 @@ export interface UseAnalysisArtifactsReturn {
|
|
|
85
85
|
clear: () => void
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Lists and manages analysis artifacts through the user-facing platform API.
|
|
90
|
+
*
|
|
91
|
+
* @example Recommended
|
|
92
|
+
* ```ts
|
|
93
|
+
* const artifacts = useAnalysisArtifacts({ pluginId: 'LEAF' })
|
|
94
|
+
* await artifacts.list()
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
89
97
|
export function useAnalysisArtifacts(
|
|
90
98
|
options: UseAnalysisArtifactsOptions = {},
|
|
91
99
|
): UseAnalysisArtifactsReturn {
|
|
@@ -93,6 +93,13 @@ export const APP_EXPERIMENT_KEY: InjectionKey<AppExperimentState> = Symbol('app-
|
|
|
93
93
|
* The composable never writes a selection of its own. `ExperimentSelectorModal`
|
|
94
94
|
* commits to the store and this reads back, so a displayed name can only ever
|
|
95
95
|
* have come from the platform.
|
|
96
|
+
*
|
|
97
|
+
* @example Recommended
|
|
98
|
+
* ```ts
|
|
99
|
+
* const appExperiment = useAppExperiment({
|
|
100
|
+
* onSelect: experiment => loadExperiment(experiment.id),
|
|
101
|
+
* })
|
|
102
|
+
* ```
|
|
96
103
|
*/
|
|
97
104
|
export function useAppExperiment(options: UseAppExperimentOptions = {}): UseAppExperimentReturn {
|
|
98
105
|
const store = useExperimentStore()
|
|
@@ -79,7 +79,15 @@ export interface UseBioTemplatePackWorkspaceReturn {
|
|
|
79
79
|
requireCurrentExperimentId: UseTemplateCollectionReturn['requireCurrentExperimentId']
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Create a complete workspace for a curated biology template pack from a single pack id.
|
|
84
|
+
*
|
|
85
|
+
* @example Recommended
|
|
86
|
+
* ```ts
|
|
87
|
+
* const pack = useBioTemplatePackWorkspace('cell-culture-screen')
|
|
88
|
+
* await pack.loadCurrent()
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
83
91
|
export function useBioTemplatePackWorkspace(
|
|
84
92
|
packId: TemplatePackId | string,
|
|
85
93
|
options: UseBioTemplatePackWorkspaceOptions = {},
|
|
@@ -44,7 +44,15 @@ import type {
|
|
|
44
44
|
UseControlWorkspaceReturn,
|
|
45
45
|
} from './controlSchemaTypes'
|
|
46
46
|
|
|
47
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Prepare shared reactive values plus AppTopBar/AppSidebar/FormBuilder bindings from one simple controls data model.
|
|
49
|
+
*
|
|
50
|
+
* @example Recommended
|
|
51
|
+
* ```ts
|
|
52
|
+
* const workspace = useControlWorkspace(workspaceModel)
|
|
53
|
+
* const { form, sidebar, topBar } = workspace.bindings
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
48
56
|
export function useControlWorkspace<TControls extends ControlSchema>(
|
|
49
57
|
controlsOrModel: TControls | (ControlModelBinding & { controls: TControls }),
|
|
50
58
|
options: ControlWorkspaceOptions = {},
|
|
@@ -161,6 +161,15 @@ export function useGeneratedAnalysis(
|
|
|
161
161
|
return createPluginJobs(options, '/generated/manifest')
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Connect a custom plugin frontend to the SDK-owned job lifecycle.
|
|
166
|
+
*
|
|
167
|
+
* @example Recommended
|
|
168
|
+
* ```ts
|
|
169
|
+
* const jobs = usePluginJobs({ baseUrl: '/api/leaf/jobs' })
|
|
170
|
+
* await jobs.initialize()
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
164
173
|
export function usePluginJobs(
|
|
165
174
|
options: UsePluginJobsOptions,
|
|
166
175
|
): UsePluginJobsReturn {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* ActionMenu — compact square trigger with a teleported, viewport-safe menu. */
|
|
2
|
+
|
|
3
|
+
.mint-action-menu {
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
flex-shrink: 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.mint-action-menu__trigger-icon {
|
|
9
|
+
width: 0.875rem;
|
|
10
|
+
height: 0.875rem;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.mint-action-menu__panel {
|
|
14
|
+
position: fixed;
|
|
15
|
+
z-index: 1000;
|
|
16
|
+
display: flex;
|
|
17
|
+
min-width: 10rem;
|
|
18
|
+
max-width: min(18rem, calc(100vw - 1rem));
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
padding: 0.25rem;
|
|
21
|
+
border: 1px solid var(--border-color);
|
|
22
|
+
border-radius: var(--radius-md);
|
|
23
|
+
background: var(--bg-secondary);
|
|
24
|
+
box-shadow: var(--shadow-md);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.mint-action-menu__item {
|
|
28
|
+
display: flex;
|
|
29
|
+
width: 100%;
|
|
30
|
+
align-items: center;
|
|
31
|
+
padding: 0.4375rem 0.625rem;
|
|
32
|
+
border: 0;
|
|
33
|
+
border-radius: 0.375rem;
|
|
34
|
+
background: transparent;
|
|
35
|
+
color: var(--text-primary);
|
|
36
|
+
font-family: inherit;
|
|
37
|
+
font-size: 0.78125rem;
|
|
38
|
+
line-height: 1.25;
|
|
39
|
+
text-align: left;
|
|
40
|
+
text-decoration: none;
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
transition: background-color var(--mint-transition);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.mint-action-menu__item:hover:not(:disabled),
|
|
46
|
+
.mint-action-menu__item:focus-visible {
|
|
47
|
+
outline: none;
|
|
48
|
+
background: var(--bg-hover);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.mint-action-menu__item--danger {
|
|
52
|
+
color: var(--mint-error);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.mint-action-menu__item:disabled {
|
|
56
|
+
color: var(--text-muted);
|
|
57
|
+
cursor: not-allowed;
|
|
58
|
+
opacity: var(--mint-disabled-opacity);
|
|
59
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
.mint-icon-button {
|
|
4
4
|
display: inline-flex;
|
|
5
|
+
flex-shrink: 0;
|
|
5
6
|
align-items: center;
|
|
6
7
|
justify-content: center;
|
|
7
8
|
border-radius: var(--radius-md);
|
|
@@ -126,18 +127,21 @@
|
|
|
126
127
|
|
|
127
128
|
/* Sizes */
|
|
128
129
|
.mint-icon-button--sm {
|
|
130
|
+
width: var(--form-height-sm);
|
|
131
|
+
height: var(--form-height-sm);
|
|
129
132
|
padding: 0.375rem;
|
|
130
|
-
min-height: var(--form-height-sm);
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
.mint-icon-button--md {
|
|
136
|
+
width: var(--form-height-md);
|
|
137
|
+
height: var(--form-height-md);
|
|
134
138
|
padding: 0.5rem;
|
|
135
|
-
min-height: var(--form-height-md);
|
|
136
139
|
}
|
|
137
140
|
|
|
138
141
|
.mint-icon-button--lg {
|
|
142
|
+
width: var(--form-height-lg);
|
|
143
|
+
height: var(--form-height-lg);
|
|
139
144
|
padding: 0.75rem;
|
|
140
|
-
min-height: var(--form-height-lg);
|
|
141
145
|
}
|
|
142
146
|
|
|
143
147
|
.mint-icon-button__icon--sm,
|