@morscherlab/mint-sdk 1.0.12 → 1.0.14

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 (63) 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-DeBE0YKT.js → ExperimentSelectorModal-CHsU-LIh.js} +3 -3
  5. package/dist/{ExperimentSelectorModal-DeBE0YKT.js.map → ExperimentSelectorModal-CHsU-LIh.js.map} +1 -1
  6. package/dist/{ExperimentSelectorModal-BXf-XnQw.js → ExperimentSelectorModal-DIFyL5ta.js} +1 -1
  7. package/dist/__tests__/composables/useCommandHistory.test.d.ts +1 -0
  8. package/dist/__tests__/composables/useFileImport.test.d.ts +1 -0
  9. package/dist/__tests__/composables/useOptimisticMutation.test.d.ts +1 -0
  10. package/dist/__tests__/composables/useResourceCrud.test.d.ts +1 -0
  11. package/dist/__tests__/composables/useWellPainting.test.d.ts +1 -0
  12. package/dist/__tests__/composables/useWellPlateAdapter.test.d.ts +1 -0
  13. package/dist/__tests__/composables/useWellPlateValidation.test.d.ts +1 -0
  14. package/dist/components/index.js +3 -3
  15. package/dist/{components-CqdBz8DI.js → components-Dq02EVZH.js} +10 -10
  16. package/dist/components-Dq02EVZH.js.map +1 -0
  17. package/dist/composables/index.d.ts +7 -0
  18. package/dist/composables/index.js +5 -5
  19. package/dist/composables/useCommandHistory.d.ts +29 -0
  20. package/dist/composables/useFileImport.d.ts +35 -0
  21. package/dist/composables/useOptimisticMutation.d.ts +28 -0
  22. package/dist/composables/useResourceCrud.d.ts +40 -0
  23. package/dist/composables/useWellPainting.d.ts +35 -0
  24. package/dist/composables/useWellPlateAdapter.d.ts +36 -0
  25. package/dist/composables/useWellPlateValidation.d.ts +27 -0
  26. package/dist/{composables-BWh0MpcK.js → composables-D9mexHSW.js} +668 -5
  27. package/dist/composables-D9mexHSW.js.map +1 -0
  28. package/dist/{experiment-utils-hGXMHlAc.js → experiment-utils-D11yT3AR.js} +1 -2
  29. package/dist/{experiment-utils-hGXMHlAc.js.map → experiment-utils-D11yT3AR.js.map} +1 -1
  30. package/dist/index.js +8 -8
  31. package/dist/install.js +3 -3
  32. package/dist/styles.css +4 -8
  33. package/dist/{useExperimentSelector-B3hAGvL4.js → useExperimentSelector-BBaz0w51.js} +2 -2
  34. package/dist/{useExperimentSelector-B3hAGvL4.js.map → useExperimentSelector-BBaz0w51.js.map} +1 -1
  35. package/dist/{useProtocolTemplates-BJxS5F0_.js → useProtocolTemplates-DODHlhxr.js} +3 -3
  36. package/dist/{useProtocolTemplates-BJxS5F0_.js.map → useProtocolTemplates-DODHlhxr.js.map} +1 -1
  37. package/package.json +1 -1
  38. package/src/__tests__/components/ExperimentPopover.test.ts +23 -0
  39. package/src/__tests__/composables/experiment-utils.test.ts +2 -2
  40. package/src/__tests__/composables/useAppExperiment.test.ts +2 -1
  41. package/src/__tests__/composables/useCommandHistory.test.ts +43 -0
  42. package/src/__tests__/composables/useFileImport.test.ts +44 -0
  43. package/src/__tests__/composables/useOptimisticMutation.test.ts +40 -0
  44. package/src/__tests__/composables/useResourceCrud.test.ts +56 -0
  45. package/src/__tests__/composables/useWellPainting.test.ts +52 -0
  46. package/src/__tests__/composables/useWellPlateAdapter.test.ts +50 -0
  47. package/src/__tests__/composables/useWellPlateValidation.test.ts +42 -0
  48. package/src/components/ExperimentPopover.story.vue +3 -4
  49. package/src/components/ExperimentPopover.vue +6 -6
  50. package/src/components/PluginWorkspaceView.vue +3 -3
  51. package/src/composables/experiment-utils.ts +1 -1
  52. package/src/composables/index.ts +67 -0
  53. package/src/composables/useCommandHistory.ts +113 -0
  54. package/src/composables/useFileImport.ts +231 -0
  55. package/src/composables/useOptimisticMutation.ts +107 -0
  56. package/src/composables/useResourceCrud.ts +245 -0
  57. package/src/composables/useWellPainting.ts +187 -0
  58. package/src/composables/useWellPlateAdapter.ts +147 -0
  59. package/src/composables/useWellPlateValidation.ts +85 -0
  60. package/src/styles/components/experiment-popover.css +2 -4
  61. package/dist/ExperimentPopover-CCYB1oWp.js.map +0 -1
  62. package/dist/components-CqdBz8DI.js.map +0 -1
  63. package/dist/composables-BWh0MpcK.js.map +0 -1
@@ -0,0 +1,187 @@
1
+ import { computed, ref, toValue, type ComputedRef, type Ref } from 'vue'
2
+ import type { WellPlateFormat } from '../types'
3
+
4
+ export type WellPlateSource<T> = T | Ref<T> | ComputedRef<T> | (() => T)
5
+
6
+ export interface WellCoordinate {
7
+ row: number
8
+ col: number
9
+ }
10
+
11
+ export interface WellPaintResult<TMode extends string = string, TPayload = unknown> {
12
+ wellIds: string[]
13
+ mode: TMode
14
+ payload?: TPayload
15
+ }
16
+
17
+ export interface UseWellPaintingOptions<TMode extends string = string, TPayload = unknown> {
18
+ format: WellPlateSource<WellPlateFormat>
19
+ initialMode?: TMode
20
+ initialPayload?: TPayload
21
+ onComplete?: (result: WellPaintResult<TMode, TPayload>) => void
22
+ }
23
+
24
+ export interface UseWellPaintingReturn<TMode extends string = string, TPayload = unknown> {
25
+ activeMode: Ref<TMode>
26
+ activePayload: Ref<TPayload | undefined>
27
+ isDragging: Ref<boolean>
28
+ previewWellIds: ComputedRef<string[]>
29
+ setMode: (mode: TMode, payload?: TPayload) => void
30
+ setPayload: (payload: TPayload | undefined) => void
31
+ onWellMouseDown: (wellId: string) => void
32
+ onWellMouseMove: (wellId: string) => void
33
+ onWellMouseUp: () => void
34
+ cancel: () => void
35
+ }
36
+
37
+ const ROW_LABELS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
38
+
39
+ const PLATE_DIMENSIONS: Record<WellPlateFormat, { rows: number; cols: number }> = {
40
+ 6: { rows: 2, cols: 3 },
41
+ 12: { rows: 3, cols: 4 },
42
+ 24: { rows: 4, cols: 6 },
43
+ 48: { rows: 6, cols: 8 },
44
+ 54: { rows: 6, cols: 9 },
45
+ 96: { rows: 8, cols: 12 },
46
+ 384: { rows: 16, cols: 24 },
47
+ }
48
+
49
+ /** Pointer painting/rectangle selection state for WellPlate-based editors. */
50
+ export function useWellPainting<TMode extends string = string, TPayload = unknown>(
51
+ options: UseWellPaintingOptions<TMode, TPayload>,
52
+ ): UseWellPaintingReturn<TMode, TPayload> {
53
+ const activeMode = ref<TMode>(options.initialMode ?? 'select' as TMode) as Ref<TMode>
54
+ const activePayload = ref<TPayload | undefined>(options.initialPayload) as Ref<TPayload | undefined>
55
+ const isDragging = ref(false)
56
+ const dragStart = ref<WellCoordinate | null>(null)
57
+ const dragEnd = ref<WellCoordinate | null>(null)
58
+ const paintTrail = ref<Set<string>>(new Set())
59
+
60
+ const previewWellIds = computed(() => {
61
+ if (!isDragging.value) return []
62
+ if (activeMode.value === 'select') {
63
+ if (!dragStart.value || !dragEnd.value) return []
64
+ return wellIdsInRectangle(dragStart.value, dragEnd.value, toValue(options.format))
65
+ }
66
+ return [...paintTrail.value]
67
+ })
68
+
69
+ function setMode(mode: TMode, payload?: TPayload): void {
70
+ activeMode.value = mode
71
+ activePayload.value = payload
72
+ }
73
+
74
+ function setPayload(payload: TPayload | undefined): void {
75
+ activePayload.value = payload
76
+ }
77
+
78
+ function onWellMouseDown(wellId: string): void {
79
+ const coord = wellIdToCoordinate(wellId)
80
+ if (!coord) return
81
+ isDragging.value = true
82
+ dragStart.value = coord
83
+ dragEnd.value = coord
84
+ paintTrail.value = new Set([wellId])
85
+ }
86
+
87
+ function onWellMouseMove(wellId: string): void {
88
+ if (!isDragging.value) return
89
+ const coord = wellIdToCoordinate(wellId)
90
+ if (!coord) return
91
+ dragEnd.value = coord
92
+ if (activeMode.value !== 'select') {
93
+ paintTrail.value = new Set([...paintTrail.value, wellId])
94
+ }
95
+ }
96
+
97
+ function onWellMouseUp(): void {
98
+ if (!isDragging.value) return
99
+ const wellIds = previewWellIds.value
100
+ isDragging.value = false
101
+ if (wellIds.length > 0) {
102
+ options.onComplete?.({
103
+ wellIds,
104
+ mode: activeMode.value,
105
+ payload: activePayload.value,
106
+ })
107
+ }
108
+ clearDragState()
109
+ }
110
+
111
+ function cancel(): void {
112
+ isDragging.value = false
113
+ clearDragState()
114
+ }
115
+
116
+ function clearDragState(): void {
117
+ dragStart.value = null
118
+ dragEnd.value = null
119
+ paintTrail.value = new Set()
120
+ }
121
+
122
+ return {
123
+ activeMode,
124
+ activePayload,
125
+ isDragging,
126
+ previewWellIds,
127
+ setMode,
128
+ setPayload,
129
+ onWellMouseDown,
130
+ onWellMouseMove,
131
+ onWellMouseUp,
132
+ cancel,
133
+ }
134
+ }
135
+
136
+ export function wellIdToCoordinate(wellId: string): WellCoordinate | null {
137
+ const match = /^([A-Z]+)(\d+)$/i.exec(wellId.trim())
138
+ if (!match) return null
139
+ return {
140
+ row: rowLabelToIndex(match[1].toUpperCase()),
141
+ col: Number(match[2]) - 1,
142
+ }
143
+ }
144
+
145
+ export function coordinateToWellId(coord: WellCoordinate): string {
146
+ return `${indexToRowLabel(coord.row)}${coord.col + 1}`
147
+ }
148
+
149
+ export function wellIdsInRectangle(
150
+ start: WellCoordinate,
151
+ end: WellCoordinate,
152
+ format: WellPlateFormat,
153
+ ): string[] {
154
+ const dimensions = PLATE_DIMENSIONS[format]
155
+ const minRow = Math.max(0, Math.min(start.row, end.row))
156
+ const maxRow = Math.min(dimensions.rows - 1, Math.max(start.row, end.row))
157
+ const minCol = Math.max(0, Math.min(start.col, end.col))
158
+ const maxCol = Math.min(dimensions.cols - 1, Math.max(start.col, end.col))
159
+ const ids: string[] = []
160
+
161
+ for (let row = minRow; row <= maxRow; row++) {
162
+ for (let col = minCol; col <= maxCol; col++) {
163
+ ids.push(coordinateToWellId({ row, col }))
164
+ }
165
+ }
166
+ return ids
167
+ }
168
+
169
+ function rowLabelToIndex(label: string): number {
170
+ let result = 0
171
+ for (const char of label) {
172
+ result = result * 26 + (char.charCodeAt(0) - 64)
173
+ }
174
+ return result - 1
175
+ }
176
+
177
+ function indexToRowLabel(index: number): string {
178
+ if (index < ROW_LABELS.length) return ROW_LABELS[index]
179
+ let value = index + 1
180
+ let label = ''
181
+ while (value > 0) {
182
+ value--
183
+ label = ROW_LABELS[value % 26] + label
184
+ value = Math.floor(value / 26)
185
+ }
186
+ return label
187
+ }
@@ -0,0 +1,147 @@
1
+ import { computed, toValue, type ComputedRef, type Ref } from 'vue'
2
+ import type { ColumnCondition, RowCondition, Well } from '../types'
3
+
4
+ export type WellPlateAdapterSource<T> =
5
+ | T
6
+ | Ref<T>
7
+ | ComputedRef<T>
8
+ | (() => T)
9
+
10
+ export interface WellPlateEntry {
11
+ wellId: string
12
+ state?: Well['state']
13
+ sampleType?: string
14
+ value?: number
15
+ metadata?: Record<string, unknown>
16
+ }
17
+
18
+ export interface WellPlateAdapterOptions<TEntry> {
19
+ entries: WellPlateAdapterSource<readonly TEntry[]>
20
+ getWellId: (entry: TEntry) => string
21
+ getState?: (entry: TEntry) => Well['state'] | undefined
22
+ getSampleType?: (entry: TEntry) => string | undefined
23
+ getValue?: (entry: TEntry) => number | undefined
24
+ getMetadata?: (entry: TEntry) => Record<string, unknown> | undefined
25
+ }
26
+
27
+ export interface UseWellPlateAdapterReturn {
28
+ wells: ComputedRef<Record<string, Partial<Well>>>
29
+ }
30
+
31
+ /** Convert domain rows into WellPlate's wells prop without hand-written adapters. */
32
+ export function useWellPlateAdapter<TEntry>(
33
+ options: WellPlateAdapterOptions<TEntry>,
34
+ ): UseWellPlateAdapterReturn {
35
+ const wells = computed(() => createWellPlateWells(toValue(options.entries), options))
36
+ return { wells }
37
+ }
38
+
39
+ export function createWellPlateWells<TEntry>(
40
+ entries: readonly TEntry[],
41
+ options: Omit<WellPlateAdapterOptions<TEntry>, 'entries'>,
42
+ ): Record<string, Partial<Well>> {
43
+ return Object.fromEntries(entries.map((entry) => {
44
+ const wellId = options.getWellId(entry)
45
+ const coord = parseWellId(wellId)
46
+ const well: Partial<Well> = {
47
+ id: wellId,
48
+ row: coord.row,
49
+ col: coord.col,
50
+ state: options.getState?.(entry) ?? 'filled',
51
+ sampleType: options.getSampleType?.(entry),
52
+ value: options.getValue?.(entry),
53
+ metadata: options.getMetadata?.(entry),
54
+ }
55
+ return [wellId, well]
56
+ }))
57
+ }
58
+
59
+ export interface AxisConditionEntry {
60
+ label: string
61
+ color: string
62
+ start: string | number
63
+ concentrations: readonly { value: number; replicates?: number }[]
64
+ unit?: string
65
+ }
66
+
67
+ export function createColumnConditions(entries: readonly AxisConditionEntry[]): ColumnCondition[] {
68
+ return entries.map((entry) => {
69
+ const startCol = typeof entry.start === 'number'
70
+ ? entry.start
71
+ : Number.parseInt(entry.start, 10)
72
+ const cols: number[] = []
73
+ const concentrations: number[] = []
74
+
75
+ for (const concentration of entry.concentrations) {
76
+ const replicates = concentration.replicates ?? 1
77
+ for (let index = 0; index < replicates; index++) {
78
+ cols.push(startCol + cols.length)
79
+ concentrations.push(concentration.value)
80
+ }
81
+ }
82
+
83
+ return {
84
+ label: entry.label,
85
+ color: entry.color,
86
+ unit: entry.unit,
87
+ cols,
88
+ concentrations,
89
+ }
90
+ })
91
+ }
92
+
93
+ export function createRowConditions(entries: readonly AxisConditionEntry[]): RowCondition[] {
94
+ return entries.map((entry) => {
95
+ const startRow = typeof entry.start === 'number'
96
+ ? entry.start
97
+ : rowLabelToIndex(entry.start)
98
+ const rows: string[] = []
99
+ const concentrations: number[] = []
100
+
101
+ for (const concentration of entry.concentrations) {
102
+ const replicates = concentration.replicates ?? 1
103
+ for (let index = 0; index < replicates; index++) {
104
+ rows.push(indexToRowLabel(startRow + rows.length))
105
+ concentrations.push(concentration.value)
106
+ }
107
+ }
108
+
109
+ return {
110
+ label: entry.label,
111
+ color: entry.color,
112
+ unit: entry.unit,
113
+ rows,
114
+ concentrations,
115
+ }
116
+ })
117
+ }
118
+
119
+ function parseWellId(wellId: string): { row: number; col: number } {
120
+ const match = /^([A-Z]+)(\d+)$/i.exec(wellId.trim())
121
+ if (!match) {
122
+ throw new Error(`[MINT SDK] Invalid well id "${wellId}".`)
123
+ }
124
+ return {
125
+ row: rowLabelToIndex(match[1].toUpperCase()),
126
+ col: Number(match[2]) - 1,
127
+ }
128
+ }
129
+
130
+ function rowLabelToIndex(label: string): number {
131
+ let result = 0
132
+ for (const char of label.toUpperCase()) {
133
+ result = result * 26 + (char.charCodeAt(0) - 64)
134
+ }
135
+ return result - 1
136
+ }
137
+
138
+ function indexToRowLabel(index: number): string {
139
+ let value = index + 1
140
+ let label = ''
141
+ while (value > 0) {
142
+ value--
143
+ label = String.fromCharCode(65 + (value % 26)) + label
144
+ value = Math.floor(value / 26)
145
+ }
146
+ return label
147
+ }
@@ -0,0 +1,85 @@
1
+ import { computed, toValue, type ComputedRef, type Ref } from 'vue'
2
+
3
+ export type WellPlateValidationSeverity = 'error' | 'warning' | 'info'
4
+
5
+ export interface WellPlateValidationWarning<TType extends string = string> {
6
+ type: TType
7
+ severity: WellPlateValidationSeverity
8
+ message: string
9
+ affectedWells?: string[]
10
+ }
11
+
12
+ export interface WellPlateValidationRule<TContext, TType extends string = string> {
13
+ type: TType
14
+ severity: WellPlateValidationSeverity
15
+ validate: (context: TContext) => false | string | Omit<WellPlateValidationWarning<TType>, 'type' | 'severity'>
16
+ }
17
+
18
+ export type WellPlateValidationSource<T> =
19
+ | T
20
+ | Ref<T>
21
+ | ComputedRef<T>
22
+ | (() => T)
23
+
24
+ export interface UseWellPlateValidationOptions<TContext, TType extends string = string> {
25
+ context: WellPlateValidationSource<TContext>
26
+ rules: WellPlateValidationSource<readonly WellPlateValidationRule<TContext, TType>[]>
27
+ }
28
+
29
+ export interface UseWellPlateValidationReturn<TType extends string = string> {
30
+ warnings: ComputedRef<WellPlateValidationWarning<TType>[]>
31
+ hasBlockingIssues: ComputedRef<boolean>
32
+ warningCount: ComputedRef<number>
33
+ errorCount: ComputedRef<number>
34
+ }
35
+
36
+ /** Rule-driven validation state for WellPlate layout/design screens. */
37
+ export function useWellPlateValidation<TContext, TType extends string = string>(
38
+ options: UseWellPlateValidationOptions<TContext, TType>,
39
+ ): UseWellPlateValidationReturn<TType> {
40
+ const warnings = computed(() => runWellPlateValidation(
41
+ toValue(options.context),
42
+ toValue(options.rules),
43
+ ))
44
+ const hasBlockingIssues = computed(() => warnings.value.some(warning => warning.severity === 'error'))
45
+ const warningCount = computed(() => warnings.value.length)
46
+ const errorCount = computed(() => warnings.value.filter(warning => warning.severity === 'error').length)
47
+
48
+ return {
49
+ warnings,
50
+ hasBlockingIssues,
51
+ warningCount,
52
+ errorCount,
53
+ }
54
+ }
55
+
56
+ export function runWellPlateValidation<TContext, TType extends string = string>(
57
+ context: TContext,
58
+ rules: readonly WellPlateValidationRule<TContext, TType>[],
59
+ ): WellPlateValidationWarning<TType>[] {
60
+ const warnings: WellPlateValidationWarning<TType>[] = []
61
+ for (const rule of rules) {
62
+ const result = rule.validate(context)
63
+ if (!result) continue
64
+ warnings.push(normalizeWarning(rule, result))
65
+ }
66
+ return warnings
67
+ }
68
+
69
+ function normalizeWarning<TContext, TType extends string>(
70
+ rule: WellPlateValidationRule<TContext, TType>,
71
+ result: string | Omit<WellPlateValidationWarning<TType>, 'type' | 'severity'>,
72
+ ): WellPlateValidationWarning<TType> {
73
+ if (typeof result === 'string') {
74
+ return {
75
+ type: rule.type,
76
+ severity: rule.severity,
77
+ message: result,
78
+ }
79
+ }
80
+ return {
81
+ type: rule.type,
82
+ severity: rule.severity,
83
+ ...result,
84
+ }
85
+ }
@@ -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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"}