@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.
Files changed (44) hide show
  1. package/dist/__tests__/components/ArtifactSaveDialog.test.d.ts +1 -0
  2. package/dist/__tests__/components/ArtifactSelectorModal.test.d.ts +1 -0
  3. package/dist/__tests__/composables/useAnalysisArtifacts.test.d.ts +1 -0
  4. package/dist/analysisArtifactTypes-Cu40dzSG.js +7 -0
  5. package/dist/analysisArtifactTypes-Cu40dzSG.js.map +1 -0
  6. package/dist/components/ArtifactSaveDialog.vue.d.ts +59 -0
  7. package/dist/components/ArtifactSelectorModal.vue.d.ts +41 -0
  8. package/dist/components/index.d.ts +2 -0
  9. package/dist/components/index.js +2 -2
  10. package/dist/{components-BQwOeyiy.js → components-CpnOaPbP.js} +1774 -1144
  11. package/dist/components-CpnOaPbP.js.map +1 -0
  12. package/dist/composables/index.d.ts +1 -0
  13. package/dist/composables/index.js +4 -3
  14. package/dist/composables/useAnalysisArtifacts.d.ts +59 -0
  15. package/dist/{composables-TpXyhRZZ.js → composables-OYTD0Y3r.js} +2 -2
  16. package/dist/{composables-TpXyhRZZ.js.map → composables-OYTD0Y3r.js.map} +1 -1
  17. package/dist/index.js +5 -4
  18. package/dist/index.js.map +1 -1
  19. package/dist/install.js +1 -1
  20. package/dist/styles.css +412 -0
  21. package/dist/types/analysisArtifactTypes.d.ts +133 -0
  22. package/dist/types/index.d.ts +2 -0
  23. package/dist/types/index.js +2 -0
  24. package/dist/{useJobsStatusTray-CVecN6Ao.js → useAnalysisArtifacts-Ob8UtVwl.js} +157 -2
  25. package/dist/useAnalysisArtifacts-Ob8UtVwl.js.map +1 -0
  26. package/package.json +1 -1
  27. package/src/__tests__/components/ArtifactSaveDialog.test.ts +566 -0
  28. package/src/__tests__/components/ArtifactSelectorModal.test.ts +178 -0
  29. package/src/__tests__/composables/useAnalysisArtifacts.test.ts +667 -0
  30. package/src/components/ArtifactSaveDialog.story.vue +319 -0
  31. package/src/components/ArtifactSaveDialog.vue +434 -0
  32. package/src/components/ArtifactSelectorModal.story.vue +295 -0
  33. package/src/components/ArtifactSelectorModal.vue +326 -0
  34. package/src/components/index.ts +2 -0
  35. package/src/composables/index.ts +6 -0
  36. package/src/composables/useAnalysisArtifacts.ts +305 -0
  37. package/src/styles/components/artifact-save-dialog.css +17 -0
  38. package/src/styles/components/artifact-selector-modal.css +216 -0
  39. package/src/styles/index.css +2 -0
  40. package/src/types/analysisArtifactTypes.ts +167 -0
  41. package/src/types/index.ts +24 -0
  42. package/dist/components-BQwOeyiy.js.map +0 -1
  43. package/dist/useJobsStatusTray-CVecN6Ao.js.map +0 -1
  44. /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>