@morscherlab/mint-sdk 1.2.0-beta.4 → 1.2.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 (136) hide show
  1. package/dist/{EmptyState-p90quq8Z.js → EmptyState-NBqf2_8z.js} +2 -2
  2. package/dist/{EmptyState-p90quq8Z.js.map → EmptyState-NBqf2_8z.js.map} +1 -1
  3. package/dist/{ExperimentPopover-tLwJ7XF4.js → ExperimentPopover-CfOdh5Rm.js} +3 -3
  4. package/dist/{ExperimentPopover-tLwJ7XF4.js.map → ExperimentPopover-CfOdh5Rm.js.map} +1 -1
  5. package/dist/{ExperimentSelectorModal-B6P2msT4.js → ExperimentSelectorModal-DvIagrxY.js} +4 -4
  6. package/dist/{ExperimentSelectorModal-B6P2msT4.js.map → ExperimentSelectorModal-DvIagrxY.js.map} +1 -1
  7. package/dist/{SettingsModal-CkNM8qAI.js → SettingsModal-W3TYfnP9.js} +4 -7
  8. package/dist/SettingsModal-W3TYfnP9.js.map +1 -0
  9. package/dist/__tests__/components/PluginWorkspaceView.internal.test.d.ts +1 -0
  10. package/dist/__tests__/components/SecretInput.test.d.ts +1 -0
  11. package/dist/__tests__/components/WellEditPopupInternal.test.d.ts +1 -0
  12. package/dist/components/AppAvatarMenu.vue.d.ts +4 -0
  13. package/dist/components/AppSidebar.vue.d.ts +1 -1
  14. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +1 -1
  15. package/dist/components/DropdownButton.vue.d.ts +2 -0
  16. package/dist/components/FitPanel.vue.d.ts +1 -1
  17. package/dist/components/PluginWorkspaceView.props.d.ts +29 -1
  18. package/dist/components/PluginWorkspaceView.vue.d.ts +5 -5
  19. package/dist/components/SecretInput.vue.d.ts +28 -0
  20. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +1 -1
  21. package/dist/components/index.d.ts +9 -0
  22. package/dist/components/index.js +8 -8
  23. package/dist/components/internal/WellEditPopupInternal.vue.d.ts +3 -1
  24. package/dist/components/internal/WorkspaceControlsInternal.vue.d.ts +23 -0
  25. package/dist/components/internal/WorkspaceShellInternal.vue.d.ts +70 -0
  26. package/dist/components/internal/WorkspaceSidebarInternal.vue.d.ts +43 -0
  27. package/dist/{components-MGNL6tRn.js → components-B80gV0P6.js} +575 -561
  28. package/dist/components-B80gV0P6.js.map +1 -0
  29. package/dist/composables/flattenWorkspaceBindings.d.ts +61 -0
  30. package/dist/composables/index.d.ts +5 -0
  31. package/dist/composables/index.js +9 -9
  32. package/dist/composables/pluginWorkspaceModel.d.ts +164 -0
  33. package/dist/composables/useAsync.d.ts +5 -1
  34. package/dist/composables/useBioTemplateWorkspace.d.ts +4 -20
  35. package/dist/composables/useCommandHistory.d.ts +5 -1
  36. package/dist/composables/useFocusTrap.d.ts +21 -0
  37. package/dist/composables/useMenuKeyboard.d.ts +29 -0
  38. package/dist/composables/useOptimisticMutation.d.ts +5 -1
  39. package/dist/composables/usePluginWorkspace.d.ts +51 -0
  40. package/dist/composables/useResourceCrud.d.ts +5 -1
  41. package/dist/composables/useRovingFocus.d.ts +24 -0
  42. package/dist/composables/useWellPainting.d.ts +5 -1
  43. package/dist/composables/useWellPlateAdapter.d.ts +5 -1
  44. package/dist/composables/useWellPlateValidation.d.ts +5 -1
  45. package/dist/composables/workspaceRecipes.d.ts +188 -0
  46. package/dist/{composables-CKxOcvgh.js → composables-C8mmD5ta.js} +65 -17
  47. package/dist/composables-C8mmD5ta.js.map +1 -0
  48. package/dist/{experiment-utils-CLnzPPq5.js → experiment-utils-DV3DrvE_.js} +70 -51
  49. package/dist/experiment-utils-DV3DrvE_.js.map +1 -0
  50. package/dist/index.d.ts +1 -0
  51. package/dist/index.js +13 -13
  52. package/dist/install.js +7 -7
  53. package/dist/styles.css +100 -49
  54. package/dist/templates/builderReadUtils.d.ts +1 -1
  55. package/dist/templates/index.js +4 -4
  56. package/dist/{templates-BTJ9C9Kb.js → templates-CK72aKO1.js} +14 -23
  57. package/dist/templates-CK72aKO1.js.map +1 -0
  58. package/dist/types/form-builder.d.ts +1 -1
  59. package/dist/{useControlSchema-CXhxTul7.js → useControlSchema-_OVhavKq.js} +280 -63
  60. package/dist/useControlSchema-_OVhavKq.js.map +1 -0
  61. package/dist/{useDropdownState-DSb3BC3N.js → useDropdownState-D5G4UvWd.js} +2 -2
  62. package/dist/{useDropdownState-DSb3BC3N.js.map → useDropdownState-D5G4UvWd.js.map} +1 -1
  63. package/dist/{useExperimentSelector-D0pjj9WH.js → useExperimentSelector-DSEckf_g.js} +3 -3
  64. package/dist/{useExperimentSelector-D0pjj9WH.js.map → useExperimentSelector-DSEckf_g.js.map} +1 -1
  65. package/dist/{useFormBuilder-CuOFB3aP.js → useFormBuilder-Bd714C7y.js} +2 -2
  66. package/dist/{useFormBuilder-CuOFB3aP.js.map → useFormBuilder-Bd714C7y.js.map} +1 -1
  67. package/dist/{useProtocolTemplates-BWhQIWV5.js → useProtocolTemplates-Dq5Pzwyd.js} +481 -46
  68. package/dist/useProtocolTemplates-Dq5Pzwyd.js.map +1 -0
  69. package/dist/utils/pluginSecrets.d.ts +53 -0
  70. package/dist/utils/records.d.ts +4 -0
  71. package/package.json +1 -1
  72. package/src/__tests__/components/AppAvatarMenu.test.ts +123 -0
  73. package/src/__tests__/components/BaseTabs.test.ts +67 -0
  74. package/src/__tests__/components/DropdownButton.test.ts +134 -0
  75. package/src/__tests__/components/PluginWorkspaceView.internal.test.ts +230 -0
  76. package/src/__tests__/components/SecretInput.test.ts +87 -0
  77. package/src/__tests__/components/WellEditPopupInternal.test.ts +108 -0
  78. package/src/__tests__/composables/usePluginClient.test.ts +140 -0
  79. package/src/components/AppAvatarMenu.vue +34 -3
  80. package/src/components/BaseModal.vue +8 -56
  81. package/src/components/BaseTabs.vue +26 -1
  82. package/src/components/BioTemplateExperimentWorkspaceView.vue +10 -21
  83. package/src/components/BioTemplatePackWorkspaceView.vue +11 -15
  84. package/src/components/BioTemplatePresetWorkspaceView.vue +8 -13
  85. package/src/components/ComponentBindingRenderer.vue +1 -3
  86. package/src/components/ControlWorkspaceView.vue +11 -22
  87. package/src/components/DoseDesignWorkspaceView.vue +6 -25
  88. package/src/components/DropdownButton.vue +32 -2
  89. package/src/components/PluginWorkspaceView.props.ts +38 -0
  90. package/src/components/PluginWorkspaceView.vue +53 -224
  91. package/src/components/SecretInput.story.vue +79 -0
  92. package/src/components/SecretInput.vue +150 -0
  93. package/src/components/index.ts +9 -0
  94. package/src/components/internal/WellEditPopupInternal.vue +20 -1
  95. package/src/components/internal/WorkspaceControlsInternal.vue +20 -0
  96. package/src/components/internal/WorkspaceShellInternal.vue +125 -0
  97. package/src/components/internal/WorkspaceSidebarInternal.vue +82 -0
  98. package/src/composables/experimentDesignData.ts +1 -3
  99. package/src/composables/flattenWorkspaceBindings.ts +104 -0
  100. package/src/composables/formBuilderRegistry.ts +5 -0
  101. package/src/composables/index.ts +26 -0
  102. package/src/composables/pluginWorkspaceModel.ts +182 -0
  103. package/src/composables/useAsync.ts +5 -1
  104. package/src/composables/useBioTemplatePackWorkspace.ts +15 -9
  105. package/src/composables/useBioTemplatePresetWorkspace.ts +14 -15
  106. package/src/composables/useBioTemplateWorkspace.ts +18 -43
  107. package/src/composables/useCommandHistory.ts +5 -1
  108. package/src/composables/useFocusTrap.ts +91 -0
  109. package/src/composables/useMenuKeyboard.ts +90 -0
  110. package/src/composables/useOptimisticMutation.ts +5 -1
  111. package/src/composables/usePluginClient.ts +42 -4
  112. package/src/composables/usePluginWorkspace.ts +208 -0
  113. package/src/composables/useResourceCrud.ts +5 -1
  114. package/src/composables/useRovingFocus.ts +66 -0
  115. package/src/composables/useWellPainting.ts +5 -1
  116. package/src/composables/useWellPlateAdapter.ts +5 -1
  117. package/src/composables/useWellPlateValidation.ts +5 -1
  118. package/src/composables/workspaceRecipes.ts +267 -0
  119. package/src/index.ts +16 -0
  120. package/src/styles/components/secret-input.css +60 -0
  121. package/src/styles/index.css +1 -0
  122. package/src/templates/builderReadUtils.ts +1 -3
  123. package/src/templates/componentTargetResolvers.ts +1 -3
  124. package/src/templates/controlSchemaTargets.ts +1 -3
  125. package/src/types/form-builder.ts +1 -0
  126. package/src/utils/formModelSync.ts +1 -3
  127. package/src/utils/pluginIcon.ts +1 -3
  128. package/src/utils/pluginSecrets.ts +102 -0
  129. package/src/utils/records.ts +9 -0
  130. package/dist/SettingsModal-CkNM8qAI.js.map +0 -1
  131. package/dist/components-MGNL6tRn.js.map +0 -1
  132. package/dist/composables-CKxOcvgh.js.map +0 -1
  133. package/dist/experiment-utils-CLnzPPq5.js.map +0 -1
  134. package/dist/templates-BTJ9C9Kb.js.map +0 -1
  135. package/dist/useControlSchema-CXhxTul7.js.map +0 -1
  136. package/dist/useProtocolTemplates-BWhQIWV5.js.map +0 -1
@@ -1,10 +1,114 @@
1
- import { d as resolveExperimentCode } from "./experiment-utils-CLnzPPq5.js";
2
- import { r as useRequestSyncState } from "./EmptyState-p90quq8Z.js";
3
- import { A as useConcentrationUnits, H as parseExperimentId, J as isActiveJobStatus, Q as normalizeJobState, U as resolveCurrentExperimentId, V as getInjectedPlatformContext, u as useControlWorkspace } from "./useControlSchema-CXhxTul7.js";
1
+ import { d as resolveExperimentCode } from "./experiment-utils-DV3DrvE_.js";
2
+ import { r as useRequestSyncState } from "./EmptyState-NBqf2_8z.js";
3
+ import { A as useConcentrationUnits, H as parseExperimentId, J as isActiveJobStatus, Q as normalizeJobState, T as getDefaultControlView, U as resolveCurrentExperimentId, V as getInjectedPlatformContext, f as resolveControlModel, g as mergeControlWorkspaceOptions, h as controlValuesToComponentProps, m as controlValuesToComponentBindingsById, p as controlValuesToComponentBindings, r as getControlDefaults, rt as isRecord, u as useControlWorkspace, w as controlsToViewItems } from "./useControlSchema-_OVhavKq.js";
4
4
  import { o as useSettingsStore, r as useApi } from "./useApi-CDBq6z23.js";
5
5
  import { t as useExperimentStore } from "./experiment-Y9Iz7KYi.js";
6
- import { B as toBioTemplateComponentProps, F as getBioTemplateComponentProps$1, H as toBioTemplateComponentPropsById, Jt as getBioTemplatePresetInfo, L as toBioTemplateComponentBindings, P as getBioTemplateComponentBindings, Qt as getBioTemplatePackInfo, R as toBioTemplateComponentBindingsById, U as toBioTemplateComponentSnippets, V as toBioTemplateComponentPropsByComponent$1, W as toBioTemplateComponentUsage, d as createBioTemplatePresetCollectionFromControls, dn as extractTemplateCollection, i as createBioTemplateControlToolkit, p as createBioTemplatePackCollection, sn as createTemplateCollection, u as createBioTemplatePresetCollection, un as ensureTemplateFromCollection, z as toBioTemplateComponentImports } from "./templates-BTJ9C9Kb.js";
7
- import { computed, effectScope, getCurrentScope, inject, onMounted, onScopeDispose, onUnmounted, provide, reactive, readonly, ref, shallowRef, toRaw, toValue, unref, watch } from "vue";
6
+ import { B as toBioTemplateComponentProps, F as getBioTemplateComponentProps$1, H as toBioTemplateComponentPropsById, Jt as getBioTemplatePresetInfo, L as toBioTemplateComponentBindings, P as getBioTemplateComponentBindings, Qt as getBioTemplatePackInfo, R as toBioTemplateComponentBindingsById, U as toBioTemplateComponentSnippets, V as toBioTemplateComponentPropsByComponent$1, W as toBioTemplateComponentUsage, d as createBioTemplatePresetCollectionFromControls, dn as extractTemplateCollection, i as createBioTemplateControlToolkit, p as createBioTemplatePackCollection, sn as createTemplateCollection, u as createBioTemplatePresetCollection, un as ensureTemplateFromCollection, z as toBioTemplateComponentImports } from "./templates-CK72aKO1.js";
7
+ import { computed, effectScope, getCurrentScope, inject, nextTick, onMounted, onScopeDispose, onUnmounted, provide, reactive, readonly, ref, shallowRef, toRaw, toValue, unref, watch } from "vue";
8
+ //#region src/composables/useRovingFocus.ts
9
+ /**
10
+ * WAI-ARIA roving-tabindex navigation with automatic activation (the tabs pattern):
11
+ * Left/Right (or Up/Down when `orientation` is `'vertical'`) move to the adjacent item
12
+ * and activate it immediately, wrapping past the ends; Home/End jump to the first/last
13
+ * item. Only the active item should carry `tabindex="0"`; give every other item
14
+ * `tabindex="-1"`. Used by BaseTabs.
15
+ */
16
+ function useRovingFocus(options) {
17
+ const orientation = options.orientation ?? "horizontal";
18
+ const prevKey = orientation === "vertical" ? "ArrowUp" : "ArrowLeft";
19
+ const nextKey = orientation === "vertical" ? "ArrowDown" : "ArrowRight";
20
+ function moveTo(id) {
21
+ if (id === void 0) return;
22
+ options.activate(id);
23
+ nextTick(() => options.getElement(id)?.focus());
24
+ }
25
+ function handleKeydown(event) {
26
+ const ids = options.ids();
27
+ if (ids.length === 0) return;
28
+ const currentIndex = ids.indexOf(options.activeId() ?? "");
29
+ if (event.key === "Home") {
30
+ event.preventDefault();
31
+ moveTo(ids[0]);
32
+ return;
33
+ }
34
+ if (event.key === "End") {
35
+ event.preventDefault();
36
+ moveTo(ids[ids.length - 1]);
37
+ return;
38
+ }
39
+ if (event.key === prevKey) {
40
+ event.preventDefault();
41
+ moveTo(ids[currentIndex <= 0 ? ids.length - 1 : currentIndex - 1]);
42
+ return;
43
+ }
44
+ if (event.key === nextKey) {
45
+ event.preventDefault();
46
+ moveTo(ids[currentIndex === -1 || currentIndex >= ids.length - 1 ? 0 : currentIndex + 1]);
47
+ }
48
+ }
49
+ return { handleKeydown };
50
+ }
51
+ //#endregion
52
+ //#region src/composables/useMenuKeyboard.ts
53
+ /**
54
+ * Shared keyboard contract for menu- and listbox-style disclosure panels (the
55
+ * WAI-ARIA menu button pattern). Used by AppAvatarMenu and DropdownButton; ActionMenu
56
+ * implements the same contract inline.
57
+ */
58
+ function useMenuKeyboard(options) {
59
+ function focusItem(position) {
60
+ nextTick(() => {
61
+ const items = options.items();
62
+ (position === "first" ? items[0] : items[items.length - 1])?.focus();
63
+ });
64
+ }
65
+ function handleTriggerKeydown(event) {
66
+ if (options.isOpen()) return;
67
+ if (event.key === "ArrowDown" || event.key === "Enter" || event.key === " ") {
68
+ event.preventDefault();
69
+ options.open();
70
+ focusItem("first");
71
+ return;
72
+ }
73
+ if (event.key === "ArrowUp") {
74
+ event.preventDefault();
75
+ options.open();
76
+ focusItem("last");
77
+ }
78
+ }
79
+ function handlePanelKeydown(event) {
80
+ const items = options.items();
81
+ const currentIndex = items.indexOf(document.activeElement);
82
+ if (event.key === "Escape") {
83
+ event.preventDefault();
84
+ options.close(true);
85
+ return;
86
+ }
87
+ if (event.key === "Tab") {
88
+ options.close();
89
+ return;
90
+ }
91
+ if (event.key === "Home") {
92
+ event.preventDefault();
93
+ items[0]?.focus();
94
+ return;
95
+ }
96
+ if (event.key === "End") {
97
+ event.preventDefault();
98
+ items[items.length - 1]?.focus();
99
+ return;
100
+ }
101
+ if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
102
+ event.preventDefault();
103
+ const direction = event.key === "ArrowDown" ? 1 : -1;
104
+ items[(Math.max(currentIndex, 0) + direction + items.length) % items.length]?.focus();
105
+ }
106
+ return {
107
+ handleTriggerKeydown,
108
+ handlePanelKeydown
109
+ };
110
+ }
111
+ //#endregion
8
112
  //#region src/composables/useSortedItems.ts
9
113
  /** Shared sorting for SDK tables and lists with stable empty-value handling. */
10
114
  function useSortedItems(options) {
@@ -528,6 +632,310 @@ function useMobileSupportGate(options = {}) {
528
632
  };
529
633
  }
530
634
  //#endregion
635
+ //#region src/components/PluginWorkspaceView.controls.ts
636
+ function usePluginWorkspaceControls(options) {
637
+ const resolvedModel = computed(() => resolveControlModel(options.model()));
638
+ const resolvedControls = computed(() => options.controls() ?? resolvedModel.value?.controls);
639
+ const resolvedControlOptions = computed(() => mergeControlWorkspaceOptions(resolvedModel.value?.controlOptions ?? {}, options.controlOptions()));
640
+ const externalControlValues = computed(() => options.modelValue() ?? options.values());
641
+ const internalControlValues = reactive({});
642
+ let syncingControlValues = false;
643
+ const generatedControlDefaults = computed(() => {
644
+ if (!resolvedControls.value) return {};
645
+ return {
646
+ ...getControlDefaults(resolvedControls.value),
647
+ ...resolvedControlOptions.value.initialValues ?? {}
648
+ };
649
+ });
650
+ function replaceControlValues(values) {
651
+ for (const key of Object.keys(internalControlValues)) if (!(key in values)) delete internalControlValues[key];
652
+ Object.assign(internalControlValues, values);
653
+ }
654
+ function emitControlValues() {
655
+ options.emitValues({ ...internalControlValues });
656
+ }
657
+ function syncResolvedControlValues() {
658
+ syncingControlValues = true;
659
+ replaceControlValues({
660
+ ...generatedControlDefaults.value,
661
+ ...externalControlValues.value === void 0 ? internalControlValues : externalControlValues.value
662
+ });
663
+ syncingControlValues = false;
664
+ }
665
+ function handleControlValuesUpdate(values) {
666
+ syncingControlValues = true;
667
+ replaceControlValues({
668
+ ...generatedControlDefaults.value,
669
+ ...values
670
+ });
671
+ syncingControlValues = false;
672
+ emitControlValues();
673
+ }
674
+ const resolvedComponentBindings = computed(() => controlValuesToComponentBindings(internalControlValues, options.componentBindings() ?? resolvedModel.value?.componentBindings));
675
+ const resolvedComponentBindingsById = computed(() => controlValuesToComponentBindingsById(internalControlValues, options.componentBindings() ?? resolvedModel.value?.componentBindings));
676
+ const resolvedComponentProps = computed(() => {
677
+ const mapping = options.componentProps() ?? resolvedModel.value?.componentProps;
678
+ return mapping === void 0 ? {} : controlValuesToComponentProps(internalControlValues, mapping);
679
+ });
680
+ const resolvedComponentPropsById = computed(() => {
681
+ const mappings = options.componentPropsById() ?? resolvedModel.value?.componentPropsById;
682
+ if (mappings === void 0) return {};
683
+ return Object.fromEntries(Object.entries(mappings).map(([id, mapping]) => [id, controlValuesToComponentProps(internalControlValues, mapping)]));
684
+ });
685
+ const generatedPillNav = computed(() => {
686
+ if (!resolvedControls.value) return [];
687
+ return controlsToViewItems(resolvedControls.value, resolvedControlOptions.value);
688
+ });
689
+ const generatedDefaultView = computed(() => {
690
+ if (!resolvedControls.value) return "";
691
+ return getDefaultControlView(resolvedControls.value, resolvedControlOptions.value);
692
+ });
693
+ syncResolvedControlValues();
694
+ watch([generatedControlDefaults, externalControlValues], syncResolvedControlValues, { deep: true });
695
+ watch(internalControlValues, () => {
696
+ if (syncingControlValues) return;
697
+ emitControlValues();
698
+ }, {
699
+ deep: true,
700
+ flush: "sync"
701
+ });
702
+ return {
703
+ resolvedModel,
704
+ resolvedControls,
705
+ resolvedControlOptions,
706
+ generatedPillNav,
707
+ generatedDefaultView,
708
+ internalControlValues,
709
+ resolvedComponentBindings,
710
+ resolvedComponentBindingsById,
711
+ resolvedComponentProps,
712
+ resolvedComponentPropsById,
713
+ handleControlValuesUpdate
714
+ };
715
+ }
716
+ //#endregion
717
+ //#region src/components/PluginWorkspaceView.navigation.ts
718
+ function usePluginWorkspaceNavigation(options) {
719
+ const internalActiveView = ref(options.defaultActiveView());
720
+ const resolvedPillNav = computed(() => {
721
+ if (options.pillNav() !== void 0) return options.pillNav();
722
+ return options.generatedPillNav.value.length > 0 ? [...options.generatedPillNav.value] : void 0;
723
+ });
724
+ const firstConfiguredViewId = computed(() => options.defaultActiveView() ?? firstItemId(options.pillNav()) ?? options.generatedDefaultView.value ?? firstItemId(options.pageSelector()) ?? options.panelIds()[0] ?? "");
725
+ const resolvedActiveView = computed({
726
+ get: () => options.activeView() ?? options.currentPillId() ?? options.currentPageSelectorId() ?? internalActiveView.value ?? firstConfiguredViewId.value,
727
+ set: (value) => {
728
+ internalActiveView.value = value;
729
+ options.emitActiveView(value);
730
+ }
731
+ });
732
+ const resolvedCurrentPillId = computed(() => options.currentPillId() ?? resolvedActiveView.value);
733
+ const resolvedCurrentPageSelectorId = computed(() => options.currentPageSelectorId() ?? resolvedActiveView.value);
734
+ function setActiveView(viewId) {
735
+ resolvedActiveView.value = viewId;
736
+ }
737
+ function handlePageSelectorSelect(page) {
738
+ setActiveView(page.id);
739
+ options.emitPageSelectorSelect(page);
740
+ }
741
+ function handlePillSelect(item) {
742
+ setActiveView(item.id);
743
+ options.emitPillSelect(item);
744
+ }
745
+ return {
746
+ resolvedPillNav,
747
+ firstConfiguredViewId,
748
+ resolvedActiveView,
749
+ resolvedCurrentPillId,
750
+ resolvedCurrentPageSelectorId,
751
+ setActiveView,
752
+ handlePageSelectorSelect,
753
+ handlePillSelect
754
+ };
755
+ }
756
+ function firstItemId(items) {
757
+ const item = items?.[0];
758
+ if (item === void 0) return void 0;
759
+ return typeof item === "string" ? item : item.id;
760
+ }
761
+ //#endregion
762
+ //#region src/components/PluginWorkspaceView.shell.ts
763
+ var topBarForwardedSlots = new Set([
764
+ "icon",
765
+ "logo",
766
+ "page-selector-icon",
767
+ "page-selector-item-icon",
768
+ "nav",
769
+ "center",
770
+ "actions",
771
+ "account-menu-items",
772
+ "account-menu-item-icon",
773
+ "settings-appearance"
774
+ ]);
775
+ var sidebarForwardedSlots = new Set([
776
+ "header",
777
+ "collapsed",
778
+ "collapsed-footer",
779
+ "footer"
780
+ ]);
781
+ function getPluginWorkspaceTopBarSlotNames(slots) {
782
+ return Object.keys(slots).filter((name) => topBarForwardedSlots.has(name) || name.startsWith("settings-tab-"));
783
+ }
784
+ function getPluginWorkspaceSidebarSlotNames(slots) {
785
+ return Object.keys(slots).filter((name) => sidebarForwardedSlots.has(name) || name.startsWith("section-"));
786
+ }
787
+ function hasPluginWorkspaceSidebarSurface(options) {
788
+ if (!options.showSidebar) return false;
789
+ return Object.keys(options.panels).length > 0 || Object.keys(options.forms).length > 0 || options.model !== void 0 || options.controls !== void 0 || options.showSidebarWhenEmpty || options.sidebarContentId !== void 0 || options.sidebarTitle !== void 0 || options.sidebarSubtitle !== void 0 || options.sidebarBadge !== void 0 || options.hasSidebarSlot || options.sidebarSlotCount > 0;
790
+ }
791
+ //#endregion
792
+ //#region src/composables/usePluginWorkspace.ts
793
+ /**
794
+ * Maps PluginWorkspaceView's flat 72-prop surface into the three typed model objects
795
+ * (`shell`, `sidebar`, `controls`) rendered by its internal pieces
796
+ * (WorkspaceShellInternal, WorkspaceSidebarInternal, WorkspaceControlsInternal). Reuses the
797
+ * existing `usePluginWorkspaceControls` / `usePluginWorkspaceNavigation` composables and the
798
+ * PluginWorkspaceView.shell slot-detection helpers, so it changes only how that state is
799
+ * assembled and exposed, not the underlying behavior.
800
+ *
801
+ * @example Recommended
802
+ * ```ts
803
+ * const props = withDefaults(defineProps<PluginWorkspaceViewProps>(), { ... })
804
+ * const emit = defineEmits<PluginWorkspaceViewEmits>()
805
+ * const slots = useSlots()
806
+ * const { shell, sidebar, controls } = usePluginWorkspace({ props, slots, emit })
807
+ * ```
808
+ */
809
+ function usePluginWorkspace(options) {
810
+ const { props, slots, emit } = options;
811
+ const controlsState = usePluginWorkspaceControls({
812
+ model: () => props.model,
813
+ controls: () => props.controls,
814
+ controlOptions: () => props.controlOptions ?? {},
815
+ modelValue: () => props.modelValue,
816
+ values: () => props.values,
817
+ componentBindings: () => props.componentBindings,
818
+ componentProps: () => props.componentProps,
819
+ componentPropsById: () => props.componentPropsById,
820
+ emitValues: (values) => {
821
+ emit("update:modelValue", values);
822
+ emit("update:values", values);
823
+ }
824
+ });
825
+ const navigationState = usePluginWorkspaceNavigation({
826
+ activeView: () => props.activeView,
827
+ defaultActiveView: () => props.defaultActiveView,
828
+ currentPillId: () => props.currentPillId,
829
+ currentPageSelectorId: () => props.currentPageSelectorId,
830
+ pillNav: () => props.pillNav,
831
+ pageSelector: () => props.pageSelector,
832
+ panelIds: () => Object.keys(props.panels ?? {}),
833
+ generatedPillNav: controlsState.generatedPillNav,
834
+ generatedDefaultView: controlsState.generatedDefaultView,
835
+ emitActiveView: (viewId) => emit("update:activeView", viewId),
836
+ emitPageSelectorSelect: (page) => emit("page-selector-select", page),
837
+ emitPillSelect: (item) => emit("pill-select", item)
838
+ });
839
+ const topBarSlotNames = computed(() => getPluginWorkspaceTopBarSlotNames(slots));
840
+ const sidebarSlotNames = computed(() => getPluginWorkspaceSidebarSlotNames(slots));
841
+ const hasSidebarSurface = computed(() => hasPluginWorkspaceSidebarSurface({
842
+ showSidebar: props.showSidebar ?? true,
843
+ panels: props.panels ?? {},
844
+ forms: props.forms ?? {},
845
+ model: props.model,
846
+ controls: props.controls,
847
+ showSidebarWhenEmpty: props.showSidebarWhenEmpty ?? false,
848
+ sidebarContentId: props.sidebarContentId,
849
+ sidebarTitle: props.sidebarTitle,
850
+ sidebarSubtitle: props.sidebarSubtitle,
851
+ sidebarBadge: props.sidebarBadge,
852
+ hasSidebarSlot: Boolean(slots.sidebar),
853
+ sidebarSlotCount: sidebarSlotNames.value.length
854
+ }));
855
+ const controls = computed(() => ({
856
+ activeView: navigationState.resolvedActiveView.value,
857
+ setActiveView: navigationState.setActiveView,
858
+ values: controlsState.internalControlValues,
859
+ componentBindings: controlsState.resolvedComponentBindings.value,
860
+ componentBindingsById: controlsState.resolvedComponentBindingsById.value,
861
+ componentProps: controlsState.resolvedComponentProps.value,
862
+ componentPropsById: controlsState.resolvedComponentPropsById.value
863
+ }));
864
+ return {
865
+ shell: computed(() => ({
866
+ sidebarPosition: props.sidebarPosition ?? "left",
867
+ sidebarWidth: props.sidebarWidth,
868
+ floating: props.floating ?? true,
869
+ responsiveSidebar: props.responsiveSidebar ?? true,
870
+ sidebarToggleLabel: props.sidebarToggleLabel ?? "Open workspace sidebar",
871
+ sidebarCloseLabel: props.sidebarCloseLabel ?? "Close workspace sidebar",
872
+ hasSidebarSurface: hasSidebarSurface.value,
873
+ title: props.title ?? "Workspace",
874
+ subtitle: props.subtitle,
875
+ topBarVariant: props.topBarVariant ?? "card",
876
+ homePath: props.homePath ?? "/",
877
+ showLogo: props.showLogo ?? true,
878
+ pageSelector: props.pageSelector,
879
+ currentPageSelectorId: navigationState.resolvedCurrentPageSelectorId.value,
880
+ pluginSwitcher: props.pluginSwitcher,
881
+ pillNav: navigationState.resolvedPillNav.value,
882
+ currentPillId: navigationState.resolvedCurrentPillId.value,
883
+ showThemeToggle: props.showThemeToggle ?? true,
884
+ showSettings: props.showSettings ?? true,
885
+ settingsConfig: props.settingsConfig,
886
+ showStandaloneLabel: props.showStandaloneLabel ?? false,
887
+ standaloneLabel: props.standaloneLabel ?? "Standalone",
888
+ accountMenu: props.accountMenu,
889
+ showNotifications: props.showNotifications ?? false,
890
+ hasNotificationDot: props.hasNotificationDot ?? false,
891
+ showAdmin: props.showAdmin ?? false,
892
+ adminPath: props.adminPath ?? "/admin",
893
+ userName: props.userName,
894
+ userInitial: props.userInitial,
895
+ userEmail: props.userEmail,
896
+ topBarSlotNames: topBarSlotNames.value,
897
+ controls: controls.value
898
+ })),
899
+ sidebar: computed(() => ({
900
+ title: props.sidebarTitle,
901
+ subtitle: props.sidebarSubtitle,
902
+ badge: props.sidebarBadge,
903
+ variant: props.sidebarVariant ?? "analysis",
904
+ panels: props.panels ?? {},
905
+ activeView: navigationState.resolvedActiveView.value,
906
+ floating: false,
907
+ dense: props.dense ?? true,
908
+ width: props.sidebarWidth,
909
+ side: props.sidebarPosition ?? "left",
910
+ forms: props.forms ?? {},
911
+ contentId: props.sidebarContentId,
912
+ showWhenEmpty: (props.showSidebarWhenEmpty ?? false) || props.sidebarContentId !== void 0,
913
+ model: props.model,
914
+ controlSchema: props.controls,
915
+ controlOptions: props.controlOptions ?? {},
916
+ values: controlsState.internalControlValues,
917
+ formEnhancements: props.formEnhancements,
918
+ showFormActions: props.showFormActions ?? false,
919
+ formLoading: props.formLoading ?? false,
920
+ formDisabled: props.formDisabled ?? false,
921
+ formReadonly: props.formReadonly ?? false,
922
+ formSize: props.formSize ?? "sm",
923
+ collapsible: props.sidebarCollapsible,
924
+ collapsed: props.sidebarCollapsed,
925
+ defaultCollapsed: props.defaultSidebarCollapsed ?? false,
926
+ collapsedWidth: props.sidebarCollapsedWidth ?? "3rem",
927
+ collapseButtonLabel: props.sidebarCollapseButtonLabel ?? "Collapse sidebar",
928
+ expandButtonLabel: props.sidebarExpandButtonLabel ?? "Expand sidebar",
929
+ slotNames: sidebarSlotNames.value
930
+ })),
931
+ controls,
932
+ hasSidebarSurface,
933
+ handleControlValuesUpdate: controlsState.handleControlValuesUpdate,
934
+ handlePageSelectorSelect: navigationState.handlePageSelectorSelect,
935
+ handlePillSelect: navigationState.handlePillSelect
936
+ };
937
+ }
938
+ //#endregion
531
939
  //#region src/composables/useJobsStatusTray.ts
532
940
  /**
533
941
  * Combines independent job runtimes without copying their reactive state.
@@ -2270,9 +2678,6 @@ function splitByTokenLength(classes, tokenized) {
2270
2678
  }
2271
2679
  //#endregion
2272
2680
  //#region src/composables/experimentDesignData.ts
2273
- function isRecord(value) {
2274
- return value !== null && typeof value === "object" && !Array.isArray(value);
2275
- }
2276
2681
  function isPlatformDesignDataResponse(value) {
2277
2682
  return "data" in value && ("experiment_id" in value || "plugin_id" in value || "schema_version" in value || "updated_at" in value);
2278
2683
  }
@@ -4031,6 +4436,57 @@ function useBioTemplateComponents(target) {
4031
4436
  };
4032
4437
  }
4033
4438
  //#endregion
4439
+ //#region src/composables/flattenWorkspaceBindings.ts
4440
+ /**
4441
+ * Pick the shared template-component binding keys off a resolved bio-template workspace
4442
+ * (or its `components` source directly), so composables that build on `useBioTemplateWorkspace`
4443
+ * spread one object instead of re-listing all 9 keys by hand.
4444
+ *
4445
+ * @example Recommended
4446
+ * ```ts
4447
+ * const bindings = { renderer, ...flattenWorkspaceComponentBindings(components) }
4448
+ * ```
4449
+ */
4450
+ function flattenWorkspaceComponentBindings(source) {
4451
+ return {
4452
+ componentBindings: source.componentBindings,
4453
+ componentBindingsById: source.componentBindingsById,
4454
+ componentImports: source.componentImports,
4455
+ componentProps: source.componentProps,
4456
+ componentPropsById: source.componentPropsById,
4457
+ componentPropsByComponent: source.componentPropsByComponent,
4458
+ componentSnippets: source.componentSnippets,
4459
+ componentUsage: source.componentUsage,
4460
+ getComponentProps: source.getComponentProps
4461
+ };
4462
+ }
4463
+ /**
4464
+ * `computed()`-lifted version of {@link flattenWorkspaceComponentBindings} for composables
4465
+ * that hold their resolved workspace behind a `ComputedRef` (e.g. a regenerated workspace
4466
+ * swapped in via `shallowRef`/`effectScope`), so each key stays independently reactive
4467
+ * instead of forcing consumers to unwrap the whole workspace to read one field.
4468
+ * `getComponentProps` stays a plain callable function, matching the un-lifted composables.
4469
+ *
4470
+ * @example Recommended
4471
+ * ```ts
4472
+ * const workspace = computed(() => workspaceRef.value)
4473
+ * const { componentBindings, componentProps, getComponentProps } = liftWorkspaceComponentBindings(workspace)
4474
+ * ```
4475
+ */
4476
+ function liftWorkspaceComponentBindings(workspace) {
4477
+ return {
4478
+ componentBindings: computed(() => workspace.value.componentBindings),
4479
+ componentBindingsById: computed(() => workspace.value.componentBindingsById),
4480
+ componentImports: computed(() => workspace.value.componentImports),
4481
+ componentProps: computed(() => workspace.value.componentProps),
4482
+ componentPropsById: computed(() => workspace.value.componentPropsById),
4483
+ componentPropsByComponent: computed(() => workspace.value.componentPropsByComponent),
4484
+ componentSnippets: computed(() => workspace.value.componentSnippets),
4485
+ componentUsage: computed(() => workspace.value.componentUsage),
4486
+ getComponentProps: (component, options) => workspace.value.getComponentProps(component, options)
4487
+ };
4488
+ }
4489
+ //#endregion
4034
4490
  //#region src/composables/useBioTemplateWorkspace.ts
4035
4491
  /** One-stop wiring for BioTemplateRenderer, FormBuilder, AppSidebar, and template-aware SDK components. */
4036
4492
  function useBioTemplateWorkspace(target, options = {}) {
@@ -4038,21 +4494,25 @@ function useBioTemplateWorkspace(target, options = {}) {
4038
4494
  const workspace = useControlWorkspace(controls.controls, options);
4039
4495
  const components = useBioTemplateComponents(target);
4040
4496
  const renderer = isTemplateObjectTarget(target) ? { target } : null;
4041
- const bindings = {
4042
- renderer,
4043
- form: workspace.bindings.form,
4044
- sidebar: workspace.bindings.sidebar,
4045
- topBar: workspace.bindings.topBar,
4046
- topBarSettings: workspace.bindings.topBarSettings,
4047
- pillNav: workspace.bindings.pillNav,
4497
+ const componentBindings = flattenWorkspaceComponentBindings({
4048
4498
  componentBindings: components.bindings,
4049
4499
  componentBindingsById: components.componentBindingsById,
4500
+ componentImports: components.imports,
4050
4501
  componentProps: components.componentProps,
4051
4502
  componentPropsById: components.componentPropsById,
4052
4503
  componentPropsByComponent: components.componentPropsByComponent,
4053
4504
  componentSnippets: components.snippets,
4054
4505
  componentUsage: components.usage,
4055
4506
  getComponentProps: components.getComponentProps
4507
+ });
4508
+ const bindings = {
4509
+ renderer,
4510
+ form: workspace.bindings.form,
4511
+ sidebar: workspace.bindings.sidebar,
4512
+ topBar: workspace.bindings.topBar,
4513
+ topBarSettings: workspace.bindings.topBarSettings,
4514
+ pillNav: workspace.bindings.pillNav,
4515
+ ...componentBindings
4056
4516
  };
4057
4517
  return {
4058
4518
  target,
@@ -4069,15 +4529,7 @@ function useBioTemplateWorkspace(target, options = {}) {
4069
4529
  topBarSettings: workspace.topBarSettings,
4070
4530
  bindings,
4071
4531
  renderer,
4072
- componentBindings: components.bindings,
4073
- componentBindingsById: components.componentBindingsById,
4074
- componentImports: components.imports,
4075
- componentProps: components.componentProps,
4076
- componentPropsById: components.componentPropsById,
4077
- componentPropsByComponent: components.componentPropsByComponent,
4078
- componentSnippets: components.snippets,
4079
- componentUsage: components.usage,
4080
- getComponentProps: components.getComponentProps
4532
+ ...componentBindings
4081
4533
  };
4082
4534
  }
4083
4535
  function isTemplateObjectTarget(target) {
@@ -4118,14 +4570,7 @@ function useBioTemplatePackWorkspace(packId, options = {}) {
4118
4570
  const pillNav = computed(() => workspace.value.pillNav);
4119
4571
  const topBarSettings = computed(() => workspace.value.topBarSettings);
4120
4572
  const bindings = computed(() => workspace.value.bindings);
4121
- const componentBindings = computed(() => workspace.value.componentBindings);
4122
- const componentBindingsById = computed(() => workspace.value.componentBindingsById);
4123
- const componentImports = computed(() => workspace.value.componentImports);
4124
- const componentProps = computed(() => workspace.value.componentProps);
4125
- const componentPropsById = computed(() => workspace.value.componentPropsById);
4126
- const componentPropsByComponent = computed(() => workspace.value.componentPropsByComponent);
4127
- const componentSnippets = computed(() => workspace.value.componentSnippets);
4128
- const componentUsage = computed(() => workspace.value.componentUsage);
4573
+ const { componentBindings, componentBindingsById, componentImports, componentProps, componentPropsById, componentPropsByComponent, componentSnippets, componentUsage, getComponentProps: getWorkspaceComponentProps } = liftWorkspaceComponentBindings(workspace);
4129
4574
  watch(collection, (value) => {
4130
4575
  workspaceRef.value = createWorkspace(value);
4131
4576
  });
@@ -4168,7 +4613,7 @@ function useBioTemplatePackWorkspace(packId, options = {}) {
4168
4613
  componentPropsByComponent,
4169
4614
  componentSnippets,
4170
4615
  componentUsage,
4171
- getComponentProps: (component, options) => workspace.value.getComponentProps(component, options),
4616
+ getComponentProps: getWorkspaceComponentProps,
4172
4617
  applyCollection,
4173
4618
  resetToDefaultCollection,
4174
4619
  save: templateCollection.save,
@@ -4199,14 +4644,7 @@ function useBioTemplatePresetWorkspace(presetId, options = {}) {
4199
4644
  const collection = templateCollection.templateCollection;
4200
4645
  const workspace = computed(() => useBioTemplateWorkspace(collection.value, resolvedControlOptions));
4201
4646
  const renderer = computed(() => ({ target: collection.value }));
4202
- const componentBindings = computed(() => workspace.value.componentBindings);
4203
- const componentBindingsById = computed(() => workspace.value.componentBindingsById);
4204
- const componentImports = computed(() => workspace.value.componentImports);
4205
- const componentProps = computed(() => workspace.value.componentProps);
4206
- const componentPropsById = computed(() => workspace.value.componentPropsById);
4207
- const componentPropsByComponent = computed(() => workspace.value.componentPropsByComponent);
4208
- const componentSnippets = computed(() => workspace.value.componentSnippets);
4209
- const componentUsage = computed(() => workspace.value.componentUsage);
4647
+ const { componentBindings, componentBindingsById, componentImports, componentProps, componentPropsById, componentPropsByComponent, componentSnippets, componentUsage, getComponentProps } = liftWorkspaceComponentBindings(workspace);
4210
4648
  const form = reactive({
4211
4649
  ...controls.form,
4212
4650
  modelValue: controlValues.value,
@@ -4239,9 +4677,6 @@ function useBioTemplatePresetWorkspace(presetId, options = {}) {
4239
4677
  onPillSelect: pillNav.onSelect,
4240
4678
  ...topBarSettings
4241
4679
  }));
4242
- function getComponentProps(component, options) {
4243
- return workspace.value.getComponentProps(component, options);
4244
- }
4245
4680
  const bindings = {
4246
4681
  renderer,
4247
4682
  form,
@@ -5144,6 +5579,6 @@ function useProtocolTemplates() {
5144
5579
  };
5145
5580
  }
5146
5581
  //#endregion
5147
- export { useToast as $, useFileImport as A, useDoseCalculator as B, getSampleGroupParentPath as C, detectDelimiter as D, hslToHex as E, extractSampleNamesFromDesignData as F, useMobileSupportGate as G, useJobsStatusTray as H, extractSampleOptionsFromDesignData as I, getInjectedFrontendRevision as J, APP_EXPERIMENT_KEY as K, unwrapExperimentDesignData as L, extractSamplesFromDesignData as M, useAutoGroup as N, parseDelimitedText as O, parseCSV as P, useTheme as Q, DEFAULT_COLORS as R, getSampleGroupMajorPrefix as S, hexToHsl as T, usePluginJobCenter as U, combinePluginJobSources as V, DEFAULT_MOBILE_VIEWPORT_QUERY as W, getPresenceClientId as X, useRuntimeAlignment as Y, usePresenceHeartbeat as Z, useScheduleDrag as _, useReagentSeries as a, useExpansionSet as b, useBioTemplatePresetWorkspace as c, getBioTemplateComponentProps as d, candidateMatchesSearch as et, toBioTemplateComponentPropsByComponent as f, useExperimentSave as g, useTemplateCollection as h, generateDilutionSeries as i, useSortedItems as it, validateImportFile as j, readFileAsText as k, useBioTemplatePackWorkspace as l, useBioTemplateControls as m, DEFAULT_PRESETS as n, useTextSearch as nt, useGroupAssignment as o, useBioTemplateComponents as p, useAppExperiment as q, DEFAULT_UNITS as r, compareSortValues as rt, useRackEditor as s, useProtocolTemplates as t, normalizeSearchQuery as tt, useBioTemplateWorkspace as u, useExperimentSamples as v, deriveShade as w, useSampleGroups as x, useExperimentData as y, useWellPlateEditor as z };
5582
+ export { useTheme as $, useFileImport as A, useDoseCalculator as B, getSampleGroupParentPath as C, detectDelimiter as D, hslToHex as E, extractSampleNamesFromDesignData as F, DEFAULT_MOBILE_VIEWPORT_QUERY as G, useJobsStatusTray as H, extractSampleOptionsFromDesignData as I, useAppExperiment as J, useMobileSupportGate as K, unwrapExperimentDesignData as L, extractSamplesFromDesignData as M, useAutoGroup as N, parseDelimitedText as O, parseCSV as P, usePresenceHeartbeat as Q, DEFAULT_COLORS as R, getSampleGroupMajorPrefix as S, hexToHsl as T, usePluginJobCenter as U, combinePluginJobSources as V, usePluginWorkspace as W, useRuntimeAlignment as X, getInjectedFrontendRevision as Y, getPresenceClientId as Z, useScheduleDrag as _, useReagentSeries as a, useSortedItems as at, useExpansionSet as b, useBioTemplatePresetWorkspace as c, getBioTemplateComponentProps as d, useToast as et, toBioTemplateComponentPropsByComponent as f, useExperimentSave as g, useTemplateCollection as h, generateDilutionSeries as i, compareSortValues as it, validateImportFile as j, readFileAsText as k, useBioTemplatePackWorkspace as l, useBioTemplateControls as m, DEFAULT_PRESETS as n, normalizeSearchQuery as nt, useGroupAssignment as o, useMenuKeyboard as ot, useBioTemplateComponents as p, APP_EXPERIMENT_KEY as q, DEFAULT_UNITS as r, useTextSearch as rt, useRackEditor as s, useRovingFocus as st, useProtocolTemplates as t, candidateMatchesSearch as tt, useBioTemplateWorkspace as u, useExperimentSamples as v, deriveShade as w, useSampleGroups as x, useExperimentData as y, useWellPlateEditor as z };
5148
5583
 
5149
- //# sourceMappingURL=useProtocolTemplates-BWhQIWV5.js.map
5584
+ //# sourceMappingURL=useProtocolTemplates-Dq5Pzwyd.js.map