@morscherlab/mint-sdk 1.0.56 → 1.0.58
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/__tests__/components/ArtifactSaveDialog.test.d.ts +1 -0
- package/dist/__tests__/components/ArtifactSelectorModal.test.d.ts +1 -0
- package/dist/__tests__/composables/useAnalysisArtifacts.test.d.ts +1 -0
- package/dist/analysisArtifactTypes-Cu40dzSG.js +7 -0
- package/dist/analysisArtifactTypes-Cu40dzSG.js.map +1 -0
- package/dist/components/ArtifactSaveDialog.vue.d.ts +59 -0
- package/dist/components/ArtifactSelectorModal.vue.d.ts +41 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +2 -2
- package/dist/{components-BQwOeyiy.js → components-CpnOaPbP.js} +1774 -1144
- package/dist/components-CpnOaPbP.js.map +1 -0
- package/dist/composables/index.d.ts +1 -0
- package/dist/composables/index.js +4 -3
- package/dist/composables/useAnalysisArtifacts.d.ts +59 -0
- package/dist/{composables-TpXyhRZZ.js → composables-OYTD0Y3r.js} +2 -2
- package/dist/{composables-TpXyhRZZ.js.map → composables-OYTD0Y3r.js.map} +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/install.js +1 -1
- package/dist/styles.css +412 -0
- package/dist/types/analysisArtifactTypes.d.ts +133 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/{useJobsStatusTray-CVecN6Ao.js → useAnalysisArtifacts-Ob8UtVwl.js} +157 -2
- package/dist/useAnalysisArtifacts-Ob8UtVwl.js.map +1 -0
- package/package.json +1 -1
- package/src/__tests__/components/ArtifactSaveDialog.test.ts +566 -0
- package/src/__tests__/components/ArtifactSelectorModal.test.ts +178 -0
- package/src/__tests__/composables/useAnalysisArtifacts.test.ts +667 -0
- package/src/components/ArtifactSaveDialog.story.vue +319 -0
- package/src/components/ArtifactSaveDialog.vue +434 -0
- package/src/components/ArtifactSelectorModal.story.vue +295 -0
- package/src/components/ArtifactSelectorModal.vue +326 -0
- package/src/components/index.ts +2 -0
- package/src/composables/index.ts +6 -0
- package/src/composables/useAnalysisArtifacts.ts +305 -0
- package/src/styles/components/artifact-save-dialog.css +17 -0
- package/src/styles/components/artifact-selector-modal.css +216 -0
- package/src/styles/index.css +2 -0
- package/src/types/analysisArtifactTypes.ts +167 -0
- package/src/types/index.ts +24 -0
- package/dist/components-BQwOeyiy.js.map +0 -1
- package/dist/useJobsStatusTray-CVecN6Ao.js.map +0 -1
- /package/dist/{ExperimentPopover-8A4Rhffp.js → ExperimentPopover-Ryusjrhv.js} +0 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import ArtifactSaveDialog from './ArtifactSaveDialog.vue'
|
|
4
|
+
import {
|
|
5
|
+
ANALYSIS_FILE_ARTIFACT_SCHEMA,
|
|
6
|
+
type AnalysisArtifactDetail,
|
|
7
|
+
type ArtifactSaveDialogPayload,
|
|
8
|
+
} from '../types/analysisArtifactTypes'
|
|
9
|
+
|
|
10
|
+
// -- Mock JSON result a plugin would hand the dialog --
|
|
11
|
+
const mockResult: Record<string, unknown> = {
|
|
12
|
+
ic50: 0.42,
|
|
13
|
+
hill_slope: 1.08,
|
|
14
|
+
r_squared: 0.994,
|
|
15
|
+
curves: [{ compound: 'Doxorubicin', points: 8 }],
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// -- Existing artifacts, used for update mode and collision warnings --
|
|
19
|
+
const existingFileArtifact: AnalysisArtifactDetail = {
|
|
20
|
+
id: 3,
|
|
21
|
+
experiment_id: 42,
|
|
22
|
+
plugin_id: 'drp',
|
|
23
|
+
artifact_key: 'qc-report',
|
|
24
|
+
display_name: 'QC report (PDF)',
|
|
25
|
+
note: 'Generated after plate re-read',
|
|
26
|
+
status: 'active',
|
|
27
|
+
result_keys: ['schema_version', 'kind', 'filename', 'file'],
|
|
28
|
+
result: {
|
|
29
|
+
schema_version: ANALYSIS_FILE_ARTIFACT_SCHEMA,
|
|
30
|
+
kind: 'report',
|
|
31
|
+
filename: 'qc-report.pdf',
|
|
32
|
+
file: { uri: 'mint-object://experiments/42/plugins/drp/analysis-artifacts/qc-report/qc-report.pdf' },
|
|
33
|
+
},
|
|
34
|
+
tree: [],
|
|
35
|
+
summary: null,
|
|
36
|
+
created_at: '2026-07-02T14:00:00Z',
|
|
37
|
+
updated_at: '2026-07-02T14:00:00Z',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const existingJsonArtifact: AnalysisArtifactDetail = {
|
|
41
|
+
...existingFileArtifact,
|
|
42
|
+
id: 1,
|
|
43
|
+
artifact_key: 'dose-response-fit',
|
|
44
|
+
display_name: 'Doxorubicin IC50 fit',
|
|
45
|
+
note: null,
|
|
46
|
+
result_keys: ['ic50', 'curves'],
|
|
47
|
+
result: mockResult,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// -- Mock handlers standing in for a generated plugin-client call --
|
|
51
|
+
const lastPayload = ref<ArtifactSaveDialogPayload | null>(null)
|
|
52
|
+
|
|
53
|
+
/** Files can't be JSON-serialized; show their name instead. */
|
|
54
|
+
function formatPayload(payload: ArtifactSaveDialogPayload): string {
|
|
55
|
+
return JSON.stringify(
|
|
56
|
+
payload,
|
|
57
|
+
(_key, value) => (value instanceof File ? `File(${value.name})` : value),
|
|
58
|
+
2,
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function delay(ms: number) {
|
|
63
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function saveHandler(payload: ArtifactSaveDialogPayload) {
|
|
67
|
+
lastPayload.value = payload
|
|
68
|
+
await delay(600)
|
|
69
|
+
return { cleanupPending: payload.mode === 'update' }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function failingHandler(payload: ArtifactSaveDialogPayload) {
|
|
73
|
+
lastPayload.value = payload
|
|
74
|
+
await delay(600)
|
|
75
|
+
throw new Error('Plugin backend unavailable (503)')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// -- Story state --
|
|
79
|
+
const isOpenFile = ref(false)
|
|
80
|
+
const isOpenJson = ref(false)
|
|
81
|
+
const isOpenBoth = ref(false)
|
|
82
|
+
const isOpenUpdateFile = ref(false)
|
|
83
|
+
const isOpenUpdateJson = ref(false)
|
|
84
|
+
const isOpenCollision = ref(false)
|
|
85
|
+
const isOpenFileConflict = ref(false)
|
|
86
|
+
const isOpenHandlerFile = ref(false)
|
|
87
|
+
const isOpenError = ref(false)
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<template>
|
|
91
|
+
<Story title="Experiment/ArtifactSaveDialog">
|
|
92
|
+
<Variant title="Playground">
|
|
93
|
+
<template #default="{ state }">
|
|
94
|
+
<div style="padding: 2rem;">
|
|
95
|
+
<button
|
|
96
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary, #6366F1); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 0.875rem;"
|
|
97
|
+
@click="isOpenFile = true"
|
|
98
|
+
>
|
|
99
|
+
Open Save Dialog
|
|
100
|
+
</button>
|
|
101
|
+
|
|
102
|
+
<pre
|
|
103
|
+
v-if="lastPayload"
|
|
104
|
+
style="margin-top: 1rem; padding: 0.75rem 1rem; background: var(--bg-secondary, #f8fafc); border: 1px solid var(--border-color, #e2e8f0); border-radius: 6px; font-size: 0.75rem; overflow-x: auto;"
|
|
105
|
+
>{{ formatPayload(lastPayload) }}</pre>
|
|
106
|
+
|
|
107
|
+
<ArtifactSaveDialog
|
|
108
|
+
v-model="isOpenFile"
|
|
109
|
+
:experiment-id="42"
|
|
110
|
+
:mode="state.mode"
|
|
111
|
+
:result="mockResult"
|
|
112
|
+
:default-artifact-key="state.defaultArtifactKey"
|
|
113
|
+
:default-file-kind="state.defaultFileKind"
|
|
114
|
+
:save-handler="saveHandler"
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
</template>
|
|
118
|
+
|
|
119
|
+
<template #controls="{ state }">
|
|
120
|
+
<HstCheckbox v-model="isOpenFile" title="Open" />
|
|
121
|
+
<HstSelect
|
|
122
|
+
v-model="state.mode"
|
|
123
|
+
title="Mode"
|
|
124
|
+
:options="{ file: 'file', json: 'json', both: 'both' }"
|
|
125
|
+
/>
|
|
126
|
+
<HstText v-model="state.defaultArtifactKey" title="Default artifact key" />
|
|
127
|
+
<HstText v-model="state.defaultFileKind" title="Default file kind" />
|
|
128
|
+
</template>
|
|
129
|
+
</Variant>
|
|
130
|
+
|
|
131
|
+
<Variant title="JSON Artifact">
|
|
132
|
+
<div style="padding: 2rem;">
|
|
133
|
+
<p style="font-size: 0.8125rem; color: var(--text-muted, #94a3b8); margin: 0 0 1rem;">
|
|
134
|
+
Metadata form around a programmatic <code>result</code> payload. Handler receives
|
|
135
|
+
<code>kind: 'json', mode: 'create'</code>.
|
|
136
|
+
</p>
|
|
137
|
+
<button
|
|
138
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary, #6366F1); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 0.875rem;"
|
|
139
|
+
@click="isOpenJson = true"
|
|
140
|
+
>
|
|
141
|
+
Save JSON Artifact
|
|
142
|
+
</button>
|
|
143
|
+
|
|
144
|
+
<ArtifactSaveDialog
|
|
145
|
+
v-model="isOpenJson"
|
|
146
|
+
:experiment-id="42"
|
|
147
|
+
mode="json"
|
|
148
|
+
:result="mockResult"
|
|
149
|
+
default-artifact-key="dose-response-fit"
|
|
150
|
+
:save-handler="saveHandler"
|
|
151
|
+
/>
|
|
152
|
+
</div>
|
|
153
|
+
</Variant>
|
|
154
|
+
|
|
155
|
+
<Variant title="Both Kinds">
|
|
156
|
+
<div style="padding: 2rem;">
|
|
157
|
+
<p style="font-size: 0.8125rem; color: var(--text-muted, #94a3b8); margin: 0 0 1rem;">
|
|
158
|
+
Shows the File/JSON toggle; the emitted payload variant follows the selection.
|
|
159
|
+
</p>
|
|
160
|
+
<button
|
|
161
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary, #6366F1); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 0.875rem;"
|
|
162
|
+
@click="isOpenBoth = true"
|
|
163
|
+
>
|
|
164
|
+
Save Artifact (Both)
|
|
165
|
+
</button>
|
|
166
|
+
|
|
167
|
+
<ArtifactSaveDialog
|
|
168
|
+
v-model="isOpenBoth"
|
|
169
|
+
:experiment-id="42"
|
|
170
|
+
mode="both"
|
|
171
|
+
:result="mockResult"
|
|
172
|
+
:save-handler="saveHandler"
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
175
|
+
</Variant>
|
|
176
|
+
|
|
177
|
+
<Variant title="Update File Artifact">
|
|
178
|
+
<div style="padding: 2rem;">
|
|
179
|
+
<p style="font-size: 0.8125rem; color: var(--text-muted, #94a3b8); margin: 0 0 1rem;">
|
|
180
|
+
Transactional replacement: key is locked, filename and kind are immutable, and the handler
|
|
181
|
+
receives <code>mode: 'update'</code>. The result reports <code>cleanupPending</code>.
|
|
182
|
+
</p>
|
|
183
|
+
<button
|
|
184
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary, #6366F1); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 0.875rem;"
|
|
185
|
+
@click="isOpenUpdateFile = true"
|
|
186
|
+
>
|
|
187
|
+
Replace File Artifact
|
|
188
|
+
</button>
|
|
189
|
+
|
|
190
|
+
<ArtifactSaveDialog
|
|
191
|
+
v-model="isOpenUpdateFile"
|
|
192
|
+
:experiment-id="42"
|
|
193
|
+
:existing-artifact="existingFileArtifact"
|
|
194
|
+
accept=".pdf"
|
|
195
|
+
:save-handler="saveHandler"
|
|
196
|
+
/>
|
|
197
|
+
</div>
|
|
198
|
+
</Variant>
|
|
199
|
+
|
|
200
|
+
<Variant title="Handler-Owned File">
|
|
201
|
+
<div style="padding: 2rem;">
|
|
202
|
+
<p style="font-size: 0.8125rem; color: var(--text-muted, #94a3b8); margin: 0 0 1rem;">
|
|
203
|
+
The plugin backend serializes the file content. No browser file is requested; the handler
|
|
204
|
+
receives <code>source: 'handler'</code> with the artifact metadata.
|
|
205
|
+
</p>
|
|
206
|
+
<button
|
|
207
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary, #6366F1); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 0.875rem;"
|
|
208
|
+
@click="isOpenHandlerFile = true"
|
|
209
|
+
>
|
|
210
|
+
Save Server-Generated File
|
|
211
|
+
</button>
|
|
212
|
+
|
|
213
|
+
<ArtifactSaveDialog
|
|
214
|
+
v-model="isOpenHandlerFile"
|
|
215
|
+
:experiment-id="42"
|
|
216
|
+
mode="file"
|
|
217
|
+
file-source="handler"
|
|
218
|
+
default-artifact-key="analysis-session"
|
|
219
|
+
default-file-kind="session"
|
|
220
|
+
:save-handler="saveHandler"
|
|
221
|
+
/>
|
|
222
|
+
</div>
|
|
223
|
+
</Variant>
|
|
224
|
+
|
|
225
|
+
<Variant title="Update JSON Artifact">
|
|
226
|
+
<div style="padding: 2rem;">
|
|
227
|
+
<p style="font-size: 0.8125rem; color: var(--text-muted, #94a3b8); margin: 0 0 1rem;">
|
|
228
|
+
Re-saves a JSON artifact by key (<code>mode: 'upsert'</code>), preserving its display name.
|
|
229
|
+
</p>
|
|
230
|
+
<button
|
|
231
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary, #6366F1); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 0.875rem;"
|
|
232
|
+
@click="isOpenUpdateJson = true"
|
|
233
|
+
>
|
|
234
|
+
Update JSON Artifact
|
|
235
|
+
</button>
|
|
236
|
+
|
|
237
|
+
<ArtifactSaveDialog
|
|
238
|
+
v-model="isOpenUpdateJson"
|
|
239
|
+
:experiment-id="42"
|
|
240
|
+
:existing-artifact="existingJsonArtifact"
|
|
241
|
+
:result="mockResult"
|
|
242
|
+
:save-handler="saveHandler"
|
|
243
|
+
/>
|
|
244
|
+
</div>
|
|
245
|
+
</Variant>
|
|
246
|
+
|
|
247
|
+
<Variant title="Overwrite Warning (JSON)">
|
|
248
|
+
<div style="padding: 2rem;">
|
|
249
|
+
<p style="font-size: 0.8125rem; color: var(--text-muted, #94a3b8); margin: 0 0 1rem;">
|
|
250
|
+
A JSON save with an existing key upserts, so the dialog warns but still allows it. The copy
|
|
251
|
+
states what the backend actually does: results are replaced, name and note are kept unless
|
|
252
|
+
set.
|
|
253
|
+
</p>
|
|
254
|
+
<button
|
|
255
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary, #6366F1); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 0.875rem;"
|
|
256
|
+
@click="isOpenCollision = true"
|
|
257
|
+
>
|
|
258
|
+
Save With Existing Key
|
|
259
|
+
</button>
|
|
260
|
+
|
|
261
|
+
<ArtifactSaveDialog
|
|
262
|
+
v-model="isOpenCollision"
|
|
263
|
+
:experiment-id="42"
|
|
264
|
+
mode="json"
|
|
265
|
+
:result="mockResult"
|
|
266
|
+
default-artifact-key="dose-response-fit"
|
|
267
|
+
:existing-artifact-keys="['dose-response-fit', 'qc-report']"
|
|
268
|
+
:save-handler="saveHandler"
|
|
269
|
+
/>
|
|
270
|
+
</div>
|
|
271
|
+
</Variant>
|
|
272
|
+
|
|
273
|
+
<Variant title="Key Conflict (File)">
|
|
274
|
+
<div style="padding: 2rem;">
|
|
275
|
+
<p style="font-size: 0.8125rem; color: var(--text-muted, #94a3b8); margin: 0 0 1rem;">
|
|
276
|
+
File artifacts are create-only, so a reused key would always conflict backend-side. The
|
|
277
|
+
dialog blocks the save and points at replacement instead of sending a doomed request.
|
|
278
|
+
</p>
|
|
279
|
+
<button
|
|
280
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary, #6366F1); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 0.875rem;"
|
|
281
|
+
@click="isOpenFileConflict = true"
|
|
282
|
+
>
|
|
283
|
+
Save File With Existing Key
|
|
284
|
+
</button>
|
|
285
|
+
|
|
286
|
+
<ArtifactSaveDialog
|
|
287
|
+
v-model="isOpenFileConflict"
|
|
288
|
+
:experiment-id="42"
|
|
289
|
+
mode="file"
|
|
290
|
+
default-artifact-key="qc-report"
|
|
291
|
+
:existing-artifact-keys="['dose-response-fit', 'qc-report']"
|
|
292
|
+
:save-handler="saveHandler"
|
|
293
|
+
/>
|
|
294
|
+
</div>
|
|
295
|
+
</Variant>
|
|
296
|
+
|
|
297
|
+
<Variant title="Save Error">
|
|
298
|
+
<div style="padding: 2rem;">
|
|
299
|
+
<p style="font-size: 0.8125rem; color: var(--text-muted, #94a3b8); margin: 0 0 1rem;">
|
|
300
|
+
The handler rejects; the dialog stays open and renders the error.
|
|
301
|
+
</p>
|
|
302
|
+
<button
|
|
303
|
+
style="padding: 0.5rem 1rem; background: var(--color-primary, #6366F1); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 0.875rem;"
|
|
304
|
+
@click="isOpenError = true"
|
|
305
|
+
>
|
|
306
|
+
Save (Failing Handler)
|
|
307
|
+
</button>
|
|
308
|
+
|
|
309
|
+
<ArtifactSaveDialog
|
|
310
|
+
v-model="isOpenError"
|
|
311
|
+
:experiment-id="42"
|
|
312
|
+
mode="json"
|
|
313
|
+
:result="mockResult"
|
|
314
|
+
:save-handler="failingHandler"
|
|
315
|
+
/>
|
|
316
|
+
</div>
|
|
317
|
+
</Variant>
|
|
318
|
+
</Story>
|
|
319
|
+
</template>
|