@morscherlab/mint-sdk 1.0.0-rc.9 → 1.0.1

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 (68) hide show
  1. package/dist/BaseModal-B9UA8Y_I.js +165 -0
  2. package/dist/BaseModal-B9UA8Y_I.js.map +1 -0
  3. package/dist/BaseSelect-DksaKYq_.js +176 -0
  4. package/dist/BaseSelect-DksaKYq_.js.map +1 -0
  5. package/dist/ExperimentPopover-CCYB1oWp.js +361 -0
  6. package/dist/ExperimentPopover-CCYB1oWp.js.map +1 -0
  7. package/dist/ExperimentPopover-D0bg_fqM.js +3 -0
  8. package/dist/ExperimentSelectorModal-B_kPbXcg.js +4 -0
  9. package/dist/ExperimentSelectorModal-wm7yUdAr.js +720 -0
  10. package/dist/ExperimentSelectorModal-wm7yUdAr.js.map +1 -0
  11. package/dist/SettingsModal-L7Ejny45.js +5 -0
  12. package/dist/SettingsModal-LEKI6Ebl.js +521 -0
  13. package/dist/SettingsModal-LEKI6Ebl.js.map +1 -0
  14. package/dist/{auth-BulIv_km.js → auth-D9q2GIcv.js} +3 -80
  15. package/dist/auth-D9q2GIcv.js.map +1 -0
  16. package/dist/components/DataFrame.vue.d.ts +3 -0
  17. package/dist/components/ExperimentDataViewer.vue.d.ts +2 -0
  18. package/dist/components/PluginWorkspaceView.vue.d.ts +2 -2
  19. package/dist/components/index.js +7 -2
  20. package/dist/{components-DtX3LDLq.js → components-CdjRzHI2.js} +533 -2025
  21. package/dist/components-CdjRzHI2.js.map +1 -0
  22. package/dist/composables/index.js +9 -3
  23. package/dist/composables/usePluginClient.d.ts +2 -1
  24. package/dist/{composables-wNt7VtkF.js → composables-DJgqPrlR.js} +7 -12
  25. package/dist/{composables-wNt7VtkF.js.map → composables-DJgqPrlR.js.map} +1 -1
  26. package/dist/experiment-utils-hGXMHlAc.js +109 -0
  27. package/dist/experiment-utils-hGXMHlAc.js.map +1 -0
  28. package/dist/index.js +16 -5
  29. package/dist/index.js.map +1 -1
  30. package/dist/install.js +7 -2
  31. package/dist/install.js.map +1 -1
  32. package/dist/permissions.js +81 -0
  33. package/dist/permissions.js.map +1 -0
  34. package/dist/stores/index.js +1 -1
  35. package/dist/styles.css +3233 -3185
  36. package/dist/templates/index.js +3 -1
  37. package/dist/templates-Do43ZIMb.js +5065 -0
  38. package/dist/templates-Do43ZIMb.js.map +1 -0
  39. package/dist/{templates-DSbHJC4v.js → useControlSchema-0n8Bcftq.js} +10 -5335
  40. package/dist/useControlSchema-0n8Bcftq.js.map +1 -0
  41. package/dist/useDropdownState-Ben4DnjJ.js +47 -0
  42. package/dist/useDropdownState-Ben4DnjJ.js.map +1 -0
  43. package/dist/useEventListener-CfVkP9Xz.js +57 -0
  44. package/dist/useEventListener-CfVkP9Xz.js.map +1 -0
  45. package/dist/useExperimentSelector-BpZklTbV.js +469 -0
  46. package/dist/useExperimentSelector-BpZklTbV.js.map +1 -0
  47. package/dist/useFormBuilder-COfYWDuC.js +729 -0
  48. package/dist/useFormBuilder-COfYWDuC.js.map +1 -0
  49. package/dist/{useProtocolTemplates-DwBhEPPU.js → useProtocolTemplates-TUQO_F3n.js} +8 -1298
  50. package/dist/useProtocolTemplates-TUQO_F3n.js.map +1 -0
  51. package/dist/utils/pluginIcon.d.ts +29 -2
  52. package/package.json +5 -1
  53. package/src/__tests__/components/DataFrame.test.ts +37 -0
  54. package/src/__tests__/components/PluginIcon.test.ts +77 -0
  55. package/src/__tests__/composables/usePluginClient.test.ts +11 -10
  56. package/src/components/AppTopBar.vue +7 -6
  57. package/src/components/DataFrame.vue +27 -2
  58. package/src/components/ExperimentDataViewer.vue +5 -1
  59. package/src/components/PluginIcon.story.vue +31 -1
  60. package/src/components/PluginIcon.vue +94 -4
  61. package/src/composables/usePluginClient.ts +3 -12
  62. package/src/styles/components/dataframe.css +26 -0
  63. package/src/styles/components/plugin-icon.css +5 -0
  64. package/src/utils/pluginIcon.ts +159 -2
  65. package/dist/auth-BulIv_km.js.map +0 -1
  66. package/dist/components-DtX3LDLq.js.map +0 -1
  67. package/dist/templates-DSbHJC4v.js.map +0 -1
  68. package/dist/useProtocolTemplates-DwBhEPPU.js.map +0 -1
@@ -0,0 +1,720 @@
1
+ import { r as BaseInput_default, t as BaseSelect_default } from "./BaseSelect-DksaKYq_.js";
2
+ import { t as BaseModal_default } from "./BaseModal-B9UA8Y_I.js";
3
+ import { a as SORT_OPTIONS, c as formatExperimentStatus, l as getExperimentStatusVariant, r as EXPERIMENT_STATUS_OPTIONS, s as formatExperimentDate, t as DATE_PRESET_OPTIONS } from "./experiment-utils-hGXMHlAc.js";
4
+ import { t as useExperimentSelector } from "./useExperimentSelector-BpZklTbV.js";
5
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, nextTick, normalizeClass, normalizeStyle, openBlock, reactive, ref, renderList, renderSlot, toDisplayString, unref, vModelCheckbox, watch, withCtx, withDirectives } from "vue";
6
+ //#region src/components/BaseButton.vue?vue&type=script&setup=true&lang.ts
7
+ var _hoisted_1$4 = ["type", "disabled"];
8
+ var _hoisted_2$3 = {
9
+ key: 0,
10
+ class: "mint-button__spinner",
11
+ fill: "none",
12
+ viewBox: "0 0 24 24"
13
+ };
14
+ //#endregion
15
+ //#region src/components/BaseButton.vue
16
+ var BaseButton_default = /* @__PURE__ */ defineComponent({
17
+ __name: "BaseButton",
18
+ props: {
19
+ variant: { default: "primary" },
20
+ size: { default: "md" },
21
+ disabled: {
22
+ type: Boolean,
23
+ default: false
24
+ },
25
+ loading: {
26
+ type: Boolean,
27
+ default: false
28
+ },
29
+ type: { default: "button" },
30
+ fullWidth: {
31
+ type: Boolean,
32
+ default: false
33
+ }
34
+ },
35
+ emits: ["click"],
36
+ setup(__props, { emit: __emit }) {
37
+ /** Renders a styled `<button>` with variant, size, loading spinner, and full-width options. */
38
+ const props = __props;
39
+ const emit = __emit;
40
+ function handleClick(event) {
41
+ if (!props.disabled && !props.loading) emit("click", event);
42
+ }
43
+ return (_ctx, _cache) => {
44
+ return openBlock(), createElementBlock("button", {
45
+ type: __props.type,
46
+ disabled: __props.disabled || __props.loading,
47
+ class: normalizeClass([
48
+ "mint-button",
49
+ `mint-button--${__props.variant}`,
50
+ `mint-button--${__props.size}`,
51
+ __props.fullWidth ? "mint-button--full-width" : "",
52
+ __props.disabled || __props.loading ? "mint-button--disabled" : ""
53
+ ]),
54
+ onClick: handleClick
55
+ }, [__props.loading ? (openBlock(), createElementBlock("svg", _hoisted_2$3, [..._cache[0] || (_cache[0] = [createElementVNode("circle", {
56
+ style: { "opacity": "0.25" },
57
+ cx: "12",
58
+ cy: "12",
59
+ r: "10",
60
+ stroke: "currentColor",
61
+ "stroke-width": "4"
62
+ }, null, -1), createElementVNode("path", {
63
+ style: { "opacity": "0.75" },
64
+ fill: "currentColor",
65
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
66
+ }, null, -1)])])) : createCommentVNode("", true), renderSlot(_ctx.$slots, "default")], 10, _hoisted_1$4);
67
+ };
68
+ }
69
+ });
70
+ //#endregion
71
+ //#region src/components/BasePill.vue?vue&type=script&setup=true&lang.ts
72
+ var _hoisted_1$3 = {
73
+ key: 0,
74
+ class: "mint-pill__icon"
75
+ };
76
+ var _hoisted_2$2 = { class: "mint-pill__label" };
77
+ //#endregion
78
+ //#region src/components/BasePill.vue
79
+ var BasePill_default = /* @__PURE__ */ defineComponent({
80
+ __name: "BasePill",
81
+ props: {
82
+ variant: { default: "default" },
83
+ color: { default: void 0 },
84
+ size: { default: "md" },
85
+ removable: {
86
+ type: Boolean,
87
+ default: false
88
+ },
89
+ disabled: {
90
+ type: Boolean,
91
+ default: false
92
+ },
93
+ icon: {
94
+ type: Boolean,
95
+ default: false
96
+ }
97
+ },
98
+ emits: ["remove"],
99
+ setup(__props, { emit: __emit }) {
100
+ /** Compact label for tags, status indicators, and badges; supports removable and icon slots. */
101
+ const props = __props;
102
+ /**
103
+ * @event remove - Emitted when the remove button is clicked
104
+ */
105
+ const emit = __emit;
106
+ function handleRemove(event) {
107
+ event.stopPropagation();
108
+ if (!props.disabled) emit("remove");
109
+ }
110
+ return (_ctx, _cache) => {
111
+ return openBlock(), createElementBlock("span", { class: normalizeClass([
112
+ "mint-pill",
113
+ `mint-pill--${__props.variant}`,
114
+ __props.color && `mint-pill--${__props.color}`,
115
+ `mint-pill--${__props.size}`,
116
+ {
117
+ "mint-pill--disabled": __props.disabled,
118
+ "mint-pill--with-icon": __props.icon
119
+ }
120
+ ]) }, [
121
+ __props.icon ? (openBlock(), createElementBlock("span", _hoisted_1$3, [renderSlot(_ctx.$slots, "icon")])) : createCommentVNode("", true),
122
+ createElementVNode("span", _hoisted_2$2, [renderSlot(_ctx.$slots, "default")]),
123
+ __props.removable && !__props.disabled ? (openBlock(), createElementBlock("button", {
124
+ key: 1,
125
+ type: "button",
126
+ class: "mint-pill__remove",
127
+ "aria-label": "Remove",
128
+ onClick: handleRemove
129
+ }, [..._cache[0] || (_cache[0] = [createElementVNode("svg", {
130
+ class: "mint-pill__remove-icon",
131
+ fill: "none",
132
+ stroke: "currentColor",
133
+ "stroke-width": "2",
134
+ "stroke-linecap": "round",
135
+ "stroke-linejoin": "round",
136
+ viewBox: "0 0 24 24"
137
+ }, [createElementVNode("path", { d: "M18 6 6 18" }), createElementVNode("path", { d: "m6 6 12 12" })], -1)])])) : createCommentVNode("", true)
138
+ ], 2);
139
+ };
140
+ }
141
+ });
142
+ //#endregion
143
+ //#region src/components/Skeleton.vue
144
+ var Skeleton_default = /* @__PURE__ */ defineComponent({
145
+ __name: "Skeleton",
146
+ props: {
147
+ variant: { default: "text" },
148
+ width: {},
149
+ height: {},
150
+ animation: { default: "wave" }
151
+ },
152
+ setup(__props) {
153
+ /** Animated loading placeholder in text, circular, rectangular, or rounded variants with pulse or wave animation. */
154
+ const props = __props;
155
+ const style = computed(() => {
156
+ const s = {};
157
+ if (props.width) s.width = typeof props.width === "number" ? `${props.width}px` : props.width;
158
+ if (props.height) s.height = typeof props.height === "number" ? `${props.height}px` : props.height;
159
+ return s;
160
+ });
161
+ const classes = computed(() => {
162
+ const base = ["bg-bg-hover"];
163
+ switch (props.variant) {
164
+ case "circular":
165
+ base.push("rounded-full");
166
+ if (!props.width && !props.height) base.push("w-10 h-10");
167
+ break;
168
+ case "rectangular":
169
+ base.push("rounded-none");
170
+ if (!props.height) base.push("h-24");
171
+ break;
172
+ case "rounded":
173
+ base.push("rounded-mint");
174
+ if (!props.height) base.push("h-24");
175
+ break;
176
+ default:
177
+ base.push("rounded");
178
+ if (!props.height) base.push("h-4");
179
+ if (!props.width) base.push("w-full");
180
+ }
181
+ switch (props.animation) {
182
+ case "pulse":
183
+ base.push("animate-pulse");
184
+ break;
185
+ case "wave":
186
+ base.push("skeleton-wave");
187
+ break;
188
+ }
189
+ return base;
190
+ });
191
+ return (_ctx, _cache) => {
192
+ return openBlock(), createElementBlock("div", {
193
+ class: normalizeClass(classes.value),
194
+ style: normalizeStyle(style.value)
195
+ }, null, 6);
196
+ };
197
+ }
198
+ });
199
+ //#endregion
200
+ //#region src/components/EmptyState.vue?vue&type=script&setup=true&lang.ts
201
+ var _hoisted_1$2 = ["d"];
202
+ var _hoisted_2$1 = ["d"];
203
+ var _hoisted_3$1 = { class: "mint-empty-state__body" };
204
+ var _hoisted_4$1 = {
205
+ key: 0,
206
+ class: "mint-empty-state__title"
207
+ };
208
+ var _hoisted_5$1 = {
209
+ key: 1,
210
+ class: "mint-empty-state__description"
211
+ };
212
+ var _hoisted_6$1 = {
213
+ key: 0,
214
+ class: "mint-empty-state__action"
215
+ };
216
+ //#endregion
217
+ //#region src/components/EmptyState.vue
218
+ var EmptyState_default = /* @__PURE__ */ defineComponent({
219
+ __name: "EmptyState",
220
+ props: {
221
+ title: {},
222
+ description: {},
223
+ iconPath: {},
224
+ color: { default: "primary" },
225
+ size: { default: "md" },
226
+ variant: { default: "illustrated" },
227
+ actionLabel: {}
228
+ },
229
+ emits: ["action"],
230
+ setup(__props, { emit: __emit }) {
231
+ /** Empty-state placeholder with icon badge, headline, description, default slot, and optional CTA button. */
232
+ const emit = __emit;
233
+ const defaultIconPaths = [
234
+ "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",
235
+ "M7 11h10",
236
+ "M7 15h6",
237
+ "M7 7h8"
238
+ ];
239
+ return (_ctx, _cache) => {
240
+ return openBlock(), createElementBlock("div", { class: normalizeClass([
241
+ "mint-empty-state",
242
+ `mint-empty-state--${__props.variant}`,
243
+ `mint-empty-state--${__props.size}`
244
+ ]) }, [
245
+ createElementVNode("div", { class: normalizeClass(["mint-empty-state__icon-wrapper", `mint-empty-state__icon-wrapper--${__props.color}`]) }, [renderSlot(_ctx.$slots, "icon", {}, () => [(openBlock(), createElementBlock("svg", {
246
+ class: normalizeClass(["mint-empty-state__icon", `mint-empty-state__icon--${__props.color}`]),
247
+ viewBox: "0 0 24 24",
248
+ fill: "none",
249
+ stroke: "currentColor",
250
+ "stroke-width": "2",
251
+ "stroke-linecap": "round",
252
+ "stroke-linejoin": "round"
253
+ }, [__props.iconPath ? (openBlock(), createElementBlock("path", {
254
+ key: 0,
255
+ d: __props.iconPath
256
+ }, null, 8, _hoisted_1$2)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, renderList(defaultIconPaths, (d, i) => {
257
+ return createElementVNode("path", {
258
+ key: i,
259
+ d
260
+ }, null, 8, _hoisted_2$1);
261
+ }), 64))], 2))])], 2),
262
+ createElementVNode("div", _hoisted_3$1, [
263
+ __props.title ? (openBlock(), createElementBlock("h3", _hoisted_4$1, toDisplayString(__props.title), 1)) : createCommentVNode("", true),
264
+ __props.description ? (openBlock(), createElementBlock("p", _hoisted_5$1, toDisplayString(__props.description), 1)) : createCommentVNode("", true),
265
+ renderSlot(_ctx.$slots, "default")
266
+ ]),
267
+ __props.actionLabel ? (openBlock(), createElementBlock("div", _hoisted_6$1, [createVNode(BaseButton_default, { onClick: _cache[0] || (_cache[0] = ($event) => emit("action")) }, {
268
+ default: withCtx(() => [createTextVNode(toDisplayString(__props.actionLabel), 1)]),
269
+ _: 1
270
+ })])) : createCommentVNode("", true)
271
+ ], 2);
272
+ };
273
+ }
274
+ });
275
+ //#endregion
276
+ //#region src/components/ExperimentCodeBadge.vue?vue&type=script&setup=true&lang.ts
277
+ var _hoisted_1$1 = [
278
+ "role",
279
+ "tabindex",
280
+ "title"
281
+ ];
282
+ //#endregion
283
+ //#region src/components/ExperimentCodeBadge.vue
284
+ var ExperimentCodeBadge_default = /* @__PURE__ */ defineComponent({
285
+ __name: "ExperimentCodeBadge",
286
+ props: {
287
+ code: {},
288
+ size: { default: "md" },
289
+ copyable: {
290
+ type: Boolean,
291
+ default: true
292
+ }
293
+ },
294
+ emits: ["copy"],
295
+ setup(__props, { emit: __emit }) {
296
+ /** Inline badge that displays an experiment code and copies it to the clipboard on click. */
297
+ const props = __props;
298
+ const emit = __emit;
299
+ const copied = ref(false);
300
+ let copyTimeout = null;
301
+ async function handleCopy() {
302
+ if (!props.copyable) return;
303
+ try {
304
+ await navigator.clipboard.writeText(props.code);
305
+ copied.value = true;
306
+ emit("copy", props.code);
307
+ if (copyTimeout) clearTimeout(copyTimeout);
308
+ copyTimeout = setTimeout(() => {
309
+ copied.value = false;
310
+ }, 1500);
311
+ } catch {}
312
+ }
313
+ function handleKeydown(event) {
314
+ if (!props.copyable) return;
315
+ if (event.key === "Enter" || event.key === " ") {
316
+ event.preventDefault();
317
+ handleCopy();
318
+ }
319
+ }
320
+ return (_ctx, _cache) => {
321
+ return openBlock(), createElementBlock("span", {
322
+ class: normalizeClass([
323
+ "mint-exp-code",
324
+ `mint-exp-code--${__props.size}`,
325
+ {
326
+ "mint-exp-code--copyable": __props.copyable,
327
+ "mint-exp-code--copied": copied.value
328
+ }
329
+ ]),
330
+ role: __props.copyable ? "button" : void 0,
331
+ tabindex: __props.copyable ? 0 : void 0,
332
+ title: __props.copyable ? copied.value ? "Copied!" : "Click to copy" : void 0,
333
+ onClick: handleCopy,
334
+ onKeydown: handleKeydown
335
+ }, toDisplayString(copied.value ? "Copied!" : __props.code), 43, _hoisted_1$1);
336
+ };
337
+ }
338
+ });
339
+ //#endregion
340
+ //#region src/components/ExperimentSelectorModal.vue?vue&type=script&setup=true&lang.ts
341
+ var _hoisted_1 = { class: "mint-experiment-selector__filters-row" };
342
+ var _hoisted_2 = { class: "mint-experiment-selector__search" };
343
+ var _hoisted_3 = { class: "mint-experiment-selector__filter-select" };
344
+ var _hoisted_4 = {
345
+ key: 0,
346
+ class: "mint-experiment-selector__filter-select"
347
+ };
348
+ var _hoisted_5 = {
349
+ key: 0,
350
+ class: "mint-experiment-selector__filters-dot"
351
+ };
352
+ var _hoisted_6 = {
353
+ key: 0,
354
+ class: "mint-experiment-selector__filters-advanced"
355
+ };
356
+ var _hoisted_7 = {
357
+ key: 0,
358
+ class: "mint-experiment-selector__filter-select"
359
+ };
360
+ var _hoisted_8 = { class: "mint-experiment-selector__filter-select" };
361
+ var _hoisted_9 = { class: "mint-experiment-selector__filter-select" };
362
+ var _hoisted_10 = { class: "mint-experiment-selector__group-toggle" };
363
+ var _hoisted_11 = {
364
+ key: 1,
365
+ class: "mint-experiment-selector__skeleton"
366
+ };
367
+ var _hoisted_12 = { class: "mint-experiment-selector__skeleton-content" };
368
+ var _hoisted_13 = {
369
+ key: 2,
370
+ class: "mint-experiment-selector__error"
371
+ };
372
+ var _hoisted_14 = ["onClick"];
373
+ var _hoisted_15 = { class: "mint-experiment-selector__group-name" };
374
+ var _hoisted_16 = { class: "mint-experiment-selector__group-count" };
375
+ var _hoisted_17 = ["onClick", "onMouseenter"];
376
+ var _hoisted_18 = { class: "mint-experiment-selector__row-content" };
377
+ var _hoisted_19 = { class: "mint-experiment-selector__name" };
378
+ var _hoisted_20 = { class: "mint-experiment-selector__meta" };
379
+ var _hoisted_21 = ["onClick", "onMouseenter"];
380
+ var _hoisted_22 = { class: "mint-experiment-selector__row-content" };
381
+ var _hoisted_23 = { class: "mint-experiment-selector__name" };
382
+ var _hoisted_24 = { class: "mint-experiment-selector__meta" };
383
+ var _hoisted_25 = { key: 0 };
384
+ var _hoisted_26 = {
385
+ key: 6,
386
+ class: "mint-experiment-selector__footer"
387
+ };
388
+ //#endregion
389
+ //#region src/components/ExperimentSelectorModal.vue
390
+ var ExperimentSelectorModal_default = /* @__PURE__ */ defineComponent({
391
+ __name: "ExperimentSelectorModal",
392
+ props: {
393
+ modelValue: { type: Boolean },
394
+ experimentType: {},
395
+ currentExperimentId: { default: null },
396
+ title: { default: "Select Experiment" },
397
+ size: { default: "full" },
398
+ groupByProject: {
399
+ type: Boolean,
400
+ default: false
401
+ },
402
+ showFilters: {
403
+ type: Boolean,
404
+ default: false
405
+ }
406
+ },
407
+ emits: [
408
+ "update:modelValue",
409
+ "select",
410
+ "deselect"
411
+ ],
412
+ setup(__props, { emit: __emit }) {
413
+ /** Modal for searching and selecting an experiment from the platform list with filters and keyboard nav. */
414
+ const props = __props;
415
+ const emit = __emit;
416
+ const { experiments, filters, isLoading, error, sortKey, experimentTypes, projects, groupedByProject, fetch: fetchExperiments, fetchFilterOptions } = useExperimentSelector({ experimentType: props.experimentType });
417
+ const activeIndex = ref(-1);
418
+ const listRef = ref(null);
419
+ const showAdvanced = ref(props.showFilters);
420
+ const groupToggle = ref(props.groupByProject);
421
+ const hasActiveAdvancedFilters = computed(() => !!(filters.project || filters.experimentType || filters.datePreset || sortKey.value !== "created_at:desc"));
422
+ const typeFilterOptions = computed(() => [{
423
+ value: "",
424
+ label: "All types"
425
+ }, ...experimentTypes.value.map((t) => ({
426
+ value: t.value,
427
+ label: t.label
428
+ }))]);
429
+ const projectFilterOptions = computed(() => [{
430
+ value: "",
431
+ label: "All projects"
432
+ }, ...projects.value]);
433
+ const flatExperiments = computed(() => {
434
+ if (!groupToggle.value) return experiments.value;
435
+ return groupedByProject.value.flatMap(([, exps]) => exps);
436
+ });
437
+ function setFilter(key, value) {
438
+ filters[key] = String(value) || void 0;
439
+ }
440
+ function handleSortChange(value) {
441
+ sortKey.value = String(value) || "created_at:desc";
442
+ }
443
+ function handleSelect(experiment) {
444
+ emit("select", experiment);
445
+ emit("update:modelValue", false);
446
+ }
447
+ function handleDeselect() {
448
+ emit("deselect");
449
+ emit("update:modelValue", false);
450
+ }
451
+ function handleKeydown(event) {
452
+ const list = flatExperiments.value;
453
+ if (!list.length) return;
454
+ switch (event.key) {
455
+ case "ArrowDown":
456
+ event.preventDefault();
457
+ activeIndex.value = Math.min(activeIndex.value + 1, list.length - 1);
458
+ scrollActiveIntoView();
459
+ break;
460
+ case "ArrowUp":
461
+ event.preventDefault();
462
+ activeIndex.value = Math.max(activeIndex.value - 1, 0);
463
+ scrollActiveIntoView();
464
+ break;
465
+ case "Enter":
466
+ event.preventDefault();
467
+ if (activeIndex.value >= 0 && activeIndex.value < list.length) handleSelect(list[activeIndex.value]);
468
+ break;
469
+ }
470
+ }
471
+ function scrollActiveIntoView() {
472
+ nextTick(() => {
473
+ (listRef.value?.querySelector(".mint-experiment-selector__row--focused"))?.scrollIntoView({ block: "nearest" });
474
+ });
475
+ }
476
+ const flatIndexMap = computed(() => {
477
+ const map = /* @__PURE__ */ new Map();
478
+ flatExperiments.value.forEach((exp, i) => map.set(exp.id, i));
479
+ return map;
480
+ });
481
+ function getFlatIndex(experiment) {
482
+ return flatIndexMap.value.get(experiment.id) ?? -1;
483
+ }
484
+ const collapsedGroups = reactive(/* @__PURE__ */ new Set());
485
+ function toggleGroup(groupName) {
486
+ if (collapsedGroups.has(groupName)) collapsedGroups.delete(groupName);
487
+ else collapsedGroups.add(groupName);
488
+ }
489
+ watch(experiments, () => {
490
+ activeIndex.value = -1;
491
+ });
492
+ watch(() => props.modelValue, (isOpen) => {
493
+ if (isOpen) {
494
+ activeIndex.value = -1;
495
+ collapsedGroups.clear();
496
+ fetchFilterOptions();
497
+ fetchExperiments();
498
+ }
499
+ });
500
+ return (_ctx, _cache) => {
501
+ return openBlock(), createBlock(BaseModal_default, {
502
+ "model-value": __props.modelValue,
503
+ title: __props.title,
504
+ size: __props.size,
505
+ "onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => emit("update:modelValue", $event))
506
+ }, {
507
+ default: withCtx(() => [createElementVNode("div", {
508
+ class: "mint-experiment-selector",
509
+ onKeydown: handleKeydown
510
+ }, [
511
+ createElementVNode("div", _hoisted_1, [
512
+ createElementVNode("div", _hoisted_2, [createVNode(BaseInput_default, {
513
+ modelValue: unref(filters).search,
514
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => unref(filters).search = $event),
515
+ placeholder: "Search experiments...",
516
+ size: "sm",
517
+ type: "search"
518
+ }, null, 8, ["modelValue"])]),
519
+ createElementVNode("div", _hoisted_3, [createVNode(BaseSelect_default, {
520
+ "model-value": unref(filters).status ?? "",
521
+ options: unref(EXPERIMENT_STATUS_OPTIONS),
522
+ size: "sm",
523
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = (v) => setFilter("status", v))
524
+ }, null, 8, ["model-value", "options"])]),
525
+ typeFilterOptions.value.length > 1 ? (openBlock(), createElementBlock("div", _hoisted_4, [createVNode(BaseSelect_default, {
526
+ "model-value": unref(filters).experimentType ?? "",
527
+ options: typeFilterOptions.value,
528
+ size: "sm",
529
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = (v) => setFilter("experimentType", v))
530
+ }, null, 8, ["model-value", "options"])])) : createCommentVNode("", true),
531
+ createElementVNode("button", {
532
+ class: normalizeClass(["mint-experiment-selector__filters-toggle", { "mint-experiment-selector__filters-toggle--active": hasActiveAdvancedFilters.value }]),
533
+ type: "button",
534
+ onClick: _cache[3] || (_cache[3] = ($event) => showAdvanced.value = !showAdvanced.value)
535
+ }, [
536
+ _cache[8] || (_cache[8] = createElementVNode("svg", {
537
+ width: "14",
538
+ height: "14",
539
+ viewBox: "0 0 24 24",
540
+ fill: "none",
541
+ stroke: "currentColor",
542
+ "stroke-width": "2",
543
+ "stroke-linecap": "round",
544
+ "stroke-linejoin": "round"
545
+ }, [
546
+ createElementVNode("line", {
547
+ x1: "4",
548
+ y1: "6",
549
+ x2: "20",
550
+ y2: "6"
551
+ }),
552
+ createElementVNode("line", {
553
+ x1: "8",
554
+ y1: "12",
555
+ x2: "20",
556
+ y2: "12"
557
+ }),
558
+ createElementVNode("line", {
559
+ x1: "12",
560
+ y1: "18",
561
+ x2: "20",
562
+ y2: "18"
563
+ }),
564
+ createElementVNode("circle", {
565
+ cx: "6",
566
+ cy: "12",
567
+ r: "2"
568
+ }),
569
+ createElementVNode("circle", {
570
+ cx: "10",
571
+ cy: "18",
572
+ r: "2"
573
+ }),
574
+ createElementVNode("circle", {
575
+ cx: "6",
576
+ cy: "6",
577
+ r: "2"
578
+ })
579
+ ], -1)),
580
+ _cache[9] || (_cache[9] = createTextVNode(" Filters ", -1)),
581
+ hasActiveAdvancedFilters.value ? (openBlock(), createElementBlock("span", _hoisted_5)) : createCommentVNode("", true)
582
+ ], 2)
583
+ ]),
584
+ showAdvanced.value ? (openBlock(), createElementBlock("div", _hoisted_6, [
585
+ projectFilterOptions.value.length > 1 ? (openBlock(), createElementBlock("div", _hoisted_7, [createVNode(BaseSelect_default, {
586
+ "model-value": unref(filters).project ?? "",
587
+ options: projectFilterOptions.value,
588
+ size: "sm",
589
+ "onUpdate:modelValue": _cache[4] || (_cache[4] = (v) => setFilter("project", v))
590
+ }, null, 8, ["model-value", "options"])])) : createCommentVNode("", true),
591
+ createElementVNode("div", _hoisted_8, [createVNode(BaseSelect_default, {
592
+ "model-value": unref(filters).datePreset ?? "",
593
+ options: unref(DATE_PRESET_OPTIONS),
594
+ size: "sm",
595
+ "onUpdate:modelValue": _cache[5] || (_cache[5] = (v) => setFilter("datePreset", v))
596
+ }, null, 8, ["model-value", "options"])]),
597
+ createElementVNode("div", _hoisted_9, [createVNode(BaseSelect_default, {
598
+ "model-value": unref(sortKey),
599
+ options: unref(SORT_OPTIONS),
600
+ size: "sm",
601
+ "onUpdate:modelValue": handleSortChange
602
+ }, null, 8, ["model-value", "options"])]),
603
+ createElementVNode("label", _hoisted_10, [withDirectives(createElementVNode("input", {
604
+ "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => groupToggle.value = $event),
605
+ type: "checkbox",
606
+ class: "mint-experiment-selector__group-checkbox"
607
+ }, null, 512), [[vModelCheckbox, groupToggle.value]]), _cache[10] || (_cache[10] = createTextVNode(" Group by project ", -1))])
608
+ ])) : createCommentVNode("", true),
609
+ unref(isLoading) ? (openBlock(), createElementBlock("div", _hoisted_11, [(openBlock(), createElementBlock(Fragment, null, renderList(4, (n) => {
610
+ return createElementVNode("div", {
611
+ key: n,
612
+ class: "mint-experiment-selector__skeleton-row"
613
+ }, [createElementVNode("div", _hoisted_12, [createVNode(Skeleton_default, {
614
+ width: 120 + n * 20,
615
+ height: "14px"
616
+ }, null, 8, ["width"]), createVNode(Skeleton_default, {
617
+ width: "80px",
618
+ height: "10px"
619
+ })]), createVNode(Skeleton_default, {
620
+ width: "60px",
621
+ height: "20px",
622
+ variant: "rounded"
623
+ })]);
624
+ }), 64))])) : unref(error) ? (openBlock(), createElementBlock("div", _hoisted_13, toDisplayString(unref(error)), 1)) : unref(experiments).length === 0 ? (openBlock(), createBlock(EmptyState_default, {
625
+ key: 3,
626
+ title: "No experiments found",
627
+ description: "Try adjusting your search or filters.",
628
+ size: "sm"
629
+ })) : groupToggle.value ? (openBlock(), createElementBlock("div", {
630
+ key: 4,
631
+ ref_key: "listRef",
632
+ ref: listRef,
633
+ class: "mint-experiment-selector__list"
634
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(groupedByProject), ([groupName, groupExps]) => {
635
+ return openBlock(), createElementBlock(Fragment, { key: groupName }, [createElementVNode("button", {
636
+ type: "button",
637
+ class: "mint-experiment-selector__group-header",
638
+ onClick: ($event) => toggleGroup(groupName)
639
+ }, [
640
+ (openBlock(), createElementBlock("svg", {
641
+ class: normalizeClass(["mint-experiment-selector__group-chevron", { "mint-experiment-selector__group-chevron--collapsed": collapsedGroups.has(groupName) }]),
642
+ width: "14",
643
+ height: "14",
644
+ viewBox: "0 0 24 24",
645
+ fill: "none",
646
+ stroke: "currentColor",
647
+ "stroke-width": "2",
648
+ "stroke-linecap": "round",
649
+ "stroke-linejoin": "round"
650
+ }, [..._cache[11] || (_cache[11] = [createElementVNode("polyline", { points: "6 9 12 15 18 9" }, null, -1)])], 2)),
651
+ createElementVNode("span", _hoisted_15, toDisplayString(groupName), 1),
652
+ createElementVNode("span", _hoisted_16, toDisplayString(groupExps.length), 1)
653
+ ], 8, _hoisted_14), !collapsedGroups.has(groupName) ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(groupExps, (exp) => {
654
+ return openBlock(), createElementBlock("div", {
655
+ key: exp.id,
656
+ class: normalizeClass(["mint-experiment-selector__row", {
657
+ "mint-experiment-selector__row--active": exp.id === __props.currentExperimentId,
658
+ "mint-experiment-selector__row--focused": getFlatIndex(exp) === activeIndex.value
659
+ }]),
660
+ onClick: ($event) => handleSelect(exp),
661
+ onMouseenter: ($event) => activeIndex.value = getFlatIndex(exp)
662
+ }, [createElementVNode("div", _hoisted_18, [createElementVNode("div", _hoisted_19, [createTextVNode(toDisplayString(exp.name) + " ", 1), exp.experiment_code ? (openBlock(), createBlock(ExperimentCodeBadge_default, {
663
+ key: 0,
664
+ code: exp.experiment_code,
665
+ size: "sm",
666
+ copyable: false
667
+ }, null, 8, ["code"])) : createCommentVNode("", true)]), createElementVNode("div", _hoisted_20, [createElementVNode("span", null, toDisplayString(unref(formatExperimentDate)(exp.created_at)), 1)])]), createVNode(BasePill_default, {
668
+ variant: unref(getExperimentStatusVariant)(exp.status),
669
+ size: "sm"
670
+ }, {
671
+ default: withCtx(() => [createTextVNode(toDisplayString(unref(formatExperimentStatus)(exp.status)), 1)]),
672
+ _: 2
673
+ }, 1032, ["variant"])], 42, _hoisted_17);
674
+ }), 128)) : createCommentVNode("", true)], 64);
675
+ }), 128))], 512)) : (openBlock(), createElementBlock("div", {
676
+ key: 5,
677
+ ref_key: "listRef",
678
+ ref: listRef,
679
+ class: "mint-experiment-selector__list"
680
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(experiments), (exp, idx) => {
681
+ return openBlock(), createElementBlock("div", {
682
+ key: exp.id,
683
+ class: normalizeClass(["mint-experiment-selector__row", {
684
+ "mint-experiment-selector__row--active": exp.id === __props.currentExperimentId,
685
+ "mint-experiment-selector__row--focused": idx === activeIndex.value
686
+ }]),
687
+ onClick: ($event) => handleSelect(exp),
688
+ onMouseenter: ($event) => activeIndex.value = idx
689
+ }, [createElementVNode("div", _hoisted_22, [createElementVNode("div", _hoisted_23, [createTextVNode(toDisplayString(exp.name) + " ", 1), exp.experiment_code ? (openBlock(), createBlock(ExperimentCodeBadge_default, {
690
+ key: 0,
691
+ code: exp.experiment_code,
692
+ size: "sm",
693
+ copyable: false
694
+ }, null, 8, ["code"])) : createCommentVNode("", true)]), createElementVNode("div", _hoisted_24, [exp.project_name || exp.project ? (openBlock(), createElementBlock("span", _hoisted_25, toDisplayString(exp.project_name || exp.project), 1)) : createCommentVNode("", true), createElementVNode("span", null, toDisplayString(unref(formatExperimentDate)(exp.created_at)), 1)])]), createVNode(BasePill_default, {
695
+ variant: unref(getExperimentStatusVariant)(exp.status),
696
+ size: "sm"
697
+ }, {
698
+ default: withCtx(() => [createTextVNode(toDisplayString(unref(formatExperimentStatus)(exp.status)), 1)]),
699
+ _: 2
700
+ }, 1032, ["variant"])], 42, _hoisted_21);
701
+ }), 128))], 512)),
702
+ __props.currentExperimentId != null ? (openBlock(), createElementBlock("div", _hoisted_26, [createElementVNode("button", {
703
+ type: "button",
704
+ class: "mint-experiment-selector__clear-btn",
705
+ onClick: handleDeselect
706
+ }, " Clear selection ")])) : createCommentVNode("", true)
707
+ ], 32)]),
708
+ _: 1
709
+ }, 8, [
710
+ "model-value",
711
+ "title",
712
+ "size"
713
+ ]);
714
+ };
715
+ }
716
+ });
717
+ //#endregion
718
+ export { BasePill_default as a, Skeleton_default as i, ExperimentCodeBadge_default as n, BaseButton_default as o, EmptyState_default as r, ExperimentSelectorModal_default as t };
719
+
720
+ //# sourceMappingURL=ExperimentSelectorModal-wm7yUdAr.js.map