@morscherlab/mint-sdk 1.1.0-beta.2 → 1.1.0-beta.20

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 (89) hide show
  1. package/README.md +18 -4
  2. package/dist/{EmptyState-D1c4RdC7.js → EmptyState-DgBRoYAy.js} +22 -13
  3. package/dist/EmptyState-DgBRoYAy.js.map +1 -0
  4. package/dist/{ExperimentSelectorModal-B7w6k3Y8.js → ExperimentSelectorModal-BBWXC7KW.js} +3 -3
  5. package/dist/{ExperimentSelectorModal-B7w6k3Y8.js.map → ExperimentSelectorModal-BBWXC7KW.js.map} +1 -1
  6. package/dist/{SettingsModal-DPDwCbXK.js → SettingsModal-Dpg-X1OM.js} +3 -3
  7. package/dist/{SettingsModal-DPDwCbXK.js.map → SettingsModal-Dpg-X1OM.js.map} +1 -1
  8. package/dist/__tests__/components/BaseToggle.test.d.ts +1 -0
  9. package/dist/__tests__/components/SectionCard.test.d.ts +1 -0
  10. package/dist/__tests__/composables/analysisArtifactTarget.test.d.ts +1 -0
  11. package/dist/__tests__/types/GeneratedJsonSchema.test.d.ts +1 -0
  12. package/dist/analysisArtifactTypes-ejqR8MX3.js.map +1 -1
  13. package/dist/components/BaseSelect.vue.d.ts +1 -1
  14. package/dist/components/BaseToggle.vue.d.ts +2 -0
  15. package/dist/components/FitPanel.vue.d.ts +1 -1
  16. package/dist/components/PluginWorkspaceView.props.d.ts +1 -1
  17. package/dist/components/SectionCard.vue.d.ts +47 -0
  18. package/dist/components/index.d.ts +1 -0
  19. package/dist/components/index.js +6 -6
  20. package/dist/{components-UfkPN1bK.js → components-Ci_BTx00.js} +711 -633
  21. package/dist/components-Ci_BTx00.js.map +1 -0
  22. package/dist/composables/analysisArtifactTarget.d.ts +5 -0
  23. package/dist/composables/index.d.ts +3 -1
  24. package/dist/composables/index.js +8 -8
  25. package/dist/composables/platformContextHelpers.d.ts +2 -0
  26. package/dist/composables/useAnalysisArtifacts.d.ts +3 -1
  27. package/dist/composables/useGeneratedAnalysis.d.ts +1 -1
  28. package/dist/composables/usePluginClient.d.ts +11 -5
  29. package/dist/{composables-B512bWJP.js → composables-C0VEJ73O.js} +40 -5
  30. package/dist/composables-C0VEJ73O.js.map +1 -0
  31. package/dist/index.js +11 -11
  32. package/dist/install.js +5 -5
  33. package/dist/styles.css +314 -17
  34. package/dist/tailwind.preset.d.ts +5 -0
  35. package/dist/tailwind.preset.js +7 -2
  36. package/dist/tailwind.preset.js.map +1 -1
  37. package/dist/templates/index.js +3 -3
  38. package/dist/{templates-Cttbjbyc.js → templates-Cdj93S9C.js} +2 -2
  39. package/dist/{templates-Cttbjbyc.js.map → templates-Cdj93S9C.js.map} +1 -1
  40. package/dist/types/analysisArtifactTypes.d.ts +8 -0
  41. package/dist/types/components.d.ts +2 -0
  42. package/dist/types/generated-ui.d.ts +13 -0
  43. package/dist/types/index.d.ts +2 -2
  44. package/dist/{useControlSchema-Dallso-L.js → useControlSchema-B_qgU4rW.js} +48 -19
  45. package/dist/useControlSchema-B_qgU4rW.js.map +1 -0
  46. package/dist/{useExperimentSelector-BMjzNcf2.js → useExperimentSelector-BuQZRgMt.js} +2 -2
  47. package/dist/{useExperimentSelector-BMjzNcf2.js.map → useExperimentSelector-BuQZRgMt.js.map} +1 -1
  48. package/dist/{useFormBuilder-BXaaDI41.js → useFormBuilder-D1G5J5Z5.js} +2 -2
  49. package/dist/{useFormBuilder-BXaaDI41.js.map → useFormBuilder-D1G5J5Z5.js.map} +1 -1
  50. package/dist/{useProtocolTemplates-BP7_4RGQ.js → useProtocolTemplates-DeaOmMaw.js} +4 -4
  51. package/dist/{useProtocolTemplates-BP7_4RGQ.js.map → useProtocolTemplates-DeaOmMaw.js.map} +1 -1
  52. package/package.json +1 -1
  53. package/src/__tests__/components/BaseSelect.test.ts +52 -0
  54. package/src/__tests__/components/BaseToggle.test.ts +40 -0
  55. package/src/__tests__/components/GeneratedPluginView.test.ts +10 -0
  56. package/src/__tests__/components/PluginWorkspaceView.test.ts +68 -14
  57. package/src/__tests__/components/SectionCard.test.ts +181 -0
  58. package/src/__tests__/composables/analysisArtifactTarget.test.ts +77 -0
  59. package/src/__tests__/composables/useAnalysisArtifacts.test.ts +74 -1
  60. package/src/__tests__/composables/useGeneratedAnalysis.test.ts +2 -0
  61. package/src/__tests__/composables/usePluginClient.test.ts +36 -1
  62. package/src/__tests__/types/GeneratedJsonSchema.test.ts +62 -0
  63. package/src/components/BaseSelect.vue +16 -2
  64. package/src/components/BaseToggle.vue +3 -1
  65. package/src/components/GeneratedPluginView.vue +1 -1
  66. package/src/components/PluginWorkspaceView.props.ts +1 -1
  67. package/src/components/PluginWorkspaceView.vue +1 -5
  68. package/src/components/SectionCard.story.vue +163 -0
  69. package/src/components/SectionCard.vue +98 -0
  70. package/src/components/index.ts +1 -0
  71. package/src/composables/analysisArtifactTarget.ts +53 -0
  72. package/src/composables/index.ts +6 -0
  73. package/src/composables/platformContextHelpers.ts +10 -3
  74. package/src/composables/useAnalysisArtifacts.ts +48 -0
  75. package/src/composables/useGeneratedAnalysis.ts +30 -12
  76. package/src/composables/usePluginClient.ts +12 -5
  77. package/src/styles/components/button.css +1 -3
  78. package/src/styles/components/section-card.css +161 -0
  79. package/src/styles/index.css +1 -0
  80. package/src/styles/variables.css +25 -0
  81. package/src/tailwind.preset.ts +5 -0
  82. package/src/types/analysisArtifactTypes.ts +9 -0
  83. package/src/types/components.ts +12 -0
  84. package/src/types/generated-ui.ts +13 -0
  85. package/src/types/index.ts +3 -0
  86. package/dist/EmptyState-D1c4RdC7.js.map +0 -1
  87. package/dist/components-UfkPN1bK.js.map +0 -1
  88. package/dist/composables-B512bWJP.js.map +0 -1
  89. package/dist/useControlSchema-Dallso-L.js.map +0 -1
@@ -75,7 +75,10 @@ export interface UsePluginJobsReturn extends PluginJobCenterSource {
75
75
  status: ShallowRef<'idle' | 'initializing' | 'ready' | 'paused' | 'error' | 'disposed'>
76
76
  initialize: () => Promise<void>
77
77
  ready: () => Promise<void>
78
- uploadPath: (input: PluginJobPathInput) => Promise<PluginJobPathUpload>
78
+ uploadPath: (
79
+ input: PluginJobPathInput,
80
+ definitionId?: string,
81
+ ) => Promise<PluginJobPathUpload>
79
82
  uploadStagedPart: <Metadata>(
80
83
  input: PluginJobStagedPartInput<Metadata>,
81
84
  ) => Promise<PluginJobStagedPartUploadResult<Metadata>>
@@ -279,7 +282,7 @@ function createPluginJobs(
279
282
  jobInput = await submitToSession(
280
283
  session.value.id,
281
284
  clientRequestId,
282
- await stagePathInputs(inputs, session.value.id),
285
+ await stagePathInputs(inputs, session.value.id, definitionId),
283
286
  )
284
287
  } catch (cause) {
285
288
  if (!isSessionNotFoundError(cause)) throw cause
@@ -287,7 +290,7 @@ function createPluginJobs(
287
290
  jobInput = await submitToSession(
288
291
  session.value.id,
289
292
  clientRequestId,
290
- await stagePathInputs(inputs, session.value.id),
293
+ await stagePathInputs(inputs, session.value.id, definitionId),
291
294
  )
292
295
  }
293
296
  const job = upsertJob(jobInput, definitionId)
@@ -317,9 +320,12 @@ function createPluginJobs(
317
320
  }
318
321
  }
319
322
 
320
- async function uploadPath(input: PluginJobPathInput): Promise<PluginJobPathUpload> {
323
+ async function uploadPath(
324
+ input: PluginJobPathInput,
325
+ definitionId?: string,
326
+ ): Promise<PluginJobPathUpload> {
321
327
  if (!session.value) throw new Error('Generated analysis session is not initialized')
322
- return uploadPathForSession(input, session.value.id)
328
+ return uploadPathForSession(input, session.value.id, definitionId)
323
329
  }
324
330
 
325
331
  async function uploadStagedPart<Metadata>(
@@ -375,6 +381,7 @@ function createPluginJobs(
375
381
  async function uploadPathForSession(
376
382
  input: PluginJobPathInput,
377
383
  sessionId: string,
384
+ definitionId?: string,
378
385
  ): Promise<PluginJobPathUpload> {
379
386
  if (!input.files.length || input.files.length !== input.relativePaths.length) {
380
387
  throw new Error('Path upload requires one relative path for each file')
@@ -386,40 +393,51 @@ function createPluginJobs(
386
393
  body.append('files', input.files[index]!)
387
394
  body.append('relative_paths', input.relativePaths[index]!)
388
395
  }
396
+ const headers: Record<string, string> = {
397
+ 'X-Mint-Job-Session': sessionId,
398
+ }
399
+ if (definitionId) headers['X-Mint-Job-Definition'] = definitionId
389
400
  return request<PluginJobPathUpload>('/jobs/uploads', {
390
401
  method: 'POST',
391
402
  body,
392
403
  signal: input.signal,
393
- headers: { 'X-Mint-Job-Session': sessionId },
404
+ headers,
394
405
  })
395
406
  }
396
407
 
397
408
  async function stagePathInputs(
398
409
  inputs: Record<string, unknown>,
399
410
  sessionId: string,
411
+ definitionId: string,
400
412
  ): Promise<Record<string, unknown>> {
401
- return await stagePathValue(inputs, sessionId) as Record<string, unknown>
413
+ return await stagePathValue(inputs, sessionId, definitionId) as Record<string, unknown>
402
414
  }
403
415
 
404
- async function stagePathValue(value: unknown, sessionId: string): Promise<unknown> {
416
+ async function stagePathValue(
417
+ value: unknown,
418
+ sessionId: string,
419
+ definitionId: string,
420
+ ): Promise<unknown> {
405
421
  if (typeof File !== 'undefined' && value instanceof File) {
406
422
  const upload = await uploadPathForSession({
407
423
  kind: 'path',
408
424
  files: [value],
409
425
  relativePaths: [value.name],
410
- }, sessionId)
426
+ }, sessionId, definitionId)
411
427
  return { token: upload.token }
412
428
  }
413
429
  if (isPluginJobPathInput(value)) {
414
- const upload = await uploadPathForSession(value, sessionId)
430
+ const upload = await uploadPathForSession(value, sessionId, definitionId)
415
431
  return { token: upload.token }
416
432
  }
417
433
  if (Array.isArray(value)) {
418
- return await Promise.all(value.map(item => stagePathValue(item, sessionId)))
434
+ return await Promise.all(value.map(
435
+ item => stagePathValue(item, sessionId, definitionId),
436
+ ))
419
437
  }
420
438
  if (value && typeof value === 'object') {
421
439
  const entries = await Promise.all(Object.entries(value).map(async ([key, child]) => (
422
- [key, await stagePathValue(child, sessionId)] as const
440
+ [key, await stagePathValue(child, sessionId, definitionId)] as const
423
441
  )))
424
442
  return Object.fromEntries(entries)
425
443
  }
@@ -45,6 +45,7 @@ export type PluginHttpMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'
45
45
 
46
46
  export interface PluginEndpointContract {
47
47
  name: string
48
+ operationId?: string
48
49
  method: PluginHttpMethod
49
50
  path: string
50
51
  requiresAuth?: boolean
@@ -75,8 +76,16 @@ export interface PluginNavItemContract {
75
76
  requiresFeature?: string
76
77
  }
77
78
 
79
+ export interface PluginContractSettings {
80
+ modelName: string | null
81
+ /** Pydantic JSON Schema used to generate typed settings values. */
82
+ valueSchema: Record<string, unknown> | null
83
+ /** SDK SettingsModal schema used to render the settings form. */
84
+ formSchema: SettingsModalSchema | null
85
+ }
86
+
78
87
  export interface PluginContract {
79
- schemaVersion: number
88
+ schemaVersion: 2
80
89
  plugin: {
81
90
  name?: string
82
91
  packageName?: string
@@ -92,11 +101,9 @@ export interface PluginContract {
92
101
  analysisResultReaders?: string[]
93
102
  capabilities?: Record<string, unknown>
94
103
  }
95
- settings?: {
96
- modelName?: string | null
97
- schema?: SettingsModalSchema | null
98
- }
104
+ settings: PluginContractSettings
99
105
  endpoints: PluginEndpointContract[]
106
+ schemas?: Record<string, Record<string, unknown>>
100
107
  jobs?: GeneratedJobDefinition[]
101
108
  hash: string
102
109
  }
@@ -139,9 +139,7 @@
139
139
  .mint-button--sm {
140
140
  padding: 0.375rem 0.75rem;
141
141
  font-size: 0.875rem;
142
- /* Small buttons live in dense/secondary contexts — regular (400) weight
143
- keeps the compact label from reading too heavy. Applies to all variants. */
144
- font-weight: 400;
142
+ font-weight: 500;
145
143
  min-height: var(--form-height-sm);
146
144
  }
147
145
 
@@ -0,0 +1,161 @@
1
+ /* SectionCard — the MINT 1.1 one-card system.
2
+ *
3
+ * One card per page region: 12px radius, one shadow token (--card-shadow),
4
+ * a single header strip, and hairline-separated regions inside — never a
5
+ * card nested in a card.
6
+ */
7
+
8
+ .mint-section-card {
9
+ display: flex;
10
+ flex-direction: column;
11
+ border-radius: 12px;
12
+ border: 1px solid var(--border-color);
13
+ background: var(--bg-secondary);
14
+ box-shadow: var(--card-shadow);
15
+ overflow: hidden;
16
+ transition: background-color var(--mint-transition), border-color var(--mint-transition);
17
+ }
18
+
19
+ /* Fill the height the parent offers: the body takes the slack and scrolls,
20
+ * so the header and footer stay pinned to the card's edges. */
21
+ .mint-section-card--fill {
22
+ flex: 1;
23
+ min-height: 0;
24
+ }
25
+
26
+ .mint-section-card--fill .mint-section-card__body {
27
+ flex: 1;
28
+ min-height: 0;
29
+ overflow: auto;
30
+ }
31
+
32
+ .mint-section-card__header {
33
+ display: flex;
34
+ align-items: center;
35
+ gap: 0.625rem;
36
+ padding: 0.6875rem 1rem;
37
+ border-bottom: 1px solid var(--border-color);
38
+ flex-shrink: 0;
39
+ }
40
+
41
+ .mint-section-card__header--divider-light {
42
+ border-bottom-color: var(--border-light);
43
+ }
44
+
45
+ .mint-section-card__icon-chip {
46
+ display: inline-flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ flex-shrink: 0;
50
+ width: 1.375rem;
51
+ height: 1.375rem;
52
+ border-radius: 6px;
53
+ }
54
+
55
+ .mint-section-card__icon {
56
+ width: 0.8125rem;
57
+ height: 0.8125rem;
58
+ }
59
+
60
+ .mint-section-card__icon-chip--primary {
61
+ background: var(--color-primary-soft);
62
+ color: var(--color-primary);
63
+ }
64
+
65
+ .mint-section-card__icon-chip--experiment {
66
+ background: var(--color-experiment-soft);
67
+ color: var(--color-experiment);
68
+ }
69
+
70
+ .mint-section-card__icon-chip--project {
71
+ background: var(--color-project-soft);
72
+ color: var(--color-project);
73
+ }
74
+
75
+ .mint-section-card__icon-chip--success {
76
+ background: var(--mint-success-bg);
77
+ color: var(--mint-success);
78
+ }
79
+
80
+ .mint-section-card__icon-chip--error {
81
+ background: var(--mint-error-bg);
82
+ color: var(--mint-error);
83
+ }
84
+
85
+ .mint-section-card__icon-chip--warning {
86
+ background: var(--mint-warning-bg);
87
+ color: var(--mint-warning);
88
+ }
89
+
90
+ .mint-section-card__icon-chip--info {
91
+ background: var(--mint-info-bg);
92
+ color: var(--mint-info);
93
+ }
94
+
95
+ .mint-section-card__title {
96
+ margin: 0;
97
+ font-size: 0.8125rem;
98
+ font-weight: 600;
99
+ color: var(--text-primary);
100
+ }
101
+
102
+ .mint-section-card__title--uppercase {
103
+ font-size: 0.75rem;
104
+ letter-spacing: 0.08em;
105
+ text-transform: uppercase;
106
+ color: var(--text-secondary);
107
+ }
108
+
109
+ .mint-section-card__count {
110
+ font-family: 'Fira Code', monospace;
111
+ font-size: 0.6875rem;
112
+ color: var(--text-muted);
113
+ }
114
+
115
+ .mint-section-card__spacer {
116
+ flex: 1;
117
+ }
118
+
119
+ .mint-section-card__toolbar {
120
+ flex-shrink: 0;
121
+ }
122
+
123
+ .mint-section-card__footer {
124
+ display: flex;
125
+ align-items: center;
126
+ justify-content: space-between;
127
+ padding: 0.5rem 1rem;
128
+ border-top: 1px solid var(--border-color);
129
+ background: var(--bg-primary);
130
+ flex-shrink: 0;
131
+ }
132
+
133
+ /* Hairline row helpers — regions inside a card separate with 1px lines,
134
+ * rendered by a 1px gap over a hairline-colored track. */
135
+ .mint-section-card__rows {
136
+ display: flex;
137
+ flex-direction: column;
138
+ gap: 1px;
139
+ background: var(--border-light);
140
+ }
141
+
142
+ .mint-section-card__row {
143
+ display: flex;
144
+ align-items: center;
145
+ gap: 0.625rem;
146
+ padding: 0.5625rem 1rem;
147
+ background: var(--bg-secondary);
148
+ transition: background-color var(--mint-transition);
149
+ }
150
+
151
+ a.mint-section-card__row,
152
+ button.mint-section-card__row {
153
+ cursor: pointer;
154
+ color: inherit;
155
+ text-decoration: none;
156
+ }
157
+
158
+ a.mint-section-card__row:hover,
159
+ button.mint-section-card__row:hover {
160
+ background: var(--bg-primary);
161
+ }
@@ -28,6 +28,7 @@
28
28
  @import './components/plate-map-editor.css';
29
29
  @import './components/radio-group.css';
30
30
  @import './components/sample-legend.css';
31
+ @import './components/section-card.css';
31
32
  @import './components/select.css';
32
33
  @import './components/skeleton.css';
33
34
  @import './components/slider.css';
@@ -36,6 +36,18 @@
36
36
  --color-cta-hover: #EA580C;
37
37
  --color-purple: #8B5CF6;
38
38
 
39
+ /* Entity accent colors — experiments (cyan) and projects (sky), from the
40
+ MINT 1.1 card system. -soft backs icon chips and pills, -border outlines
41
+ pills that sit on a card surface. */
42
+ --color-experiment: #06B6D4;
43
+ --color-experiment-hover: #0891B2;
44
+ --color-experiment-soft: rgba(6, 182, 212, 0.12);
45
+ --color-experiment-border: rgba(6, 182, 212, 0.3);
46
+ --color-project: #0EA5E9;
47
+ --color-project-hover: #0284C7;
48
+ --color-project-soft: rgba(14, 165, 233, 0.12);
49
+ --color-project-border: rgba(14, 165, 233, 0.32);
50
+
39
51
  /* MINT brand mark — used for browser theme-color, marketing chrome, app icon background */
40
52
  --mint-brand: #7BD0B5;
41
53
  --mint-brand-hover: #5FB89A;
@@ -51,6 +63,8 @@
51
63
  --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
52
64
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
53
65
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
66
+ /* The MINT 1.1 card system uses exactly one shadow for every card. */
67
+ --card-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.04), 0 1px 3px 0 rgb(0 0 0 / 0.06);
54
68
 
55
69
  /* Border radius */
56
70
  --radius-sm: 0.25rem;
@@ -148,6 +162,17 @@ html.dark {
148
162
  --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3), 0 1px 2px -1px rgb(0 0 0 / 0.2);
149
163
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.3), 0 2px 4px -2px rgb(0 0 0 / 0.2);
150
164
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.3), 0 4px 6px -4px rgb(0 0 0 / 0.2);
165
+ --card-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.24), 0 1px 3px 0 rgb(0 0 0 / 0.32);
166
+
167
+ /* Entity accent colors - Dark mode */
168
+ --color-experiment: #22D3EE;
169
+ --color-experiment-hover: #67E8F9;
170
+ --color-experiment-soft: rgba(34, 211, 238, 0.16);
171
+ --color-experiment-border: rgba(34, 211, 238, 0.36);
172
+ --color-project: #38BDF8;
173
+ --color-project-hover: #7DD3FC;
174
+ --color-project-soft: rgba(56, 189, 248, 0.16);
175
+ --color-project-border: rgba(56, 189, 248, 0.38);
151
176
 
152
177
  /* Semantic colors - Dark mode */
153
178
  --mint-success-bg: rgba(16, 185, 129, 0.15);
@@ -18,6 +18,10 @@ const mintPreset = {
18
18
  'warning-hover': 'var(--mint-warning-hover)',
19
19
  brand: 'var(--mint-brand)',
20
20
  'brand-hover': 'var(--mint-brand-hover)',
21
+ experiment: 'var(--color-experiment)',
22
+ 'experiment-hover': 'var(--color-experiment-hover)',
23
+ project: 'var(--color-project)',
24
+ 'project-hover': 'var(--color-project-hover)',
21
25
  },
22
26
  bg: {
23
27
  primary: 'var(--mint-bg-primary)',
@@ -50,6 +54,7 @@ const mintPreset = {
50
54
  mint: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
51
55
  'mint-md': '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
52
56
  'mint-lg': '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
57
+ 'mint-card': 'var(--card-shadow)',
53
58
  },
54
59
  transitionDuration: {
55
60
  mint: '150ms',
@@ -38,6 +38,15 @@ export interface AnalysisArtifactListResponse {
38
38
  artifacts: AnalysisArtifactSummary[]
39
39
  }
40
40
 
41
+ /** Stable identity used when one plugin opens another plugin's artifact. */
42
+ export interface AnalysisArtifactTarget {
43
+ experimentId: number
44
+ artifactPluginId: string
45
+ artifactKey: string
46
+ /** Optional lookup hint. The stable identity remains the three fields above. */
47
+ artifactId?: number
48
+ }
49
+
41
50
  /** Body of PATCH /experiments/{id}/analysis-artifacts/{artifactId}. */
42
51
  export interface AnalysisArtifactMetadataUpdate {
43
52
  display_name?: string
@@ -66,6 +66,18 @@ export interface FormFieldProps {
66
66
  required?: boolean
67
67
  }
68
68
 
69
+ // Section card types
70
+ export type SectionCardAccent =
71
+ | 'primary'
72
+ | 'experiment'
73
+ | 'project'
74
+ | 'success'
75
+ | 'error'
76
+ | 'warning'
77
+ | 'info'
78
+
79
+ export type SectionCardDivider = 'strong' | 'light'
80
+
69
81
  // Sidebar types
70
82
  export type SidebarBadgeTone =
71
83
  | 'primary'
@@ -10,14 +10,25 @@ export interface GeneratedJsonSchema {
10
10
  enum?: unknown[]
11
11
  minimum?: number
12
12
  maximum?: number
13
+ exclusiveMinimum?: number
13
14
  minLength?: number
14
15
  maxLength?: number
16
+ pattern?: string
17
+ minItems?: number
18
+ maxItems?: number
19
+ writeOnly?: boolean
15
20
  properties?: Record<string, GeneratedJsonSchema>
16
21
  additionalProperties?: boolean | GeneratedJsonSchema
17
22
  required?: string[]
18
23
  anyOf?: GeneratedJsonSchema[]
19
24
  allOf?: GeneratedJsonSchema[]
25
+ oneOf?: GeneratedJsonSchema[]
26
+ prefixItems?: GeneratedJsonSchema[]
20
27
  items?: GeneratedJsonSchema
28
+ discriminator?: {
29
+ propertyName: string
30
+ mapping?: Record<string, string>
31
+ }
21
32
  $ref?: string
22
33
  $defs?: Record<string, GeneratedJsonSchema>
23
34
  'x-mint-component'?: 'path'
@@ -102,6 +113,8 @@ export interface GeneratedJobDefinition {
102
113
  title: string
103
114
  description: string
104
115
  cpu: number
116
+ /** Whether integrated submission requires a platform or current-plugin administrator. */
117
+ requires_admin?: boolean
105
118
  /** Defaults to process for manifests generated before lifecycle profiles. */
106
119
  lifecycle?: GeneratedJobLifecycle
107
120
  /** Defaults to `{ kind: 'process' }` for older manifests. */
@@ -16,6 +16,8 @@ export type {
16
16
  RadioOption,
17
17
  RadioOptionInput,
18
18
  FormFieldProps,
19
+ SectionCardAccent,
20
+ SectionCardDivider,
19
21
  SidebarBadgeTone,
20
22
  SidebarToolSection,
21
23
  SidebarToolSectionAction,
@@ -184,6 +186,7 @@ export type {
184
186
  AnalysisArtifactSummary,
185
187
  AnalysisArtifactDetail,
186
188
  AnalysisArtifactListResponse,
189
+ AnalysisArtifactTarget,
187
190
  AnalysisArtifactMetadataUpdate,
188
191
  ArtifactSaveMode,
189
192
  ArtifactFileSource,
@@ -1 +0,0 @@
1
- {"version":3,"file":"EmptyState-D1c4RdC7.js","names":[],"sources":["../src/components/BaseInput.vue","../src/components/BaseInput.vue","../src/utils/options.ts","../src/components/BaseSelect.vue","../src/components/BaseSelect.vue","../src/components/BasePill.vue","../src/components/BasePill.vue","../src/composables/useApi.ts","../src/composables/useRequestSyncState.ts","../src/components/Skeleton.vue","../src/components/Skeleton.vue","../src/components/EmptyState.vue","../src/components/EmptyState.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/** Renders a styled `<input>` supporting text/number/email types, size, error, and readonly states. */\nimport type { InputType } from '../types'\n\ninterface Props {\n modelValue?: string | number\n type?: InputType\n placeholder?: string\n disabled?: boolean\n readonly?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n autocomplete?: string\n autofocus?: boolean\n min?: number\n max?: number\n step?: number\n list?: string\n ariaDescribedby?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n type: 'text',\n disabled: false,\n readonly: false,\n error: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number | null]\n focus: [event: FocusEvent]\n blur: [event: FocusEvent]\n keydown: [event: KeyboardEvent]\n}>()\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = props.type === 'number'\n ? (target.value === '' ? null : Number(target.value))\n : target.value\n emit('update:modelValue', value as string | number)\n}\n</script>\n\n<template>\n <input\n :type=\"type\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :autocomplete=\"autocomplete\"\n :autofocus=\"autofocus\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :list=\"list\"\n :aria-invalid=\"error || undefined\"\n :aria-describedby=\"ariaDescribedby || undefined\"\n :class=\"[\n 'mint-input',\n `mint-input--${size}`,\n error ? 'mint-input--error' : '',\n disabled ? 'mint-input--disabled' : '',\n ]\"\n @input=\"handleInput\"\n @focus=\"emit('focus', $event)\"\n @blur=\"emit('blur', $event)\"\n @keydown=\"emit('keydown', $event)\"\n />\n</template>\n\n<style>\n@import '../styles/components/input.css';\n</style>\n","<script setup lang=\"ts\">\n/** Renders a styled `<input>` supporting text/number/email types, size, error, and readonly states. */\nimport type { InputType } from '../types'\n\ninterface Props {\n modelValue?: string | number\n type?: InputType\n placeholder?: string\n disabled?: boolean\n readonly?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n autocomplete?: string\n autofocus?: boolean\n min?: number\n max?: number\n step?: number\n list?: string\n ariaDescribedby?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n type: 'text',\n disabled: false,\n readonly: false,\n error: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number | null]\n focus: [event: FocusEvent]\n blur: [event: FocusEvent]\n keydown: [event: KeyboardEvent]\n}>()\n\nfunction handleInput(event: Event) {\n const target = event.target as HTMLInputElement\n const value = props.type === 'number'\n ? (target.value === '' ? null : Number(target.value))\n : target.value\n emit('update:modelValue', value as string | number)\n}\n</script>\n\n<template>\n <input\n :type=\"type\"\n :value=\"modelValue\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :autocomplete=\"autocomplete\"\n :autofocus=\"autofocus\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n :list=\"list\"\n :aria-invalid=\"error || undefined\"\n :aria-describedby=\"ariaDescribedby || undefined\"\n :class=\"[\n 'mint-input',\n `mint-input--${size}`,\n error ? 'mint-input--error' : '',\n disabled ? 'mint-input--disabled' : '',\n ]\"\n @input=\"handleInput\"\n @focus=\"emit('focus', $event)\"\n @blur=\"emit('blur', $event)\"\n @keydown=\"emit('keydown', $event)\"\n />\n</template>\n\n<style>\n@import '../styles/components/input.css';\n</style>\n","export type OptionLike<TValue extends string | number> = {\n value: TValue\n label: string\n}\n\nexport function normalizeOptionInput<\n TValue extends string | number,\n TOption extends OptionLike<TValue>,\n>(option: TOption | TValue): TOption {\n if (typeof option === 'object') return option\n\n return {\n value: option,\n label: String(option),\n } as TOption\n}\n\nexport function findOptionByDomValue<\n TValue extends string | number,\n TOption extends OptionLike<TValue>,\n>(options: TOption[], value: string): TOption | undefined {\n return options.find(option => String(option.value) === value)\n}\n","<script setup lang=\"ts\">\n/** Renders a native `<select>` element with size, error, and disabled states. */\nimport { computed } from 'vue'\nimport type { SelectOption, SelectOptionInput } from '../types'\nimport { findOptionByDomValue, normalizeOptionInput } from '../utils/options'\n\ninterface Props {\n modelValue?: string | number\n options: SelectOptionInput<string | number>[]\n placeholder?: string\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n ariaDescribedby?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n error: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number]\n}>()\n\nconst normalizedOptions = computed<SelectOption<string | number>[]>(() =>\n props.options.map(normalizeOptionInput)\n)\n\nfunction handleChange(event: Event) {\n const target = event.target as HTMLSelectElement\n const option = findOptionByDomValue(normalizedOptions.value, target.value)\n emit('update:modelValue', option?.value ?? target.value)\n}\n</script>\n\n<template>\n <div class=\"mint-select\">\n <select\n :value=\"modelValue\"\n :disabled=\"disabled\"\n :aria-invalid=\"error || undefined\"\n :aria-describedby=\"ariaDescribedby || undefined\"\n :class=\"[\n 'mint-select__control',\n `mint-select__control--${size}`,\n error ? 'mint-select__control--error' : '',\n disabled ? 'mint-select__control--disabled' : '',\n ]\"\n @change=\"handleChange\"\n >\n <option v-if=\"placeholder\" value=\"\" disabled>\n {{ placeholder }}\n </option>\n <option\n v-for=\"option in normalizedOptions\"\n :key=\"String(option.value)\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ option.label }}\n </option>\n </select>\n <div class=\"mint-select__icon\">\n <svg fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/select.css';\n</style>\n","<script setup lang=\"ts\">\n/** Renders a native `<select>` element with size, error, and disabled states. */\nimport { computed } from 'vue'\nimport type { SelectOption, SelectOptionInput } from '../types'\nimport { findOptionByDomValue, normalizeOptionInput } from '../utils/options'\n\ninterface Props {\n modelValue?: string | number\n options: SelectOptionInput<string | number>[]\n placeholder?: string\n disabled?: boolean\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n ariaDescribedby?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n disabled: false,\n error: false,\n size: 'md',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: string | number]\n}>()\n\nconst normalizedOptions = computed<SelectOption<string | number>[]>(() =>\n props.options.map(normalizeOptionInput)\n)\n\nfunction handleChange(event: Event) {\n const target = event.target as HTMLSelectElement\n const option = findOptionByDomValue(normalizedOptions.value, target.value)\n emit('update:modelValue', option?.value ?? target.value)\n}\n</script>\n\n<template>\n <div class=\"mint-select\">\n <select\n :value=\"modelValue\"\n :disabled=\"disabled\"\n :aria-invalid=\"error || undefined\"\n :aria-describedby=\"ariaDescribedby || undefined\"\n :class=\"[\n 'mint-select__control',\n `mint-select__control--${size}`,\n error ? 'mint-select__control--error' : '',\n disabled ? 'mint-select__control--disabled' : '',\n ]\"\n @change=\"handleChange\"\n >\n <option v-if=\"placeholder\" value=\"\" disabled>\n {{ placeholder }}\n </option>\n <option\n v-for=\"option in normalizedOptions\"\n :key=\"String(option.value)\"\n :value=\"option.value\"\n :disabled=\"option.disabled\"\n >\n {{ option.label }}\n </option>\n </select>\n <div class=\"mint-select__icon\">\n <svg fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/select.css';\n</style>\n","<script setup lang=\"ts\">\n/** Compact label for tags, status indicators, and badges; supports removable and icon slots. */\nimport type { PillVariant, PillColor, PillSize } from '../types'\n\n/**\n * BasePill - Compact label component for tags, status indicators, and badges.\n *\n * @example\n * ```vue\n * <BasePill variant=\"success\">Active</BasePill>\n * <BasePill variant=\"warning\" removable @remove=\"handleRemove\">Tag</BasePill>\n * <BasePill :icon=\"true\">\n * <template #icon><CheckIcon /></template>\n * Verified\n * </BasePill>\n * ```\n */\ninterface Props {\n /** Visual style variant */\n variant?: PillVariant\n /** Semantic color modifier — use with variant=\"outline\" for colored outlines */\n color?: PillColor\n /** Size of the pill */\n size?: PillSize\n /** Show remove button */\n removable?: boolean\n /** Disable interaction */\n disabled?: boolean\n /** Show icon slot */\n icon?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'default',\n color: undefined,\n size: 'md',\n removable: false,\n disabled: false,\n icon: false,\n})\n\n/**\n * @event remove - Emitted when the remove button is clicked\n */\nconst emit = defineEmits<{\n remove: []\n}>()\n\nfunction handleRemove(event: MouseEvent) {\n event.stopPropagation()\n if (!props.disabled) {\n emit('remove')\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mint-pill',\n `mint-pill--${variant}`,\n color && `mint-pill--${color}`,\n `mint-pill--${size}`,\n { 'mint-pill--disabled': disabled, 'mint-pill--with-icon': icon },\n ]\"\n >\n <span v-if=\"icon\" class=\"mint-pill__icon\">\n <slot name=\"icon\" />\n </span>\n <span class=\"mint-pill__label\">\n <slot />\n </span>\n <button\n v-if=\"removable && !disabled\"\n type=\"button\"\n class=\"mint-pill__remove\"\n aria-label=\"Remove\"\n @click=\"handleRemove\"\n >\n <svg class=\"mint-pill__remove-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </span>\n</template>\n\n<style>\n@import '../styles/components/pill.css';\n</style>\n","<script setup lang=\"ts\">\n/** Compact label for tags, status indicators, and badges; supports removable and icon slots. */\nimport type { PillVariant, PillColor, PillSize } from '../types'\n\n/**\n * BasePill - Compact label component for tags, status indicators, and badges.\n *\n * @example\n * ```vue\n * <BasePill variant=\"success\">Active</BasePill>\n * <BasePill variant=\"warning\" removable @remove=\"handleRemove\">Tag</BasePill>\n * <BasePill :icon=\"true\">\n * <template #icon><CheckIcon /></template>\n * Verified\n * </BasePill>\n * ```\n */\ninterface Props {\n /** Visual style variant */\n variant?: PillVariant\n /** Semantic color modifier — use with variant=\"outline\" for colored outlines */\n color?: PillColor\n /** Size of the pill */\n size?: PillSize\n /** Show remove button */\n removable?: boolean\n /** Disable interaction */\n disabled?: boolean\n /** Show icon slot */\n icon?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'default',\n color: undefined,\n size: 'md',\n removable: false,\n disabled: false,\n icon: false,\n})\n\n/**\n * @event remove - Emitted when the remove button is clicked\n */\nconst emit = defineEmits<{\n remove: []\n}>()\n\nfunction handleRemove(event: MouseEvent) {\n event.stopPropagation()\n if (!props.disabled) {\n emit('remove')\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mint-pill',\n `mint-pill--${variant}`,\n color && `mint-pill--${color}`,\n `mint-pill--${size}`,\n { 'mint-pill--disabled': disabled, 'mint-pill--with-icon': icon },\n ]\"\n >\n <span v-if=\"icon\" class=\"mint-pill__icon\">\n <slot name=\"icon\" />\n </span>\n <span class=\"mint-pill__label\">\n <slot />\n </span>\n <button\n v-if=\"removable && !disabled\"\n type=\"button\"\n class=\"mint-pill__remove\"\n aria-label=\"Remove\"\n @click=\"handleRemove\"\n >\n <svg class=\"mint-pill__remove-icon\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" viewBox=\"0 0 24 24\">\n <path d=\"M18 6 6 18\" /><path d=\"m6 6 12 12\" />\n </svg>\n </button>\n </span>\n</template>\n\n<style>\n@import '../styles/components/pill.css';\n</style>\n","import axios, { type AxiosInstance, type AxiosRequestConfig, type InternalAxiosRequestConfig } from 'axios'\nimport { useSettingsStore } from '../stores/settings'\nimport { useAuthStore } from '../stores/auth'\n\nlet apiClientInstance: AxiosInstance | null = null\nlet interceptorAttached = false\n\ninterface MintAxiosRequestConfig extends AxiosRequestConfig {\n _mintSkipAuth?: boolean\n _mintAuthToken?: string\n}\n\ninterface MintInternalAxiosRequestConfig extends InternalAxiosRequestConfig {\n _mintSkipAuth?: boolean\n _mintAuthToken?: string\n}\n\ntype MutableHeaders = Record<string, unknown> & {\n has?: (header: string) => boolean\n set?: (header: string, value: string) => void\n delete?: (header: string) => void\n}\n\nfunction joinUrlPath(baseUrl: string, path: string): string {\n if (/^https?:\\/\\//i.test(path)) return path\n if (!path) return baseUrl\n if (path.startsWith('?') || path.startsWith('#')) return `${baseUrl.replace(/\\/+$/, '')}${path}`\n const normalizedBase = baseUrl.replace(/\\/+$/, '')\n const normalizedPath = path.replace(/^\\/+/, '/')\n return `${normalizedBase}${normalizedPath.startsWith('/') ? normalizedPath : `/${normalizedPath}`}`\n}\n\nfunction getBasePath(baseUrl: string): string {\n if (!baseUrl) return '/'\n\n try {\n const origin = typeof window !== 'undefined' ? window.location.origin : 'http://localhost'\n return new URL(baseUrl, origin).pathname.replace(/\\/+$/, '') || '/'\n } catch {\n const path = baseUrl.replace(/^https?:\\/\\/[^/]+/i, '')\n return path.replace(/\\/+$/, '') || '/'\n }\n}\n\nfunction normalizeRequestUrl(baseUrl: string, url: string): string {\n if (!url || /^https?:\\/\\//.test(url)) return url\n\n const basePath = getBasePath(baseUrl)\n if (basePath === '/') return url\n\n const normalizedUrl = url.startsWith('/') ? url : `/${url}`\n if (\n normalizedUrl === basePath\n || normalizedUrl.startsWith(`${basePath}?`)\n || normalizedUrl.startsWith(`${basePath}#`)\n ) {\n return normalizedUrl.slice(basePath.length)\n }\n if (normalizedUrl.startsWith(`${basePath}/`)) {\n return normalizedUrl.slice(basePath.length) || ''\n }\n return url\n}\n\nfunction asMutableHeaders(headers: AxiosRequestConfig['headers']): MutableHeaders | null {\n return headers ? (headers as MutableHeaders) : null\n}\n\nfunction hasAuthorizationHeader(headers: AxiosRequestConfig['headers']): boolean {\n const bag = asMutableHeaders(headers)\n if (!bag) return false\n if (typeof bag.has === 'function') return bag.has('Authorization')\n return Object.keys(bag).some((key) => key.toLowerCase() === 'authorization')\n}\n\nfunction setAuthorizationHeader(headers: AxiosRequestConfig['headers'], value: string): void {\n const bag = asMutableHeaders(headers)\n if (!bag) return\n if (typeof bag.set === 'function') {\n bag.set('Authorization', value)\n return\n }\n bag.Authorization = value\n}\n\nfunction deleteAuthorizationHeader(headers: AxiosRequestConfig['headers']): void {\n const bag = asMutableHeaders(headers)\n if (!bag) return\n if (typeof bag.delete === 'function') {\n bag.delete('Authorization')\n return\n }\n for (const key of Object.keys(bag)) {\n if (key.toLowerCase() === 'authorization') delete bag[key]\n }\n}\n\nfunction getAuthorizationHeader(headers: AxiosRequestConfig['headers']): unknown {\n const bag = asMutableHeaders(headers) as (MutableHeaders & { get?: (header: string) => unknown }) | null\n if (!bag) return undefined\n if (typeof bag.get === 'function') return bag.get('Authorization')\n const key = Object.keys(bag).find((candidate) => candidate.toLowerCase() === 'authorization')\n return key ? bag[key] : undefined\n}\n\n/** Raised when an authenticated SDK request is rejected because the login is no longer valid. */\nexport class AuthenticationRequiredError extends Error {\n readonly code = 'AUTHENTICATION_REQUIRED'\n readonly status = 401\n\n constructor(readonly cause?: unknown) {\n super('Authentication required. Please sign in again.')\n this.name = 'AuthenticationRequiredError'\n }\n}\n\n/** @internal Convert a rejected authenticated request into the SDK's public auth error. */\nexport function authenticationRequiredError(\n cause: unknown,\n rejectedToken?: string,\n): AuthenticationRequiredError {\n const currentAuthStore = useAuthStore()\n if (rejectedToken && currentAuthStore.token === rejectedToken) {\n currentAuthStore.clearToken()\n }\n return new AuthenticationRequiredError(cause)\n}\n\nfunction getApiClient(): AxiosInstance {\n if (!apiClientInstance) {\n apiClientInstance = axios.create({\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n }\n return apiClientInstance\n}\n\nexport interface ApiClientOptions {\n baseUrl?: string\n timeout?: number\n withAuth?: boolean\n}\n\nexport interface UseApiReturn {\n client: AxiosInstance\n get: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>\n post: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => Promise<T>\n put: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => Promise<T>\n patch: <T>(url: string, data?: unknown, config?: AxiosRequestConfig) => Promise<T>\n delete: <T>(url: string, config?: AxiosRequestConfig) => Promise<T>\n upload: <T>(url: string, file: File, fieldName?: string, additionalData?: Record<string, unknown>) => Promise<T>\n download: (url: string, filename?: string) => Promise<string>\n buildUrl: (path: string) => string\n buildWsUrl: (path: string) => string\n}\n\n/** Axios-based API client that injects the plugin base URL and JWT auth header on every request. */\nexport function useApi(options: ApiClientOptions = {}): UseApiReturn {\n const settingsStore = useSettingsStore()\n const authStore = useAuthStore()\n const apiClient = getApiClient()\n\n // Ensure auth store is initialized (reads token from localStorage)\n if (!authStore.isInitialized) {\n authStore.initialize()\n }\n\n // Attach auth interceptor only once (reads token dynamically, not from closure)\n if (!interceptorAttached) {\n apiClient.interceptors.request.use((config) => {\n const request = config as MintInternalAxiosRequestConfig\n if (request._mintSkipAuth) {\n deleteAuthorizationHeader(request.headers)\n return request\n }\n\n const currentAuthStore = useAuthStore()\n if (currentAuthStore.token && config.headers && !hasAuthorizationHeader(config.headers)) {\n setAuthorizationHeader(config.headers, `Bearer ${currentAuthStore.token}`)\n }\n if (getAuthorizationHeader(config.headers) === `Bearer ${currentAuthStore.token}`) {\n request._mintAuthToken = currentAuthStore.token ?? undefined\n }\n return config\n })\n apiClient.interceptors.response.use(\n (response) => response,\n (error: unknown) => {\n if (axios.isAxiosError(error) && error.response?.status === 401) {\n const request = error.config as MintInternalAxiosRequestConfig | undefined\n if (request?._mintSkipAuth !== true) {\n return Promise.reject(authenticationRequiredError(error, request?._mintAuthToken))\n }\n }\n return Promise.reject(error)\n },\n )\n interceptorAttached = true\n }\n\n function getBaseUrl(): string {\n return options.baseUrl ?? settingsStore.getApiBaseUrl()\n }\n\n function normalizeUrl(url: string): string {\n return normalizeRequestUrl(getBaseUrl(), url)\n }\n\n // Build per-request config that applies this caller's options\n function requestConfig(config?: AxiosRequestConfig): MintAxiosRequestConfig {\n const base: MintAxiosRequestConfig = {\n baseURL: getBaseUrl(),\n timeout: options.timeout ?? settingsStore.requestTimeout,\n ...config,\n }\n // Strip auth header if explicitly disabled\n if (options.withAuth === false) {\n base._mintSkipAuth = true\n deleteAuthorizationHeader(base.headers)\n }\n return base\n }\n\n // Generic request methods\n async function get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.get<T>(normalizeUrl(url), requestConfig(config))\n return response.data\n }\n\n async function post<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.post<T>(normalizeUrl(url), data, requestConfig(config))\n return response.data\n }\n\n async function put<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.put<T>(normalizeUrl(url), data, requestConfig(config))\n return response.data\n }\n\n async function patch<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.patch<T>(normalizeUrl(url), data, requestConfig(config))\n return response.data\n }\n\n async function del<T>(url: string, config?: AxiosRequestConfig): Promise<T> {\n const response = await apiClient.delete<T>(normalizeUrl(url), requestConfig(config))\n return response.data\n }\n\n // File upload helper\n async function upload<T>(url: string, file: File, fieldName = 'file', additionalData?: Record<string, unknown>): Promise<T> {\n const formData = new FormData()\n formData.append(fieldName, file)\n\n if (additionalData) {\n Object.entries(additionalData).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n formData.append(key, typeof value === 'object' ? JSON.stringify(value) : String(value))\n }\n })\n }\n\n const response = await apiClient.post<T>(normalizeUrl(url), formData, requestConfig({\n // Let Axios set Content-Type with the correct multipart boundary\n headers: { 'Content-Type': undefined },\n }))\n return response.data\n }\n\n // Download helper - returns blob URL\n async function download(url: string, filename?: string): Promise<string> {\n const response = await apiClient.get(normalizeUrl(url), requestConfig({ responseType: 'blob' }))\n const blob = new Blob([response.data])\n const blobUrl = URL.createObjectURL(blob)\n\n // Optionally trigger download\n if (filename) {\n const link = document.createElement('a')\n link.href = blobUrl\n link.download = filename\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n // Revoke after a short delay to allow download to start;\n // return empty string since the URL will be invalidated\n setTimeout(() => URL.revokeObjectURL(blobUrl), 100)\n return ''\n }\n\n // Caller is responsible for revoking when no filename is provided\n return blobUrl\n }\n\n // Build full URL for external use (e.g., <a href=\"...\">)\n function buildUrl(path: string): string {\n const baseUrl = getBaseUrl()\n return joinUrlPath(baseUrl, normalizeRequestUrl(baseUrl, path))\n }\n\n // WebSocket URL builder\n function buildWsUrl(path: string): string {\n return joinUrlPath(settingsStore.getWsBaseUrl(), path)\n }\n\n return {\n client: apiClient,\n get,\n post,\n put,\n patch,\n delete: del,\n upload,\n download,\n buildUrl,\n buildWsUrl,\n }\n}\n","import { ref, type Ref } from 'vue'\n\nexport type RequestSyncSuccessKind = 'load' | 'save' | 'run'\n\nexport interface RequestSyncRunOptions {\n success?: RequestSyncSuccessKind\n errorMessage?: string\n}\n\nexport interface UseRequestSyncStateReturn {\n /** Whether a request is in progress. */\n loading: Ref<boolean>\n /** Message from the last failed request, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful load operation, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Timestamp of the last successful save operation, or null. */\n lastSavedAt: Ref<Date | null>\n /** Timestamp of the last successful run operation, or null. */\n lastRunAt: Ref<Date | null>\n /** Clear the current error without changing loading or timestamps. */\n clearError: () => void\n /** Convert thrown values into a developer-facing error message. */\n readErrorMessage: (value: unknown, fallback?: string) => string\n /** Store and return a normalized error message. */\n setError: (value: unknown, fallback?: string) => string\n /** Mark the resource as successfully loaded. */\n markLoaded: (date?: Date) => void\n /** Mark the resource as successfully saved. */\n markSaved: (date?: Date) => void\n /** Mark the operation as successfully run. */\n markRun: (date?: Date) => void\n /** Run a request with shared loading/error handling and optional sync marking. */\n run: <T>(operation: () => Promise<T>, options?: RequestSyncRunOptions) => Promise<T>\n}\n\n/** Shared loading/error/timestamp state for generated plugin request helpers. */\nexport function useRequestSyncState(\n defaultErrorMessage = 'Request failed.',\n): UseRequestSyncStateReturn {\n const loading = ref(false)\n const error = ref<string | null>(null)\n const lastLoadedAt = ref<Date | null>(null)\n const lastSavedAt = ref<Date | null>(null)\n const lastRunAt = ref<Date | null>(null)\n\n function clearError(): void {\n error.value = null\n }\n\n function readErrorMessage(value: unknown, fallback = defaultErrorMessage): string {\n if (value instanceof Error && value.message) {\n return value.message\n }\n if (typeof value === 'string' && value.trim()) {\n return value\n }\n if (\n typeof value === 'object'\n && value !== null\n && 'message' in value\n && typeof (value as { message?: unknown }).message === 'string'\n && (value as { message: string }).message\n ) {\n return (value as { message: string }).message\n }\n return fallback\n }\n\n function setError(value: unknown, fallback?: string): string {\n const message = readErrorMessage(value, fallback)\n error.value = message\n return message\n }\n\n function markLoaded(date = new Date()): void {\n lastLoadedAt.value = date\n }\n\n function markSaved(date = new Date()): void {\n lastSavedAt.value = date\n }\n\n function markRun(date = new Date()): void {\n lastRunAt.value = date\n }\n\n async function run<T>(\n operation: () => Promise<T>,\n options: RequestSyncRunOptions = {},\n ): Promise<T> {\n loading.value = true\n clearError()\n try {\n const response = await operation()\n if (options.success === 'load') {\n markLoaded()\n } else if (options.success === 'save') {\n markSaved()\n } else if (options.success === 'run') {\n markRun()\n }\n return response\n } catch (err) {\n setError(err, options.errorMessage)\n throw err\n } finally {\n loading.value = false\n }\n }\n\n return {\n loading,\n error,\n lastLoadedAt,\n lastSavedAt,\n lastRunAt,\n clearError,\n readErrorMessage,\n setError,\n markLoaded,\n markSaved,\n markRun,\n run,\n }\n}\n","<script setup lang=\"ts\">\n/** Animated loading placeholder in text, circular, rectangular, or rounded variants with pulse or wave animation. */\nimport { computed } from 'vue'\n\ninterface Props {\n variant?: 'text' | 'circular' | 'rectangular' | 'rounded'\n width?: string | number\n height?: string | number\n animation?: 'pulse' | 'wave' | 'none'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'text',\n animation: 'wave',\n})\n\nconst style = computed(() => {\n const s: Record<string, string> = {}\n\n if (props.width) {\n s.width = typeof props.width === 'number' ? `${props.width}px` : props.width\n }\n\n if (props.height) {\n s.height = typeof props.height === 'number' ? `${props.height}px` : props.height\n }\n\n return s\n})\n\nconst classes = computed(() => {\n const base = ['bg-bg-hover']\n\n switch (props.variant) {\n case 'circular':\n base.push('rounded-full')\n if (!props.width && !props.height) {\n base.push('w-10 h-10')\n }\n break\n case 'rectangular':\n base.push('rounded-none')\n if (!props.height) base.push('h-24')\n break\n case 'rounded':\n base.push('rounded-mint')\n if (!props.height) base.push('h-24')\n break\n default:\n base.push('rounded')\n if (!props.height) base.push('h-4')\n if (!props.width) base.push('w-full')\n }\n\n switch (props.animation) {\n case 'pulse':\n base.push('animate-pulse')\n break\n case 'wave':\n base.push('skeleton-wave')\n break\n }\n\n return base\n})\n</script>\n\n<template>\n <div :class=\"classes\" :style=\"style\" />\n</template>\n\n<style>\n@import '../styles/components/skeleton.css';\n</style>\n","<script setup lang=\"ts\">\n/** Animated loading placeholder in text, circular, rectangular, or rounded variants with pulse or wave animation. */\nimport { computed } from 'vue'\n\ninterface Props {\n variant?: 'text' | 'circular' | 'rectangular' | 'rounded'\n width?: string | number\n height?: string | number\n animation?: 'pulse' | 'wave' | 'none'\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'text',\n animation: 'wave',\n})\n\nconst style = computed(() => {\n const s: Record<string, string> = {}\n\n if (props.width) {\n s.width = typeof props.width === 'number' ? `${props.width}px` : props.width\n }\n\n if (props.height) {\n s.height = typeof props.height === 'number' ? `${props.height}px` : props.height\n }\n\n return s\n})\n\nconst classes = computed(() => {\n const base = ['bg-bg-hover']\n\n switch (props.variant) {\n case 'circular':\n base.push('rounded-full')\n if (!props.width && !props.height) {\n base.push('w-10 h-10')\n }\n break\n case 'rectangular':\n base.push('rounded-none')\n if (!props.height) base.push('h-24')\n break\n case 'rounded':\n base.push('rounded-mint')\n if (!props.height) base.push('h-24')\n break\n default:\n base.push('rounded')\n if (!props.height) base.push('h-4')\n if (!props.width) base.push('w-full')\n }\n\n switch (props.animation) {\n case 'pulse':\n base.push('animate-pulse')\n break\n case 'wave':\n base.push('skeleton-wave')\n break\n }\n\n return base\n})\n</script>\n\n<template>\n <div :class=\"classes\" :style=\"style\" />\n</template>\n\n<style>\n@import '../styles/components/skeleton.css';\n</style>\n","<script setup lang=\"ts\">\n/** Empty-state placeholder with icon badge, headline, description, default slot, and optional CTA button. */\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n title?: string\n description?: string\n iconPath?: string\n color?: 'primary' | 'cta' | 'success' | 'warning' | 'error' | 'muted'\n size?: 'sm' | 'md' | 'lg'\n variant?: 'illustrated' | 'inline'\n actionLabel?: string\n}\n\nwithDefaults(defineProps<Props>(), {\n color: 'primary',\n size: 'md',\n variant: 'illustrated',\n})\n\nconst emit = defineEmits<{\n action: []\n}>()\n\nconst defaultIconPaths = [\n 'M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z',\n 'M7 11h10',\n 'M7 15h6',\n 'M7 7h8',\n]\n</script>\n\n<template>\n <div :class=\"['mint-empty-state', `mint-empty-state--${variant}`, `mint-empty-state--${size}`]\">\n <div :class=\"['mint-empty-state__icon-wrapper', `mint-empty-state__icon-wrapper--${color}`]\">\n <slot name=\"icon\">\n <svg\n class=\"mint-empty-state__icon\"\n :class=\"`mint-empty-state__icon--${color}`\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <template v-if=\"iconPath\">\n <path :d=\"iconPath\" />\n </template>\n <template v-else>\n <path v-for=\"(d, i) in defaultIconPaths\" :key=\"i\" :d=\"d\" />\n </template>\n </svg>\n </slot>\n </div>\n <div class=\"mint-empty-state__body\">\n <h3 v-if=\"title\" class=\"mint-empty-state__title\">{{ title }}</h3>\n <p v-if=\"description\" class=\"mint-empty-state__description\">{{ description }}</p>\n <slot />\n </div>\n <div v-if=\"actionLabel\" class=\"mint-empty-state__action\">\n <BaseButton @click=\"emit('action')\">\n {{ actionLabel }}\n </BaseButton>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/empty-state.css';\n</style>\n","<script setup lang=\"ts\">\n/** Empty-state placeholder with icon badge, headline, description, default slot, and optional CTA button. */\nimport BaseButton from './BaseButton.vue'\n\ninterface Props {\n title?: string\n description?: string\n iconPath?: string\n color?: 'primary' | 'cta' | 'success' | 'warning' | 'error' | 'muted'\n size?: 'sm' | 'md' | 'lg'\n variant?: 'illustrated' | 'inline'\n actionLabel?: string\n}\n\nwithDefaults(defineProps<Props>(), {\n color: 'primary',\n size: 'md',\n variant: 'illustrated',\n})\n\nconst emit = defineEmits<{\n action: []\n}>()\n\nconst defaultIconPaths = [\n 'M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z',\n 'M7 11h10',\n 'M7 15h6',\n 'M7 7h8',\n]\n</script>\n\n<template>\n <div :class=\"['mint-empty-state', `mint-empty-state--${variant}`, `mint-empty-state--${size}`]\">\n <div :class=\"['mint-empty-state__icon-wrapper', `mint-empty-state__icon-wrapper--${color}`]\">\n <slot name=\"icon\">\n <svg\n class=\"mint-empty-state__icon\"\n :class=\"`mint-empty-state__icon--${color}`\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <template v-if=\"iconPath\">\n <path :d=\"iconPath\" />\n </template>\n <template v-else>\n <path v-for=\"(d, i) in defaultIconPaths\" :key=\"i\" :d=\"d\" />\n </template>\n </svg>\n </slot>\n </div>\n <div class=\"mint-empty-state__body\">\n <h3 v-if=\"title\" class=\"mint-empty-state__title\">{{ title }}</h3>\n <p v-if=\"description\" class=\"mint-empty-state__description\">{{ description }}</p>\n <slot />\n </div>\n <div v-if=\"actionLabel\" class=\"mint-empty-state__action\">\n <BaseButton @click=\"emit('action')\">\n {{ actionLabel }}\n </BaseButton>\n </div>\n </div>\n</template>\n\n<style>\n@import '../styles/components/empty-state.css';\n</style>\n"],"mappings":"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"}