@morscherlab/mld-sdk 0.7.7 → 0.8.0

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 (59) hide show
  1. package/dist/components/ExperimentCodeBadge.vue.d.ts +7 -1
  2. package/dist/components/ExperimentCodeBadge.vue.js +41 -5
  3. package/dist/components/ExperimentCodeBadge.vue.js.map +1 -1
  4. package/dist/components/ExperimentDataViewer.vue.d.ts +1 -1
  5. package/dist/components/ExperimentDataViewer.vue.js +98 -44
  6. package/dist/components/ExperimentDataViewer.vue.js.map +1 -1
  7. package/dist/components/ExperimentSelectorModal.vue.d.ts +22 -0
  8. package/dist/components/ExperimentSelectorModal.vue.js +218 -0
  9. package/dist/components/ExperimentSelectorModal.vue.js.map +1 -0
  10. package/dist/components/ExperimentSelectorModal.vue3.js +6 -0
  11. package/dist/components/ExperimentSelectorModal.vue3.js.map +1 -0
  12. package/dist/components/FitPanel.vue.d.ts +46 -0
  13. package/dist/components/FitPanel.vue.js +118 -0
  14. package/dist/components/FitPanel.vue.js.map +1 -0
  15. package/dist/components/FitPanel.vue3.js +6 -0
  16. package/dist/components/FitPanel.vue3.js.map +1 -0
  17. package/dist/components/index.d.ts +2 -0
  18. package/dist/components/index.js +6 -0
  19. package/dist/components/index.js.map +1 -1
  20. package/dist/composables/experiment-utils.d.ts +5 -0
  21. package/dist/composables/experiment-utils.js +34 -0
  22. package/dist/composables/experiment-utils.js.map +1 -0
  23. package/dist/composables/index.d.ts +3 -0
  24. package/dist/composables/index.js +9 -0
  25. package/dist/composables/index.js.map +1 -1
  26. package/dist/composables/useExperimentData.d.ts +17 -0
  27. package/dist/composables/useExperimentData.js +62 -0
  28. package/dist/composables/useExperimentData.js.map +1 -0
  29. package/dist/composables/useExperimentSelector.d.ts +24 -0
  30. package/dist/composables/useExperimentSelector.js +112 -0
  31. package/dist/composables/useExperimentSelector.js.map +1 -0
  32. package/dist/index.d.ts +3 -3
  33. package/dist/index.js +15 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/styles.css +407 -2
  36. package/dist/types/components.d.ts +30 -0
  37. package/dist/types/index.d.ts +1 -1
  38. package/package.json +1 -1
  39. package/src/components/ExperimentCodeBadge.story.vue +77 -0
  40. package/src/components/ExperimentCodeBadge.vue +46 -3
  41. package/src/components/ExperimentDataViewer.story.vue +174 -0
  42. package/src/components/ExperimentDataViewer.vue +49 -12
  43. package/src/components/ExperimentSelectorModal.story.vue +244 -0
  44. package/src/components/ExperimentSelectorModal.vue +195 -0
  45. package/src/components/FitPanel.story.vue +125 -0
  46. package/src/components/FitPanel.vue +119 -0
  47. package/src/components/index.ts +4 -0
  48. package/src/composables/experiment-utils.ts +32 -0
  49. package/src/composables/index.ts +16 -0
  50. package/src/composables/useExperimentData.ts +85 -0
  51. package/src/composables/useExperimentSelector.ts +152 -0
  52. package/src/index.ts +24 -0
  53. package/src/styles/components/experiment-code-badge.css +20 -0
  54. package/src/styles/components/experiment-data-viewer.css +8 -1
  55. package/src/styles/components/experiment-selector-modal.css +136 -0
  56. package/src/styles/components/fit-panel.css +67 -0
  57. package/src/styles/index.css +2 -0
  58. package/src/types/components.ts +38 -0
  59. package/src/types/index.ts +8 -0
@@ -0,0 +1,218 @@
1
+ import { defineComponent, ref, watch, openBlock, createBlock, withCtx, createElementVNode, createVNode, unref, createElementBlock, Fragment, renderList, toDisplayString, normalizeClass, createTextVNode, createCommentVNode, nextTick } from "vue";
2
+ import { useExperimentSelector } from "../composables/useExperimentSelector.js";
3
+ import { EXPERIMENT_STATUS_OPTIONS, formatExperimentDate, EXPERIMENT_STATUS_VARIANT_MAP } from "../composables/experiment-utils.js";
4
+ import _sfc_main$1 from "./BaseModal.vue.js";
5
+ /* empty css */
6
+ import _sfc_main$2 from "./BaseInput.vue.js";
7
+ /* empty css */
8
+ import _sfc_main$3 from "./BaseSelect.vue.js";
9
+ /* empty css */
10
+ import _sfc_main$7 from "./BasePill.vue.js";
11
+ /* empty css */
12
+ import _sfc_main$4 from "./Skeleton.vue.js";
13
+ /* empty css */
14
+ import _sfc_main$5 from "./EmptyState.vue.js";
15
+ /* empty css */
16
+ import _sfc_main$6 from "./ExperimentCodeBadge.vue.js";
17
+ /* empty css */
18
+ const _hoisted_1 = { class: "mld-experiment-selector__filters" };
19
+ const _hoisted_2 = { class: "mld-experiment-selector__search" };
20
+ const _hoisted_3 = { class: "mld-experiment-selector__status-filter" };
21
+ const _hoisted_4 = {
22
+ key: 0,
23
+ class: "mld-experiment-selector__skeleton"
24
+ };
25
+ const _hoisted_5 = { class: "mld-experiment-selector__skeleton-content" };
26
+ const _hoisted_6 = {
27
+ key: 1,
28
+ class: "mld-experiment-selector__error"
29
+ };
30
+ const _hoisted_7 = ["onClick", "onMouseenter"];
31
+ const _hoisted_8 = { class: "mld-experiment-selector__row-content" };
32
+ const _hoisted_9 = { class: "mld-experiment-selector__name" };
33
+ const _hoisted_10 = { class: "mld-experiment-selector__meta" };
34
+ const _hoisted_11 = { key: 0 };
35
+ const _sfc_main = /* @__PURE__ */ defineComponent({
36
+ __name: "ExperimentSelectorModal",
37
+ props: {
38
+ modelValue: { type: Boolean },
39
+ experimentType: {},
40
+ currentExperimentId: { default: null },
41
+ title: { default: "Select Experiment" },
42
+ size: { default: "full" }
43
+ },
44
+ emits: ["update:modelValue", "select"],
45
+ setup(__props, { emit: __emit }) {
46
+ const props = __props;
47
+ const emit = __emit;
48
+ const {
49
+ experiments,
50
+ filters,
51
+ isLoading,
52
+ error,
53
+ fetch: fetchExperiments
54
+ } = useExperimentSelector({
55
+ experimentType: props.experimentType
56
+ });
57
+ const activeIndex = ref(-1);
58
+ const listRef = ref(null);
59
+ function handleStatusChange(value) {
60
+ filters.status = String(value) || null;
61
+ }
62
+ function handleSelect(experiment) {
63
+ emit("select", experiment);
64
+ emit("update:modelValue", false);
65
+ }
66
+ function handleKeydown(event) {
67
+ if (!experiments.value.length) return;
68
+ switch (event.key) {
69
+ case "ArrowDown":
70
+ event.preventDefault();
71
+ activeIndex.value = Math.min(activeIndex.value + 1, experiments.value.length - 1);
72
+ scrollActiveIntoView();
73
+ break;
74
+ case "ArrowUp":
75
+ event.preventDefault();
76
+ activeIndex.value = Math.max(activeIndex.value - 1, 0);
77
+ scrollActiveIntoView();
78
+ break;
79
+ case "Enter":
80
+ event.preventDefault();
81
+ if (activeIndex.value >= 0 && activeIndex.value < experiments.value.length) {
82
+ handleSelect(experiments.value[activeIndex.value]);
83
+ }
84
+ break;
85
+ }
86
+ }
87
+ function scrollActiveIntoView() {
88
+ nextTick(() => {
89
+ var _a;
90
+ const row = (_a = listRef.value) == null ? void 0 : _a.querySelector(".mld-experiment-selector__row--focused");
91
+ row == null ? void 0 : row.scrollIntoView({ block: "nearest" });
92
+ });
93
+ }
94
+ watch(experiments, () => {
95
+ activeIndex.value = -1;
96
+ });
97
+ watch(
98
+ () => props.modelValue,
99
+ (isOpen) => {
100
+ if (isOpen) {
101
+ activeIndex.value = -1;
102
+ fetchExperiments();
103
+ }
104
+ }
105
+ );
106
+ return (_ctx, _cache) => {
107
+ return openBlock(), createBlock(_sfc_main$1, {
108
+ "model-value": __props.modelValue,
109
+ title: __props.title,
110
+ size: __props.size,
111
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => emit("update:modelValue", $event))
112
+ }, {
113
+ default: withCtx(() => [
114
+ createElementVNode("div", {
115
+ class: "mld-experiment-selector",
116
+ onKeydown: handleKeydown
117
+ }, [
118
+ createElementVNode("div", _hoisted_1, [
119
+ createElementVNode("div", _hoisted_2, [
120
+ createVNode(_sfc_main$2, {
121
+ modelValue: unref(filters).search,
122
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => unref(filters).search = $event),
123
+ placeholder: "Search experiments...",
124
+ size: "sm",
125
+ type: "search"
126
+ }, null, 8, ["modelValue"])
127
+ ]),
128
+ createElementVNode("div", _hoisted_3, [
129
+ createVNode(_sfc_main$3, {
130
+ "model-value": unref(filters).status ?? "",
131
+ options: unref(EXPERIMENT_STATUS_OPTIONS),
132
+ size: "sm",
133
+ "onUpdate:modelValue": handleStatusChange
134
+ }, null, 8, ["model-value", "options"])
135
+ ])
136
+ ]),
137
+ unref(isLoading) ? (openBlock(), createElementBlock("div", _hoisted_4, [
138
+ (openBlock(), createElementBlock(Fragment, null, renderList(4, (n) => {
139
+ return createElementVNode("div", {
140
+ key: n,
141
+ class: "mld-experiment-selector__skeleton-row"
142
+ }, [
143
+ createElementVNode("div", _hoisted_5, [
144
+ createVNode(_sfc_main$4, {
145
+ width: 120 + n * 20,
146
+ height: "14px"
147
+ }, null, 8, ["width"]),
148
+ createVNode(_sfc_main$4, {
149
+ width: "80px",
150
+ height: "10px"
151
+ })
152
+ ]),
153
+ createVNode(_sfc_main$4, {
154
+ width: "60px",
155
+ height: "20px",
156
+ variant: "rounded"
157
+ })
158
+ ]);
159
+ }), 64))
160
+ ])) : unref(error) ? (openBlock(), createElementBlock("div", _hoisted_6, toDisplayString(unref(error)), 1)) : unref(experiments).length === 0 ? (openBlock(), createBlock(_sfc_main$5, {
161
+ key: 2,
162
+ title: "No experiments found",
163
+ description: "Try adjusting your search or filters.",
164
+ size: "sm"
165
+ })) : (openBlock(), createElementBlock("div", {
166
+ key: 3,
167
+ ref_key: "listRef",
168
+ ref: listRef,
169
+ class: "mld-experiment-selector__list"
170
+ }, [
171
+ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(experiments), (exp, idx) => {
172
+ return openBlock(), createElementBlock("div", {
173
+ key: exp.id,
174
+ class: normalizeClass(["mld-experiment-selector__row", {
175
+ "mld-experiment-selector__row--active": exp.id === __props.currentExperimentId,
176
+ "mld-experiment-selector__row--focused": idx === activeIndex.value
177
+ }]),
178
+ onClick: ($event) => handleSelect(exp),
179
+ onMouseenter: ($event) => activeIndex.value = idx
180
+ }, [
181
+ createElementVNode("div", _hoisted_8, [
182
+ createElementVNode("div", _hoisted_9, [
183
+ createTextVNode(toDisplayString(exp.name) + " ", 1),
184
+ exp.experiment_code ? (openBlock(), createBlock(_sfc_main$6, {
185
+ key: 0,
186
+ code: exp.experiment_code,
187
+ size: "sm",
188
+ copyable: false
189
+ }, null, 8, ["code"])) : createCommentVNode("", true)
190
+ ]),
191
+ createElementVNode("div", _hoisted_10, [
192
+ exp.project ? (openBlock(), createElementBlock("span", _hoisted_11, toDisplayString(exp.project), 1)) : createCommentVNode("", true),
193
+ createElementVNode("span", null, toDisplayString(unref(formatExperimentDate)(exp.created_at)), 1)
194
+ ])
195
+ ]),
196
+ createVNode(_sfc_main$7, {
197
+ variant: unref(EXPERIMENT_STATUS_VARIANT_MAP)[exp.status],
198
+ size: "sm"
199
+ }, {
200
+ default: withCtx(() => [
201
+ createTextVNode(toDisplayString(exp.status), 1)
202
+ ]),
203
+ _: 2
204
+ }, 1032, ["variant"])
205
+ ], 42, _hoisted_7);
206
+ }), 128))
207
+ ], 512))
208
+ ], 32)
209
+ ]),
210
+ _: 1
211
+ }, 8, ["model-value", "title", "size"]);
212
+ };
213
+ }
214
+ });
215
+ export {
216
+ _sfc_main as default
217
+ };
218
+ //# sourceMappingURL=ExperimentSelectorModal.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExperimentSelectorModal.vue.js","sources":["../../src/components/ExperimentSelectorModal.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, nextTick } from 'vue'\nimport type { ModalSize, ExperimentSummary, ExperimentStatus } from '../types'\nimport { useExperimentSelector } from '../composables/useExperimentSelector'\nimport {\n formatExperimentDate,\n EXPERIMENT_STATUS_OPTIONS,\n EXPERIMENT_STATUS_VARIANT_MAP,\n} from '../composables/experiment-utils'\nimport BaseModal from './BaseModal.vue'\nimport BaseInput from './BaseInput.vue'\nimport BaseSelect from './BaseSelect.vue'\nimport BasePill from './BasePill.vue'\nimport Skeleton from './Skeleton.vue'\nimport EmptyState from './EmptyState.vue'\nimport ExperimentCodeBadge from './ExperimentCodeBadge.vue'\n\ninterface Props {\n modelValue: boolean\n experimentType?: string\n currentExperimentId?: number | null\n title?: string\n size?: ModalSize\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n currentExperimentId: null,\n title: 'Select Experiment',\n size: 'full',\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean]\n select: [experiment: ExperimentSummary]\n}>()\n\nconst {\n experiments,\n filters,\n isLoading,\n error,\n fetch: fetchExperiments,\n} = useExperimentSelector({\n experimentType: props.experimentType,\n})\n\nconst activeIndex = ref(-1)\nconst listRef = ref<HTMLElement | null>(null)\n\nfunction handleStatusChange(value: string | number) {\n filters.status = (String(value) || null) as ExperimentStatus | null\n}\n\nfunction handleSelect(experiment: ExperimentSummary) {\n emit('select', experiment)\n emit('update:modelValue', false)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (!experiments.value.length) return\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n activeIndex.value = Math.min(activeIndex.value + 1, experiments.value.length - 1)\n scrollActiveIntoView()\n break\n case 'ArrowUp':\n event.preventDefault()\n activeIndex.value = Math.max(activeIndex.value - 1, 0)\n scrollActiveIntoView()\n break\n case 'Enter':\n event.preventDefault()\n if (activeIndex.value >= 0 && activeIndex.value < experiments.value.length) {\n handleSelect(experiments.value[activeIndex.value])\n }\n break\n }\n}\n\nfunction scrollActiveIntoView() {\n nextTick(() => {\n const row = listRef.value?.querySelector('.mld-experiment-selector__row--focused')\n row?.scrollIntoView({ block: 'nearest' })\n })\n}\n\n// Reset active index when experiments change\nwatch(experiments, () => { activeIndex.value = -1 })\n\n// Fetch on open\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (isOpen) {\n activeIndex.value = -1\n fetchExperiments()\n }\n },\n)\n</script>\n\n<template>\n <BaseModal\n :model-value=\"modelValue\"\n :title=\"title\"\n :size=\"size\"\n @update:model-value=\"emit('update:modelValue', $event)\"\n >\n <div class=\"mld-experiment-selector\" @keydown=\"handleKeydown\">\n <!-- Filter bar -->\n <div class=\"mld-experiment-selector__filters\">\n <div class=\"mld-experiment-selector__search\">\n <BaseInput\n v-model=\"filters.search\"\n placeholder=\"Search experiments...\"\n size=\"sm\"\n type=\"search\"\n />\n </div>\n <div class=\"mld-experiment-selector__status-filter\">\n <BaseSelect\n :model-value=\"filters.status ?? ''\"\n :options=\"EXPERIMENT_STATUS_OPTIONS\"\n size=\"sm\"\n @update:model-value=\"handleStatusChange\"\n />\n </div>\n </div>\n\n <!-- Loading skeleton -->\n <div v-if=\"isLoading\" class=\"mld-experiment-selector__skeleton\">\n <div v-for=\"n in 4\" :key=\"n\" class=\"mld-experiment-selector__skeleton-row\">\n <div class=\"mld-experiment-selector__skeleton-content\">\n <Skeleton :width=\"120 + n * 20\" height=\"14px\" />\n <Skeleton width=\"80px\" height=\"10px\" />\n </div>\n <Skeleton width=\"60px\" height=\"20px\" variant=\"rounded\" />\n </div>\n </div>\n\n <!-- Error -->\n <div v-else-if=\"error\" class=\"mld-experiment-selector__error\">\n {{ error }}\n </div>\n\n <!-- Empty -->\n <EmptyState\n v-else-if=\"experiments.length === 0\"\n title=\"No experiments found\"\n description=\"Try adjusting your search or filters.\"\n size=\"sm\"\n />\n\n <!-- Experiment list -->\n <div v-else ref=\"listRef\" class=\"mld-experiment-selector__list\">\n <div\n v-for=\"(exp, idx) in experiments\"\n :key=\"exp.id\"\n class=\"mld-experiment-selector__row\"\n :class=\"{\n 'mld-experiment-selector__row--active': exp.id === currentExperimentId,\n 'mld-experiment-selector__row--focused': idx === activeIndex,\n }\"\n @click=\"handleSelect(exp)\"\n @mouseenter=\"activeIndex = idx\"\n >\n <div class=\"mld-experiment-selector__row-content\">\n <div class=\"mld-experiment-selector__name\">\n {{ exp.name }}\n <ExperimentCodeBadge\n v-if=\"exp.experiment_code\"\n :code=\"exp.experiment_code\"\n size=\"sm\"\n :copyable=\"false\"\n />\n </div>\n <div class=\"mld-experiment-selector__meta\">\n <span v-if=\"exp.project\">{{ exp.project }}</span>\n <span>{{ formatExperimentDate(exp.created_at) }}</span>\n </div>\n </div>\n <BasePill :variant=\"EXPERIMENT_STATUS_VARIANT_MAP[exp.status]\" size=\"sm\">\n {{ exp.status }}\n </BasePill>\n </div>\n </div>\n </div>\n </BaseModal>\n</template>\n\n<style>\n@import '../styles/components/experiment-selector-modal.css';\n</style>\n"],"names":["_createBlock","BaseModal","_createElementVNode","_createVNode","BaseInput","_unref","BaseSelect","_openBlock","_createElementBlock","_Fragment","_renderList","Skeleton","_toDisplayString","EmptyState","ExperimentCodeBadge","BasePill","_createTextVNode"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import _sfc_main from "./ExperimentSelectorModal.vue.js";
2
+ /* empty css */
3
+ export {
4
+ _sfc_main as default
5
+ };
6
+ //# sourceMappingURL=ExperimentSelectorModal.vue3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExperimentSelectorModal.vue3.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -0,0 +1,46 @@
1
+ import { FitState, FitResultSummary } from '../types';
2
+ interface Props {
3
+ state?: FitState;
4
+ progress?: number;
5
+ progressLabel?: string;
6
+ indeterminate?: boolean;
7
+ results?: FitResultSummary[];
8
+ errorMessage?: string;
9
+ runLabel?: string;
10
+ cancelLabel?: string;
11
+ disabled?: boolean;
12
+ }
13
+ declare function __VLS_template(): {
14
+ attrs: Partial<{}>;
15
+ slots: {
16
+ controls?(_: {}): any;
17
+ results?(_: {}): any;
18
+ footer?(_: {}): any;
19
+ };
20
+ refs: {};
21
+ rootEl: HTMLDivElement;
22
+ };
23
+ type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
24
+ declare const __VLS_component: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
25
+ cancel: () => any;
26
+ run: () => any;
27
+ }, string, import('vue').PublicProps, Readonly<Props> & Readonly<{
28
+ onCancel?: (() => any) | undefined;
29
+ onRun?: (() => any) | undefined;
30
+ }>, {
31
+ disabled: boolean;
32
+ progress: number;
33
+ indeterminate: boolean;
34
+ state: FitState;
35
+ results: FitResultSummary[];
36
+ cancelLabel: string;
37
+ progressLabel: string;
38
+ runLabel: string;
39
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
40
+ declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
41
+ export default _default;
42
+ type __VLS_WithTemplateSlots<T, S> = T & {
43
+ new (): {
44
+ $slots: S;
45
+ };
46
+ };
@@ -0,0 +1,118 @@
1
+ import { defineComponent, computed, openBlock, createElementBlock, renderSlot, createElementVNode, createBlock, withCtx, createTextVNode, toDisplayString, createCommentVNode, createVNode, Fragment, renderList, normalizeClass } from "vue";
2
+ import _sfc_main$1 from "./BaseButton.vue.js";
3
+ /* empty css */
4
+ import _sfc_main$2 from "./ProgressBar.vue.js";
5
+ /* empty css */
6
+ import _sfc_main$3 from "./AlertBox.vue.js";
7
+ /* empty css */
8
+ const _hoisted_1 = { class: "mld-fit-panel" };
9
+ const _hoisted_2 = { class: "mld-fit-panel__actions" };
10
+ const _hoisted_3 = {
11
+ key: 0,
12
+ class: "mld-fit-panel__progress"
13
+ };
14
+ const _hoisted_4 = {
15
+ key: 1,
16
+ class: "mld-fit-panel__results"
17
+ };
18
+ const _hoisted_5 = { class: "mld-fit-panel__result-list" };
19
+ const _hoisted_6 = { class: "mld-fit-panel__result-label" };
20
+ const _sfc_main = /* @__PURE__ */ defineComponent({
21
+ __name: "FitPanel",
22
+ props: {
23
+ state: { default: "idle" },
24
+ progress: { default: 0 },
25
+ progressLabel: { default: "Fitting..." },
26
+ indeterminate: { type: Boolean, default: false },
27
+ results: { default: () => [] },
28
+ errorMessage: {},
29
+ runLabel: { default: "Run Fit" },
30
+ cancelLabel: { default: "Cancel" },
31
+ disabled: { type: Boolean, default: false }
32
+ },
33
+ emits: ["run", "cancel"],
34
+ setup(__props, { emit: __emit }) {
35
+ const props = __props;
36
+ const emit = __emit;
37
+ const isRunning = computed(() => props.state === "running");
38
+ const showRunButton = computed(() => !isRunning.value);
39
+ const showResults = computed(() => props.state === "completed" && props.results.length > 0);
40
+ const showError = computed(() => props.state === "error" && props.errorMessage);
41
+ function variantClass(variant) {
42
+ if (!variant || variant === "default") return "";
43
+ return `mld-fit-panel__result-value--${variant}`;
44
+ }
45
+ return (_ctx, _cache) => {
46
+ return openBlock(), createElementBlock("div", _hoisted_1, [
47
+ renderSlot(_ctx.$slots, "controls"),
48
+ createElementVNode("div", _hoisted_2, [
49
+ showRunButton.value ? (openBlock(), createBlock(_sfc_main$1, {
50
+ key: 0,
51
+ variant: "primary",
52
+ size: "sm",
53
+ disabled: __props.disabled,
54
+ "full-width": "",
55
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("run"))
56
+ }, {
57
+ default: withCtx(() => [
58
+ createTextVNode(toDisplayString(__props.runLabel), 1)
59
+ ]),
60
+ _: 1
61
+ }, 8, ["disabled"])) : createCommentVNode("", true),
62
+ isRunning.value ? (openBlock(), createBlock(_sfc_main$1, {
63
+ key: 1,
64
+ variant: "ghost",
65
+ size: "sm",
66
+ "full-width": "",
67
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("cancel"))
68
+ }, {
69
+ default: withCtx(() => [
70
+ createTextVNode(toDisplayString(__props.cancelLabel), 1)
71
+ ]),
72
+ _: 1
73
+ })) : createCommentVNode("", true)
74
+ ]),
75
+ isRunning.value ? (openBlock(), createElementBlock("div", _hoisted_3, [
76
+ createVNode(_sfc_main$2, {
77
+ value: __props.progress,
78
+ label: __props.progressLabel,
79
+ indeterminate: __props.indeterminate,
80
+ "show-value": !__props.indeterminate,
81
+ size: "sm"
82
+ }, null, 8, ["value", "label", "indeterminate", "show-value"])
83
+ ])) : createCommentVNode("", true),
84
+ showResults.value ? (openBlock(), createElementBlock("div", _hoisted_4, [
85
+ renderSlot(_ctx.$slots, "results", {}, () => [
86
+ createElementVNode("div", _hoisted_5, [
87
+ (openBlock(true), createElementBlock(Fragment, null, renderList(__props.results, (item, idx) => {
88
+ return openBlock(), createElementBlock("div", {
89
+ key: idx,
90
+ class: "mld-fit-panel__result-row"
91
+ }, [
92
+ createElementVNode("span", _hoisted_6, toDisplayString(item.label), 1),
93
+ createElementVNode("span", {
94
+ class: normalizeClass(["mld-fit-panel__result-value", variantClass(item.variant)])
95
+ }, toDisplayString(item.value), 3)
96
+ ]);
97
+ }), 128))
98
+ ])
99
+ ])
100
+ ])) : createCommentVNode("", true),
101
+ showError.value ? (openBlock(), createBlock(_sfc_main$3, {
102
+ key: 2,
103
+ type: "error"
104
+ }, {
105
+ default: withCtx(() => [
106
+ createTextVNode(toDisplayString(__props.errorMessage), 1)
107
+ ]),
108
+ _: 1
109
+ })) : createCommentVNode("", true),
110
+ renderSlot(_ctx.$slots, "footer")
111
+ ]);
112
+ };
113
+ }
114
+ });
115
+ export {
116
+ _sfc_main as default
117
+ };
118
+ //# sourceMappingURL=FitPanel.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FitPanel.vue.js","sources":["../../src/components/FitPanel.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { FitState, FitResultSummary } from '../types'\nimport BaseButton from './BaseButton.vue'\nimport ProgressBar from './ProgressBar.vue'\nimport AlertBox from './AlertBox.vue'\n\ninterface Props {\n state?: FitState\n progress?: number\n progressLabel?: string\n indeterminate?: boolean\n results?: FitResultSummary[]\n errorMessage?: string\n runLabel?: string\n cancelLabel?: string\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n state: 'idle',\n progress: 0,\n progressLabel: 'Fitting...',\n indeterminate: false,\n results: () => [],\n runLabel: 'Run Fit',\n cancelLabel: 'Cancel',\n disabled: false,\n})\n\nconst emit = defineEmits<{\n run: []\n cancel: []\n}>()\n\nconst isRunning = computed(() => props.state === 'running')\nconst showRunButton = computed(() => !isRunning.value)\nconst showResults = computed(() => props.state === 'completed' && props.results.length > 0)\nconst showError = computed(() => props.state === 'error' && props.errorMessage)\n\nfunction variantClass(variant?: string): string {\n if (!variant || variant === 'default') return ''\n return `mld-fit-panel__result-value--${variant}`\n}\n</script>\n\n<template>\n <div class=\"mld-fit-panel\">\n <!-- Plugin controls slot -->\n <slot name=\"controls\" />\n\n <!-- Action bar -->\n <div class=\"mld-fit-panel__actions\">\n <BaseButton\n v-if=\"showRunButton\"\n variant=\"primary\"\n size=\"sm\"\n :disabled=\"disabled\"\n full-width\n @click=\"emit('run')\"\n >\n {{ runLabel }}\n </BaseButton>\n <BaseButton\n v-if=\"isRunning\"\n variant=\"ghost\"\n size=\"sm\"\n full-width\n @click=\"emit('cancel')\"\n >\n {{ cancelLabel }}\n </BaseButton>\n </div>\n\n <!-- Progress -->\n <div v-if=\"isRunning\" class=\"mld-fit-panel__progress\">\n <ProgressBar\n :value=\"progress\"\n :label=\"progressLabel\"\n :indeterminate=\"indeterminate\"\n :show-value=\"!indeterminate\"\n size=\"sm\"\n />\n </div>\n\n <!-- Results -->\n <div v-if=\"showResults\" class=\"mld-fit-panel__results\">\n <slot name=\"results\">\n <div class=\"mld-fit-panel__result-list\">\n <div\n v-for=\"(item, idx) in results\"\n :key=\"idx\"\n class=\"mld-fit-panel__result-row\"\n >\n <span class=\"mld-fit-panel__result-label\">{{ item.label }}</span>\n <span\n class=\"mld-fit-panel__result-value\"\n :class=\"variantClass(item.variant)\"\n >\n {{ item.value }}\n </span>\n </div>\n </div>\n </slot>\n </div>\n\n <!-- Error -->\n <AlertBox v-if=\"showError\" type=\"error\">\n {{ errorMessage }}\n </AlertBox>\n\n <!-- Footer slot -->\n <slot name=\"footer\" />\n </div>\n</template>\n\n<style>\n@import '../styles/components/fit-panel.css';\n</style>\n"],"names":["_openBlock","_createElementBlock","_renderSlot","_createElementVNode","_createBlock","BaseButton","_createVNode","ProgressBar","_Fragment","_renderList","_toDisplayString","AlertBox"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA,UAAM,QAAQ;AAWd,UAAM,OAAO;AAKb,UAAM,YAAY,SAAS,MAAM,MAAM,UAAU,SAAS;AAC1D,UAAM,gBAAgB,SAAS,MAAM,CAAC,UAAU,KAAK;AACrD,UAAM,cAAc,SAAS,MAAM,MAAM,UAAU,eAAe,MAAM,QAAQ,SAAS,CAAC;AAC1F,UAAM,YAAY,SAAS,MAAM,MAAM,UAAU,WAAW,MAAM,YAAY;AAE9E,aAAS,aAAa,SAA0B;AAC9C,UAAI,CAAC,WAAW,YAAY,UAAW,QAAO;AAC9C,aAAO,gCAAgC,OAAO;AAAA,IAChD;;AAIE,aAAAA,UAAA,GAAAC,mBAkEM,OAlEN,YAkEM;AAAA,QAhEJC,WAAwB,KAAA,QAAA,UAAA;AAAA,QAGxBC,mBAoBM,OApBN,YAoBM;AAAA,UAlBI,cAAA,sBADRC,YASaC,aAAA;AAAA;YAPX,SAAQ;AAAA,YACR,MAAK;AAAA,YACJ,UAAU,QAAA;AAAA,YACX,cAAA;AAAA,YACC,+CAAO,KAAI,KAAA;AAAA,UAAA;6BAEZ,MAAc;AAAA,8CAAX,QAAA,QAAQ,GAAA,CAAA;AAAA,YAAA;;;UAGL,UAAA,sBADRD,YAQaC,aAAA;AAAA;YANX,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,cAAA;AAAA,YACC,+CAAO,KAAI,QAAA;AAAA,UAAA;6BAEZ,MAAiB;AAAA,8CAAd,QAAA,WAAW,GAAA,CAAA;AAAA,YAAA;;;;QAKP,UAAA,SAAXL,UAAA,GAAAC,mBAQM,OARN,YAQM;AAAA,UAPJK,YAMEC,aAAA;AAAA,YALC,OAAO,QAAA;AAAA,YACP,OAAO,QAAA;AAAA,YACP,eAAe,QAAA;AAAA,YACf,eAAa,QAAA;AAAA,YACd,MAAK;AAAA,UAAA;;QAKE,YAAA,SAAXP,UAAA,GAAAC,mBAkBM,OAlBN,YAkBM;AAAA,UAjBJC,WAgBO,4BAhBP,MAgBO;AAAA,YAfLC,mBAcM,OAdN,YAcM;AAAA,eAbJH,UAAA,IAAA,GAAAC,mBAYMO,UAAA,MAAAC,WAXkB,QAAA,SAAO,CAArB,MAAM,QAAG;oCADnBR,mBAYM,OAAA;AAAA,kBAVH,KAAK;AAAA,kBACN,OAAM;AAAA,gBAAA;kBAENE,mBAAiE,QAAjE,YAAiEO,gBAApB,KAAK,KAAK,GAAA,CAAA;AAAA,kBACvDP,mBAKO,QAAA;AAAA,oBAJL,uBAAM,+BACE,aAAa,KAAK,OAAO,CAAA,CAAA;AAAA,kBAAA,GAE9BO,gBAAA,KAAK,KAAK,GAAA,CAAA;AAAA,gBAAA;;;;;QAQP,UAAA,sBAAhBN,YAEWO,aAAA;AAAA;UAFgB,MAAK;AAAA,QAAA;2BAC9B,MAAkB;AAAA,4CAAf,QAAA,YAAY,GAAA,CAAA;AAAA,UAAA;;;QAIjBT,WAAsB,KAAA,QAAA,QAAA;AAAA,MAAA;;;;"}
@@ -0,0 +1,6 @@
1
+ import _sfc_main from "./FitPanel.vue.js";
2
+ /* empty css */
3
+ export {
4
+ _sfc_main as default
5
+ };
6
+ //# sourceMappingURL=FitPanel.vue3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FitPanel.vue3.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -76,3 +76,5 @@ export { default as DateTimePicker } from './DateTimePicker.vue';
76
76
  export { default as TimeRangeInput } from './TimeRangeInput.vue';
77
77
  export { default as ScheduleCalendar } from './ScheduleCalendar.vue';
78
78
  export { default as ResourceCard } from './ResourceCard.vue';
79
+ export { default as ExperimentSelectorModal } from './ExperimentSelectorModal.vue';
80
+ export { default as FitPanel } from './FitPanel.vue';
@@ -152,6 +152,10 @@ import { default as default78 } from "./ScheduleCalendar.vue.js";
152
152
  /* empty css */
153
153
  import { default as default79 } from "./ResourceCard.vue.js";
154
154
  /* empty css */
155
+ import { default as default80 } from "./ExperimentSelectorModal.vue.js";
156
+ /* empty css */
157
+ import { default as default81 } from "./FitPanel.vue.js";
158
+ /* empty css */
155
159
  export {
156
160
  default25 as AlertBox,
157
161
  default34 as AppContainer,
@@ -190,8 +194,10 @@ export {
190
194
  default41 as EmptyState,
191
195
  default75 as ExperimentCodeBadge,
192
196
  default74 as ExperimentDataViewer,
197
+ default80 as ExperimentSelectorModal,
193
198
  default51 as ExperimentTimeline,
194
199
  default24 as FileUploader,
200
+ default81 as FitPanel,
195
201
  default72 as FormActions,
196
202
  default70 as FormBuilder,
197
203
  default19 as FormField,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,5 @@
1
+ import { ExperimentStatus, SelectOption, PillVariant } from '../types';
2
+ export declare function formatExperimentDate(dateStr: string): string;
3
+ export declare const EXPERIMENT_STATUS_OPTIONS: SelectOption<string>[];
4
+ export declare const EXPERIMENT_STATUS_VARIANT_MAP: Record<ExperimentStatus, PillVariant>;
5
+ export declare const EXPERIMENT_STATUS_LABELS: Record<ExperimentStatus, string>;
@@ -0,0 +1,34 @@
1
+ function formatExperimentDate(dateStr) {
2
+ try {
3
+ return new Date(dateStr).toLocaleDateString(void 0, {
4
+ year: "numeric",
5
+ month: "short",
6
+ day: "numeric"
7
+ });
8
+ } catch {
9
+ return dateStr;
10
+ }
11
+ }
12
+ const EXPERIMENT_STATUS_OPTIONS = [
13
+ { value: "", label: "All statuses" },
14
+ { value: "planned", label: "Planned" },
15
+ { value: "ongoing", label: "Ongoing" },
16
+ { value: "completed", label: "Completed" }
17
+ ];
18
+ const EXPERIMENT_STATUS_VARIANT_MAP = {
19
+ planned: "default",
20
+ ongoing: "primary",
21
+ completed: "success"
22
+ };
23
+ const EXPERIMENT_STATUS_LABELS = {
24
+ planned: "Planned",
25
+ ongoing: "Ongoing",
26
+ completed: "Completed"
27
+ };
28
+ export {
29
+ EXPERIMENT_STATUS_LABELS,
30
+ EXPERIMENT_STATUS_OPTIONS,
31
+ EXPERIMENT_STATUS_VARIANT_MAP,
32
+ formatExperimentDate
33
+ };
34
+ //# sourceMappingURL=experiment-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"experiment-utils.js","sources":["../../src/composables/experiment-utils.ts"],"sourcesContent":["import type { ExperimentStatus, SelectOption, PillVariant } from '../types'\n\nexport function formatExperimentDate(dateStr: string): string {\n try {\n return new Date(dateStr).toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })\n } catch {\n return dateStr\n }\n}\n\nexport const EXPERIMENT_STATUS_OPTIONS: SelectOption<string>[] = [\n { value: '', label: 'All statuses' },\n { value: 'planned', label: 'Planned' },\n { value: 'ongoing', label: 'Ongoing' },\n { value: 'completed', label: 'Completed' },\n]\n\nexport const EXPERIMENT_STATUS_VARIANT_MAP: Record<ExperimentStatus, PillVariant> = {\n planned: 'default',\n ongoing: 'primary',\n completed: 'success',\n}\n\nexport const EXPERIMENT_STATUS_LABELS: Record<ExperimentStatus, string> = {\n planned: 'Planned',\n ongoing: 'Ongoing',\n completed: 'Completed',\n}\n"],"names":[],"mappings":"AAEO,SAAS,qBAAqB,SAAyB;AAC5D,MAAI;AACF,WAAO,IAAI,KAAK,OAAO,EAAE,mBAAmB,QAAW;AAAA,MACrD,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAK;AAAA,IAAA,CACN;AAAA,EACH,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,MAAM,4BAAoD;AAAA,EAC/D,EAAE,OAAO,IAAI,OAAO,eAAA;AAAA,EACpB,EAAE,OAAO,WAAW,OAAO,UAAA;AAAA,EAC3B,EAAE,OAAO,WAAW,OAAO,UAAA;AAAA,EAC3B,EAAE,OAAO,aAAa,OAAO,YAAA;AAC/B;AAEO,MAAM,gCAAuE;AAAA,EAClF,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AACb;AAEO,MAAM,2BAA6D;AAAA,EACxE,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AACb;"}
@@ -18,4 +18,7 @@ export { useScheduleDrag } from './useScheduleDrag';
18
18
  export { useFormBuilder, evaluateCondition } from './useFormBuilder';
19
19
  export { useAutoGroup, DEFAULT_COLORS } from './useAutoGroup';
20
20
  export { usePluginConfig, type UsePluginConfigReturn } from './usePluginConfig';
21
+ export { useExperimentSelector, type UseExperimentSelectorOptions, type UseExperimentSelectorReturn, } from './useExperimentSelector';
22
+ export { formatExperimentDate, EXPERIMENT_STATUS_OPTIONS, EXPERIMENT_STATUS_VARIANT_MAP, EXPERIMENT_STATUS_LABELS, } from './experiment-utils';
23
+ export { useExperimentData, type UseExperimentDataOptions, type UseExperimentDataReturn, } from './useExperimentData';
21
24
  export { getFieldRegistryEntry, getTypeDefault, type RegistryEntry, } from './formBuilderRegistry';
@@ -18,16 +18,23 @@ import { useScheduleDrag } from "./useScheduleDrag.js";
18
18
  import { evaluateCondition, useFormBuilder } from "./useFormBuilder.js";
19
19
  import { DEFAULT_COLORS, useAutoGroup } from "./useAutoGroup.js";
20
20
  import { usePluginConfig } from "./usePluginConfig.js";
21
+ import { useExperimentSelector } from "./useExperimentSelector.js";
22
+ import { EXPERIMENT_STATUS_LABELS, EXPERIMENT_STATUS_OPTIONS, EXPERIMENT_STATUS_VARIANT_MAP, formatExperimentDate } from "./experiment-utils.js";
23
+ import { useExperimentData } from "./useExperimentData.js";
21
24
  import { getFieldRegistryEntry, getTypeDefault } from "./formBuilderRegistry.js";
22
25
  export {
23
26
  ATOMIC_WEIGHTS,
24
27
  DEFAULT_COLORS,
28
+ EXPERIMENT_STATUS_LABELS,
29
+ EXPERIMENT_STATUS_OPTIONS,
30
+ EXPERIMENT_STATUS_VARIANT_MAP,
25
31
  addMinutes,
26
32
  compareTime,
27
33
  durationMinutes,
28
34
  evaluateCondition,
29
35
  findAvailableSlots,
30
36
  formatDuration,
37
+ formatExperimentDate,
31
38
  formatTime,
32
39
  generateTimeSlots,
33
40
  getFieldRegistryEntry,
@@ -44,6 +51,8 @@ export {
44
51
  useChemicalFormula,
45
52
  useConcentrationUnits,
46
53
  useDoseCalculator,
54
+ useExperimentData,
55
+ useExperimentSelector,
47
56
  useForm,
48
57
  useFormBuilder,
49
58
  usePasskey,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,17 @@
1
+ import { Ref, ComputedRef } from 'vue';
2
+ import { TreeNode, SummaryData } from '../types';
3
+ export interface UseExperimentDataOptions {
4
+ apiBaseUrl?: string;
5
+ immediate?: boolean;
6
+ }
7
+ export interface UseExperimentDataReturn {
8
+ data: Ref<Record<string, unknown> | null>;
9
+ treeData: ComputedRef<TreeNode[]>;
10
+ tableData: ComputedRef<Record<string, unknown>[]>;
11
+ summaryData: ComputedRef<SummaryData | null>;
12
+ isLoading: Ref<boolean>;
13
+ error: Ref<string | null>;
14
+ fetch: (experimentId: number) => Promise<void>;
15
+ refresh: () => Promise<void>;
16
+ }
17
+ export declare function useExperimentData(options?: UseExperimentDataOptions): UseExperimentDataReturn;
@@ -0,0 +1,62 @@
1
+ import { ref, computed } from "vue";
2
+ import { useApi } from "./useApi.js";
3
+ function useExperimentData(options = {}) {
4
+ const api = useApi({ baseUrl: options.apiBaseUrl });
5
+ const data = ref(null);
6
+ const isLoading = ref(false);
7
+ const error = ref(null);
8
+ let lastExperimentId = null;
9
+ const treeData = computed(() => {
10
+ if (!data.value) return [];
11
+ const tree = data.value.tree_data ?? data.value.treeData;
12
+ return Array.isArray(tree) ? tree : [];
13
+ });
14
+ const tableData = computed(() => {
15
+ if (!data.value) return [];
16
+ const table = data.value.table_data ?? data.value.tableData;
17
+ return Array.isArray(table) ? table : [];
18
+ });
19
+ const summaryData = computed(() => {
20
+ if (!data.value) return null;
21
+ const summary = data.value.summary_data ?? data.value.summaryData;
22
+ if (summary && typeof summary === "object" && "metadata" in summary) {
23
+ return summary;
24
+ }
25
+ return null;
26
+ });
27
+ async function fetchData(experimentId) {
28
+ lastExperimentId = experimentId;
29
+ isLoading.value = true;
30
+ error.value = null;
31
+ try {
32
+ const result = await api.get(
33
+ `/api/experiments/${experimentId}/data`
34
+ );
35
+ data.value = result;
36
+ } catch (e) {
37
+ error.value = e instanceof Error ? e.message : "Failed to fetch experiment data";
38
+ data.value = null;
39
+ } finally {
40
+ isLoading.value = false;
41
+ }
42
+ }
43
+ async function refresh() {
44
+ if (lastExperimentId !== null) {
45
+ await fetchData(lastExperimentId);
46
+ }
47
+ }
48
+ return {
49
+ data,
50
+ treeData,
51
+ tableData,
52
+ summaryData,
53
+ isLoading,
54
+ error,
55
+ fetch: fetchData,
56
+ refresh
57
+ };
58
+ }
59
+ export {
60
+ useExperimentData
61
+ };
62
+ //# sourceMappingURL=useExperimentData.js.map