@morscherlab/mint-sdk 1.0.11 → 1.0.13

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 (34) hide show
  1. package/dist/{ExperimentPopover-CCYB1oWp.js → ExperimentPopover-B29fIHQz.js} +6 -6
  2. package/dist/ExperimentPopover-B29fIHQz.js.map +1 -0
  3. package/dist/{ExperimentPopover-D0bg_fqM.js → ExperimentPopover-gdSA9ZCF.js} +1 -1
  4. package/dist/{ExperimentSelectorModal-K_IhFb0-.js → ExperimentSelectorModal-CHsU-LIh.js} +4 -4
  5. package/dist/ExperimentSelectorModal-CHsU-LIh.js.map +1 -0
  6. package/dist/{ExperimentSelectorModal-GHCfgaan.js → ExperimentSelectorModal-DIFyL5ta.js} +1 -1
  7. package/dist/__tests__/components/ExperimentSelectorModal.test.d.ts +1 -0
  8. package/dist/components/index.js +3 -3
  9. package/dist/{components-Ban_JZNk.js → components-CzFtQExv.js} +7 -7
  10. package/dist/{components-Ban_JZNk.js.map → components-CzFtQExv.js.map} +1 -1
  11. package/dist/composables/index.js +4 -4
  12. package/dist/{composables-BWh0MpcK.js → composables-BuG5yAb7.js} +3 -3
  13. package/dist/{composables-BWh0MpcK.js.map → composables-BuG5yAb7.js.map} +1 -1
  14. package/dist/{experiment-utils-hGXMHlAc.js → experiment-utils-D11yT3AR.js} +1 -2
  15. package/dist/{experiment-utils-hGXMHlAc.js.map → experiment-utils-D11yT3AR.js.map} +1 -1
  16. package/dist/index.js +7 -7
  17. package/dist/install.js +3 -3
  18. package/dist/styles.css +4 -8
  19. package/dist/{useExperimentSelector-B3hAGvL4.js → useExperimentSelector-BBaz0w51.js} +2 -2
  20. package/dist/{useExperimentSelector-B3hAGvL4.js.map → useExperimentSelector-BBaz0w51.js.map} +1 -1
  21. package/dist/{useProtocolTemplates-BJxS5F0_.js → useProtocolTemplates-DODHlhxr.js} +3 -3
  22. package/dist/{useProtocolTemplates-BJxS5F0_.js.map → useProtocolTemplates-DODHlhxr.js.map} +1 -1
  23. package/package.json +1 -1
  24. package/src/__tests__/components/ExperimentPopover.test.ts +23 -0
  25. package/src/__tests__/components/ExperimentSelectorModal.test.ts +74 -0
  26. package/src/__tests__/composables/experiment-utils.test.ts +2 -2
  27. package/src/__tests__/composables/useAppExperiment.test.ts +2 -1
  28. package/src/components/ExperimentPopover.story.vue +3 -4
  29. package/src/components/ExperimentPopover.vue +6 -6
  30. package/src/components/ExperimentSelectorModal.vue +1 -0
  31. package/src/composables/experiment-utils.ts +1 -1
  32. package/src/styles/components/experiment-popover.css +2 -4
  33. package/dist/ExperimentPopover-CCYB1oWp.js.map +0 -1
  34. package/dist/ExperimentSelectorModal-K_IhFb0-.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morscherlab/mint-sdk",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "MINT Platform SDK — Vue 3 components, composables, and types for plugin development. MINT = Mass-spec INtegrated Toolkit.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,6 +21,29 @@ describe('ExperimentPopover', () => {
21
21
  document.body.innerHTML = ''
22
22
  })
23
23
 
24
+ it('prefers the experiment name in the trigger when both name and code are available', () => {
25
+ const wrapper = mountPopover({
26
+ experimentName: 'Dose response run',
27
+ experimentCode: 'EXP-001',
28
+ })
29
+
30
+ const trigger = wrapper.find('.mint-experiment-popover__trigger')
31
+ expect(trigger.text()).toContain('Dose response run')
32
+ expect(trigger.text()).not.toContain('EXP-001')
33
+
34
+ wrapper.unmount()
35
+ })
36
+
37
+ it('falls back to the experiment code in the trigger when the name is unavailable', () => {
38
+ const wrapper = mountPopover({
39
+ experimentCode: 'EXP-001',
40
+ })
41
+
42
+ expect(wrapper.find('.mint-experiment-popover__trigger').text()).toContain('EXP-001')
43
+
44
+ wrapper.unmount()
45
+ })
46
+
24
47
  it('opens and closes the popover on trigger and outside click', async () => {
25
48
  const wrapper = mountPopover({
26
49
  experimentName: 'Dose response run',
@@ -0,0 +1,74 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import { computed, reactive, ref } from 'vue'
4
+
5
+ import ExperimentSelectorModal from '../../components/ExperimentSelectorModal.vue'
6
+
7
+ const fetchExperiments = vi.fn(async () => undefined)
8
+ const fetchFilterOptions = vi.fn(async () => undefined)
9
+
10
+ vi.mock('../../composables/useExperimentSelector', () => ({
11
+ useExperimentSelector: vi.fn(() => ({
12
+ experiments: ref([]),
13
+ filters: reactive({
14
+ search: undefined,
15
+ status: undefined,
16
+ project: undefined,
17
+ experimentType: undefined,
18
+ datePreset: undefined,
19
+ }),
20
+ isLoading: ref(false),
21
+ error: ref(null),
22
+ sortKey: ref('created_at:desc'),
23
+ experimentTypes: ref([]),
24
+ projects: ref([]),
25
+ groupedByProject: computed(() => []),
26
+ fetch: fetchExperiments,
27
+ fetchFilterOptions,
28
+ })),
29
+ }))
30
+
31
+ describe('ExperimentSelectorModal', () => {
32
+ beforeEach(() => {
33
+ fetchExperiments.mockClear()
34
+ fetchFilterOptions.mockClear()
35
+ })
36
+
37
+ it('fetches experiments when mounted already open', () => {
38
+ mount(ExperimentSelectorModal, {
39
+ props: {
40
+ modelValue: true,
41
+ },
42
+ global: {
43
+ stubs: {
44
+ BaseModal: {
45
+ props: ['modelValue', 'title', 'size'],
46
+ template: '<div><slot /></div>',
47
+ },
48
+ },
49
+ },
50
+ })
51
+
52
+ expect(fetchFilterOptions).toHaveBeenCalledTimes(1)
53
+ expect(fetchExperiments).toHaveBeenCalledTimes(1)
54
+ })
55
+
56
+ it('does not fetch while mounted closed', () => {
57
+ mount(ExperimentSelectorModal, {
58
+ props: {
59
+ modelValue: false,
60
+ },
61
+ global: {
62
+ stubs: {
63
+ BaseModal: {
64
+ props: ['modelValue', 'title', 'size'],
65
+ template: '<div><slot /></div>',
66
+ },
67
+ },
68
+ },
69
+ })
70
+
71
+ expect(fetchFilterOptions).not.toHaveBeenCalled()
72
+ expect(fetchExperiments).not.toHaveBeenCalled()
73
+ })
74
+ })
@@ -22,9 +22,9 @@ describe('experiment-utils', () => {
22
22
  expect(getExperimentStatusVariant(undefined)).toBe('default')
23
23
  })
24
24
 
25
- it('resolves formal experiment codes before generated compact fallbacks', () => {
25
+ it('resolves only formal experiment codes', () => {
26
26
  expect(resolveExperimentCode({ id: 42, experiment_code: 'DR-042' })).toBe('DR-042')
27
- expect(resolveExperimentCode({ id: 42 })).toBe('EXP-42')
27
+ expect(resolveExperimentCode({ id: 42 })).toBeUndefined()
28
28
  expect(resolveExperimentCode({ id: null })).toBeUndefined()
29
29
  })
30
30
  })
@@ -121,12 +121,13 @@ describe('useAppExperiment', () => {
121
121
  })
122
122
 
123
123
  it('should update experimentName and experimentId on set', () => {
124
- const { set, experimentName, experimentId } = useAppExperiment()
124
+ const { set, experimentName, experimentCode, experimentId } = useAppExperiment()
125
125
  const experiment = makeExperiment({ id: 7, name: 'Alpha Run' })
126
126
 
127
127
  set(experiment)
128
128
 
129
129
  expect(experimentName.value).toBe('Alpha Run')
130
+ expect(experimentCode.value).toBeUndefined()
130
131
  expect(experimentId.value).toBe(7)
131
132
  })
132
133
 
@@ -48,8 +48,8 @@ const STATUSES = ['planned', 'ongoing', 'completed', 'ready_to_extract', 'proces
48
48
  </template>
49
49
 
50
50
  <template #controls="{ state }">
51
- <HstText v-model="state.experimentCode" title="Experiment code (shown in trigger)" />
52
- <HstText v-model="state.experimentName" title="Experiment name (shown in panel)" />
51
+ <HstText v-model="state.experimentCode" title="Experiment code (shown in panel)" />
52
+ <HstText v-model="state.experimentName" title="Experiment name (shown in trigger)" />
53
53
  <HstSelect
54
54
  v-model="state.experimentStatus"
55
55
  title="Status"
@@ -93,8 +93,7 @@ const STATUSES = ['planned', 'ongoing', 'completed', 'ready_to_extract', 'proces
93
93
  />
94
94
  </div>
95
95
  <div style="text-align: center; font-size: 0.75rem; color: var(--text-muted); padding: 0 1rem 1rem;">
96
- Trigger shows only the code (<code>EXP-042</code>). Click to open the panel — full name, status, and actions
97
- live inside.
96
+ Trigger shows the name. Click to open the panel — formal code, status, and actions live inside.
98
97
  </div>
99
98
  </Variant>
100
99
 
@@ -92,8 +92,8 @@ onUnmounted(() => {
92
92
  { 'mint-experiment-popover__split--with-save': showSave && experimentName },
93
93
  ]"
94
94
  >
95
- <!-- Left: experiment trigger (opens popover) shows only the code for
96
- maximum topbar compactness; full name + status live in the panel. -->
95
+ <!-- Left: experiment trigger (opens popover). Prefer the human-readable
96
+ name; the formal code, when present, stays in the detail panel. -->
97
97
  <button
98
98
  type="button"
99
99
  :class="[
@@ -101,7 +101,7 @@ onUnmounted(() => {
101
101
  { 'mint-experiment-popover__trigger--active': isOpen },
102
102
  { 'mint-experiment-popover__trigger--empty': !experimentCode && !experimentName },
103
103
  ]"
104
- :title="experimentName || undefined"
104
+ :title="experimentName || experimentCode || undefined"
105
105
  @click.stop="toggle"
106
106
  >
107
107
  <!-- Flask icon -->
@@ -113,9 +113,9 @@ onUnmounted(() => {
113
113
  d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"
114
114
  />
115
115
  </svg>
116
- <!-- Code preferred, name as fallback, "No experiment" as empty state -->
117
- <span v-if="experimentCode" class="mint-experiment-popover__trigger-code">{{ experimentCode }}</span>
118
- <span v-else-if="experimentName" class="mint-experiment-popover__trigger-text">{{ experimentName }}</span>
116
+ <!-- Name preferred, code as fallback, "No experiment" as empty state -->
117
+ <span v-if="experimentName" class="mint-experiment-popover__trigger-text">{{ experimentName }}</span>
118
+ <span v-else-if="experimentCode" class="mint-experiment-popover__trigger-code">{{ experimentCode }}</span>
119
119
  <span v-else class="mint-experiment-popover__trigger-text">No experiment</span>
120
120
  <!-- Chevron -->
121
121
  <svg class="mint-experiment-popover__trigger-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@@ -174,6 +174,7 @@ watch(
174
174
  fetchExperiments()
175
175
  }
176
176
  },
177
+ { immediate: true },
177
178
  )
178
179
  </script>
179
180
 
@@ -64,7 +64,7 @@ export function getExperimentStatusVariant(status?: ExperimentStatus | string |
64
64
 
65
65
  export function resolveExperimentCode(experiment: ExperimentCodeSource): string | undefined {
66
66
  if (experiment.experiment_code) return experiment.experiment_code
67
- return experiment.id != null ? `EXP-${experiment.id}` : undefined
67
+ return undefined
68
68
  }
69
69
 
70
70
  export const DATE_PRESET_OPTIONS: SelectOption<string>[] = [
@@ -20,9 +20,7 @@
20
20
  }
21
21
 
22
22
  /* ─── Trigger (left / main chip) ───
23
- Shows only the experiment code (e.g., "EXP-001") for maximum topbar
24
- compactness. The trigger is now sized to its content — no fixed max-width
25
- is needed once the long name is gone. */
23
+ Shows the experiment name, truncated to preserve topbar density. */
26
24
  .mint-experiment-popover__trigger {
27
25
  display: inline-flex;
28
26
  align-items: center;
@@ -99,7 +97,7 @@
99
97
  text-align: left;
100
98
  }
101
99
 
102
- /* Experiment code in the trigger — monospace, tabular, slightly tighter tracking */
100
+ /* Experiment code fallback in the trigger — monospace, tabular */
103
101
  .mint-experiment-popover__trigger-code {
104
102
  font-family: var(--font-mono, 'Fira Code', monospace);
105
103
  font-size: 0.75rem;
@@ -1 +0,0 @@
1
- {"version":3,"file":"ExperimentPopover-CCYB1oWp.js","names":["$slots"],"sources":["../src/components/ConfirmDialog.vue","../src/components/ConfirmDialog.vue","../src/components/ExperimentPopover.vue","../src/components/ExperimentPopover.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/** Confirm/cancel dialog with danger, warning, and info variants; blocks close while loading. */\nimport BaseModal from './BaseModal.vue'\n\ninterface Props {\n modelValue: boolean\n title?: string\n subtitle?: string\n message?: string\n variant?: 'danger' | 'warning' | 'info'\n confirmLabel?: string\n cancelLabel?: string\n loading?: boolean\n}\n\nwithDefaults(defineProps<Props>(), {\n title: 'Confirm',\n variant: 'danger',\n confirmLabel: 'Confirm',\n cancelLabel: 'Cancel',\n loading: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n confirm: []\n cancel: []\n}>()\n\nfunction handleCancel() {\n emit('update:modelValue', false)\n emit('cancel')\n}\n\nfunction handleConfirm() {\n emit('confirm')\n}\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :subtitle=\"subtitle\"\n size=\"sm\"\n :closable=\"!loading\"\n :close-on-overlay=\"!loading\"\n :close-on-escape=\"!loading\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mint-confirm\">\n <!-- Icon is now opt-in via slot. Refresh design keeps the body focused on\n the message; variant intent is carried by the confirm button's color. -->\n <div\n v-if=\"$slots.icon\"\n :class=\"['mint-confirm__icon', `mint-confirm__icon--${variant}`]\"\n >\n <slot name=\"icon\" />\n </div>\n <p v-if=\"message\" class=\"mint-confirm__message\">{{ message }}</p>\n <slot />\n </div>\n\n <template #footer>\n <div class=\"mint-confirm__footer\">\n <button\n type=\"button\"\n class=\"mint-confirm__btn-cancel\"\n :disabled=\"loading\"\n @click=\"handleCancel\"\n >\n {{ cancelLabel }}\n </button>\n <button\n type=\"button\"\n :class=\"['mint-confirm__btn-confirm', `mint-confirm__btn-confirm--${variant}`]\"\n :disabled=\"loading\"\n @click=\"handleConfirm\"\n >\n <svg v-if=\"loading\" class=\"mint-confirm__btn-spinner\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle style=\"opacity: 0.25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\" />\n <path style=\"opacity: 0.75\" fill=\"currentColor\" 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\" />\n </svg>\n {{ confirmLabel }}\n </button>\n </div>\n </template>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/confirm-dialog.css';\n</style>\n","<script setup lang=\"ts\">\n/** Confirm/cancel dialog with danger, warning, and info variants; blocks close while loading. */\nimport BaseModal from './BaseModal.vue'\n\ninterface Props {\n modelValue: boolean\n title?: string\n subtitle?: string\n message?: string\n variant?: 'danger' | 'warning' | 'info'\n confirmLabel?: string\n cancelLabel?: string\n loading?: boolean\n}\n\nwithDefaults(defineProps<Props>(), {\n title: 'Confirm',\n variant: 'danger',\n confirmLabel: 'Confirm',\n cancelLabel: 'Cancel',\n loading: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n confirm: []\n cancel: []\n}>()\n\nfunction handleCancel() {\n emit('update:modelValue', false)\n emit('cancel')\n}\n\nfunction handleConfirm() {\n emit('confirm')\n}\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :subtitle=\"subtitle\"\n size=\"sm\"\n :closable=\"!loading\"\n :close-on-overlay=\"!loading\"\n :close-on-escape=\"!loading\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mint-confirm\">\n <!-- Icon is now opt-in via slot. Refresh design keeps the body focused on\n the message; variant intent is carried by the confirm button's color. -->\n <div\n v-if=\"$slots.icon\"\n :class=\"['mint-confirm__icon', `mint-confirm__icon--${variant}`]\"\n >\n <slot name=\"icon\" />\n </div>\n <p v-if=\"message\" class=\"mint-confirm__message\">{{ message }}</p>\n <slot />\n </div>\n\n <template #footer>\n <div class=\"mint-confirm__footer\">\n <button\n type=\"button\"\n class=\"mint-confirm__btn-cancel\"\n :disabled=\"loading\"\n @click=\"handleCancel\"\n >\n {{ cancelLabel }}\n </button>\n <button\n type=\"button\"\n :class=\"['mint-confirm__btn-confirm', `mint-confirm__btn-confirm--${variant}`]\"\n :disabled=\"loading\"\n @click=\"handleConfirm\"\n >\n <svg v-if=\"loading\" class=\"mint-confirm__btn-spinner\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle style=\"opacity: 0.25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\" />\n <path style=\"opacity: 0.75\" fill=\"currentColor\" 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\" />\n </svg>\n {{ confirmLabel }}\n </button>\n </div>\n </template>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/confirm-dialog.css';\n</style>\n","<script setup lang=\"ts\">\n/** Floating popover showing the active experiment with save, detach, and select actions. */\nimport { ref, watch, onUnmounted } from 'vue'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport { formatExperimentStatus } from '../composables/experiment-utils'\nimport ConfirmDialog from './ConfirmDialog.vue'\n\ninterface Props {\n experimentName?: string\n experimentCode?: string\n experimentStatus?: string\n showSave?: boolean\n showDetach?: boolean\n saveDisabled?: boolean\n saveLoading?: boolean\n saveSuccessMessage?: string\n saveDisabledMessage?: string\n confirmSave?: boolean\n confirmTitle?: string\n confirmMessage?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showSave: false,\n showDetach: false,\n saveDisabled: false,\n saveLoading: false,\n confirmSave: true,\n})\n\nconst emit = defineEmits<{\n select: []\n save: []\n detach: []\n}>()\n\nconst { isOpen, rootRef: popoverRef, close, toggle } = useDropdownState({\n closeOnEscape: false,\n})\nconst showSuccess = ref(false)\nconst showConfirm = ref(false)\n\nfunction handleSelect() {\n emit('select')\n close()\n}\n\nfunction handleSave() {\n if (props.saveDisabled || props.saveLoading) return\n if (props.confirmSave) {\n showConfirm.value = true\n } else {\n emit('save')\n }\n}\n\nfunction handleConfirmSave() {\n showConfirm.value = false\n emit('save')\n}\n\nfunction handleDetach() {\n emit('detach')\n close()\n}\n\nlet successTimer: ReturnType<typeof setTimeout> | null = null\n\n// Show success state when saveSuccessMessage changes from empty to a value\nwatch(() => props.saveSuccessMessage, (msg) => {\n if (successTimer) clearTimeout(successTimer)\n if (msg) {\n showSuccess.value = true\n successTimer = setTimeout(() => {\n showSuccess.value = false\n successTimer = null\n }, 3000)\n }\n})\n\nonUnmounted(() => {\n if (successTimer) clearTimeout(successTimer)\n})\n</script>\n\n<template>\n <div ref=\"popoverRef\" class=\"mint-experiment-popover\">\n <!-- Split trigger: experiment pill + inline save -->\n <div\n :class=\"[\n 'mint-experiment-popover__split',\n { 'mint-experiment-popover__split--with-save': showSave && experimentName },\n ]\"\n >\n <!-- Left: experiment trigger (opens popover) — shows only the code for\n maximum topbar compactness; full name + status live in the panel. -->\n <button\n type=\"button\"\n :class=\"[\n 'mint-experiment-popover__trigger',\n { 'mint-experiment-popover__trigger--active': isOpen },\n { 'mint-experiment-popover__trigger--empty': !experimentCode && !experimentName },\n ]\"\n :title=\"experimentName || undefined\"\n @click.stop=\"toggle\"\n >\n <!-- Flask icon -->\n <svg class=\"mint-experiment-popover__trigger-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n <!-- Code preferred, name as fallback, \"No experiment\" as empty state -->\n <span v-if=\"experimentCode\" class=\"mint-experiment-popover__trigger-code\">{{ experimentCode }}</span>\n <span v-else-if=\"experimentName\" class=\"mint-experiment-popover__trigger-text\">{{ experimentName }}</span>\n <span v-else class=\"mint-experiment-popover__trigger-text\">No experiment</span>\n <!-- Chevron -->\n <svg class=\"mint-experiment-popover__trigger-chevron\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <!-- Right: inline save button (direct action) -->\n <button\n v-if=\"showSave && experimentName\"\n type=\"button\"\n :class=\"[\n 'mint-experiment-popover__save-trigger',\n { 'mint-experiment-popover__save-trigger--loading': saveLoading },\n { 'mint-experiment-popover__save-trigger--success': showSuccess },\n { 'mint-experiment-popover__save-trigger--disabled': saveDisabled && !showSuccess },\n ]\"\n :disabled=\"saveDisabled && !showSuccess\"\n :title=\"saveDisabled && saveDisabledMessage ? saveDisabledMessage : showSuccess && saveSuccessMessage ? saveSuccessMessage : 'Save to Experiment'\"\n @click.stop=\"handleSave\"\n >\n <!-- Loading spinner -->\n <span v-if=\"saveLoading\" class=\"mint-experiment-popover__spinner--inline\" />\n <!-- Success check -->\n <svg v-else-if=\"showSuccess\" class=\"mint-experiment-popover__save-trigger-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2.5\" d=\"M5 13l4 4L19 7\" />\n </svg>\n <!-- Save icon -->\n <svg v-else class=\"mint-experiment-popover__save-trigger-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4\" />\n </svg>\n </button>\n </div>\n\n <!-- Popover panel -->\n <div v-if=\"isOpen\" class=\"mint-experiment-popover__panel\">\n <!-- Header -->\n <div class=\"mint-experiment-popover__header\">\n <div class=\"mint-experiment-popover__title\">Experiment</div>\n <div class=\"mint-experiment-popover__subtitle\">\n {{ experimentName ? 'Linked experiment context' : 'Link to an MINT experiment' }}\n </div>\n </div>\n\n <!-- No experiment selected -->\n <div v-if=\"!experimentName\" class=\"mint-experiment-popover__body\">\n <button type=\"button\" class=\"mint-experiment-popover__select-btn\" @click=\"handleSelect\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n Select Experiment\n </button>\n </div>\n\n <!-- Experiment selected -->\n <div v-else class=\"mint-experiment-popover__body\">\n <div class=\"mint-experiment-popover__card\">\n <div class=\"mint-experiment-popover__card-icon\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n </div>\n <div class=\"mint-experiment-popover__card-info\">\n <span v-if=\"experimentCode\" class=\"mint-experiment-popover__card-code\">{{ experimentCode }}</span>\n <div class=\"mint-experiment-popover__card-name\">{{ experimentName }}</div>\n <div v-if=\"experimentStatus\" class=\"mint-experiment-popover__card-status\">\n {{ formatExperimentStatus(experimentStatus) }}\n </div>\n </div>\n </div>\n <div class=\"mint-experiment-popover__card-actions\">\n <button type=\"button\" class=\"mint-experiment-popover__change-btn\" @click=\"handleSelect\">\n Change\n </button>\n <button v-if=\"showDetach\" type=\"button\" class=\"mint-experiment-popover__detach-btn\" @click=\"handleDetach\">\n Detach\n </button>\n </div>\n </div>\n </div>\n\n <!-- Save confirmation dialog -->\n <ConfirmDialog\n v-model=\"showConfirm\"\n :title=\"confirmTitle ?? 'Save to Experiment'\"\n :message=\"confirmMessage ?? `Save current data to ${experimentName}?`\"\n variant=\"info\"\n confirm-label=\"Save\"\n :loading=\"saveLoading\"\n @confirm=\"handleConfirmSave\"\n />\n </div>\n</template>\n\n<style>\n@import '../styles/components/experiment-popover.css';\n</style>\n","<script setup lang=\"ts\">\n/** Floating popover showing the active experiment with save, detach, and select actions. */\nimport { ref, watch, onUnmounted } from 'vue'\nimport { useDropdownState } from '../composables/useDropdownState'\nimport { formatExperimentStatus } from '../composables/experiment-utils'\nimport ConfirmDialog from './ConfirmDialog.vue'\n\ninterface Props {\n experimentName?: string\n experimentCode?: string\n experimentStatus?: string\n showSave?: boolean\n showDetach?: boolean\n saveDisabled?: boolean\n saveLoading?: boolean\n saveSuccessMessage?: string\n saveDisabledMessage?: string\n confirmSave?: boolean\n confirmTitle?: string\n confirmMessage?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n showSave: false,\n showDetach: false,\n saveDisabled: false,\n saveLoading: false,\n confirmSave: true,\n})\n\nconst emit = defineEmits<{\n select: []\n save: []\n detach: []\n}>()\n\nconst { isOpen, rootRef: popoverRef, close, toggle } = useDropdownState({\n closeOnEscape: false,\n})\nconst showSuccess = ref(false)\nconst showConfirm = ref(false)\n\nfunction handleSelect() {\n emit('select')\n close()\n}\n\nfunction handleSave() {\n if (props.saveDisabled || props.saveLoading) return\n if (props.confirmSave) {\n showConfirm.value = true\n } else {\n emit('save')\n }\n}\n\nfunction handleConfirmSave() {\n showConfirm.value = false\n emit('save')\n}\n\nfunction handleDetach() {\n emit('detach')\n close()\n}\n\nlet successTimer: ReturnType<typeof setTimeout> | null = null\n\n// Show success state when saveSuccessMessage changes from empty to a value\nwatch(() => props.saveSuccessMessage, (msg) => {\n if (successTimer) clearTimeout(successTimer)\n if (msg) {\n showSuccess.value = true\n successTimer = setTimeout(() => {\n showSuccess.value = false\n successTimer = null\n }, 3000)\n }\n})\n\nonUnmounted(() => {\n if (successTimer) clearTimeout(successTimer)\n})\n</script>\n\n<template>\n <div ref=\"popoverRef\" class=\"mint-experiment-popover\">\n <!-- Split trigger: experiment pill + inline save -->\n <div\n :class=\"[\n 'mint-experiment-popover__split',\n { 'mint-experiment-popover__split--with-save': showSave && experimentName },\n ]\"\n >\n <!-- Left: experiment trigger (opens popover) — shows only the code for\n maximum topbar compactness; full name + status live in the panel. -->\n <button\n type=\"button\"\n :class=\"[\n 'mint-experiment-popover__trigger',\n { 'mint-experiment-popover__trigger--active': isOpen },\n { 'mint-experiment-popover__trigger--empty': !experimentCode && !experimentName },\n ]\"\n :title=\"experimentName || undefined\"\n @click.stop=\"toggle\"\n >\n <!-- Flask icon -->\n <svg class=\"mint-experiment-popover__trigger-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n <!-- Code preferred, name as fallback, \"No experiment\" as empty state -->\n <span v-if=\"experimentCode\" class=\"mint-experiment-popover__trigger-code\">{{ experimentCode }}</span>\n <span v-else-if=\"experimentName\" class=\"mint-experiment-popover__trigger-text\">{{ experimentName }}</span>\n <span v-else class=\"mint-experiment-popover__trigger-text\">No experiment</span>\n <!-- Chevron -->\n <svg class=\"mint-experiment-popover__trigger-chevron\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </button>\n\n <!-- Right: inline save button (direct action) -->\n <button\n v-if=\"showSave && experimentName\"\n type=\"button\"\n :class=\"[\n 'mint-experiment-popover__save-trigger',\n { 'mint-experiment-popover__save-trigger--loading': saveLoading },\n { 'mint-experiment-popover__save-trigger--success': showSuccess },\n { 'mint-experiment-popover__save-trigger--disabled': saveDisabled && !showSuccess },\n ]\"\n :disabled=\"saveDisabled && !showSuccess\"\n :title=\"saveDisabled && saveDisabledMessage ? saveDisabledMessage : showSuccess && saveSuccessMessage ? saveSuccessMessage : 'Save to Experiment'\"\n @click.stop=\"handleSave\"\n >\n <!-- Loading spinner -->\n <span v-if=\"saveLoading\" class=\"mint-experiment-popover__spinner--inline\" />\n <!-- Success check -->\n <svg v-else-if=\"showSuccess\" class=\"mint-experiment-popover__save-trigger-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2.5\" d=\"M5 13l4 4L19 7\" />\n </svg>\n <!-- Save icon -->\n <svg v-else class=\"mint-experiment-popover__save-trigger-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4\" />\n </svg>\n </button>\n </div>\n\n <!-- Popover panel -->\n <div v-if=\"isOpen\" class=\"mint-experiment-popover__panel\">\n <!-- Header -->\n <div class=\"mint-experiment-popover__header\">\n <div class=\"mint-experiment-popover__title\">Experiment</div>\n <div class=\"mint-experiment-popover__subtitle\">\n {{ experimentName ? 'Linked experiment context' : 'Link to an MINT experiment' }}\n </div>\n </div>\n\n <!-- No experiment selected -->\n <div v-if=\"!experimentName\" class=\"mint-experiment-popover__body\">\n <button type=\"button\" class=\"mint-experiment-popover__select-btn\" @click=\"handleSelect\">\n <svg width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\" />\n </svg>\n Select Experiment\n </button>\n </div>\n\n <!-- Experiment selected -->\n <div v-else class=\"mint-experiment-popover__body\">\n <div class=\"mint-experiment-popover__card\">\n <div class=\"mint-experiment-popover__card-icon\">\n <svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.75\"\n d=\"M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z\"\n />\n </svg>\n </div>\n <div class=\"mint-experiment-popover__card-info\">\n <span v-if=\"experimentCode\" class=\"mint-experiment-popover__card-code\">{{ experimentCode }}</span>\n <div class=\"mint-experiment-popover__card-name\">{{ experimentName }}</div>\n <div v-if=\"experimentStatus\" class=\"mint-experiment-popover__card-status\">\n {{ formatExperimentStatus(experimentStatus) }}\n </div>\n </div>\n </div>\n <div class=\"mint-experiment-popover__card-actions\">\n <button type=\"button\" class=\"mint-experiment-popover__change-btn\" @click=\"handleSelect\">\n Change\n </button>\n <button v-if=\"showDetach\" type=\"button\" class=\"mint-experiment-popover__detach-btn\" @click=\"handleDetach\">\n Detach\n </button>\n </div>\n </div>\n </div>\n\n <!-- Save confirmation dialog -->\n <ConfirmDialog\n v-model=\"showConfirm\"\n :title=\"confirmTitle ?? 'Save to Experiment'\"\n :message=\"confirmMessage ?? `Save current data to ${experimentName}?`\"\n variant=\"info\"\n confirm-label=\"Save\"\n :loading=\"saveLoading\"\n @confirm=\"handleConfirmSave\"\n />\n </div>\n</template>\n\n<style>\n@import '../styles/components/experiment-popover.css';\n</style>\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ExperimentSelectorModal-K_IhFb0-.js","names":[],"sources":["../src/components/BaseButton.vue","../src/components/BaseButton.vue","../src/components/BasePill.vue","../src/components/BasePill.vue","../src/components/Skeleton.vue","../src/components/Skeleton.vue","../src/components/EmptyState.vue","../src/components/EmptyState.vue","../src/components/ExperimentCodeBadge.vue","../src/components/ExperimentCodeBadge.vue","../src/components/ExperimentSelectorModal.vue","../src/components/ExperimentSelectorModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/** Renders a styled `<button>` with variant, size, loading spinner, and full-width options. */\nimport type { ButtonVariant, ButtonSize } from '../types'\n\ninterface Props {\n variant?: ButtonVariant\n size?: ButtonSize\n disabled?: boolean\n loading?: boolean\n type?: 'button' | 'submit' | 'reset'\n fullWidth?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'primary',\n size: 'md',\n disabled: false,\n loading: false,\n type: 'button',\n fullWidth: false,\n})\n\nconst emit = defineEmits<{\n click: [event: MouseEvent]\n}>()\n\nfunction handleClick(event: MouseEvent) {\n if (!props.disabled && !props.loading) {\n emit('click', event)\n }\n}\n</script>\n\n<template>\n <button\n :type=\"type\"\n :disabled=\"disabled || loading\"\n :class=\"[\n 'mint-button',\n `mint-button--${variant}`,\n `mint-button--${size}`,\n fullWidth ? 'mint-button--full-width' : '',\n (disabled || loading) ? 'mint-button--disabled' : '',\n ]\"\n @click=\"handleClick\"\n >\n <svg\n v-if=\"loading\"\n class=\"mint-button__spinner\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n style=\"opacity: 0.25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n />\n <path\n style=\"opacity: 0.75\"\n fill=\"currentColor\"\n 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\"\n />\n </svg>\n <slot />\n </button>\n</template>\n\n<style>\n@import '../styles/components/button.css';\n</style>\n","<script setup lang=\"ts\">\n/** Renders a styled `<button>` with variant, size, loading spinner, and full-width options. */\nimport type { ButtonVariant, ButtonSize } from '../types'\n\ninterface Props {\n variant?: ButtonVariant\n size?: ButtonSize\n disabled?: boolean\n loading?: boolean\n type?: 'button' | 'submit' | 'reset'\n fullWidth?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n variant: 'primary',\n size: 'md',\n disabled: false,\n loading: false,\n type: 'button',\n fullWidth: false,\n})\n\nconst emit = defineEmits<{\n click: [event: MouseEvent]\n}>()\n\nfunction handleClick(event: MouseEvent) {\n if (!props.disabled && !props.loading) {\n emit('click', event)\n }\n}\n</script>\n\n<template>\n <button\n :type=\"type\"\n :disabled=\"disabled || loading\"\n :class=\"[\n 'mint-button',\n `mint-button--${variant}`,\n `mint-button--${size}`,\n fullWidth ? 'mint-button--full-width' : '',\n (disabled || loading) ? 'mint-button--disabled' : '',\n ]\"\n @click=\"handleClick\"\n >\n <svg\n v-if=\"loading\"\n class=\"mint-button__spinner\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n style=\"opacity: 0.25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n />\n <path\n style=\"opacity: 0.75\"\n fill=\"currentColor\"\n 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\"\n />\n </svg>\n <slot />\n </button>\n</template>\n\n<style>\n@import '../styles/components/button.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","<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","<script setup lang=\"ts\">\n/** Inline badge that displays an experiment code and copies it to the clipboard on click. */\nimport { ref } from 'vue'\n\ninterface Props {\n code: string\n size?: 'sm' | 'md' | 'lg'\n copyable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n copyable: true,\n})\n\nconst emit = defineEmits<{\n copy: [code: string]\n}>()\n\nconst copied = ref(false)\nlet copyTimeout: ReturnType<typeof setTimeout> | null = null\n\nasync function handleCopy() {\n if (!props.copyable) return\n try {\n await navigator.clipboard.writeText(props.code)\n copied.value = true\n emit('copy', props.code)\n if (copyTimeout) clearTimeout(copyTimeout)\n copyTimeout = setTimeout(() => { copied.value = false }, 1500)\n } catch {\n // Clipboard API not available (e.g. insecure context)\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!props.copyable) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleCopy()\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mint-exp-code',\n `mint-exp-code--${size}`,\n { 'mint-exp-code--copyable': copyable, 'mint-exp-code--copied': copied },\n ]\"\n :role=\"copyable ? 'button' : undefined\"\n :tabindex=\"copyable ? 0 : undefined\"\n :title=\"copyable ? (copied ? 'Copied!' : 'Click to copy') : undefined\"\n @click=\"handleCopy\"\n @keydown=\"handleKeydown\"\n >\n {{ copied ? 'Copied!' : code }}\n </span>\n</template>\n\n<style>\n@import '../styles/components/experiment-code-badge.css';\n</style>\n","<script setup lang=\"ts\">\n/** Inline badge that displays an experiment code and copies it to the clipboard on click. */\nimport { ref } from 'vue'\n\ninterface Props {\n code: string\n size?: 'sm' | 'md' | 'lg'\n copyable?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n copyable: true,\n})\n\nconst emit = defineEmits<{\n copy: [code: string]\n}>()\n\nconst copied = ref(false)\nlet copyTimeout: ReturnType<typeof setTimeout> | null = null\n\nasync function handleCopy() {\n if (!props.copyable) return\n try {\n await navigator.clipboard.writeText(props.code)\n copied.value = true\n emit('copy', props.code)\n if (copyTimeout) clearTimeout(copyTimeout)\n copyTimeout = setTimeout(() => { copied.value = false }, 1500)\n } catch {\n // Clipboard API not available (e.g. insecure context)\n }\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!props.copyable) return\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n handleCopy()\n }\n}\n</script>\n\n<template>\n <span\n :class=\"[\n 'mint-exp-code',\n `mint-exp-code--${size}`,\n { 'mint-exp-code--copyable': copyable, 'mint-exp-code--copied': copied },\n ]\"\n :role=\"copyable ? 'button' : undefined\"\n :tabindex=\"copyable ? 0 : undefined\"\n :title=\"copyable ? (copied ? 'Copied!' : 'Click to copy') : undefined\"\n @click=\"handleCopy\"\n @keydown=\"handleKeydown\"\n >\n {{ copied ? 'Copied!' : code }}\n </span>\n</template>\n\n<style>\n@import '../styles/components/experiment-code-badge.css';\n</style>\n","<script setup lang=\"ts\">\n/** Modal for searching and selecting an experiment from the platform list with filters and keyboard nav. */\nimport { ref, reactive, computed, watch, nextTick } from 'vue'\nimport type { ModalSize, ExperimentSummary, ExperimentFilters } from '../types'\nimport { useExperimentSelector } from '../composables/useExperimentSelector'\nimport {\n formatExperimentDate,\n formatExperimentStatus,\n getExperimentStatusVariant,\n EXPERIMENT_STATUS_OPTIONS,\n DATE_PRESET_OPTIONS,\n SORT_OPTIONS,\n} from '../composables/experiment-utils'\nimport BaseModal from './BaseModal.vue'\nimport BaseInput from './BaseInput.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport BasePill from './BasePill.vue'\nimport Skeleton from './Skeleton.vue'\nimport EmptyState from './EmptyState.vue'\nimport ExperimentCodeBadge from './ExperimentCodeBadge.vue'\n\ninterface Props {\n modelValue: boolean\n experimentType?: string\n currentExperimentId?: number | null\n title?: string\n size?: ModalSize\n groupByProject?: boolean\n showFilters?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n currentExperimentId: null,\n title: 'Select Experiment',\n size: 'full',\n groupByProject: false,\n showFilters: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n select: [experiment: ExperimentSummary]\n deselect: []\n}>()\n\nconst {\n experiments,\n filters,\n isLoading,\n error,\n sortKey,\n experimentTypes,\n projects,\n groupedByProject,\n fetch: fetchExperiments,\n fetchFilterOptions,\n} = useExperimentSelector({\n experimentType: props.experimentType,\n})\n\nconst activeIndex = ref(-1)\nconst listRef = ref<HTMLElement | null>(null)\nconst showAdvanced = ref(props.showFilters)\nconst groupToggle = ref(props.groupByProject)\n\n// Track whether any advanced filter is active (for badge dot)\nconst hasActiveAdvancedFilters = computed(() =>\n !!(filters.project || filters.experimentType || filters.datePreset || sortKey.value !== 'created_at:desc'),\n)\n\n// Build type filter options from fetched experiment types\nconst typeFilterOptions = computed(() => [\n { value: '', label: 'All types' },\n ...experimentTypes.value.map(t => ({ value: t.value, label: t.label })),\n])\nconst canChooseExperimentType = computed(() =>\n !props.experimentType && typeFilterOptions.value.length > 2,\n)\n\n// Build project filter options from fetched projects\nconst projectFilterOptions = computed(() => [\n { value: '', label: 'All projects' },\n ...projects.value,\n])\n\n// Flat list of experiments for keyboard navigation (works across groups too)\nconst flatExperiments = computed(() => {\n if (!groupToggle.value) return experiments.value\n return groupedByProject.value.flatMap(([, exps]) => exps)\n})\n\nfunction setFilter<K extends keyof ExperimentFilters>(key: K, value: string | number) {\n ;(filters as Record<string, unknown>)[key] = String(value) || undefined\n}\n\nfunction handleSortChange(value: string | number) {\n sortKey.value = String(value) || 'created_at:desc'\n}\n\nfunction handleSelect(experiment: ExperimentSummary) {\n emit('select', experiment)\n emit('update:modelValue', false)\n}\n\nfunction handleDeselect() {\n emit('deselect')\n emit('update:modelValue', false)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n const list = flatExperiments.value\n if (!list.length) return\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n activeIndex.value = Math.min(activeIndex.value + 1, list.length - 1)\n scrollActiveIntoView()\n break\n case 'ArrowUp':\n event.preventDefault()\n activeIndex.value = Math.max(activeIndex.value - 1, 0)\n scrollActiveIntoView()\n break\n case 'Enter':\n event.preventDefault()\n if (activeIndex.value >= 0 && activeIndex.value < list.length) {\n handleSelect(list[activeIndex.value])\n }\n break\n }\n}\n\nfunction scrollActiveIntoView() {\n nextTick(() => {\n const row = listRef.value?.querySelector('.mint-experiment-selector__row--focused')\n row?.scrollIntoView({ block: 'nearest' })\n })\n}\n\n// Precomputed id → flat index for O(1) lookup in grouped mode\nconst flatIndexMap = computed(() => {\n const map = new Map<number, number>()\n flatExperiments.value.forEach((exp, i) => map.set(exp.id, i))\n return map\n})\n\nfunction getFlatIndex(experiment: ExperimentSummary): number {\n return flatIndexMap.value.get(experiment.id) ?? -1\n}\n\n// Track collapsed groups (reactive Set tracks .add/.delete/.has automatically)\nconst collapsedGroups = reactive(new Set<string>())\n\nfunction toggleGroup(groupName: string) {\n if (collapsedGroups.has(groupName)) {\n collapsedGroups.delete(groupName)\n } else {\n collapsedGroups.add(groupName)\n }\n}\n\n// Reset active index when experiments change\nwatch(experiments, () => { activeIndex.value = -1 })\n\n// Fetch on open\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (isOpen) {\n activeIndex.value = -1\n collapsedGroups.clear()\n fetchFilterOptions()\n fetchExperiments()\n }\n },\n)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :size=\"size\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mint-experiment-selector\" @keydown=\"handleKeydown\">\n <!-- Filter bar row 1 -->\n <div class=\"mint-experiment-selector__filters-row\">\n <div class=\"mint-experiment-selector__search\">\n <BaseInput\n v-model=\"filters.search\"\n placeholder=\"Search experiments...\"\n size=\"sm\"\n type=\"search\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.status ?? ''\"\n :options=\"EXPERIMENT_STATUS_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('status', v)\"\n />\n </div>\n <div v-if=\"canChooseExperimentType\" class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.experimentType ?? ''\"\n :options=\"typeFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('experimentType', v)\"\n />\n </div>\n <button\n class=\"mint-experiment-selector__filters-toggle\"\n :class=\"{ 'mint-experiment-selector__filters-toggle--active': hasActiveAdvancedFilters }\"\n type=\"button\"\n @click=\"showAdvanced = !showAdvanced\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\" /><line x1=\"8\" y1=\"12\" x2=\"20\" y2=\"12\" /><line x1=\"12\" y1=\"18\" x2=\"20\" y2=\"18\" />\n <circle cx=\"6\" cy=\"12\" r=\"2\" /><circle cx=\"10\" cy=\"18\" r=\"2\" /><circle cx=\"6\" cy=\"6\" r=\"2\" />\n </svg>\n Filters\n <span v-if=\"hasActiveAdvancedFilters\" class=\"mint-experiment-selector__filters-dot\" />\n </button>\n </div>\n\n <!-- Filter bar row 2 (advanced, collapsible) -->\n <div v-if=\"showAdvanced\" class=\"mint-experiment-selector__filters-advanced\">\n <div v-if=\"projectFilterOptions.length > 1\" class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.project ?? ''\"\n :options=\"projectFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('project', v)\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.datePreset ?? ''\"\n :options=\"DATE_PRESET_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('datePreset', v)\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"sortKey\"\n :options=\"SORT_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"handleSortChange\"\n />\n </div>\n <label class=\"mint-experiment-selector__group-toggle\">\n <input\n v-model=\"groupToggle\"\n type=\"checkbox\"\n class=\"mint-experiment-selector__group-checkbox\"\n />\n Group by project\n </label>\n </div>\n\n <!-- Loading skeleton -->\n <div v-if=\"isLoading\" class=\"mint-experiment-selector__skeleton\">\n <div v-for=\"n in 4\" :key=\"n\" class=\"mint-experiment-selector__skeleton-row\">\n <div class=\"mint-experiment-selector__skeleton-content\">\n <Skeleton :width=\"120 + n * 20\" height=\"14px\" />\n <Skeleton width=\"80px\" height=\"10px\" />\n </div>\n <Skeleton width=\"60px\" height=\"20px\" variant=\"rounded\" />\n </div>\n </div>\n\n <!-- Error -->\n <div v-else-if=\"error\" class=\"mint-experiment-selector__error\">\n {{ error }}\n </div>\n\n <!-- Empty -->\n <EmptyState\n v-else-if=\"experiments.length === 0\"\n title=\"No experiments found\"\n description=\"Try adjusting your search or filters.\"\n size=\"sm\"\n />\n\n <!-- Experiment list: grouped mode -->\n <div v-else-if=\"groupToggle\" ref=\"listRef\" class=\"mint-experiment-selector__list\">\n <template v-for=\"([groupName, groupExps]) in groupedByProject\" :key=\"groupName\">\n <button\n type=\"button\"\n class=\"mint-experiment-selector__group-header\"\n @click=\"toggleGroup(groupName)\"\n >\n <svg\n class=\"mint-experiment-selector__group-chevron\"\n :class=\"{ 'mint-experiment-selector__group-chevron--collapsed': collapsedGroups.has(groupName) }\"\n width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n <span class=\"mint-experiment-selector__group-name\">{{ groupName }}</span>\n <span class=\"mint-experiment-selector__group-count\">{{ groupExps.length }}</span>\n </button>\n <template v-if=\"!collapsedGroups.has(groupName)\">\n <div\n v-for=\"exp in groupExps\"\n :key=\"exp.id\"\n class=\"mint-experiment-selector__row\"\n :class=\"{\n 'mint-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mint-experiment-selector__row--focused': getFlatIndex(exp) === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = getFlatIndex(exp)\"\n >\n <div class=\"mint-experiment-selector__row-content\">\n <div class=\"mint-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mint-experiment-selector__meta\">\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"getExperimentStatusVariant(exp.status)\" size=\"sm\">\n {{ formatExperimentStatus(exp.status) }}\n </BasePill>\n </div>\n </template>\n </template>\n </div>\n\n <!-- Experiment list: flat mode -->\n <div v-else ref=\"listRef\" class=\"mint-experiment-selector__list\">\n <div\n v-for=\"(exp, idx) in experiments\"\n :key=\"exp.id\"\n class=\"mint-experiment-selector__row\"\n :class=\"{\n 'mint-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mint-experiment-selector__row--focused': idx === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <div class=\"mint-experiment-selector__row-content\">\n <div class=\"mint-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mint-experiment-selector__meta\">\n <span v-if=\"exp.project_name || exp.project\">{{ exp.project_name || exp.project }}</span>\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"getExperimentStatusVariant(exp.status)\" size=\"sm\">\n {{ formatExperimentStatus(exp.status) }}\n </BasePill>\n </div>\n </div>\n\n <!-- Footer: clear selection -->\n <div v-if=\"currentExperimentId != null\" class=\"mint-experiment-selector__footer\">\n <button\n type=\"button\"\n class=\"mint-experiment-selector__clear-btn\"\n @click=\"handleDeselect\"\n >\n Clear selection\n </button>\n </div>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/experiment-selector-modal.css';\n</style>\n","<script setup lang=\"ts\">\n/** Modal for searching and selecting an experiment from the platform list with filters and keyboard nav. */\nimport { ref, reactive, computed, watch, nextTick } from 'vue'\nimport type { ModalSize, ExperimentSummary, ExperimentFilters } from '../types'\nimport { useExperimentSelector } from '../composables/useExperimentSelector'\nimport {\n formatExperimentDate,\n formatExperimentStatus,\n getExperimentStatusVariant,\n EXPERIMENT_STATUS_OPTIONS,\n DATE_PRESET_OPTIONS,\n SORT_OPTIONS,\n} from '../composables/experiment-utils'\nimport BaseModal from './BaseModal.vue'\nimport BaseInput from './BaseInput.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport BasePill from './BasePill.vue'\nimport Skeleton from './Skeleton.vue'\nimport EmptyState from './EmptyState.vue'\nimport ExperimentCodeBadge from './ExperimentCodeBadge.vue'\n\ninterface Props {\n modelValue: boolean\n experimentType?: string\n currentExperimentId?: number | null\n title?: string\n size?: ModalSize\n groupByProject?: boolean\n showFilters?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n currentExperimentId: null,\n title: 'Select Experiment',\n size: 'full',\n groupByProject: false,\n showFilters: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n select: [experiment: ExperimentSummary]\n deselect: []\n}>()\n\nconst {\n experiments,\n filters,\n isLoading,\n error,\n sortKey,\n experimentTypes,\n projects,\n groupedByProject,\n fetch: fetchExperiments,\n fetchFilterOptions,\n} = useExperimentSelector({\n experimentType: props.experimentType,\n})\n\nconst activeIndex = ref(-1)\nconst listRef = ref<HTMLElement | null>(null)\nconst showAdvanced = ref(props.showFilters)\nconst groupToggle = ref(props.groupByProject)\n\n// Track whether any advanced filter is active (for badge dot)\nconst hasActiveAdvancedFilters = computed(() =>\n !!(filters.project || filters.experimentType || filters.datePreset || sortKey.value !== 'created_at:desc'),\n)\n\n// Build type filter options from fetched experiment types\nconst typeFilterOptions = computed(() => [\n { value: '', label: 'All types' },\n ...experimentTypes.value.map(t => ({ value: t.value, label: t.label })),\n])\nconst canChooseExperimentType = computed(() =>\n !props.experimentType && typeFilterOptions.value.length > 2,\n)\n\n// Build project filter options from fetched projects\nconst projectFilterOptions = computed(() => [\n { value: '', label: 'All projects' },\n ...projects.value,\n])\n\n// Flat list of experiments for keyboard navigation (works across groups too)\nconst flatExperiments = computed(() => {\n if (!groupToggle.value) return experiments.value\n return groupedByProject.value.flatMap(([, exps]) => exps)\n})\n\nfunction setFilter<K extends keyof ExperimentFilters>(key: K, value: string | number) {\n ;(filters as Record<string, unknown>)[key] = String(value) || undefined\n}\n\nfunction handleSortChange(value: string | number) {\n sortKey.value = String(value) || 'created_at:desc'\n}\n\nfunction handleSelect(experiment: ExperimentSummary) {\n emit('select', experiment)\n emit('update:modelValue', false)\n}\n\nfunction handleDeselect() {\n emit('deselect')\n emit('update:modelValue', false)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n const list = flatExperiments.value\n if (!list.length) return\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n activeIndex.value = Math.min(activeIndex.value + 1, list.length - 1)\n scrollActiveIntoView()\n break\n case 'ArrowUp':\n event.preventDefault()\n activeIndex.value = Math.max(activeIndex.value - 1, 0)\n scrollActiveIntoView()\n break\n case 'Enter':\n event.preventDefault()\n if (activeIndex.value >= 0 && activeIndex.value < list.length) {\n handleSelect(list[activeIndex.value])\n }\n break\n }\n}\n\nfunction scrollActiveIntoView() {\n nextTick(() => {\n const row = listRef.value?.querySelector('.mint-experiment-selector__row--focused')\n row?.scrollIntoView({ block: 'nearest' })\n })\n}\n\n// Precomputed id → flat index for O(1) lookup in grouped mode\nconst flatIndexMap = computed(() => {\n const map = new Map<number, number>()\n flatExperiments.value.forEach((exp, i) => map.set(exp.id, i))\n return map\n})\n\nfunction getFlatIndex(experiment: ExperimentSummary): number {\n return flatIndexMap.value.get(experiment.id) ?? -1\n}\n\n// Track collapsed groups (reactive Set tracks .add/.delete/.has automatically)\nconst collapsedGroups = reactive(new Set<string>())\n\nfunction toggleGroup(groupName: string) {\n if (collapsedGroups.has(groupName)) {\n collapsedGroups.delete(groupName)\n } else {\n collapsedGroups.add(groupName)\n }\n}\n\n// Reset active index when experiments change\nwatch(experiments, () => { activeIndex.value = -1 })\n\n// Fetch on open\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (isOpen) {\n activeIndex.value = -1\n collapsedGroups.clear()\n fetchFilterOptions()\n fetchExperiments()\n }\n },\n)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :size=\"size\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mint-experiment-selector\" @keydown=\"handleKeydown\">\n <!-- Filter bar row 1 -->\n <div class=\"mint-experiment-selector__filters-row\">\n <div class=\"mint-experiment-selector__search\">\n <BaseInput\n v-model=\"filters.search\"\n placeholder=\"Search experiments...\"\n size=\"sm\"\n type=\"search\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.status ?? ''\"\n :options=\"EXPERIMENT_STATUS_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('status', v)\"\n />\n </div>\n <div v-if=\"canChooseExperimentType\" class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.experimentType ?? ''\"\n :options=\"typeFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('experimentType', v)\"\n />\n </div>\n <button\n class=\"mint-experiment-selector__filters-toggle\"\n :class=\"{ 'mint-experiment-selector__filters-toggle--active': hasActiveAdvancedFilters }\"\n type=\"button\"\n @click=\"showAdvanced = !showAdvanced\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\" /><line x1=\"8\" y1=\"12\" x2=\"20\" y2=\"12\" /><line x1=\"12\" y1=\"18\" x2=\"20\" y2=\"18\" />\n <circle cx=\"6\" cy=\"12\" r=\"2\" /><circle cx=\"10\" cy=\"18\" r=\"2\" /><circle cx=\"6\" cy=\"6\" r=\"2\" />\n </svg>\n Filters\n <span v-if=\"hasActiveAdvancedFilters\" class=\"mint-experiment-selector__filters-dot\" />\n </button>\n </div>\n\n <!-- Filter bar row 2 (advanced, collapsible) -->\n <div v-if=\"showAdvanced\" class=\"mint-experiment-selector__filters-advanced\">\n <div v-if=\"projectFilterOptions.length > 1\" class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.project ?? ''\"\n :options=\"projectFilterOptions\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('project', v)\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"filters.datePreset ?? ''\"\n :options=\"DATE_PRESET_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"v => setFilter('datePreset', v)\"\n />\n </div>\n <div class=\"mint-experiment-selector__filter-select\">\n <BaseSelect\n :model-value=\"sortKey\"\n :options=\"SORT_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"handleSortChange\"\n />\n </div>\n <label class=\"mint-experiment-selector__group-toggle\">\n <input\n v-model=\"groupToggle\"\n type=\"checkbox\"\n class=\"mint-experiment-selector__group-checkbox\"\n />\n Group by project\n </label>\n </div>\n\n <!-- Loading skeleton -->\n <div v-if=\"isLoading\" class=\"mint-experiment-selector__skeleton\">\n <div v-for=\"n in 4\" :key=\"n\" class=\"mint-experiment-selector__skeleton-row\">\n <div class=\"mint-experiment-selector__skeleton-content\">\n <Skeleton :width=\"120 + n * 20\" height=\"14px\" />\n <Skeleton width=\"80px\" height=\"10px\" />\n </div>\n <Skeleton width=\"60px\" height=\"20px\" variant=\"rounded\" />\n </div>\n </div>\n\n <!-- Error -->\n <div v-else-if=\"error\" class=\"mint-experiment-selector__error\">\n {{ error }}\n </div>\n\n <!-- Empty -->\n <EmptyState\n v-else-if=\"experiments.length === 0\"\n title=\"No experiments found\"\n description=\"Try adjusting your search or filters.\"\n size=\"sm\"\n />\n\n <!-- Experiment list: grouped mode -->\n <div v-else-if=\"groupToggle\" ref=\"listRef\" class=\"mint-experiment-selector__list\">\n <template v-for=\"([groupName, groupExps]) in groupedByProject\" :key=\"groupName\">\n <button\n type=\"button\"\n class=\"mint-experiment-selector__group-header\"\n @click=\"toggleGroup(groupName)\"\n >\n <svg\n class=\"mint-experiment-selector__group-chevron\"\n :class=\"{ 'mint-experiment-selector__group-chevron--collapsed': collapsedGroups.has(groupName) }\"\n width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\n >\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n <span class=\"mint-experiment-selector__group-name\">{{ groupName }}</span>\n <span class=\"mint-experiment-selector__group-count\">{{ groupExps.length }}</span>\n </button>\n <template v-if=\"!collapsedGroups.has(groupName)\">\n <div\n v-for=\"exp in groupExps\"\n :key=\"exp.id\"\n class=\"mint-experiment-selector__row\"\n :class=\"{\n 'mint-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mint-experiment-selector__row--focused': getFlatIndex(exp) === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = getFlatIndex(exp)\"\n >\n <div class=\"mint-experiment-selector__row-content\">\n <div class=\"mint-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mint-experiment-selector__meta\">\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"getExperimentStatusVariant(exp.status)\" size=\"sm\">\n {{ formatExperimentStatus(exp.status) }}\n </BasePill>\n </div>\n </template>\n </template>\n </div>\n\n <!-- Experiment list: flat mode -->\n <div v-else ref=\"listRef\" class=\"mint-experiment-selector__list\">\n <div\n v-for=\"(exp, idx) in experiments\"\n :key=\"exp.id\"\n class=\"mint-experiment-selector__row\"\n :class=\"{\n 'mint-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mint-experiment-selector__row--focused': idx === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <div class=\"mint-experiment-selector__row-content\">\n <div class=\"mint-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mint-experiment-selector__meta\">\n <span v-if=\"exp.project_name || exp.project\">{{ exp.project_name || exp.project }}</span>\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"getExperimentStatusVariant(exp.status)\" size=\"sm\">\n {{ formatExperimentStatus(exp.status) }}\n </BasePill>\n </div>\n </div>\n\n <!-- Footer: clear selection -->\n <div v-if=\"currentExperimentId != null\" class=\"mint-experiment-selector__footer\">\n <button\n type=\"button\"\n class=\"mint-experiment-selector__clear-btn\"\n @click=\"handleDeselect\"\n >\n Clear selection\n </button>\n </div>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/experiment-selector-modal.css';\n</style>\n"],"mappings":"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"}