@morscherlab/mint-sdk 1.3.0-beta.5 → 1.3.0-beta.7

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 (49) hide show
  1. package/dist/{ResourcePickerListInternal-BEXnyBNz.js → ResourcePickerListInternal-CFAw4vTZ.js} +20 -17
  2. package/dist/ResourcePickerListInternal-CFAw4vTZ.js.map +1 -0
  3. package/dist/components/SettingsModal.schema.d.ts +1 -1
  4. package/dist/components/SettingsModal.vue.d.ts +2 -0
  5. package/dist/components/index.d.ts +3 -3
  6. package/dist/components/index.js +3 -3
  7. package/dist/components/internal/ResourcePickerListInternal.vue.d.ts +9 -3
  8. package/dist/{components-BTzcqrJp.js → components-GDDvof8D.js} +19 -15
  9. package/dist/components-GDDvof8D.js.map +1 -0
  10. package/dist/composables/index.js +3 -3
  11. package/dist/composables/usePlatformContext.d.ts +3 -0
  12. package/dist/{composables-Doqb7g1z.js → composables-D6uqp0UF.js} +3 -3
  13. package/dist/{composables-Doqb7g1z.js.map → composables-D6uqp0UF.js.map} +1 -1
  14. package/dist/index.js +5 -5
  15. package/dist/install.js +1 -1
  16. package/dist/styles.css +114 -115
  17. package/dist/templates/index.js +1 -1
  18. package/dist/{templates-DgAG5kMq.js → templates-BU1pet28.js} +31 -44
  19. package/dist/templates-BU1pet28.js.map +1 -0
  20. package/dist/types/components.d.ts +4 -0
  21. package/dist/types/platform.d.ts +2 -0
  22. package/dist/{validation-DILksa0c.js → validation-Dmp9Q5W0.js} +2 -2
  23. package/dist/{validation-DILksa0c.js.map → validation-Dmp9Q5W0.js.map} +1 -1
  24. package/package.json +1 -1
  25. package/src/__tests__/components/AppTopBar.test.ts +8 -0
  26. package/src/__tests__/components/ExperimentSelectorModal.test.ts +12 -5
  27. package/src/__tests__/components/FileUploader.test.ts +6 -9
  28. package/src/__tests__/components/ResourcePickerListInternal.test.ts +23 -4
  29. package/src/__tests__/components/SettingsModal.test.ts +22 -0
  30. package/src/__tests__/styles/controlRefreshRules.test.ts +22 -3
  31. package/src/__tests__/styles/menusShellRefresh.test.ts +9 -5
  32. package/src/__tests__/templates/templatesDeprecation.test.ts +8 -1
  33. package/src/components/AppTopBar.vue +5 -1
  34. package/src/components/ExperimentSelectorModal.vue +4 -1
  35. package/src/components/FileUploader.vue +30 -46
  36. package/src/components/SettingsModal.schema.ts +4 -2
  37. package/src/components/SettingsModal.vue +4 -2
  38. package/src/components/index.ts +3 -3
  39. package/src/components/internal/ResourcePickerListInternal.vue +21 -27
  40. package/src/styles/components/app-page-selector.css +3 -3
  41. package/src/styles/components/app-top-bar.css +16 -12
  42. package/src/styles/components/experiment-selector-modal.css +3 -9
  43. package/src/styles/components/file-uploader.css +22 -55
  44. package/src/styles/components/resource-picker-list.css +70 -36
  45. package/src/types/components.ts +4 -0
  46. package/src/types/platform.ts +2 -0
  47. package/dist/ResourcePickerListInternal-BEXnyBNz.js.map +0 -1
  48. package/dist/components-BTzcqrJp.js.map +0 -1
  49. package/dist/templates-DgAG5kMq.js.map +0 -1
@@ -5,6 +5,7 @@
5
5
  align-items: center;
6
6
  gap: 0.5rem;
7
7
  flex-wrap: wrap;
8
+ padding: 0 1.25rem;
8
9
  }
9
10
 
10
11
  .mint-experiment-selector__filter-select {
@@ -65,14 +66,6 @@ html.dark .mint-experiment-selector__filters-toggle--active {
65
66
  flex-shrink: 0;
66
67
  }
67
68
 
68
- /* Advanced filters row */
69
- .mint-experiment-selector__filters-advanced {
70
- padding: 0.625rem;
71
- border: 1px solid var(--border-light);
72
- border-radius: var(--radius-md);
73
- background-color: color-mix(in srgb, var(--bg-secondary) 72%, transparent);
74
- }
75
-
76
69
  /* Group toggle checkbox label */
77
70
  .mint-experiment-selector__group-toggle {
78
71
  display: inline-flex;
@@ -103,7 +96,8 @@ html.dark .mint-experiment-selector__filters-toggle--active {
103
96
  .mint-experiment-selector__footer {
104
97
  display: flex;
105
98
  justify-content: flex-start;
106
- padding-top: 0.25rem;
99
+ padding: 0.5rem 1.25rem 0.75rem;
100
+ border-top: 1px solid var(--border-light);
107
101
  }
108
102
 
109
103
  .mint-experiment-selector__clear-btn {
@@ -14,113 +14,80 @@
14
14
 
15
15
  .mint-file-uploader__dropzone {
16
16
  position: relative;
17
- border: 1px dashed var(--_control-border-strong);
17
+ border: 2px dashed var(--border-color);
18
18
  border-radius: var(--radius-lg);
19
19
  cursor: pointer;
20
20
  background: var(--bg-secondary);
21
21
  transition: border-color 0.15s ease, background-color 0.15s ease;
22
22
  }
23
23
 
24
- /* 2g: hover only changes the surface; the border change is reserved for dragover. */
25
24
  .mint-file-uploader__dropzone:hover {
26
- background: var(--bg-hover);
25
+ border-color: var(--color-primary);
26
+ background: color-mix(in srgb, var(--color-primary) 3%, var(--bg-secondary));
27
27
  }
28
28
 
29
29
  .mint-file-uploader__dropzone:focus-visible {
30
- outline: none;
31
- box-shadow: var(--focus-ring-offset);
30
+ outline: 2px solid var(--color-primary);
31
+ outline-offset: 2px;
32
32
  }
33
33
 
34
- .mint-file-uploader__dropzone--dragover,
35
- .mint-file-uploader__dropzone--dragover:hover {
36
- border: 1.5px solid var(--color-primary);
37
- background: var(--color-primary-soft);
34
+ .mint-file-uploader__dropzone--dragover {
35
+ border-color: var(--color-primary);
36
+ background: color-mix(in srgb, var(--color-primary) 5%, var(--bg-secondary));
38
37
  }
39
38
 
40
- .mint-file-uploader__dropzone--disabled,
41
- .mint-file-uploader__dropzone--disabled:hover {
42
- background: var(--bg-tertiary);
39
+ .mint-file-uploader__dropzone--disabled {
43
40
  opacity: var(--mint-disabled-opacity);
44
41
  cursor: not-allowed;
45
42
  }
46
43
 
47
44
  .mint-file-uploader__dropzone--sm {
48
- padding: 0.75rem;
45
+ padding: 1.25rem;
49
46
  }
50
47
 
51
48
  .mint-file-uploader__dropzone--md {
52
- padding: 1.125rem;
49
+ padding: 1.5rem;
53
50
  }
54
51
 
55
52
  .mint-file-uploader__dropzone--lg {
56
- padding: 1.5rem;
53
+ padding: 2rem;
57
54
  }
58
55
 
59
56
  /* ==========================================================================
60
- Content area — compact row: icon tile + prompt + accepted formats
57
+ Content area
61
58
  ========================================================================== */
62
59
 
63
60
  .mint-file-uploader__content {
64
61
  display: flex;
62
+ flex-direction: column;
65
63
  align-items: center;
66
- gap: 0.75rem;
64
+ text-align: center;
67
65
  }
68
66
 
69
67
  .mint-file-uploader__input {
70
68
  display: none;
71
69
  }
72
70
 
73
- .mint-file-uploader__icon-tile {
74
- display: flex;
75
- align-items: center;
76
- justify-content: center;
77
- flex-shrink: 0;
71
+ .mint-file-uploader__icon {
78
72
  width: 2rem;
79
73
  height: 2rem;
80
- border-radius: var(--radius-md);
81
- background: var(--bg-tertiary);
82
- color: var(--text-secondary);
83
- transition: background-color 0.15s ease, color 0.15s ease;
84
- }
85
-
86
- .mint-file-uploader__dropzone--dragover .mint-file-uploader__icon-tile {
87
- background: var(--color-primary);
88
- color: white;
89
- }
90
-
91
- .mint-file-uploader__icon {
92
- width: 1rem;
93
- height: 1rem;
94
- stroke-width: 1.8;
74
+ color: var(--text-muted);
75
+ margin-bottom: 0.5rem;
95
76
  }
96
77
 
97
- .mint-file-uploader__copy {
98
- display: flex;
99
- flex-direction: column;
100
- gap: 0.125rem;
101
- min-width: 0;
78
+ .mint-file-uploader__dropzone:hover .mint-file-uploader__icon,
79
+ .mint-file-uploader__dropzone--dragover .mint-file-uploader__icon {
80
+ color: var(--color-primary);
102
81
  }
103
82
 
104
83
  .mint-file-uploader__text {
105
- color: var(--text-primary);
84
+ color: var(--text-secondary);
106
85
  font-size: 0.8125rem;
107
86
  font-weight: 500;
108
87
  margin: 0;
109
88
  padding: 0;
110
89
  }
111
90
 
112
- .mint-file-uploader__dropzone--dragover .mint-file-uploader__text {
113
- color: var(--_ink-primary);
114
- }
115
-
116
- .mint-file-uploader__formats {
117
- color: var(--text-secondary);
118
- font-family: var(--font-mono);
119
- font-size: 0.6875rem;
120
- margin: 0;
121
- padding: 0;
122
- }
123
-
124
91
  /* ==========================================================================
125
92
  Actions slot
126
93
  ========================================================================== */
@@ -1,4 +1,16 @@
1
- /* ResourcePickerList (8b): shared list for ExperimentSelectorModal and ArtifactSelectorModal. */
1
+ /* ResourcePickerList: shared list for ExperimentSelectorModal and ArtifactSelectorModal.
2
+ No frame of its own (the modal is the card); rows are inset and rounded, groups are
3
+ pinned bars. */
4
+
5
+ /* The modal is the only card: the list runs edge to edge, and the toolbar, group
6
+ bars and row text keep the modal's 1.25rem gutter themselves. */
7
+ .mint-modal__body:has(> .mint-resource-picker) {
8
+ padding: 0;
9
+ }
10
+ .mint-modal__container:has(.mint-resource-picker) .mint-modal__header {
11
+ border-bottom: none;
12
+ padding-bottom: 0.75rem;
13
+ }
2
14
 
3
15
  .mint-resource-picker {
4
16
  display: flex;
@@ -11,6 +23,7 @@
11
23
  align-items: center;
12
24
  flex-wrap: wrap;
13
25
  gap: 0.5rem;
26
+ padding: 0 1.25rem;
14
27
  }
15
28
  .mint-resource-picker__search {
16
29
  flex: 1;
@@ -20,12 +33,10 @@
20
33
  .mint-resource-picker__frame {
21
34
  display: flex;
22
35
  flex-direction: column;
23
- overflow: hidden;
24
- border: 1px solid var(--border-color);
25
- border-radius: var(--radius-md);
26
- background-color: var(--bg-card);
36
+ border-top: 1px solid var(--border-color);
27
37
  }
28
38
  .mint-resource-picker__list {
39
+ padding-bottom: 0.5rem;
29
40
  max-height: min(28rem, calc(100vh - 16rem));
30
41
  overflow-y: auto;
31
42
  overscroll-behavior: contain;
@@ -38,14 +49,17 @@
38
49
  gap: 0.5rem;
39
50
  flex-shrink: 0;
40
51
  width: 100%;
52
+ position: sticky;
53
+ top: 0;
54
+ z-index: 1;
41
55
  height: 30px;
42
- padding: 0 12px;
56
+ padding: 0 1.25rem;
43
57
  border: 0;
44
58
  border-bottom: 1px solid var(--border-light);
45
59
  background-color: var(--bg-primary);
46
60
  font-family: inherit;
47
61
  font-size: 12px;
48
- font-weight: 500;
62
+ font-weight: 600;
49
63
  color: var(--text-secondary);
50
64
  text-align: left;
51
65
  cursor: pointer;
@@ -77,24 +91,25 @@
77
91
  .mint-resource-picker__group-count {
78
92
  font: 11px var(--font-mono);
79
93
  font-variant-numeric: tabular-nums;
94
+ color: var(--text-muted);
80
95
  }
81
96
 
82
97
  .mint-resource-picker__row {
83
98
  display: grid;
84
- grid-template-columns: minmax(0, 1fr) auto 14px;
99
+ grid-template-columns: minmax(0, 1fr);
100
+ grid-auto-flow: column;
101
+ grid-auto-columns: auto;
85
102
  align-items: center;
86
- gap: 10px;
103
+ gap: 12px;
87
104
  box-sizing: border-box;
88
105
  flex-shrink: 0;
89
- min-height: 44px;
90
- padding: 5px 12px;
91
- border-bottom: 1px solid var(--border-light);
106
+ min-height: 56px;
107
+ margin: 2px 0.5rem;
108
+ padding: 10px 12px;
109
+ border-radius: var(--radius-md);
92
110
  cursor: pointer;
93
111
  transition: background-color 150ms ease;
94
112
  }
95
- .mint-resource-picker__row:last-child {
96
- border-bottom: 0;
97
- }
98
113
  .mint-resource-picker__row:hover {
99
114
  background-color: var(--bg-hover);
100
115
  }
@@ -110,7 +125,7 @@
110
125
  .mint-resource-picker__text {
111
126
  display: flex;
112
127
  flex-direction: column;
113
- gap: 1px;
128
+ gap: 4px;
114
129
  min-width: 0;
115
130
  line-height: 1.25;
116
131
  }
@@ -118,18 +133,49 @@
118
133
  overflow: hidden;
119
134
  text-overflow: ellipsis;
120
135
  white-space: nowrap;
121
- font-size: 13px;
122
- font-weight: 500;
136
+ font-size: 15px;
137
+ font-weight: 600;
123
138
  color: var(--text-primary);
124
139
  }
125
140
  .mint-resource-picker__row--selected .mint-resource-picker__title {
126
141
  color: var(--_ink-primary);
127
142
  }
128
- .mint-resource-picker__code,
143
+ .mint-resource-picker__code {
144
+ margin-right: 0.5rem;
145
+ }
146
+ .mint-resource-picker__aside {
147
+ display: flex;
148
+ flex-direction: column;
149
+ align-items: flex-end;
150
+ gap: 4px;
151
+ line-height: 1.25;
152
+ white-space: nowrap;
153
+ }
129
154
  .mint-resource-picker__date {
130
- font-family: var(--font-mono);
155
+ font: 12px var(--font-mono);
131
156
  font-variant-numeric: tabular-nums;
157
+ color: var(--text-muted);
158
+ }
159
+ .mint-resource-picker__status {
160
+ display: inline-flex;
161
+ align-items: center;
162
+ gap: 5px;
163
+ font-size: 12px;
164
+ font-weight: 500;
165
+ color: var(--text-secondary);
132
166
  }
167
+ .mint-resource-picker__status::before {
168
+ content: '';
169
+ width: 6px;
170
+ height: 6px;
171
+ border-radius: 50%;
172
+ background-color: currentColor;
173
+ }
174
+ .mint-resource-picker__status--primary { color: var(--_ink-primary); }
175
+ .mint-resource-picker__status--success { color: var(--mint-success); }
176
+ .mint-resource-picker__status--warning { color: var(--mint-warning); }
177
+ .mint-resource-picker__status--error { color: var(--mint-error); }
178
+ .mint-resource-picker__status--info { color: var(--mint-info); }
133
179
  .mint-resource-picker__meta {
134
180
  overflow: hidden;
135
181
  text-overflow: ellipsis;
@@ -142,29 +188,17 @@
142
188
  align-items: center;
143
189
  gap: 4px;
144
190
  }
145
- .mint-resource-picker__check-slot {
146
- display: flex;
147
- width: 14px;
148
- }
149
- .mint-resource-picker__check {
150
- width: 14px;
151
- height: 14px;
152
- color: var(--color-primary);
153
- }
154
191
 
155
192
  .mint-resource-picker__skeleton-row {
156
193
  display: flex;
157
194
  flex-direction: column;
158
195
  justify-content: center;
159
- gap: 6px;
160
- min-height: 44px;
161
- padding: 5px 12px;
162
- border-bottom: 1px solid var(--border-light);
163
- }
164
- .mint-resource-picker__skeleton-row:last-child {
165
- border-bottom: 0;
196
+ gap: 8px;
197
+ min-height: 56px;
198
+ padding: 10px 1.25rem;
166
199
  }
167
200
  .mint-resource-picker__error {
201
+ margin: 0 1.25rem 1.25rem;
168
202
  padding: 0.75rem;
169
203
  border: 1px solid var(--mint-error-border);
170
204
  border-radius: var(--radius-md);
@@ -211,6 +211,8 @@ export interface TopBarSettingsConfig {
211
211
  title?: string
212
212
  tabs?: SettingsTabInput[]
213
213
  showAppearance?: boolean
214
+ /** List Appearance before the other tabs and open on it. */
215
+ appearanceFirst?: boolean
214
216
  size?: 'md' | 'lg' | 'xl'
215
217
  layout?: SettingsModalLayout
216
218
  /** Schema-driven config — when set, fields auto-render via SDK form components. */
@@ -500,6 +502,8 @@ export interface SettingsTab extends AccessControlled {
500
502
  description?: string
501
503
  /** Optional monospace meta shown right of the tab label — a count, "2 unsaved", etc. */
502
504
  meta?: string
505
+ /** Rail section heading for this tab when Appearance is shown; defaults to "Plugin". */
506
+ section?: string
503
507
  }
504
508
 
505
509
  export type SettingsTabInput = SettingsTab | string
@@ -4,6 +4,8 @@ import type { RoleInfo } from '../permissions'
4
4
  export interface PluginInfo {
5
5
  id: string
6
6
  name: string
7
+ /** Name to show in the UI; the platform falls back to the plugin ID. Absent before MINT 1.3. */
8
+ display_name?: string
7
9
  version: string
8
10
  plugin_type?: string
9
11
  description?: string
@@ -1 +0,0 @@
1
- {"version":3,"file":"ResourcePickerListInternal-BEXnyBNz.js","names":["$slots","$slots"],"sources":["../src/components/BaseInput.vue","../src/components/BaseInput.vue","../src/utils/options.ts","../src/components/BaseSelect.vue","../src/components/BaseSelect.vue","../src/components/EmptyState.vue","../src/components/EmptyState.vue","../src/components/Skeleton.vue","../src/components/Skeleton.vue","../src/components/internal/ResourcePickerListInternal.vue","../src/components/internal/ResourcePickerListInternal.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * Renders a styled `<input>` supporting text/number/email types, size, error, and readonly states.\n * Optional `#prefix` (bare leading glyph, e.g. a search icon) and `#suffix` (neutral unit segment) slots sit inside the field border.\n */\nimport { computed, useAttrs, useSlots, type StyleValue } from 'vue'\nimport type { InputType } from '../types'\n\ndefineOptions({ inheritAttrs: false })\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\ndefineSlots<{\n /** Leading glyph inside the field border (search icon, currency, …), no segment background. */\n prefix?: () => unknown\n /** Trailing segment inside the field border, e.g. a unit (mono 12px on `--bg-primary`). */\n suffix?: () => unknown\n}>()\n\nconst attrs = useAttrs()\nconst slots = useSlots()\nconst wrapped = computed(() => Boolean(slots.prefix || slots.suffix))\n\n/** Wrapped: class/style style the group, every other attribute (id, name, aria-*) stays on the input. */\nconst inputAttrs = computed(() => {\n if (!wrapped.value) return attrs\n const { class: _class, style: _style, ...rest } = attrs\n return rest\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 <div\n v-if=\"wrapped\"\n :class=\"[\n 'mint-input-group',\n `mint-input-group--${size}`,\n error ? 'mint-input-group--error' : '',\n disabled ? 'mint-input-group--disabled' : '',\n attrs.class,\n ]\"\n :style=\"attrs.style as StyleValue\"\n >\n <span v-if=\"slots.prefix\" class=\"mint-input-group__prefix\"><slot name=\"prefix\" /></span>\n <input\n v-bind=\"inputAttrs\"\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=\"['mint-input', `mint-input--${size}`, 'mint-input-group__control']\"\n @input=\"handleInput\"\n @focus=\"emit('focus', $event)\"\n @blur=\"emit('blur', $event)\"\n @keydown=\"emit('keydown', $event)\"\n />\n <span v-if=\"slots.suffix\" class=\"mint-input-group__suffix\"><slot name=\"suffix\" /></span>\n </div>\n <input\n v-else\n v-bind=\"inputAttrs\"\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","<script setup lang=\"ts\">\n/**\n * Renders a styled `<input>` supporting text/number/email types, size, error, and readonly states.\n * Optional `#prefix` (bare leading glyph, e.g. a search icon) and `#suffix` (neutral unit segment) slots sit inside the field border.\n */\nimport { computed, useAttrs, useSlots, type StyleValue } from 'vue'\nimport type { InputType } from '../types'\n\ndefineOptions({ inheritAttrs: false })\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\ndefineSlots<{\n /** Leading glyph inside the field border (search icon, currency, …), no segment background. */\n prefix?: () => unknown\n /** Trailing segment inside the field border, e.g. a unit (mono 12px on `--bg-primary`). */\n suffix?: () => unknown\n}>()\n\nconst attrs = useAttrs()\nconst slots = useSlots()\nconst wrapped = computed(() => Boolean(slots.prefix || slots.suffix))\n\n/** Wrapped: class/style style the group, every other attribute (id, name, aria-*) stays on the input. */\nconst inputAttrs = computed(() => {\n if (!wrapped.value) return attrs\n const { class: _class, style: _style, ...rest } = attrs\n return rest\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 <div\n v-if=\"wrapped\"\n :class=\"[\n 'mint-input-group',\n `mint-input-group--${size}`,\n error ? 'mint-input-group--error' : '',\n disabled ? 'mint-input-group--disabled' : '',\n attrs.class,\n ]\"\n :style=\"attrs.style as StyleValue\"\n >\n <span v-if=\"slots.prefix\" class=\"mint-input-group__prefix\"><slot name=\"prefix\" /></span>\n <input\n v-bind=\"inputAttrs\"\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=\"['mint-input', `mint-input--${size}`, 'mint-input-group__control']\"\n @input=\"handleInput\"\n @focus=\"emit('focus', $event)\"\n @blur=\"emit('blur', $event)\"\n @keydown=\"emit('keydown', $event)\"\n />\n <span v-if=\"slots.suffix\" class=\"mint-input-group__suffix\"><slot name=\"suffix\" /></span>\n </div>\n <input\n v-else\n v-bind=\"inputAttrs\"\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","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, useAttrs } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport type { SelectOption, SelectOptionInput } from '../types'\nimport { findOptionByDomValue, normalizeOptionInput } from '../utils/options'\n\ndefineOptions({ inheritAttrs: false })\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})\nconst attrs = useAttrs()\n\nconst controlAttrs = computed(() => {\n const { class: _class, style: _style, ...nativeAttrs } = attrs\n return nativeAttrs\n})\nconst wrapperAttrs = computed<Pick<HTMLAttributes, 'class' | 'style'>>(() => ({\n class: attrs.class as HTMLAttributes['class'],\n style: attrs.style as HTMLAttributes['style'],\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 v-bind=\"wrapperAttrs\" class=\"mint-select\">\n <select\n v-bind=\"controlAttrs\"\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","<script setup lang=\"ts\">\n/** Renders a native `<select>` element with size, error, and disabled states. */\nimport { computed, useAttrs } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport type { SelectOption, SelectOptionInput } from '../types'\nimport { findOptionByDomValue, normalizeOptionInput } from '../utils/options'\n\ndefineOptions({ inheritAttrs: false })\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})\nconst attrs = useAttrs()\n\nconst controlAttrs = computed(() => {\n const { class: _class, style: _style, ...nativeAttrs } = attrs\n return nativeAttrs\n})\nconst wrapperAttrs = computed<Pick<HTMLAttributes, 'class' | 'style'>>(() => ({\n class: attrs.class as HTMLAttributes['class'],\n style: attrs.style as HTMLAttributes['style'],\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 v-bind=\"wrapperAttrs\" class=\"mint-select\">\n <select\n v-bind=\"controlAttrs\"\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","<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 <div v-if=\"$slots.default\" class=\"mint-empty-state__content\">\n <slot />\n </div>\n </div>\n <div v-if=\"actionLabel\" class=\"mint-empty-state__action\">\n <BaseButton variant=\"secondary\" @click=\"emit('action')\">\n {{ actionLabel }}\n </BaseButton>\n </div>\n </div>\n</template>\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 <div v-if=\"$slots.default\" class=\"mint-empty-state__content\">\n <slot />\n </div>\n </div>\n <div v-if=\"actionLabel\" class=\"mint-empty-state__action\">\n <BaseButton variant=\"secondary\" @click=\"emit('action')\">\n {{ actionLabel }}\n </BaseButton>\n </div>\n </div>\n</template>\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 'mint-skeleton',\n `mint-skeleton--${props.variant}`,\n // pulse and wave both breathe (3e); the shimmer is gone.\n props.animation === 'none' ? '' : 'skeleton-wave',\n])\n</script>\n\n<template>\n <div :class=\"classes\" :style=\"style\" />\n</template>\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 'mint-skeleton',\n `mint-skeleton--${props.variant}`,\n // pulse and wave both breathe (3e); the shimmer is gone.\n props.animation === 'none' ? '' : 'skeleton-wave',\n])\n</script>\n\n<template>\n <div :class=\"classes\" :style=\"style\" />\n</template>\n","<script setup lang=\"ts\" generic=\"T\">\n/**\n * Searchable, optionally grouped single-select list shared by the experiment and\n * artifact selector modals (8b): 44px two-line rows, trailing check, skeleton,\n * error and empty states, and the combobox keyboard model (↑ ↓ Enter).\n */\nimport { computed, nextTick, reactive, ref, useId, watch } from 'vue'\nimport BaseInput from '../BaseInput.vue'\nimport Skeleton from '../Skeleton.vue'\nimport EmptyState from '../EmptyState.vue'\n\nexport interface ResourcePickerRow {\n /** Identifier shown in mono before the title, e.g. an experiment code. */\n code?: string\n title: string\n /** Second-line facts, joined with \" · \". */\n meta?: string[]\n /** Rendered last on the second line, in mono. */\n date?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n items: T[]\n /** When set, rows render under collapsible group headers in this order. */\n groups?: { label: string; items: T[] }[] | null\n itemKey: (item: T) => string | number\n describe: (item: T) => ResourcePickerRow\n selectedKey?: string | number | null\n search: string\n /** Accessible name of the list; the search field is labelled \"Search {label}\". */\n label: string\n placeholder?: string\n loading?: boolean\n error?: string | null\n emptyTitle?: string\n emptyDescription?: string\n }>(),\n {\n groups: null,\n selectedKey: null,\n placeholder: 'Search…',\n loading: false,\n error: null,\n emptyTitle: 'Nothing found',\n emptyDescription: 'Try adjusting your search or filters.',\n },\n)\nconst emit = defineEmits<{\n select: [item: T]\n 'update:search': [value: string]\n}>()\ndefineSlots<{\n /** Extra filters on the search row. */\n filters?: () => unknown\n /** A second filter row under the search. */\n advanced?: () => unknown\n /** Row controls before the check; keys pressed inside them never select the row. */\n actions?: (props: { item: T }) => unknown\n}>()\n\nconst listId = `mint-resource-picker-${useId().replace(/:/g, '')}`\nconst collapsed = reactive(new Set<string>())\nconst activeIndex = ref(-1)\nconst listRef = ref<HTMLElement | null>(null)\n\nconst visibleGroups = computed(() =>\n props.groups?.map((group) => ({\n ...group,\n rows: collapsed.has(group.label) ? [] : group.items,\n })),\n)\n/** Keyboard order: the rows on screen, top to bottom. */\nconst flat = computed(() =>\n visibleGroups.value ? visibleGroups.value.flatMap((group) => group.rows) : props.items,\n)\nconst indexByKey = computed(() => new Map(flat.value.map((item, i) => [props.itemKey(item), i])))\nconst showList = computed(() => !props.loading && !props.error && props.items.length > 0)\n\nfunction optionId(item: T): string {\n return `${listId}-${props.itemKey(item)}`\n}\nconst activeDescendant = computed(() => {\n const item = flat.value[activeIndex.value]\n return item === undefined ? undefined : optionId(item)\n})\n\nfunction indexOf(item: T): number {\n return indexByKey.value.get(props.itemKey(item)) ?? -1\n}\nfunction toggleGroup(label: string) {\n if (collapsed.has(label)) collapsed.delete(label)\n else collapsed.add(label)\n}\nfunction keydown(event: KeyboardEvent) {\n // Keys belong to the search combobox; a focused header, filter or row action keeps its own Enter.\n const target = event.target as HTMLElement | null\n if (target !== event.currentTarget && !target?.closest('.mint-resource-picker__search')) return\n const rows = flat.value\n if (!rows.length) return\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault()\n activeIndex.value =\n event.key === 'ArrowDown'\n ? Math.min(activeIndex.value + 1, rows.length - 1)\n : Math.max(activeIndex.value - 1, 0)\n nextTick(() =>\n listRef.value\n ?.querySelector('.mint-resource-picker__row--active')\n ?.scrollIntoView({ block: 'nearest' }),\n )\n } else if (event.key === 'Enter' && rows[activeIndex.value] !== undefined) {\n event.preventDefault()\n emit('select', rows[activeIndex.value]!)\n }\n}\n\nwatch(\n () => [props.items, props.groups],\n () => {\n activeIndex.value = -1\n },\n)\n</script>\n\n<template>\n <div class=\"mint-resource-picker\" @keydown=\"keydown\">\n <div class=\"mint-resource-picker__toolbar\">\n <BaseInput\n class=\"mint-resource-picker__search\"\n :model-value=\"search\"\n :placeholder=\"placeholder\"\n size=\"sm\"\n type=\"search\"\n role=\"combobox\"\n :aria-label=\"`Search ${label.toLowerCase()}`\"\n :aria-expanded=\"showList\"\n :aria-controls=\"listId\"\n :aria-activedescendant=\"activeDescendant\"\n @update:model-value=\"emit('update:search', String($event ?? ''))\"\n >\n <template #prefix>\n <svg viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\">\n <path d=\"M7 11.5a4.5 4.5 0 1 1 0-9 4.5 4.5 0 0 1 0 9zM10.5 10.5L14 14\" />\n </svg>\n </template>\n </BaseInput>\n <slot name=\"filters\" />\n </div>\n <slot name=\"advanced\" />\n\n <div v-if=\"loading\" class=\"mint-resource-picker__frame\" aria-busy=\"true\">\n <div v-for=\"n in 4\" :key=\"n\" class=\"mint-resource-picker__skeleton-row\">\n <Skeleton :width=\"`${40 + n * 10}%`\" height=\"0.75rem\" />\n <Skeleton width=\"30%\" height=\"0.625rem\" />\n </div>\n </div>\n <div v-else-if=\"error\" class=\"mint-resource-picker__error\" role=\"alert\">{{ error }}</div>\n <EmptyState\n v-else-if=\"!items.length\"\n :title=\"emptyTitle\"\n :description=\"emptyDescription\"\n size=\"sm\"\n />\n <div\n v-else\n :id=\"listId\"\n ref=\"listRef\"\n class=\"mint-resource-picker__frame mint-resource-picker__list\"\n role=\"listbox\"\n :aria-label=\"label\"\n >\n <template v-for=\"group in visibleGroups ?? [{ label: '', items, rows: items }]\" :key=\"group.label\">\n <button\n v-if=\"visibleGroups\"\n type=\"button\"\n class=\"mint-resource-picker__group\"\n :aria-expanded=\"!collapsed.has(group.label)\"\n @click=\"toggleGroup(group.label)\"\n >\n <svg\n class=\"mint-resource-picker__chevron\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path d=\"M4 6l4 4 4-4\" />\n </svg>\n <span class=\"mint-resource-picker__group-label\">{{ group.label }}</span>\n <span class=\"mint-resource-picker__group-count\">{{ group.items.length }}</span>\n </button>\n <div\n v-for=\"item in group.rows\"\n :id=\"optionId(item)\"\n :key=\"itemKey(item)\"\n role=\"option\"\n :aria-selected=\"itemKey(item) === selectedKey\"\n class=\"mint-resource-picker__row\"\n :class=\"{\n 'mint-resource-picker__row--selected': itemKey(item) === selectedKey,\n 'mint-resource-picker__row--active': indexOf(item) === activeIndex,\n }\"\n @click=\"emit('select', item)\"\n @mouseenter=\"activeIndex = indexOf(item)\"\n >\n <span class=\"mint-resource-picker__text\">\n <span class=\"mint-resource-picker__title\"\n ><template v-if=\"describe(item).code\"\n ><span class=\"mint-resource-picker__code\">{{ describe(item).code }}</span> · </template\n >{{ describe(item).title }}</span\n >\n <span\n v-if=\"describe(item).meta?.length || describe(item).date\"\n class=\"mint-resource-picker__meta\"\n >{{ [...(describe(item).meta ?? [])].join(' · ')\n }}<template v-if=\"describe(item).date\"\n >{{ describe(item).meta?.length ? ' · ' : ''\n }}<span class=\"mint-resource-picker__date\">{{ describe(item).date }}</span></template\n ></span\n >\n </span>\n <span v-if=\"$slots.actions\" class=\"mint-resource-picker__actions\" @click.stop>\n <slot name=\"actions\" :item=\"item\" />\n </span>\n <span class=\"mint-resource-picker__check-slot\" aria-hidden=\"true\">\n <svg\n v-if=\"itemKey(item) === selectedKey\"\n class=\"mint-resource-picker__check\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path d=\"M3.5 8.5l3 3 6-7\" />\n </svg>\n </span>\n </div>\n </template>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\" generic=\"T\">\n/**\n * Searchable, optionally grouped single-select list shared by the experiment and\n * artifact selector modals (8b): 44px two-line rows, trailing check, skeleton,\n * error and empty states, and the combobox keyboard model (↑ ↓ Enter).\n */\nimport { computed, nextTick, reactive, ref, useId, watch } from 'vue'\nimport BaseInput from '../BaseInput.vue'\nimport Skeleton from '../Skeleton.vue'\nimport EmptyState from '../EmptyState.vue'\n\nexport interface ResourcePickerRow {\n /** Identifier shown in mono before the title, e.g. an experiment code. */\n code?: string\n title: string\n /** Second-line facts, joined with \" · \". */\n meta?: string[]\n /** Rendered last on the second line, in mono. */\n date?: string\n}\n\nconst props = withDefaults(\n defineProps<{\n items: T[]\n /** When set, rows render under collapsible group headers in this order. */\n groups?: { label: string; items: T[] }[] | null\n itemKey: (item: T) => string | number\n describe: (item: T) => ResourcePickerRow\n selectedKey?: string | number | null\n search: string\n /** Accessible name of the list; the search field is labelled \"Search {label}\". */\n label: string\n placeholder?: string\n loading?: boolean\n error?: string | null\n emptyTitle?: string\n emptyDescription?: string\n }>(),\n {\n groups: null,\n selectedKey: null,\n placeholder: 'Search…',\n loading: false,\n error: null,\n emptyTitle: 'Nothing found',\n emptyDescription: 'Try adjusting your search or filters.',\n },\n)\nconst emit = defineEmits<{\n select: [item: T]\n 'update:search': [value: string]\n}>()\ndefineSlots<{\n /** Extra filters on the search row. */\n filters?: () => unknown\n /** A second filter row under the search. */\n advanced?: () => unknown\n /** Row controls before the check; keys pressed inside them never select the row. */\n actions?: (props: { item: T }) => unknown\n}>()\n\nconst listId = `mint-resource-picker-${useId().replace(/:/g, '')}`\nconst collapsed = reactive(new Set<string>())\nconst activeIndex = ref(-1)\nconst listRef = ref<HTMLElement | null>(null)\n\nconst visibleGroups = computed(() =>\n props.groups?.map((group) => ({\n ...group,\n rows: collapsed.has(group.label) ? [] : group.items,\n })),\n)\n/** Keyboard order: the rows on screen, top to bottom. */\nconst flat = computed(() =>\n visibleGroups.value ? visibleGroups.value.flatMap((group) => group.rows) : props.items,\n)\nconst indexByKey = computed(() => new Map(flat.value.map((item, i) => [props.itemKey(item), i])))\nconst showList = computed(() => !props.loading && !props.error && props.items.length > 0)\n\nfunction optionId(item: T): string {\n return `${listId}-${props.itemKey(item)}`\n}\nconst activeDescendant = computed(() => {\n const item = flat.value[activeIndex.value]\n return item === undefined ? undefined : optionId(item)\n})\n\nfunction indexOf(item: T): number {\n return indexByKey.value.get(props.itemKey(item)) ?? -1\n}\nfunction toggleGroup(label: string) {\n if (collapsed.has(label)) collapsed.delete(label)\n else collapsed.add(label)\n}\nfunction keydown(event: KeyboardEvent) {\n // Keys belong to the search combobox; a focused header, filter or row action keeps its own Enter.\n const target = event.target as HTMLElement | null\n if (target !== event.currentTarget && !target?.closest('.mint-resource-picker__search')) return\n const rows = flat.value\n if (!rows.length) return\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault()\n activeIndex.value =\n event.key === 'ArrowDown'\n ? Math.min(activeIndex.value + 1, rows.length - 1)\n : Math.max(activeIndex.value - 1, 0)\n nextTick(() =>\n listRef.value\n ?.querySelector('.mint-resource-picker__row--active')\n ?.scrollIntoView({ block: 'nearest' }),\n )\n } else if (event.key === 'Enter' && rows[activeIndex.value] !== undefined) {\n event.preventDefault()\n emit('select', rows[activeIndex.value]!)\n }\n}\n\nwatch(\n () => [props.items, props.groups],\n () => {\n activeIndex.value = -1\n },\n)\n</script>\n\n<template>\n <div class=\"mint-resource-picker\" @keydown=\"keydown\">\n <div class=\"mint-resource-picker__toolbar\">\n <BaseInput\n class=\"mint-resource-picker__search\"\n :model-value=\"search\"\n :placeholder=\"placeholder\"\n size=\"sm\"\n type=\"search\"\n role=\"combobox\"\n :aria-label=\"`Search ${label.toLowerCase()}`\"\n :aria-expanded=\"showList\"\n :aria-controls=\"listId\"\n :aria-activedescendant=\"activeDescendant\"\n @update:model-value=\"emit('update:search', String($event ?? ''))\"\n >\n <template #prefix>\n <svg viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" aria-hidden=\"true\">\n <path d=\"M7 11.5a4.5 4.5 0 1 1 0-9 4.5 4.5 0 0 1 0 9zM10.5 10.5L14 14\" />\n </svg>\n </template>\n </BaseInput>\n <slot name=\"filters\" />\n </div>\n <slot name=\"advanced\" />\n\n <div v-if=\"loading\" class=\"mint-resource-picker__frame\" aria-busy=\"true\">\n <div v-for=\"n in 4\" :key=\"n\" class=\"mint-resource-picker__skeleton-row\">\n <Skeleton :width=\"`${40 + n * 10}%`\" height=\"0.75rem\" />\n <Skeleton width=\"30%\" height=\"0.625rem\" />\n </div>\n </div>\n <div v-else-if=\"error\" class=\"mint-resource-picker__error\" role=\"alert\">{{ error }}</div>\n <EmptyState\n v-else-if=\"!items.length\"\n :title=\"emptyTitle\"\n :description=\"emptyDescription\"\n size=\"sm\"\n />\n <div\n v-else\n :id=\"listId\"\n ref=\"listRef\"\n class=\"mint-resource-picker__frame mint-resource-picker__list\"\n role=\"listbox\"\n :aria-label=\"label\"\n >\n <template v-for=\"group in visibleGroups ?? [{ label: '', items, rows: items }]\" :key=\"group.label\">\n <button\n v-if=\"visibleGroups\"\n type=\"button\"\n class=\"mint-resource-picker__group\"\n :aria-expanded=\"!collapsed.has(group.label)\"\n @click=\"toggleGroup(group.label)\"\n >\n <svg\n class=\"mint-resource-picker__chevron\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n aria-hidden=\"true\"\n >\n <path d=\"M4 6l4 4 4-4\" />\n </svg>\n <span class=\"mint-resource-picker__group-label\">{{ group.label }}</span>\n <span class=\"mint-resource-picker__group-count\">{{ group.items.length }}</span>\n </button>\n <div\n v-for=\"item in group.rows\"\n :id=\"optionId(item)\"\n :key=\"itemKey(item)\"\n role=\"option\"\n :aria-selected=\"itemKey(item) === selectedKey\"\n class=\"mint-resource-picker__row\"\n :class=\"{\n 'mint-resource-picker__row--selected': itemKey(item) === selectedKey,\n 'mint-resource-picker__row--active': indexOf(item) === activeIndex,\n }\"\n @click=\"emit('select', item)\"\n @mouseenter=\"activeIndex = indexOf(item)\"\n >\n <span class=\"mint-resource-picker__text\">\n <span class=\"mint-resource-picker__title\"\n ><template v-if=\"describe(item).code\"\n ><span class=\"mint-resource-picker__code\">{{ describe(item).code }}</span> · </template\n >{{ describe(item).title }}</span\n >\n <span\n v-if=\"describe(item).meta?.length || describe(item).date\"\n class=\"mint-resource-picker__meta\"\n >{{ [...(describe(item).meta ?? [])].join(' · ')\n }}<template v-if=\"describe(item).date\"\n >{{ describe(item).meta?.length ? ' · ' : ''\n }}<span class=\"mint-resource-picker__date\">{{ describe(item).date }}</span></template\n ></span\n >\n </span>\n <span v-if=\"$slots.actions\" class=\"mint-resource-picker__actions\" @click.stop>\n <slot name=\"actions\" :item=\"item\" />\n </span>\n <span class=\"mint-resource-picker__check-slot\" aria-hidden=\"true\">\n <svg\n v-if=\"itemKey(item) === selectedKey\"\n class=\"mint-resource-picker__check\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path d=\"M3.5 8.5l3 3 6-7\" />\n </svg>\n </span>\n </div>\n </template>\n </div>\n </div>\n</template>\n"],"mappings":"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"}