@morscherlab/mint-sdk 1.0.0-beta.3 → 1.0.0-beta.5

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 (181) hide show
  1. package/README.md +9 -2
  2. package/dist/__tests__/composables/experiment-utils.test.d.ts +1 -0
  3. package/dist/__tests__/composables/useApi.test.d.ts +1 -0
  4. package/dist/components/AppContainer.vue.d.ts +1 -1
  5. package/dist/components/AppLayout.vue.d.ts +20 -1
  6. package/dist/components/AppSidebar.vue.d.ts +57 -5
  7. package/dist/components/AppTopBar.vue.d.ts +7 -25
  8. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +3 -1
  9. package/dist/components/BioTemplatePackWorkspaceView.vue.d.ts +1 -0
  10. package/dist/components/BioTemplatePresetWorkspaceView.vue.d.ts +5 -0
  11. package/dist/components/ComponentBindingRenderer.vue.d.ts +44 -0
  12. package/dist/components/ControlWorkspaceView.vue.d.ts +24 -7
  13. package/dist/components/DoseDesignWorkspaceView.vue.d.ts +149 -0
  14. package/dist/components/ExperimentTimeline.vue.d.ts +1 -1
  15. package/dist/components/FormBuilder.vue.d.ts +9 -9
  16. package/dist/components/PlateMapEditor.vue.d.ts +1 -1
  17. package/dist/components/PluginWorkspaceView.vue.d.ts +310 -0
  18. package/dist/components/SettingsModal.vue.d.ts +1 -1
  19. package/dist/components/WellPlate.vue.d.ts +2 -2
  20. package/dist/components/index.d.ts +3 -12
  21. package/dist/components/index.js +3 -3
  22. package/dist/components/{AppPageSelector.vue.d.ts → internal/AppTopBarPageSelectorInternal.vue.d.ts} +1 -1
  23. package/dist/components/{AppPillNav.vue.d.ts → internal/AppTopBarPillNavInternal.vue.d.ts} +3 -1
  24. package/dist/components/{CalendarGridPanel.vue.d.ts → internal/CalendarGridPanelInternal.vue.d.ts} +1 -1
  25. package/dist/components/internal/FormSectionRenderer.vue.d.ts +4 -4
  26. package/dist/components/{WellEditPopup.vue.d.ts → internal/WellEditPopupInternal.vue.d.ts} +1 -1
  27. package/dist/{components-D_Sr0adg.js → components-DihbSJjU.js} +5932 -5408
  28. package/dist/components-DihbSJjU.js.map +1 -0
  29. package/dist/composables/experiment-utils.d.ts +8 -0
  30. package/dist/composables/index.d.ts +5 -7
  31. package/dist/composables/index.js +4 -4
  32. package/dist/composables/useAppExperiment.d.ts +31 -2
  33. package/dist/composables/useBioTemplateComponents.d.ts +5 -3
  34. package/dist/composables/useBioTemplatePackWorkspace.d.ts +3 -2
  35. package/dist/composables/useBioTemplatePresetWorkspace.d.ts +6 -5
  36. package/dist/composables/useBioTemplateWorkspace.d.ts +5 -4
  37. package/dist/composables/useControlSchema.d.ts +43 -21
  38. package/dist/composables/usePluginClient.d.ts +5 -2
  39. package/dist/{composables-C3dpXQN5.js → composables-BcgZ6diz.js} +40 -28
  40. package/dist/composables-BcgZ6diz.js.map +1 -0
  41. package/dist/index.d.ts +5 -12
  42. package/dist/index.js +5 -5
  43. package/dist/install.js +2 -2
  44. package/dist/styles.css +5637 -5663
  45. package/dist/templates/adapters.d.ts +7 -1
  46. package/dist/templates/catalog.d.ts +5 -5
  47. package/dist/templates/componentBindings.d.ts +13 -0
  48. package/dist/templates/index.d.ts +5 -5
  49. package/dist/templates/index.js +2 -2
  50. package/dist/templates/presets.d.ts +4 -4
  51. package/dist/templates/types.d.ts +4 -1
  52. package/dist/{templates-50NPjaxL.js → templates-Cyt0Suwf.js} +322 -73
  53. package/dist/templates-Cyt0Suwf.js.map +1 -0
  54. package/dist/types/components.d.ts +6 -25
  55. package/dist/types/index.d.ts +1 -1
  56. package/dist/{useScheduleDrag-D4oWdh41.js → useExperimentData-CM6Y0u5L.js} +400 -357
  57. package/dist/useExperimentData-CM6Y0u5L.js.map +1 -0
  58. package/package.json +1 -1
  59. package/src/__tests__/components/ActionItem.test.ts +6 -6
  60. package/src/__tests__/components/AppLayout.test.ts +44 -0
  61. package/src/__tests__/components/AppSidebar.test.ts +130 -2
  62. package/src/__tests__/components/AppToastContainer.test.ts +0 -11
  63. package/src/__tests__/components/AppTopBar.test.ts +189 -120
  64. package/src/__tests__/components/{AppPageSelector.test.ts → AppTopBarPageSelector.test.ts} +8 -8
  65. package/src/__tests__/components/{AppPillNav.test.ts → AppTopBarPillNav.test.ts} +53 -6
  66. package/src/__tests__/components/BioTemplateExperimentWorkspaceView.test.ts +7 -1
  67. package/src/__tests__/components/BioTemplatePackWorkspaceView.test.ts +32 -1
  68. package/src/__tests__/components/BioTemplatePresetWorkspaceView.test.ts +48 -1
  69. package/src/__tests__/components/BioTemplateRenderer.test.ts +25 -0
  70. package/src/__tests__/components/CalendarGridPanel.test.ts +3 -3
  71. package/src/__tests__/components/ComponentBindingRenderer.test.ts +278 -0
  72. package/src/__tests__/components/ControlWorkspaceView.test.ts +134 -63
  73. package/src/__tests__/components/DateTimePicker.test.ts +2 -2
  74. package/src/__tests__/components/DoseDesignWorkspaceView.test.ts +185 -0
  75. package/src/__tests__/components/PluginWorkspaceView.test.ts +548 -0
  76. package/src/__tests__/composables/experiment-utils.test.ts +30 -0
  77. package/src/__tests__/composables/useApi.test.ts +30 -0
  78. package/src/__tests__/composables/useAppExperiment.test.ts +100 -1
  79. package/src/__tests__/composables/useBioTemplatePackWorkspace.test.ts +7 -4
  80. package/src/__tests__/composables/useBioTemplatePresetWorkspace.test.ts +7 -7
  81. package/src/__tests__/composables/useBioTemplateWorkspace.test.ts +6 -1
  82. package/src/__tests__/composables/useControlSchema.test.ts +151 -37
  83. package/src/__tests__/composables/usePluginClient.test.ts +99 -2
  84. package/src/__tests__/docs/frontendDocsCatalog.test.ts +120 -25
  85. package/src/__tests__/templates/templates.test.ts +56 -0
  86. package/src/components/AppAvatarMenu.vue +3 -3
  87. package/src/components/AppLayout.story.vue +39 -0
  88. package/src/components/AppLayout.vue +83 -2
  89. package/src/components/AppPluginSwitcher.vue +5 -5
  90. package/src/components/AppSidebar.story.vue +113 -5
  91. package/src/components/AppSidebar.vue +147 -27
  92. package/src/components/AppTopBar.story.vue +2 -5
  93. package/src/components/AppTopBar.vue +35 -425
  94. package/src/components/BioTemplateExperimentWorkspaceView.story.vue +2 -2
  95. package/src/components/BioTemplateExperimentWorkspaceView.vue +6 -0
  96. package/src/components/BioTemplatePackWorkspaceView.story.vue +4 -4
  97. package/src/components/BioTemplatePackWorkspaceView.vue +1 -0
  98. package/src/components/BioTemplatePresetWorkspaceView.story.vue +14 -2
  99. package/src/components/BioTemplatePresetWorkspaceView.vue +12 -3
  100. package/src/components/BioTemplateRenderer.story.vue +2 -2
  101. package/src/components/BioTemplateRenderer.vue +15 -227
  102. package/src/components/ComponentBindingRenderer.story.vue +87 -0
  103. package/src/components/ComponentBindingRenderer.vue +317 -0
  104. package/src/components/ControlWorkspaceView.story.vue +20 -9
  105. package/src/components/ControlWorkspaceView.vue +43 -12
  106. package/src/components/DatePicker.vue +2 -2
  107. package/src/components/DateTimePicker.vue +2 -2
  108. package/src/components/DoseDesignWorkspaceView.story.vue +77 -0
  109. package/src/components/DoseDesignWorkspaceView.vue +255 -0
  110. package/src/components/ExperimentPopover.story.vue +2 -2
  111. package/src/components/ExperimentPopover.vue +2 -6
  112. package/src/components/ExperimentSelectorModal.vue +6 -5
  113. package/src/components/FormBuilder.story.vue +190 -0
  114. package/src/components/PluginWorkspaceView.story.vue +334 -0
  115. package/src/components/PluginWorkspaceView.vue +708 -0
  116. package/src/components/SettingsModal.story.vue +87 -0
  117. package/src/components/WellPlate.vue +2 -2
  118. package/src/components/index.ts +3 -12
  119. package/src/components/{AppPageSelector.vue → internal/AppTopBarPageSelectorInternal.vue} +9 -9
  120. package/src/components/internal/AppTopBarPillNavInternal.vue +194 -0
  121. package/src/components/{CalendarGridPanel.vue → internal/CalendarGridPanelInternal.vue} +1 -1
  122. package/src/components/{WellEditPopup.vue → internal/WellEditPopupInternal.vue} +3 -3
  123. package/src/composables/experiment-utils.ts +26 -0
  124. package/src/composables/index.ts +21 -7
  125. package/src/composables/useApi.ts +9 -2
  126. package/src/composables/useAppExperiment.ts +85 -13
  127. package/src/composables/useBioTemplateComponents.ts +12 -0
  128. package/src/composables/useBioTemplatePackWorkspace.ts +6 -2
  129. package/src/composables/useBioTemplatePresetWorkspace.ts +10 -21
  130. package/src/composables/useBioTemplateWorkspace.ts +6 -4
  131. package/src/composables/useControlSchema.ts +157 -69
  132. package/src/composables/usePluginClient.ts +50 -9
  133. package/src/index.ts +6 -563
  134. package/src/styles/components/app-layout.css +82 -0
  135. package/src/styles/components/app-page-selector.css +1 -1
  136. package/src/styles/components/app-pill-nav.css +71 -1
  137. package/src/styles/components/app-sidebar.css +119 -0
  138. package/src/styles/components/app-top-bar.css +0 -235
  139. package/src/styles/components/experiment-popover.css +2 -2
  140. package/src/styles/index.css +0 -1
  141. package/src/templates/adapters.ts +193 -0
  142. package/src/templates/catalog.ts +5 -5
  143. package/src/templates/componentBindings.ts +90 -3
  144. package/src/templates/index.ts +10 -0
  145. package/src/templates/packs.ts +10 -1
  146. package/src/templates/presets.ts +14 -4
  147. package/src/templates/types.ts +4 -0
  148. package/src/types/components.ts +6 -31
  149. package/src/types/index.ts +2 -6
  150. package/dist/__tests__/composables/usePluginApi.test.d.ts +0 -13
  151. package/dist/components/FormFieldRenderer.vue.d.ts +0 -28
  152. package/dist/components/FormSection.vue.d.ts +0 -30
  153. package/dist/components/GroupingModal.vue.d.ts +0 -12
  154. package/dist/components/SettingsButton.vue.d.ts +0 -30
  155. package/dist/components/ToastNotification.vue.d.ts +0 -2
  156. package/dist/components-D_Sr0adg.js.map +0 -1
  157. package/dist/composables/usePluginApi.d.ts +0 -22
  158. package/dist/composables-C3dpXQN5.js.map +0 -1
  159. package/dist/templates-50NPjaxL.js.map +0 -1
  160. package/dist/useScheduleDrag-D4oWdh41.js.map +0 -1
  161. package/src/__tests__/components/FormCompatibility.test.ts +0 -94
  162. package/src/__tests__/components/GroupingModal.test.ts +0 -73
  163. package/src/__tests__/components/SettingsButton.test.ts +0 -44
  164. package/src/__tests__/composables/usePluginApi.test.ts +0 -81
  165. package/src/components/AppPillNav.vue +0 -71
  166. package/src/components/FormFieldRenderer.vue +0 -35
  167. package/src/components/FormSection.vue +0 -37
  168. package/src/components/GroupingModal.story.vue +0 -52
  169. package/src/components/GroupingModal.vue +0 -61
  170. package/src/components/SettingsButton.story.vue +0 -58
  171. package/src/components/SettingsButton.vue +0 -64
  172. package/src/components/ToastNotification.vue +0 -9
  173. package/src/composables/usePluginApi.ts +0 -32
  174. package/src/styles/components/settings-button.css +0 -31
  175. /package/dist/__tests__/components/{AppPageSelector.test.d.ts → AppTopBarPageSelector.test.d.ts} +0 -0
  176. /package/dist/__tests__/components/{AppPillNav.test.d.ts → AppTopBarPillNav.test.d.ts} +0 -0
  177. /package/dist/__tests__/components/{FormCompatibility.test.d.ts → ComponentBindingRenderer.test.d.ts} +0 -0
  178. /package/dist/__tests__/components/{GroupingModal.test.d.ts → DoseDesignWorkspaceView.test.d.ts} +0 -0
  179. /package/dist/__tests__/components/{SettingsButton.test.d.ts → PluginWorkspaceView.test.d.ts} +0 -0
  180. /package/dist/components/{ActionItem.vue.d.ts → internal/ActionItemInternal.vue.d.ts} +0 -0
  181. /package/src/components/{ActionItem.vue → internal/ActionItemInternal.vue} +0 -0
@@ -1,4 +1,4 @@
1
- import { Dn as useControlWorkspace, Ft as extractTemplateCollection, Mn as useConcentrationUnits, On as getFieldRegistryEntry, Ot as createTemplateCollection, Pt as ensureTemplateFromCollection, Xt as getBioTemplatePresetInfo, _ as toBioTemplateComponentSnippets, d as getBioTemplateComponentProps$1, dt as createBioTemplatePresetCollection, en as getBioTemplatePackInfo, ft as createBioTemplatePresetCollectionFromControls, g as toBioTemplateComponentPropsById, h as toBioTemplateComponentPropsByComponent$1, i as createBioTemplateControlToolkit, kn as getTypeDefault, m as toBioTemplateComponentProps, p as toBioTemplateComponentImports, u as getBioTemplateComponentBindings, ut as createBioTemplatePackCollection, v as toBioTemplateComponentUsage } from "./templates-50NPjaxL.js";
1
+ import { Bn as useConcentrationUnits, Bt as extractTemplateCollection, Fn as useControlWorkspace, In as getFieldRegistryEntry, Ln as getTypeDefault, Nt as createTemplateCollection, _ as toBioTemplateComponentPropsByComponent$1, _t as createBioTemplatePresetCollectionFromControls, an as getBioTemplatePackInfo, b as toBioTemplateComponentUsage, d as getBioTemplateComponentProps$1, g as toBioTemplateComponentProps, gt as createBioTemplatePresetCollection, h as toBioTemplateComponentImports, ht as createBioTemplatePackCollection, i as createBioTemplateControlToolkit, m as toBioTemplateComponentBindingsById, p as toBioTemplateComponentBindings, tn as getBioTemplatePresetInfo, u as getBioTemplateComponentBindings, v as toBioTemplateComponentPropsById, y as toBioTemplateComponentSnippets, zt as ensureTemplateFromCollection } from "./templates-Cyt0Suwf.js";
2
2
  import { r as useSettingsStore, t as useAuthStore } from "./auth-QQj2kkze.js";
3
3
  import { computed, effectScope, getCurrentScope, onMounted, onScopeDispose, onUnmounted, provide, reactive, readonly, ref, shallowRef, toRaw, toValue, watch } from "vue";
4
4
  import axios from "axios";
@@ -673,9 +673,121 @@ function useFormBuilder(schema, initialData, enhancements) {
673
673
  };
674
674
  }
675
675
  //#endregion
676
+ //#region src/composables/experiment-utils.ts
677
+ function formatExperimentDate(dateStr) {
678
+ try {
679
+ return new Date(dateStr).toLocaleDateString(void 0, {
680
+ year: "numeric",
681
+ month: "short",
682
+ day: "numeric"
683
+ });
684
+ } catch {
685
+ return dateStr;
686
+ }
687
+ }
688
+ function datePresetToISO(preset) {
689
+ const now = /* @__PURE__ */ new Date();
690
+ const days = preset === "last_7_days" ? 7 : preset === "last_30_days" ? 30 : 90;
691
+ return (/* @__PURE__ */ new Date(now.getTime() - days * 864e5)).toISOString();
692
+ }
693
+ var EXPERIMENT_STATUS_OPTIONS = [
694
+ {
695
+ value: "",
696
+ label: "All statuses"
697
+ },
698
+ {
699
+ value: "planned",
700
+ label: "Planned"
701
+ },
702
+ {
703
+ value: "ongoing",
704
+ label: "Ongoing"
705
+ },
706
+ {
707
+ value: "completed",
708
+ label: "Completed"
709
+ },
710
+ {
711
+ value: "cancelled",
712
+ label: "Cancelled"
713
+ }
714
+ ];
715
+ var EXPERIMENT_STATUS_VARIANT_MAP = {
716
+ planned: "default",
717
+ ongoing: "primary",
718
+ completed: "success",
719
+ cancelled: "error"
720
+ };
721
+ var EXPERIMENT_STATUS_LABELS = {
722
+ planned: "Planned",
723
+ ongoing: "Ongoing",
724
+ completed: "Completed",
725
+ cancelled: "Cancelled"
726
+ };
727
+ function formatExperimentStatus(status) {
728
+ if (!status) return "";
729
+ if (status in EXPERIMENT_STATUS_LABELS) return EXPERIMENT_STATUS_LABELS[status];
730
+ const label = String(status).replace(/[-_]+/g, " ").trim();
731
+ return label ? label.replace(/^\w/, (c) => c.toUpperCase()) : "";
732
+ }
733
+ function getExperimentStatusVariant(status) {
734
+ if (status && status in EXPERIMENT_STATUS_VARIANT_MAP) return EXPERIMENT_STATUS_VARIANT_MAP[status];
735
+ return "default";
736
+ }
737
+ function resolveExperimentCode(experiment) {
738
+ if (experiment.experiment_code) return experiment.experiment_code;
739
+ return experiment.id != null ? `EXP-${experiment.id}` : void 0;
740
+ }
741
+ var DATE_PRESET_OPTIONS = [
742
+ {
743
+ value: "",
744
+ label: "Any time"
745
+ },
746
+ {
747
+ value: "last_7_days",
748
+ label: "Last 7 days"
749
+ },
750
+ {
751
+ value: "last_30_days",
752
+ label: "Last 30 days"
753
+ },
754
+ {
755
+ value: "last_90_days",
756
+ label: "Last 90 days"
757
+ }
758
+ ];
759
+ var SORT_OPTIONS = [
760
+ {
761
+ value: "created_at:desc",
762
+ label: "Newest first"
763
+ },
764
+ {
765
+ value: "created_at:asc",
766
+ label: "Oldest first"
767
+ },
768
+ {
769
+ value: "updated_at:desc",
770
+ label: "Recently updated"
771
+ },
772
+ {
773
+ value: "name:asc",
774
+ label: "Name A–Z"
775
+ },
776
+ {
777
+ value: "name:desc",
778
+ label: "Name Z–A"
779
+ }
780
+ ];
781
+ //#endregion
676
782
  //#region src/composables/useApi.ts
677
783
  var apiClientInstance = null;
678
784
  var interceptorAttached = false;
785
+ function joinUrlPath(baseUrl, path) {
786
+ if (!path) return baseUrl;
787
+ const normalizedBase = baseUrl.replace(/\/+$/, "");
788
+ const normalizedPath = path.replace(/^\/+/, "/");
789
+ return `${normalizedBase}${normalizedPath.startsWith("/") ? normalizedPath : `/${normalizedPath}`}`;
790
+ }
679
791
  function getApiClient() {
680
792
  if (!apiClientInstance) apiClientInstance = axios.create({ headers: { "Content-Type": "application/json" } });
681
793
  return apiClientInstance;
@@ -745,10 +857,10 @@ function useApi(options = {}) {
745
857
  return blobUrl;
746
858
  }
747
859
  function buildUrl(path) {
748
- return `${options.baseUrl ?? settingsStore.getApiBaseUrl()}${path}`;
860
+ return joinUrlPath(options.baseUrl ?? settingsStore.getApiBaseUrl(), path);
749
861
  }
750
862
  function buildWsUrl(path) {
751
- return `${settingsStore.getWsBaseUrl()}${path}`;
863
+ return joinUrlPath(settingsStore.getWsBaseUrl(), path);
752
864
  }
753
865
  return {
754
866
  client: apiClient,
@@ -896,98 +1008,6 @@ function useRequestSyncState(defaultErrorMessage = "Request failed.") {
896
1008
  };
897
1009
  }
898
1010
  //#endregion
899
- //#region src/composables/experiment-utils.ts
900
- function formatExperimentDate(dateStr) {
901
- try {
902
- return new Date(dateStr).toLocaleDateString(void 0, {
903
- year: "numeric",
904
- month: "short",
905
- day: "numeric"
906
- });
907
- } catch {
908
- return dateStr;
909
- }
910
- }
911
- function datePresetToISO(preset) {
912
- const now = /* @__PURE__ */ new Date();
913
- const days = preset === "last_7_days" ? 7 : preset === "last_30_days" ? 30 : 90;
914
- return (/* @__PURE__ */ new Date(now.getTime() - days * 864e5)).toISOString();
915
- }
916
- var EXPERIMENT_STATUS_OPTIONS = [
917
- {
918
- value: "",
919
- label: "All statuses"
920
- },
921
- {
922
- value: "planned",
923
- label: "Planned"
924
- },
925
- {
926
- value: "ongoing",
927
- label: "Ongoing"
928
- },
929
- {
930
- value: "completed",
931
- label: "Completed"
932
- },
933
- {
934
- value: "cancelled",
935
- label: "Cancelled"
936
- }
937
- ];
938
- var EXPERIMENT_STATUS_VARIANT_MAP = {
939
- planned: "default",
940
- ongoing: "primary",
941
- completed: "success",
942
- cancelled: "error"
943
- };
944
- var EXPERIMENT_STATUS_LABELS = {
945
- planned: "Planned",
946
- ongoing: "Ongoing",
947
- completed: "Completed",
948
- cancelled: "Cancelled"
949
- };
950
- var DATE_PRESET_OPTIONS = [
951
- {
952
- value: "",
953
- label: "Any time"
954
- },
955
- {
956
- value: "last_7_days",
957
- label: "Last 7 days"
958
- },
959
- {
960
- value: "last_30_days",
961
- label: "Last 30 days"
962
- },
963
- {
964
- value: "last_90_days",
965
- label: "Last 90 days"
966
- }
967
- ];
968
- var SORT_OPTIONS = [
969
- {
970
- value: "created_at:desc",
971
- label: "Newest first"
972
- },
973
- {
974
- value: "created_at:asc",
975
- label: "Oldest first"
976
- },
977
- {
978
- value: "updated_at:desc",
979
- label: "Recently updated"
980
- },
981
- {
982
- value: "name:asc",
983
- label: "Name A–Z"
984
- },
985
- {
986
- value: "name:desc",
987
- label: "Name Z–A"
988
- }
989
- ];
990
- //#endregion
991
1011
  //#region src/composables/useExperimentSelector.ts
992
1012
  function getPlatformContext() {
993
1013
  if (typeof window === "undefined") return void 0;
@@ -1380,7 +1400,7 @@ function usePlatformContext(options = {}) {
1380
1400
  //#endregion
1381
1401
  //#region src/composables/useAppExperiment.ts
1382
1402
  var APP_EXPERIMENT_KEY = Symbol("app-experiment");
1383
- /** Manages the active experiment selection, save flow, and detach action for a plugin's app shell. */
1403
+ /** Manages the current experiment selection, save flow, and detach action for a plugin's AppTopBar app shell. */
1384
1404
  function useAppExperiment(options = {}) {
1385
1405
  const experimentName = ref();
1386
1406
  const experimentCode = ref();
@@ -1394,10 +1414,25 @@ function useAppExperiment(options = {}) {
1394
1414
  const showDetach = computed(() => experimentId.value !== null);
1395
1415
  const saveDisabled = computed(() => toValue(options.saveDisabled) ?? false);
1396
1416
  const saveDisabledMessage = computed(() => toValue(options.saveDisabledMessage));
1417
+ const popover = computed(() => ({
1418
+ experimentName: experimentName.value,
1419
+ experimentCode: experimentCode.value,
1420
+ experimentStatus: experimentStatus.value,
1421
+ showSave: showSave.value,
1422
+ showDetach: showDetach.value,
1423
+ saveDisabled: saveDisabled.value,
1424
+ saveDisabledMessage: saveDisabledMessage.value,
1425
+ saveLoading: saveLoading.value,
1426
+ saveSuccessMessage: saveSuccessMessage.value
1427
+ }));
1428
+ const selectorModal = computed(() => ({
1429
+ modelValue: showModal.value,
1430
+ currentExperimentId: experimentId.value
1431
+ }));
1397
1432
  function set(experiment) {
1398
1433
  experimentId.value = experiment.id;
1399
1434
  experimentName.value = experiment.name;
1400
- experimentCode.value = experiment.experiment_code ?? (experiment.id != null ? `EXP-${experiment.id}` : void 0);
1435
+ experimentCode.value = resolveExperimentCode(experiment);
1401
1436
  experimentStatus.value = experiment.status;
1402
1437
  }
1403
1438
  function clear() {
@@ -1406,6 +1441,10 @@ function useAppExperiment(options = {}) {
1406
1441
  experimentCode.value = void 0;
1407
1442
  experimentStatus.value = void 0;
1408
1443
  }
1444
+ if (options.experiment !== void 0) watch(() => toValue(options.experiment), (experiment) => {
1445
+ if (experiment) set(experiment);
1446
+ else clear();
1447
+ }, { immediate: true });
1409
1448
  function openModal() {
1410
1449
  showModal.value = true;
1411
1450
  }
@@ -1453,6 +1492,8 @@ function useAppExperiment(options = {}) {
1453
1492
  showDetach,
1454
1493
  saveDisabled,
1455
1494
  saveDisabledMessage,
1495
+ popover,
1496
+ selectorModal,
1456
1497
  openModal,
1457
1498
  closeModal,
1458
1499
  handleSelect,
@@ -1464,7 +1505,15 @@ function useAppExperiment(options = {}) {
1464
1505
  clear,
1465
1506
  experimentName: readonly(experimentName),
1466
1507
  experimentCode: readonly(experimentCode),
1467
- experimentId: readonly(experimentId)
1508
+ experimentStatus: readonly(experimentStatus),
1509
+ experimentId: readonly(experimentId),
1510
+ popover,
1511
+ selectorModal,
1512
+ openModal,
1513
+ closeModal,
1514
+ handleSelect,
1515
+ handleSave,
1516
+ handleDetach
1468
1517
  };
1469
1518
  }
1470
1519
  //#endregion
@@ -2660,66 +2709,246 @@ function normalizeIds(ids) {
2660
2709
  return [...new Set(ids.filter(Boolean))];
2661
2710
  }
2662
2711
  //#endregion
2663
- //#region src/composables/platformContextHelpers.ts
2664
- function getInjectedPlatformContext() {
2665
- if (typeof window === "undefined") return void 0;
2666
- return window.__MINT_PLATFORM__;
2667
- }
2668
- function getInjectedExperimentContext() {
2669
- return getInjectedPlatformContext();
2670
- }
2671
- function currentExperimentFromContext(context = getInjectedExperimentContext()) {
2672
- const candidate = context?.currentExperiment ?? context?.experiment;
2673
- return candidate && typeof candidate === "object" ? candidate : void 0;
2674
- }
2675
- function currentExperimentIdFromContext(context = getInjectedExperimentContext()) {
2676
- return parseExperimentId(context?.currentExperimentId ?? context?.experimentId ?? context?.currentExperiment ?? context?.experiment);
2677
- }
2678
- function currentExperimentIdFromUrl() {
2679
- if (typeof window === "undefined") return void 0;
2680
- const params = new URLSearchParams(window.location.search);
2681
- return parseExperimentId(params.get("experimentId") ?? params.get("experiment_id")) ?? experimentIdFromPath(window.location.pathname) ?? experimentIdFromPath(window.location.hash.replace(/^#\/?/, "/"));
2682
- }
2683
- function resolveCurrentExperimentId(context = getInjectedExperimentContext()) {
2684
- return currentExperimentIdFromContext(context) ?? currentExperimentIdFromUrl();
2685
- }
2686
- function parseExperimentId(value) {
2687
- if (typeof value === "number" && Number.isFinite(value)) return value;
2688
- if (typeof value === "string" && value.trim()) {
2689
- const parsed = Number(value);
2690
- return Number.isFinite(parsed) ? parsed : void 0;
2691
- }
2692
- if (value && typeof value === "object" && "id" in value) return parseExperimentId(value.id);
2693
- }
2694
- function experimentIdFromPath(path) {
2695
- const segments = path.split("/").filter(Boolean);
2696
- for (let index = 0; index < segments.length - 1; index += 1) {
2697
- const segment = segments[index]?.toLowerCase();
2698
- if (segment === "experiment" || segment === "experiments") return parseExperimentId(decodeURIComponent(segments[index + 1] ?? ""));
2699
- }
2700
- }
2701
- //#endregion
2702
- //#region src/composables/useExperimentSave.ts
2703
- /** Persists experiment design, analysis, and built-in template preset data through the platform API. */
2704
- function useExperimentSave(options = {}) {
2705
- const injectedContext = getInjectedPlatformContext();
2706
- const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl });
2707
- const pluginId = options.pluginId ?? injectedContext?.plugin?.id;
2708
- const schemaVersion = options.schemaVersion ?? "1.0";
2709
- const currentExperimentId = computed(() => {
2710
- return resolveCurrentExperimentId(injectedContext);
2711
- });
2712
- const hasCurrentExperiment = computed(() => currentExperimentId.value !== void 0);
2713
- const request = useRequestSyncState("Experiment request failed.");
2714
- const isSaving = ref(false);
2715
- const isLoading = ref(false);
2716
- const error = request.error;
2717
- const lastLoadedAt = request.lastLoadedAt;
2718
- const lastSavedAt = request.lastSavedAt;
2719
- function requireCurrentExperimentId() {
2720
- const id = currentExperimentId.value;
2721
- if (id === void 0) throw new Error("[MINT SDK] No current experiment is selected.");
2722
- return id;
2712
+ //#region src/composables/useScheduleDrag.ts
2713
+ /** Handles pointer-driven create, move, and resize drag interactions for the ScheduleCalendar grid. */
2714
+ function useScheduleDrag(options) {
2715
+ const isDragging = ref(false);
2716
+ const dragState = ref(null);
2717
+ const ghost = computed(() => {
2718
+ if (!dragState.value) return null;
2719
+ const state = dragState.value;
2720
+ const slotH = options.slotHeight.value;
2721
+ const slotMin = options.slotDuration.value;
2722
+ const dayStart = options.dayStartHour.value * 60;
2723
+ if (state.type === "create") {
2724
+ const deltaY = state.currentY - state.startY;
2725
+ const startOffset = state.startY;
2726
+ const endOffset = startOffset + deltaY;
2727
+ const topPx = Math.min(startOffset, endOffset);
2728
+ const bottomPx = Math.max(startOffset, endOffset);
2729
+ const startMinutes = dayStart + Math.round(topPx / slotH * slotMin);
2730
+ const endMinutes = dayStart + Math.round(bottomPx / slotH * slotMin);
2731
+ const snappedStart = snapMinutes(startMinutes, slotMin);
2732
+ const snappedEnd = Math.max(snapMinutes(endMinutes, slotMin), snappedStart + slotMin);
2733
+ const snappedTopPx = (snappedStart - dayStart) / slotMin * slotH;
2734
+ const snappedHeightPx = (snappedEnd - snappedStart) / slotMin * slotH;
2735
+ return {
2736
+ start: minutesToDate(state.startDate, snappedStart),
2737
+ end: minutesToDate(state.startDate, snappedEnd),
2738
+ dayIndex: state.dayIndex,
2739
+ style: {
2740
+ top: `${snappedTopPx}px`,
2741
+ height: `${snappedHeightPx}px`
2742
+ }
2743
+ };
2744
+ }
2745
+ if (state.type === "move" && state.event) {
2746
+ const deltaY = state.currentY - state.startY;
2747
+ const eventStart = dateToMinutes(new Date(state.event.start));
2748
+ const duration = dateToMinutes(new Date(state.event.end)) - eventStart;
2749
+ const newStart = snapMinutes(eventStart + Math.round(deltaY / slotH * slotMin), slotMin);
2750
+ const clamped = clampRange(newStart, newStart + duration, dayStart, options.dayEndHour.value * 60);
2751
+ const topPx = (clamped.start - dayStart) / slotMin * slotH;
2752
+ const heightPx = (clamped.end - clamped.start) / slotMin * slotH;
2753
+ return {
2754
+ start: minutesToDate(state.startDate, clamped.start),
2755
+ end: minutesToDate(state.startDate, clamped.end),
2756
+ dayIndex: state.currentDayIndex,
2757
+ style: {
2758
+ top: `${topPx}px`,
2759
+ height: `${heightPx}px`
2760
+ }
2761
+ };
2762
+ }
2763
+ if ((state.type === "resize-top" || state.type === "resize-bottom") && state.event) {
2764
+ const eventStart = dateToMinutes(new Date(state.event.start));
2765
+ const eventEnd = dateToMinutes(new Date(state.event.end));
2766
+ const deltaY = state.currentY - state.startY;
2767
+ const pixelDelta = Math.round(deltaY / slotH * slotMin);
2768
+ let newStart = eventStart;
2769
+ let newEnd = eventEnd;
2770
+ if (state.type === "resize-top") {
2771
+ newStart = snapMinutes(eventStart + pixelDelta, slotMin);
2772
+ newStart = Math.min(newStart, newEnd - slotMin);
2773
+ } else {
2774
+ newEnd = snapMinutes(eventEnd + pixelDelta, slotMin);
2775
+ newEnd = Math.max(newEnd, newStart + slotMin);
2776
+ }
2777
+ const clamped = clampRange(newStart, newEnd, dayStart, options.dayEndHour.value * 60);
2778
+ const topPx = (clamped.start - dayStart) / slotMin * slotH;
2779
+ const heightPx = (clamped.end - clamped.start) / slotMin * slotH;
2780
+ return {
2781
+ start: minutesToDate(state.startDate, clamped.start),
2782
+ end: minutesToDate(state.startDate, clamped.end),
2783
+ dayIndex: state.dayIndex,
2784
+ style: {
2785
+ top: `${topPx}px`,
2786
+ height: `${heightPx}px`
2787
+ }
2788
+ };
2789
+ }
2790
+ return null;
2791
+ });
2792
+ function startCreate(date, y, dayIndex) {
2793
+ if (options.readonly.value) return;
2794
+ isDragging.value = true;
2795
+ dragState.value = {
2796
+ type: "create",
2797
+ startDate: date,
2798
+ startY: y,
2799
+ currentY: y,
2800
+ dayIndex,
2801
+ currentDayIndex: dayIndex
2802
+ };
2803
+ addListeners();
2804
+ }
2805
+ function startMove(event, y, dayIndex) {
2806
+ if (options.readonly.value || event.draggable === false) return;
2807
+ isDragging.value = true;
2808
+ dragState.value = {
2809
+ type: "move",
2810
+ event,
2811
+ startDate: new Date(event.start),
2812
+ startY: y,
2813
+ currentY: y,
2814
+ dayIndex,
2815
+ currentDayIndex: dayIndex
2816
+ };
2817
+ addListeners();
2818
+ }
2819
+ function startResize(event, edge, y, dayIndex) {
2820
+ if (options.readonly.value || event.resizable === false) return;
2821
+ isDragging.value = true;
2822
+ dragState.value = {
2823
+ type: edge === "top" ? "resize-top" : "resize-bottom",
2824
+ event,
2825
+ startDate: new Date(event.start),
2826
+ startY: y,
2827
+ currentY: y,
2828
+ dayIndex,
2829
+ currentDayIndex: dayIndex
2830
+ };
2831
+ addListeners();
2832
+ }
2833
+ function onPointerMove(e) {
2834
+ if (!dragState.value) return;
2835
+ dragState.value = {
2836
+ ...dragState.value,
2837
+ currentY: e.clientY
2838
+ };
2839
+ }
2840
+ function onPointerUp() {
2841
+ if (!dragState.value || !ghost.value) {
2842
+ cleanup();
2843
+ return;
2844
+ }
2845
+ const g = ghost.value;
2846
+ const state = dragState.value;
2847
+ if (state.type === "create" && options.onCreateComplete) options.onCreateComplete(g.start, g.end);
2848
+ else if (state.type === "move" && state.event && options.onMoveComplete) options.onMoveComplete(state.event, g.start, g.end);
2849
+ else if ((state.type === "resize-top" || state.type === "resize-bottom") && state.event && options.onResizeComplete) options.onResizeComplete(state.event, g.start, g.end);
2850
+ cleanup();
2851
+ }
2852
+ function addListeners() {
2853
+ document.addEventListener("pointermove", onPointerMove);
2854
+ document.addEventListener("pointerup", onPointerUp);
2855
+ }
2856
+ function cleanup() {
2857
+ isDragging.value = false;
2858
+ dragState.value = null;
2859
+ document.removeEventListener("pointermove", onPointerMove);
2860
+ document.removeEventListener("pointerup", onPointerUp);
2861
+ }
2862
+ onUnmounted(cleanup);
2863
+ return {
2864
+ isDragging,
2865
+ dragState,
2866
+ ghost,
2867
+ startCreate,
2868
+ startMove,
2869
+ startResize
2870
+ };
2871
+ }
2872
+ function snapMinutes(minutes, step) {
2873
+ return Math.round(minutes / step) * step;
2874
+ }
2875
+ function dateToMinutes(date) {
2876
+ return date.getHours() * 60 + date.getMinutes();
2877
+ }
2878
+ function minutesToDate(baseDate, minutes) {
2879
+ const d = new Date(baseDate);
2880
+ d.setHours(Math.floor(minutes / 60), minutes % 60, 0, 0);
2881
+ return d;
2882
+ }
2883
+ function clampRange(start, end, min, max) {
2884
+ const s = Math.max(start, min);
2885
+ const e = Math.min(end, max);
2886
+ return {
2887
+ start: s,
2888
+ end: Math.max(e, s)
2889
+ };
2890
+ }
2891
+ //#endregion
2892
+ //#region src/composables/platformContextHelpers.ts
2893
+ function getInjectedPlatformContext() {
2894
+ if (typeof window === "undefined") return void 0;
2895
+ return window.__MINT_PLATFORM__;
2896
+ }
2897
+ function getInjectedExperimentContext() {
2898
+ return getInjectedPlatformContext();
2899
+ }
2900
+ function currentExperimentFromContext(context = getInjectedExperimentContext()) {
2901
+ const candidate = context?.currentExperiment ?? context?.experiment;
2902
+ return candidate && typeof candidate === "object" ? candidate : void 0;
2903
+ }
2904
+ function currentExperimentIdFromContext(context = getInjectedExperimentContext()) {
2905
+ return parseExperimentId(context?.currentExperimentId ?? context?.experimentId ?? context?.currentExperiment ?? context?.experiment);
2906
+ }
2907
+ function currentExperimentIdFromUrl() {
2908
+ if (typeof window === "undefined") return void 0;
2909
+ const params = new URLSearchParams(window.location.search);
2910
+ return parseExperimentId(params.get("experimentId") ?? params.get("experiment_id")) ?? experimentIdFromPath(window.location.pathname) ?? experimentIdFromPath(window.location.hash.replace(/^#\/?/, "/"));
2911
+ }
2912
+ function resolveCurrentExperimentId(context = getInjectedExperimentContext()) {
2913
+ return currentExperimentIdFromContext(context) ?? currentExperimentIdFromUrl();
2914
+ }
2915
+ function parseExperimentId(value) {
2916
+ if (typeof value === "number" && Number.isFinite(value)) return value;
2917
+ if (typeof value === "string" && value.trim()) {
2918
+ const parsed = Number(value);
2919
+ return Number.isFinite(parsed) ? parsed : void 0;
2920
+ }
2921
+ if (value && typeof value === "object" && "id" in value) return parseExperimentId(value.id);
2922
+ }
2923
+ function experimentIdFromPath(path) {
2924
+ const segments = path.split("/").filter(Boolean);
2925
+ for (let index = 0; index < segments.length - 1; index += 1) {
2926
+ const segment = segments[index]?.toLowerCase();
2927
+ if (segment === "experiment" || segment === "experiments") return parseExperimentId(decodeURIComponent(segments[index + 1] ?? ""));
2928
+ }
2929
+ }
2930
+ //#endregion
2931
+ //#region src/composables/useExperimentSave.ts
2932
+ /** Persists experiment design, analysis, and built-in template preset data through the platform API. */
2933
+ function useExperimentSave(options = {}) {
2934
+ const injectedContext = getInjectedPlatformContext();
2935
+ const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl });
2936
+ const pluginId = options.pluginId ?? injectedContext?.plugin?.id;
2937
+ const schemaVersion = options.schemaVersion ?? "1.0";
2938
+ const currentExperimentId = computed(() => {
2939
+ return resolveCurrentExperimentId(injectedContext);
2940
+ });
2941
+ const hasCurrentExperiment = computed(() => currentExperimentId.value !== void 0);
2942
+ const request = useRequestSyncState("Experiment request failed.");
2943
+ const isSaving = ref(false);
2944
+ const isLoading = ref(false);
2945
+ const error = request.error;
2946
+ const lastLoadedAt = request.lastLoadedAt;
2947
+ const lastSavedAt = request.lastSavedAt;
2948
+ function requireCurrentExperimentId() {
2949
+ const id = currentExperimentId.value;
2950
+ if (id === void 0) throw new Error("[MINT SDK] No current experiment is selected.");
2951
+ return id;
2723
2952
  }
2724
2953
  function currentExperimentIdOrError(action) {
2725
2954
  const id = currentExperimentId.value;
@@ -3022,6 +3251,8 @@ function useBioTemplateComponents(target) {
3022
3251
  return {
3023
3252
  bindings: getBioTemplateComponentBindings(target),
3024
3253
  imports: toBioTemplateComponentImports(target),
3254
+ componentBindings: typeof target === "string" ? [] : toBioTemplateComponentBindings(target),
3255
+ componentBindingsById: typeof target === "string" ? {} : toBioTemplateComponentBindingsById(target),
3025
3256
  componentProps: typeof target === "string" ? [] : toBioTemplateComponentProps(target),
3026
3257
  componentPropsById: typeof target === "string" ? {} : toBioTemplateComponentPropsById(target),
3027
3258
  componentPropsByComponent: typeof target === "string" ? {} : toBioTemplateComponentPropsByComponent(target),
@@ -3043,10 +3274,10 @@ function useBioTemplateWorkspace(target, options = {}) {
3043
3274
  form: workspace.bindings.form,
3044
3275
  sidebar: workspace.bindings.sidebar,
3045
3276
  topBar: workspace.bindings.topBar,
3046
- topBarTabs: workspace.bindings.topBarTabs,
3047
3277
  topBarSettings: workspace.bindings.topBarSettings,
3048
3278
  pillNav: workspace.bindings.pillNav,
3049
3279
  componentBindings: components.bindings,
3280
+ componentBindingsById: components.componentBindingsById,
3050
3281
  componentProps: components.componentProps,
3051
3282
  componentPropsById: components.componentPropsById,
3052
3283
  componentPropsByComponent: components.componentPropsByComponent,
@@ -3070,6 +3301,7 @@ function useBioTemplateWorkspace(target, options = {}) {
3070
3301
  bindings,
3071
3302
  renderer,
3072
3303
  componentBindings: components.bindings,
3304
+ componentBindingsById: components.componentBindingsById,
3073
3305
  componentImports: components.imports,
3074
3306
  componentProps: components.componentProps,
3075
3307
  componentPropsById: components.componentPropsById,
@@ -3105,11 +3337,12 @@ function useBioTemplatePackWorkspace(packId, options = {}) {
3105
3337
  const renderer = computed(() => ({ target: collection.value }));
3106
3338
  const form = computed(() => workspace.value.form);
3107
3339
  const sidebar = computed(() => workspace.value.sidebar);
3108
- const topBar = computed(() => workspace.value.topBar);
3340
+ const topBar = computed(() => workspace.value.topBar.value);
3109
3341
  const pillNav = computed(() => workspace.value.pillNav);
3110
3342
  const topBarSettings = computed(() => workspace.value.topBarSettings);
3111
3343
  const bindings = computed(() => workspace.value.bindings);
3112
3344
  const componentBindings = computed(() => workspace.value.componentBindings);
3345
+ const componentBindingsById = computed(() => workspace.value.componentBindingsById);
3113
3346
  const componentImports = computed(() => workspace.value.componentImports);
3114
3347
  const componentProps = computed(() => workspace.value.componentProps);
3115
3348
  const componentPropsById = computed(() => workspace.value.componentPropsById);
@@ -3151,6 +3384,7 @@ function useBioTemplatePackWorkspace(packId, options = {}) {
3151
3384
  topBarSettings,
3152
3385
  bindings,
3153
3386
  componentBindings,
3387
+ componentBindingsById,
3154
3388
  componentImports,
3155
3389
  componentProps,
3156
3390
  componentPropsById,
@@ -3189,6 +3423,7 @@ function useBioTemplatePresetWorkspace(presetId, options = {}) {
3189
3423
  const workspace = computed(() => useBioTemplateWorkspace(collection.value, resolvedControlOptions));
3190
3424
  const renderer = computed(() => ({ target: collection.value }));
3191
3425
  const componentBindings = computed(() => workspace.value.componentBindings);
3426
+ const componentBindingsById = computed(() => workspace.value.componentBindingsById);
3192
3427
  const componentImports = computed(() => workspace.value.componentImports);
3193
3428
  const componentProps = computed(() => workspace.value.componentProps);
3194
3429
  const componentPropsById = computed(() => workspace.value.componentPropsById);
@@ -3208,11 +3443,6 @@ function useBioTemplatePresetWorkspace(presetId, options = {}) {
3208
3443
  values: controlValues.value,
3209
3444
  "onUpdate:values": setControlValues
3210
3445
  });
3211
- const topBar = reactive({
3212
- tabs: controls.topBarTabs,
3213
- currentTabId: activeControlView.value,
3214
- onTabSelect: (tab) => setActiveControlView(tab.id)
3215
- });
3216
3446
  const pillNav = reactive({
3217
3447
  items: controls.viewItems,
3218
3448
  currentItemId: activeControlView.value,
@@ -3232,12 +3462,6 @@ function useBioTemplatePresetWorkspace(presetId, options = {}) {
3232
3462
  onPillSelect: pillNav.onSelect,
3233
3463
  ...topBarSettings
3234
3464
  }));
3235
- const topBarTabsProps = computed(() => ({
3236
- tabs: topBar.tabs,
3237
- currentTabId: topBar.currentTabId,
3238
- onTabSelect: topBar.onTabSelect,
3239
- ...topBarSettings
3240
- }));
3241
3465
  function getComponentProps(component, options) {
3242
3466
  return workspace.value.getComponentProps(component, options);
3243
3467
  }
@@ -3246,10 +3470,10 @@ function useBioTemplatePresetWorkspace(presetId, options = {}) {
3246
3470
  form,
3247
3471
  sidebar,
3248
3472
  topBar: topBarProps,
3249
- topBarTabs: topBarTabsProps,
3250
3473
  topBarSettings,
3251
3474
  pillNav,
3252
3475
  componentBindings,
3476
+ componentBindingsById,
3253
3477
  componentProps,
3254
3478
  componentPropsById,
3255
3479
  componentPropsByComponent,
@@ -3266,11 +3490,9 @@ function useBioTemplatePresetWorkspace(presetId, options = {}) {
3266
3490
  }, { deep: true });
3267
3491
  watch(activeControlView, (viewId) => {
3268
3492
  sidebar.activeView = viewId;
3269
- topBar.currentTabId = viewId;
3270
3493
  pillNav.currentItemId = viewId;
3271
3494
  }, { flush: "sync" });
3272
3495
  watch(() => sidebar.activeView, setActiveControlView, { flush: "sync" });
3273
- watch(() => topBar.currentTabId, setActiveControlView, { flush: "sync" });
3274
3496
  watch(() => pillNav.currentItemId, setActiveControlView, { flush: "sync" });
3275
3497
  function applyCollection(value) {
3276
3498
  return templateCollection.apply(value);
@@ -3323,13 +3545,14 @@ function useBioTemplatePresetWorkspace(presetId, options = {}) {
3323
3545
  activeControlView,
3324
3546
  form,
3325
3547
  sidebar,
3326
- topBar,
3548
+ topBar: topBarProps,
3327
3549
  pillNav,
3328
3550
  topBarSettings,
3329
3551
  bindings,
3330
3552
  renderer,
3331
3553
  workspace,
3332
3554
  componentBindings,
3555
+ componentBindingsById,
3333
3556
  componentImports,
3334
3557
  componentProps,
3335
3558
  componentPropsById,
@@ -4186,186 +4409,6 @@ function useExperimentData(options = {}) {
4186
4409
  };
4187
4410
  }
4188
4411
  //#endregion
4189
- //#region src/composables/useScheduleDrag.ts
4190
- /** Handles pointer-driven create, move, and resize drag interactions for the ScheduleCalendar grid. */
4191
- function useScheduleDrag(options) {
4192
- const isDragging = ref(false);
4193
- const dragState = ref(null);
4194
- const ghost = computed(() => {
4195
- if (!dragState.value) return null;
4196
- const state = dragState.value;
4197
- const slotH = options.slotHeight.value;
4198
- const slotMin = options.slotDuration.value;
4199
- const dayStart = options.dayStartHour.value * 60;
4200
- if (state.type === "create") {
4201
- const deltaY = state.currentY - state.startY;
4202
- const startOffset = state.startY;
4203
- const endOffset = startOffset + deltaY;
4204
- const topPx = Math.min(startOffset, endOffset);
4205
- const bottomPx = Math.max(startOffset, endOffset);
4206
- const startMinutes = dayStart + Math.round(topPx / slotH * slotMin);
4207
- const endMinutes = dayStart + Math.round(bottomPx / slotH * slotMin);
4208
- const snappedStart = snapMinutes(startMinutes, slotMin);
4209
- const snappedEnd = Math.max(snapMinutes(endMinutes, slotMin), snappedStart + slotMin);
4210
- const snappedTopPx = (snappedStart - dayStart) / slotMin * slotH;
4211
- const snappedHeightPx = (snappedEnd - snappedStart) / slotMin * slotH;
4212
- return {
4213
- start: minutesToDate(state.startDate, snappedStart),
4214
- end: minutesToDate(state.startDate, snappedEnd),
4215
- dayIndex: state.dayIndex,
4216
- style: {
4217
- top: `${snappedTopPx}px`,
4218
- height: `${snappedHeightPx}px`
4219
- }
4220
- };
4221
- }
4222
- if (state.type === "move" && state.event) {
4223
- const deltaY = state.currentY - state.startY;
4224
- const eventStart = dateToMinutes(new Date(state.event.start));
4225
- const duration = dateToMinutes(new Date(state.event.end)) - eventStart;
4226
- const newStart = snapMinutes(eventStart + Math.round(deltaY / slotH * slotMin), slotMin);
4227
- const clamped = clampRange(newStart, newStart + duration, dayStart, options.dayEndHour.value * 60);
4228
- const topPx = (clamped.start - dayStart) / slotMin * slotH;
4229
- const heightPx = (clamped.end - clamped.start) / slotMin * slotH;
4230
- return {
4231
- start: minutesToDate(state.startDate, clamped.start),
4232
- end: minutesToDate(state.startDate, clamped.end),
4233
- dayIndex: state.currentDayIndex,
4234
- style: {
4235
- top: `${topPx}px`,
4236
- height: `${heightPx}px`
4237
- }
4238
- };
4239
- }
4240
- if ((state.type === "resize-top" || state.type === "resize-bottom") && state.event) {
4241
- const eventStart = dateToMinutes(new Date(state.event.start));
4242
- const eventEnd = dateToMinutes(new Date(state.event.end));
4243
- const deltaY = state.currentY - state.startY;
4244
- const pixelDelta = Math.round(deltaY / slotH * slotMin);
4245
- let newStart = eventStart;
4246
- let newEnd = eventEnd;
4247
- if (state.type === "resize-top") {
4248
- newStart = snapMinutes(eventStart + pixelDelta, slotMin);
4249
- newStart = Math.min(newStart, newEnd - slotMin);
4250
- } else {
4251
- newEnd = snapMinutes(eventEnd + pixelDelta, slotMin);
4252
- newEnd = Math.max(newEnd, newStart + slotMin);
4253
- }
4254
- const clamped = clampRange(newStart, newEnd, dayStart, options.dayEndHour.value * 60);
4255
- const topPx = (clamped.start - dayStart) / slotMin * slotH;
4256
- const heightPx = (clamped.end - clamped.start) / slotMin * slotH;
4257
- return {
4258
- start: minutesToDate(state.startDate, clamped.start),
4259
- end: minutesToDate(state.startDate, clamped.end),
4260
- dayIndex: state.dayIndex,
4261
- style: {
4262
- top: `${topPx}px`,
4263
- height: `${heightPx}px`
4264
- }
4265
- };
4266
- }
4267
- return null;
4268
- });
4269
- function startCreate(date, y, dayIndex) {
4270
- if (options.readonly.value) return;
4271
- isDragging.value = true;
4272
- dragState.value = {
4273
- type: "create",
4274
- startDate: date,
4275
- startY: y,
4276
- currentY: y,
4277
- dayIndex,
4278
- currentDayIndex: dayIndex
4279
- };
4280
- addListeners();
4281
- }
4282
- function startMove(event, y, dayIndex) {
4283
- if (options.readonly.value || event.draggable === false) return;
4284
- isDragging.value = true;
4285
- dragState.value = {
4286
- type: "move",
4287
- event,
4288
- startDate: new Date(event.start),
4289
- startY: y,
4290
- currentY: y,
4291
- dayIndex,
4292
- currentDayIndex: dayIndex
4293
- };
4294
- addListeners();
4295
- }
4296
- function startResize(event, edge, y, dayIndex) {
4297
- if (options.readonly.value || event.resizable === false) return;
4298
- isDragging.value = true;
4299
- dragState.value = {
4300
- type: edge === "top" ? "resize-top" : "resize-bottom",
4301
- event,
4302
- startDate: new Date(event.start),
4303
- startY: y,
4304
- currentY: y,
4305
- dayIndex,
4306
- currentDayIndex: dayIndex
4307
- };
4308
- addListeners();
4309
- }
4310
- function onPointerMove(e) {
4311
- if (!dragState.value) return;
4312
- dragState.value = {
4313
- ...dragState.value,
4314
- currentY: e.clientY
4315
- };
4316
- }
4317
- function onPointerUp() {
4318
- if (!dragState.value || !ghost.value) {
4319
- cleanup();
4320
- return;
4321
- }
4322
- const g = ghost.value;
4323
- const state = dragState.value;
4324
- if (state.type === "create" && options.onCreateComplete) options.onCreateComplete(g.start, g.end);
4325
- else if (state.type === "move" && state.event && options.onMoveComplete) options.onMoveComplete(state.event, g.start, g.end);
4326
- else if ((state.type === "resize-top" || state.type === "resize-bottom") && state.event && options.onResizeComplete) options.onResizeComplete(state.event, g.start, g.end);
4327
- cleanup();
4328
- }
4329
- function addListeners() {
4330
- document.addEventListener("pointermove", onPointerMove);
4331
- document.addEventListener("pointerup", onPointerUp);
4332
- }
4333
- function cleanup() {
4334
- isDragging.value = false;
4335
- dragState.value = null;
4336
- document.removeEventListener("pointermove", onPointerMove);
4337
- document.removeEventListener("pointerup", onPointerUp);
4338
- }
4339
- onUnmounted(cleanup);
4340
- return {
4341
- isDragging,
4342
- dragState,
4343
- ghost,
4344
- startCreate,
4345
- startMove,
4346
- startResize
4347
- };
4348
- }
4349
- function snapMinutes(minutes, step) {
4350
- return Math.round(minutes / step) * step;
4351
- }
4352
- function dateToMinutes(date) {
4353
- return date.getHours() * 60 + date.getMinutes();
4354
- }
4355
- function minutesToDate(baseDate, minutes) {
4356
- const d = new Date(baseDate);
4357
- d.setHours(Math.floor(minutes / 60), minutes % 60, 0, 0);
4358
- return d;
4359
- }
4360
- function clampRange(start, end, min, max) {
4361
- const s = Math.max(start, min);
4362
- const e = Math.min(end, max);
4363
- return {
4364
- start: s,
4365
- end: Math.max(e, s)
4366
- };
4367
- }
4368
- //#endregion
4369
- export { normalizeSearchQuery as $, useAutoGroup as A, EXPERIMENT_STATUS_VARIANT_MAP as B, useSampleGroups as C, DEFAULT_COLORS as D, hslToHex as E, usePlatformContext as F, useDebouncedWatch as G, datePresetToISO as H, useExperimentSelector as I, useFormBuilder as J, useApi as K, DATE_PRESET_OPTIONS as L, useDoseCalculator as M, APP_EXPERIMENT_KEY as N, extractSamplesFromDesignData as O, useAppExperiment as P, candidateMatchesSearch as Q, EXPERIMENT_STATUS_LABELS as R, useExpansionSet as S, hexToHsl as T, formatExperimentDate as U, SORT_OPTIONS as V, useRequestSyncState as W, useTheme as X, useForm as Y, useToast as Z, useTemplateCollection as _, DEFAULT_UNITS as a, getInjectedPlatformContext as b, useGroupAssignment as c, useBioTemplatePackWorkspace as d, useTextSearch as et, useBioTemplateWorkspace as f, useBioTemplateControls as g, useBioTemplateComponents as h, DEFAULT_PRESETS as i, useWellPlateEditor as j, parseCSV as k, useRackEditor as l, toBioTemplateComponentPropsByComponent as m, useExperimentData as n, useSortedItems as nt, generateDilutionSeries as o, getBioTemplateComponentProps as p, evaluateCondition as q, useProtocolTemplates as r, useReagentSeries as s, useScheduleDrag as t, compareSortValues as tt, useBioTemplatePresetWorkspace as u, useExperimentSave as v, deriveShade as w, resolveCurrentExperimentId as x, currentExperimentFromContext as y, EXPERIMENT_STATUS_OPTIONS as z };
4412
+ export { useTheme as $, useAutoGroup as A, DATE_PRESET_OPTIONS as B, useSampleGroups as C, DEFAULT_COLORS as D, hslToHex as E, usePlatformContext as F, datePresetToISO as G, EXPERIMENT_STATUS_OPTIONS as H, useExperimentSelector as I, getExperimentStatusVariant as J, formatExperimentDate as K, useRequestSyncState as L, useDoseCalculator as M, APP_EXPERIMENT_KEY as N, extractSamplesFromDesignData as O, useAppExperiment as P, useForm as Q, useDebouncedWatch as R, useExpansionSet as S, hexToHsl as T, EXPERIMENT_STATUS_VARIANT_MAP as U, EXPERIMENT_STATUS_LABELS as V, SORT_OPTIONS as W, evaluateCondition as X, resolveExperimentCode as Y, useFormBuilder as Z, useExperimentSave as _, generateDilutionSeries as a, useSortedItems as at, resolveCurrentExperimentId as b, useRackEditor as c, useBioTemplateWorkspace as d, useToast as et, getBioTemplateComponentProps as f, useTemplateCollection as g, useBioTemplateControls as h, DEFAULT_UNITS as i, compareSortValues as it, useWellPlateEditor as j, parseCSV as k, useBioTemplatePresetWorkspace as l, useBioTemplateComponents as m, useProtocolTemplates as n, normalizeSearchQuery as nt, useReagentSeries as o, toBioTemplateComponentPropsByComponent as p, formatExperimentStatus as q, DEFAULT_PRESETS as r, useTextSearch as rt, useGroupAssignment as s, useExperimentData as t, candidateMatchesSearch as tt, useBioTemplatePackWorkspace as u, currentExperimentFromContext as v, deriveShade as w, useScheduleDrag as x, getInjectedPlatformContext as y, useApi as z };
4370
4413
 
4371
- //# sourceMappingURL=useScheduleDrag-D4oWdh41.js.map
4414
+ //# sourceMappingURL=useExperimentData-CM6Y0u5L.js.map