@morscherlab/mint-sdk 1.1.8 → 1.1.10

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 (91) hide show
  1. package/dist/__tests__/components/RetiredStyles.test.d.ts +1 -0
  2. package/dist/__tests__/composables/autoGroup/groupByOrder.test.d.ts +1 -0
  3. package/dist/__tests__/composables/useSampleGroups.tree.test.d.ts +1 -0
  4. package/dist/components/AppTopBar.vue.d.ts +6 -11
  5. package/dist/components/AutoGroupModal.adapter.d.ts +22 -7
  6. package/dist/components/PluginIcon.vue.d.ts +2 -2
  7. package/dist/components/PluginWorkspaceView.props.d.ts +5 -7
  8. package/dist/components/PluginWorkspaceView.vue.d.ts +0 -3
  9. package/dist/components/SampleSelector.groups.d.ts +7 -7
  10. package/dist/components/SampleSelector.selection.d.ts +6 -12
  11. package/dist/components/SampleSelector.tree.d.ts +39 -0
  12. package/dist/components/SampleSelectorGroupNode.vue.d.ts +9 -0
  13. package/dist/components/SmartGroup.types.d.ts +5 -0
  14. package/dist/components/SmartGroupFieldRecipe.groups.d.ts +3 -2
  15. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +2 -0
  16. package/dist/components/index.js +1 -1
  17. package/dist/{components-CDomhGSj.js → components-HXV-ZkBg.js} +939 -948
  18. package/dist/components-HXV-ZkBg.js.map +1 -0
  19. package/dist/composables/index.d.ts +1 -1
  20. package/dist/composables/index.js +1 -1
  21. package/dist/composables/useAutoGroup.d.ts +3 -0
  22. package/dist/composables/usePlatformContext.d.ts +3 -0
  23. package/dist/composables/useSampleGroups.d.ts +25 -0
  24. package/dist/index.js +2 -2
  25. package/dist/install.js +1 -1
  26. package/dist/styles.css +130 -557
  27. package/dist/types/platform.d.ts +1 -0
  28. package/dist/{useProtocolTemplates-BMt0LlXf.js → useProtocolTemplates-D3IZKLqr.js} +198 -24
  29. package/dist/useProtocolTemplates-D3IZKLqr.js.map +1 -0
  30. package/dist/utils/pluginIcon.d.ts +1 -1
  31. package/dist/utils/sampleGroupPath.d.ts +6 -0
  32. package/package.json +1 -1
  33. package/src/__tests__/components/AppAvatarMenu.test.ts +11 -0
  34. package/src/__tests__/components/AppTopBar.test.ts +84 -10
  35. package/src/__tests__/components/AppTopBarPageSelector.test.ts +1 -1
  36. package/src/__tests__/components/AutoGroupModal.adapter.test.ts +47 -7
  37. package/src/__tests__/components/PluginIcon.test.ts +19 -0
  38. package/src/__tests__/components/PluginWorkspaceView.test.ts +8 -0
  39. package/src/__tests__/components/RetiredStyles.test.ts +82 -0
  40. package/src/__tests__/components/SampleSelector.drag.test.ts +26 -0
  41. package/src/__tests__/components/SampleSelector.groups.test.ts +19 -6
  42. package/src/__tests__/components/SampleSelector.selection.test.ts +9 -23
  43. package/src/__tests__/components/SampleSelector.test.ts +35 -0
  44. package/src/__tests__/components/SmartGroupFieldRecipe.groups.test.ts +5 -0
  45. package/src/__tests__/composables/autoGroup/compose.test.ts +9 -10
  46. package/src/__tests__/composables/autoGroup/groupByOrder.test.ts +278 -0
  47. package/src/__tests__/composables/autoGroup/integration.test.ts +3 -7
  48. package/src/__tests__/composables/useSampleGroups.tree.test.ts +145 -0
  49. package/src/components/AppTopBar.story.vue +9 -7
  50. package/src/components/AppTopBar.vue +79 -72
  51. package/src/components/AutoGroupModal.adapter.ts +69 -13
  52. package/src/components/AutoGroupModal.story.vue +89 -0
  53. package/src/components/AutoGroupModal.vue +18 -8
  54. package/src/components/PluginIcon.story.vue +11 -9
  55. package/src/components/PluginIcon.vue +3 -3
  56. package/src/components/PluginWorkspaceView.props.ts +5 -7
  57. package/src/components/PluginWorkspaceView.vue +0 -4
  58. package/src/components/SampleSelector.drag.ts +5 -5
  59. package/src/components/SampleSelector.groups.ts +15 -15
  60. package/src/components/SampleSelector.selection.ts +7 -46
  61. package/src/components/SampleSelector.story.vue +37 -0
  62. package/src/components/SampleSelector.tree.ts +48 -0
  63. package/src/components/SampleSelector.vue +52 -320
  64. package/src/components/SampleSelectorGroupNode.vue +197 -0
  65. package/src/components/SmartGroup.types.ts +5 -0
  66. package/src/components/SmartGroupFieldRecipe.groups.ts +6 -2
  67. package/src/components/SmartGroupFieldRecipe.vue +76 -4
  68. package/src/components/internal/AppTopBarPageSelectorInternal.vue +2 -2
  69. package/src/composables/autoGroup/compose.ts +31 -10
  70. package/src/composables/index.ts +1 -0
  71. package/src/composables/useAutoGroup.ts +112 -5
  72. package/src/composables/useSampleGroups.ts +118 -33
  73. package/src/styles/components/app-avatar-menu.css +2 -1
  74. package/src/styles/components/app-plugin-switcher.css +0 -13
  75. package/src/styles/components/app-top-bar.css +13 -54
  76. package/src/styles/components/dose-calculator.css +0 -4
  77. package/src/styles/components/experiment-popover.css +2 -104
  78. package/src/styles/components/experiment-selector-modal.css +0 -7
  79. package/src/styles/components/plugin-icon.css +3 -0
  80. package/src/styles/components/protocol-step-editor.css +0 -6
  81. package/src/styles/components/reagent-editor.css +0 -5
  82. package/src/styles/components/sample-hierarchy-tree.css +0 -4
  83. package/src/styles/components/sample-selector.css +0 -96
  84. package/src/styles/components/schedule-calendar.css +0 -6
  85. package/src/styles/components/smart-group.css +22 -0
  86. package/src/styles/components/well-plate.css +1 -27
  87. package/src/types/platform.ts +1 -0
  88. package/src/utils/pluginIcon.ts +5 -2
  89. package/src/utils/sampleGroupPath.ts +19 -0
  90. package/dist/components-CDomhGSj.js.map +0 -1
  91. package/dist/useProtocolTemplates-BMt0LlXf.js.map +0 -1
@@ -3,6 +3,7 @@ export interface PluginInfo {
3
3
  id: string;
4
4
  name: string;
5
5
  version: string;
6
+ plugin_type?: string;
6
7
  description?: string;
7
8
  icon?: string;
8
9
  color?: string;
@@ -1657,6 +1657,19 @@ function composeGroups(input) {
1657
1657
  const excludedSamples = [];
1658
1658
  const metadata = [];
1659
1659
  let colorIdx = 0;
1660
+ const experimentalClasses = input.classes.filter((c) => c.disposition === "group" && input.schemas[classKey(c)]);
1661
+ const classPrefix = /* @__PURE__ */ new Map();
1662
+ if (input.prefixClassLabels !== false && experimentalClasses.length > 1) {
1663
+ const shortCounts = /* @__PURE__ */ new Map();
1664
+ for (const c of experimentalClasses) {
1665
+ const short = c.subKind ?? c.label;
1666
+ shortCounts.set(short, (shortCounts.get(short) ?? 0) + 1);
1667
+ }
1668
+ for (const c of experimentalClasses) {
1669
+ const short = c.subKind ?? c.label;
1670
+ classPrefix.set(c, (shortCounts.get(short) ?? 0) > 1 ? c.label : short);
1671
+ }
1672
+ }
1660
1673
  const PATH_SEP = String.fromCharCode(1);
1661
1674
  const groupTree = [];
1662
1675
  const treeIndex = /* @__PURE__ */ new Map();
@@ -1696,8 +1709,8 @@ function composeGroups(input) {
1696
1709
  return fieldValues[col.displayName ?? col.name];
1697
1710
  }) : [cls.label];
1698
1711
  const hasMissingGroupPart = input.missingGroupName && cls.disposition === "group" && keyParts.some((part) => part.trim().length === 0);
1699
- const useClassPrefix = input.prefixClassLabels !== false && cls.disposition === "group" && cls.kind !== "unknown" && keyParts.length > 0;
1700
- const groupKey = hasMissingGroupPart ? input.missingGroupName : useClassPrefix ? `${cls.label} / ${keyParts.join(" / ")}` : keyParts.length > 0 ? keyParts.join(" / ") : cls.label;
1712
+ const prefix = cls.kind !== "unknown" && keyParts.length > 0 ? classPrefix.get(cls) : void 0;
1713
+ const groupKey = hasMissingGroupPart ? input.missingGroupName : prefix ? `${prefix} / ${keyParts.join(" / ")}` : keyParts.length > 0 ? keyParts.join(" / ") : cls.label;
1701
1714
  const bucket = groupMap.get(groupKey);
1702
1715
  if (bucket) bucket.samples.push(rowName);
1703
1716
  else groupMap.set(groupKey, {
@@ -2357,17 +2370,108 @@ function useAutoGroup() {
2357
2370
  function toggleGroupBy(key, idx) {
2358
2371
  const schema = schemas.value[key];
2359
2372
  if (!schema) return;
2360
- const set = new Set(schema.groupBy);
2361
- if (set.has(idx)) set.delete(idx);
2362
- else set.add(idx);
2373
+ const groupBy = schema.groupBy.includes(idx) ? schema.groupBy.filter((i) => i !== idx) : [...schema.groupBy, idx];
2363
2374
  schemas.value = {
2364
2375
  ...schemas.value,
2365
2376
  [key]: {
2366
2377
  ...schema,
2367
- groupBy: [...set].sort((a, b) => a - b)
2378
+ groupBy
2368
2379
  }
2369
2380
  };
2370
2381
  }
2382
+ /** Move an enabled column one step up (toward layer 1) or down in the layer order. */
2383
+ function moveGroupBy(key, idx, direction) {
2384
+ const schema = schemas.value[key];
2385
+ if (!schema) return;
2386
+ const from = schema.groupBy.indexOf(idx);
2387
+ if (from === -1) return;
2388
+ const to = direction === "up" ? from - 1 : from + 1;
2389
+ if (to < 0 || to >= schema.groupBy.length) return;
2390
+ const groupBy = [...schema.groupBy];
2391
+ [groupBy[from], groupBy[to]] = [groupBy[to], groupBy[from]];
2392
+ schemas.value = {
2393
+ ...schemas.value,
2394
+ [key]: {
2395
+ ...schema,
2396
+ groupBy
2397
+ }
2398
+ };
2399
+ }
2400
+ function schemasExposing(fieldKey) {
2401
+ return Object.entries(schemas.value).flatMap(([key, schema]) => {
2402
+ const col = schema.columns.find((c) => (c.displayName ?? c.name) === fieldKey && (c.role === "factor" || c.role === "replicate" || schema.groupBy.includes(c.index)));
2403
+ return col ? [{
2404
+ key,
2405
+ index: col.index
2406
+ }] : [];
2407
+ });
2408
+ }
2409
+ function fieldIdentity(schema, index) {
2410
+ const column = schema.columns.find((candidate) => candidate.index === index);
2411
+ return column ? column.displayName ?? column.name : void 0;
2412
+ }
2413
+ /**
2414
+ * Toggle a grouping field across every class exposing it. The batch card is
2415
+ * on when any schema groups by the field, so clicking it disables all such
2416
+ * schemas; only a fully disabled field is enabled across the batch.
2417
+ */
2418
+ function toggleGroupByField(fieldKey) {
2419
+ const targets = schemasExposing(fieldKey);
2420
+ if (targets.length === 0) return;
2421
+ const enable = !targets.some(({ key, index }) => schemas.value[key]?.groupBy.includes(index));
2422
+ for (const { key, index } of targets) {
2423
+ const schema = schemas.value[key];
2424
+ if (!schema || schema.groupBy.includes(index) === enable) continue;
2425
+ toggleGroupBy(key, index);
2426
+ }
2427
+ }
2428
+ /**
2429
+ * Move a grouping field in the active schema, then project that visible field
2430
+ * order across every class. Class-only fields keep their relative order after
2431
+ * the shared fields because the Field Recipe has no card with which to place
2432
+ * them explicitly.
2433
+ */
2434
+ function moveGroupByField(fieldKey, direction) {
2435
+ const targets = schemasExposing(fieldKey);
2436
+ if (targets.length === 0) return;
2437
+ const active = targets.find((target) => target.key === activeClassKey.value && schemas.value[target.key]?.groupBy.includes(target.index)) ?? targets.find((target) => schemas.value[target.key]?.groupBy.includes(target.index)) ?? targets[0];
2438
+ const activeSchema = schemas.value[active.key];
2439
+ if (!activeSchema) return;
2440
+ const from = activeSchema.groupBy.indexOf(active.index);
2441
+ if (from === -1) return;
2442
+ const to = direction === "up" ? from - 1 : from + 1;
2443
+ if (to < 0 || to >= activeSchema.groupBy.length) return;
2444
+ const activeOrder = [...activeSchema.groupBy];
2445
+ [activeOrder[from], activeOrder[to]] = [activeOrder[to], activeOrder[from]];
2446
+ const desiredRank = new Map(activeOrder.flatMap((index, rank) => {
2447
+ const identity = fieldIdentity(activeSchema, index);
2448
+ return identity === void 0 ? [] : [[identity, rank]];
2449
+ }));
2450
+ let changed = false;
2451
+ const nextSchemas = { ...schemas.value };
2452
+ for (const [key, schema] of Object.entries(schemas.value)) {
2453
+ const shared = [];
2454
+ const classOnly = [];
2455
+ for (const index of schema.groupBy) {
2456
+ const identity = fieldIdentity(schema, index);
2457
+ const rank = identity === void 0 ? void 0 : desiredRank.get(identity);
2458
+ if (rank === void 0) classOnly.push(index);
2459
+ else shared.push({
2460
+ index,
2461
+ rank
2462
+ });
2463
+ }
2464
+ shared.sort((left, right) => left.rank - right.rank);
2465
+ const groupBy = [...shared.map((entry) => entry.index), ...classOnly];
2466
+ if (groupBy.every((index, position) => index === schema.groupBy[position])) continue;
2467
+ nextSchemas[key] = {
2468
+ ...schema,
2469
+ groupBy
2470
+ };
2471
+ changed = true;
2472
+ }
2473
+ if (changed) schemas.value = nextSchemas;
2474
+ }
2371
2475
  function updateColumn(key, idx, patch) {
2372
2476
  const schema = schemas.value[key];
2373
2477
  if (!schema) return;
@@ -2552,6 +2656,9 @@ function useAutoGroup() {
2552
2656
  setClassDisposition,
2553
2657
  setClassDispositions,
2554
2658
  toggleGroupBy,
2659
+ moveGroupBy,
2660
+ toggleGroupByField,
2661
+ moveGroupByField,
2555
2662
  setRole,
2556
2663
  setColumnName,
2557
2664
  setValueOps,
@@ -2882,6 +2989,22 @@ function deriveShade(parentHex, index, total) {
2882
2989
  return hslToHex(h, clamp(s, 35, 100), childL);
2883
2990
  }
2884
2991
  //#endregion
2992
+ //#region src/utils/sampleGroupPath.ts
2993
+ /** Split a group name into normalized hierarchy segments. */
2994
+ function splitSampleGroupPath(groupName, separator) {
2995
+ const parts = groupName.split(separator).map((part) => part.trim()).filter((part) => part.length > 0);
2996
+ return parts.length > 0 ? parts : [groupName];
2997
+ }
2998
+ /** First normalized hierarchy segment, used as the reorderable family key. */
2999
+ function getSampleGroupMajorPrefix(groupName, separator) {
3000
+ return splitSampleGroupPath(groupName, separator)[0];
3001
+ }
3002
+ /** Normalized path above a concrete leaf group. */
3003
+ function getSampleGroupParentPath(groupName, separator) {
3004
+ const parts = splitSampleGroupPath(groupName, separator);
3005
+ return parts.length > 1 ? parts.slice(0, -1).join(separator) : "";
3006
+ }
3007
+ //#endregion
2885
3008
  //#region src/composables/useSampleGroups.ts
2886
3009
  /** Shared sample-group hierarchy, color, and ungrouped-sample derivation for lab selectors. */
2887
3010
  function useSampleGroups(options) {
@@ -2893,34 +3016,84 @@ function useSampleGroups(options) {
2893
3016
  if (explicit) return explicit;
2894
3017
  return groups.value.some((group) => group.name.includes("/")) ? "/" : "_";
2895
3018
  });
2896
- const hierarchicalGroups = computed(() => {
2897
- if (groups.value.length === 0) return [];
2898
- const groupedByMajor = /* @__PURE__ */ new Map();
3019
+ const families = computed(() => {
3020
+ const byMajor = /* @__PURE__ */ new Map();
2899
3021
  for (const group of groups.value) {
2900
3022
  const majorName = getMajorGroupName(group.name, separator.value);
2901
- const existing = groupedByMajor.get(majorName);
3023
+ const existing = byMajor.get(majorName);
2902
3024
  if (existing) existing.push(group);
2903
- else groupedByMajor.set(majorName, [group]);
3025
+ else byMajor.set(majorName, [group]);
2904
3026
  }
2905
- return [...groupedByMajor.entries()].map(([majorName, subGroups]) => {
2906
- const color = subGroups[0]?.color || fallbackColor.value;
3027
+ return [...byMajor.entries()].map(([name, members]) => {
3028
+ const color = members[0]?.color || fallbackColor.value;
2907
3029
  return {
2908
- name: majorName,
3030
+ name,
2909
3031
  color,
2910
- subGroups: subGroups.map((subGroup, index) => {
2911
- const displayColor = deriveShade(color, index, subGroups.length);
3032
+ subGroups: members.map((group, index) => {
3033
+ const displayColor = deriveShade(color, index, members.length);
2912
3034
  return {
2913
- ...subGroup,
3035
+ ...group,
2914
3036
  displayColor,
2915
3037
  displayBg: displayColor + "20",
2916
3038
  displayBorder: displayColor + "40"
2917
3039
  };
2918
- }),
2919
- allSamples: subGroups.flatMap((group) => group.samples)
3040
+ })
2920
3041
  };
2921
3042
  });
2922
3043
  });
2923
- const showHierarchy = computed(() => hierarchicalGroups.value.some((major) => major.subGroups.length > 1 || major.subGroups.length === 1 && major.name !== major.subGroups[0].name));
3044
+ const groupTree = computed(() => {
3045
+ const roots = [];
3046
+ for (const family of families.value) {
3047
+ const interior = /* @__PURE__ */ new Map();
3048
+ for (const subGroup of family.subGroups) {
3049
+ const segments = splitSampleGroupPath(subGroup.name, separator.value);
3050
+ const leafLabel = segments[segments.length - 1] ?? subGroup.name;
3051
+ let siblings = roots;
3052
+ let path = "";
3053
+ for (const segment of segments.slice(0, -1)) {
3054
+ path = path ? `${path}/${segment}` : segment;
3055
+ let node = interior.get(path);
3056
+ if (!node) {
3057
+ node = {
3058
+ key: `node:${family.name}/${path}`,
3059
+ label: segment,
3060
+ color: family.color,
3061
+ displayBg: family.color + "20",
3062
+ displayBorder: family.color + "40",
3063
+ allSamples: [],
3064
+ children: []
3065
+ };
3066
+ interior.set(path, node);
3067
+ siblings.push(node);
3068
+ }
3069
+ node.allSamples.push(...subGroup.samples);
3070
+ siblings = node.children;
3071
+ }
3072
+ siblings.push({
3073
+ key: `leaf:${subGroup.name}`,
3074
+ label: leafLabel,
3075
+ color: subGroup.displayColor,
3076
+ displayBg: subGroup.displayBg,
3077
+ displayBorder: subGroup.displayBorder,
3078
+ allSamples: [...subGroup.samples],
3079
+ children: [],
3080
+ group: subGroup
3081
+ });
3082
+ }
3083
+ }
3084
+ return roots;
3085
+ });
3086
+ const maxDepth = computed(() => {
3087
+ const depthOf = (nodes) => nodes.reduce((deepest, node) => Math.max(deepest, 1 + depthOf(node.children)), 0);
3088
+ return depthOf(groupTree.value);
3089
+ });
3090
+ const hierarchicalGroups = computed(() => families.value.map((family) => ({
3091
+ name: family.name,
3092
+ color: family.color,
3093
+ subGroups: family.subGroups,
3094
+ allSamples: family.subGroups.flatMap((group) => group.samples)
3095
+ })));
3096
+ const showHierarchy = computed(() => maxDepth.value > 1);
2924
3097
  const groupedSamples = computed(() => new Set(groups.value.flatMap((group) => group.samples)));
2925
3098
  const ungroupedSamples = computed(() => samples.value.filter((sample) => !groupedSamples.value.has(sample)));
2926
3099
  function findGroup(groupName) {
@@ -2932,6 +3105,8 @@ function useSampleGroups(options) {
2932
3105
  return {
2933
3106
  groups,
2934
3107
  separator,
3108
+ groupTree,
3109
+ maxDepth,
2935
3110
  hierarchicalGroups,
2936
3111
  showHierarchy,
2937
3112
  groupedSamples,
@@ -2941,8 +3116,7 @@ function useSampleGroups(options) {
2941
3116
  };
2942
3117
  }
2943
3118
  function getMajorGroupName(groupName, separator) {
2944
- const parts = groupName.split(separator);
2945
- return parts.length > 1 ? parts[0] : groupName;
3119
+ return splitSampleGroupPath(groupName, separator)[0];
2946
3120
  }
2947
3121
  //#endregion
2948
3122
  //#region src/composables/useExpansionSet.ts
@@ -4760,6 +4934,6 @@ function useProtocolTemplates() {
4760
4934
  };
4761
4935
  }
4762
4936
  //#endregion
4763
- export { extractSamplesFromDesignData as A, useJobsStatusTray as B, hexToHsl as C, readFileAsText as D, parseDelimitedText as E, unwrapExperimentDesignData as F, useAppExperiment as G, DEFAULT_MOBILE_VIEWPORT_QUERY as H, DEFAULT_COLORS as I, candidateMatchesSearch as J, useTheme as K, useWellPlateEditor as L, parseCSV as M, extractSampleNamesFromDesignData as N, useFileImport as O, extractSampleOptionsFromDesignData as P, useSortedItems as Q, useDoseCalculator as R, deriveShade as S, detectDelimiter as T, useMobileSupportGate as U, usePluginJobCenter as V, APP_EXPERIMENT_KEY as W, useTextSearch as X, normalizeSearchQuery as Y, compareSortValues as Z, useScheduleDrag as _, useReagentSeries as a, useExpansionSet as b, useBioTemplatePresetWorkspace as c, getBioTemplateComponentProps as d, toBioTemplateComponentPropsByComponent as f, useExperimentSave as g, useTemplateCollection as h, generateDilutionSeries as i, useAutoGroup as j, validateImportFile as k, useBioTemplatePackWorkspace as l, useBioTemplateControls as m, DEFAULT_PRESETS as n, useGroupAssignment as o, useBioTemplateComponents as p, useToast as q, DEFAULT_UNITS as r, useRackEditor as s, useProtocolTemplates as t, useBioTemplateWorkspace as u, useExperimentSamples as v, hslToHex as w, useSampleGroups as x, useExperimentData as y, combinePluginJobSources as z };
4937
+ export { compareSortValues 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, useTheme as J, APP_EXPERIMENT_KEY as K, unwrapExperimentDesignData as L, extractSamplesFromDesignData as M, useAutoGroup as N, parseDelimitedText as O, parseCSV as P, useTextSearch as Q, DEFAULT_COLORS as R, getSampleGroupMajorPrefix as S, hexToHsl as T, usePluginJobCenter as U, combinePluginJobSources as V, DEFAULT_MOBILE_VIEWPORT_QUERY as W, candidateMatchesSearch as X, useToast as Y, normalizeSearchQuery as Z, useScheduleDrag as _, useReagentSeries as a, useExpansionSet as b, useBioTemplatePresetWorkspace as c, getBioTemplateComponentProps as d, useSortedItems as et, toBioTemplateComponentPropsByComponent as f, useExperimentSave as g, useTemplateCollection as h, generateDilutionSeries as i, validateImportFile as j, readFileAsText as k, useBioTemplatePackWorkspace as l, useBioTemplateControls as m, DEFAULT_PRESETS as n, useGroupAssignment as o, useBioTemplateComponents as p, useAppExperiment as q, DEFAULT_UNITS as r, useRackEditor as s, useProtocolTemplates as t, useBioTemplateWorkspace as u, useExperimentSamples as v, deriveShade as w, useSampleGroups as x, useExperimentData as y, useWellPlateEditor as z };
4764
4938
 
4765
- //# sourceMappingURL=useProtocolTemplates-BMt0LlXf.js.map
4939
+ //# sourceMappingURL=useProtocolTemplates-D3IZKLqr.js.map