@morscherlab/mint-sdk 1.4.0 → 1.4.2
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.
- package/dist/{AlertBox-DwgNTEqf.js → AlertBox-CIhqsWbN.js} +2 -2
- package/dist/{AlertBox-DwgNTEqf.js.map → AlertBox-CIhqsWbN.js.map} +1 -1
- package/dist/{AppSidebar-CZZaxni0.js → AppSidebar-BDrd7Kwn.js} +6 -6
- package/dist/{AppSidebar-CZZaxni0.js.map → AppSidebar-BDrd7Kwn.js.map} +1 -1
- package/dist/{ExperimentPopover-DujiuQIO.js → ExperimentPopover-BM92LlrQ.js} +2 -2
- package/dist/{ExperimentPopover-DujiuQIO.js.map → ExperimentPopover-BM92LlrQ.js.map} +1 -1
- package/dist/{ExperimentSelectorModal-B4j4eUOJ.js → ExperimentSelectorModal-LsB1ABmh.js} +3 -3
- package/dist/{ExperimentSelectorModal-B4j4eUOJ.js.map → ExperimentSelectorModal-LsB1ABmh.js.map} +1 -1
- package/dist/{SegmentedControl-DyUPH3I5.js → SegmentedControl-Dz41vAFM.js} +2 -2
- package/dist/{SegmentedControl-DyUPH3I5.js.map → SegmentedControl-Dz41vAFM.js.map} +1 -1
- package/dist/{SettingsModal-D2MOOtuO.js → SettingsModal-C-MSR-bk.js} +6 -6
- package/dist/{SettingsModal-D2MOOtuO.js.map → SettingsModal-C-MSR-bk.js.map} +1 -1
- package/dist/{controlSchemaModel-Bsg0t5Ab.js → controlSchemaModel-BbXbMgc6.js} +2 -2
- package/dist/{controlSchemaModel-Bsg0t5Ab.js.map → controlSchemaModel-BbXbMgc6.js.map} +1 -1
- package/dist/{data-Boeh4uPI.js → data-LzdkEZJ-.js} +9 -9
- package/dist/{data-Boeh4uPI.js.map → data-LzdkEZJ-.js.map} +1 -1
- package/dist/data.js +7 -7
- package/dist/index.js +16 -16
- package/dist/{install-BEHp7Mo-.js → install-UTqrAgHe.js} +13 -13
- package/dist/{install-BEHp7Mo-.js.map → install-UTqrAgHe.js.map} +1 -1
- package/dist/install.js +1 -1
- package/dist/{lab-D61eaX6f.js → lab-CbdDLAVr.js} +9 -9
- package/dist/{lab-D61eaX6f.js.map → lab-CbdDLAVr.js.map} +1 -1
- package/dist/lab.js +4 -4
- package/dist/{records-D4aQXyZu.js → records-CWRztE5M.js} +9 -2
- package/dist/{records-D4aQXyZu.js.map → records-CWRztE5M.js.map} +1 -1
- package/dist/{runtime-q-R0CNLh.js → runtime-DrL6lKAb.js} +8 -8
- package/dist/{runtime-q-R0CNLh.js.map → runtime-DrL6lKAb.js.map} +1 -1
- package/dist/runtime.js +6 -6
- package/dist/{shell-6CGFvUUw.js → shell-Ch7cJnjo.js} +4 -4
- package/dist/{shell-6CGFvUUw.js.map → shell-Ch7cJnjo.js.map} +1 -1
- package/dist/shell.js +4 -4
- package/dist/styles.css +4 -7
- package/dist/{useExperimentSamples-QEFoq4tf.js → useExperimentSamples-Cz2v3V_q.js} +3 -3
- package/dist/{useExperimentSamples-QEFoq4tf.js.map → useExperimentSamples-Cz2v3V_q.js.map} +1 -1
- package/dist/{useListReorder-DsuzwWGS.js → useListReorder-QqJc1dpX.js} +2 -2
- package/dist/{useListReorder-DsuzwWGS.js.map → useListReorder-QqJc1dpX.js.map} +1 -1
- package/dist/{useRackEditor-KpZ4iJ6o.js → useRackEditor-BHGM1Ngz.js} +6 -6
- package/dist/{useRackEditor-KpZ4iJ6o.js.map → useRackEditor-BHGM1Ngz.js.map} +1 -1
- package/dist/{useRealtime-B58Lo-Tm.js → useRealtime-CNQbrS01.js} +2 -2
- package/dist/{useRealtime-B58Lo-Tm.js.map → useRealtime-CNQbrS01.js.map} +1 -1
- package/dist/{useRequest-BuDmW8Jc.js → useRequest-Bfsh7gIm.js} +2 -2
- package/dist/{useRequest-BuDmW8Jc.js.map → useRequest-Bfsh7gIm.js.map} +1 -1
- package/package.json +1 -1
- package/src/__tests__/components/ResourcePickerListInternal.test.ts +5 -0
- package/src/__tests__/styles/controlRefreshRules.test.ts +12 -0
- package/src/components/internal/ResourcePickerListInternal.vue +8 -0
- package/src/styles/components/artifact-save-dialog.css +0 -1
- package/src/styles/components/dataframe.css +0 -5
- package/src/styles/components/resource-picker-list.css +4 -1
package/dist/runtime.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { i as AuthenticationRequiredError, o as useApi } from "./experiment-DyO54FQB.js";
|
|
2
|
-
import { d as isActiveJobStatus, f as isTerminalJobStatus, g as resolveJobCapabilities, h as normalizeJobState, m as normalizeJobPercent, o as resolveCurrentPluginId, p as jobStatusLabel } from "./records-
|
|
3
|
-
import { A as useJobsEndpoint, E as GeneratedPathInput_default, _ as getTypeDefault, g as getFieldRegistryEntry, k as createGeneratedAnalysisTransport } from "./useListReorder-
|
|
4
|
-
import { a as GeneratedPlotlyResult_default, c as downloadEndpoint, d as useEventStream, f as useSettingsEndpoint, i as GeneratedResultRenderer_default, l as pluginFormDataFromPayload, m as resolvePluginBaseUrl, o as createClient, p as buildEndpointUrl, u as uploadEndpoint } from "./useRackEditor-
|
|
5
|
-
import { a as ComponentBindingRenderer_default, c as generatedJobFormSchema, f as useControlWorkspace, i as RuntimeUpdateNotice_default, l as normalizeGeneratedJobInput, m as useJobCenter, n as PresenceAvatars_default, o as GeneratedPluginView_default, p as combinePluginJobSources, r as usePresence, s as generatedJobDefaults } from "./runtime-
|
|
6
|
-
import { D as useControlSchema, E as getControlDefaults, F as getDefaultControlView, M as controlsTo, O as controlBindings, T as defineControls, d as useForm, l as useFormBuilder, u as evaluateCondition, w as defineControlComponentBindings } from "./AppSidebar-
|
|
7
|
-
import { r as useRealtime } from "./useRealtime-
|
|
2
|
+
import { d as isActiveJobStatus, f as isTerminalJobStatus, g as resolveJobCapabilities, h as normalizeJobState, m as normalizeJobPercent, o as resolveCurrentPluginId, p as jobStatusLabel } from "./records-CWRztE5M.js";
|
|
3
|
+
import { A as useJobsEndpoint, E as GeneratedPathInput_default, _ as getTypeDefault, g as getFieldRegistryEntry, k as createGeneratedAnalysisTransport } from "./useListReorder-QqJc1dpX.js";
|
|
4
|
+
import { a as GeneratedPlotlyResult_default, c as downloadEndpoint, d as useEventStream, f as useSettingsEndpoint, i as GeneratedResultRenderer_default, l as pluginFormDataFromPayload, m as resolvePluginBaseUrl, o as createClient, p as buildEndpointUrl, u as uploadEndpoint } from "./useRackEditor-BHGM1Ngz.js";
|
|
5
|
+
import { a as ComponentBindingRenderer_default, c as generatedJobFormSchema, f as useControlWorkspace, i as RuntimeUpdateNotice_default, l as normalizeGeneratedJobInput, m as useJobCenter, n as PresenceAvatars_default, o as GeneratedPluginView_default, p as combinePluginJobSources, r as usePresence, s as generatedJobDefaults } from "./runtime-DrL6lKAb.js";
|
|
6
|
+
import { D as useControlSchema, E as getControlDefaults, F as getDefaultControlView, M as controlsTo, O as controlBindings, T as defineControls, d as useForm, l as useFormBuilder, u as evaluateCondition, w as defineControlComponentBindings } from "./AppSidebar-BDrd7Kwn.js";
|
|
7
|
+
import { r as useRealtime } from "./useRealtime-CNQbrS01.js";
|
|
8
8
|
export { AuthenticationRequiredError, ComponentBindingRenderer_default as ComponentBindingRenderer, GeneratedPathInput_default as GeneratedPathInput, GeneratedPlotlyResult_default as GeneratedPlotlyResult, GeneratedPluginView_default as GeneratedPluginView, GeneratedResultRenderer_default as GeneratedResultRenderer, PresenceAvatars_default as PresenceAvatars, RuntimeUpdateNotice_default as RuntimeUpdateNotice, buildEndpointUrl, combinePluginJobSources, controlBindings, controlsTo, createClient, createGeneratedAnalysisTransport, defineControlComponentBindings, defineControls, downloadEndpoint, evaluateCondition, generatedJobDefaults, generatedJobFormSchema, getControlDefaults, getDefaultControlView, getFieldRegistryEntry, getTypeDefault, isActiveJobStatus, isTerminalJobStatus, jobStatusLabel, normalizeGeneratedJobInput, normalizeJobPercent, normalizeJobState, pluginFormDataFromPayload, resolveCurrentPluginId, resolveJobCapabilities, resolvePluginBaseUrl, uploadEndpoint, useApi, useControlSchema, useControlWorkspace, useEventStream, useForm, useFormBuilder, useJobCenter, useJobsEndpoint, usePresence, useRealtime, useSettingsEndpoint };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { i as useEventListener, o as __exportAll, r as useFocusTrap } from "./useDropdownState-uFIz6Yv8.js";
|
|
2
|
-
import { E as useMenuKeyboard, H as useListSelection, R as useRovingFocus, U as useSelectionLimit } from "./records-
|
|
3
|
-
import { B as compareSortValues, V as useSortedItems, i as useReorderAnnouncer, n as reorderList, r as useListReorder, t as REORDER_ANNOUNCER_STYLE } from "./useListReorder-
|
|
4
|
-
import { C as usePluginWorkspace, S as AppLayout_default, _ as provideOnboarding, f as AppTopBar_default, g as defineOnboarding, h as ONBOARDING_KEY, m as ANCHOR_WAIT_MS, t as AppSidebar_default, v as useOnboarding } from "./AppSidebar-
|
|
2
|
+
import { E as useMenuKeyboard, H as useListSelection, R as useRovingFocus, U as useSelectionLimit } from "./records-CWRztE5M.js";
|
|
3
|
+
import { B as compareSortValues, V as useSortedItems, i as useReorderAnnouncer, n as reorderList, r as useListReorder, t as REORDER_ANNOUNCER_STYLE } from "./useListReorder-QqJc1dpX.js";
|
|
4
|
+
import { C as usePluginWorkspace, S as AppLayout_default, _ as provideOnboarding, f as AppTopBar_default, g as defineOnboarding, h as ONBOARDING_KEY, m as ANCHOR_WAIT_MS, t as AppSidebar_default, v as useOnboarding } from "./AppSidebar-BDrd7Kwn.js";
|
|
5
5
|
import { t as useDebouncedWatch } from "./useDebouncedWatch-BPwyl6eS.js";
|
|
6
6
|
import { t as useExpansionSet } from "./useExpansionSet-CvoNMZJi.js";
|
|
7
7
|
import { computed, createElementBlock, createElementVNode, defineComponent, normalizeClass, onBeforeUnmount, openBlock, shallowRef, toValue, watch } from "vue";
|
|
@@ -199,4 +199,4 @@ var shell_exports = /* @__PURE__ */ __exportAll({
|
|
|
199
199
|
//#endregion
|
|
200
200
|
export { useManualLayoutResize as a, resizedVerticalSplitPercent as i, resizedLeadingPanelWidth as n, LayoutResizeHandle_default as o, resizedTrailingPanelWidth as r, shell_exports as t };
|
|
201
201
|
|
|
202
|
-
//# sourceMappingURL=shell-
|
|
202
|
+
//# sourceMappingURL=shell-Ch7cJnjo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-6CGFvUUw.js","names":[],"sources":["../src/components/LayoutResizeHandle.vue","../src/components/LayoutResizeHandle.vue","../src/composables/useManualLayoutResize.ts","../src/shell.ts"],"sourcesContent":["<script setup lang=\"ts\">\n/** Accessible pointer and keyboard handle for resizable workbench panes. */\ninterface Props {\n modelValue: number\n min: number\n max: number\n label: string\n orientation: 'vertical' | 'horizontal'\n edge?: 'left' | 'right'\n step?: number\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n edge: undefined,\n step: 10,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number]\n 'resize-start': [event: PointerEvent]\n}>()\n\nfunction update(value: number): void {\n emit('update:modelValue', Math.min(props.max, Math.max(props.min, value)))\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (props.disabled) return\n const decrement = props.orientation === 'vertical' ? 'ArrowLeft' : 'ArrowUp'\n const increment = props.orientation === 'vertical' ? 'ArrowRight' : 'ArrowDown'\n if (event.key === decrement) update(props.modelValue - props.step)\n else if (event.key === increment) update(props.modelValue + props.step)\n else if (event.key === 'Home') update(props.min)\n else if (event.key === 'End') update(props.max)\n else return\n event.preventDefault()\n}\n</script>\n\n<template>\n <div\n role=\"separator\"\n :tabindex=\"disabled ? -1 : 0\"\n :class=\"[\n 'mint-layout-resize-handle',\n `mint-layout-resize-handle--${orientation}`,\n edge ? `mint-layout-resize-handle--edge-${edge}` : '',\n ]\"\n :aria-label=\"label\"\n :aria-orientation=\"orientation\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-valuenow=\"modelValue\"\n :aria-disabled=\"disabled || undefined\"\n @pointerdown=\"!disabled && emit('resize-start', $event)\"\n @keydown=\"handleKeydown\"\n >\n <span class=\"mint-layout-resize-handle__bar\" aria-hidden=\"true\" />\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Accessible pointer and keyboard handle for resizable workbench panes. */\ninterface Props {\n modelValue: number\n min: number\n max: number\n label: string\n orientation: 'vertical' | 'horizontal'\n edge?: 'left' | 'right'\n step?: number\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n edge: undefined,\n step: 10,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number]\n 'resize-start': [event: PointerEvent]\n}>()\n\nfunction update(value: number): void {\n emit('update:modelValue', Math.min(props.max, Math.max(props.min, value)))\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (props.disabled) return\n const decrement = props.orientation === 'vertical' ? 'ArrowLeft' : 'ArrowUp'\n const increment = props.orientation === 'vertical' ? 'ArrowRight' : 'ArrowDown'\n if (event.key === decrement) update(props.modelValue - props.step)\n else if (event.key === increment) update(props.modelValue + props.step)\n else if (event.key === 'Home') update(props.min)\n else if (event.key === 'End') update(props.max)\n else return\n event.preventDefault()\n}\n</script>\n\n<template>\n <div\n role=\"separator\"\n :tabindex=\"disabled ? -1 : 0\"\n :class=\"[\n 'mint-layout-resize-handle',\n `mint-layout-resize-handle--${orientation}`,\n edge ? `mint-layout-resize-handle--edge-${edge}` : '',\n ]\"\n :aria-label=\"label\"\n :aria-orientation=\"orientation\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-valuenow=\"modelValue\"\n :aria-disabled=\"disabled || undefined\"\n @pointerdown=\"!disabled && emit('resize-start', $event)\"\n @keydown=\"handleKeydown\"\n >\n <span class=\"mint-layout-resize-handle__bar\" aria-hidden=\"true\" />\n </div>\n</template>\n","import { computed, onBeforeUnmount, shallowRef, toValue, watch, type MaybeRefOrGetter } from 'vue'\n\nexport interface ResizePanelWidthConstraints {\n minWidth: number\n maxWidth: number\n mainMinWidth?: number\n layoutGap?: number\n}\n\nexport interface ResizePercentConstraints {\n minPercent: number\n maxPercent: number\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\nfunction availablePanelWidth(\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return Math.min(\n constraints.maxWidth,\n containerWidth - (constraints.mainMinWidth ?? 0) - (constraints.layoutGap ?? 0),\n )\n}\n\nexport function resizedLeadingPanelWidth(\n startWidth: number,\n deltaX: number,\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return Math.round(clamp(\n startWidth + deltaX,\n constraints.minWidth,\n Math.max(constraints.minWidth, availablePanelWidth(containerWidth, constraints)),\n ))\n}\n\nexport function resizedTrailingPanelWidth(\n startWidth: number,\n deltaX: number,\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return resizedLeadingPanelWidth(startWidth, -deltaX, containerWidth, constraints)\n}\n\nexport function resizedVerticalSplitPercent(\n startPercent: number,\n deltaY: number,\n containerHeight: number,\n constraints: ResizePercentConstraints,\n): number {\n if (containerHeight <= 0) return startPercent\n return Math.round(clamp(\n startPercent + (deltaY / containerHeight) * 100,\n constraints.minPercent,\n constraints.maxPercent,\n ))\n}\n\nexport interface ManualLayoutResizeContext<TKind extends string> {\n kind: TKind\n startValue: number\n deltaX: number\n deltaY: number\n containerRect: DOMRectReadOnly\n secondaryRect: DOMRectReadOnly | null\n}\n\nexport interface ManualLayoutResizeTarget<TKind extends string> {\n cursor?: string\n getContainerElement: () => HTMLElement | null\n getSecondaryElement?: () => HTMLElement | null\n getStartValue: () => number\n onResize: (context: ManualLayoutResizeContext<TKind>) => void\n}\n\n/** Coordinates pointer-driven resizing for one or more layout splitters. */\nexport function useManualLayoutResize<TKind extends string>(options: {\n enabled: MaybeRefOrGetter<boolean>\n resolveTarget: (kind: TKind) => ManualLayoutResizeTarget<TKind> | null\n}) {\n const activeResize = shallowRef<{\n kind: TKind\n startClientX: number\n startClientY: number\n startValue: number\n containerRect: DOMRectReadOnly\n secondaryRect: DOMRectReadOnly | null\n cursor: string\n } | null>(null)\n const isResizing = computed(() => activeResize.value !== null)\n let previousBodyCursor = ''\n let previousBodyUserSelect = ''\n\n function stopResize(): void {\n if (!activeResize.value) return\n activeResize.value = null\n window.removeEventListener('pointermove', handlePointerMove)\n window.removeEventListener('pointerup', stopResize)\n window.removeEventListener('pointercancel', stopResize)\n if (typeof document !== 'undefined') {\n document.body.style.cursor = previousBodyCursor\n document.body.style.userSelect = previousBodyUserSelect\n }\n }\n\n function handlePointerMove(event: PointerEvent): void {\n const state = activeResize.value\n if (!state) return\n options.resolveTarget(state.kind)?.onResize({\n kind: state.kind,\n startValue: state.startValue,\n deltaX: event.clientX - state.startClientX,\n deltaY: event.clientY - state.startClientY,\n containerRect: state.containerRect,\n secondaryRect: state.secondaryRect,\n })\n }\n\n function startResize(kind: TKind, event: PointerEvent): void {\n if (!toValue(options.enabled) || event.button !== 0) return\n const target = options.resolveTarget(kind)\n const containerRect = target?.getContainerElement()?.getBoundingClientRect()\n if (!target || !containerRect) return\n\n event.preventDefault()\n if (event.currentTarget instanceof HTMLElement) {\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n const secondary = target.getSecondaryElement?.() ?? null\n activeResize.value = {\n kind,\n startClientX: event.clientX,\n startClientY: event.clientY,\n startValue: target.getStartValue(),\n containerRect,\n secondaryRect: secondary?.getBoundingClientRect() ?? null,\n cursor: target.cursor ?? 'col-resize',\n }\n\n if (typeof document !== 'undefined') {\n previousBodyCursor = document.body.style.cursor\n previousBodyUserSelect = document.body.style.userSelect\n document.body.style.cursor = activeResize.value.cursor\n document.body.style.userSelect = 'none'\n }\n window.addEventListener('pointermove', handlePointerMove)\n window.addEventListener('pointerup', stopResize)\n window.addEventListener('pointercancel', stopResize)\n }\n\n function isResizingKind(kind: TKind): boolean {\n return activeResize.value?.kind === kind\n }\n\n watch(() => toValue(options.enabled), enabled => { if (!enabled) stopResize() })\n onBeforeUnmount(stopResize)\n\n return { activeResize, isResizing, isResizingKind, startResize, stopResize }\n}\n","// Application shell components (`@morscherlab/mint-sdk/shell`).\n\n// Layout components\nexport { default as AppTopBar } from './components/AppTopBar.vue'\nexport { default as AppSidebar } from './components/AppSidebar.vue'\nexport { default as AppLayout } from './components/AppLayout.vue'\nexport { default as LayoutResizeHandle } from './components/LayoutResizeHandle.vue'\n\n// Headless interaction composables\nexport {\n useEventListener,\n type EventListenerEnabled,\n type EventFor,\n type EventListenerStop,\n type EventMapFor,\n type EventTargetGetter,\n type EventTargetLike,\n type UseEventListenerObjectOptions,\n type UseEventListenerOptions,\n} from './composables/useEventListener'\nexport {\n useDebouncedWatch,\n type DebouncedWatchCallback,\n type DebouncedWatchCleanup,\n type DebouncedWatchSource,\n type UseDebouncedWatchOptions,\n type UseDebouncedWatchReturn,\n} from './composables/useDebouncedWatch'\nexport {\n useFocusTrap,\n type UseFocusTrapOptions,\n type UseFocusTrapReturn,\n} from './composables/useFocusTrap'\nexport {\n useRovingFocus,\n type UseRovingFocusOptions,\n type UseRovingFocusReturn,\n} from './composables/useRovingFocus'\nexport {\n reorderList,\n useListReorder,\n useReorderAnnouncer,\n REORDER_ANNOUNCER_STYLE,\n type ListReorderMove,\n type UseListReorderOptions,\n type UseListReorderReturn,\n type UseReorderAnnouncerReturn,\n} from './composables/useListReorder'\nexport {\n useMenuKeyboard,\n type UseMenuKeyboardOptions,\n type UseMenuKeyboardReturn,\n} from './composables/useMenuKeyboard'\n\n// Layout, selection, expansion and sort primitives\nexport {\n resizedLeadingPanelWidth,\n resizedTrailingPanelWidth,\n resizedVerticalSplitPercent,\n useManualLayoutResize,\n type ManualLayoutResizeContext,\n type ManualLayoutResizeTarget,\n type ResizePanelWidthConstraints,\n type ResizePercentConstraints,\n} from './composables/useManualLayoutResize'\nexport {\n useSelectionLimit,\n type SelectionLimitSource,\n type UseSelectionLimitOptions,\n type UseSelectionLimitReturn,\n} from './composables/useSelectionLimit'\nexport {\n useListSelection,\n type ListSelectionSource,\n type ListSelectionValue,\n type UseListSelectionOptions,\n type UseListSelectionReturn,\n type WidenListSelectionValue,\n} from './composables/useListSelection'\nexport {\n useExpansionSet,\n type ExpansionSetSource,\n type UseExpansionSetOptions,\n type UseExpansionSetReturn,\n} from './composables/useExpansionSet'\nexport {\n compareSortValues,\n useSortedItems,\n type CompareSortValuesOptions,\n type SortComparator,\n type SortComparatorContext,\n type SortDescriptor,\n type SortedItemsSource,\n type SortOrder,\n type UseSortedItemsOptions,\n type UseSortedItemsReturn,\n} from './composables/useSortedItems'\nexport {\n usePluginWorkspace,\n type PluginWorkspaceSlotsLike,\n type UsePluginWorkspaceOptions,\n type UsePluginWorkspaceReturn,\n} from './composables/usePluginWorkspace'\n\n// Onboarding: tours, the Getting started checklist and what's-new hints\nexport {\n ANCHOR_WAIT_MS,\n defineOnboarding,\n provideOnboarding,\n useOnboarding,\n ONBOARDING_KEY,\n type OnboardingActiveStep,\n type OnboardingDefinition,\n type OnboardingNews,\n type OnboardingState,\n type OnboardingStep,\n type OnboardingTask,\n type OnboardingTour,\n type ProvideOnboardingOptions,\n} from './composables/useOnboarding'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shell-Ch7cJnjo.js","names":[],"sources":["../src/components/LayoutResizeHandle.vue","../src/components/LayoutResizeHandle.vue","../src/composables/useManualLayoutResize.ts","../src/shell.ts"],"sourcesContent":["<script setup lang=\"ts\">\n/** Accessible pointer and keyboard handle for resizable workbench panes. */\ninterface Props {\n modelValue: number\n min: number\n max: number\n label: string\n orientation: 'vertical' | 'horizontal'\n edge?: 'left' | 'right'\n step?: number\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n edge: undefined,\n step: 10,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number]\n 'resize-start': [event: PointerEvent]\n}>()\n\nfunction update(value: number): void {\n emit('update:modelValue', Math.min(props.max, Math.max(props.min, value)))\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (props.disabled) return\n const decrement = props.orientation === 'vertical' ? 'ArrowLeft' : 'ArrowUp'\n const increment = props.orientation === 'vertical' ? 'ArrowRight' : 'ArrowDown'\n if (event.key === decrement) update(props.modelValue - props.step)\n else if (event.key === increment) update(props.modelValue + props.step)\n else if (event.key === 'Home') update(props.min)\n else if (event.key === 'End') update(props.max)\n else return\n event.preventDefault()\n}\n</script>\n\n<template>\n <div\n role=\"separator\"\n :tabindex=\"disabled ? -1 : 0\"\n :class=\"[\n 'mint-layout-resize-handle',\n `mint-layout-resize-handle--${orientation}`,\n edge ? `mint-layout-resize-handle--edge-${edge}` : '',\n ]\"\n :aria-label=\"label\"\n :aria-orientation=\"orientation\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-valuenow=\"modelValue\"\n :aria-disabled=\"disabled || undefined\"\n @pointerdown=\"!disabled && emit('resize-start', $event)\"\n @keydown=\"handleKeydown\"\n >\n <span class=\"mint-layout-resize-handle__bar\" aria-hidden=\"true\" />\n </div>\n</template>\n","<script setup lang=\"ts\">\n/** Accessible pointer and keyboard handle for resizable workbench panes. */\ninterface Props {\n modelValue: number\n min: number\n max: number\n label: string\n orientation: 'vertical' | 'horizontal'\n edge?: 'left' | 'right'\n step?: number\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n edge: undefined,\n step: 10,\n disabled: false,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: number]\n 'resize-start': [event: PointerEvent]\n}>()\n\nfunction update(value: number): void {\n emit('update:modelValue', Math.min(props.max, Math.max(props.min, value)))\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (props.disabled) return\n const decrement = props.orientation === 'vertical' ? 'ArrowLeft' : 'ArrowUp'\n const increment = props.orientation === 'vertical' ? 'ArrowRight' : 'ArrowDown'\n if (event.key === decrement) update(props.modelValue - props.step)\n else if (event.key === increment) update(props.modelValue + props.step)\n else if (event.key === 'Home') update(props.min)\n else if (event.key === 'End') update(props.max)\n else return\n event.preventDefault()\n}\n</script>\n\n<template>\n <div\n role=\"separator\"\n :tabindex=\"disabled ? -1 : 0\"\n :class=\"[\n 'mint-layout-resize-handle',\n `mint-layout-resize-handle--${orientation}`,\n edge ? `mint-layout-resize-handle--edge-${edge}` : '',\n ]\"\n :aria-label=\"label\"\n :aria-orientation=\"orientation\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-valuenow=\"modelValue\"\n :aria-disabled=\"disabled || undefined\"\n @pointerdown=\"!disabled && emit('resize-start', $event)\"\n @keydown=\"handleKeydown\"\n >\n <span class=\"mint-layout-resize-handle__bar\" aria-hidden=\"true\" />\n </div>\n</template>\n","import { computed, onBeforeUnmount, shallowRef, toValue, watch, type MaybeRefOrGetter } from 'vue'\n\nexport interface ResizePanelWidthConstraints {\n minWidth: number\n maxWidth: number\n mainMinWidth?: number\n layoutGap?: number\n}\n\nexport interface ResizePercentConstraints {\n minPercent: number\n maxPercent: number\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\nfunction availablePanelWidth(\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return Math.min(\n constraints.maxWidth,\n containerWidth - (constraints.mainMinWidth ?? 0) - (constraints.layoutGap ?? 0),\n )\n}\n\nexport function resizedLeadingPanelWidth(\n startWidth: number,\n deltaX: number,\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return Math.round(clamp(\n startWidth + deltaX,\n constraints.minWidth,\n Math.max(constraints.minWidth, availablePanelWidth(containerWidth, constraints)),\n ))\n}\n\nexport function resizedTrailingPanelWidth(\n startWidth: number,\n deltaX: number,\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return resizedLeadingPanelWidth(startWidth, -deltaX, containerWidth, constraints)\n}\n\nexport function resizedVerticalSplitPercent(\n startPercent: number,\n deltaY: number,\n containerHeight: number,\n constraints: ResizePercentConstraints,\n): number {\n if (containerHeight <= 0) return startPercent\n return Math.round(clamp(\n startPercent + (deltaY / containerHeight) * 100,\n constraints.minPercent,\n constraints.maxPercent,\n ))\n}\n\nexport interface ManualLayoutResizeContext<TKind extends string> {\n kind: TKind\n startValue: number\n deltaX: number\n deltaY: number\n containerRect: DOMRectReadOnly\n secondaryRect: DOMRectReadOnly | null\n}\n\nexport interface ManualLayoutResizeTarget<TKind extends string> {\n cursor?: string\n getContainerElement: () => HTMLElement | null\n getSecondaryElement?: () => HTMLElement | null\n getStartValue: () => number\n onResize: (context: ManualLayoutResizeContext<TKind>) => void\n}\n\n/** Coordinates pointer-driven resizing for one or more layout splitters. */\nexport function useManualLayoutResize<TKind extends string>(options: {\n enabled: MaybeRefOrGetter<boolean>\n resolveTarget: (kind: TKind) => ManualLayoutResizeTarget<TKind> | null\n}) {\n const activeResize = shallowRef<{\n kind: TKind\n startClientX: number\n startClientY: number\n startValue: number\n containerRect: DOMRectReadOnly\n secondaryRect: DOMRectReadOnly | null\n cursor: string\n } | null>(null)\n const isResizing = computed(() => activeResize.value !== null)\n let previousBodyCursor = ''\n let previousBodyUserSelect = ''\n\n function stopResize(): void {\n if (!activeResize.value) return\n activeResize.value = null\n window.removeEventListener('pointermove', handlePointerMove)\n window.removeEventListener('pointerup', stopResize)\n window.removeEventListener('pointercancel', stopResize)\n if (typeof document !== 'undefined') {\n document.body.style.cursor = previousBodyCursor\n document.body.style.userSelect = previousBodyUserSelect\n }\n }\n\n function handlePointerMove(event: PointerEvent): void {\n const state = activeResize.value\n if (!state) return\n options.resolveTarget(state.kind)?.onResize({\n kind: state.kind,\n startValue: state.startValue,\n deltaX: event.clientX - state.startClientX,\n deltaY: event.clientY - state.startClientY,\n containerRect: state.containerRect,\n secondaryRect: state.secondaryRect,\n })\n }\n\n function startResize(kind: TKind, event: PointerEvent): void {\n if (!toValue(options.enabled) || event.button !== 0) return\n const target = options.resolveTarget(kind)\n const containerRect = target?.getContainerElement()?.getBoundingClientRect()\n if (!target || !containerRect) return\n\n event.preventDefault()\n if (event.currentTarget instanceof HTMLElement) {\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n const secondary = target.getSecondaryElement?.() ?? null\n activeResize.value = {\n kind,\n startClientX: event.clientX,\n startClientY: event.clientY,\n startValue: target.getStartValue(),\n containerRect,\n secondaryRect: secondary?.getBoundingClientRect() ?? null,\n cursor: target.cursor ?? 'col-resize',\n }\n\n if (typeof document !== 'undefined') {\n previousBodyCursor = document.body.style.cursor\n previousBodyUserSelect = document.body.style.userSelect\n document.body.style.cursor = activeResize.value.cursor\n document.body.style.userSelect = 'none'\n }\n window.addEventListener('pointermove', handlePointerMove)\n window.addEventListener('pointerup', stopResize)\n window.addEventListener('pointercancel', stopResize)\n }\n\n function isResizingKind(kind: TKind): boolean {\n return activeResize.value?.kind === kind\n }\n\n watch(() => toValue(options.enabled), enabled => { if (!enabled) stopResize() })\n onBeforeUnmount(stopResize)\n\n return { activeResize, isResizing, isResizingKind, startResize, stopResize }\n}\n","// Application shell components (`@morscherlab/mint-sdk/shell`).\n\n// Layout components\nexport { default as AppTopBar } from './components/AppTopBar.vue'\nexport { default as AppSidebar } from './components/AppSidebar.vue'\nexport { default as AppLayout } from './components/AppLayout.vue'\nexport { default as LayoutResizeHandle } from './components/LayoutResizeHandle.vue'\n\n// Headless interaction composables\nexport {\n useEventListener,\n type EventListenerEnabled,\n type EventFor,\n type EventListenerStop,\n type EventMapFor,\n type EventTargetGetter,\n type EventTargetLike,\n type UseEventListenerObjectOptions,\n type UseEventListenerOptions,\n} from './composables/useEventListener'\nexport {\n useDebouncedWatch,\n type DebouncedWatchCallback,\n type DebouncedWatchCleanup,\n type DebouncedWatchSource,\n type UseDebouncedWatchOptions,\n type UseDebouncedWatchReturn,\n} from './composables/useDebouncedWatch'\nexport {\n useFocusTrap,\n type UseFocusTrapOptions,\n type UseFocusTrapReturn,\n} from './composables/useFocusTrap'\nexport {\n useRovingFocus,\n type UseRovingFocusOptions,\n type UseRovingFocusReturn,\n} from './composables/useRovingFocus'\nexport {\n reorderList,\n useListReorder,\n useReorderAnnouncer,\n REORDER_ANNOUNCER_STYLE,\n type ListReorderMove,\n type UseListReorderOptions,\n type UseListReorderReturn,\n type UseReorderAnnouncerReturn,\n} from './composables/useListReorder'\nexport {\n useMenuKeyboard,\n type UseMenuKeyboardOptions,\n type UseMenuKeyboardReturn,\n} from './composables/useMenuKeyboard'\n\n// Layout, selection, expansion and sort primitives\nexport {\n resizedLeadingPanelWidth,\n resizedTrailingPanelWidth,\n resizedVerticalSplitPercent,\n useManualLayoutResize,\n type ManualLayoutResizeContext,\n type ManualLayoutResizeTarget,\n type ResizePanelWidthConstraints,\n type ResizePercentConstraints,\n} from './composables/useManualLayoutResize'\nexport {\n useSelectionLimit,\n type SelectionLimitSource,\n type UseSelectionLimitOptions,\n type UseSelectionLimitReturn,\n} from './composables/useSelectionLimit'\nexport {\n useListSelection,\n type ListSelectionSource,\n type ListSelectionValue,\n type UseListSelectionOptions,\n type UseListSelectionReturn,\n type WidenListSelectionValue,\n} from './composables/useListSelection'\nexport {\n useExpansionSet,\n type ExpansionSetSource,\n type UseExpansionSetOptions,\n type UseExpansionSetReturn,\n} from './composables/useExpansionSet'\nexport {\n compareSortValues,\n useSortedItems,\n type CompareSortValuesOptions,\n type SortComparator,\n type SortComparatorContext,\n type SortDescriptor,\n type SortedItemsSource,\n type SortOrder,\n type UseSortedItemsOptions,\n type UseSortedItemsReturn,\n} from './composables/useSortedItems'\nexport {\n usePluginWorkspace,\n type PluginWorkspaceSlotsLike,\n type UsePluginWorkspaceOptions,\n type UsePluginWorkspaceReturn,\n} from './composables/usePluginWorkspace'\n\n// Onboarding: tours, the Getting started checklist and what's-new hints\nexport {\n ANCHOR_WAIT_MS,\n defineOnboarding,\n provideOnboarding,\n useOnboarding,\n ONBOARDING_KEY,\n type OnboardingActiveStep,\n type OnboardingDefinition,\n type OnboardingNews,\n type OnboardingState,\n type OnboardingStep,\n type OnboardingTask,\n type OnboardingTour,\n type ProvideOnboardingOptions,\n} from './composables/useOnboarding'\n"],"mappings":"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"}
|
package/dist/shell.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { i as useEventListener, r as useFocusTrap } from "./useDropdownState-uFIz6Yv8.js";
|
|
2
|
-
import { E as useMenuKeyboard, H as useListSelection, R as useRovingFocus, U as useSelectionLimit } from "./records-
|
|
3
|
-
import { B as compareSortValues, V as useSortedItems, i as useReorderAnnouncer, n as reorderList, r as useListReorder, t as REORDER_ANNOUNCER_STYLE } from "./useListReorder-
|
|
4
|
-
import { C as usePluginWorkspace, S as AppLayout_default, _ as provideOnboarding, f as AppTopBar_default, g as defineOnboarding, h as ONBOARDING_KEY, m as ANCHOR_WAIT_MS, t as AppSidebar_default, v as useOnboarding } from "./AppSidebar-
|
|
2
|
+
import { E as useMenuKeyboard, H as useListSelection, R as useRovingFocus, U as useSelectionLimit } from "./records-CWRztE5M.js";
|
|
3
|
+
import { B as compareSortValues, V as useSortedItems, i as useReorderAnnouncer, n as reorderList, r as useListReorder, t as REORDER_ANNOUNCER_STYLE } from "./useListReorder-QqJc1dpX.js";
|
|
4
|
+
import { C as usePluginWorkspace, S as AppLayout_default, _ as provideOnboarding, f as AppTopBar_default, g as defineOnboarding, h as ONBOARDING_KEY, m as ANCHOR_WAIT_MS, t as AppSidebar_default, v as useOnboarding } from "./AppSidebar-BDrd7Kwn.js";
|
|
5
5
|
import { t as useDebouncedWatch } from "./useDebouncedWatch-BPwyl6eS.js";
|
|
6
6
|
import { t as useExpansionSet } from "./useExpansionSet-CvoNMZJi.js";
|
|
7
|
-
import { a as useManualLayoutResize, i as resizedVerticalSplitPercent, n as resizedLeadingPanelWidth, o as LayoutResizeHandle_default, r as resizedTrailingPanelWidth } from "./shell-
|
|
7
|
+
import { a as useManualLayoutResize, i as resizedVerticalSplitPercent, n as resizedLeadingPanelWidth, o as LayoutResizeHandle_default, r as resizedTrailingPanelWidth } from "./shell-Ch7cJnjo.js";
|
|
8
8
|
export { ANCHOR_WAIT_MS, AppLayout_default as AppLayout, AppSidebar_default as AppSidebar, AppTopBar_default as AppTopBar, LayoutResizeHandle_default as LayoutResizeHandle, ONBOARDING_KEY, REORDER_ANNOUNCER_STYLE, compareSortValues, defineOnboarding, provideOnboarding, reorderList, resizedLeadingPanelWidth, resizedTrailingPanelWidth, resizedVerticalSplitPercent, useDebouncedWatch, useEventListener, useExpansionSet, useFocusTrap, useListReorder, useListSelection, useManualLayoutResize, useMenuKeyboard, useOnboarding, usePluginWorkspace, useReorderAnnouncer, useRovingFocus, useSelectionLimit, useSortedItems };
|
package/dist/styles.css
CHANGED
|
@@ -11816,11 +11816,6 @@ html.dark .mint-pill--soft {
|
|
|
11816
11816
|
background-color: color-mix(in srgb, var(--color-primary) 8%, transparent) !important;
|
|
11817
11817
|
}
|
|
11818
11818
|
|
|
11819
|
-
/* On the first cell: the cell background would paint over an inset on the row. */
|
|
11820
|
-
.mint-dataframe__row--selected > .mint-dataframe__td:first-child {
|
|
11821
|
-
box-shadow: inset 2px 0 0 var(--color-primary);
|
|
11822
|
-
}
|
|
11823
|
-
|
|
11824
11819
|
.mint-dataframe__row--selected:hover > .mint-dataframe__td {
|
|
11825
11820
|
background-color: color-mix(in srgb, var(--color-primary) 11%, transparent) !important;
|
|
11826
11821
|
}
|
|
@@ -17077,7 +17072,6 @@ button.mint-unit-input__unit:focus-visible {
|
|
|
17077
17072
|
.mint-resource-picker__row--selected,
|
|
17078
17073
|
.mint-resource-picker__row--selected:hover {
|
|
17079
17074
|
background-color: var(--color-primary-soft);
|
|
17080
|
-
box-shadow: inset 2px 0 0 var(--color-primary);
|
|
17081
17075
|
}
|
|
17082
17076
|
/* Keyboard focus is the ring alone; it never adds a fill or a bar. */
|
|
17083
17077
|
.mint-resource-picker__row--active {
|
|
@@ -17113,6 +17107,10 @@ button.mint-unit-input__unit:focus-visible {
|
|
|
17113
17107
|
line-height: 1.25;
|
|
17114
17108
|
white-space: nowrap;
|
|
17115
17109
|
}
|
|
17110
|
+
.mint-resource-picker__check {
|
|
17111
|
+
flex-shrink: 0;
|
|
17112
|
+
color: var(--_ink-primary);
|
|
17113
|
+
}
|
|
17116
17114
|
.mint-resource-picker__date {
|
|
17117
17115
|
font: 12px var(--font-mono);
|
|
17118
17116
|
font-variant-numeric: tabular-nums;
|
|
@@ -17471,7 +17469,6 @@ button.mint-unit-input__unit:focus-visible {
|
|
|
17471
17469
|
|
|
17472
17470
|
.mint-artifact-save-dialog__row--selected {
|
|
17473
17471
|
background: var(--color-primary-soft);
|
|
17474
|
-
box-shadow: inset 2px 0 0 var(--color-primary);
|
|
17475
17472
|
}
|
|
17476
17473
|
|
|
17477
17474
|
.mint-artifact-save-dialog__row-icon {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { o as useApi, r as readErrorMessage } from "./experiment-DyO54FQB.js";
|
|
2
|
-
import { _ as APP_EXPERIMENT_KEY, a as parseExperimentId, t as isRecord } from "./records-
|
|
3
|
-
import { t as useRequest } from "./useRequest-
|
|
2
|
+
import { _ as APP_EXPERIMENT_KEY, a as parseExperimentId, t as isRecord } from "./records-CWRztE5M.js";
|
|
3
|
+
import { t as useRequest } from "./useRequest-Bfsh7gIm.js";
|
|
4
4
|
import { computed, inject, ref, shallowRef, toValue, watch } from "vue";
|
|
5
5
|
//#region src/composables/experimentDesignData.ts
|
|
6
6
|
function isPlatformDesignDataResponse(value) {
|
|
@@ -405,4 +405,4 @@ function useExperimentSamples(options = {}) {
|
|
|
405
405
|
//#endregion
|
|
406
406
|
export { readTableFileAsCsv as a, useExperimentData as c, unwrapExperimentDesignData as d, readFileAsText as i, extractSampleNamesFromDesignData as l, detectDelimiter as n, useFileImport as o, parseDelimitedText as r, validateImportFile as s, useExperimentSamples as t, extractSampleOptionsFromDesignData as u };
|
|
407
407
|
|
|
408
|
-
//# sourceMappingURL=useExperimentSamples-
|
|
408
|
+
//# sourceMappingURL=useExperimentSamples-Cz2v3V_q.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useExperimentSamples-QEFoq4tf.js","names":[],"sources":["../src/composables/experimentDesignData.ts","../src/composables/useExperimentData.ts","../src/composables/useFileImport.ts","../src/composables/useExperimentSamples.ts"],"sourcesContent":["import type { SelectOption } from '../types'\nimport { isRecord } from '../utils/records'\n\nexport interface ExperimentDesignDataResponse {\n experiment_id?: number\n plugin_id?: string\n data?: Record<string, unknown>\n schema_version?: string\n updated_at?: string | null\n}\n\nexport interface ExtractExperimentSamplesOptions {\n includeControls?: boolean\n}\n\n\nfunction isPlatformDesignDataResponse(value: Record<string, unknown>): boolean {\n return (\n 'data' in value\n && (\n 'experiment_id' in value\n || 'plugin_id' in value\n || 'schema_version' in value\n || 'updated_at' in value\n )\n )\n}\n\n/** Return the plugin-defined design_data payload from common platform and plugin response shapes. */\nexport function unwrapExperimentDesignData(\n rawData: Record<string, unknown> | null | undefined,\n): Record<string, unknown> | null {\n if (!rawData) return null\n\n if (isRecord(rawData.data) && isPlatformDesignDataResponse(rawData)) {\n return rawData.data\n }\n\n if (isRecord(rawData.design_data)) {\n return rawData.design_data\n }\n\n if (isRecord(rawData.designData)) {\n return rawData.designData\n }\n\n return rawData\n}\n\nfunction shouldIncludeSample(record: Record<string, unknown>, options: ExtractExperimentSamplesOptions): boolean {\n if (options.includeControls) return true\n const rawType = record.sample_type ?? record.sampleType ?? record.type ?? record.well_type ?? record.wellType\n const type = typeof rawType === 'string' ? rawType.toLowerCase() : ''\n return !['blank', 'qc'].includes(type)\n}\n\nfunction readSampleValue(record: Record<string, unknown>): string | null {\n const candidates = [\n record.sample_name,\n record.sampleName,\n record.sample_id,\n record.sampleId,\n record.name,\n record.id,\n record.label,\n ]\n\n for (const candidate of candidates) {\n if (typeof candidate === 'string' && candidate.trim()) {\n return candidate\n }\n if (typeof candidate === 'number' && Number.isFinite(candidate)) {\n return String(candidate)\n }\n }\n\n return null\n}\n\nfunction readSampleLabel(record: Record<string, unknown>, fallback: string): string {\n const label = record.name ?? record.label ?? record.sample_name ?? record.sampleName\n return typeof label === 'string' && label.trim() ? label : fallback\n}\n\nfunction readSampleDescription(record: Record<string, unknown>): string | undefined {\n const parts = [\n record.group,\n record.batch,\n record.batch_id,\n record.batchId,\n record.plate_id,\n record.plateId,\n record.well_id,\n record.wellId,\n ].filter((value): value is string | number =>\n (typeof value === 'string' && value.trim().length > 0)\n || typeof value === 'number',\n )\n\n return parts.length > 0 ? parts.map(String).join(' / ') : undefined\n}\n\nfunction addSampleRecord(\n options: SelectOption<string>[],\n seen: Set<string>,\n value: unknown,\n extractOptions: ExtractExperimentSamplesOptions,\n): void {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed && !seen.has(trimmed)) {\n seen.add(trimmed)\n options.push({ value: trimmed, label: trimmed })\n }\n return\n }\n\n if (!isRecord(value) || !shouldIncludeSample(value, extractOptions)) return\n\n const sampleValue = readSampleValue(value)\n if (!sampleValue || seen.has(sampleValue)) return\n\n seen.add(sampleValue)\n options.push({\n value: sampleValue,\n label: readSampleLabel(value, sampleValue),\n description: readSampleDescription(value),\n })\n}\n\nfunction addSampleArray(\n options: SelectOption<string>[],\n seen: Set<string>,\n value: unknown,\n extractOptions: ExtractExperimentSamplesOptions,\n): void {\n if (!Array.isArray(value)) return\n for (const sample of value) {\n addSampleRecord(options, seen, sample, extractOptions)\n }\n}\n\n/** Extract selectable sample options from raw or wrapped experiment design_data. */\nexport function extractSampleOptionsFromDesignData(\n rawData: Record<string, unknown> | null | undefined,\n options: ExtractExperimentSamplesOptions = {},\n): SelectOption<string>[] {\n const designData = unwrapExperimentDesignData(rawData)\n if (!designData) return []\n\n const sampleOptions: SelectOption<string>[] = []\n const seen = new Set<string>()\n\n addSampleArray(sampleOptions, seen, designData.samples, options)\n\n if (isRecord(designData.templates)) {\n for (const template of Object.values(designData.templates)) {\n if (!isRecord(template)) continue\n const templateData = isRecord(template.data) ? template.data : template\n addSampleArray(sampleOptions, seen, templateData.samples, options)\n }\n }\n\n if (Array.isArray(designData.plates)) {\n for (const plate of designData.plates) {\n if (!isRecord(plate)) continue\n addSampleArray(sampleOptions, seen, plate.samples, options)\n }\n }\n\n return sampleOptions\n}\n\n/** Extract SampleSelector-compatible string values from raw or wrapped experiment design_data. */\nexport function extractSampleNamesFromDesignData(\n rawData: Record<string, unknown> | null | undefined,\n options: ExtractExperimentSamplesOptions = {},\n): string[] {\n return extractSampleOptionsFromDesignData(rawData, options).map(sample => sample.value)\n}\n","import { shallowRef, computed, type Ref, type ComputedRef } from 'vue'\nimport { useApi, type UseApiReturn } from './useApi'\nimport { useRequest } from './useRequest'\nimport {\n extractSampleNamesFromDesignData,\n extractSampleOptionsFromDesignData,\n unwrapExperimentDesignData,\n} from './experimentDesignData'\nimport type { TreeNode, SummaryData, SelectOption } from '../types'\n\nexport interface UseExperimentDataOptions {\n apiBaseUrl?: string\n immediate?: boolean\n}\n\nexport interface UseExperimentDataReturn {\n /** Raw experiment data response from `/experiments/{id}/data`, or null. */\n data: Ref<Record<string, unknown> | null>\n /** Plugin-defined design_data payload unwrapped from platform response shapes, or null. */\n designData: ComputedRef<Record<string, unknown> | null>\n /** Sample values extracted from design_data for SampleSelector-style components. */\n sampleNames: ComputedRef<string[]>\n /** Sample options extracted from design_data for select/dropdown components. */\n sampleOptions: ComputedRef<SelectOption<string>[]>\n /** Hierarchy tree rows normalised from `tree_data` or `treeData`. */\n treeData: ComputedRef<TreeNode[]>\n /** Table rows normalised from `table_data` or `tableData`. */\n tableData: ComputedRef<Record<string, unknown>[]>\n /** Summary payload normalised from `summary_data` or `summaryData`, or null. */\n summaryData: ComputedRef<SummaryData | null>\n /** Whether experiment data is currently loading. */\n isLoading: Ref<boolean>\n /** Error message from the last failed fetch, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful experiment data fetch, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Fetch experiment data for an experiment id. */\n fetch: (experimentId: number) => Promise<Record<string, unknown> | null>\n /** Refetch the most recently fetched experiment id. */\n refresh: () => Promise<void>\n /** Clear local experiment data and current fetch target. */\n clear: () => void\n}\n\n/** Fetches and normalises experiment output data (tree, table, summary) from the platform API. */\nexport function useExperimentData(\n options: UseExperimentDataOptions = {},\n): UseExperimentDataReturn {\n const data = shallowRef<Record<string, unknown> | null>(null)\n const request = useRequest('Failed to fetch experiment data')\n const isLoading = request.isLoading\n const error = request.error\n const lastLoadedAt = request.lastLoadedAt\n let lastExperimentId: number | null = null\n let api: UseApiReturn | null = null\n\n function getApi(): UseApiReturn {\n api ??= useApi({ baseUrl: options.apiBaseUrl })\n return api\n }\n\n const designData = computed<Record<string, unknown> | null>(() =>\n unwrapExperimentDesignData(data.value),\n )\n\n const sampleNames = computed<string[]>(() =>\n extractSampleNamesFromDesignData(designData.value),\n )\n\n const sampleOptions = computed<SelectOption<string>[]>(() =>\n extractSampleOptionsFromDesignData(designData.value),\n )\n\n const treeData = computed<TreeNode[]>(() => {\n if (!designData.value) return []\n const tree = designData.value.tree_data ?? designData.value.treeData\n return Array.isArray(tree) ? tree as TreeNode[] : []\n })\n\n const tableData = computed<Record<string, unknown>[]>(() => {\n if (!designData.value) return []\n const table = designData.value.table_data ?? designData.value.tableData\n return Array.isArray(table) ? table as Record<string, unknown>[] : []\n })\n\n const summaryData = computed<SummaryData | null>(() => {\n if (!designData.value) return null\n const summary = designData.value.summary_data ?? designData.value.summaryData\n if (summary && typeof summary === 'object' && 'metadata' in (summary as Record<string, unknown>)) {\n return summary as SummaryData\n }\n return null\n })\n\n async function fetchData(experimentId: number): Promise<Record<string, unknown> | null> {\n lastExperimentId = experimentId\n try {\n data.value = await request.run(\n () => getApi().get<Record<string, unknown>>(`/experiments/${experimentId}/data`),\n { kind: 'load', errorMessage: 'Failed to fetch experiment data' },\n )\n return data.value\n } catch {\n data.value = null\n return null\n }\n }\n\n async function refresh(): Promise<void> {\n if (lastExperimentId !== null) {\n await fetchData(lastExperimentId)\n }\n }\n\n function clear(): void {\n data.value = null\n lastExperimentId = null\n request.clearError()\n }\n\n return {\n data,\n designData,\n sampleNames,\n sampleOptions,\n treeData,\n tableData,\n summaryData,\n isLoading,\n error,\n lastLoadedAt,\n fetch: fetchData,\n refresh,\n clear,\n }\n}\n","import { ref, shallowRef, type Ref, type ShallowRef } from 'vue'\nimport type { SheetData } from 'read-excel-file/universal'\nimport { readErrorMessage } from '../utils/errors'\n\nexport interface DelimitedTable {\n columns: string[]\n rows: Record<string, string>[]\n rawRows: string[][]\n delimiter: string\n}\n\nexport interface ParseDelimitedTextOptions {\n delimiter?: string\n headers?: readonly string[]\n hasHeaderRow?: boolean\n trim?: boolean\n}\n\nexport interface FileImportValidationOptions {\n accept?: string | readonly string[]\n maxSizeBytes?: number\n}\n\nexport interface FileImportOptions<TResult = string> extends FileImportValidationOptions {\n encoding?: string\n parse?: (text: string, file: File) => TResult | Promise<TResult>\n}\n\nexport interface UseFileImportReturn<TResult> {\n loading: ShallowRef<boolean>\n error: Ref<string | null>\n fileName: Ref<string>\n result: Ref<TResult | null>\n importFile: (file: File) => Promise<TResult>\n clear: () => void\n}\n\n/** Stateful file reader for plugin import flows with shared validation and error handling. */\nexport function useFileImport<TResult = string>(\n options: FileImportOptions<TResult> = {},\n): UseFileImportReturn<TResult> {\n const loading = shallowRef(false)\n const error = ref<string | null>(null)\n const fileName = ref('')\n const result = ref<TResult | null>(null) as Ref<TResult | null>\n\n function clear(): void {\n loading.value = false\n error.value = null\n fileName.value = ''\n result.value = null\n }\n\n async function importFile(file: File): Promise<TResult> {\n loading.value = true\n error.value = null\n fileName.value = file.name\n\n try {\n validateImportFile(file, options)\n const text = await readFileAsText(file, options.encoding)\n const parsed = options.parse\n ? await options.parse(text, file)\n : text as TResult\n result.value = parsed\n return parsed\n } catch (err) {\n const message = readErrorMessage(err, 'Failed to import file.')\n error.value = message\n throw err\n } finally {\n loading.value = false\n }\n }\n\n return {\n loading,\n error,\n fileName,\n result,\n importFile,\n clear,\n }\n}\n\nexport function validateImportFile(file: File, options: FileImportValidationOptions = {}): void {\n if (options.maxSizeBytes !== undefined && file.size > options.maxSizeBytes) {\n throw new Error(`File is too large. Maximum size is ${formatBytes(options.maxSizeBytes)}.`)\n }\n\n const accept = normalizeAccept(options.accept)\n if (accept.length === 0) return\n\n const name = file.name.toLowerCase()\n const type = file.type.toLowerCase()\n const matches = accept.some((entry) => {\n if (entry.startsWith('.')) return name.endsWith(entry)\n if (entry.endsWith('/*')) return type.startsWith(entry.slice(0, -1))\n return type === entry\n })\n\n if (!matches) {\n throw new Error(`Unsupported file type. Expected ${accept.join(', ')}.`)\n }\n}\n\nexport function readFileAsText(file: File, encoding?: string): Promise<string> {\n if (typeof FileReader === 'undefined' && typeof file.text === 'function') {\n return file.text()\n }\n\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(String(reader.result ?? ''))\n reader.onerror = () => reject(reader.error ?? new Error('Failed to read file.'))\n reader.readAsText(file, encoding)\n })\n}\n\n/**\n * Read a `.csv`, `.tsv` or `.xlsx` upload as comma-delimited CSV text.\n * A `.csv` file (or any other extension) comes back unchanged. An `.xlsx`\n * file gives its first sheet; each cell is its stored value, so number\n * formats such as leading zeros are not applied. Dates become ISO text.\n */\nexport async function readTableFileAsCsv(file: File): Promise<string> {\n const name = file.name.toLowerCase()\n if (name.endsWith('.xlsx')) return rowsToCsv(await readFirstSheet(file))\n const text = await readFileAsText(file)\n return name.endsWith('.tsv') ? rowsToCsv(parseDelimitedRows(text, '\\t')) : text\n}\n\nasync function readFirstSheet(file: File): Promise<string[][]> {\n // Lazy import: plugins that never read .xlsx do not load the library.\n const { readSheet } = await import('read-excel-file/universal')\n let rows: SheetData<string>\n try {\n // Keep the stored text: no trim, and no float parse that rounds big numbers.\n rows = await readSheet(file, { trim: false, parseNumber: text => text })\n } catch {\n throw new Error(`${file.name} is not a valid .xlsx workbook.`)\n }\n return rows.map(row => row.map(formatCell))\n}\n\nfunction formatCell(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'boolean') return value ? 'TRUE' : 'FALSE'\n if (value instanceof Date) {\n // Excel stores dates as float day counts; round off the float error.\n const [date, time] = new Date(Math.round(value.getTime() / 1000) * 1000)\n .toISOString().slice(0, 19).split('T')\n if (date === '1899-12-30') return time // a time-only cell\n return time === '00:00:00' ? date : `${date}T${time}`\n }\n return String(value)\n}\n\nfunction rowsToCsv(rows: readonly (readonly string[])[]): string {\n return rows.map(row => row.map(quoteCsvField).join(',')).join('\\n')\n}\n\nfunction quoteCsvField(value: string): string {\n return /[\",\\r\\n]/.test(value) ? `\"${value.replace(/\"/g, '\"\"')}\"` : value\n}\n\nexport function parseDelimitedText(\n text: string,\n options: ParseDelimitedTextOptions = {},\n): DelimitedTable {\n const trim = options.trim ?? true\n const delimiter = options.delimiter ?? detectDelimiter(text)\n const rawRows = parseDelimitedRows(text, delimiter)\n .filter(row => row.some(cell => cell.trim().length > 0))\n .map(row => trim ? row.map(cell => cell.trim()) : row)\n\n const hasHeaderRow = options.hasHeaderRow ?? !options.headers\n const columns = options.headers\n ? [...options.headers]\n : hasHeaderRow\n ? rawRows[0] ?? []\n : createColumnNames(maxRowLength(rawRows))\n\n const dataRows = hasHeaderRow && !options.headers ? rawRows.slice(1) : rawRows\n const rows = dataRows.map(row => Object.fromEntries(\n columns.map((column, index) => [column, row[index] ?? '']),\n ))\n\n return {\n columns,\n rows,\n rawRows,\n delimiter,\n }\n}\n\nexport function detectDelimiter(text: string): string {\n const firstLine = text.split(/\\r?\\n/, 1)[0] ?? ''\n const commaCount = countChar(firstLine, ',')\n const tabCount = countChar(firstLine, '\\t')\n const semicolonCount = countChar(firstLine, ';')\n if (tabCount > commaCount && tabCount >= semicolonCount) return '\\t'\n if (semicolonCount > commaCount) return ';'\n return ','\n}\n\nfunction parseDelimitedRows(text: string, delimiter: string): string[][] {\n const rows: string[][] = []\n let row: string[] = []\n let field = ''\n let quoted = false\n\n for (let index = 0; index < text.length; index++) {\n const char = text[index]\n const next = text[index + 1]\n\n if (char === '\"') {\n if (quoted && next === '\"') {\n field += '\"'\n index++\n } else if (quoted || field === '') {\n quoted = !quoted\n } else {\n field += '\"' // a quote inside an unquoted field is text\n }\n continue\n }\n\n if (!quoted && char === delimiter) {\n row.push(field)\n field = ''\n continue\n }\n\n if (!quoted && (char === '\\n' || char === '\\r')) {\n if (char === '\\r' && next === '\\n') index++\n row.push(field)\n rows.push(row)\n row = []\n field = ''\n continue\n }\n\n field += char\n }\n\n row.push(field)\n rows.push(row)\n return rows\n}\n\nfunction normalizeAccept(accept: string | readonly string[] | undefined): string[] {\n if (!accept) return []\n const entries = typeof accept === 'string' ? accept.split(',') : [...accept]\n return entries\n .map((entry: string) => entry.trim().toLowerCase())\n .filter(Boolean)\n}\n\nfunction createColumnNames(count: number): string[] {\n return Array.from({ length: count }, (_, index) => `column_${index + 1}`)\n}\n\nfunction maxRowLength(rows: readonly string[][]): number {\n return rows.reduce((max, row) => Math.max(max, row.length), 0)\n}\n\nfunction countChar(value: string, char: string): number {\n return [...value].filter(candidate => candidate === char).length\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`\n return `${Math.round(bytes / (1024 * 1024))} MB`\n}\n","import {\n computed,\n inject,\n toValue,\n watch,\n type ComputedRef,\n type MaybeRefOrGetter,\n type Ref,\n} from 'vue'\nimport type { SelectOption } from '../types'\nimport { APP_EXPERIMENT_KEY } from './useAppExperiment'\nimport {\n extractSampleNamesFromDesignData,\n extractSampleOptionsFromDesignData,\n unwrapExperimentDesignData,\n type ExtractExperimentSamplesOptions,\n} from './experimentDesignData'\nimport { parseExperimentId } from './platformContextHelpers'\nimport { useExperimentData } from './useExperimentData'\n\nexport type ExperimentIdSource = MaybeRefOrGetter<number | string | null | undefined>\nexport type ExperimentDesignDataSource = MaybeRefOrGetter<Record<string, unknown> | null | undefined>\n\nexport interface UseExperimentSamplesOptions extends ExtractExperimentSamplesOptions {\n /** Experiment id to load from `/experiments/{id}/data`; defaults to provided AppExperiment context. */\n experimentId?: ExperimentIdSource\n /** Pre-fetched design_data or full platform design-data response. */\n designData?: ExperimentDesignDataSource\n /** API base URL override for platform calls. */\n apiBaseUrl?: string\n /** Auto-load when an experiment id becomes available. Defaults to true. */\n immediate?: boolean\n /** Enable or disable automatic loading while keeping derived samples reactive. Defaults to true. */\n enabled?: MaybeRefOrGetter<boolean>\n /** Use `useAppExperiment` provided state when `experimentId` is not set. Defaults to true. */\n syncAppExperiment?: boolean\n}\n\nexport interface UseExperimentSamplesReturn {\n /** Experiment id currently used for automatic sample loading. */\n experimentId: ComputedRef<number | undefined>\n /** Raw `/experiments/{id}/data` response when loaded through the platform API. */\n data: Ref<Record<string, unknown> | null>\n /** Plugin-defined design_data payload from props or platform response. */\n designData: ComputedRef<Record<string, unknown> | null>\n /** SampleSelector-compatible string values extracted from design_data. */\n samples: ComputedRef<string[]>\n /** Select/dropdown-friendly sample options extracted from design_data. */\n sampleOptions: ComputedRef<SelectOption<string>[]>\n isLoading: Ref<boolean>\n error: Ref<string | null>\n lastLoadedAt: Ref<Date | null>\n /** Fetch samples for a specific experiment id, defaulting to the resolved id. */\n fetch: (experimentId?: number | string | null) => Promise<Record<string, unknown> | null>\n /** Refetch the current experiment id. */\n refresh: () => Promise<void>\n /** Clear fetched platform data. */\n clear: () => void\n}\n\n/** Loads experiment design_data and derives samples for SampleSelector-style UIs. */\nexport function useExperimentSamples(\n options: UseExperimentSamplesOptions = {},\n): UseExperimentSamplesReturn {\n const appExperiment = options.syncAppExperiment === false\n ? undefined\n : inject(APP_EXPERIMENT_KEY, undefined)\n const experimentData = useExperimentData({ apiBaseUrl: options.apiBaseUrl })\n\n const experimentId = computed<number | undefined>(() => {\n const explicitId = parseExperimentId(toValue(options.experimentId))\n return explicitId ?? parseExperimentId(appExperiment?.experimentId.value)\n })\n\n const explicitDesignData = computed<Record<string, unknown> | null>(() =>\n unwrapExperimentDesignData(toValue(options.designData) ?? null),\n )\n\n const designData = computed<Record<string, unknown> | null>(() =>\n explicitDesignData.value ?? experimentData.designData.value,\n )\n\n const extractOptions = computed<ExtractExperimentSamplesOptions>(() => ({\n includeControls: options.includeControls,\n }))\n\n const samples = computed<string[]>(() =>\n extractSampleNamesFromDesignData(designData.value, extractOptions.value),\n )\n\n const sampleOptions = computed<SelectOption<string>[]>(() =>\n extractSampleOptionsFromDesignData(designData.value, extractOptions.value),\n )\n\n const enabled = computed(() => toValue(options.enabled) ?? true)\n\n async function fetchSamples(\n id: number | string | null | undefined = experimentId.value,\n ): Promise<Record<string, unknown> | null> {\n const parsedId = parseExperimentId(id)\n if (parsedId === undefined) {\n experimentData.clear()\n return null\n }\n return experimentData.fetch(parsedId)\n }\n\n watch(\n () => ({\n designData: explicitDesignData.value,\n enabled: enabled.value,\n experimentId: experimentId.value,\n }),\n ({ designData: currentDesignData, enabled: isEnabled, experimentId: currentExperimentId }) => {\n if (options.immediate === false || !isEnabled) return\n if (currentDesignData) {\n experimentData.clear()\n return\n }\n if (currentExperimentId === undefined) {\n experimentData.clear()\n return\n }\n void experimentData.fetch(currentExperimentId)\n },\n { immediate: options.immediate !== false },\n )\n\n return {\n experimentId,\n data: experimentData.data,\n designData,\n samples,\n sampleOptions,\n isLoading: experimentData.isLoading,\n error: experimentData.error,\n lastLoadedAt: experimentData.lastLoadedAt,\n fetch: fetchSamples,\n refresh: experimentData.refresh,\n clear: experimentData.clear,\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useExperimentSamples-Cz2v3V_q.js","names":[],"sources":["../src/composables/experimentDesignData.ts","../src/composables/useExperimentData.ts","../src/composables/useFileImport.ts","../src/composables/useExperimentSamples.ts"],"sourcesContent":["import type { SelectOption } from '../types'\nimport { isRecord } from '../utils/records'\n\nexport interface ExperimentDesignDataResponse {\n experiment_id?: number\n plugin_id?: string\n data?: Record<string, unknown>\n schema_version?: string\n updated_at?: string | null\n}\n\nexport interface ExtractExperimentSamplesOptions {\n includeControls?: boolean\n}\n\n\nfunction isPlatformDesignDataResponse(value: Record<string, unknown>): boolean {\n return (\n 'data' in value\n && (\n 'experiment_id' in value\n || 'plugin_id' in value\n || 'schema_version' in value\n || 'updated_at' in value\n )\n )\n}\n\n/** Return the plugin-defined design_data payload from common platform and plugin response shapes. */\nexport function unwrapExperimentDesignData(\n rawData: Record<string, unknown> | null | undefined,\n): Record<string, unknown> | null {\n if (!rawData) return null\n\n if (isRecord(rawData.data) && isPlatformDesignDataResponse(rawData)) {\n return rawData.data\n }\n\n if (isRecord(rawData.design_data)) {\n return rawData.design_data\n }\n\n if (isRecord(rawData.designData)) {\n return rawData.designData\n }\n\n return rawData\n}\n\nfunction shouldIncludeSample(record: Record<string, unknown>, options: ExtractExperimentSamplesOptions): boolean {\n if (options.includeControls) return true\n const rawType = record.sample_type ?? record.sampleType ?? record.type ?? record.well_type ?? record.wellType\n const type = typeof rawType === 'string' ? rawType.toLowerCase() : ''\n return !['blank', 'qc'].includes(type)\n}\n\nfunction readSampleValue(record: Record<string, unknown>): string | null {\n const candidates = [\n record.sample_name,\n record.sampleName,\n record.sample_id,\n record.sampleId,\n record.name,\n record.id,\n record.label,\n ]\n\n for (const candidate of candidates) {\n if (typeof candidate === 'string' && candidate.trim()) {\n return candidate\n }\n if (typeof candidate === 'number' && Number.isFinite(candidate)) {\n return String(candidate)\n }\n }\n\n return null\n}\n\nfunction readSampleLabel(record: Record<string, unknown>, fallback: string): string {\n const label = record.name ?? record.label ?? record.sample_name ?? record.sampleName\n return typeof label === 'string' && label.trim() ? label : fallback\n}\n\nfunction readSampleDescription(record: Record<string, unknown>): string | undefined {\n const parts = [\n record.group,\n record.batch,\n record.batch_id,\n record.batchId,\n record.plate_id,\n record.plateId,\n record.well_id,\n record.wellId,\n ].filter((value): value is string | number =>\n (typeof value === 'string' && value.trim().length > 0)\n || typeof value === 'number',\n )\n\n return parts.length > 0 ? parts.map(String).join(' / ') : undefined\n}\n\nfunction addSampleRecord(\n options: SelectOption<string>[],\n seen: Set<string>,\n value: unknown,\n extractOptions: ExtractExperimentSamplesOptions,\n): void {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed && !seen.has(trimmed)) {\n seen.add(trimmed)\n options.push({ value: trimmed, label: trimmed })\n }\n return\n }\n\n if (!isRecord(value) || !shouldIncludeSample(value, extractOptions)) return\n\n const sampleValue = readSampleValue(value)\n if (!sampleValue || seen.has(sampleValue)) return\n\n seen.add(sampleValue)\n options.push({\n value: sampleValue,\n label: readSampleLabel(value, sampleValue),\n description: readSampleDescription(value),\n })\n}\n\nfunction addSampleArray(\n options: SelectOption<string>[],\n seen: Set<string>,\n value: unknown,\n extractOptions: ExtractExperimentSamplesOptions,\n): void {\n if (!Array.isArray(value)) return\n for (const sample of value) {\n addSampleRecord(options, seen, sample, extractOptions)\n }\n}\n\n/** Extract selectable sample options from raw or wrapped experiment design_data. */\nexport function extractSampleOptionsFromDesignData(\n rawData: Record<string, unknown> | null | undefined,\n options: ExtractExperimentSamplesOptions = {},\n): SelectOption<string>[] {\n const designData = unwrapExperimentDesignData(rawData)\n if (!designData) return []\n\n const sampleOptions: SelectOption<string>[] = []\n const seen = new Set<string>()\n\n addSampleArray(sampleOptions, seen, designData.samples, options)\n\n if (isRecord(designData.templates)) {\n for (const template of Object.values(designData.templates)) {\n if (!isRecord(template)) continue\n const templateData = isRecord(template.data) ? template.data : template\n addSampleArray(sampleOptions, seen, templateData.samples, options)\n }\n }\n\n if (Array.isArray(designData.plates)) {\n for (const plate of designData.plates) {\n if (!isRecord(plate)) continue\n addSampleArray(sampleOptions, seen, plate.samples, options)\n }\n }\n\n return sampleOptions\n}\n\n/** Extract SampleSelector-compatible string values from raw or wrapped experiment design_data. */\nexport function extractSampleNamesFromDesignData(\n rawData: Record<string, unknown> | null | undefined,\n options: ExtractExperimentSamplesOptions = {},\n): string[] {\n return extractSampleOptionsFromDesignData(rawData, options).map(sample => sample.value)\n}\n","import { shallowRef, computed, type Ref, type ComputedRef } from 'vue'\nimport { useApi, type UseApiReturn } from './useApi'\nimport { useRequest } from './useRequest'\nimport {\n extractSampleNamesFromDesignData,\n extractSampleOptionsFromDesignData,\n unwrapExperimentDesignData,\n} from './experimentDesignData'\nimport type { TreeNode, SummaryData, SelectOption } from '../types'\n\nexport interface UseExperimentDataOptions {\n apiBaseUrl?: string\n immediate?: boolean\n}\n\nexport interface UseExperimentDataReturn {\n /** Raw experiment data response from `/experiments/{id}/data`, or null. */\n data: Ref<Record<string, unknown> | null>\n /** Plugin-defined design_data payload unwrapped from platform response shapes, or null. */\n designData: ComputedRef<Record<string, unknown> | null>\n /** Sample values extracted from design_data for SampleSelector-style components. */\n sampleNames: ComputedRef<string[]>\n /** Sample options extracted from design_data for select/dropdown components. */\n sampleOptions: ComputedRef<SelectOption<string>[]>\n /** Hierarchy tree rows normalised from `tree_data` or `treeData`. */\n treeData: ComputedRef<TreeNode[]>\n /** Table rows normalised from `table_data` or `tableData`. */\n tableData: ComputedRef<Record<string, unknown>[]>\n /** Summary payload normalised from `summary_data` or `summaryData`, or null. */\n summaryData: ComputedRef<SummaryData | null>\n /** Whether experiment data is currently loading. */\n isLoading: Ref<boolean>\n /** Error message from the last failed fetch, or null. */\n error: Ref<string | null>\n /** Timestamp of the last successful experiment data fetch, or null. */\n lastLoadedAt: Ref<Date | null>\n /** Fetch experiment data for an experiment id. */\n fetch: (experimentId: number) => Promise<Record<string, unknown> | null>\n /** Refetch the most recently fetched experiment id. */\n refresh: () => Promise<void>\n /** Clear local experiment data and current fetch target. */\n clear: () => void\n}\n\n/** Fetches and normalises experiment output data (tree, table, summary) from the platform API. */\nexport function useExperimentData(\n options: UseExperimentDataOptions = {},\n): UseExperimentDataReturn {\n const data = shallowRef<Record<string, unknown> | null>(null)\n const request = useRequest('Failed to fetch experiment data')\n const isLoading = request.isLoading\n const error = request.error\n const lastLoadedAt = request.lastLoadedAt\n let lastExperimentId: number | null = null\n let api: UseApiReturn | null = null\n\n function getApi(): UseApiReturn {\n api ??= useApi({ baseUrl: options.apiBaseUrl })\n return api\n }\n\n const designData = computed<Record<string, unknown> | null>(() =>\n unwrapExperimentDesignData(data.value),\n )\n\n const sampleNames = computed<string[]>(() =>\n extractSampleNamesFromDesignData(designData.value),\n )\n\n const sampleOptions = computed<SelectOption<string>[]>(() =>\n extractSampleOptionsFromDesignData(designData.value),\n )\n\n const treeData = computed<TreeNode[]>(() => {\n if (!designData.value) return []\n const tree = designData.value.tree_data ?? designData.value.treeData\n return Array.isArray(tree) ? tree as TreeNode[] : []\n })\n\n const tableData = computed<Record<string, unknown>[]>(() => {\n if (!designData.value) return []\n const table = designData.value.table_data ?? designData.value.tableData\n return Array.isArray(table) ? table as Record<string, unknown>[] : []\n })\n\n const summaryData = computed<SummaryData | null>(() => {\n if (!designData.value) return null\n const summary = designData.value.summary_data ?? designData.value.summaryData\n if (summary && typeof summary === 'object' && 'metadata' in (summary as Record<string, unknown>)) {\n return summary as SummaryData\n }\n return null\n })\n\n async function fetchData(experimentId: number): Promise<Record<string, unknown> | null> {\n lastExperimentId = experimentId\n try {\n data.value = await request.run(\n () => getApi().get<Record<string, unknown>>(`/experiments/${experimentId}/data`),\n { kind: 'load', errorMessage: 'Failed to fetch experiment data' },\n )\n return data.value\n } catch {\n data.value = null\n return null\n }\n }\n\n async function refresh(): Promise<void> {\n if (lastExperimentId !== null) {\n await fetchData(lastExperimentId)\n }\n }\n\n function clear(): void {\n data.value = null\n lastExperimentId = null\n request.clearError()\n }\n\n return {\n data,\n designData,\n sampleNames,\n sampleOptions,\n treeData,\n tableData,\n summaryData,\n isLoading,\n error,\n lastLoadedAt,\n fetch: fetchData,\n refresh,\n clear,\n }\n}\n","import { ref, shallowRef, type Ref, type ShallowRef } from 'vue'\nimport type { SheetData } from 'read-excel-file/universal'\nimport { readErrorMessage } from '../utils/errors'\n\nexport interface DelimitedTable {\n columns: string[]\n rows: Record<string, string>[]\n rawRows: string[][]\n delimiter: string\n}\n\nexport interface ParseDelimitedTextOptions {\n delimiter?: string\n headers?: readonly string[]\n hasHeaderRow?: boolean\n trim?: boolean\n}\n\nexport interface FileImportValidationOptions {\n accept?: string | readonly string[]\n maxSizeBytes?: number\n}\n\nexport interface FileImportOptions<TResult = string> extends FileImportValidationOptions {\n encoding?: string\n parse?: (text: string, file: File) => TResult | Promise<TResult>\n}\n\nexport interface UseFileImportReturn<TResult> {\n loading: ShallowRef<boolean>\n error: Ref<string | null>\n fileName: Ref<string>\n result: Ref<TResult | null>\n importFile: (file: File) => Promise<TResult>\n clear: () => void\n}\n\n/** Stateful file reader for plugin import flows with shared validation and error handling. */\nexport function useFileImport<TResult = string>(\n options: FileImportOptions<TResult> = {},\n): UseFileImportReturn<TResult> {\n const loading = shallowRef(false)\n const error = ref<string | null>(null)\n const fileName = ref('')\n const result = ref<TResult | null>(null) as Ref<TResult | null>\n\n function clear(): void {\n loading.value = false\n error.value = null\n fileName.value = ''\n result.value = null\n }\n\n async function importFile(file: File): Promise<TResult> {\n loading.value = true\n error.value = null\n fileName.value = file.name\n\n try {\n validateImportFile(file, options)\n const text = await readFileAsText(file, options.encoding)\n const parsed = options.parse\n ? await options.parse(text, file)\n : text as TResult\n result.value = parsed\n return parsed\n } catch (err) {\n const message = readErrorMessage(err, 'Failed to import file.')\n error.value = message\n throw err\n } finally {\n loading.value = false\n }\n }\n\n return {\n loading,\n error,\n fileName,\n result,\n importFile,\n clear,\n }\n}\n\nexport function validateImportFile(file: File, options: FileImportValidationOptions = {}): void {\n if (options.maxSizeBytes !== undefined && file.size > options.maxSizeBytes) {\n throw new Error(`File is too large. Maximum size is ${formatBytes(options.maxSizeBytes)}.`)\n }\n\n const accept = normalizeAccept(options.accept)\n if (accept.length === 0) return\n\n const name = file.name.toLowerCase()\n const type = file.type.toLowerCase()\n const matches = accept.some((entry) => {\n if (entry.startsWith('.')) return name.endsWith(entry)\n if (entry.endsWith('/*')) return type.startsWith(entry.slice(0, -1))\n return type === entry\n })\n\n if (!matches) {\n throw new Error(`Unsupported file type. Expected ${accept.join(', ')}.`)\n }\n}\n\nexport function readFileAsText(file: File, encoding?: string): Promise<string> {\n if (typeof FileReader === 'undefined' && typeof file.text === 'function') {\n return file.text()\n }\n\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(String(reader.result ?? ''))\n reader.onerror = () => reject(reader.error ?? new Error('Failed to read file.'))\n reader.readAsText(file, encoding)\n })\n}\n\n/**\n * Read a `.csv`, `.tsv` or `.xlsx` upload as comma-delimited CSV text.\n * A `.csv` file (or any other extension) comes back unchanged. An `.xlsx`\n * file gives its first sheet; each cell is its stored value, so number\n * formats such as leading zeros are not applied. Dates become ISO text.\n */\nexport async function readTableFileAsCsv(file: File): Promise<string> {\n const name = file.name.toLowerCase()\n if (name.endsWith('.xlsx')) return rowsToCsv(await readFirstSheet(file))\n const text = await readFileAsText(file)\n return name.endsWith('.tsv') ? rowsToCsv(parseDelimitedRows(text, '\\t')) : text\n}\n\nasync function readFirstSheet(file: File): Promise<string[][]> {\n // Lazy import: plugins that never read .xlsx do not load the library.\n const { readSheet } = await import('read-excel-file/universal')\n let rows: SheetData<string>\n try {\n // Keep the stored text: no trim, and no float parse that rounds big numbers.\n rows = await readSheet(file, { trim: false, parseNumber: text => text })\n } catch {\n throw new Error(`${file.name} is not a valid .xlsx workbook.`)\n }\n return rows.map(row => row.map(formatCell))\n}\n\nfunction formatCell(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'boolean') return value ? 'TRUE' : 'FALSE'\n if (value instanceof Date) {\n // Excel stores dates as float day counts; round off the float error.\n const [date, time] = new Date(Math.round(value.getTime() / 1000) * 1000)\n .toISOString().slice(0, 19).split('T')\n if (date === '1899-12-30') return time // a time-only cell\n return time === '00:00:00' ? date : `${date}T${time}`\n }\n return String(value)\n}\n\nfunction rowsToCsv(rows: readonly (readonly string[])[]): string {\n return rows.map(row => row.map(quoteCsvField).join(',')).join('\\n')\n}\n\nfunction quoteCsvField(value: string): string {\n return /[\",\\r\\n]/.test(value) ? `\"${value.replace(/\"/g, '\"\"')}\"` : value\n}\n\nexport function parseDelimitedText(\n text: string,\n options: ParseDelimitedTextOptions = {},\n): DelimitedTable {\n const trim = options.trim ?? true\n const delimiter = options.delimiter ?? detectDelimiter(text)\n const rawRows = parseDelimitedRows(text, delimiter)\n .filter(row => row.some(cell => cell.trim().length > 0))\n .map(row => trim ? row.map(cell => cell.trim()) : row)\n\n const hasHeaderRow = options.hasHeaderRow ?? !options.headers\n const columns = options.headers\n ? [...options.headers]\n : hasHeaderRow\n ? rawRows[0] ?? []\n : createColumnNames(maxRowLength(rawRows))\n\n const dataRows = hasHeaderRow && !options.headers ? rawRows.slice(1) : rawRows\n const rows = dataRows.map(row => Object.fromEntries(\n columns.map((column, index) => [column, row[index] ?? '']),\n ))\n\n return {\n columns,\n rows,\n rawRows,\n delimiter,\n }\n}\n\nexport function detectDelimiter(text: string): string {\n const firstLine = text.split(/\\r?\\n/, 1)[0] ?? ''\n const commaCount = countChar(firstLine, ',')\n const tabCount = countChar(firstLine, '\\t')\n const semicolonCount = countChar(firstLine, ';')\n if (tabCount > commaCount && tabCount >= semicolonCount) return '\\t'\n if (semicolonCount > commaCount) return ';'\n return ','\n}\n\nfunction parseDelimitedRows(text: string, delimiter: string): string[][] {\n const rows: string[][] = []\n let row: string[] = []\n let field = ''\n let quoted = false\n\n for (let index = 0; index < text.length; index++) {\n const char = text[index]\n const next = text[index + 1]\n\n if (char === '\"') {\n if (quoted && next === '\"') {\n field += '\"'\n index++\n } else if (quoted || field === '') {\n quoted = !quoted\n } else {\n field += '\"' // a quote inside an unquoted field is text\n }\n continue\n }\n\n if (!quoted && char === delimiter) {\n row.push(field)\n field = ''\n continue\n }\n\n if (!quoted && (char === '\\n' || char === '\\r')) {\n if (char === '\\r' && next === '\\n') index++\n row.push(field)\n rows.push(row)\n row = []\n field = ''\n continue\n }\n\n field += char\n }\n\n row.push(field)\n rows.push(row)\n return rows\n}\n\nfunction normalizeAccept(accept: string | readonly string[] | undefined): string[] {\n if (!accept) return []\n const entries = typeof accept === 'string' ? accept.split(',') : [...accept]\n return entries\n .map((entry: string) => entry.trim().toLowerCase())\n .filter(Boolean)\n}\n\nfunction createColumnNames(count: number): string[] {\n return Array.from({ length: count }, (_, index) => `column_${index + 1}`)\n}\n\nfunction maxRowLength(rows: readonly string[][]): number {\n return rows.reduce((max, row) => Math.max(max, row.length), 0)\n}\n\nfunction countChar(value: string, char: string): number {\n return [...value].filter(candidate => candidate === char).length\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`\n return `${Math.round(bytes / (1024 * 1024))} MB`\n}\n","import {\n computed,\n inject,\n toValue,\n watch,\n type ComputedRef,\n type MaybeRefOrGetter,\n type Ref,\n} from 'vue'\nimport type { SelectOption } from '../types'\nimport { APP_EXPERIMENT_KEY } from './useAppExperiment'\nimport {\n extractSampleNamesFromDesignData,\n extractSampleOptionsFromDesignData,\n unwrapExperimentDesignData,\n type ExtractExperimentSamplesOptions,\n} from './experimentDesignData'\nimport { parseExperimentId } from './platformContextHelpers'\nimport { useExperimentData } from './useExperimentData'\n\nexport type ExperimentIdSource = MaybeRefOrGetter<number | string | null | undefined>\nexport type ExperimentDesignDataSource = MaybeRefOrGetter<Record<string, unknown> | null | undefined>\n\nexport interface UseExperimentSamplesOptions extends ExtractExperimentSamplesOptions {\n /** Experiment id to load from `/experiments/{id}/data`; defaults to provided AppExperiment context. */\n experimentId?: ExperimentIdSource\n /** Pre-fetched design_data or full platform design-data response. */\n designData?: ExperimentDesignDataSource\n /** API base URL override for platform calls. */\n apiBaseUrl?: string\n /** Auto-load when an experiment id becomes available. Defaults to true. */\n immediate?: boolean\n /** Enable or disable automatic loading while keeping derived samples reactive. Defaults to true. */\n enabled?: MaybeRefOrGetter<boolean>\n /** Use `useAppExperiment` provided state when `experimentId` is not set. Defaults to true. */\n syncAppExperiment?: boolean\n}\n\nexport interface UseExperimentSamplesReturn {\n /** Experiment id currently used for automatic sample loading. */\n experimentId: ComputedRef<number | undefined>\n /** Raw `/experiments/{id}/data` response when loaded through the platform API. */\n data: Ref<Record<string, unknown> | null>\n /** Plugin-defined design_data payload from props or platform response. */\n designData: ComputedRef<Record<string, unknown> | null>\n /** SampleSelector-compatible string values extracted from design_data. */\n samples: ComputedRef<string[]>\n /** Select/dropdown-friendly sample options extracted from design_data. */\n sampleOptions: ComputedRef<SelectOption<string>[]>\n isLoading: Ref<boolean>\n error: Ref<string | null>\n lastLoadedAt: Ref<Date | null>\n /** Fetch samples for a specific experiment id, defaulting to the resolved id. */\n fetch: (experimentId?: number | string | null) => Promise<Record<string, unknown> | null>\n /** Refetch the current experiment id. */\n refresh: () => Promise<void>\n /** Clear fetched platform data. */\n clear: () => void\n}\n\n/** Loads experiment design_data and derives samples for SampleSelector-style UIs. */\nexport function useExperimentSamples(\n options: UseExperimentSamplesOptions = {},\n): UseExperimentSamplesReturn {\n const appExperiment = options.syncAppExperiment === false\n ? undefined\n : inject(APP_EXPERIMENT_KEY, undefined)\n const experimentData = useExperimentData({ apiBaseUrl: options.apiBaseUrl })\n\n const experimentId = computed<number | undefined>(() => {\n const explicitId = parseExperimentId(toValue(options.experimentId))\n return explicitId ?? parseExperimentId(appExperiment?.experimentId.value)\n })\n\n const explicitDesignData = computed<Record<string, unknown> | null>(() =>\n unwrapExperimentDesignData(toValue(options.designData) ?? null),\n )\n\n const designData = computed<Record<string, unknown> | null>(() =>\n explicitDesignData.value ?? experimentData.designData.value,\n )\n\n const extractOptions = computed<ExtractExperimentSamplesOptions>(() => ({\n includeControls: options.includeControls,\n }))\n\n const samples = computed<string[]>(() =>\n extractSampleNamesFromDesignData(designData.value, extractOptions.value),\n )\n\n const sampleOptions = computed<SelectOption<string>[]>(() =>\n extractSampleOptionsFromDesignData(designData.value, extractOptions.value),\n )\n\n const enabled = computed(() => toValue(options.enabled) ?? true)\n\n async function fetchSamples(\n id: number | string | null | undefined = experimentId.value,\n ): Promise<Record<string, unknown> | null> {\n const parsedId = parseExperimentId(id)\n if (parsedId === undefined) {\n experimentData.clear()\n return null\n }\n return experimentData.fetch(parsedId)\n }\n\n watch(\n () => ({\n designData: explicitDesignData.value,\n enabled: enabled.value,\n experimentId: experimentId.value,\n }),\n ({ designData: currentDesignData, enabled: isEnabled, experimentId: currentExperimentId }) => {\n if (options.immediate === false || !isEnabled) return\n if (currentDesignData) {\n experimentData.clear()\n return\n }\n if (currentExperimentId === undefined) {\n experimentData.clear()\n return\n }\n void experimentData.fetch(currentExperimentId)\n },\n { immediate: options.immediate !== false },\n )\n\n return {\n experimentId,\n data: experimentData.data,\n designData,\n samples,\n sampleOptions,\n isLoading: experimentData.isLoading,\n error: experimentData.error,\n lastLoadedAt: experimentData.lastLoadedAt,\n fetch: fetchSamples,\n refresh: experimentData.refresh,\n clear: experimentData.clear,\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as BaseButton_default, n as BaseModal_default, o as __exportAll, r as useFocusTrap, t as useDropdownState } from "./useDropdownState-uFIz6Yv8.js";
|
|
2
2
|
import { d as MintApiError, f as mintApiErrorFromResponse, l as tryUseSettingsStore, t as useExperimentStore } from "./experiment-DyO54FQB.js";
|
|
3
|
-
import { C as formatExperimentDate, D as compareTime, E as useMenuKeyboard, F as toMinutes, G as normalizeOptionInput, K as BaseInput_default, M as formatTimeSlot, N as generateTimeSlots, T as FileUploader_default, V as BaseSelect_default, W as useFloatingPanel, c as SecretInput_default, d as isActiveJobStatus, h as normalizeJobState, i as getInjectedPlatformContext, j as formatTime, k as findNearestTimeSlotIndex, n as ResourcePickerListInternal_default, s as TagsInput_default, z as BaseCheckbox_default } from "./records-
|
|
3
|
+
import { C as formatExperimentDate, D as compareTime, E as useMenuKeyboard, F as toMinutes, G as normalizeOptionInput, K as BaseInput_default, M as formatTimeSlot, N as generateTimeSlots, T as FileUploader_default, V as BaseSelect_default, W as useFloatingPanel, c as SecretInput_default, d as isActiveJobStatus, h as normalizeJobState, i as getInjectedPlatformContext, j as formatTime, k as findNearestTimeSlotIndex, n as ResourcePickerListInternal_default, s as TagsInput_default, z as BaseCheckbox_default } from "./records-CWRztE5M.js";
|
|
4
4
|
import "./auth-SliLnFre.js";
|
|
5
5
|
import { getRoleInfo, hasAllPermissions, isAdminUser } from "./permissions.js";
|
|
6
6
|
import { Fragment, Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createStaticVNode, createTextVNode, createVNode, defineComponent, getCurrentInstance, getCurrentScope, inject, mergeProps, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, onScopeDispose, onUnmounted, openBlock, ref, renderList, renderSlot, shallowRef, toDisplayString, toValue, unref, useAttrs, useId, useTemplateRef, vShow, watch, withCtx, withDirectives, withKeys, withModifiers } from "vue";
|
|
@@ -5241,4 +5241,4 @@ function useListReorder(options) {
|
|
|
5241
5241
|
//#endregion
|
|
5242
5242
|
export { useJobsEndpoint as A, compareSortValues as B, FormulaInput_default as C, _plugin_vue_export_helper_default as D, GeneratedPathInput_default as E, applySpecDefaults as F, BaseSlider_default as H, defineDataFrame as I, definePlateEditor as L, usePlatform as M, NumberInput_default as N, GENERATED_JOBS_API_KEY as O, DatePicker_default as P, definePluginWorkspaceView as R, useSequenceUtils as S, renderFormulaParts as T, BaseRadioGroup_default as U, useSortedItems as V, getTypeDefault as _, StepWizard_default as a, MoleculeInput_default as b, normalizeControls as c, numericValue as d, omitUndefined as f, getFieldRegistryEntry as g, replaceRecord as h, useReorderAnnouncer as i, createEventStream as j, createGeneratedAnalysisTransport as k, normalizeOption as l, recordValue as m, reorderList as n, defaultValueForControl as o, orderedUnique as p, useListReorder as r, normalizeControlDefinition as s, REORDER_ANNOUNCER_STYLE as t, humanize as u, ConcentrationInput_default as v, formula_exports as w, SequenceInput_default as x, useConcentrationUnits as y, defineWellPlate as z };
|
|
5243
5243
|
|
|
5244
|
-
//# sourceMappingURL=useListReorder-
|
|
5244
|
+
//# sourceMappingURL=useListReorder-QqJc1dpX.js.map
|