@morscherlab/mint-sdk 1.0.63 → 1.1.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.
- package/README.md +119 -6
- package/dist/EmptyState-DgBRoYAy.js +647 -0
- package/dist/EmptyState-DgBRoYAy.js.map +1 -0
- package/dist/{ExperimentPopover-BbPkIFsI.js → ExperimentPopover-BFw3QX8L.js} +10 -9
- package/dist/{ExperimentPopover-BbPkIFsI.js.map → ExperimentPopover-BFw3QX8L.js.map} +1 -1
- package/dist/{ExperimentSelectorModal-DZeSd-yr.js → ExperimentSelectorModal-BBWXC7KW.js} +12 -216
- package/dist/ExperimentSelectorModal-BBWXC7KW.js.map +1 -0
- package/dist/{SettingsModal-CJgLadsN.js → SettingsModal-Dpg-X1OM.js} +12 -9
- package/dist/{SettingsModal-CJgLadsN.js.map → SettingsModal-Dpg-X1OM.js.map} +1 -1
- package/dist/__tests__/components/BaseToggle.test.d.ts +1 -0
- package/dist/__tests__/components/GeneratedPathInput.test.d.ts +1 -0
- package/dist/__tests__/components/GeneratedPlotlyResult.test.d.ts +1 -0
- package/dist/__tests__/components/GeneratedPluginView.test.d.ts +1 -0
- package/dist/__tests__/components/GeneratedResultRenderer.test.d.ts +1 -0
- package/dist/__tests__/components/InstrumentAlertLog.test.d.ts +1 -0
- package/dist/__tests__/components/SectionCard.test.d.ts +1 -0
- package/dist/__tests__/composables/useGeneratedAnalysis.test.d.ts +1 -0
- package/dist/__tests__/generatedUi.test.d.ts +1 -0
- package/dist/__tests__/types/GeneratedJsonSchema.test.d.ts +1 -0
- package/dist/{analysisArtifactTypes-Cu40dzSG.js → analysisArtifactTypes-ejqR8MX3.js} +1 -1
- package/dist/{analysisArtifactTypes-Cu40dzSG.js.map → analysisArtifactTypes-ejqR8MX3.js.map} +1 -1
- package/dist/{auth-DZAwEeis.js → auth-B87xM--s.js} +1 -1
- package/dist/{auth-DZAwEeis.js.map → auth-B87xM--s.js.map} +1 -1
- package/dist/chunk-BoAXSpZd.js +33 -0
- package/dist/components/AppSidebar.vue.d.ts +1 -1
- package/dist/components/AutoGroupModal.vue.d.ts +2 -2
- package/dist/components/BaseSelect.vue.d.ts +1 -1
- package/dist/components/BaseToggle.vue.d.ts +2 -0
- package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +2 -2
- package/dist/components/CollapsibleCard.vue.d.ts +1 -1
- package/dist/components/ComponentBindingRenderer.vue.d.ts +1 -1
- package/dist/components/ConfirmDialog.vue.d.ts +1 -1
- package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
- package/dist/components/ExperimentTimeline.vue.d.ts +1 -1
- package/dist/components/FileUploader.vue.d.ts +4 -2
- package/dist/components/FitPanel.vue.d.ts +2 -2
- package/dist/components/FormActions.vue.d.ts +1 -1
- package/dist/components/FormBuilder.vue.d.ts +1 -1
- package/dist/components/GeneratedPathInput.vue.d.ts +12 -0
- package/dist/components/GeneratedPlotlyResult.vue.d.ts +11 -0
- package/dist/components/GeneratedPluginView.vue.d.ts +10 -0
- package/dist/components/GeneratedResultRenderer.vue.d.ts +6 -0
- package/dist/components/JobsStatusTray.vue.d.ts +5 -1
- package/dist/components/LcmsSequenceTable.vue.d.ts +1 -1
- package/dist/components/PluginWorkspaceView.props.d.ts +1 -1
- package/dist/components/SampleLegend.vue.d.ts +1 -1
- package/dist/components/SampleSelector.vue.d.ts +2 -2
- package/dist/components/SectionCard.vue.d.ts +47 -0
- package/dist/components/SettingsModal.vue.d.ts +1 -1
- package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -2
- package/dist/components/index.d.ts +5 -0
- package/dist/components/index.js +8 -9
- package/dist/components/internal/PlateMapEditorToolbarInternal.vue.d.ts +2 -2
- package/dist/{components-DdzHh3a_.js → components-Ci_BTx00.js} +2322 -2292
- package/dist/components-Ci_BTx00.js.map +1 -0
- package/dist/composables/index.d.ts +3 -2
- package/dist/composables/index.js +10 -11
- package/dist/composables/useGeneratedAnalysis.d.ts +62 -0
- package/dist/composables/useJobsStatusTray.d.ts +48 -4
- package/dist/composables/usePlatformContext.d.ts +3 -0
- package/dist/composables/usePluginClient.d.ts +17 -8
- package/dist/{composables-BUUphBqg.js → composables-C0VEJ73O.js} +154 -38
- package/dist/composables-C0VEJ73O.js.map +1 -0
- package/dist/experiment-utils-CxkruZ3G.js +392 -0
- package/dist/experiment-utils-CxkruZ3G.js.map +1 -0
- package/dist/generated/schema.d.ts +7 -0
- package/dist/generated-ui/main.d.ts +0 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -18
- package/dist/index.js.map +1 -1
- package/dist/install.js +7 -8
- package/dist/install.js.map +1 -1
- package/dist/plotly.min-BR53hja4.js +180634 -0
- package/dist/plotly.min-BR53hja4.js.map +1 -0
- package/dist/stores/auth.d.ts +2 -2
- package/dist/stores/index.js +1 -1
- package/dist/styles.css +8657 -8217
- package/dist/tailwind.preset.d.ts +5 -0
- package/dist/tailwind.preset.js +7 -2
- package/dist/tailwind.preset.js.map +1 -1
- package/dist/templates/index.js +4 -3
- package/dist/templates/packs.d.ts +0 -1
- package/dist/{templates-CNbPQNID.js → templates-Cdj93S9C.js} +2 -4
- package/dist/templates-Cdj93S9C.js.map +1 -0
- package/dist/types/components.d.ts +2 -0
- package/dist/types/form-builder.d.ts +1 -1
- package/dist/types/generated-ui.d.ts +183 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +1 -1
- package/dist/types/instrument.d.ts +1 -0
- package/dist/types/jobs.d.ts +7 -1
- package/dist/types/platform.d.ts +2 -0
- package/dist/{useControlSchema-BZNdalmL.js → useControlSchema-B_qgU4rW.js} +1882 -172
- package/dist/useControlSchema-B_qgU4rW.js.map +1 -0
- package/dist/{useDropdownState-Bb-c_PeE.js → useDropdownState-gVaMXtwW.js} +2 -2
- package/dist/{useDropdownState-Bb-c_PeE.js.map → useDropdownState-gVaMXtwW.js.map} +1 -1
- package/dist/{useExperimentSelector-DRbj6vse.js → useExperimentSelector-BuQZRgMt.js} +4 -255
- package/dist/useExperimentSelector-BuQZRgMt.js.map +1 -0
- package/dist/{useFormBuilder-DKekvXRs.js → useFormBuilder-D1G5J5Z5.js} +2 -2
- package/dist/{useFormBuilder-DKekvXRs.js.map → useFormBuilder-D1G5J5Z5.js.map} +1 -1
- package/dist/{useAnalysisArtifacts-C3l0a0vu.js → useProtocolTemplates-DeaOmMaw.js} +307 -475
- package/dist/useProtocolTemplates-DeaOmMaw.js.map +1 -0
- package/package.json +7 -3
- package/src/__tests__/components/BaseSelect.test.ts +52 -0
- package/src/__tests__/components/BaseToggle.test.ts +40 -0
- package/src/__tests__/components/GeneratedPathInput.test.ts +132 -0
- package/src/__tests__/components/GeneratedPlotlyResult.test.ts +117 -0
- package/src/__tests__/components/GeneratedPluginView.test.ts +240 -0
- package/src/__tests__/components/GeneratedResultRenderer.test.ts +93 -0
- package/src/__tests__/components/InstrumentAlertLog.test.ts +59 -0
- package/src/__tests__/components/PluginWorkspaceView.test.ts +68 -14
- package/src/__tests__/components/SectionCard.test.ts +181 -0
- package/src/__tests__/composables/formBuilderRegistry.test.ts +5 -1
- package/src/__tests__/composables/useApi.test.ts +8 -0
- package/src/__tests__/composables/useGeneratedAnalysis.test.ts +1742 -0
- package/src/__tests__/composables/useJobsStatusTray.test.ts +179 -1
- package/src/__tests__/composables/usePlatformContext.test.ts +28 -0
- package/src/__tests__/composables/usePluginClient.test.ts +402 -21
- package/src/__tests__/composables/usePluginConfig.test.ts +323 -7
- package/src/__tests__/docs/extractDocsTemplates.test.ts +0 -1
- package/src/__tests__/generatedUi.test.ts +119 -0
- package/src/__tests__/templates/templates.test.ts +1 -1
- package/src/__tests__/types/GeneratedJsonSchema.test.ts +62 -0
- package/src/components/BaseSelect.vue +16 -2
- package/src/components/BaseToggle.vue +3 -1
- package/src/components/FileUploader.vue +49 -11
- package/src/components/GeneratedPathInput.vue +218 -0
- package/src/components/GeneratedPlotlyResult.vue +87 -0
- package/src/components/GeneratedPluginView.vue +279 -0
- package/src/components/GeneratedResultRenderer.vue +137 -0
- package/src/components/InstrumentAlertLog.vue +2 -2
- package/src/components/JobsStatusTray.vue +14 -8
- package/src/components/PluginWorkspaceView.props.ts +1 -1
- package/src/components/PluginWorkspaceView.vue +1 -5
- package/src/components/SectionCard.story.vue +163 -0
- package/src/components/SectionCard.vue +98 -0
- package/src/components/index.ts +5 -0
- package/src/composables/formBuilderRegistry.ts +2 -0
- package/src/composables/index.ts +19 -0
- package/src/composables/useApi.ts +1 -0
- package/src/composables/useGeneratedAnalysis.ts +1270 -0
- package/src/composables/useJobsStatusTray.ts +234 -45
- package/src/composables/usePluginClient.ts +164 -30
- package/src/composables/usePluginConfig.ts +156 -20
- package/src/generated/schema.ts +130 -0
- package/src/generated-ui/main.ts +21 -0
- package/src/index.ts +3 -0
- package/src/jobs.ts +5 -0
- package/src/plotly-dist.d.ts +3 -0
- package/src/styles/components/section-card.css +161 -0
- package/src/styles/index.css +1 -0
- package/src/styles/variables.css +25 -0
- package/src/tailwind.preset.ts +5 -0
- package/src/templates/packs.ts +0 -3
- package/src/types/components.ts +12 -0
- package/src/types/form-builder.ts +1 -0
- package/src/types/generated-ui.ts +189 -0
- package/src/types/index.ts +4 -0
- package/src/types/instrument.ts +1 -0
- package/src/types/jobs.ts +10 -1
- package/src/types/platform.ts +2 -0
- package/dist/BaseButton-Dgqrze41.js +0 -74
- package/dist/BaseButton-Dgqrze41.js.map +0 -1
- package/dist/BaseModal-B9UA8Y_I.js +0 -165
- package/dist/BaseModal-B9UA8Y_I.js.map +0 -1
- package/dist/BaseSelect-ekgr9fDo.js +0 -179
- package/dist/BaseSelect-ekgr9fDo.js.map +0 -1
- package/dist/ExperimentPopover-Ryusjrhv.js +0 -4
- package/dist/ExperimentSelectorModal-Cg4-LXBD.js +0 -5
- package/dist/ExperimentSelectorModal-DZeSd-yr.js.map +0 -1
- package/dist/SettingsModal-Bl_9w2Y3.js +0 -5
- package/dist/components-DdzHh3a_.js.map +0 -1
- package/dist/composables-BUUphBqg.js.map +0 -1
- package/dist/experiment-utils-Bfa7CwPU.js +0 -108
- package/dist/experiment-utils-Bfa7CwPU.js.map +0 -1
- package/dist/templates-CNbPQNID.js.map +0 -1
- package/dist/useAnalysisArtifacts-C3l0a0vu.js.map +0 -1
- package/dist/useControlSchema-BZNdalmL.js.map +0 -1
- package/dist/useEventListener-CfVkP9Xz.js +0 -57
- package/dist/useEventListener-CfVkP9Xz.js.map +0 -1
- package/dist/useExperimentSelector-DRbj6vse.js.map +0 -1
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
|
3
|
+
|
|
4
|
+
import { generatedJobDefaults, generatedJobFormSchema, normalizeGeneratedJobInput } from '../generated/schema'
|
|
5
|
+
import type { FormSchema } from '../types/form-builder'
|
|
6
|
+
import type {
|
|
7
|
+
GeneratedAnalysisJob,
|
|
8
|
+
GeneratedAnalysisResult,
|
|
9
|
+
GeneratedJobDefinition,
|
|
10
|
+
} from '../types/generated-ui'
|
|
11
|
+
import type { PillNavItem, SidebarToolSection } from '../types'
|
|
12
|
+
import {
|
|
13
|
+
useGeneratedAnalysis,
|
|
14
|
+
type GeneratedAnalysisTransport,
|
|
15
|
+
} from '../composables/useGeneratedAnalysis'
|
|
16
|
+
import AlertBox from './AlertBox.vue'
|
|
17
|
+
import EmptyState from './EmptyState.vue'
|
|
18
|
+
import GeneratedResultRenderer from './GeneratedResultRenderer.vue'
|
|
19
|
+
import JobsStatusTray from './JobsStatusTray.vue'
|
|
20
|
+
import LoadingSpinner from './LoadingSpinner.vue'
|
|
21
|
+
import PluginWorkspaceView from './PluginWorkspaceView.vue'
|
|
22
|
+
|
|
23
|
+
const props = withDefaults(defineProps<{
|
|
24
|
+
baseUrl: string
|
|
25
|
+
showStandaloneLabel?: boolean
|
|
26
|
+
transport?: GeneratedAnalysisTransport
|
|
27
|
+
}>(), {
|
|
28
|
+
showStandaloneLabel: false,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const generated = useGeneratedAnalysis({
|
|
32
|
+
baseUrl: props.baseUrl,
|
|
33
|
+
transport: props.transport,
|
|
34
|
+
})
|
|
35
|
+
const activeDefinitionId = ref('')
|
|
36
|
+
const selectedJobId = ref('')
|
|
37
|
+
const valuesByDefinition = reactive<Record<string, Record<string, unknown>>>({})
|
|
38
|
+
const definitions = computed(() => generated.manifest.value?.jobs ?? [])
|
|
39
|
+
const activeDefinition = computed(() => (
|
|
40
|
+
definitions.value.find(definition => definition.id === activeDefinitionId.value) ?? null
|
|
41
|
+
))
|
|
42
|
+
|
|
43
|
+
const pillNav = computed<PillNavItem[]>(() => definitions.value.map(definition => ({
|
|
44
|
+
id: definition.id,
|
|
45
|
+
label: definition.title,
|
|
46
|
+
})))
|
|
47
|
+
|
|
48
|
+
const panels = computed<Record<string, SidebarToolSection[]>>(() => Object.fromEntries(
|
|
49
|
+
definitions.value.map(definition => [definition.id, [{
|
|
50
|
+
id: definition.id,
|
|
51
|
+
label: 'Parameters',
|
|
52
|
+
subtitle: resourceSummary(definition),
|
|
53
|
+
defaultOpen: true,
|
|
54
|
+
}]]),
|
|
55
|
+
))
|
|
56
|
+
|
|
57
|
+
const forms = computed<Record<string, FormSchema>>(() => Object.fromEntries(
|
|
58
|
+
definitions.value.map(definition => [definition.id, generatedJobFormSchema(definition)]),
|
|
59
|
+
))
|
|
60
|
+
|
|
61
|
+
const currentValues = computed(() => valuesByDefinition[activeDefinitionId.value] ?? {})
|
|
62
|
+
const currentJob = computed(() => {
|
|
63
|
+
const currentSessionId = generated.session.value?.id
|
|
64
|
+
const currentDefinitionId = activeDefinitionId.value
|
|
65
|
+
const selected = generated.jobs.value.find(candidate => (
|
|
66
|
+
candidate.job_id === selectedJobId.value
|
|
67
|
+
&& candidate.status === 'completed'
|
|
68
|
+
&& candidate.metadata.definition_id === currentDefinitionId
|
|
69
|
+
&& candidate.metadata.has_result === true
|
|
70
|
+
))
|
|
71
|
+
return selected ?? generated.jobs.value.find(candidate => (
|
|
72
|
+
candidate.session_id === currentSessionId
|
|
73
|
+
&& candidate.status === 'completed'
|
|
74
|
+
&& candidate.metadata.definition_id === currentDefinitionId
|
|
75
|
+
&& candidate.metadata.has_result === true
|
|
76
|
+
))
|
|
77
|
+
})
|
|
78
|
+
const currentResult = computed<GeneratedAnalysisResult | null>(() => (
|
|
79
|
+
currentJob.value ? generated.resultForJob(currentJob.value.job_id) : null
|
|
80
|
+
))
|
|
81
|
+
|
|
82
|
+
watch(definitions, (nextDefinitions) => {
|
|
83
|
+
for (const definition of nextDefinitions) {
|
|
84
|
+
valuesByDefinition[definition.id] ??= generatedJobDefaults(definition)
|
|
85
|
+
}
|
|
86
|
+
if (!nextDefinitions.some(definition => definition.id === activeDefinitionId.value)) {
|
|
87
|
+
activeDefinitionId.value = nextDefinitions[0]?.id ?? ''
|
|
88
|
+
}
|
|
89
|
+
}, { immediate: true })
|
|
90
|
+
|
|
91
|
+
watch(currentJob, (job) => {
|
|
92
|
+
if (job) void generated.loadResult(job)
|
|
93
|
+
}, { immediate: true })
|
|
94
|
+
|
|
95
|
+
function updateValues(values: Record<string, unknown>): void {
|
|
96
|
+
if (activeDefinitionId.value) valuesByDefinition[activeDefinitionId.value] = { ...values }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function submitJob(sectionId: string, values: Record<string, unknown>): Promise<void> {
|
|
100
|
+
const definition = definitions.value.find(candidate => candidate.id === sectionId)
|
|
101
|
+
if (!definition) return
|
|
102
|
+
selectedJobId.value = ''
|
|
103
|
+
valuesByDefinition[definition.id] = { ...values }
|
|
104
|
+
await generated.submit(definition.id, normalizeGeneratedJobInput(definition, values))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function selectJob(job: GeneratedAnalysisJob): void {
|
|
108
|
+
selectedJobId.value = job.job_id
|
|
109
|
+
activeDefinitionId.value = String(job.metadata.definition_id ?? activeDefinitionId.value)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function resourceSummary(definition: GeneratedJobDefinition): string {
|
|
113
|
+
if (definition.lifecycle === 'service' || definition.profile?.kind === 'service') {
|
|
114
|
+
return 'Host service'
|
|
115
|
+
}
|
|
116
|
+
if (definition.lifecycle === 'staged' || definition.profile?.kind === 'staged') {
|
|
117
|
+
return 'Staged upload'
|
|
118
|
+
}
|
|
119
|
+
return `Up to ${definition.cpu} CPU`
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
onMounted(() => {
|
|
123
|
+
void generated.initialize().catch(() => undefined)
|
|
124
|
+
})
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<template>
|
|
128
|
+
<PluginWorkspaceView
|
|
129
|
+
:title="generated.manifest.value?.ui.title ?? 'Analysis'"
|
|
130
|
+
:subtitle="generated.manifest.value?.ui.description"
|
|
131
|
+
:show-standalone-label="showStandaloneLabel"
|
|
132
|
+
:show-settings="false"
|
|
133
|
+
:pill-nav="pillNav"
|
|
134
|
+
:active-view="activeDefinitionId"
|
|
135
|
+
:panels="panels"
|
|
136
|
+
:forms="forms"
|
|
137
|
+
sidebar-title="Parameters"
|
|
138
|
+
sidebar-position="left"
|
|
139
|
+
:values="currentValues"
|
|
140
|
+
:show-form-actions="true"
|
|
141
|
+
:form-loading="generated.isSubmitting.value"
|
|
142
|
+
:form-disabled="generated.isSubmitting.value"
|
|
143
|
+
@update:active-view="activeDefinitionId = $event"
|
|
144
|
+
@update:values="updateValues"
|
|
145
|
+
@form-submit="submitJob"
|
|
146
|
+
>
|
|
147
|
+
<main class="mint-generated-workspace">
|
|
148
|
+
<div v-if="generated.isLoading.value" class="mint-generated-workspace__loading">
|
|
149
|
+
<LoadingSpinner size="lg" />
|
|
150
|
+
<span>Loading analysis workspace</span>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<template v-else>
|
|
154
|
+
<AlertBox
|
|
155
|
+
v-if="generated.error.value"
|
|
156
|
+
type="error"
|
|
157
|
+
:message="generated.error.value"
|
|
158
|
+
/>
|
|
159
|
+
|
|
160
|
+
<header v-if="activeDefinition" class="mint-generated-workspace__header">
|
|
161
|
+
<div>
|
|
162
|
+
<p class="mint-generated-workspace__eyebrow">Analysis result</p>
|
|
163
|
+
<h1>{{ activeDefinition.title }}</h1>
|
|
164
|
+
<p v-if="activeDefinition.description">{{ activeDefinition.description }}</p>
|
|
165
|
+
</div>
|
|
166
|
+
<div class="mint-generated-workspace__resources" aria-label="Job resource requirements">
|
|
167
|
+
<span>{{ resourceSummary(activeDefinition) }}</span>
|
|
168
|
+
</div>
|
|
169
|
+
</header>
|
|
170
|
+
|
|
171
|
+
<section class="mint-generated-workspace__result">
|
|
172
|
+
<GeneratedResultRenderer
|
|
173
|
+
v-if="currentResult"
|
|
174
|
+
:result="currentResult"
|
|
175
|
+
/>
|
|
176
|
+
<EmptyState
|
|
177
|
+
v-else
|
|
178
|
+
title="No result yet"
|
|
179
|
+
description="Set the parameters and run the analysis. Jobs continue independently if you switch tabs."
|
|
180
|
+
size="md"
|
|
181
|
+
/>
|
|
182
|
+
</section>
|
|
183
|
+
</template>
|
|
184
|
+
|
|
185
|
+
<JobsStatusTray
|
|
186
|
+
:source="generated"
|
|
187
|
+
:teleport-to="false"
|
|
188
|
+
@select="selectJob"
|
|
189
|
+
/>
|
|
190
|
+
</main>
|
|
191
|
+
</PluginWorkspaceView>
|
|
192
|
+
</template>
|
|
193
|
+
|
|
194
|
+
<style scoped>
|
|
195
|
+
.mint-generated-workspace {
|
|
196
|
+
position: relative;
|
|
197
|
+
min-width: 0;
|
|
198
|
+
min-height: calc(100vh - 5rem);
|
|
199
|
+
padding: clamp(1rem, 2.5vw, 2.5rem);
|
|
200
|
+
background: var(--color-bg-primary);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.mint-generated-workspace__loading {
|
|
204
|
+
display: flex;
|
|
205
|
+
min-height: 24rem;
|
|
206
|
+
align-items: center;
|
|
207
|
+
justify-content: center;
|
|
208
|
+
gap: 0.75rem;
|
|
209
|
+
color: var(--color-text-secondary);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.mint-generated-workspace__header {
|
|
213
|
+
display: flex;
|
|
214
|
+
align-items: flex-start;
|
|
215
|
+
justify-content: space-between;
|
|
216
|
+
gap: 1.5rem;
|
|
217
|
+
max-width: 76rem;
|
|
218
|
+
margin: 0 auto 1.25rem;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.mint-generated-workspace__header h1 {
|
|
222
|
+
margin: 0;
|
|
223
|
+
color: var(--color-text-primary);
|
|
224
|
+
font-size: clamp(1.35rem, 2vw, 1.8rem);
|
|
225
|
+
line-height: 1.2;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.mint-generated-workspace__header p:not(.mint-generated-workspace__eyebrow) {
|
|
229
|
+
margin: 0.4rem 0 0;
|
|
230
|
+
color: var(--color-text-secondary);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.mint-generated-workspace__eyebrow {
|
|
234
|
+
margin: 0 0 0.35rem;
|
|
235
|
+
color: var(--color-text-muted);
|
|
236
|
+
font-size: 0.72rem;
|
|
237
|
+
font-weight: 700;
|
|
238
|
+
letter-spacing: 0.08em;
|
|
239
|
+
text-transform: uppercase;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.mint-generated-workspace__resources {
|
|
243
|
+
display: flex;
|
|
244
|
+
flex-wrap: wrap;
|
|
245
|
+
justify-content: flex-end;
|
|
246
|
+
gap: 0.4rem;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.mint-generated-workspace__resources span {
|
|
250
|
+
padding: 0.3rem 0.55rem;
|
|
251
|
+
border: 1px solid var(--color-border);
|
|
252
|
+
border-radius: 999px;
|
|
253
|
+
background: var(--color-bg-secondary);
|
|
254
|
+
color: var(--color-text-secondary);
|
|
255
|
+
font-size: 0.75rem;
|
|
256
|
+
font-weight: 600;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.mint-generated-workspace__result {
|
|
260
|
+
max-width: 76rem;
|
|
261
|
+
min-height: 24rem;
|
|
262
|
+
margin: 0 auto;
|
|
263
|
+
padding: clamp(1rem, 2vw, 1.75rem);
|
|
264
|
+
border: 1px solid var(--color-border);
|
|
265
|
+
border-radius: 1rem;
|
|
266
|
+
background: var(--color-bg-secondary);
|
|
267
|
+
box-shadow: var(--shadow-sm);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
@media (max-width: 640px) {
|
|
271
|
+
.mint-generated-workspace__header {
|
|
272
|
+
flex-direction: column;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.mint-generated-workspace__resources {
|
|
276
|
+
justify-content: flex-start;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
</style>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
import type { DataFrameColumn } from '../types'
|
|
5
|
+
import type { GeneratedAnalysisResult } from '../types/generated-ui'
|
|
6
|
+
import ChartContainer from './ChartContainer.vue'
|
|
7
|
+
import DataFrame from './DataFrame.vue'
|
|
8
|
+
import GeneratedPlotlyResult from './GeneratedPlotlyResult.vue'
|
|
9
|
+
|
|
10
|
+
const props = defineProps<{
|
|
11
|
+
result: GeneratedAnalysisResult
|
|
12
|
+
}>()
|
|
13
|
+
|
|
14
|
+
const tableColumns = computed<DataFrameColumn[]>(() => {
|
|
15
|
+
if (props.result.kind !== 'table') return []
|
|
16
|
+
return props.result.columns.map(key => ({
|
|
17
|
+
key,
|
|
18
|
+
label: humanize(key),
|
|
19
|
+
sortable: true,
|
|
20
|
+
}))
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const formattedJson = computed(() => (
|
|
24
|
+
props.result.kind === 'json'
|
|
25
|
+
? JSON.stringify(props.result.value, null, 2)
|
|
26
|
+
: ''
|
|
27
|
+
))
|
|
28
|
+
|
|
29
|
+
const artifactHref = computed(() => {
|
|
30
|
+
if (props.result.kind !== 'artifact') return ''
|
|
31
|
+
if (props.result.download_url) return props.result.download_url
|
|
32
|
+
return props.result.data
|
|
33
|
+
? `data:${props.result.media_type};base64,${props.result.data}`
|
|
34
|
+
: ''
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
function humanize(value: string): string {
|
|
38
|
+
return value.replace(/[_-]+/g, ' ').replace(/^\w/, character => character.toUpperCase())
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<template>
|
|
43
|
+
<article class="mint-generated-result" aria-live="polite">
|
|
44
|
+
<p v-if="result.kind === 'text'" class="mint-generated-result__text">
|
|
45
|
+
{{ result.text }}
|
|
46
|
+
</p>
|
|
47
|
+
|
|
48
|
+
<pre v-else-if="result.kind === 'json'" class="mint-generated-result__json">{{ formattedJson }}</pre>
|
|
49
|
+
|
|
50
|
+
<DataFrame
|
|
51
|
+
v-else-if="result.kind === 'table'"
|
|
52
|
+
:data="result.rows"
|
|
53
|
+
:columns="tableColumns"
|
|
54
|
+
sortable
|
|
55
|
+
searchable
|
|
56
|
+
sticky-header
|
|
57
|
+
max-height="32rem"
|
|
58
|
+
/>
|
|
59
|
+
|
|
60
|
+
<ChartContainer v-else-if="result.kind === 'image'" title="Analysis chart">
|
|
61
|
+
<img
|
|
62
|
+
class="mint-generated-result__image"
|
|
63
|
+
:src="`data:${result.media_type};base64,${result.data}`"
|
|
64
|
+
:alt="result.alt"
|
|
65
|
+
>
|
|
66
|
+
</ChartContainer>
|
|
67
|
+
|
|
68
|
+
<GeneratedPlotlyResult
|
|
69
|
+
v-else-if="result.kind === 'plotly'"
|
|
70
|
+
:spec="result.spec"
|
|
71
|
+
:fallback="result.fallback"
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
<a
|
|
75
|
+
v-else
|
|
76
|
+
class="mint-generated-result__artifact"
|
|
77
|
+
:href="artifactHref"
|
|
78
|
+
:download="result.name"
|
|
79
|
+
>
|
|
80
|
+
<span>{{ result.name }}</span>
|
|
81
|
+
<small>{{ result.media_type }}</small>
|
|
82
|
+
</a>
|
|
83
|
+
</article>
|
|
84
|
+
</template>
|
|
85
|
+
|
|
86
|
+
<style scoped>
|
|
87
|
+
.mint-generated-result {
|
|
88
|
+
width: 100%;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.mint-generated-result__text,
|
|
92
|
+
.mint-generated-result__json {
|
|
93
|
+
margin: 0;
|
|
94
|
+
color: var(--color-text-primary);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.mint-generated-result__text {
|
|
98
|
+
max-width: 72ch;
|
|
99
|
+
white-space: pre-wrap;
|
|
100
|
+
line-height: 1.65;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.mint-generated-result__json {
|
|
104
|
+
overflow: auto;
|
|
105
|
+
padding: 1rem;
|
|
106
|
+
border: 1px solid var(--color-border);
|
|
107
|
+
border-radius: 0.75rem;
|
|
108
|
+
background: var(--color-bg-secondary);
|
|
109
|
+
font-size: 0.8125rem;
|
|
110
|
+
line-height: 1.55;
|
|
111
|
+
white-space: pre-wrap;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.mint-generated-result__image {
|
|
115
|
+
display: block;
|
|
116
|
+
max-width: 100%;
|
|
117
|
+
height: auto;
|
|
118
|
+
margin: 0 auto;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.mint-generated-result__plotly {
|
|
122
|
+
width: 100%;
|
|
123
|
+
min-height: 24rem;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.mint-generated-result__artifact {
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
justify-content: space-between;
|
|
130
|
+
gap: 1rem;
|
|
131
|
+
padding: 1rem;
|
|
132
|
+
border: 1px solid var(--color-border);
|
|
133
|
+
border-radius: 0.75rem;
|
|
134
|
+
color: var(--color-primary);
|
|
135
|
+
text-decoration: none;
|
|
136
|
+
}
|
|
137
|
+
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
/** Filterable instrument alert/event list
|
|
2
|
+
/** Filterable instrument alert/event list keyed by each alert's required stable event_key. */
|
|
3
3
|
import { computed, ref } from 'vue'
|
|
4
4
|
import type { InstrumentAlert, InstrumentAlertLevel } from '../types'
|
|
5
5
|
|
|
@@ -146,7 +146,7 @@ function alertDetail(alert: InstrumentAlert): string {
|
|
|
146
146
|
<TransitionGroup name="mint-instrument-alert" tag="div" class="mint-instrument-alert-log__list">
|
|
147
147
|
<div
|
|
148
148
|
v-for="alert in filteredAlerts"
|
|
149
|
-
:key="alert.
|
|
149
|
+
:key="alert.event_key"
|
|
150
150
|
class="mint-instrument-alert"
|
|
151
151
|
:class="[
|
|
152
152
|
levelConfig[alert.level]?.cssClass,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
/** Floating,
|
|
2
|
+
/** Floating, source-driven job status tray for plugin and platform workflows. */
|
|
3
3
|
import { computed, nextTick, ref, toRef, useId } from 'vue'
|
|
4
4
|
|
|
5
5
|
import {
|
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
normalizeJobPercent,
|
|
8
8
|
} from '../jobs'
|
|
9
9
|
import {
|
|
10
|
-
|
|
10
|
+
usePluginJobCenter,
|
|
11
11
|
type JobsStatusTrayAdapter,
|
|
12
|
+
type PluginJobCenterSource,
|
|
12
13
|
} from '../composables/useJobsStatusTray'
|
|
13
14
|
import type { UsePluginEventStreamReturn } from '../composables/usePluginClient'
|
|
14
15
|
import type {
|
|
@@ -18,8 +19,12 @@ import type {
|
|
|
18
19
|
} from '../types/jobs'
|
|
19
20
|
|
|
20
21
|
interface Props {
|
|
22
|
+
source?: PluginJobCenterSource
|
|
23
|
+
/** @deprecated Pass a source instead. */
|
|
21
24
|
jobs?: readonly JobStateInput[]
|
|
25
|
+
/** @deprecated Pass source actions instead. */
|
|
22
26
|
adapter?: JobsStatusTrayAdapter
|
|
27
|
+
/** @deprecated Event streams belong to the source runtime. */
|
|
23
28
|
eventStream?: UsePluginEventStreamReturn<JobStreamData>
|
|
24
29
|
loadOnMount?: boolean
|
|
25
30
|
title?: string
|
|
@@ -42,7 +47,8 @@ const titleId = `${panelId}-title`
|
|
|
42
47
|
const triggerRef = ref<HTMLButtonElement | null>(null)
|
|
43
48
|
const panelRef = ref<HTMLElement | null>(null)
|
|
44
49
|
const teleportTarget = computed(() => props.teleportTo || 'body')
|
|
45
|
-
const tray =
|
|
50
|
+
const tray = usePluginJobCenter({
|
|
51
|
+
source: toRef(props, 'source'),
|
|
46
52
|
jobs: toRef(props, 'jobs'),
|
|
47
53
|
adapter: toRef(props, 'adapter'),
|
|
48
54
|
eventStream: toRef(props, 'eventStream'),
|
|
@@ -50,7 +56,7 @@ const tray = useJobsStatusTray({
|
|
|
50
56
|
})
|
|
51
57
|
const canClear = computed(() => (
|
|
52
58
|
tray.deletableJobs.value.length > 0
|
|
53
|
-
&&
|
|
59
|
+
&& tray.canClearFinished.value
|
|
54
60
|
))
|
|
55
61
|
|
|
56
62
|
async function openTray(): Promise<void> {
|
|
@@ -201,7 +207,7 @@ async function clearFinished(): Promise<void> {
|
|
|
201
207
|
</div>
|
|
202
208
|
<div class="mint-jobs-tray__header-actions">
|
|
203
209
|
<button
|
|
204
|
-
v-if="
|
|
210
|
+
v-if="tray.canRefresh.value"
|
|
205
211
|
type="button"
|
|
206
212
|
class="mint-jobs-tray__icon-button"
|
|
207
213
|
aria-label="Refresh jobs"
|
|
@@ -297,7 +303,7 @@ async function clearFinished(): Promise<void> {
|
|
|
297
303
|
<span>{{ progressText(job) }}</span>
|
|
298
304
|
<div class="mint-jobs-tray__job-actions">
|
|
299
305
|
<button
|
|
300
|
-
v-if="job.can_cancel &&
|
|
306
|
+
v-if="job.can_cancel && tray.canCancel.value"
|
|
301
307
|
type="button"
|
|
302
308
|
class="mint-jobs-tray__action mint-jobs-tray__action--danger"
|
|
303
309
|
aria-label="Cancel job"
|
|
@@ -307,7 +313,7 @@ async function clearFinished(): Promise<void> {
|
|
|
307
313
|
Cancel
|
|
308
314
|
</button>
|
|
309
315
|
<button
|
|
310
|
-
v-if="job.can_delete &&
|
|
316
|
+
v-if="job.can_delete && tray.canDelete.value"
|
|
311
317
|
type="button"
|
|
312
318
|
class="mint-jobs-tray__icon-button mint-jobs-tray__icon-button--danger"
|
|
313
319
|
aria-label="Delete job"
|
|
@@ -359,7 +365,7 @@ async function clearFinished(): Promise<void> {
|
|
|
359
365
|
<div class="mint-jobs-tray__job-footer">
|
|
360
366
|
<span>{{ formatTimestamp(job.completed_at || job.created_at) }}</span>
|
|
361
367
|
<button
|
|
362
|
-
v-if="job.can_delete &&
|
|
368
|
+
v-if="job.can_delete && tray.canDelete.value"
|
|
363
369
|
type="button"
|
|
364
370
|
class="mint-jobs-tray__icon-button mint-jobs-tray__icon-button--danger"
|
|
365
371
|
aria-label="Delete job"
|
|
@@ -34,7 +34,7 @@ export interface PluginWorkspaceViewProps {
|
|
|
34
34
|
homePath?: string
|
|
35
35
|
/** Show the default MINT logo when no icon/logo slot is provided. */
|
|
36
36
|
showLogo?: boolean
|
|
37
|
-
/** Preferred route-level page selector entries. */
|
|
37
|
+
/** Preferred route-level page selector entries. Pass [] to suppress integrated platform fallback. */
|
|
38
38
|
pageSelector?: PageSelectorItemInput[]
|
|
39
39
|
/** Active page selector id. Defaults to activeView. */
|
|
40
40
|
currentPageSelectorId?: string
|
|
@@ -208,10 +208,6 @@ const {
|
|
|
208
208
|
emitPillSelect: (item) => emit('pill-select', item),
|
|
209
209
|
})
|
|
210
210
|
|
|
211
|
-
const visiblePageSelector = computed(() =>
|
|
212
|
-
(props.pageSelector?.length ?? 0) > 1 ? props.pageSelector : undefined,
|
|
213
|
-
)
|
|
214
|
-
|
|
215
211
|
const hasSidebarSurface = computed(() => {
|
|
216
212
|
return hasPluginWorkspaceSidebarSurface({
|
|
217
213
|
showSidebar: props.showSidebar,
|
|
@@ -274,7 +270,7 @@ function handleFormCancel(sectionId: string) {
|
|
|
274
270
|
:variant="topBarVariant"
|
|
275
271
|
:home-path="homePath"
|
|
276
272
|
:show-logo="showLogo"
|
|
277
|
-
:page-selector="
|
|
273
|
+
:page-selector="pageSelector"
|
|
278
274
|
:current-page-selector-id="resolvedCurrentPageSelectorId"
|
|
279
275
|
:plugin-switcher="pluginSwitcher"
|
|
280
276
|
:pill-nav="resolvedPillNav"
|