@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,305 @@
1
+ import {
2
+ computed,
3
+ ref,
4
+ toValue,
5
+ watch,
6
+ type ComputedRef,
7
+ type MaybeRefOrGetter,
8
+ type Ref,
9
+ } from 'vue'
10
+ import { useApi } from './useApi'
11
+ import { useRequestSyncState } from './useRequestSyncState'
12
+ import {
13
+ getInjectedPlatformContext,
14
+ resolveCurrentExperimentId,
15
+ } from './platformContextHelpers'
16
+ import type {
17
+ AnalysisArtifactDetail,
18
+ AnalysisArtifactListResponse,
19
+ AnalysisArtifactMetadataUpdate,
20
+ AnalysisArtifactStatusFilter,
21
+ AnalysisArtifactSummary,
22
+ } from '../types/analysisArtifactTypes'
23
+
24
+ export { ANALYSIS_FILE_ARTIFACT_SCHEMA } from '../types/analysisArtifactTypes'
25
+
26
+ export interface UseAnalysisArtifactsOptions {
27
+ /** Experiment id; defaults to platform injection or URL. Refetches when it changes. */
28
+ experimentId?: MaybeRefOrGetter<number | undefined>
29
+ /** Override API base URL. */
30
+ apiBaseUrl?: string
31
+ /** Scope to a single owning plugin; omit or null to keep all plugins. */
32
+ pluginId?: MaybeRefOrGetter<string | null | undefined>
33
+ /** Always request archived artifacts alongside active ones. */
34
+ includeArchived?: MaybeRefOrGetter<boolean | undefined>
35
+ /** Fetch as soon as an experiment id is available. */
36
+ immediate?: boolean
37
+ }
38
+
39
+ export interface UseAnalysisArtifactsReturn {
40
+ /** Fetched artifacts, scoped to pluginId when set. */
41
+ artifacts: ComputedRef<AnalysisArtifactSummary[]>
42
+ /** Artifacts after applying statusFilter, pluginFilter, and search. */
43
+ visibleArtifacts: ComputedRef<AnalysisArtifactSummary[]>
44
+ /** Detail loaded by the last successful getDetail call, or null. */
45
+ selectedDetail: Ref<AnalysisArtifactDetail | null>
46
+ /** Resolved experiment id from options, platform injection, or URL. */
47
+ currentExperimentId: ComputedRef<number | undefined>
48
+ /** Whether an experiment id is available. */
49
+ hasCurrentExperiment: ComputedRef<boolean>
50
+ /** Whether any list or detail fetch is in progress. */
51
+ isLoading: ComputedRef<boolean>
52
+ /** Whether any archive/restore/metadata mutation is in progress. */
53
+ isMutating: ComputedRef<boolean>
54
+ /** Error message from the last failed operation, or null. */
55
+ error: Ref<string | null>
56
+ /** Timestamp of the last successful load, or null. */
57
+ lastLoadedAt: Ref<Date | null>
58
+ /** Case-insensitive filter over display_name and artifact_key. */
59
+ search: Ref<string>
60
+ /** Lifecycle filter; widening past "active" refetches with include_archived. */
61
+ statusFilter: Ref<AnalysisArtifactStatusFilter>
62
+ /** Plugin filter applied to visibleArtifacts, or null for all plugins. */
63
+ pluginFilter: Ref<string | null>
64
+ /** Fetch the artifact list for the current experiment. */
65
+ list: () => Promise<AnalysisArtifactSummary[]>
66
+ /** Alias of list. */
67
+ refresh: () => Promise<AnalysisArtifactSummary[]>
68
+ /** Fetch one artifact with its result payload; null when it fails or is superseded. */
69
+ getDetail: (artifactId: number) => Promise<AnalysisArtifactDetail | null>
70
+ /** Update display_name and/or note; resolves to the updated summary or null. */
71
+ updateMetadata: (
72
+ artifactId: number,
73
+ updates: AnalysisArtifactMetadataUpdate,
74
+ ) => Promise<AnalysisArtifactSummary | null>
75
+ /** Archive an artifact; resolves to the updated summary or null. */
76
+ archive: (artifactId: number) => Promise<AnalysisArtifactSummary | null>
77
+ /** Restore an archived artifact; resolves to the updated summary or null. */
78
+ restore: (artifactId: number) => Promise<AnalysisArtifactSummary | null>
79
+ /** Whether a key exists in the loaded artifacts (no network call). */
80
+ hasArtifactKey: (artifactKey: string) => boolean
81
+ /** Reset artifacts, selection, and error. */
82
+ clear: () => void
83
+ }
84
+
85
+ /** Lists and manages analysis artifacts through the user-facing platform API. */
86
+ export function useAnalysisArtifacts(
87
+ options: UseAnalysisArtifactsOptions = {},
88
+ ): UseAnalysisArtifactsReturn {
89
+ const injectedContext = getInjectedPlatformContext()
90
+ const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl })
91
+
92
+ const currentExperimentId = computed<number | undefined>(
93
+ () => toValue(options.experimentId) ?? resolveCurrentExperimentId(injectedContext),
94
+ )
95
+ const hasCurrentExperiment = computed(() => currentExperimentId.value !== undefined)
96
+ const scopePluginId = computed(() => toValue(options.pluginId) ?? null)
97
+
98
+ const request = useRequestSyncState('Analysis artifact request failed.')
99
+ const fetchedArtifacts = ref<AnalysisArtifactSummary[]>([])
100
+ const selectedDetail = ref<AnalysisArtifactDetail | null>(null)
101
+
102
+ // Counters, not booleans: overlapping requests must not clear each other's flag.
103
+ const loadingCount = ref(0)
104
+ const mutatingCount = ref(0)
105
+ const isLoading = computed(() => loadingCount.value > 0)
106
+ const isMutating = computed(() => mutatingCount.value > 0)
107
+
108
+ const search = ref('')
109
+ const statusFilter = ref<AnalysisArtifactStatusFilter>('active')
110
+ const pluginFilter = ref<string | null>(null)
111
+
112
+ const needsArchived = computed(
113
+ () => Boolean(toValue(options.includeArchived)) || statusFilter.value !== 'active',
114
+ )
115
+
116
+ const artifacts = computed(() => (
117
+ scopePluginId.value
118
+ ? fetchedArtifacts.value.filter((artifact) => artifact.plugin_id === scopePluginId.value)
119
+ : fetchedArtifacts.value
120
+ ))
121
+
122
+ const visibleArtifacts = computed(() => {
123
+ const query = search.value.trim().toLowerCase()
124
+ return artifacts.value.filter((artifact) => {
125
+ if (statusFilter.value !== 'all' && artifact.status !== statusFilter.value) return false
126
+ if (pluginFilter.value && artifact.plugin_id !== pluginFilter.value) return false
127
+ if (!query) return true
128
+ return (
129
+ artifact.display_name.toLowerCase().includes(query)
130
+ || artifact.artifact_key.toLowerCase().includes(query)
131
+ )
132
+ })
133
+ })
134
+
135
+ // Generation tokens: a superseded response must commit nothing — not rows, not the
136
+ // shared error, not lastLoadedAt — or a late failure would bury fresh results.
137
+ let listToken = 0
138
+ let detailToken = 0
139
+ const hasRequestedList = ref(false)
140
+
141
+ function currentExperimentIdOrError(action: string): number | undefined {
142
+ const id = currentExperimentId.value
143
+ if (id === undefined) {
144
+ request.setError(`No current experiment is selected for ${action}`)
145
+ return undefined
146
+ }
147
+ return id
148
+ }
149
+
150
+ function replaceArtifact(summary: AnalysisArtifactSummary): void {
151
+ const index = fetchedArtifacts.value.findIndex((artifact) => artifact.id === summary.id)
152
+ if (index >= 0) {
153
+ fetchedArtifacts.value.splice(index, 1, summary)
154
+ }
155
+ }
156
+
157
+ async function list(): Promise<AnalysisArtifactSummary[]> {
158
+ const experimentId = currentExperimentIdOrError('listing analysis artifacts')
159
+ if (experimentId === undefined) {
160
+ fetchedArtifacts.value = []
161
+ return []
162
+ }
163
+ const token = ++listToken
164
+ hasRequestedList.value = true
165
+ loadingCount.value += 1
166
+ request.clearError()
167
+ try {
168
+ const data = await api.get<AnalysisArtifactListResponse>(
169
+ `/experiments/${experimentId}/analysis-artifacts?include_archived=${needsArchived.value}`,
170
+ )
171
+ if (token !== listToken) return artifacts.value
172
+ // Copy: replaceArtifact splices in place, and the response array is not ours.
173
+ fetchedArtifacts.value = [...(data?.artifacts ?? [])]
174
+ request.markLoaded()
175
+ return artifacts.value
176
+ } catch (err) {
177
+ if (token !== listToken) return []
178
+ request.setError(err)
179
+ fetchedArtifacts.value = []
180
+ return []
181
+ } finally {
182
+ loadingCount.value -= 1
183
+ }
184
+ }
185
+
186
+ async function getDetail(artifactId: number): Promise<AnalysisArtifactDetail | null> {
187
+ const experimentId = currentExperimentIdOrError('loading an analysis artifact')
188
+ if (experimentId === undefined) return null
189
+ const token = ++detailToken
190
+ loadingCount.value += 1
191
+ request.clearError()
192
+ try {
193
+ const detail = await api.get<AnalysisArtifactDetail>(
194
+ `/experiments/${experimentId}/analysis-artifacts/${artifactId}`,
195
+ )
196
+ if (token !== detailToken) return null
197
+ selectedDetail.value = detail
198
+ request.markLoaded()
199
+ return detail
200
+ } catch (err) {
201
+ if (token !== detailToken) return null
202
+ request.setError(err)
203
+ return null
204
+ } finally {
205
+ loadingCount.value -= 1
206
+ }
207
+ }
208
+
209
+ async function mutate(
210
+ action: string,
211
+ operation: (experimentId: number) => Promise<AnalysisArtifactSummary>,
212
+ ): Promise<AnalysisArtifactSummary | null> {
213
+ const experimentId = currentExperimentIdOrError(action)
214
+ if (experimentId === undefined) return null
215
+ mutatingCount.value += 1
216
+ request.clearError()
217
+ try {
218
+ const summary = await operation(experimentId)
219
+ replaceArtifact(summary)
220
+ request.markSaved()
221
+ return summary
222
+ } catch (err) {
223
+ request.setError(err)
224
+ return null
225
+ } finally {
226
+ mutatingCount.value -= 1
227
+ }
228
+ }
229
+
230
+ async function updateMetadata(
231
+ artifactId: number,
232
+ updates: AnalysisArtifactMetadataUpdate,
233
+ ): Promise<AnalysisArtifactSummary | null> {
234
+ return mutate('updating artifact metadata', (experimentId) =>
235
+ api.patch<AnalysisArtifactSummary>(
236
+ `/experiments/${experimentId}/analysis-artifacts/${artifactId}`,
237
+ updates,
238
+ ))
239
+ }
240
+
241
+ async function archive(artifactId: number): Promise<AnalysisArtifactSummary | null> {
242
+ return mutate('archiving an analysis artifact', (experimentId) =>
243
+ api.post<AnalysisArtifactSummary>(
244
+ `/experiments/${experimentId}/analysis-artifacts/${artifactId}/archive`,
245
+ ))
246
+ }
247
+
248
+ async function restore(artifactId: number): Promise<AnalysisArtifactSummary | null> {
249
+ return mutate('restoring an analysis artifact', (experimentId) =>
250
+ api.post<AnalysisArtifactSummary>(
251
+ `/experiments/${experimentId}/analysis-artifacts/${artifactId}/restore`,
252
+ ))
253
+ }
254
+
255
+ function hasArtifactKey(artifactKey: string): boolean {
256
+ return artifacts.value.some((artifact) => artifact.artifact_key === artifactKey)
257
+ }
258
+
259
+ /** Drop experiment-scoped state and orphan every in-flight response. */
260
+ function clear(): void {
261
+ listToken += 1
262
+ detailToken += 1
263
+ fetchedArtifacts.value = []
264
+ selectedDetail.value = null
265
+ request.clearError()
266
+ }
267
+
268
+ // Refetch when the scope changes or archived rows are newly needed. `immediate`
269
+ // covers a late-resolving experiment id; otherwise stay quiet until something
270
+ // has asked for a list at least once.
271
+ watch([currentExperimentId, needsArchived], ([experimentId], [previousExperimentId]) => {
272
+ if (experimentId !== previousExperimentId) {
273
+ // Scope changed: the previous experiment's rows and any in-flight reply for
274
+ // them must not survive into the new one.
275
+ clear()
276
+ }
277
+ if (experimentId === undefined) return
278
+ if (options.immediate || hasRequestedList.value) {
279
+ void list()
280
+ }
281
+ }, { immediate: options.immediate })
282
+
283
+ return {
284
+ artifacts,
285
+ visibleArtifacts,
286
+ selectedDetail,
287
+ currentExperimentId,
288
+ hasCurrentExperiment,
289
+ isLoading,
290
+ isMutating,
291
+ error: request.error,
292
+ lastLoadedAt: request.lastLoadedAt,
293
+ search,
294
+ statusFilter,
295
+ pluginFilter,
296
+ list,
297
+ refresh: list,
298
+ getDetail,
299
+ updateMetadata,
300
+ archive,
301
+ restore,
302
+ hasArtifactKey,
303
+ clear,
304
+ }
305
+ }
@@ -0,0 +1,17 @@
1
+ .mint-artifact-save-dialog {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 1rem;
5
+ }
6
+
7
+ .mint-artifact-save-dialog__immutable {
8
+ margin: 0;
9
+ font-size: 0.8125rem;
10
+ color: var(--text-muted);
11
+ }
12
+
13
+ .mint-artifact-save-dialog__immutable code {
14
+ font-family: var(--font-mono, 'Fira Code', monospace);
15
+ font-size: 0.75rem;
16
+ color: var(--text-secondary);
17
+ }
@@ -0,0 +1,216 @@
1
+ /* ArtifactSelectorModal Component Styles */
2
+
3
+ .mint-artifact-selector {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 0.75rem;
7
+ min-height: 0;
8
+ }
9
+
10
+ /* Filter row */
11
+ .mint-artifact-selector__filters-row {
12
+ display: flex;
13
+ align-items: center;
14
+ gap: 0.5rem;
15
+ flex-wrap: wrap;
16
+ }
17
+
18
+ .mint-artifact-selector__search {
19
+ flex: 1;
20
+ min-width: 10rem;
21
+ }
22
+
23
+ .mint-artifact-selector__filter-select {
24
+ flex-shrink: 0;
25
+ width: 10rem;
26
+ }
27
+
28
+ /* Loading skeleton */
29
+ .mint-artifact-selector__skeleton {
30
+ display: flex;
31
+ flex-direction: column;
32
+ border: 1px solid var(--border-color);
33
+ border-radius: var(--radius-md);
34
+ overflow: hidden;
35
+ }
36
+
37
+ .mint-artifact-selector__skeleton-row {
38
+ display: flex;
39
+ align-items: center;
40
+ gap: 0.75rem;
41
+ padding: 0.75rem 1rem;
42
+ background-color: var(--bg-primary);
43
+ }
44
+
45
+ .mint-artifact-selector__skeleton-content {
46
+ flex: 1;
47
+ display: flex;
48
+ flex-direction: column;
49
+ gap: 6px;
50
+ }
51
+
52
+ /* Error */
53
+ .mint-artifact-selector__error {
54
+ padding: 0.75rem;
55
+ border: 1px solid color-mix(in srgb, var(--mint-error) 30%, transparent);
56
+ border-radius: var(--radius-md);
57
+ background-color: var(--mint-error-bg);
58
+ color: var(--mint-error);
59
+ font-size: 0.875rem;
60
+ }
61
+
62
+ /* Scrollable list */
63
+ .mint-artifact-selector__list {
64
+ max-height: min(28rem, calc(100vh - 16rem));
65
+ overflow-y: auto;
66
+ display: flex;
67
+ flex-direction: column;
68
+ border: 1px solid var(--border-color);
69
+ border-radius: var(--radius-md);
70
+ background-color: var(--bg-primary);
71
+ scrollbar-width: thin;
72
+ overscroll-behavior: contain;
73
+ }
74
+
75
+ .mint-artifact-selector__list::-webkit-scrollbar {
76
+ width: 0.5rem;
77
+ }
78
+
79
+ .mint-artifact-selector__list::-webkit-scrollbar-track {
80
+ background: transparent;
81
+ }
82
+
83
+ .mint-artifact-selector__list::-webkit-scrollbar-thumb {
84
+ background-color: color-mix(in srgb, var(--text-muted) 30%, transparent);
85
+ border: 2px solid transparent;
86
+ border-radius: 9999px;
87
+ background-clip: content-box;
88
+ }
89
+
90
+ /* Row */
91
+ .mint-artifact-selector__row {
92
+ position: relative;
93
+ display: flex;
94
+ align-items: center;
95
+ gap: 0.75rem;
96
+ min-height: 4.125rem;
97
+ padding: 0.75rem 1rem 0.75rem 1.125rem;
98
+ cursor: pointer;
99
+ border-left: 0 solid transparent;
100
+ border-bottom: 1px solid var(--border-light);
101
+ background-color: var(--bg-primary);
102
+ transition:
103
+ background-color 0.15s ease,
104
+ box-shadow 0.15s ease;
105
+ }
106
+
107
+ .mint-artifact-selector__row:hover {
108
+ background-color: var(--bg-hover);
109
+ }
110
+
111
+ .mint-artifact-selector__row--active {
112
+ box-shadow: inset 3px 0 0 var(--color-primary);
113
+ background-color: var(--color-primary-soft);
114
+ }
115
+
116
+ .mint-artifact-selector__row--active:hover {
117
+ background-color: var(--color-primary-soft);
118
+ }
119
+
120
+ /* Keyboard focused row */
121
+ .mint-artifact-selector__row--focused {
122
+ background-color: var(--bg-hover);
123
+ outline: none;
124
+ box-shadow:
125
+ inset 0 0 0 2px var(--color-primary),
126
+ inset 3px 0 0 var(--color-primary);
127
+ }
128
+
129
+ .mint-artifact-selector__row--active.mint-artifact-selector__row--focused {
130
+ background-color: var(--color-primary-soft);
131
+ }
132
+
133
+ .mint-artifact-selector__row--archived .mint-artifact-selector__name,
134
+ .mint-artifact-selector__row--archived .mint-artifact-selector__meta {
135
+ opacity: var(--mint-disabled-opacity, 0.6);
136
+ }
137
+
138
+ /* Row content */
139
+ .mint-artifact-selector__row-content {
140
+ flex: 1;
141
+ min-width: 0;
142
+ }
143
+
144
+ .mint-artifact-selector__name {
145
+ display: flex;
146
+ align-items: center;
147
+ gap: 0.5rem;
148
+ font-size: 0.875rem;
149
+ font-weight: 500;
150
+ color: var(--text-primary);
151
+ min-width: 0;
152
+ }
153
+
154
+ .mint-artifact-selector__key {
155
+ font-family: var(--font-mono, 'Fira Code', monospace);
156
+ font-size: 0.6875rem;
157
+ padding: 0.0625rem 0.375rem;
158
+ border-radius: var(--radius-sm);
159
+ background-color: var(--bg-secondary);
160
+ color: var(--text-secondary);
161
+ white-space: nowrap;
162
+ }
163
+
164
+ .mint-artifact-selector__meta {
165
+ display: flex;
166
+ align-items: center;
167
+ gap: 0.5rem;
168
+ flex-wrap: wrap;
169
+ font-size: 0.75rem;
170
+ color: var(--text-muted);
171
+ margin-top: 0.125rem;
172
+ }
173
+
174
+ .mint-artifact-selector__plugin {
175
+ font-family: var(--font-mono, 'Fira Code', monospace);
176
+ color: var(--text-secondary);
177
+ }
178
+
179
+ /* Status pill + row actions */
180
+ .mint-artifact-selector__row-side {
181
+ display: flex;
182
+ align-items: center;
183
+ gap: 0.5rem;
184
+ flex-shrink: 0;
185
+ }
186
+
187
+ .mint-artifact-selector__action {
188
+ padding: 0.25rem 0.5rem;
189
+ border: 1px solid var(--border-color);
190
+ border-radius: var(--radius-sm);
191
+ background: var(--bg-primary);
192
+ color: var(--text-secondary);
193
+ font-size: 0.75rem;
194
+ cursor: pointer;
195
+ white-space: nowrap;
196
+ transition:
197
+ background-color 0.15s ease,
198
+ border-color 0.15s ease,
199
+ color 0.15s ease;
200
+ }
201
+
202
+ .mint-artifact-selector__action:hover:not(:disabled) {
203
+ border-color: var(--color-primary);
204
+ color: var(--color-primary);
205
+ background-color: var(--bg-hover);
206
+ }
207
+
208
+ .mint-artifact-selector__action:focus-visible {
209
+ outline: none;
210
+ box-shadow: var(--focus-ring);
211
+ }
212
+
213
+ .mint-artifact-selector__action:disabled {
214
+ opacity: var(--mint-disabled-opacity, 0.6);
215
+ cursor: not-allowed;
216
+ }
@@ -85,5 +85,7 @@
85
85
  @import './components/experiment-data-viewer.css';
86
86
  @import './components/experiment-code-badge.css';
87
87
  @import './components/experiment-selector-modal.css';
88
+ @import './components/artifact-selector-modal.css';
89
+ @import './components/artifact-save-dialog.css';
88
90
  @import './components/experiment-popover.css';
89
91
  @import './components/fit-panel.css';