@ohif/app 3.13.0-beta.8 → 3.13.0-beta.81

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 (72) hide show
  1. package/dist/{1459.bundle.9fbac3c8e089c433777e.js → 1459.bundle.8dac0d666a38bfa74d75.js} +5 -6
  2. package/dist/{1608.bundle.0687c661f1c9edfb3b8a.js → 1608.bundle.26306ea38618db59eff7.js} +12 -12
  3. package/dist/{1927.bundle.3050588e95f43cf57cdd.js → 1927.bundle.be67b3aafe238ca9f191.js} +27 -38
  4. package/dist/{1933.bundle.7ae9ef1a7b5ed55d9550.js → 1933.bundle.98bb859a3903ca9f7073.js} +32 -27
  5. package/dist/{2018.bundle.b8f9fe454ee996818531.js → 2018.bundle.9a31f91584577a24862e.js} +18 -19
  6. package/dist/{6409.bundle.b36048896cb11c8571fb.js → 2075.bundle.ba9ac60662f1fa739378.js} +337 -261
  7. package/dist/{2108.bundle.e84aa8d858d8c4f2413e.js → 2108.bundle.aea8d3b39486dd5ab39e.js} +569 -558
  8. package/dist/{213.bundle.9cff7f3868971abefe05.js → 213.bundle.54412c1510ab6ac3e129.js} +3 -5
  9. package/dist/{2424.bundle.9c081095926163e359e0.js → 2424.bundle.a7c50eea1404f7706d03.js} +3 -5
  10. package/dist/{2516.bundle.1ea0988d309a757bb6da.js → 2516.bundle.f62228e9a800de8d4b31.js} +6 -6
  11. package/dist/{2701.bundle.12bd01a80a9f8ea4cd94.js → 2701.bundle.6873805ddfdccc7a8b1e.js} +10 -10
  12. package/dist/{9195.bundle.a749686750f08e4adf93.js → 2851.bundle.bbb7743b8a06e85a2d8b.js} +484 -177
  13. package/dist/{1730.bundle.4f947de94428632e2499.js → 3138.bundle.f0c27e0fa51e8e522a03.js} +16 -310
  14. package/dist/{3461.bundle.8ce95fd079101d3e01b6.js → 3461.bundle.b8e87344d06051ccab12.js} +131 -107
  15. package/dist/{147.bundle.37d627289453cb6c3937.js → 3754.bundle.d2435bb372912595708c.js} +486 -49
  16. package/dist/{4202.bundle.5a0f8e4004c5d8a68548.js → 4202.bundle.4fcd0de412907efd5b53.js} +6 -6
  17. package/dist/{4019.bundle.83a604779f7da0101ced.js → 4287.bundle.4f28e48c1e39a46dfc87.js} +352 -238
  18. package/dist/{5462.bundle.21beddaca145b7465c72.js → 4406.bundle.573d234b4641d23cf8db.js} +1083 -1308
  19. package/dist/{1403.bundle.f7392178d9262e1d99c2.js → 4507.bundle.48936299167b13472d81.js} +10 -305
  20. package/dist/{6347.bundle.784c48912700f281de1d.js → 4579.bundle.1c315389368cc476aed3.js} +1439 -1879
  21. package/dist/{4819.bundle.c0b6dd8690d5755fde43.js → 4819.bundle.1793d3fea8733ed305f7.js} +88 -53
  22. package/dist/{4775.bundle.5671e155db52c026a803.js → 5015.bundle.efefa774795b62cde37f.js} +29 -333
  23. package/dist/{5028.bundle.bc79f49125393ecc4f5f.js → 5028.bundle.41653a25d1fe816326d2.js} +11 -13
  24. package/dist/{5261.bundle.6e1a017f8f1027557f5b.js → 5261.bundle.2655560097e9250eac44.js} +412 -345
  25. package/dist/{5457.bundle.7720e5daa4e2bdff83ee.js → 5457.bundle.c34363f925f44fb40bbf.js} +16 -23
  26. package/dist/{5485.bundle.2c2a09db8c7a29258e9f.js → 5485.bundle.d829c2a13b08d0c13e94.js} +30 -36
  27. package/dist/{5491.bundle.2e01dd7ad29e4cc01bc1.js → 5491.bundle.4866d2ecb20dd089e071.js} +54 -51
  28. package/dist/{5802.bundle.3bf5e6b3ab330a594a47.js → 5802.bundle.cd36386cbe1613913ed4.js} +79 -20
  29. package/dist/5802.css +1 -1
  30. package/dist/{5830.bundle.b073c265c4fcea1afff3.js → 5830.bundle.791019deddd536980a11.js} +3 -3
  31. package/dist/{5858.bundle.ff6b340cf7457db76a1a.js → 5858.bundle.466e58128de344ab53f3.js} +90 -86
  32. package/dist/{6027.bundle.49a5c78a49ed6cd15f72.js → 6027.bundle.68fec283e9239785829d.js} +3 -5
  33. package/dist/{6354.bundle.c387737dc09c9cab4ff2.js → 6354.bundle.929febcf6d326e582e00.js} +151 -140
  34. package/dist/{6376.bundle.9cb74d7bc08476e2f1a7.js → 6376.bundle.527820a5cb1eece2a8d2.js} +6 -6
  35. package/dist/{2842.bundle.860b9f10fcdd9656947a.js → 6386.bundle.dc7e3b159d6b4733647f.js} +913 -2290
  36. package/dist/6939.bundle.41fbdef87597b5172ec6.js +3 -3
  37. package/dist/{7159.bundle.a5991a5d4f0dd8f1c95f.js → 7159.bundle.fb9df255868960f69765.js} +7 -5
  38. package/dist/{3081.bundle.30cd450e2dd213b1b978.js → 7166.bundle.d78a2e98bfcd9467f4c5.js} +1257 -467
  39. package/dist/{7190.bundle.e8f0193e0e06472f795c.js → 7190.bundle.479332d662ffe08d1f92.js} +1228 -855
  40. package/dist/{732.bundle.6978ac30a1e36abf06db.js → 732.bundle.ea6f9d8504e37e01a208.js} +5 -13
  41. package/dist/{7431.bundle.b01791d10e6cf9f503b0.js → 7431.bundle.a9b4dbf97a8c196efe51.js} +28 -28
  42. package/dist/{4410.bundle.c5224cd7d6238a7d4660.js → 7537.bundle.88314e5a6269f96a0bae.js} +5226 -2250
  43. package/dist/{7639.bundle.78e54c6f8dee95f4a6b7.js → 7639.bundle.0894138f5a71ebb77573.js} +3 -5
  44. package/dist/7758.bundle.c8d106364298e7d288f0.js +3 -3
  45. package/dist/{8094.bundle.5c44190a325ac23e3e5c.js → 8094.bundle.148a66619607e37cbf19.js} +3 -4
  46. package/dist/{8305.bundle.d7423a538dca30c376ce.js → 8305.bundle.889f34636979b7d84c2f.js} +71 -66
  47. package/dist/{6163.bundle.ab7295912f610ef8b0eb.js → 8499.bundle.a5bcbdc972c96cc2a9b8.js} +7 -302
  48. package/dist/8499.css +2 -0
  49. package/dist/{85.bundle.0efe2e23b40b836a1fc1.js → 85.bundle.12227ce146b7ba52a74b.js} +3 -5
  50. package/dist/{8558.bundle.53e0018bbb77300516b5.js → 8558.bundle.71a05b3a3031e9638f52.js} +15 -309
  51. package/dist/{8583.bundle.4658de27c0d59037cc9b.js → 8583.bundle.ba19b136c21afeebaed0.js} +24 -27
  52. package/dist/{997.bundle.822b33e561263084e18c.js → 9039.bundle.f5db80328c99cd11293b.js} +4086 -3015
  53. package/dist/{7412.bundle.fab1742191b7fe937330.js → 9205.bundle.f2121894550d46d17398.js} +6180 -3330
  54. package/dist/{3584.bundle.8cc0750425513433e9cc.js → 9567.bundle.ff782480a4c66e306027.js} +3751 -2856
  55. package/dist/{9845.bundle.255e7c7f7a88193b4e47.js → 9845.bundle.8c450e8d65a78a5afcd3.js} +10 -10
  56. package/dist/{9862.bundle.3a8958a82c572015d25d.js → 9862.bundle.6d793a997f85d73002e9.js} +8 -6
  57. package/dist/{9927.bundle.34fec2c87a385cd104be.js → 9927.bundle.f9f850442da32cf50f8a.js} +8 -6
  58. package/dist/app-config.js +12 -0
  59. package/dist/{app.bundle.43da7d5a1360d749cf34.js → app.bundle.bee5d88f966ce61e1d93.js} +65888 -62863
  60. package/dist/app.bundle.css +3 -3
  61. package/dist/{polySeg.bundle.a5aa9130b4191253c410.js → compute.bundle.5e9691bc58308d865058.js} +8 -20
  62. package/dist/{histogram-worker.bundle.d4e40a8018d2698b072e.js → histogram-worker.bundle.a2a50c4674d99c619ca7.js} +11 -23
  63. package/dist/index.html +1 -1
  64. package/dist/{interpolation.bundle.c70cb95d164dc494e6dc.js → interpolation.bundle.829844ff3f72f5645f29.js} +14 -26
  65. package/dist/{compute.bundle.64280c7af19ff567465f.js → polySeg.bundle.c394421c24d8c27d54ca.js} +10 -22
  66. package/dist/sw.js +1 -1
  67. package/package.json +23 -22
  68. package/dist/3343.bundle.d7578ce8f75d158c0bab.js +0 -297
  69. /package/dist/{1730.css → 3138.css} +0 -0
  70. /package/dist/{147.css → 3754.css} +0 -0
  71. /package/dist/{3343.css → 4972.css} +0 -0
  72. /package/dist/{6163.css → 7829.css} +0 -0
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
- (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[3081],{
2
+ (globalThis["webpackChunk"] = globalThis["webpackChunk"] || []).push([[7166],{
3
3
 
4
- /***/ 38007:
5
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
4
+ /***/ 38007
5
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
6
6
 
7
7
 
8
8
  // EXPORTS
@@ -25,8 +25,8 @@ var useSegmentations = __webpack_require__(73421);
25
25
  var useViewportSegmentations = __webpack_require__(79063);
26
26
  // EXTERNAL MODULE: ../../../node_modules/react/index.js
27
27
  var react = __webpack_require__(86326);
28
- // EXTERNAL MODULE: ../../ui-next/src/index.ts + 3075 modules
29
- var src = __webpack_require__(12517);
28
+ // EXTERNAL MODULE: ../../ui-next/src/index.ts + 3085 modules
29
+ var src = __webpack_require__(564);
30
30
  ;// ../../../extensions/cornerstone/src/hooks/useViewportHover.ts
31
31
 
32
32
 
@@ -109,15 +109,15 @@ var useViewportRendering = __webpack_require__(47488);
109
109
 
110
110
 
111
111
 
112
- /***/ }),
112
+ /***/ },
113
113
 
114
- /***/ 9234:
115
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
114
+ /***/ 9234
115
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
116
116
 
117
117
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
118
118
  /* harmony export */ c: () => (/* binding */ useActiveViewportSegmentationRepresentations)
119
119
  /* harmony export */ });
120
- /* harmony import */ var _ohif_ui_next__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12517);
120
+ /* harmony import */ var _ohif_ui_next__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(564);
121
121
  /* harmony import */ var _useViewportSegmentations__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(79063);
122
122
 
123
123
 
@@ -131,10 +131,10 @@ function useActiveViewportSegmentationRepresentations() {
131
131
  }
132
132
 
133
133
 
134
- /***/ }),
134
+ /***/ },
135
135
 
136
- /***/ 84535:
137
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
136
+ /***/ 84535
137
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
138
138
 
139
139
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
140
140
  /* harmony export */ R: () => (/* binding */ useMeasurementTracking)
@@ -238,10 +238,10 @@ function useMeasurementTracking({
238
238
  }
239
239
  /* unused harmony default export */ var __WEBPACK_DEFAULT_EXPORT__ = ((/* unused pure expression or super */ null && (useMeasurementTracking)));
240
240
 
241
- /***/ }),
241
+ /***/ },
242
242
 
243
- /***/ 19214:
244
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
243
+ /***/ 19214
244
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
245
245
 
246
246
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
247
247
  /* harmony export */ d: () => (/* binding */ useMeasurements)
@@ -348,10 +348,10 @@ function useMeasurements({
348
348
  return displayMeasurements;
349
349
  }
350
350
 
351
- /***/ }),
351
+ /***/ },
352
352
 
353
- /***/ 73421:
354
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
353
+ /***/ 73421
354
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
355
355
 
356
356
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
357
357
  /* harmony export */ j: () => (/* binding */ useSegmentations)
@@ -483,10 +483,10 @@ function useSegmentations(options) {
483
483
  return segmentations;
484
484
  }
485
485
 
486
- /***/ }),
486
+ /***/ },
487
487
 
488
- /***/ 10225:
489
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
488
+ /***/ 10225
489
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
490
490
 
491
491
 
492
492
  // EXPORTS
@@ -498,8 +498,8 @@ __webpack_require__.d(__webpack_exports__, {
498
498
  var react = __webpack_require__(86326);
499
499
  // EXTERNAL MODULE: ../../core/src/index.ts + 69 modules
500
500
  var src = __webpack_require__(42356);
501
- // EXTERNAL MODULE: ../../ui-next/src/index.ts + 3075 modules
502
- var ui_next_src = __webpack_require__(12517);
501
+ // EXTERNAL MODULE: ../../ui-next/src/index.ts + 3085 modules
502
+ var ui_next_src = __webpack_require__(564);
503
503
  // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/core/dist/esm/index.js + 1 modules
504
504
  var esm = __webpack_require__(15327);
505
505
  ;// ../../../extensions/cornerstone/src/components/ViewportDataOverlaySettingMenu/utils.ts
@@ -805,10 +805,10 @@ function useViewportDisplaySets(viewportId, options) {
805
805
  return result;
806
806
  }
807
807
 
808
- /***/ }),
808
+ /***/ },
809
809
 
810
- /***/ 47488:
811
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
810
+ /***/ 47488
811
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
812
812
 
813
813
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
814
814
  /* harmony export */ A: () => (__WEBPACK_DEFAULT_EXPORT__),
@@ -818,7 +818,9 @@ function useViewportDisplaySets(viewportId, options) {
818
818
  /* harmony import */ var _ohif_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(42356);
819
819
  /* harmony import */ var _useViewportDisplaySets__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10225);
820
820
  /* harmony import */ var _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(15327);
821
- /* harmony import */ var _ohif_core_src_services_ToolBarService_ToolbarService__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(93813);
821
+ /* harmony import */ var _utils_getDataIdForViewport__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(67142);
822
+ /* harmony import */ var _ohif_core_src_services_ToolBarService_ToolbarService__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(93813);
823
+
822
824
 
823
825
 
824
826
 
@@ -826,13 +828,13 @@ function useViewportDisplaySets(viewportId, options) {
826
828
 
827
829
  const getPosition = location => {
828
830
  switch (location) {
829
- case _ohif_core_src_services_ToolBarService_ToolbarService__WEBPACK_IMPORTED_MODULE_4__/* .ButtonLocation */ .ij.LeftMiddle:
831
+ case _ohif_core_src_services_ToolBarService_ToolbarService__WEBPACK_IMPORTED_MODULE_5__/* .ButtonLocation */ .ij.LeftMiddle:
830
832
  return 'left';
831
- case _ohif_core_src_services_ToolBarService_ToolbarService__WEBPACK_IMPORTED_MODULE_4__/* .ButtonLocation */ .ij.RightMiddle:
833
+ case _ohif_core_src_services_ToolBarService_ToolbarService__WEBPACK_IMPORTED_MODULE_5__/* .ButtonLocation */ .ij.RightMiddle:
832
834
  return 'right';
833
- case _ohif_core_src_services_ToolBarService_ToolbarService__WEBPACK_IMPORTED_MODULE_4__/* .ButtonLocation */ .ij.BottomMiddle:
835
+ case _ohif_core_src_services_ToolBarService_ToolbarService__WEBPACK_IMPORTED_MODULE_5__/* .ButtonLocation */ .ij.BottomMiddle:
834
836
  return 'bottom';
835
- case _ohif_core_src_services_ToolBarService_ToolbarService__WEBPACK_IMPORTED_MODULE_4__/* .ButtonLocation */ .ij.TopMiddle:
837
+ case _ohif_core_src_services_ToolBarService_ToolbarService__WEBPACK_IMPORTED_MODULE_5__/* .ButtonLocation */ .ij.TopMiddle:
836
838
  return 'top';
837
839
  default:
838
840
  return 'bottom';
@@ -846,13 +848,6 @@ const linearToOpacity = linearValue => {
846
848
  const opacityToLinear = opacityValue => {
847
849
  return Math.pow(opacityValue, 1.0 / GAMMA);
848
850
  };
849
- const is3DViewport = ({
850
- viewportId,
851
- cornerstoneViewportService
852
- }) => {
853
- const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
854
- return viewport instanceof _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.VolumeViewport3D;
855
- };
856
851
 
857
852
  /**
858
853
  * Hook to access window level functionality for a specific viewport
@@ -871,14 +866,13 @@ function useViewportRendering(viewportId, options) {
871
866
  colorbarService,
872
867
  customizationService
873
868
  } = servicesManager.services;
874
- const [is3DVolume, setIs3DVolume] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(is3DViewport({
875
- viewportId,
876
- cornerstoneViewportService
877
- }));
878
869
  const [hasColorbar, setHasColorbar] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(colorbarService.hasColorbar(viewportId));
879
870
  const [colorbarPosition, setColorbarPosition] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(options?.location ? getPosition(options.location) : 'bottom');
880
871
  const [voiRange, setVoiRange] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)();
881
872
  const voiRangeRef = react__WEBPACK_IMPORTED_MODULE_0__.useRef();
873
+ // Viewport from service; kept in state so we can subscribe to VIEWPORT_DATA_CHANGED when null and re-run effects when it becomes available
874
+ const [viewport, setViewport] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(() => viewportId ? cornerstoneViewportService.getCornerstoneViewport(viewportId) ?? null : null);
875
+ const [is3DVolume, setIs3DVolume] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(viewport instanceof _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.VolumeViewport3D);
882
876
  const [opacity, setOpacityState] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)();
883
877
  const [opacityLinear, setOpacityLinearState] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)();
884
878
  const [threshold, setThresholdState] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)();
@@ -922,6 +916,35 @@ function useViewportRendering(viewportId, options) {
922
916
  }) || [];
923
917
  }, [viewportDisplaySets, presets]);
924
918
 
919
+ // Keep viewport in state; when not available, subscribe to VIEWPORT_DATA_CHANGED so we set it when the viewport is ready
920
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
921
+ if (!viewportId) {
922
+ setViewport(null);
923
+ return;
924
+ }
925
+ const vp = cornerstoneViewportService.getCornerstoneViewport(viewportId);
926
+ setViewport(vp ?? null);
927
+ if (vp) {
928
+ return;
929
+ }
930
+ const {
931
+ unsubscribe
932
+ } = cornerstoneViewportService.subscribe(cornerstoneViewportService.EVENTS.VIEWPORT_DATA_CHANGED, ({
933
+ viewportId: eventViewportId
934
+ }) => {
935
+ if (eventViewportId !== viewportId) {
936
+ return;
937
+ }
938
+ const next = cornerstoneViewportService.getCornerstoneViewport(viewportId);
939
+ if (next) {
940
+ setViewport(next);
941
+ }
942
+ });
943
+ return () => {
944
+ unsubscribe();
945
+ };
946
+ }, [viewportId, cornerstoneViewportService]);
947
+
925
948
  // Calculate pixel value range for the active display set
926
949
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
927
950
  if (!activeDisplaySetInstanceUID) {
@@ -931,21 +954,20 @@ function useViewportRendering(viewportId, options) {
931
954
  if (!selectedDisplaySet?.imageIds?.length) {
932
955
  return;
933
956
  }
934
- const csViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
935
- if (!csViewport) {
957
+ if (!viewport) {
936
958
  return;
937
959
  }
938
- if (!(csViewport instanceof _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.BaseVolumeViewport)) {
960
+ if (!(viewport instanceof _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.BaseVolumeViewport)) {
939
961
  return;
940
962
  }
941
- const volumeIds = csViewport.getAllVolumeIds();
963
+ const volumeIds = viewport.getAllVolumeIds();
942
964
  const volumeId = volumeIds.find(id => id.includes(activeDisplaySetInstanceUID));
943
965
  if (!volumeId) {
944
966
  return;
945
967
  }
946
968
 
947
969
  // only handle volume viewports for now
948
- const imageData = csViewport.getImageData(volumeId);
970
+ const imageData = viewport.getImageData(volumeId);
949
971
  if (!imageData) {
950
972
  return;
951
973
  }
@@ -958,7 +980,7 @@ function useViewportRendering(viewportId, options) {
958
980
  min: range[0],
959
981
  max: range[1]
960
982
  });
961
- }, [activeDisplaySetInstanceUID, displaySetService, cornerstoneViewportService, viewportId]);
983
+ }, [activeDisplaySetInstanceUID, displaySetService, viewport]);
962
984
 
963
985
  // Get the presets specifically for the active display set
964
986
  const activeDisplaySetPresets = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => {
@@ -972,52 +994,33 @@ function useViewportRendering(viewportId, options) {
972
994
  return activePresetData.presets;
973
995
  }, [allWindowLevelPresets, activeDisplaySetInstanceUID]);
974
996
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
975
- setIs3DVolume(is3DViewport({
976
- viewportId,
977
- cornerstoneViewportService
978
- }));
979
- const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
980
-
981
- // Initialize the VOI range from the viewport
982
- if (viewport && activeDisplaySetInstanceUID) {
983
- try {
984
- let properties;
985
- if (viewport instanceof _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.StackViewport) {
986
- properties = viewport.getProperties();
987
- if (properties.voiRange) {
988
- setVoiRange(properties.voiRange);
989
- voiRangeRef.current = properties.voiRange;
990
- }
991
- } else if (viewport instanceof _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.BaseVolumeViewport) {
992
- // For volume viewports, find the actor for the active display set
993
- const volumeIds = viewport.getAllVolumeIds();
994
- const volumeId = volumeIds.find(id => id.includes(activeDisplaySetInstanceUID));
995
- if (volumeId) {
996
- properties = viewport.getProperties(volumeId);
997
- if (properties?.voiRange) {
998
- setVoiRange(properties.voiRange);
999
- voiRangeRef.current = properties.voiRange;
1000
- }
1001
-
1002
- // Get opacity from colormap if available
1003
- if (properties?.colormap?.opacity !== undefined) {
1004
- const isArray = Array.isArray(properties.colormap.opacity);
1005
- const opacity = isArray ? properties.colormap.opacity.reduce((max, current) => Math.max(max, current), 0) : properties.colormap.opacity;
1006
- setOpacityState(opacity);
1007
- setOpacityLinearState(opacityToLinear(opacity));
1008
- }
1009
-
1010
- // Get threshold from colormap if available
1011
- if (properties?.colormap && properties.colormap.threshold !== undefined) {
1012
- setThresholdState(properties.colormap.threshold);
1013
- }
1014
- }
1015
- }
1016
- } catch (error) {
1017
- console.error('Error initializing VOI range:', error);
997
+ setIs3DVolume(viewport instanceof _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.VolumeViewport3D);
998
+ if (!viewport || !activeDisplaySetInstanceUID) {
999
+ return;
1000
+ }
1001
+ try {
1002
+ const dataId = (0,_utils_getDataIdForViewport__WEBPACK_IMPORTED_MODULE_4__/* .getDataIdForViewport */ .V)(viewport, activeDisplaySetInstanceUID);
1003
+ const properties = dataId != null ? viewport.getProperties(dataId) : viewport.getProperties();
1004
+ if (!properties) {
1005
+ return;
1018
1006
  }
1007
+ if (properties.voiRange) {
1008
+ setVoiRange(properties.voiRange);
1009
+ voiRangeRef.current = properties.voiRange;
1010
+ }
1011
+ if (properties.colormap?.opacity !== undefined) {
1012
+ const opacityVal = properties.colormap.opacity;
1013
+ const opacity = Array.isArray(opacityVal) ? opacityVal.reduce((max, current) => Math.max(max, current), 0) : opacityVal;
1014
+ setOpacityState(opacity);
1015
+ setOpacityLinearState(opacityToLinear(opacity));
1016
+ }
1017
+ if (properties.colormap?.threshold !== undefined) {
1018
+ setThresholdState(properties.colormap.threshold);
1019
+ }
1020
+ } catch (error) {
1021
+ console.error('Error initializing VOI range:', error);
1019
1022
  }
1020
- }, [cornerstoneViewportService, viewportId, activeDisplaySetInstanceUID]);
1023
+ }, [activeDisplaySetInstanceUID, viewport]);
1021
1024
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
1022
1025
  if (!viewportId) {
1023
1026
  return;
@@ -1035,17 +1038,10 @@ function useViewportRendering(viewportId, options) {
1035
1038
  };
1036
1039
  }, [colorbarService, viewportId]);
1037
1040
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
1038
- if (!viewportId || !activeDisplaySetInstanceUID) {
1039
- return;
1040
- }
1041
- const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
1042
- if (!viewport) {
1041
+ if (!activeDisplaySetInstanceUID || !viewport?.element) {
1043
1042
  return;
1044
1043
  }
1045
1044
  const element = viewport.element;
1046
- if (!element) {
1047
- return;
1048
- }
1049
1045
  const updateVOI = eventDetail => {
1050
1046
  const {
1051
1047
  range
@@ -1085,7 +1081,7 @@ function useViewportRendering(viewportId, options) {
1085
1081
  element.removeEventListener(_cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.Enums.Events.VOI_MODIFIED, updateVOI);
1086
1082
  element.removeEventListener(_cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.Enums.Events.COLORMAP_MODIFIED, updateColormap);
1087
1083
  };
1088
- }, [viewportId, activeDisplaySetInstanceUID, cornerstoneViewportService, opacityToLinear]);
1084
+ }, [activeDisplaySetInstanceUID, viewport, opacityToLinear]);
1089
1085
  const validateActiveDisplaySet = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(() => {
1090
1086
  if (!activeDisplaySetInstanceUID) {
1091
1087
  throw new Error('No active display set instance UID is available');
@@ -1238,12 +1234,8 @@ function useViewportRendering(viewportId, options) {
1238
1234
  },
1239
1235
  context: 'CORNERSTONE'
1240
1236
  });
1241
- }, [commandsManager, viewportId, validateActiveDisplaySet]);
1237
+ }, [commandsManager, validateActiveDisplaySet, viewport, viewportId]);
1242
1238
  const setOpacity = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(opacityValue => {
1243
- if (!viewportId) {
1244
- return;
1245
- }
1246
- const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
1247
1239
  if (!viewport || !(viewport instanceof _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.BaseVolumeViewport)) {
1248
1240
  return;
1249
1241
  }
@@ -1274,17 +1266,13 @@ function useViewportRendering(viewportId, options) {
1274
1266
  colormap: updatedColormap
1275
1267
  }, volumeId);
1276
1268
  viewport.render();
1277
- }, [cornerstoneViewportService, viewportId, validateActiveDisplaySet, opacityToLinear]);
1269
+ }, [validateActiveDisplaySet, opacityToLinear, viewport]);
1278
1270
  const setOpacityLinear = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(linearValue => {
1279
1271
  // Convert linear UI value to actual opacity value and apply it
1280
1272
  const actualOpacity = linearToOpacity(linearValue);
1281
1273
  setOpacity(actualOpacity);
1282
1274
  }, [linearToOpacity, setOpacity]);
1283
1275
  const setThreshold = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(thresholdValue => {
1284
- if (!viewportId) {
1285
- return;
1286
- }
1287
- const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
1288
1276
  if (!viewport || !(viewport instanceof _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.BaseVolumeViewport)) {
1289
1277
  return;
1290
1278
  }
@@ -1302,15 +1290,14 @@ function useViewportRendering(viewportId, options) {
1302
1290
  }
1303
1291
  }, volumeId);
1304
1292
  viewport.render();
1305
- }, [cornerstoneViewportService, viewportId, validateActiveDisplaySet]);
1293
+ }, [validateActiveDisplaySet, viewport]);
1306
1294
 
1307
1295
  // Get the current colormap for the active display set
1308
1296
  const colormap = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => {
1309
- if (!viewportId || !activeDisplaySetInstanceUID || !viewportDisplaySets?.length) {
1297
+ if (!activeDisplaySetInstanceUID || !viewportDisplaySets?.length) {
1310
1298
  return null;
1311
1299
  }
1312
1300
  try {
1313
- const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
1314
1301
  if (!viewport) {
1315
1302
  return null;
1316
1303
  }
@@ -1339,7 +1326,7 @@ function useViewportRendering(viewportId, options) {
1339
1326
  console.error('Error getting viewport colormap:', error);
1340
1327
  return colorbarProperties?.colormaps?.find(c => c.Name === 'Grayscale') || colorbarProperties?.colormaps?.[0];
1341
1328
  }
1342
- }, [cornerstoneViewportService, viewportId, activeDisplaySetInstanceUID, viewportDisplaySets, colorbarProperties?.colormaps]);
1329
+ }, [activeDisplaySetInstanceUID, viewportDisplaySets, colorbarProperties?.colormaps, viewport]);
1343
1330
 
1344
1331
  // 3D volume rendering functions
1345
1332
  const setVolumeRenderingPreset = (0,react__WEBPACK_IMPORTED_MODULE_0__.useCallback)(preset => {
@@ -1405,7 +1392,10 @@ function useViewportRendering(viewportId, options) {
1405
1392
  setWindowLevel,
1406
1393
  setVOIRange,
1407
1394
  voiRange,
1408
- windowLevel: _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.utilities.windowLevel.toWindowLevel(voiRange?.lower, voiRange?.upper),
1395
+ windowLevel: voiRange ? _cornerstonejs_core__WEBPACK_IMPORTED_MODULE_3__.utilities.windowLevel.toWindowLevel(voiRange?.lower, voiRange?.upper) : {
1396
+ windowCenter: null,
1397
+ windowWidth: null
1398
+ },
1409
1399
  // Colorbar functions
1410
1400
  hasColorbar,
1411
1401
  toggleColorbar,
@@ -1473,10 +1463,10 @@ function getCustomizationData(customizationService) {
1473
1463
  };
1474
1464
  }
1475
1465
 
1476
- /***/ }),
1466
+ /***/ },
1477
1467
 
1478
- /***/ 79063:
1479
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1468
+ /***/ 79063
1469
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
1480
1470
 
1481
1471
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
1482
1472
  /* harmony export */ L: () => (/* binding */ useViewportSegmentations)
@@ -1494,7 +1484,8 @@ const excludedModalities = ['SM', 'OT', 'DOC', 'ECG'];
1494
1484
  function mapSegmentationToDisplay(segmentation, customizationService) {
1495
1485
  const {
1496
1486
  label,
1497
- segments
1487
+ segments,
1488
+ fallbackLabel
1498
1489
  } = segmentation;
1499
1490
 
1500
1491
  // Get the readable text mapping once
@@ -1555,6 +1546,7 @@ function mapSegmentationToDisplay(segmentation, customizationService) {
1555
1546
  return {
1556
1547
  ...segmentation,
1557
1548
  label,
1549
+ fallbackLabel,
1558
1550
  segments: updatedSegments
1559
1551
  };
1560
1552
  }
@@ -1657,74 +1649,36 @@ function useViewportSegmentations({
1657
1649
  return segmentationsWithRepresentations;
1658
1650
  }
1659
1651
 
1660
- /***/ }),
1652
+ /***/ },
1661
1653
 
1662
- /***/ 63081:
1663
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
1654
+ /***/ 87166
1655
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
1664
1656
 
1665
- // ESM COMPAT FLAG
1666
- __webpack_require__.r(__webpack_exports__);
1667
1657
 
1668
1658
  // EXPORTS
1669
1659
  __webpack_require__.d(__webpack_exports__, {
1670
- AccordionGroup: () => (/* reexport */ AccordionGroup),
1671
- CloneChildren: () => (/* reexport */ CloneChildren),
1672
- Colorbar: () => (/* reexport */ Colorbar),
1673
- Colormap: () => (/* reexport */ Colormap),
1674
- CornerstoneViewportDownloadForm: () => (/* reexport */ utils_CornerstoneViewportDownloadForm),
1675
- DYNAMIC_VOLUME_LOADER_SCHEME: () => (/* reexport */ DYNAMIC_VOLUME_LOADER_SCHEME),
1676
- DicomUpload: () => (/* reexport */ DicomUpload_DicomUpload),
1677
- Enums: () => (/* reexport */ enums),
1678
- ImageOverlayViewerTool: () => (/* reexport */ tools_ImageOverlayViewerTool),
1679
- MeasumentsMenu: () => (/* reexport */ MeasumentsMenu),
1680
- MeasurementOrAdditionalFindingSets: () => (/* reexport */ MeasurementOrAdditionalFindingSets),
1681
- MeasurementsOrAdditionalFindings: () => (/* reexport */ MeasurementsOrAdditionalFindings),
1682
- OHIFCornerstoneViewport: () => (/* binding */ OHIFCornerstoneViewport),
1683
- PanelMeasurement: () => (/* reexport */ PanelMeasurement),
1684
- PanelSegmentation: () => (/* reexport */ PanelSegmentation),
1685
- PlanarFreehandROI: () => (/* reexport */ measurementServiceMappings_PlanarFreehandROI),
1686
- RectangleROI: () => (/* reexport */ measurementServiceMappings_RectangleROI),
1687
- SeriesMeasurementTrigger: () => (/* reexport */ SeriesMeasurementTrigger),
1688
- SeriesMeasurements: () => (/* reexport */ SeriesMeasurements),
1689
- StudyMeasurements: () => (/* reexport */ StudyMeasurements),
1690
- StudyMeasurementsActions: () => (/* reexport */ StudyMeasurementsActions),
1691
- StudySummaryFromMetadata: () => (/* reexport */ StudySummaryFromMetadata),
1692
- Types: () => (/* reexport */ types_namespaceObject),
1693
- VOLUME_LOADER_SCHEME: () => (/* reexport */ VOLUME_LOADER_SCHEME),
1694
- VolumeLighting: () => (/* reexport */ VolumeLighting),
1695
- VolumeRenderingOptions: () => (/* reexport */ VolumeRenderingOptions),
1696
- VolumeRenderingPresets: () => (/* reexport */ VolumeRenderingPresets),
1697
- VolumeRenderingQuality: () => (/* reexport */ VolumeRenderingQuality),
1698
- VolumeShade: () => (/* reexport */ VolumeShade),
1699
- VolumeShift: () => (/* reexport */ VolumeShift),
1700
- WindowLevel: () => (/* reexport */ WindowLevel),
1701
- WindowLevelActionMenu: () => (/* reexport */ WindowLevelActionMenu),
1702
- WindowLevelActionMenuContent: () => (/* reexport */ WindowLevelActionMenuContent),
1703
- WindowLevelActionMenuWrapper: () => (/* reexport */ WindowLevelActionMenuWrapper),
1660
+ _I: () => (/* reexport */ AccordionGroup),
1661
+ fX: () => (/* reexport */ enums),
1662
+ CP: () => (/* reexport */ MeasurementsOrAdditionalFindings),
1663
+ R9: () => (/* binding */ OHIFCornerstoneViewport),
1664
+ re: () => (/* reexport */ PanelMeasurement),
1665
+ rE: () => (/* reexport */ PanelSegmentation),
1666
+ V: () => (/* reexport */ SeriesMeasurements),
1667
+ B9: () => (/* reexport */ StudyMeasurements),
1668
+ _9: () => (/* reexport */ StudyMeasurementsActions),
1669
+ x3: () => (/* reexport */ StudySummaryFromMetadata),
1670
+ Rj: () => (/* reexport */ VOLUME_LOADER_SCHEME),
1704
1671
  "default": () => (/* binding */ cornerstone_src),
1705
- dicomLoaderService: () => (/* reexport */ utils_dicomLoaderService),
1706
- findNearbyToolData: () => (/* reexport */ findNearbyToolData),
1707
- getActiveViewportEnabledElement: () => (/* reexport */ getActiveViewportEnabledElement),
1708
- getEnabledElement: () => (/* reexport */ state/* getEnabledElement */.kJ),
1709
- getSOPInstanceAttributes: () => (/* reexport */ getSOPInstanceAttributes),
1710
- groupByDisplaySet: () => (/* reexport */ groupByDisplaySet),
1711
- groupByNamedSets: () => (/* reexport */ groupByNamedSets),
1712
- groupByStudy: () => (/* reexport */ groupByStudy),
1713
- measurementMappingUtils: () => (/* reexport */ utils_namespaceObject),
1714
- setEnabledElement: () => (/* reexport */ state/* setEnabledElement */.ye),
1715
- toolNames: () => (/* reexport */ toolNames),
1716
- useActiveViewportSegmentationRepresentations: () => (/* reexport */ useActiveViewportSegmentationRepresentations/* useActiveViewportSegmentationRepresentations */.c),
1717
- useLutPresentationStore: () => (/* reexport */ useLutPresentationStore/* useLutPresentationStore */.I),
1718
- useMeasurementTracking: () => (/* reexport */ useMeasurementTracking/* useMeasurementTracking */.R),
1719
- useMeasurements: () => (/* reexport */ useMeasurements/* useMeasurements */.d),
1720
- usePositionPresentationStore: () => (/* reexport */ usePositionPresentationStore/* usePositionPresentationStore */.q),
1721
- useSegmentationPresentationStore: () => (/* reexport */ useSegmentationPresentationStore/* useSegmentationPresentationStore */.v),
1722
- useSegmentations: () => (/* reexport */ useSegmentations/* useSegmentations */.j),
1723
- useSelectedSegmentationsForViewportStore: () => (/* reexport */ useSelectedSegmentationsForViewportStore),
1724
- useSynchronizersStore: () => (/* reexport */ useSynchronizersStore/* useSynchronizersStore */.U),
1725
- utils: () => (/* reexport */ src_utils)
1672
+ HA: () => (/* reexport */ utils_dicomLoaderService),
1673
+ J4: () => (/* reexport */ getSOPInstanceAttributes),
1674
+ c3: () => (/* reexport */ useActiveViewportSegmentationRepresentations/* useActiveViewportSegmentationRepresentations */.c),
1675
+ qu: () => (/* reexport */ usePositionPresentationStore/* usePositionPresentationStore */.q),
1676
+ jo: () => (/* reexport */ useSegmentations/* useSegmentations */.j),
1677
+ Wp: () => (/* reexport */ src_utils)
1726
1678
  });
1727
1679
 
1680
+ // UNUSED EXPORTS: CloneChildren, Colorbar, Colormap, CornerstoneViewportDownloadForm, DYNAMIC_VOLUME_LOADER_SCHEME, DicomUpload, ImageOverlayViewerTool, MeasumentsMenu, MeasurementOrAdditionalFindingSets, PlanarFreehandROI, RectangleROI, SeriesMeasurementTrigger, Types, VolumeLighting, VolumeRenderingOptions, VolumeRenderingPresets, VolumeRenderingQuality, VolumeShade, VolumeShift, WindowLevel, WindowLevelActionMenu, WindowLevelActionMenuContent, WindowLevelActionMenuWrapper, findNearbyToolData, getActiveViewportEnabledElement, getEnabledElement, groupByDisplaySet, groupByNamedSets, groupByStudy, measurementMappingUtils, setEnabledElement, toolNames, useLutPresentationStore, useMeasurementTracking, useMeasurements, useSegmentationPresentationStore, useSelectedSegmentationsForViewportStore, useSynchronizersStore
1681
+
1728
1682
  // NAMESPACE OBJECT: ../../../extensions/cornerstone/src/enums.ts
1729
1683
  var enums_namespaceObject = {};
1730
1684
  __webpack_require__.r(enums_namespaceObject);
@@ -1734,22 +1688,6 @@ __webpack_require__.d(enums_namespaceObject, {
1734
1688
  Ay: () => (enums)
1735
1689
  });
1736
1690
 
1737
- // NAMESPACE OBJECT: ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/index.ts
1738
- var utils_namespaceObject = {};
1739
- __webpack_require__.r(utils_namespaceObject);
1740
- __webpack_require__.d(utils_namespaceObject, {
1741
- getDisplayUnit: () => (utils_getDisplayUnit),
1742
- getFirstAnnotationSelected: () => (getFirstAnnotationSelected),
1743
- getHandlesFromPoints: () => (getHandlesFromPoints),
1744
- getSOPInstanceAttributes: () => (getSOPInstanceAttributes),
1745
- isAnnotationSelected: () => (isAnnotationSelected),
1746
- setAnnotationSelected: () => (setAnnotationSelected)
1747
- });
1748
-
1749
- // NAMESPACE OBJECT: ../../../extensions/cornerstone/src/types/index.ts
1750
- var types_namespaceObject = {};
1751
- __webpack_require__.r(types_namespaceObject);
1752
-
1753
1691
  // EXTERNAL MODULE: ../../../node_modules/react/index.js
1754
1692
  var react = __webpack_require__(86326);
1755
1693
  // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/core/dist/esm/index.js + 1 modules
@@ -1770,8 +1708,8 @@ var src = __webpack_require__(42356);
1770
1708
  var loaders = __webpack_require__(19742);
1771
1709
  // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/core/dist/esm/enums/RequestType.js
1772
1710
  var RequestType = __webpack_require__(43213);
1773
- // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/dicom-image-loader/dist/esm/index.js + 77 modules
1774
- var dicom_image_loader_dist_esm = __webpack_require__(75524);
1711
+ // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/dicom-image-loader/dist/esm/index.js + 80 modules
1712
+ var dicom_image_loader_dist_esm = __webpack_require__(76318);
1775
1713
  ;// ../../../extensions/cornerstone/src/initWADOImageLoader.js
1776
1714
 
1777
1715
 
@@ -1810,8 +1748,8 @@ function destroy() {
1810
1748
  var ai_dist_esm = __webpack_require__(75165);
1811
1749
  // EXTERNAL MODULE: ../../../node_modules/@cornerstonejs/polymorphic-segmentation/dist/esm/index.js + 14 modules
1812
1750
  var polymorphic_segmentation_dist_esm = __webpack_require__(56983);
1813
- // EXTERNAL MODULE: ../../../extensions/default/src/index.ts + 139 modules
1814
- var default_src = __webpack_require__(39195);
1751
+ // EXTERNAL MODULE: ../../../extensions/default/src/index.ts + 140 modules
1752
+ var default_src = __webpack_require__(62851);
1815
1753
  // EXTERNAL MODULE: ../../../extensions/cornerstone/src/state.ts
1816
1754
  var state = __webpack_require__(71353);
1817
1755
  ;// ../../../extensions/cornerstone/src/utils/getViewportEnabledElement.ts
@@ -1913,7 +1851,7 @@ function onCompletedCalibrationLine(servicesManager, csToolsEvent) {
1913
1851
  reject('UIDialogService is not initiated');
1914
1852
  return;
1915
1853
  }
1916
- (0,default_src.callInputDialog)({
1854
+ (0,default_src/* callInputDialog */.l5)({
1917
1855
  uiDialogService,
1918
1856
  title: 'Calibration',
1919
1857
  placeholder: 'Actual Physical distance (mm)',
@@ -2614,6 +2552,7 @@ function getHandlesFromPoints(points) {
2614
2552
  }), {});
2615
2553
  }
2616
2554
  ;// ../../../extensions/cornerstone/src/utils/measurementServiceMappings/utils/selection.ts
2555
+ /* unused harmony import specifier */ var cs3dToolAnnotationUtils;
2617
2556
 
2618
2557
 
2619
2558
  /**
@@ -2622,7 +2561,7 @@ function getHandlesFromPoints(points) {
2622
2561
  * @returns boolean
2623
2562
  */
2624
2563
  function isAnnotationSelected(annotationUID) {
2625
- return dist_esm.annotation.selection.isAnnotationSelected(annotationUID);
2564
+ return cs3dToolAnnotationUtils.selection.isAnnotationSelected(annotationUID);
2626
2565
  }
2627
2566
 
2628
2567
  /**
@@ -2634,7 +2573,7 @@ function setAnnotationSelected(annotationUID, selected) {
2634
2573
  const isCurrentSelected = isAnnotationSelected(annotationUID);
2635
2574
  // branch cut, avoid invoking imaging library unnecessarily.
2636
2575
  if (isCurrentSelected !== selected) {
2637
- dist_esm.annotation.selection.setAnnotationSelected(annotationUID, selected);
2576
+ cs3dToolAnnotationUtils.selection.setAnnotationSelected(annotationUID, selected);
2638
2577
  }
2639
2578
  }
2640
2579
  function getFirstAnnotationSelected(element) {
@@ -4034,10 +3973,10 @@ function getColumnValueReport(annotation, customizationService) {
4034
3973
  } = annotation;
4035
3974
  const stats = data.cachedStats[`imageId:${metadata.referencedImageId}`];
4036
3975
  report.forEach(({
4037
- name,
3976
+ displayName,
4038
3977
  value
4039
3978
  }) => {
4040
- columns.push(name);
3979
+ columns.push(displayName);
4041
3980
  stats[value] ? values.push(stats[value]) : values.push('not available');
4042
3981
  });
4043
3982
 
@@ -4504,10 +4443,10 @@ function SplineROI_getColumnValueReport(annotation, customizationService) {
4504
4443
  } = annotation;
4505
4444
  const stats = data.cachedStats[`imageId:${metadata.referencedImageId}`];
4506
4445
  report.forEach(({
4507
- name,
4446
+ displayName,
4508
4447
  value
4509
4448
  }) => {
4510
- columns.push(name);
4449
+ columns.push(displayName);
4511
4450
  stats[value] ? values.push(stats[value]) : values.push('not available');
4512
4451
  });
4513
4452
 
@@ -5699,6 +5638,9 @@ const {
5699
5638
  removeAnnotation
5700
5639
  } = dist_esm.annotation.state;
5701
5640
  const csToolsEvents = dist_esm.Enums.Events;
5641
+ const {
5642
+ DefaultHistoryMemo
5643
+ } = esm.utilities.HistoryMemo;
5702
5644
  const initMeasurementService = (measurementService, displaySetService, cornerstoneViewportService, customizationService) => {
5703
5645
  /* Initialization */
5704
5646
  const {
@@ -5902,7 +5844,8 @@ const connectMeasurementServiceToTools = ({
5902
5844
  RAW_MEASUREMENT_ADDED
5903
5845
  } = measurementService.EVENTS;
5904
5846
  measurementService.subscribe(MEASUREMENTS_CLEARED, ({
5905
- measurements
5847
+ measurements,
5848
+ trackingContext
5906
5849
  }) => {
5907
5850
  if (!Object.keys(measurements).length) {
5908
5851
  return;
@@ -5926,6 +5869,28 @@ const connectMeasurementServiceToTools = ({
5926
5869
  }
5927
5870
  });
5928
5871
  }
5872
+
5873
+ // If tracking context was provided, push a memo that keeps XState in sync with
5874
+ // Cornerstone's annotation history across unlimited undo/redo cycles:
5875
+ // undo → re-populate trackedStudy/trackedSeries so the panel reflects the
5876
+ // restored annotations.
5877
+ // redo → wipe trackedStudy/trackedSeries because the annotations have been
5878
+ // re-deleted by their own Cornerstone memos; no measurements are
5879
+ // deleted here (they are already gone), so CLEAR_TRACKING_CONTEXT is
5880
+ // used instead of UNTRACK_ALL to avoid a double-delete.
5881
+ if (trackingContext) {
5882
+ DefaultHistoryMemo.push({
5883
+ id: esm.utilities.uuidv4(),
5884
+ operationType: 'trackingState',
5885
+ restoreMemo(undo) {
5886
+ if (undo === true) {
5887
+ commandsManager.run('restoreTrackedSeries', trackingContext);
5888
+ } else if (undo === false) {
5889
+ commandsManager.run('clearTrackedSeries');
5890
+ }
5891
+ }
5892
+ });
5893
+ }
5929
5894
  commandsManager.run('endRecordingForAnnotationGroup');
5930
5895
 
5931
5896
  // trigger a render
@@ -6065,6 +6030,12 @@ const connectMeasurementServiceToTools = ({
6065
6030
  if (source?.name && source.name !== initMeasurementService_CORNERSTONE_3D_TOOLS_SOURCE_NAME) {
6066
6031
  return;
6067
6032
  }
6033
+ // Cancel any active tool manipulation (e.g., Spline/Livewire) to avoid leaving the tool
6034
+ // in a drawing state after deleting a not completed measurement, which can block viewport interactivity.
6035
+ const element = getActiveViewportEnabledElement(viewportGridService)?.viewport?.element;
6036
+ if (element) {
6037
+ (0,dist_esm.cancelActiveManipulations)(element);
6038
+ }
6068
6039
  const removedAnnotation = dist_esm.annotation.state.getAnnotation(removedMeasurementId);
6069
6040
  removeAnnotation(removedMeasurementId);
6070
6041
  // Ensure `removedAnnotation` is available before triggering the memo,
@@ -7241,10 +7212,6 @@ async function init({
7241
7212
  hangingProtocolService.registerImageLoadStrategy(name, createMetadataWrappedStrategy(strategyFn));
7242
7213
  });
7243
7214
 
7244
- // add metadata providers
7245
- esm.metaData.addProvider(esm.utilities.calibratedPixelSpacingMetadataProvider.get.bind(esm.utilities.calibratedPixelSpacingMetadataProvider)); // this provider is required for Calibration tool
7246
- esm.metaData.addProvider(metadataProvider.get.bind(metadataProvider), 9999);
7247
-
7248
7215
  // These are set reasonably low to allow for interleaved retrieves and slower
7249
7216
  // connections.
7250
7217
  esm.imageLoadPoolManager.maxNumRequests = {
@@ -7255,6 +7222,12 @@ async function init({
7255
7222
  };
7256
7223
  initWADOImageLoader(userAuthenticationService, appConfig, extensionManager);
7257
7224
 
7225
+ // Add OHIF metadata providers after dicomImageLoader.init().
7226
+ // The linked metadata branch clears providers during loader init.
7227
+ esm.metaData.addProvider(esm.utilities.genericMetadataProvider.get, 9998);
7228
+ esm.metaData.addProvider(esm.utilities.calibratedPixelSpacingMetadataProvider.get.bind(esm.utilities.calibratedPixelSpacingMetadataProvider)); // this provider is required for Calibration tool
7229
+ esm.metaData.addProvider(metadataProvider.get.bind(metadataProvider), 9999);
7230
+
7258
7231
  /* Measurement Service */
7259
7232
  this.measurementServiceSource = connectToolsToMeasurementService({
7260
7233
  servicesManager,
@@ -7463,7 +7436,8 @@ function _showCPURenderingModal(uiModalService, hangingProtocolService) {
7463
7436
  'viewportOverlay.topRight': [],
7464
7437
  'viewportOverlay.bottomLeft': [{
7465
7438
  id: 'WindowLevel',
7466
- inheritsFrom: 'ohif.overlayItem.windowLevel'
7439
+ inheritsFrom: 'ohif.overlayItem.windowLevel',
7440
+ title: 'Window Level'
7467
7441
  }, {
7468
7442
  id: 'ZoomLevel',
7469
7443
  inheritsFrom: 'ohif.overlayItem.zoomLevel',
@@ -7474,11 +7448,12 @@ function _showCPURenderingModal(uiModalService, hangingProtocolService) {
7474
7448
  }],
7475
7449
  'viewportOverlay.bottomRight': [{
7476
7450
  id: 'InstanceNumber',
7477
- inheritsFrom: 'ohif.overlayItem.instanceNumber'
7451
+ inheritsFrom: 'ohif.overlayItem.instanceNumber',
7452
+ title: 'Instance Number'
7478
7453
  }]
7479
7454
  });
7480
- // EXTERNAL MODULE: ../../ui-next/src/index.ts + 3075 modules
7481
- var ui_next_src = __webpack_require__(12517);
7455
+ // EXTERNAL MODULE: ../../ui-next/src/index.ts + 3085 modules
7456
+ var ui_next_src = __webpack_require__(564);
7482
7457
  // EXTERNAL MODULE: ../../../node_modules/react-i18next/dist/es/index.js + 15 modules
7483
7458
  var es = __webpack_require__(99993);
7484
7459
  ;// ../../../extensions/cornerstone/src/components/ExportSegmentationSubMenuItem.tsx
@@ -7501,37 +7476,13 @@ const ExportSegmentationSubMenuItem = ({
7501
7476
  className: "text-foreground"
7502
7477
  }), /*#__PURE__*/react.createElement("span", {
7503
7478
  className: "pl-2"
7504
- }, t('Download & Export'))), /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuPortal */.dce, null, /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuSubContent */.M56, null, /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuLabel */.lpj, {
7505
- className: "flex items-center pl-0"
7506
- }, /*#__PURE__*/react.createElement(ui_next_src/* Icons */.FI1.Download, {
7507
- className: "h-5 w-5"
7508
- }), /*#__PURE__*/react.createElement("span", {
7509
- className: "pl-1"
7510
- }, t('Download'))), segmentationRepresentationType === esm_enums.SegmentationRepresentations.Labelmap && /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuItem */._26, {
7479
+ }, t('Export'))), /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuPortal */.dce, null, /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuSubContent */.M56, null, segmentationRepresentationType === esm_enums.SegmentationRepresentations.Labelmap && /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuItem */._26, {
7511
7480
  onClick: e => {
7512
7481
  e.preventDefault();
7513
7482
  actions.downloadCSVSegmentationReport(segmentationId);
7514
7483
  },
7515
7484
  disabled: !allowExport
7516
7485
  }, t('CSV Report')), segmentationRepresentationType === esm_enums.SegmentationRepresentations.Labelmap && /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuItem */._26, {
7517
- onClick: e => {
7518
- e.preventDefault();
7519
- actions.onSegmentationDownload(segmentationId);
7520
- },
7521
- disabled: !allowExport
7522
- }, t('DICOM SEG')), /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuItem */._26, {
7523
- onClick: e => {
7524
- e.preventDefault();
7525
- actions.onSegmentationDownloadRTSS(segmentationId);
7526
- },
7527
- disabled: !allowExport
7528
- }, t('DICOM RTSS')), /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuSeparator */.mBJ, null), /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuLabel */.lpj, {
7529
- className: "flex items-center pl-0"
7530
- }, /*#__PURE__*/react.createElement(ui_next_src/* Icons */.FI1.Export, {
7531
- className: "h-5 w-5"
7532
- }), /*#__PURE__*/react.createElement("span", {
7533
- className: "pl-1 pt-1"
7534
- }, t('Export'))), segmentationRepresentationType === esm_enums.SegmentationRepresentations.Labelmap && /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuItem */._26, {
7535
7486
  onClick: e => {
7536
7487
  e.preventDefault();
7537
7488
  actions.storeSegmentation(segmentationId, 'SEG');
@@ -7608,16 +7559,6 @@ const CustomDropdownMenuContent = () => {
7608
7559
  context: 'CORNERSTONE'
7609
7560
  });
7610
7561
  },
7611
- onSegmentationDownloadRTSS: segmentationId => {
7612
- commandsManager.run('downloadRTSS', {
7613
- segmentationId
7614
- });
7615
- },
7616
- onSegmentationDownload: segmentationId => {
7617
- commandsManager.run('downloadSegmentation', {
7618
- segmentationId
7619
- });
7620
- },
7621
7562
  downloadCSVSegmentationReport: segmentationId => {
7622
7563
  commandsManager.run('downloadCSVSegmentationReport', {
7623
7564
  segmentationId
@@ -7815,7 +7756,7 @@ function SegmentationToolConfig() {
7815
7756
  });
7816
7757
  };
7817
7758
  return /*#__PURE__*/react.createElement("div", {
7818
- className: "bg-muted flex flex-col gap-2 border-b border-b-[2px] border-black px-2 py-3"
7759
+ className: "bg-muted flex flex-col gap-2 border-b border-b-[2px] border-background px-2 py-3"
7819
7760
  }, /*#__PURE__*/react.createElement("div", {
7820
7761
  className: "flex items-center gap-2"
7821
7762
  }, /*#__PURE__*/react.createElement(ui_next_src/* Switch */.dOG, {
@@ -8624,7 +8565,7 @@ const DicomUploadProgressItem = /*#__PURE__*/(0,react.memo)(({
8624
8565
  }, /*#__PURE__*/react.createElement("div", {
8625
8566
  className: "flex w-6 shrink-0 items-center justify-center"
8626
8567
  }, getStatusIcon()), /*#__PURE__*/react.createElement("div", {
8627
- className: "overflow-hidden text-ellipsis whitespace-nowrap text-white"
8568
+ className: "text-foreground overflow-hidden text-ellipsis whitespace-nowrap"
8628
8569
  }, dicomFileUploader.getFileName())), failedReason && /*#__PURE__*/react.createElement("div", {
8629
8570
  className: "pl-10"
8630
8571
  }, failedReason)), /*#__PURE__*/react.createElement("div", {
@@ -8866,7 +8807,7 @@ function DicomUploadProgress({
8866
8807
  }, [numFilesCompleted]);
8867
8808
  const getNumCompletedAndTimeRemainingComponent = () => {
8868
8809
  return /*#__PURE__*/react.createElement("div", {
8869
- className: "bg-muted flex h-14 items-center px-1 pb-4 text-lg text-white"
8810
+ className: "bg-muted text-foreground flex h-14 items-center px-1 pb-4 text-lg"
8870
8811
  }, numFilesCompleted === dicomFileUploaderArr.length ? /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement("span", {
8871
8812
  className: NO_WRAP_ELLIPSIS_CLASS_NAMES
8872
8813
  }, `${dicomFileUploaderArr.length} ${dicomFileUploaderArr.length > 1 ? 'files' : 'file'} completed.`), /*#__PURE__*/react.createElement(ui_next_src/* Button */.$nd, {
@@ -8916,7 +8857,7 @@ function DicomUploadProgress({
8916
8857
  return /*#__PURE__*/react.createElement("div", {
8917
8858
  className: "flex grow flex-col"
8918
8859
  }, getNumCompletedAndTimeRemainingComponent(), /*#__PURE__*/react.createElement("div", {
8919
- className: "flex grow flex-col overflow-hidden bg-black text-lg"
8860
+ className: "flex grow flex-col overflow-hidden bg-background text-lg"
8920
8861
  }, getPercentCompleteComponent(), /*#__PURE__*/react.createElement("div", {
8921
8862
  className: "ohif-scrollbar h-1 grow overflow-y-scroll px-2"
8922
8863
  }, dicomFileUploaderArr.filter(dicomFileUploader => !showFailedOnly || dicomFileUploader.getStatus() === UploadStatus.Failed).map(dicomFileUploader => /*#__PURE__*/react.createElement(DicomUpload_DicomUploadProgressItem, {
@@ -8945,7 +8886,7 @@ function DicomUpload({
8945
8886
  onComplete,
8946
8887
  onStarted
8947
8888
  }) {
8948
- const baseClassNames = 'min-h-[375px] flex flex-col bg-black select-none rounded-lg overflow-hidden';
8889
+ const baseClassNames = 'min-h-[375px] flex flex-col bg-background select-none rounded-lg overflow-hidden';
8949
8890
  const [dicomFileUploaderArr, setDicomFileUploaderArr] = (0,react.useState)([]);
8950
8891
  const onDrop = (0,react.useCallback)(async acceptedFiles => {
8951
8892
  onStarted();
@@ -9067,7 +9008,8 @@ function ViewportDownloadFormNew({
9067
9008
  onDimensionsChange,
9068
9009
  onEnableViewport,
9069
9010
  onDisableViewport,
9070
- onDownload
9011
+ onDownload,
9012
+ onCopyToClipboard
9071
9013
  }) {
9072
9014
  const [viewportElement, setViewportElement] = (0,react.useState)(null);
9073
9015
  const [showWarningMessage, setShowWarningMessage] = (0,react.useState)(true);
@@ -9094,7 +9036,7 @@ function ViewportDownloadFormNew({
9094
9036
  "data-viewport-uid": viewportId,
9095
9037
  ref: setViewportElement
9096
9038
  }, warningState.enabled && showWarningMessage && /*#__PURE__*/react.createElement("div", {
9097
- className: "text-foreground absolute left-1/2 bottom-[5px] z-[1000] -translate-x-1/2 whitespace-nowrap rounded bg-black p-3 text-xs font-bold",
9039
+ className: "text-foreground absolute left-1/2 bottom-[5px] z-[1000] -translate-x-1/2 whitespace-nowrap rounded bg-background p-3 text-xs font-bold",
9098
9040
  style: {
9099
9041
  fontSize: '12px'
9100
9042
  }
@@ -9140,12 +9082,23 @@ function ViewportDownloadFormNew({
9140
9082
  className: "mt-2"
9141
9083
  }, /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu.Right, null, /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu.Secondary, {
9142
9084
  onClick: onClose
9143
- }, t('Common:Cancel')), /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu.Primary, {
9085
+ }, t('Common:Cancel')), /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu.Secondary, {
9086
+ onClick: async () => {
9087
+ try {
9088
+ await onCopyToClipboard();
9089
+ ui_next_src/* toast */.oRP.success(t('Image copied to clipboard'));
9090
+ onClose();
9091
+ } catch (error) {
9092
+ ui_next_src/* toast */.oRP.error(t('Failed to copy image to clipboard'));
9093
+ console.error('Failed to copy to clipboard:', error);
9094
+ }
9095
+ }
9096
+ }, t('Copy to Clipboard')), /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu.Primary, {
9144
9097
  onClick: () => {
9145
9098
  onDownload(filename || DEFAULT_FILENAME, fileType);
9146
9099
  onClose();
9147
9100
  }
9148
- }, t('Common:Save')))))));
9101
+ }, t('Save Image')))))));
9149
9102
  }
9150
9103
  /* harmony default export */ const captureViewportModalCustomization = ({
9151
9104
  'ohif.captureViewportModal': ViewportDownloadFormNew
@@ -9157,6 +9110,34 @@ function ViewportDownloadFormNew({
9157
9110
  value: 'Not For Diagnostic Use'
9158
9111
  }
9159
9112
  });
9113
+ ;// ../../../extensions/cornerstone/src/customizations/viewportScrollbarCustomization.tsx
9114
+ /**
9115
+ * Default customization values for viewport scrollbar behavior.
9116
+ * The `progress` scrollbar variant is in full mode for stack viewports and
9117
+ * acquisition-plane orthographic viewports.
9118
+ * Otherwise, viewports using the `progress` scrollbar show only the indicator.
9119
+ *
9120
+ * - `viewportScrollbar.variant`: `'progress' | 'legacy'` (default: `'progress'`)
9121
+ * - `viewportScrollbar.showLoadedEndpoints`: show loaded-range endpoint caps in full mode
9122
+ * - `viewportScrollbar.showLoadedFill`: show loaded/cached fill in full mode
9123
+ * - `viewportScrollbar.showViewedFill`: show viewed fill in full mode
9124
+ * - `viewportScrollbar.showLoadingPattern`: show loading pattern in full mode while not fully loaded
9125
+ * - `viewportScrollbar.viewedDwellMs`: delay before marking current image viewed in full mode (ms)
9126
+ * - `viewportScrollbar.loadedBatchIntervalMs`: loaded-state version batch interval in full mode (ms)
9127
+ * - `viewportScrollbar.indicator`: optional custom indicator config
9128
+ */
9129
+ function getViewportScrollbarCustomization() {
9130
+ return {
9131
+ 'viewportScrollbar.variant': 'progress',
9132
+ 'viewportScrollbar.showLoadedEndpoints': true,
9133
+ 'viewportScrollbar.showLoadedFill': true,
9134
+ 'viewportScrollbar.showViewedFill': true,
9135
+ 'viewportScrollbar.showLoadingPattern': true,
9136
+ 'viewportScrollbar.viewedDwellMs': 0,
9137
+ 'viewportScrollbar.loadedBatchIntervalMs': 200,
9138
+ 'viewportScrollbar.indicator': {}
9139
+ };
9140
+ }
9160
9141
  ;// ../../../extensions/cornerstone/src/getCustomizationModule.tsx
9161
9142
 
9162
9143
 
@@ -9171,6 +9152,7 @@ function ViewportDownloadFormNew({
9171
9152
 
9172
9153
 
9173
9154
 
9155
+
9174
9156
  function getCustomizationModule({
9175
9157
  commandsManager,
9176
9158
  servicesManager,
@@ -9195,7 +9177,8 @@ function getCustomizationModule({
9195
9177
  ...windowLevelPresetsCustomization,
9196
9178
  ...miscCustomization,
9197
9179
  ...captureViewportModalCustomization,
9198
- ...viewportDownloadWarningCustomization
9180
+ ...viewportDownloadWarningCustomization,
9181
+ ...getViewportScrollbarCustomization()
9199
9182
  }
9200
9183
  }];
9201
9184
  }
@@ -9563,17 +9546,39 @@ const CornerstoneViewportDownloadForm = ({
9563
9546
  } = activeViewportEnabledElement;
9564
9547
  const downloadViewport = renderingEngine.getViewport(VIEWPORT_ID);
9565
9548
  try {
9549
+ // Capture current viewport state
9550
+ // - properties: VOI, colormap, interpolation, etc.
9551
+ // - viewPresentation: flip/rotate/zoom presentation state added for
9552
+ // saving flip and rotation for capture
9553
+ // - viewReference: image/volume reference
9554
+ const properties = viewport.getProperties();
9555
+ const viewPresentation = viewport.getViewPresentation?.();
9556
+ const viewRef = viewport.getViewReference?.();
9566
9557
  if (downloadViewport instanceof esm.StackViewport) {
9567
9558
  const imageId = viewport.getCurrentImageId();
9568
- const properties = viewport.getProperties();
9569
9559
  await downloadViewport.setStack([imageId]);
9570
- downloadViewport.setProperties(properties);
9571
9560
  } else if (downloadViewport instanceof esm.BaseVolumeViewport) {
9572
9561
  const volumeIds = viewport.getAllVolumeIds();
9573
- downloadViewport.setVolumes([{
9562
+ await downloadViewport.setVolumes([{
9574
9563
  volumeId: volumeIds[0]
9575
9564
  }]);
9576
9565
  }
9566
+
9567
+ // Apply presentation state so captured image preserves flip/rotate
9568
+ if (viewPresentation && downloadViewport.setViewPresentation) {
9569
+ downloadViewport.setViewPresentation(viewPresentation);
9570
+ }
9571
+
9572
+ // Apply viewport display properties
9573
+ downloadViewport.setProperties(properties);
9574
+
9575
+ // Ensure correct image/volume reference
9576
+ if (viewRef && downloadViewport.setViewReference) {
9577
+ downloadViewport.setViewReference(viewRef);
9578
+ }
9579
+ downloadViewport.render();
9580
+
9581
+ // Re-apply segmentation overlays to the download viewport
9577
9582
  if (segmentationRepresentations?.length) {
9578
9583
  segmentationRepresentations.forEach(segRepresentation => {
9579
9584
  const {
@@ -9667,6 +9672,36 @@ const CornerstoneViewportDownloadForm = ({
9667
9672
  filename
9668
9673
  });
9669
9674
  };
9675
+ const handleCopyToClipboard = async () => {
9676
+ const divForDownloadViewport = document.querySelector(`div[data-viewport-uid="${VIEWPORT_ID}"]`);
9677
+ if (!divForDownloadViewport) {
9678
+ console.debug('No viewport found for copy');
9679
+ return;
9680
+ }
9681
+ try {
9682
+ const canvas = await (0,html2canvas_esm/* default */.A)(divForDownloadViewport);
9683
+
9684
+ // Clipboard API only supports PNG format in most browsers
9685
+ const blob = await new Promise((resolve, reject) => {
9686
+ canvas.toBlob(blob => {
9687
+ if (blob) {
9688
+ resolve(blob);
9689
+ } else {
9690
+ reject(new Error('Failed to create blob from canvas'));
9691
+ }
9692
+ }, 'image/png', 1.0);
9693
+ });
9694
+
9695
+ // Copy to clipboard using the Clipboard API
9696
+ await navigator.clipboard.write([new ClipboardItem({
9697
+ 'image/png': blob
9698
+ })]);
9699
+ console.log('Image copied to clipboard successfully');
9700
+ } catch (error) {
9701
+ console.error('Failed to copy image to clipboard:', error);
9702
+ throw error;
9703
+ }
9704
+ };
9670
9705
  const ViewportDownloadFormNew = customizationService.getCustomization('ohif.captureViewportModal');
9671
9706
  return /*#__PURE__*/react.createElement(ViewportDownloadFormNew, {
9672
9707
  onClose: hide,
@@ -9680,6 +9715,7 @@ const CornerstoneViewportDownloadForm = ({
9680
9715
  onEnableViewport: handleEnableViewport,
9681
9716
  onDisableViewport: handleDisableViewport,
9682
9717
  onDownload: handleDownload,
9718
+ onCopyToClipboard: handleCopyToClipboard,
9683
9719
  warningState: warningState
9684
9720
  });
9685
9721
  };
@@ -9942,6 +9978,51 @@ function generateSegmentationCSVReport(segmentationData, info) {
9942
9978
  });
9943
9979
  }
9944
9980
  ;// ../../../extensions/cornerstone/src/utils/hydrationUtils.ts
9981
+ /**
9982
+ * After SEG hydration we must refresh every viewport that shows the referenced volume so
9983
+ * presentations (including segmentation) apply to all MPR/3D tiles. Hanging-protocol matching
9984
+ * can return only the active viewport when protocol definitions omit viewportId (e.g. 3D four-up)
9985
+ * or when layout state diverges from the protocol; this merges in all grid panes that already
9986
+ * list that volume in `displaySetInstanceUIDs`.
9987
+ *
9988
+ * Only exact displaySetInstanceUID matches are used (no Frame-of-Reference inference): sibling
9989
+ * MPR planes must already share the same referenced volume UID in grid state, or HP matching
9990
+ * must list them; otherwise forcing a different UID onto a volume viewport can leave it blank.
9991
+ */
9992
+ function mergeVolumeSharingViewports(hangingProtocolUpdates, volumeUid, viewports) {
9993
+ if (!volumeUid) {
9994
+ return hangingProtocolUpdates ?? [];
9995
+ }
9996
+ const byId = new Map();
9997
+ const add = (viewportId, uids) => {
9998
+ if (!viewportId) {
9999
+ return;
10000
+ }
10001
+ byId.set(viewportId, {
10002
+ viewportId,
10003
+ displaySetInstanceUIDs: uids
10004
+ });
10005
+ };
10006
+ if (Array.isArray(hangingProtocolUpdates)) {
10007
+ for (const entry of hangingProtocolUpdates) {
10008
+ const vid = entry.viewportId ?? entry.viewportOptions?.viewportId;
10009
+ if (vid) {
10010
+ add(vid, entry.displaySetInstanceUIDs?.length ? entry.displaySetInstanceUIDs : [volumeUid]);
10011
+ }
10012
+ }
10013
+ }
10014
+ viewports.forEach(vp => {
10015
+ const uids = vp.displaySetInstanceUIDs || [];
10016
+ if (uids.includes(volumeUid)) {
10017
+ add(vp.viewportId, [volumeUid]);
10018
+ }
10019
+ });
10020
+ const merged = Array.from(byId.values());
10021
+ if (merged.length === 0 && Array.isArray(hangingProtocolUpdates) && hangingProtocolUpdates.length > 0) {
10022
+ return hangingProtocolUpdates;
10023
+ }
10024
+ return merged;
10025
+ }
9945
10026
  function getUpdatedViewportsForSegmentation({
9946
10027
  viewportId,
9947
10028
  servicesManager,
@@ -9952,7 +10033,8 @@ function getUpdatedViewportsForSegmentation({
9952
10033
  viewportGridService
9953
10034
  } = servicesManager.services;
9954
10035
  const {
9955
- isHangingProtocolLayout
10036
+ isHangingProtocolLayout,
10037
+ viewports
9956
10038
  } = viewportGridService.getState();
9957
10039
  const viewport = getTargetViewport({
9958
10040
  viewportId,
@@ -9960,7 +10042,10 @@ function getUpdatedViewportsForSegmentation({
9960
10042
  });
9961
10043
  const targetViewportId = viewport.viewportOptions.viewportId;
9962
10044
  const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(targetViewportId, displaySetInstanceUIDs[0], isHangingProtocolLayout);
9963
- return updatedViewports;
10045
+ if (updatedViewports == null || !isHangingProtocolLayout) {
10046
+ return updatedViewports;
10047
+ }
10048
+ return mergeVolumeSharingViewports(updatedViewports, displaySetInstanceUIDs[0], viewports);
9964
10049
  }
9965
10050
  const getTargetViewport = ({
9966
10051
  viewportId,
@@ -10437,7 +10522,7 @@ const {
10437
10522
  copy
10438
10523
  } = dist_esm.utilities.contourSegmentation;
10439
10524
  const {
10440
- DefaultHistoryMemo
10525
+ DefaultHistoryMemo: commandsModule_DefaultHistoryMemo
10441
10526
  } = esm.utilities.HistoryMemo;
10442
10527
  const toggleSyncFunctions = {
10443
10528
  imageSlice: toggleImageSliceSync,
@@ -10520,7 +10605,14 @@ function commandsModule({
10520
10605
  }
10521
10606
  function _getActiveViewportToolGroupId() {
10522
10607
  const viewport = _getActiveViewportEnabledElement();
10523
- return toolGroupService.getToolGroupForViewport(viewport.id);
10608
+ const toolGroup = viewport && toolGroupService.getToolGroupForViewport(viewport.id);
10609
+ return toolGroup?.id;
10610
+ }
10611
+ function _usesPrimaryActivation(bindings) {
10612
+ if (!bindings?.length) {
10613
+ return true;
10614
+ }
10615
+ return bindings.some(binding => binding.mouseButton === dist_esm.Enums.MouseBindings.Primary && binding.modifierKey == null && binding.numTouchPoints == null);
10524
10616
  }
10525
10617
  function _getActiveSegmentationInfo() {
10526
10618
  const viewportId = viewportGridService.getActiveViewportId();
@@ -10756,11 +10848,26 @@ function commandsModule({
10756
10848
  segmentIndex: targetIndex
10757
10849
  } = targetSegmentation;
10758
10850
 
10851
+ // Check if the segment has voxels before computing bidirectional measurement
10852
+ const uniqueSegmentIndices = dist_esm.utilities.segmentation.getUniqueSegmentIndices(targetId);
10853
+ const hasVoxels = uniqueSegmentIndices.includes(targetIndex);
10854
+ if (!hasVoxels) {
10855
+ uiNotificationService.show({
10856
+ title: i18n_src/* default */.A.t('SegmentationPanel:Segment Bidirectional'),
10857
+ message: i18n_src/* default */.A.t('SegmentationPanel:Draw a segment before using bidirectional measurement'),
10858
+ type: 'warning'
10859
+ });
10860
+ return;
10861
+ }
10862
+
10759
10863
  // Get bidirectional measurement data
10760
10864
  const bidirectionalData = await dist_esm.utilities.segmentation.getSegmentLargestBidirectional({
10761
10865
  segmentationId: targetId,
10762
10866
  segmentIndices: [targetIndex]
10763
10867
  });
10868
+ if (!bidirectionalData.length) {
10869
+ return;
10870
+ }
10764
10871
  const activeViewportId = viewportGridService.getActiveViewportId();
10765
10872
 
10766
10873
  // Process each bidirectional measurement
@@ -10799,7 +10906,7 @@ function commandsModule({
10799
10906
  // get the active segmentIndex bidirectional annotation and jump to it
10800
10907
  const activeBidirectional = bidirectionalData.find(measurement => measurement.segmentIndex === targetIndex);
10801
10908
  commandsManager.run('jumpToMeasurement', {
10802
- uid: activeBidirectional.annotationUID
10909
+ uid: activeBidirectional?.annotationUID
10803
10910
  });
10804
10911
  },
10805
10912
  interpolateLabelmap: () => {
@@ -10962,7 +11069,7 @@ function commandsModule({
10962
11069
  return;
10963
11070
  }
10964
11071
  if (!labelConfig) {
10965
- const label = await (0,default_src.callInputDialog)({
11072
+ const label = await (0,default_src/* callInputDialog */.l5)({
10966
11073
  uiDialogService,
10967
11074
  title: i18n_src/* default */.A.t('Tools:Edit Measurement Label'),
10968
11075
  placeholder: measurement.label || i18n_src/* default */.A.t('Tools:Enter new label'),
@@ -10976,7 +11083,7 @@ function commandsModule({
10976
11083
  }
10977
11084
  return;
10978
11085
  }
10979
- const val = await (0,default_src.callInputDialogAutoComplete)({
11086
+ const val = await (0,default_src/* callInputDialogAutoComplete */.fq)({
10980
11087
  measurement,
10981
11088
  uiDialogService,
10982
11089
  labelConfig,
@@ -11099,6 +11206,9 @@ function commandsModule({
11099
11206
  uid,
11100
11207
  displayMeasurements = []
11101
11208
  }) => {
11209
+ if (!uid) {
11210
+ return;
11211
+ }
11102
11212
  measurementService.jumpToMeasurement(viewportGridService.getActiveViewportId(), uid);
11103
11213
  for (const measurement of displayMeasurements) {
11104
11214
  measurement.isActive = measurement.uid === uid;
@@ -11196,7 +11306,7 @@ function commandsModule({
11196
11306
  const labelConfig = customizationService.getCustomization('measurementLabels');
11197
11307
  const renderContent = customizationService.getCustomization('ui.labellingComponent');
11198
11308
  if (!labelConfig) {
11199
- const label = await (0,default_src.callInputDialog)({
11309
+ const label = await (0,default_src/* callInputDialog */.l5)({
11200
11310
  uiDialogService,
11201
11311
  title: i18n_src/* default */.A.t('Tools:Edit Arrow Text'),
11202
11312
  placeholder: data?.data?.label || i18n_src/* default */.A.t('Tools:Enter new text'),
@@ -11205,7 +11315,7 @@ function commandsModule({
11205
11315
  callback?.(label);
11206
11316
  return;
11207
11317
  }
11208
- const value = await (0,default_src.callInputDialogAutoComplete)({
11318
+ const value = await (0,default_src/* callInputDialogAutoComplete */.fq)({
11209
11319
  uiDialogService,
11210
11320
  labelConfig,
11211
11321
  renderContent
@@ -11369,31 +11479,46 @@ function commandsModule({
11369
11479
  toggleActiveDisabledToolbar({
11370
11480
  value,
11371
11481
  itemId,
11372
- toolGroupId
11482
+ toolGroupId,
11483
+ toolGroupIds
11373
11484
  }) {
11374
11485
  const toolName = itemId || value;
11375
- toolGroupId = toolGroupId ?? _getActiveViewportToolGroupId();
11376
- const toolGroup = toolGroupService.getToolGroup(toolGroupId);
11377
- if (!toolGroup || !toolGroup.hasTool(toolName)) {
11378
- return;
11379
- }
11380
- const toolIsActive = [dist_esm.Enums.ToolModes.Active, dist_esm.Enums.ToolModes.Enabled, dist_esm.Enums.ToolModes.Passive].includes(toolGroup.getToolOptions(toolName).mode);
11381
- toolIsActive ? toolGroup.setToolDisabled(toolName) : actions.setToolActive({
11382
- toolName,
11383
- toolGroupId
11384
- });
11385
-
11386
- // we should set the previously active tool to active after we set the
11387
- // current tool disabled
11388
- if (toolIsActive) {
11389
- const prevToolName = toolGroup.getPrevActivePrimaryToolName();
11390
- if (prevToolName !== toolName) {
11486
+ const resolvedToolGroupIds = toolGroupIds?.length ? toolGroupIds : [toolGroupId ?? _getActiveViewportToolGroupId()];
11487
+ resolvedToolGroupIds.forEach(toolGroupId => {
11488
+ const toolGroup = toolGroupService.getToolGroup(toolGroupId);
11489
+ if (!toolGroup || !toolGroup.hasTool(toolName)) {
11490
+ return;
11491
+ }
11492
+ const toolIsActive = [dist_esm.Enums.ToolModes.Active, dist_esm.Enums.ToolModes.Enabled, dist_esm.Enums.ToolModes.Passive].includes(toolGroup.getToolOptions(toolName).mode);
11493
+ if (toolIsActive) {
11494
+ toolGroup.setToolDisabled(toolName);
11495
+ const bindings = toolGroupService.getToolBindings(toolGroupId, toolName);
11496
+ if (_usesPrimaryActivation(bindings)) {
11497
+ // we should set the previously active tool to active after we set the
11498
+ // current tool disabled
11499
+ const prevToolName = toolGroup.getPrevActivePrimaryToolName();
11500
+ if (prevToolName !== toolName) {
11501
+ actions.setToolActive({
11502
+ toolName: prevToolName,
11503
+ toolGroupId
11504
+ });
11505
+ }
11506
+ }
11507
+ return;
11508
+ }
11509
+ const bindings = toolGroupService.getToolBindings(toolGroupId, toolName);
11510
+ if (_usesPrimaryActivation(bindings)) {
11391
11511
  actions.setToolActive({
11392
- toolName: prevToolName,
11393
- toolGroupId
11512
+ toolName,
11513
+ toolGroupId,
11514
+ bindings
11515
+ });
11516
+ } else {
11517
+ toolGroup.setToolActive(toolName, {
11518
+ bindings
11394
11519
  });
11395
11520
  }
11396
- }
11521
+ });
11397
11522
  },
11398
11523
  setToolActiveToolbar: ({
11399
11524
  value,
@@ -12101,7 +12226,7 @@ function commandsModule({
12101
12226
  segmentationService,
12102
12227
  viewportGridService
12103
12228
  } = servicesManager.services;
12104
- const displaySetInstanceUIDs = await (0,default_src.createReportAsync)({
12229
+ const displaySetInstanceUIDs = await (0,default_src/* createReportAsync */.Vy)({
12105
12230
  servicesManager,
12106
12231
  getReport: () => commandsManager.runCommand('storeSegmentation', args),
12107
12232
  reportType: 'Segmentation'
@@ -12178,14 +12303,15 @@ function commandsModule({
12178
12303
  * @param props.segmentationId - The ID of the segmentation to remove
12179
12304
  */
12180
12305
  removeSegmentationFromViewportCommand: ({
12181
- segmentationId
12306
+ segmentationId: displaySetInstanceUID
12182
12307
  }) => {
12183
12308
  const {
12184
- segmentationService,
12185
12309
  viewportGridService
12186
12310
  } = servicesManager.services;
12187
- segmentationService.removeSegmentationRepresentations(viewportGridService.getActiveViewportId(), {
12188
- segmentationId
12311
+ const viewportId = viewportGridService.getActiveViewportId();
12312
+ commandsManager.runCommand('removeDisplaySetLayer', {
12313
+ viewportId,
12314
+ displaySetInstanceUID
12189
12315
  });
12190
12316
  },
12191
12317
  /**
@@ -12213,7 +12339,7 @@ function commandsModule({
12213
12339
  return;
12214
12340
  }
12215
12341
  const segment = segmentation.segments[segmentIndex];
12216
- (0,default_src.callInputDialog)({
12342
+ (0,default_src/* callInputDialog */.l5)({
12217
12343
  uiDialogService,
12218
12344
  title: i18n_src/* default */.A.t('Tools:Edit Segment Label'),
12219
12345
  placeholder: i18n_src/* default */.A.t('Tools:Enter new label'),
@@ -12236,7 +12362,7 @@ function commandsModule({
12236
12362
  const {
12237
12363
  label
12238
12364
  } = segmentation;
12239
- (0,default_src.callInputDialog)({
12365
+ (0,default_src/* callInputDialog */.l5)({
12240
12366
  uiDialogService,
12241
12367
  title: i18n_src/* default */.A.t('Tools:Edit Segmentation Label'),
12242
12368
  placeholder: i18n_src/* default */.A.t('Tools:Enter new label'),
@@ -12266,7 +12392,7 @@ function commandsModule({
12266
12392
  a: color[3] / 255.0
12267
12393
  };
12268
12394
  uiDialogService.show({
12269
- content: default_src.colorPickerDialog,
12395
+ content: default_src/* colorPickerDialog */.wS,
12270
12396
  title: i18n_src/* default */.A.t('Tools:Segment Color'),
12271
12397
  contentProps: {
12272
12398
  value: rgbaColor,
@@ -12311,10 +12437,10 @@ function commandsModule({
12311
12437
  viewportGridService.setDisplaySetsForViewports(viewportsToUpdate);
12312
12438
  },
12313
12439
  undo: () => {
12314
- DefaultHistoryMemo.undo();
12440
+ commandsModule_DefaultHistoryMemo.undo();
12315
12441
  },
12316
12442
  redo: () => {
12317
- DefaultHistoryMemo.redo();
12443
+ commandsModule_DefaultHistoryMemo.redo();
12318
12444
  },
12319
12445
  toggleSegmentPreviewEdit: ({
12320
12446
  toggle
@@ -12545,6 +12671,15 @@ function commandsModule({
12545
12671
  servicesManager,
12546
12672
  displaySetInstanceUIDs
12547
12673
  });
12674
+ if (!updatedViewports?.length) {
12675
+ return;
12676
+ }
12677
+ updatedViewports.forEach(({
12678
+ viewportId: csViewportId
12679
+ }) => {
12680
+ const csViewport = cornerstoneViewportService.getCornerstoneViewport(csViewportId);
12681
+ csViewport?.setNeedsRender?.();
12682
+ });
12548
12683
  actions.setDisplaySetsForViewports({
12549
12684
  viewportsToUpdate: updatedViewports.map(viewport => ({
12550
12685
  viewportId: viewport.viewportId,
@@ -13256,6 +13391,29 @@ const HYDRATE_SEG_SYNC_GROUP = {
13256
13391
  matchingRules: ['sameFOR']
13257
13392
  }
13258
13393
  };
13394
+ const viewportStructure = {
13395
+ layoutType: 'grid',
13396
+ properties: {
13397
+ rows: 1,
13398
+ columns: 3,
13399
+ layoutOptions: [{
13400
+ x: 0,
13401
+ y: 0,
13402
+ width: 1 / 3,
13403
+ height: 1
13404
+ }, {
13405
+ x: 1 / 3,
13406
+ y: 0,
13407
+ width: 1 / 3,
13408
+ height: 1
13409
+ }, {
13410
+ x: 2 / 3,
13411
+ y: 0,
13412
+ width: 1 / 3,
13413
+ height: 1
13414
+ }]
13415
+ }
13416
+ };
13259
13417
  const mpr = {
13260
13418
  id: 'mpr',
13261
13419
  name: i18next/* default */.A.t('Hps:MPR'),
@@ -13286,29 +13444,7 @@ const mpr = {
13286
13444
  },
13287
13445
  stages: [{
13288
13446
  name: 'MPR 1x3',
13289
- viewportStructure: {
13290
- layoutType: 'grid',
13291
- properties: {
13292
- rows: 1,
13293
- columns: 3,
13294
- layoutOptions: [{
13295
- x: 0,
13296
- y: 0,
13297
- width: 1 / 3,
13298
- height: 1
13299
- }, {
13300
- x: 1 / 3,
13301
- y: 0,
13302
- width: 1 / 3,
13303
- height: 1
13304
- }, {
13305
- x: 2 / 3,
13306
- y: 0,
13307
- width: 1 / 3,
13308
- height: 1
13309
- }]
13310
- }
13311
- },
13447
+ viewportStructure,
13312
13448
  viewports: [{
13313
13449
  viewportOptions: {
13314
13450
  viewportId: 'mpr-axial',
@@ -13352,21 +13488,67 @@ const mpr = {
13352
13488
  id: 'activeDisplaySet'
13353
13489
  }]
13354
13490
  }]
13355
- }]
13356
- };
13357
- ;// ../../../extensions/cornerstone/src/hps/fourUp.ts
13358
-
13359
-
13360
- const fourUp = {
13361
- id: 'fourUp',
13362
- locked: true,
13363
- name: i18next/* default */.A.t('Hps:3D four up'),
13364
- icon: 'layout-advanced-3d-four-up',
13365
- isPreset: true,
13366
- createdDate: '2023-03-15T10:29:44.894Z',
13367
- modifiedDate: '2023-03-15T10:29:44.894Z',
13368
- availableTo: {},
13369
- editableBy: {},
13491
+ }, {
13492
+ name: 'MPR Reformat 1x3',
13493
+ viewportStructure,
13494
+ viewports: [{
13495
+ viewportOptions: {
13496
+ viewportId: 'mpr-axial',
13497
+ toolGroupId: 'mpr',
13498
+ viewportType: 'volume',
13499
+ orientation: 'axial_reformat',
13500
+ initialImageOptions: {
13501
+ preset: 'middle'
13502
+ },
13503
+ syncGroups: [VOI_SYNC_GROUP, HYDRATE_SEG_SYNC_GROUP]
13504
+ },
13505
+ displaySets: [{
13506
+ id: 'activeDisplaySet'
13507
+ }]
13508
+ }, {
13509
+ viewportOptions: {
13510
+ viewportId: 'mpr-sagittal',
13511
+ toolGroupId: 'mpr',
13512
+ viewportType: 'volume',
13513
+ orientation: 'sagittal_reformat',
13514
+ initialImageOptions: {
13515
+ preset: 'middle'
13516
+ },
13517
+ syncGroups: [VOI_SYNC_GROUP, HYDRATE_SEG_SYNC_GROUP]
13518
+ },
13519
+ displaySets: [{
13520
+ id: 'activeDisplaySet'
13521
+ }]
13522
+ }, {
13523
+ viewportOptions: {
13524
+ viewportId: 'mpr-coronal',
13525
+ toolGroupId: 'mpr',
13526
+ viewportType: 'volume',
13527
+ orientation: 'coronal_reformat',
13528
+ initialImageOptions: {
13529
+ preset: 'middle'
13530
+ },
13531
+ syncGroups: [VOI_SYNC_GROUP, HYDRATE_SEG_SYNC_GROUP]
13532
+ },
13533
+ displaySets: [{
13534
+ id: 'activeDisplaySet'
13535
+ }]
13536
+ }]
13537
+ }]
13538
+ };
13539
+ ;// ../../../extensions/cornerstone/src/hps/fourUp.ts
13540
+
13541
+
13542
+ const fourUp = {
13543
+ id: 'fourUp',
13544
+ locked: true,
13545
+ name: i18next/* default */.A.t('Hps:3D four up'),
13546
+ icon: 'layout-advanced-3d-four-up',
13547
+ isPreset: true,
13548
+ createdDate: '2023-03-15T10:29:44.894Z',
13549
+ modifiedDate: '2023-03-15T10:29:44.894Z',
13550
+ availableTo: {},
13551
+ editableBy: {},
13370
13552
  protocolMatchingRules: [],
13371
13553
  imageLoadStrategy: 'interleaveCenter',
13372
13554
  displaySetSelectors: {
@@ -15342,6 +15524,9 @@ function ViewportDataOverlayMenu({
15342
15524
  } = (0,useViewportDisplaySets/* useViewportDisplaySets */.N)(viewportId);
15343
15525
  const [optimisticOverlayDisplaySets, setOptimisticOverlayDisplaySets] = (0,react.useState)(overlayDisplaySets);
15344
15526
  const [thresholdOpacityEnabled, setThresholdOpacityEnabled] = (0,react.useState)(false);
15527
+ (0,react.useEffect)(() => {
15528
+ setOptimisticOverlayDisplaySets(overlayDisplaySets);
15529
+ }, [backgroundDisplaySet?.displaySetInstanceUID, overlayDisplaySets]);
15345
15530
 
15346
15531
  /**
15347
15532
  * Change the background display set
@@ -15648,7 +15833,8 @@ function ViewportDataOverlayMenu({
15648
15833
  }
15649
15834
  }
15650
15835
  }, /*#__PURE__*/react.createElement(ui_next_src/* SelectTrigger */.bqE, {
15651
- className: "flex-1"
15836
+ className: "flex-1",
15837
+ "data-cy": `overlay-background-ds-select-${viewportId}`
15652
15838
  }, /*#__PURE__*/react.createElement(ui_next_src/* SelectValue */.yvm, null, (backgroundDisplaySet?.SeriesDescription || backgroundDisplaySet?.label || 'background').toUpperCase())), /*#__PURE__*/react.createElement(ui_next_src/* SelectContent */.gCo, null, potentialBackgroundDisplaySets.map(displaySet => /*#__PURE__*/react.createElement(ui_next_src/* SelectItem */.ebT, {
15653
15839
  key: displaySet.displaySetInstanceUID,
15654
15840
  value: displaySet.displaySetInstanceUID,
@@ -15923,7 +16109,7 @@ function ViewportOrientationMenu({
15923
16109
  }) : null), /*#__PURE__*/react.createElement("div", {
15924
16110
  className: "flex-1 text-left"
15925
16111
  }, "Acquisition")), /*#__PURE__*/react.createElement("div", {
15926
- className: "mx-1 my-2 border-t border-white/20"
16112
+ className: "border-input mx-1 my-2 border-t"
15927
16113
  }), /*#__PURE__*/react.createElement(ui_next_src/* Button */.$nd, {
15928
16114
  variant: "ghost",
15929
16115
  className: "flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0",
@@ -16264,7 +16450,7 @@ function VolumeRenderingPresetsContent({
16264
16450
  }
16265
16451
  }, /*#__PURE__*/react.createElement(ui_next_src/* Icons */.FI1.ByName, {
16266
16452
  name: preset.name,
16267
- className: selectedPreset?.name === preset.name ? 'border-highlight h-[75px] w-[95px] max-w-none rounded border-2' : 'hover:border-highlight h-[75px] w-[95px] max-w-none rounded border-2 border-black'
16453
+ className: selectedPreset?.name === preset.name ? 'border-highlight h-[75px] w-[95px] max-w-none rounded border-2' : 'hover:border-highlight h-[75px] w-[95px] max-w-none rounded border-2 border-background'
16268
16454
  }), /*#__PURE__*/react.createElement("label", {
16269
16455
  className: "text-muted-foreground mt-1 text-left text-xs"
16270
16456
  }, formatLabel(preset.name, 11)))))), /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu, {
@@ -17410,9 +17596,9 @@ function ModalityLoadBadge({
17410
17596
  if (!statusInfo.isHydrated) {
17411
17597
  return /*#__PURE__*/react.createElement("div", {
17412
17598
  "data-cy": `ModalityLoadBadge-${viewportId}`,
17413
- className: "flex h-6 cursor-default text-sm leading-6 text-white"
17599
+ className: "text-foreground flex h-6 cursor-default text-sm leading-6"
17414
17600
  }, /*#__PURE__*/react.createElement("div", {
17415
- className: "bg-customgray-100 flex min-w-[45px] items-center rounded-l-xl rounded-r p-1"
17601
+ className: "bg-popover flex min-w-[45px] items-center rounded-l-xl rounded-r p-1"
17416
17602
  }, /*#__PURE__*/react.createElement(StatusIcon, null), /*#__PURE__*/react.createElement("span", {
17417
17603
  className: "ml-1"
17418
17604
  }, statusInfo.type)), statusInfo.type !== 'SR' && /*#__PURE__*/react.createElement(ui_next_src/* ViewportActionButton */.N8H, {
@@ -18339,6 +18525,36 @@ function getToolbarModule({
18339
18525
  isActive: isPrimaryActive
18340
18526
  };
18341
18527
  }
18528
+ }, {
18529
+ name: 'evaluate.cornerstoneTool.toggleWithModifier',
18530
+ evaluate: ({
18531
+ viewportId,
18532
+ button,
18533
+ disabledText,
18534
+ toggledOnIcon,
18535
+ defaultIcon
18536
+ }) => {
18537
+ const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
18538
+ if (!toolGroup) {
18539
+ return;
18540
+ }
18541
+ const toolName = toolbarService.getToolNameForButton(button);
18542
+ if (!toolGroup.hasTool(toolName)) {
18543
+ return getDisabledState(disabledText);
18544
+ }
18545
+ const {
18546
+ mode
18547
+ } = toolGroup.getToolOptions(toolName) ?? {};
18548
+ const isToggled = mode === dist_esm.Enums.ToolModes.Passive || mode === dist_esm.Enums.ToolModes.Active || mode === dist_esm.Enums.ToolModes.Enabled;
18549
+ const toolBindings = toolGroupService.getToolBindings(toolGroup.id, toolName);
18550
+ const hasModifierKey = toolBindings?.some(binding => binding.modifierKey != null) ?? false;
18551
+ return {
18552
+ disabled: false,
18553
+ isActive: false,
18554
+ isToggled,
18555
+ icon: isToggled && hasModifierKey && toggledOnIcon ? toggledOnIcon : defaultIcon ?? button.props.icon
18556
+ };
18557
+ }
18342
18558
  }, {
18343
18559
  name: 'evaluate.action',
18344
18560
  evaluate: () => {
@@ -18491,7 +18707,10 @@ class ToolGroupService {
18491
18707
  this.cornerstoneViewportService = void 0;
18492
18708
  this.viewportGridService = void 0;
18493
18709
  this.uiNotificationService = void 0;
18710
+ this.customizationService = void 0;
18494
18711
  this.toolGroupIds = new Set();
18712
+ this.toolBindingsMap = new Map();
18713
+ this.defaultToolBindingsMap = new Map();
18495
18714
  /**
18496
18715
  * Service-specific
18497
18716
  */
@@ -18517,11 +18736,13 @@ class ToolGroupService {
18517
18736
  const {
18518
18737
  cornerstoneViewportService,
18519
18738
  viewportGridService,
18520
- uiNotificationService
18739
+ uiNotificationService,
18740
+ customizationService
18521
18741
  } = servicesManager.services;
18522
18742
  this.cornerstoneViewportService = cornerstoneViewportService;
18523
18743
  this.viewportGridService = viewportGridService;
18524
18744
  this.uiNotificationService = uiNotificationService;
18745
+ this.customizationService = customizationService;
18525
18746
  this.listeners = {};
18526
18747
  this.EVENTS = ToolGroupService_EVENTS;
18527
18748
  Object.assign(this, src/* pubSubServiceInterface */.Ml);
@@ -18579,6 +18800,8 @@ class ToolGroupService {
18579
18800
  destroy() {
18580
18801
  dist_esm.ToolGroupManager.destroy();
18581
18802
  this.toolGroupIds = new Set();
18803
+ this.toolBindingsMap.clear();
18804
+ this.defaultToolBindingsMap.clear();
18582
18805
  esm.eventTarget.removeEventListener(dist_esm.Enums.Events.TOOL_ACTIVATED, this._onToolActivated);
18583
18806
  }
18584
18807
  destroyToolGroup(toolGroupId) {
@@ -18633,6 +18856,7 @@ class ToolGroupService {
18633
18856
  // this.changeConfigurationIfNecessary(toolGroup, volumeId);
18634
18857
  this._addTools(toolGroup, tools, configs);
18635
18858
  this._setToolsMode(toolGroup, tools);
18859
+ this._loadPersistedBindings(toolGroupId);
18636
18860
  }
18637
18861
  createToolGroupAndAddTools(toolGroupId, tools) {
18638
18862
  const toolGroup = this.createToolGroup(toolGroupId);
@@ -18672,6 +18896,84 @@ class ToolGroupService {
18672
18896
  getActivePrimaryMouseButtonTool(toolGroupId) {
18673
18897
  return this.getToolGroup(toolGroupId)?.getActivePrimaryMouseButtonTool();
18674
18898
  }
18899
+ getToolBindings(toolGroupId, toolName) {
18900
+ return this.toolBindingsMap.get(toolGroupId)?.get(toolName);
18901
+ }
18902
+ setToolBindings(toolGroupId, toolName, bindings) {
18903
+ if (!this.toolBindingsMap.has(toolGroupId)) {
18904
+ this.toolBindingsMap.set(toolGroupId, new Map());
18905
+ }
18906
+ this.toolBindingsMap.get(toolGroupId).set(toolName, this._cloneToolBindings(bindings));
18907
+ }
18908
+ getDefaultToolBindings(toolGroupId, toolName) {
18909
+ const defaultBindings = this.defaultToolBindingsMap.get(toolGroupId)?.get(toolName);
18910
+ return defaultBindings ? this._cloneToolBindings(defaultBindings) : undefined;
18911
+ }
18912
+ persistToolBindings(toolGroupId, toolName, bindings) {
18913
+ const persistedBindings = this._readPersistedToolBindings();
18914
+ if (!persistedBindings[toolGroupId]) {
18915
+ persistedBindings[toolGroupId] = {};
18916
+ }
18917
+ persistedBindings[toolGroupId][toolName] = bindings;
18918
+ this._writePersistedToolBindings(persistedBindings);
18919
+ }
18920
+ removePersistedToolBindings(toolGroupId, toolName) {
18921
+ const persistedBindings = this._readPersistedToolBindings();
18922
+ if (!persistedBindings[toolGroupId]) {
18923
+ return;
18924
+ }
18925
+ if (toolName) {
18926
+ delete persistedBindings[toolGroupId][toolName];
18927
+ if (!Object.keys(persistedBindings[toolGroupId]).length) {
18928
+ delete persistedBindings[toolGroupId];
18929
+ }
18930
+ } else {
18931
+ delete persistedBindings[toolGroupId];
18932
+ }
18933
+ this._writePersistedToolBindings(persistedBindings);
18934
+ }
18935
+
18936
+ /**
18937
+ * Applies the currently tracked bindings to the runtime tool instance.
18938
+ *
18939
+ * Note: this method may activate tools that are currently Passive or Enabled.
18940
+ * Assigning bindings is treated as making the tool interactable.
18941
+ */
18942
+ applyToolBindings(toolGroupId, toolName, options = {}) {
18943
+ const toolGroup = dist_esm.ToolGroupManager.getToolGroup(toolGroupId);
18944
+ if (!toolGroup || !toolGroup.hasTool(toolName)) {
18945
+ return;
18946
+ }
18947
+ const bindings = this.getToolBindings(toolGroupId, toolName);
18948
+ if (!bindings) {
18949
+ return;
18950
+ }
18951
+ const {
18952
+ mode
18953
+ } = toolGroup.getToolOptions(toolName);
18954
+ if (mode === dist_esm.Enums.ToolModes.Active || mode === dist_esm.Enums.ToolModes.Passive || mode === dist_esm.Enums.ToolModes.Enabled) {
18955
+ if (options.replaceExisting) {
18956
+ // Opt-in behavior for callers that need replacement semantics.
18957
+ toolGroup.setToolDisabled(toolName);
18958
+ }
18959
+ toolGroup.setToolActive(toolName, {
18960
+ bindings
18961
+ });
18962
+ }
18963
+ }
18964
+ getAllToolBindings() {
18965
+ const result = [];
18966
+ for (const [toolGroupId, toolMap] of this.toolBindingsMap) {
18967
+ for (const [toolName, bindings] of toolMap) {
18968
+ result.push({
18969
+ toolGroupId,
18970
+ toolName,
18971
+ bindings
18972
+ });
18973
+ }
18974
+ }
18975
+ return result;
18976
+ }
18675
18977
  _setToolsMode(toolGroup, tools) {
18676
18978
  const {
18677
18979
  active,
@@ -18684,6 +18986,10 @@ class ToolGroupService {
18684
18986
  toolName,
18685
18987
  bindings
18686
18988
  }) => {
18989
+ if (bindings) {
18990
+ this.setToolBindings(toolGroup.id, toolName, bindings);
18991
+ this._setDefaultToolBindingsIfMissing(toolGroup.id, toolName, bindings);
18992
+ }
18687
18993
  toolGroup.setToolActive(toolName, {
18688
18994
  bindings
18689
18995
  });
@@ -18691,26 +18997,50 @@ class ToolGroupService {
18691
18997
  }
18692
18998
  if (passive) {
18693
18999
  passive.forEach(({
18694
- toolName
19000
+ toolName,
19001
+ bindings
18695
19002
  }) => {
19003
+ if (bindings) {
19004
+ this.setToolBindings(toolGroup.id, toolName, bindings);
19005
+ this._setDefaultToolBindingsIfMissing(toolGroup.id, toolName, bindings);
19006
+ }
18696
19007
  toolGroup.setToolPassive(toolName);
18697
19008
  });
18698
19009
  }
18699
19010
  if (enabled) {
18700
19011
  enabled.forEach(({
18701
- toolName
19012
+ toolName,
19013
+ bindings
18702
19014
  }) => {
19015
+ if (bindings) {
19016
+ this.setToolBindings(toolGroup.id, toolName, bindings);
19017
+ this._setDefaultToolBindingsIfMissing(toolGroup.id, toolName, bindings);
19018
+ }
18703
19019
  toolGroup.setToolEnabled(toolName);
18704
19020
  });
18705
19021
  }
18706
19022
  if (disabled) {
18707
19023
  disabled.forEach(({
18708
- toolName
19024
+ toolName,
19025
+ bindings
18709
19026
  }) => {
19027
+ if (bindings) {
19028
+ this.setToolBindings(toolGroup.id, toolName, bindings);
19029
+ this._setDefaultToolBindingsIfMissing(toolGroup.id, toolName, bindings);
19030
+ }
18710
19031
  toolGroup.setToolDisabled(toolName);
18711
19032
  });
18712
19033
  }
18713
19034
  }
19035
+ _setDefaultToolBindingsIfMissing(toolGroupId, toolName, bindings) {
19036
+ if (!this.defaultToolBindingsMap.has(toolGroupId)) {
19037
+ this.defaultToolBindingsMap.set(toolGroupId, new Map());
19038
+ }
19039
+ const toolMap = this.defaultToolBindingsMap.get(toolGroupId);
19040
+ if (!toolMap.has(toolName)) {
19041
+ toolMap.set(toolName, this._cloneToolBindings(bindings));
19042
+ }
19043
+ }
18714
19044
  _addTools(toolGroup, tools) {
18715
19045
  const addTools = tools => {
18716
19046
  tools.forEach(({
@@ -18742,6 +19072,60 @@ class ToolGroupService {
18742
19072
  addTools(tools.disabled);
18743
19073
  }
18744
19074
  }
19075
+ _loadPersistedBindings(toolGroupId) {
19076
+ const toolGroupBindings = this._readPersistedToolBindings()[toolGroupId];
19077
+ if (!toolGroupBindings) {
19078
+ return;
19079
+ }
19080
+ const toolGroup = dist_esm.ToolGroupManager.getToolGroup(toolGroupId);
19081
+ for (const [toolName, bindings] of Object.entries(toolGroupBindings)) {
19082
+ this.setToolBindings(toolGroupId, toolName, bindings);
19083
+ if (!toolGroup || !toolGroup.hasTool(toolName)) {
19084
+ continue;
19085
+ }
19086
+ const {
19087
+ mode
19088
+ } = toolGroup.getToolOptions(toolName);
19089
+ if (mode === dist_esm.Enums.ToolModes.Active) {
19090
+ this.applyToolBindings(toolGroupId, toolName, {
19091
+ replaceExisting: true
19092
+ });
19093
+ }
19094
+ }
19095
+ }
19096
+ _readPersistedToolBindings() {
19097
+ try {
19098
+ const stored = localStorage.getItem(this._getToolBindingsStorageKey());
19099
+ if (!stored) {
19100
+ return {};
19101
+ }
19102
+ const parsed = JSON.parse(stored);
19103
+ if (!parsed || typeof parsed !== 'object') {
19104
+ return {};
19105
+ }
19106
+ return parsed;
19107
+ } catch {
19108
+ // ignore corrupt localStorage
19109
+ return {};
19110
+ }
19111
+ }
19112
+ _writePersistedToolBindings(bindings) {
19113
+ const storageKey = this._getToolBindingsStorageKey();
19114
+ if (!Object.keys(bindings).length) {
19115
+ localStorage.removeItem(storageKey);
19116
+ return;
19117
+ }
19118
+ localStorage.setItem(storageKey, JSON.stringify(bindings));
19119
+ }
19120
+ _cloneToolBindings(bindings) {
19121
+ return bindings.map(binding => ({
19122
+ ...binding
19123
+ }));
19124
+ }
19125
+ _getToolBindingsStorageKey() {
19126
+ const customizationValue = this.customizationService?.getCustomization('ohif.userPreferences.toolBindingsStorageKey');
19127
+ return typeof customizationValue === 'string' && customizationValue.length > 0 ? customizationValue : 'user-preferred-tool-bindings';
19128
+ }
18745
19129
  }
18746
19130
  _ToolGroupService = ToolGroupService;
18747
19131
  ToolGroupService.REGISTRATION = {
@@ -18829,7 +19213,12 @@ const segmentationRepresentationModifiedCallback = async (synchronizerInstance,
18829
19213
  const sourceDisplaySetUIDs = extractDisplaySetUIDs(sourceViewportInfo);
18830
19214
  const targetDisplaySetUIDs = extractDisplaySetUIDs(targetViewportInfo);
18831
19215
  const sharedDisplaySetExists = isAnyDisplaySetCommon(sourceDisplaySetUIDs, targetDisplaySetUIDs);
18832
- if (!sharedDisplaySetExists && !viewport.getFrameOfReferenceUID()) {
19216
+ const targetFrameOfReferenceUID = viewport.getFrameOfReferenceUID();
19217
+ const sourceFrameOfReferenceUID = (0,esm.getEnabledElementByViewportId)(sourceViewportId)?.viewport?.getFrameOfReferenceUID();
19218
+ if (!sharedDisplaySetExists && !targetFrameOfReferenceUID) {
19219
+ return;
19220
+ }
19221
+ if (!sharedDisplaySetExists && targetFrameOfReferenceUID !== sourceFrameOfReferenceUID) {
18833
19222
  return;
18834
19223
  }
18835
19224
  const targetViewportRepresentation = segmentationService.getSegmentationRepresentations(targetViewportId, {
@@ -19196,11 +19585,11 @@ class SegmentationService extends src/* PubSubService */.Rc {
19196
19585
  this.EVENTS = SegmentationService_EVENTS;
19197
19586
  this.destroy = () => {
19198
19587
  esm.eventTarget.removeEventListener(dist_esm.Enums.Events.SEGMENTATION_MODIFIED, this._onSegmentationModifiedFromSource);
19199
- esm.eventTarget.removeEventListener(dist_esm.Enums.Events.SEGMENTATION_REMOVED, this._onSegmentationModifiedFromSource);
19588
+ esm.eventTarget.removeEventListener(dist_esm.Enums.Events.SEGMENTATION_REMOVED, this._onSegmentationRemovedFromSource);
19200
19589
  esm.eventTarget.removeEventListener(dist_esm.Enums.Events.SEGMENTATION_DATA_MODIFIED, this._onSegmentationDataModifiedFromSource);
19201
19590
  esm.eventTarget.removeEventListener(dist_esm.Enums.Events.SEGMENTATION_REPRESENTATION_MODIFIED, this._onSegmentationRepresentationModifiedFromSource);
19202
19591
  esm.eventTarget.removeEventListener(dist_esm.Enums.Events.SEGMENTATION_REPRESENTATION_ADDED, this._onSegmentationRepresentationModifiedFromSource);
19203
- esm.eventTarget.removeEventListener(dist_esm.Enums.Events.SEGMENTATION_REPRESENTATION_REMOVED, this._onSegmentationRepresentationModifiedFromSource);
19592
+ esm.eventTarget.removeEventListener(dist_esm.Enums.Events.SEGMENTATION_REPRESENTATION_REMOVED, this._onSegmentationRepresentationRemovedFromSource);
19204
19593
  esm.eventTarget.removeEventListener(dist_esm.Enums.Events.SEGMENTATION_ADDED, this._onSegmentationAddedFromSource);
19205
19594
  this.reset();
19206
19595
  };
@@ -19261,6 +19650,16 @@ class SegmentationService extends src/* PubSubService */.Rc {
19261
19650
  viewportId
19262
19651
  });
19263
19652
  };
19653
+ this._onSegmentationRepresentationRemovedFromSource = evt => {
19654
+ const {
19655
+ segmentationId,
19656
+ viewportId
19657
+ } = evt.detail;
19658
+ this._broadcastEvent(this.EVENTS.SEGMENTATION_REPRESENTATION_REMOVED, {
19659
+ segmentationId,
19660
+ viewportId
19661
+ });
19662
+ };
19264
19663
  this._onSegmentationModifiedFromSource = evt => {
19265
19664
  const {
19266
19665
  segmentationId
@@ -19277,6 +19676,14 @@ class SegmentationService extends src/* PubSubService */.Rc {
19277
19676
  segmentationId
19278
19677
  });
19279
19678
  };
19679
+ this._onSegmentationRemovedFromSource = evt => {
19680
+ const {
19681
+ segmentationId
19682
+ } = evt.detail;
19683
+ this._broadcastEvent(this.EVENTS.SEGMENTATION_REMOVED, {
19684
+ segmentationId
19685
+ });
19686
+ };
19280
19687
  this._onAnnotationCutMergeProcessCompletedFromSource = evt => {
19281
19688
  const {
19282
19689
  segmentationId
@@ -19482,6 +19889,7 @@ class SegmentationService extends src/* PubSubService */.Rc {
19482
19889
  },
19483
19890
  config: {
19484
19891
  label,
19892
+ fallbackLabel: `S:${displaySet.SeriesNumber} ${displaySet.Modality}`,
19485
19893
  segments: options?.segments && Object.keys(options.segments).length > 0 ? options.segments : {
19486
19894
  1: {
19487
19895
  label: `${i18n_src/* default */.A.t('Segment')} 1`,
@@ -19613,6 +20021,7 @@ class SegmentationService extends src/* PubSubService */.Rc {
19613
20021
  },
19614
20022
  config: {
19615
20023
  label: segDisplaySet.SeriesDescription,
20024
+ fallbackLabel: `S:${segDisplaySet.SeriesNumber} ${segDisplaySet.Modality}`,
19616
20025
  segments
19617
20026
  }
19618
20027
  };
@@ -19673,7 +20082,8 @@ class SegmentationService extends src/* PubSubService */.Rc {
19673
20082
  }
19674
20083
  },
19675
20084
  config: {
19676
- label: rtDisplaySet.SeriesDescription
20085
+ label: rtDisplaySet.SeriesDescription,
20086
+ fallbackLabel: `S:${rtDisplaySet.SeriesNumber} ${rtDisplaySet.Modality}`
19677
20087
  }
19678
20088
  };
19679
20089
  const segments = {};
@@ -20170,14 +20580,14 @@ class SegmentationService extends src/* PubSubService */.Rc {
20170
20580
  }
20171
20581
  /**
20172
20582
  * Clears segmentation representations from the viewport.
20173
- * Unlike removeSegmentationRepresentations, this doesn't update
20583
+ * Unlike removeRepresentationsFromViewport, this doesn't update
20174
20584
  * removed display set and representation maps.
20175
20585
  * We track removed segmentations manually to avoid re-adding them
20176
20586
  * when the display set is added again.
20177
20587
  * @param viewportId - The viewport ID to clear segmentation representations from.
20178
20588
  */
20179
20589
  clearSegmentationRepresentations(viewportId) {
20180
- this.removeSegmentationRepresentations(viewportId);
20590
+ this.removeRepresentationsFromViewport(viewportId);
20181
20591
  }
20182
20592
 
20183
20593
  /**
@@ -20192,7 +20602,7 @@ class SegmentationService extends src/* PubSubService */.Rc {
20192
20602
  }
20193
20603
 
20194
20604
  /**
20195
- * It removes the segmentation representations from the viewport.
20605
+ * Removes segmentation representations from the viewport.
20196
20606
  * @param viewportId - The viewport id to remove the segmentation representations from.
20197
20607
  * @param specifier - The specifier to remove the segmentation representations.
20198
20608
  *
@@ -20202,7 +20612,7 @@ class SegmentationService extends src/* PubSubService */.Rc {
20202
20612
  * If a type specifier is provided, only the segmentation representation with the specified type are removed.
20203
20613
  * If both a segmentationId and type specifier are provided, only the segmentation representation with the specified segmentationId and type are removed.
20204
20614
  */
20205
- removeSegmentationRepresentations(viewportId, specifier = {}) {
20615
+ removeRepresentationsFromViewport(viewportId, specifier = {}) {
20206
20616
  dist_esm.segmentation.removeSegmentationRepresentations(viewportId, specifier);
20207
20617
  }
20208
20618
 
@@ -20558,6 +20968,7 @@ class SegmentationService extends src/* PubSubService */.Rc {
20558
20968
  id: id,
20559
20969
  segmentationId,
20560
20970
  label: segmentation.label,
20971
+ fallbackLabel: segmentation.fallbackLabel,
20561
20972
  active,
20562
20973
  type,
20563
20974
  visible,
@@ -20570,11 +20981,11 @@ class SegmentationService extends src/* PubSubService */.Rc {
20570
20981
  }
20571
20982
  _initSegmentationService() {
20572
20983
  esm.eventTarget.addEventListener(dist_esm.Enums.Events.SEGMENTATION_MODIFIED, this._onSegmentationModifiedFromSource);
20573
- esm.eventTarget.addEventListener(dist_esm.Enums.Events.SEGMENTATION_REMOVED, this._onSegmentationModifiedFromSource);
20984
+ esm.eventTarget.addEventListener(dist_esm.Enums.Events.SEGMENTATION_REMOVED, this._onSegmentationRemovedFromSource);
20574
20985
  esm.eventTarget.addEventListener(dist_esm.Enums.Events.SEGMENTATION_DATA_MODIFIED, this._onSegmentationDataModifiedFromSource);
20575
20986
  esm.eventTarget.addEventListener(dist_esm.Enums.Events.SEGMENTATION_REPRESENTATION_MODIFIED, this._onSegmentationRepresentationModifiedFromSource);
20576
20987
  esm.eventTarget.addEventListener(dist_esm.Enums.Events.SEGMENTATION_REPRESENTATION_ADDED, this._onSegmentationRepresentationModifiedFromSource);
20577
- esm.eventTarget.addEventListener(dist_esm.Enums.Events.SEGMENTATION_REPRESENTATION_REMOVED, this._onSegmentationRepresentationModifiedFromSource);
20988
+ esm.eventTarget.addEventListener(dist_esm.Enums.Events.SEGMENTATION_REPRESENTATION_REMOVED, this._onSegmentationRepresentationRemovedFromSource);
20578
20989
  esm.eventTarget.addEventListener(dist_esm.Enums.Events.SEGMENTATION_ADDED, this._onSegmentationAddedFromSource);
20579
20990
  esm.eventTarget.addEventListener(dist_esm.Enums.Events.ANNOTATION_CUT_MERGE_PROCESS_COMPLETED, this._onAnnotationCutMergeProcessCompletedFromSource);
20580
20991
  }
@@ -20780,6 +21191,7 @@ const ORTHOGRAPHIC = 'orthographic';
20780
21191
  const VOLUME_3D = 'volume3d';
20781
21192
  const VIDEO = 'video';
20782
21193
  const WHOLESLIDE = 'wholeslide';
21194
+ const ECG = 'ecg';
20783
21195
  function getCornerstoneViewportType(viewportType, displaySets) {
20784
21196
  const lowerViewportType = displaySets?.[0]?.viewportType?.toLowerCase() || viewportType.toLowerCase();
20785
21197
  if (lowerViewportType === STACK) {
@@ -20791,13 +21203,16 @@ function getCornerstoneViewportType(viewportType, displaySets) {
20791
21203
  if (lowerViewportType === WHOLESLIDE) {
20792
21204
  return esm.Enums.ViewportType.WHOLE_SLIDE;
20793
21205
  }
21206
+ if (lowerViewportType === ECG) {
21207
+ return esm.Enums.ViewportType.ECG;
21208
+ }
20794
21209
  if (lowerViewportType === VOLUME || lowerViewportType === ORTHOGRAPHIC) {
20795
21210
  return esm.Enums.ViewportType.ORTHOGRAPHIC;
20796
21211
  }
20797
21212
  if (lowerViewportType === VOLUME_3D) {
20798
21213
  return esm.Enums.ViewportType.VOLUME_3D;
20799
21214
  }
20800
- throw new Error(`Invalid viewport type: ${viewportType}. Valid types are: stack, volume, video, wholeslide`);
21215
+ throw new Error(`Invalid viewport type: ${viewportType}. Valid types are: stack, volume, video, wholeslide, ecg`);
20801
21216
  }
20802
21217
  ;// ../../../extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts
20803
21218
  var _CornerstoneCacheService;
@@ -21104,15 +21519,24 @@ function getCornerstoneBlendMode(blendMode) {
21104
21519
  const AXIAL = 'axial';
21105
21520
  const SAGITTAL = 'sagittal';
21106
21521
  const CORONAL = 'coronal';
21522
+ const AXIAL_REFORMAT = 'axial_reformat';
21523
+ const SAGITTAL_REFORMAT = 'sagittal_reformat';
21524
+ const CORONAL_REFORMAT = 'coronal_reformat';
21107
21525
  function getCornerstoneOrientation(orientation) {
21108
21526
  if (orientation) {
21109
21527
  switch (orientation.toLowerCase()) {
21110
21528
  case AXIAL:
21111
21529
  return esm.Enums.OrientationAxis.AXIAL;
21530
+ case AXIAL_REFORMAT:
21531
+ return esm.Enums.OrientationAxis.AXIAL_REFORMAT;
21112
21532
  case SAGITTAL:
21113
21533
  return esm.Enums.OrientationAxis.SAGITTAL;
21534
+ case SAGITTAL_REFORMAT:
21535
+ return esm.Enums.OrientationAxis.SAGITTAL_REFORMAT;
21114
21536
  case CORONAL:
21115
21537
  return esm.Enums.OrientationAxis.CORONAL;
21538
+ case CORONAL_REFORMAT:
21539
+ return esm.Enums.OrientationAxis.CORONAL_REFORMAT;
21116
21540
  default:
21117
21541
  return esm.Enums.OrientationAxis.ACQUISITION;
21118
21542
  }
@@ -21385,6 +21809,32 @@ const CornerstoneViewportService_EVENTS = {
21385
21809
  };
21386
21810
  const MIN_STACK_VIEWPORTS_TO_ENQUEUE_RESIZE = 12;
21387
21811
  const MIN_VOLUME_VIEWPORTS_TO_ENQUEUE_RESIZE = 6;
21812
+ function getVolumeActorReferencedIds(viewport) {
21813
+ const actors = viewport.getActors?.() ?? [];
21814
+ return actors.filter(ac => ac.actor?.getClassName?.() === 'vtkVolume').map(ac => ac.referencedId).filter(Boolean);
21815
+ }
21816
+ function volumeIdPrefixesMatch(existingIds, prefixLen, targetIds) {
21817
+ if (prefixLen > targetIds.length) {
21818
+ return false;
21819
+ }
21820
+ for (let i = 0; i < prefixLen; i++) {
21821
+ if (existingIds[i] !== targetIds[i]) {
21822
+ return false;
21823
+ }
21824
+ }
21825
+ return true;
21826
+ }
21827
+
21828
+ /**
21829
+ * Returns true when the viewport type matches a volume-based presentation (ORTHOGRAPHIC or VOLUME_3D).
21830
+ */
21831
+ function viewportMatchesDesiredVolumePresentation(viewport, desiredViewportInfo) {
21832
+ const desiredType = desiredViewportInfo.getViewportType();
21833
+ if (viewport.type !== desiredType) {
21834
+ return false;
21835
+ }
21836
+ return desiredType === esm.Enums.ViewportType.ORTHOGRAPHIC || desiredType === esm.Enums.ViewportType.VOLUME_3D;
21837
+ }
21388
21838
  const WITH_NAVIGATION = {
21389
21839
  withNavigation: true,
21390
21840
  withOrientation: false
@@ -22072,6 +22522,15 @@ class CornerstoneViewportService extends src/* PubSubService */.Rc {
22072
22522
  /**
22073
22523
  * Sets the image data for the given viewport.
22074
22524
  */
22525
+ async _setEcgViewport(viewport, viewportData) {
22526
+ const [displaySet] = viewportData.data;
22527
+ const imageId = displaySet.imageIds?.[0];
22528
+ if (!imageId) {
22529
+ console.error('[CornerstoneViewportService] ECG display set has no imageId');
22530
+ return;
22531
+ }
22532
+ return viewport.setEcg(imageId);
22533
+ }
22075
22534
  async _setOtherViewport(viewport, viewportData, viewportInfo, _presentations = {}) {
22076
22535
  const [displaySet] = viewportData.data;
22077
22536
  return viewport.setDataIds(displaySet.imageIds, {
@@ -22140,32 +22599,25 @@ class CornerstoneViewportService extends src/* PubSubService */.Rc {
22140
22599
  if (initialImageIndexToUse === undefined || initialImageIndexToUse === null || initialImageIndexToUse < 0) {
22141
22600
  initialImageIndexToUse = this._getInitialImageIndexForViewport(viewportInfo, imageIds) || 0;
22142
22601
  }
22143
- return viewport.setStack(imageIds, initialImageIndexToUse).then(() => {
22602
+ await viewport.setStack(imageIds, initialImageIndexToUse);
22603
+ viewport.setProperties({
22604
+ ...properties
22605
+ });
22606
+ this.setPresentations(viewport.id, presentations, viewportInfo);
22607
+ await this._addOverlayRepresentations(overlayProcessingResults);
22608
+ if (displayArea) {
22609
+ viewport.setDisplayArea(displayArea);
22610
+ }
22611
+ if (rotation) {
22144
22612
  viewport.setProperties({
22145
- ...properties
22613
+ rotation
22146
22614
  });
22147
- this.setPresentations(viewport.id, presentations, viewportInfo);
22148
- if (overlayProcessingResults?.length) {
22149
- overlayProcessingResults.forEach(overlayProcessingResult => {
22150
- if (overlayProcessingResult?.addOverlayFn) {
22151
- overlayProcessingResult.addOverlayFn();
22152
- }
22153
- });
22154
- }
22155
- if (displayArea) {
22156
- viewport.setDisplayArea(displayArea);
22157
- }
22158
- if (rotation) {
22159
- viewport.setProperties({
22160
- rotation
22161
- });
22162
- }
22163
- if (flipHorizontal) {
22164
- viewport.setCamera({
22165
- flipHorizontal: true
22166
- });
22167
- }
22168
- });
22615
+ }
22616
+ if (flipHorizontal) {
22617
+ viewport.setCamera({
22618
+ flipHorizontal: true
22619
+ });
22620
+ }
22169
22621
  }
22170
22622
  _getInitialImageIndexForViewport(viewportInfo, imageIds) {
22171
22623
  const initialImageOptions = viewportInfo.getInitialImageOptions();
@@ -22388,16 +22840,34 @@ class CornerstoneViewportService extends src/* PubSubService */.Rc {
22388
22840
  }
22389
22841
  });
22390
22842
  }
22391
- await viewport.setVolumes(volumeInputArray);
22392
- if (overlayProcessingResults?.length) {
22393
- overlayProcessingResults.forEach(({
22394
- addOverlayFn
22395
- }) => {
22396
- if (addOverlayFn) {
22397
- addOverlayFn();
22843
+ const baseVolumeInputs = filteredVolumeInputArray.map(({
22844
+ volumeInput
22845
+ }) => volumeInput);
22846
+ const nextBaseVolumeIds = baseVolumeInputs.map(v => v.volumeId);
22847
+ const existingVolumeIds = getVolumeActorReferencedIds(viewport);
22848
+ let skippedIdenticalBaseVolumes = false;
22849
+ if (baseVolumeInputs.length) {
22850
+ const singleBaseViewport = nextBaseVolumeIds.length === 1;
22851
+
22852
+ // Only skip setVolumes() when the viewport type already matches the desired OHIF type
22853
+ // (ORTHOGRAPHIC / VOLUME_3D); otherwise a stack → MPR switch with the same volumeId
22854
+ // would incorrectly skip rebuilding the viewport.
22855
+ if (singleBaseViewport && existingVolumeIds.length >= nextBaseVolumeIds.length && volumeIdPrefixesMatch(existingVolumeIds, nextBaseVolumeIds.length, nextBaseVolumeIds) && viewportMatchesDesiredVolumePresentation(viewport, viewportInfo)) {
22856
+ // Same primary volume already loaded (e.g. labelmap / extra actors after it) — avoid
22857
+ // setVolumes(), which tears down all actors and blanks MPR during SEG hydrate.
22858
+ skippedIdenticalBaseVolumes = true;
22859
+ } else if (existingVolumeIds.length && nextBaseVolumeIds.length > existingVolumeIds.length && volumeIdPrefixesMatch(existingVolumeIds, existingVolumeIds.length, nextBaseVolumeIds) && typeof viewport.addVolumes === 'function') {
22860
+ const toAdd = baseVolumeInputs.slice(existingVolumeIds.length);
22861
+ if (toAdd.length) {
22862
+ await viewport.addVolumes(toAdd);
22398
22863
  }
22399
- });
22864
+ } else {
22865
+ await viewport.setVolumes(baseVolumeInputs);
22866
+ }
22867
+ } else if (volumeInputArray.length) {
22868
+ await viewport.setVolumes(volumeInputArray);
22400
22869
  }
22870
+ await this._addOverlayRepresentations(overlayProcessingResults);
22401
22871
  viewport.render();
22402
22872
  volumesProperties.forEach(({
22403
22873
  properties,
@@ -22409,7 +22879,10 @@ class CornerstoneViewportService extends src/* PubSubService */.Rc {
22409
22879
  });
22410
22880
  });
22411
22881
  this.setPresentations(viewport.id, presentations);
22412
- if (!presentations.positionPresentation) {
22882
+ // Presentations apply segmentation (hydrated labelmap etc.) after the render above — redraw so
22883
+ // every orthographic/3D tile shows the updated scene (fixes MPR siblings blank after SEG hydrate).
22884
+ viewport.render();
22885
+ if (!presentations.positionPresentation && !skippedIdenticalBaseVolumes) {
22413
22886
  const imageIndex = this._getInitialImageIndexForViewport(viewportInfo);
22414
22887
  if (imageIndex !== undefined) {
22415
22888
  esm.utilities.jumpToSlice(viewport.element, {
@@ -22460,7 +22933,7 @@ class CornerstoneViewportService extends src/* PubSubService */.Rc {
22460
22933
  const {
22461
22934
  predecessorImageId
22462
22935
  } = displaySet;
22463
- segmentationService.addSegmentationRepresentation(viewport.id, {
22936
+ const segmentationRepresentationPromise = segmentationService.addSegmentationRepresentation(viewport.id, {
22464
22937
  segmentationId,
22465
22938
  predecessorImageId,
22466
22939
  type: representationType,
@@ -22468,11 +22941,21 @@ class CornerstoneViewportService extends src/* PubSubService */.Rc {
22468
22941
  blendMode: viewport?.getBlendMode?.() === 1 ? dist_esm_enums.BlendModes.LABELMAP_EDGE_PROJECTION_BLEND : undefined
22469
22942
  }
22470
22943
  });
22471
-
22472
22944
  // store the segmentation presentation id in the viewport info
22473
22945
  this.storePresentation({
22474
22946
  viewportId: viewport.id
22475
22947
  });
22948
+ return segmentationRepresentationPromise;
22949
+ }
22950
+ async _addOverlayRepresentations(overlayProcessingResults) {
22951
+ if (!overlayProcessingResults?.length) {
22952
+ return;
22953
+ }
22954
+ for (const overlayProcessingResult of overlayProcessingResults) {
22955
+ if (overlayProcessingResult?.addOverlayFn) {
22956
+ await overlayProcessingResult.addOverlayFn();
22957
+ }
22958
+ }
22476
22959
  }
22477
22960
 
22478
22961
  // Todo: keepCamera is an interim solution until we have a better solution for
@@ -22507,6 +22990,9 @@ class CornerstoneViewportService extends src/* PubSubService */.Rc {
22507
22990
  if ([esm.VolumeViewport, esm.VolumeViewport3D].some(type => viewport instanceof type)) {
22508
22991
  return this._setVolumeViewport(viewport, viewportData, viewportInfo, presentations);
22509
22992
  }
22993
+ if (viewport instanceof esm.ECGViewport) {
22994
+ return this._setEcgViewport(viewport, viewportData);
22995
+ }
22510
22996
  return this._setOtherViewport(viewport, viewportData, viewportInfo, presentations);
22511
22997
  }
22512
22998
 
@@ -22549,8 +23035,8 @@ class CornerstoneViewportService extends src/* PubSubService */.Rc {
22549
23035
  if (!displaySet) {
22550
23036
  return;
22551
23037
  }
22552
- if (displaySet.frameOfReferenceUID) {
22553
- return displaySet.frameOfReferenceUID;
23038
+ if (displaySet.FrameOfReferenceUID) {
23039
+ return displaySet.FrameOfReferenceUID;
22554
23040
  }
22555
23041
  if (displaySet.Modality === 'SEG') {
22556
23042
  const {
@@ -22728,6 +23214,8 @@ CornerstoneViewportService.REGISTRATION = {
22728
23214
  }
22729
23215
  };
22730
23216
  /* harmony default export */ const ViewportService_CornerstoneViewportService = (CornerstoneViewportService);
23217
+ // EXTERNAL MODULE: ../../../extensions/cornerstone/src/utils/getDataIdForViewport.ts
23218
+ var getDataIdForViewport = __webpack_require__(67142);
22731
23219
  ;// ../../../extensions/cornerstone/src/types/Colorbar.ts
22732
23220
  // Position options
22733
23221
 
@@ -22770,6 +23258,7 @@ var _ColorbarService;
22770
23258
 
22771
23259
 
22772
23260
 
23261
+
22773
23262
  class ColorbarService extends src/* PubSubService */.Rc {
22774
23263
  constructor(servicesManager) {
22775
23264
  super(ColorbarService.EVENTS);
@@ -22793,23 +23282,6 @@ class ColorbarService extends src/* PubSubService */.Rc {
22793
23282
  this.servicesManager = servicesManager;
22794
23283
  }
22795
23284
 
22796
- /**
22797
- * Gets the appropriate data ID for a viewport and display set
22798
- * @param viewport - The viewport instance
22799
- * @param displaySetInstanceUID - The display set instance UID to identify data
22800
- * @returns The appropriate data ID for the viewport type (volumeId for volume viewports, undefined for stack)
22801
- */
22802
- getDataIdForViewport(viewport, displaySetInstanceUID) {
22803
- // For volume viewports, find the matching volumeId
22804
- if (viewport.getAllVolumeIds) {
22805
- const volumeIds = viewport.getAllVolumeIds() || [];
22806
- return volumeIds.length > 0 ? volumeIds.find(id => id.includes(displaySetInstanceUID)) || undefined : undefined;
22807
- }
22808
-
22809
- // For other viewports, no specific dataId is needed for now
22810
- return undefined;
22811
- }
22812
-
22813
23285
  /**
22814
23286
  * Adds a colorbar to a specific viewport identified by `viewportId`, using the provided `displaySetInstanceUIDs` and `options`.
22815
23287
  * This method prepares the colorbar state that will be used by the ViewportColorbarsContainer component.
@@ -22841,7 +23313,7 @@ class ColorbarService extends src/* PubSubService */.Rc {
22841
23313
  if (displaySet.isOverlayDisplaySet) {
22842
23314
  return;
22843
23315
  }
22844
- const dataId = this.getDataIdForViewport(viewport, displaySetInstanceUID);
23316
+ const dataId = (0,getDataIdForViewport/* getDataIdForViewport */.V)(viewport, displaySetInstanceUID);
22845
23317
  const properties = dataId ? viewport.getProperties(dataId) : viewport.getProperties();
22846
23318
  const colormap = properties?.colormap;
22847
23319
  if (activeColormapName && !colormap) {
@@ -22979,7 +23451,7 @@ class ColorbarService extends src/* PubSubService */.Rc {
22979
23451
  }
22980
23452
 
22981
23453
  // Get the appropriate dataId for this viewport/displaySet combination
22982
- const dataId = this.getDataIdForViewport(viewport, displaySetInstanceUID);
23454
+ const dataId = (0,getDataIdForViewport/* getDataIdForViewport */.V)(viewport, displaySetInstanceUID);
22983
23455
 
22984
23456
  // Set properties with or without dataId based on what the viewport supports
22985
23457
  viewport.setProperties({
@@ -23005,6 +23477,54 @@ ColorbarService.REGISTRATION = {
23005
23477
  ;// ../../../extensions/cornerstone/src/services/ColorbarService/index.ts
23006
23478
 
23007
23479
  /* harmony default export */ const services_ColorbarService = (ColorbarService);
23480
+ ;// ../../../extensions/cornerstone/src/services/ViewedDataService/ViewedDataService.ts
23481
+ var _ViewedDataService;
23482
+
23483
+ class ViewedDataService extends src/* PubSubService */.Rc {
23484
+ constructor() {
23485
+ super(ViewedDataService.EVENTS);
23486
+ this.viewedDataIds = new Set();
23487
+ }
23488
+ markDataViewed(dataId) {
23489
+ if (!dataId || this.viewedDataIds.has(dataId)) {
23490
+ return;
23491
+ }
23492
+ this.viewedDataIds.add(dataId);
23493
+ this._broadcastEvent(this.EVENTS.VIEWED_DATA_CHANGED, {
23494
+ viewedDataId: dataId
23495
+ });
23496
+ }
23497
+ isDataViewed(dataId) {
23498
+ if (!dataId) {
23499
+ return false;
23500
+ }
23501
+ return this.viewedDataIds.has(dataId);
23502
+ }
23503
+ clearViewedData() {
23504
+ this.viewedDataIds.clear();
23505
+ this._broadcastEvent(this.EVENTS.VIEWED_DATA_CHANGED, {
23506
+ viewedDataCleared: true
23507
+ });
23508
+ }
23509
+ subscribeViewedDataChanges(listener) {
23510
+ return this.subscribe(this.EVENTS.VIEWED_DATA_CHANGED, listener);
23511
+ }
23512
+ }
23513
+ _ViewedDataService = ViewedDataService;
23514
+ ViewedDataService.EVENTS = {
23515
+ VIEWED_DATA_CHANGED: 'event::viewedDataChanged'
23516
+ };
23517
+ ViewedDataService.REGISTRATION = {
23518
+ name: 'viewedDataService',
23519
+ altName: 'ViewedDataService',
23520
+ create: () => {
23521
+ return new _ViewedDataService();
23522
+ }
23523
+ };
23524
+ /* harmony default export */ const ViewedDataService_ViewedDataService = (ViewedDataService);
23525
+ ;// ../../../extensions/cornerstone/src/services/ViewedDataService/index.ts
23526
+
23527
+ /* harmony default export */ const services_ViewedDataService = (ViewedDataService_ViewedDataService);
23008
23528
  ;// ../../../extensions/cornerstone/src/types/Presentation.ts
23009
23529
 
23010
23530
 
@@ -23249,12 +23769,114 @@ const createFrameViewSynchronizer = synchronizerName => {
23249
23769
 
23250
23770
  // EXTERNAL MODULE: ../../../node_modules/dcmjs/build/dcmjs.es.js
23251
23771
  var dcmjs_es = __webpack_require__(5842);
23772
+ ;// ../../../extensions/cornerstone/src/utils/ecgMetadata.ts
23773
+ /**
23774
+ * Decode a multiplexed Int16 buffer into per-channel arrays.
23775
+ * Layout: sample0ch0, sample0ch1 ... sample0chN, sample1ch0, …
23776
+ * Note: DICOM ECG data is canonically SS (signed short). The sampleInterpretation
23777
+ * field is forwarded to ECGViewport for its own use; the raw buffer is always
23778
+ * treated as Int16 because Cornerstone ECGViewport expects Int16Array[].
23779
+ */
23780
+ function decodeInt16Multiplex(buffer, numberOfChannels, numberOfSamples) {
23781
+ const src = new Int16Array(buffer);
23782
+ const channels = [];
23783
+ for (let ch = 0; ch < numberOfChannels; ch++) {
23784
+ const out = new Int16Array(numberOfSamples);
23785
+ for (let s = 0; s < numberOfSamples; s++) {
23786
+ out[s] = src[s * numberOfChannels + ch];
23787
+ }
23788
+ channels.push(out);
23789
+ }
23790
+ return channels;
23791
+ }
23792
+
23793
+ /**
23794
+ * Decode a base64 InlineBinary string into a raw ArrayBuffer.
23795
+ */
23796
+ function base64ToArrayBuffer(base64) {
23797
+ const binaryStr = atob(base64);
23798
+ const bytes = new Uint8Array(binaryStr.length);
23799
+ for (let i = 0; i < binaryStr.length; i++) {
23800
+ bytes[i] = binaryStr.charCodeAt(i);
23801
+ }
23802
+ return bytes.buffer;
23803
+ }
23804
+ /**
23805
+ * Parse the naturalized DICOM instance's WaveformSequence and build the ecgModule
23806
+ * that Cornerstone's ECGViewport.setEcg() expects via
23807
+ * metaData.get(MetadataModules.ECG, imageId).
23808
+ *
23809
+ * Returns null if the instance has no WaveformSequence.
23810
+ */
23811
+ function buildEcgModule(instance, userAuthenticationService) {
23812
+ const waveformGroups = instance?.WaveformSequence;
23813
+ if (!waveformGroups?.length) {
23814
+ return null;
23815
+ }
23816
+
23817
+ // Use the first (and typically only) multiplex group
23818
+ const group = waveformGroups[0];
23819
+ const numberOfChannels = group.NumberOfWaveformChannels ?? 0;
23820
+ const numberOfSamples = group.NumberOfWaveformSamples ?? 0;
23821
+ const samplingFrequency = group.SamplingFrequency ?? 1;
23822
+ const bitsAllocated = group.WaveformBitsAllocated ?? 16;
23823
+ const sampleInterpretation = group.WaveformSampleInterpretation ?? 'SS';
23824
+ const multiplexGroupLabel = group.MultiplexGroupLabel ?? '';
23825
+ const channelDefinitionSequence = (group.ChannelDefinitionSequence ?? []).map(ch => ({
23826
+ channelSourceSequence: {
23827
+ codeMeaning: ch?.ChannelSourceSequence?.[0]?.CodeMeaning ?? ch?.ChannelSourceSequence?.[0]?.codeMeaning ?? ''
23828
+ }
23829
+ }));
23830
+ const retrieveBulkData = async () => {
23831
+ const waveformData = group.WaveformData;
23832
+ if (!waveformData) {
23833
+ console.warn('[ECGViewport] No WaveformData found on instance');
23834
+ return [];
23835
+ }
23836
+ let buffer;
23837
+ if (waveformData.InlineBinary) {
23838
+ buffer = base64ToArrayBuffer(waveformData.InlineBinary);
23839
+ } else if (waveformData.BulkDataURI) {
23840
+ const headers = {
23841
+ Accept: 'application/octet-stream'
23842
+ };
23843
+ const authHeader = userAuthenticationService?.getAuthorizationHeader?.();
23844
+ if (authHeader) {
23845
+ Object.assign(headers, authHeader);
23846
+ }
23847
+ const response = await fetch(waveformData.BulkDataURI, {
23848
+ headers
23849
+ });
23850
+ if (!response.ok) {
23851
+ throw new Error(`[ECGViewport] Failed to fetch waveform BulkDataURI: ${response.status}`);
23852
+ }
23853
+ buffer = await response.arrayBuffer();
23854
+ } else {
23855
+ console.warn('[ECGViewport] WaveformData has no InlineBinary or BulkDataURI');
23856
+ return [];
23857
+ }
23858
+ return decodeInt16Multiplex(buffer, numberOfChannels, numberOfSamples);
23859
+ };
23860
+ return {
23861
+ numberOfWaveformChannels: numberOfChannels,
23862
+ numberOfWaveformSamples: numberOfSamples,
23863
+ samplingFrequency,
23864
+ waveformBitsAllocated: bitsAllocated,
23865
+ waveformSampleInterpretation: sampleInterpretation,
23866
+ multiplexGroupLabel,
23867
+ channelDefinitionSequence,
23868
+ waveformData: {
23869
+ retrieveBulkData
23870
+ }
23871
+ };
23872
+ }
23252
23873
  ;// ../../../extensions/cornerstone/src/getSopClassHandlerModule.js
23253
23874
 
23254
23875
 
23255
23876
 
23256
23877
 
23257
23878
 
23879
+
23258
23880
  const {
23259
23881
  MetadataModules
23260
23882
  } = esm.Enums;
@@ -23265,9 +23887,9 @@ const {
23265
23887
  denaturalizeDataset
23266
23888
  } = dcmjs_es/* default.data */.Ay.data.DicomMetaDictionary;
23267
23889
  const {
23268
- transferDenaturalizedDataset,
23269
- fixMultiValueKeys
23270
- } = default_src.dicomWebUtils;
23890
+ /* transferDenaturalizedDataset */ "If": transferDenaturalizedDataset,
23891
+ /* fixMultiValueKeys */ "Uk": fixMultiValueKeys
23892
+ } = default_src/* dicomWebUtils */.CA;
23271
23893
  const SOP_CLASS_UIDS = {
23272
23894
  VL_WHOLE_SLIDE_MICROSCOPY_IMAGE_STORAGE: '1.2.840.10008.5.1.4.1.1.77.1.6'
23273
23895
  };
@@ -23393,8 +24015,92 @@ function getDicomMicroscopySopClassHandler({
23393
24015
  getDisplaySetsFromSeries
23394
24016
  };
23395
24017
  }
24018
+
24019
+ /**
24020
+ * DICOM Waveform SOP Class UIDs for ECG / cardiac electrophysiology.
24021
+ * Reference: https://dicom.nema.org/medical/dicom/current/output/chtml/part04/sect_B.5.html
24022
+ */
24023
+ const ECG_SOP_CLASS_UIDS = {
24024
+ TWELVE_LEAD_ECG_WAVEFORM_STORAGE: '1.2.840.10008.5.1.4.1.1.9.1.1',
24025
+ GENERAL_ECG_WAVEFORM_STORAGE: '1.2.840.10008.5.1.4.1.1.9.1.2',
24026
+ AMBULATORY_ECG_WAVEFORM_STORAGE: '1.2.840.10008.5.1.4.1.1.9.1.3',
24027
+ HEMODYNAMIC_WAVEFORM_STORAGE: '1.2.840.10008.5.1.4.1.1.9.2.1',
24028
+ CARDIAC_ELECTROPHYSIOLOGY_WAVEFORM_STORAGE: '1.2.840.10008.5.1.4.1.1.9.3.1'
24029
+ };
24030
+ const ecgSopClassUids = Object.values(ECG_SOP_CLASS_UIDS);
24031
+ const DicomEcgSOPClassHandlerId = '@ohif/extension-cornerstone.sopClassHandlerModule.DicomEcgSopClassHandler';
24032
+ function _getEcgDisplaySetsFromSeries(instances, servicesManager) {
24033
+ const {
24034
+ userAuthenticationService
24035
+ } = servicesManager.services;
24036
+ return instances.map(instance => {
24037
+ const {
24038
+ Modality,
24039
+ SOPInstanceUID
24040
+ } = instance;
24041
+ const {
24042
+ SeriesDescription,
24043
+ SeriesNumber,
24044
+ SeriesDate
24045
+ } = instance;
24046
+ const {
24047
+ SeriesInstanceUID,
24048
+ StudyInstanceUID,
24049
+ SOPClassUID
24050
+ } = instance;
24051
+ const imageId = instance.imageId;
24052
+
24053
+ // Register ECG metadata in the OHIF metadata provider so that
24054
+ // Cornerstone's ECGViewport can retrieve it via metaData.get('ecgModule', imageId).
24055
+ if (imageId) {
24056
+ const ecgModule = buildEcgModule(instance, userAuthenticationService);
24057
+ if (ecgModule) {
24058
+ esm.utilities.genericMetadataProvider.addRaw(imageId, {
24059
+ type: MetadataModules.ECG,
24060
+ metadata: ecgModule
24061
+ });
24062
+ }
24063
+ }
24064
+ return {
24065
+ Modality,
24066
+ displaySetInstanceUID: getSopClassHandlerModule_utils.guid(),
24067
+ SeriesDescription,
24068
+ SeriesNumber,
24069
+ SeriesDate,
24070
+ SOPInstanceUID,
24071
+ SeriesInstanceUID,
24072
+ StudyInstanceUID,
24073
+ SOPClassHandlerId: DicomEcgSOPClassHandlerId,
24074
+ SOPClassUID,
24075
+ referencedImages: null,
24076
+ measurements: null,
24077
+ viewportType: esm.Enums.ViewportType.ECG,
24078
+ instances: [instance],
24079
+ instance,
24080
+ thumbnailSrc: null,
24081
+ isDerivedDisplaySet: false,
24082
+ isLoaded: false,
24083
+ sopClassUids: ecgSopClassUids,
24084
+ numImageFrames: 0,
24085
+ numInstances: 1,
24086
+ imageIds: imageId ? [imageId] : [],
24087
+ supportsWindowLevel: false,
24088
+ label: SeriesDescription || 'ECG'
24089
+ };
24090
+ });
24091
+ }
24092
+ function getDicomEcgSopClassHandler({
24093
+ servicesManager
24094
+ }) {
24095
+ const getDisplaySetsFromSeries = instances => _getEcgDisplaySetsFromSeries(instances, servicesManager);
24096
+ return {
24097
+ name: 'DicomEcgSopClassHandler',
24098
+ sopClassUids: ecgSopClassUids,
24099
+ getDisplaySetsFromSeries
24100
+ };
24101
+ }
23396
24102
  function getSopClassHandlerModule(params) {
23397
- return [getDicomMicroscopySopClassHandler(params)];
24103
+ return [getDicomMicroscopySopClassHandler(params), getDicomEcgSopClassHandler(params)];
23398
24104
  }
23399
24105
  // EXTERNAL MODULE: ../../../extensions/cornerstone/src/hooks/useActiveViewportSegmentationRepresentations.ts
23400
24106
  var useActiveViewportSegmentationRepresentations = __webpack_require__(9234);
@@ -23424,7 +24130,7 @@ function SegmentationUtilityButton(props) {
23424
24130
  isActive,
23425
24131
  id
23426
24132
  } = props;
23427
- const activeSegmentationUtility = (0,default_src.useUIStateStore)(store => store.uiState.activeSegmentationUtility);
24133
+ const activeSegmentationUtility = (0,default_src/* useUIStateStore */.FS)(store => store.uiState.activeSegmentationUtility);
23428
24134
  const toolButtonClassName = (0,ui_next_src.cn)('w-7 h-7 text-primary hover:text-primary hover:!bg-primary/30', className, isActive && 'bg-primary/30');
23429
24135
  const handleMouseDownCapture = (0,react.useCallback)(event => {
23430
24136
  if (activeSegmentationUtility === id) {
@@ -23555,7 +24261,7 @@ function PanelSegmentation({
23555
24261
  segmentationsWithRepresentations,
23556
24262
  disabled
23557
24263
  } = (0,useActiveViewportSegmentationRepresentations/* useActiveViewportSegmentationRepresentations */.c)();
23558
- const setUIState = (0,default_src.useUIStateStore)(store => store.setUIState);
24264
+ const setUIState = (0,default_src/* useUIStateStore */.FS)(store => store.setUIState);
23559
24265
 
23560
24266
  // useEffect for handling clicks on any of the non-active viewports.
23561
24267
  // The ViewportGrid stops the propagation of pointer/mouse events
@@ -23816,7 +24522,7 @@ function PanelSegmentation({
23816
24522
  IconContainer: components_SegmentationUtilityButton
23817
24523
  }, /*#__PURE__*/react.createElement("div", {
23818
24524
  className: "flex flex-wrap gap-[3px] bg-transparent pb-[2px] pl-[8px] pt-[6px]"
23819
- }, /*#__PURE__*/react.createElement(default_src.Toolbar, {
24525
+ }, /*#__PURE__*/react.createElement(default_src/* Toolbar */.M7, {
23820
24526
  buttonSection: buttonSection
23821
24527
  })));
23822
24528
  };
@@ -24343,12 +25049,12 @@ function DefaultAccordion(props) {
24343
25049
  if (!allChildren || !groups) {
24344
25050
  return null;
24345
25051
  }
24346
- if (Boolean(asChild)) {
25052
+ if (asChild) {
24347
25053
  return /*#__PURE__*/react.cloneElement(props.children, props);
24348
25054
  }
24349
25055
  return /*#__PURE__*/react.createElement(ui_next_src/* Accordion */.nD3, {
24350
25056
  type: grouping.type || 'multiple',
24351
- className: "text-white",
25057
+ className: "text-foreground",
24352
25058
  defaultValue: defaultValue
24353
25059
  }, [...groups.entries()].map(([key, group]) => {
24354
25060
  return /*#__PURE__*/react.createElement(ui_next_src/* AccordionItem */.AsP, {
@@ -24826,7 +25532,7 @@ function PanelMeasurement(props) {
24826
25532
  return EmptyComponent ? /*#__PURE__*/react.createElement(EmptyComponent, {
24827
25533
  items: displayMeasurements
24828
25534
  }) : /*#__PURE__*/react.createElement("span", {
24829
- className: "text-white"
25535
+ className: "text-foreground"
24830
25536
  }, "No Measurements");
24831
25537
  }
24832
25538
  if (children) {
@@ -24891,7 +25597,7 @@ const getPanelModule = ({
24891
25597
  } = (0,es/* useTranslation */.Bd)('SegmentationPanel');
24892
25598
  const tKey = `${props.segmentationRepresentationTypes?.[0] ?? 'Segmentation'} tools`;
24893
25599
  const tValue = t(tKey);
24894
- return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement(default_src.Toolbox, {
25600
+ return /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement(default_src/* Toolbox */.OO, {
24895
25601
  buttonSectionId: toolSectionMap[props.segmentationRepresentationTypes?.[0]],
24896
25602
  title: tValue
24897
25603
  }), /*#__PURE__*/react.createElement(PanelSegmentation, {
@@ -25245,7 +25951,7 @@ function promptHydrationDialog({
25245
25951
  customizationService
25246
25952
  } = servicesManager.services;
25247
25953
  const extensionManager = servicesManager._extensionManager;
25248
- const appConfig = extensionManager._appConfig;
25954
+ const appConfig = extensionManager.appConfig;
25249
25955
 
25250
25956
  // Todo: make this use enum from the extension, we should move the enum
25251
25957
  const standardMode = appConfig?.measurementTrackingMode === 'standard';
@@ -25262,20 +25968,30 @@ function promptHydrationDialog({
25262
25968
  if (type === HydrationType.SEG) {
25263
25969
  // SEG needs setTimeout
25264
25970
  window.setTimeout(async () => {
25265
- const isHydrated = await hydrateCallback({
25266
- segDisplaySet: displaySet,
25267
- viewportId
25268
- });
25269
- resolve(isHydrated);
25971
+ try {
25972
+ const isHydrated = await hydrateCallback({
25973
+ segDisplaySet: displaySet,
25974
+ viewportId
25975
+ });
25976
+ resolve(isHydrated);
25977
+ } catch (error) {
25978
+ reject(error);
25979
+ }
25270
25980
  }, 0);
25271
25981
  } else if (type === HydrationType.RTSTRUCT) {
25272
25982
  // RT hydration
25273
- const isHydrated = await hydrateCallback({
25274
- rtDisplaySet: displaySet,
25275
- viewportId,
25276
- servicesManager
25277
- });
25278
- resolve(isHydrated);
25983
+ window.setTimeout(async () => {
25984
+ try {
25985
+ const isHydrated = await hydrateCallback({
25986
+ rtDisplaySet: displaySet,
25987
+ viewportId,
25988
+ servicesManager
25989
+ });
25990
+ resolve(isHydrated);
25991
+ } catch (error) {
25992
+ reject(error);
25993
+ }
25994
+ }, 0);
25279
25995
  } else if (type === HydrationType.SR) {
25280
25996
  // SR has a different result structure
25281
25997
  const hydrationResult = await hydrateCallback(displaySet);
@@ -25368,7 +26084,8 @@ const setUpSegmentationEventHandlers = ({
25368
26084
  const {
25369
26085
  segmentationService,
25370
26086
  customizationService,
25371
- displaySetService
26087
+ displaySetService,
26088
+ viewportGridService
25372
26089
  } = servicesManager.services;
25373
26090
  const {
25374
26091
  unsubscribe: unsubscribeSegmentationDataModifiedHandler
@@ -25413,12 +26130,42 @@ const setUpSegmentationEventHandlers = ({
25413
26130
  };
25414
26131
  displaySetService.addDisplaySets(segmentationDisplaySet);
25415
26132
  });
26133
+ const {
26134
+ unsubscribe: unsubscribeSegmentationRemoved
26135
+ } = segmentationService.subscribe(segmentationService.EVENTS.SEGMENTATION_REMOVED, ({
26136
+ segmentationId
26137
+ }) => {
26138
+ const displaySet = displaySetService.getDisplaySetByUID(segmentationId);
26139
+
26140
+ // Remove the display set layer from all viewports that have it
26141
+ if (displaySet) {
26142
+ const state = viewportGridService.getState();
26143
+ const viewports = state.viewports;
26144
+
26145
+ // Find all viewports that contain this segmentation's display set as a layer
26146
+ for (const [viewportId, viewport] of viewports.entries()) {
26147
+ const displaySetInstanceUIDs = viewport.displaySetInstanceUIDs || [];
26148
+ if (displaySetInstanceUIDs.includes(segmentationId)) {
26149
+ // Remove the display set layer from this viewport
26150
+ commandsManager.runCommand('removeDisplaySetLayer', {
26151
+ viewportId,
26152
+ displaySetInstanceUID: segmentationId
26153
+ });
26154
+ }
26155
+ }
26156
+
26157
+ // Delete the display set from the service if it was made in client
26158
+ if (displaySet.madeInClient) {
26159
+ displaySetService.deleteDisplaySet(segmentationId);
26160
+ }
26161
+ }
26162
+ });
25416
26163
  const {
25417
26164
  unsubscribeSelectedSegmentationsForViewportEvents
25418
26165
  } = setUpSelectedSegmentationsForViewportHandler({
25419
26166
  segmentationService
25420
26167
  });
25421
- const unsubscriptions = [unsubscribeSegmentationDataModifiedHandler, unsubscribeSegmentationModifiedHandler, unsubscribeSegmentationCreated, ...unsubscribeSelectedSegmentationsForViewportEvents];
26168
+ const unsubscriptions = [unsubscribeSegmentationDataModifiedHandler, unsubscribeSegmentationModifiedHandler, unsubscribeSegmentationCreated, unsubscribeSegmentationRemoved, ...unsubscribeSelectedSegmentationsForViewportEvents];
25422
26169
  return {
25423
26170
  unsubscriptions
25424
26171
  };
@@ -25561,7 +26308,7 @@ function PanelAccordionTrigger(props) {
25561
26308
  }, /*#__PURE__*/react.createElement("button", {
25562
26309
  onClick: onClickDefault.bind(props)
25563
26310
  }, /*#__PURE__*/react.createElement("span", {
25564
- className: `inline-flex rounded-l border-r border-black ${isActive ? 'bg-highlight' : 'bg-muted'}`
26311
+ className: `inline-flex rounded-l border-r border-background ${isActive ? 'bg-highlight' : 'bg-muted'}`
25565
26312
  }, count !== undefined ? /*#__PURE__*/react.createElement("span", {
25566
26313
  className: "px-2"
25567
26314
  }, count) : null, colorHex && /*#__PURE__*/react.createElement(ui_next_src/* ColorCircle */.cd8, {
@@ -25810,13 +26557,14 @@ function src_extends() { return src_extends = Object.assign ? Object.assign.bind
25810
26557
 
25811
26558
 
25812
26559
 
26560
+
25813
26561
 
25814
26562
 
25815
26563
  const {
25816
26564
  imageRetrieveMetadataProvider
25817
26565
  } = esm.utilities;
25818
26566
  const Component = /*#__PURE__*/react.lazy(() => {
25819
- return __webpack_require__.e(/* import() */ 147).then(__webpack_require__.bind(__webpack_require__, 30147));
26567
+ return __webpack_require__.e(/* import() */ 3754).then(__webpack_require__.bind(__webpack_require__, 33754));
25820
26568
  });
25821
26569
  const OHIFCornerstoneViewport = props => {
25822
26570
  return /*#__PURE__*/react.createElement(react.Suspense, {
@@ -25909,9 +26657,10 @@ const cornerstoneExtension = {
25909
26657
  useLutPresentationStore/* useLutPresentationStore */.I.getState().clearLutPresentationStore();
25910
26658
  usePositionPresentationStore/* usePositionPresentationStore */.q.getState().clearPositionPresentationStore();
25911
26659
  useSynchronizersStore/* useSynchronizersStore */.U.getState().clearSynchronizersStore();
25912
- default_src.useToggleOneUpViewportGridStore.getState().clearToggleOneUpViewportGridStore();
26660
+ default_src/* useToggleOneUpViewportGridStore */.Yd.getState().clearToggleOneUpViewportGridStore();
25913
26661
  useSegmentationPresentationStore/* useSegmentationPresentationStore */.v.getState().clearSegmentationPresentationStore();
25914
26662
  useSelectedSegmentationsForViewportStore.getState().clearSelectedSegmentationsForViewportStore();
26663
+ servicesManager.services.viewedDataService?.clearViewedData();
25915
26664
  segmentationService.removeAllSegmentations();
25916
26665
  },
25917
26666
  /**
@@ -25929,6 +26678,7 @@ const cornerstoneExtension = {
25929
26678
  servicesManager.registerService(services_SegmentationService.REGISTRATION);
25930
26679
  servicesManager.registerService(services_CornerstoneCacheService.REGISTRATION);
25931
26680
  servicesManager.registerService(services_ColorbarService.REGISTRATION);
26681
+ servicesManager.registerService(services_ViewedDataService.REGISTRATION);
25932
26682
  const {
25933
26683
  syncGroupService
25934
26684
  } = servicesManager.services;
@@ -26000,10 +26750,10 @@ const cornerstoneExtension = {
26000
26750
 
26001
26751
  /* harmony default export */ const cornerstone_src = (cornerstoneExtension);
26002
26752
 
26003
- /***/ }),
26753
+ /***/ },
26004
26754
 
26005
- /***/ 71353:
26006
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
26755
+ /***/ 71353
26756
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
26007
26757
 
26008
26758
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
26009
26759
  /* harmony export */ cL: () => (/* binding */ reset),
@@ -26042,10 +26792,10 @@ const reset = () => {
26042
26792
  };
26043
26793
 
26044
26794
 
26045
- /***/ }),
26795
+ /***/ },
26046
26796
 
26047
- /***/ 46026:
26048
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
26797
+ /***/ 46026
26798
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
26049
26799
 
26050
26800
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
26051
26801
  /* harmony export */ FG: () => (/* binding */ JOIN_STR),
@@ -26085,10 +26835,10 @@ const addUniqueIndex = (arr, key, viewports, isUpdatingSameViewport) => {
26085
26835
  };
26086
26836
 
26087
26837
 
26088
- /***/ }),
26838
+ /***/ },
26089
26839
 
26090
- /***/ 10182:
26091
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
26840
+ /***/ 10182
26841
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
26092
26842
 
26093
26843
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
26094
26844
  /* harmony export */ I: () => (/* binding */ useLutPresentationStore)
@@ -26200,10 +26950,10 @@ const useLutPresentationStore = (0,zustand__WEBPACK_IMPORTED_MODULE_0__/* .creat
26200
26950
  name: 'LutPresentationStore'
26201
26951
  }) : createLutPresentationStore);
26202
26952
 
26203
- /***/ }),
26953
+ /***/ },
26204
26954
 
26205
- /***/ 44646:
26206
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
26955
+ /***/ 44646
26956
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
26207
26957
 
26208
26958
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
26209
26959
  /* harmony export */ q: () => (/* binding */ usePositionPresentationStore)
@@ -26311,10 +27061,10 @@ const usePositionPresentationStore = (0,zustand__WEBPACK_IMPORTED_MODULE_0__/* .
26311
27061
  name: 'PositionPresentationStore'
26312
27062
  }) : createPositionPresentationStore);
26313
27063
 
26314
- /***/ }),
27064
+ /***/ },
26315
27065
 
26316
- /***/ 2847:
26317
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
27066
+ /***/ 2847
27067
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
26318
27068
 
26319
27069
 
26320
27070
  // EXPORTS
@@ -26426,20 +27176,34 @@ const _getSegmentationPresentationId = ({
26426
27176
  if (!viewport?.viewportOptions || !viewport.displaySetInstanceUIDs?.length) {
26427
27177
  return;
26428
27178
  }
27179
+ const {
27180
+ displaySetService
27181
+ } = servicesManager.services;
26429
27182
  const {
26430
27183
  displaySetInstanceUIDs,
26431
27184
  viewportOptions
26432
27185
  } = viewport;
27186
+
27187
+ // Match keys used by updateStoredSegmentationPresentation (referenced volume only).
27188
+ // Including overlay UIDs (e.g. SEG) produced ids like "MR&SEG" while the store
27189
+ // entry is under "MR", so hydrated segmentations never applied and viewports could mis-render.
27190
+ const nonOverlayUIDs = displaySetInstanceUIDs.filter(uid => {
27191
+ const ds = displaySetService.getDisplaySetByUID(uid);
27192
+ return ds && !ds.isOverlayDisplaySet;
27193
+ });
27194
+ if (!nonOverlayUIDs.length) {
27195
+ return;
27196
+ }
26433
27197
  let orientation = viewportOptions.orientation;
26434
27198
  if (!orientation) {
26435
27199
  // Calculate orientation from the viewport sample image
26436
- const displaySet = servicesManager.services.displaySetService.getDisplaySetByUID(displaySetInstanceUIDs[0]);
26437
- const sampleImage = displaySet.images?.[0];
27200
+ const displaySet = displaySetService.getDisplaySetByUID(nonOverlayUIDs[0]);
27201
+ const sampleImage = displaySet?.images?.[0];
26438
27202
  const imageOrientationPatient = sampleImage?.ImageOrientationPatient;
26439
27203
  orientation = getViewportOrientationFromImageOrientationPatient(imageOrientationPatient);
26440
27204
  }
26441
27205
  const segmentationPresentationArr = [];
26442
- segmentationPresentationArr.push(...displaySetInstanceUIDs);
27206
+ segmentationPresentationArr.push(...nonOverlayUIDs);
26443
27207
 
26444
27208
  // Uncomment if unique indexing is needed
26445
27209
  // addUniqueIndex(
@@ -26520,10 +27284,10 @@ const useSegmentationPresentationStore = (0,esm/* create */.vt)()(DEBUG_STORE ?
26520
27284
  name: 'Segmentation Presentation Store'
26521
27285
  }) : createSegmentationPresentationStore);
26522
27286
 
26523
- /***/ }),
27287
+ /***/ },
26524
27288
 
26525
- /***/ 68578:
26526
- /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
27289
+ /***/ 68578
27290
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
26527
27291
 
26528
27292
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
26529
27293
  /* harmony export */ U: () => (/* binding */ useSynchronizersStore)
@@ -26584,6 +27348,32 @@ const useSynchronizersStore = (0,zustand__WEBPACK_IMPORTED_MODULE_0__/* .create
26584
27348
  name: 'SynchronizersStore'
26585
27349
  }) : createSynchronizersStore);
26586
27350
 
26587
- /***/ })
27351
+ /***/ },
27352
+
27353
+ /***/ 67142
27354
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
27355
+
27356
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
27357
+ /* harmony export */ V: () => (/* binding */ getDataIdForViewport)
27358
+ /* harmony export */ });
27359
+ /**
27360
+ * Resolves the data ID (e.g. volumeId) for a viewport and display set.
27361
+ * For viewports with multiple volumes/actors, returns the id that matches the display set; otherwise undefined.
27362
+ * Use this to call viewport.getProperties(dataId) in a viewport-type-agnostic way.
27363
+ *
27364
+ * @param viewport - Viewport instance (stack, volume, or future types with optional getAllVolumeIds)
27365
+ * @param displaySetInstanceUID - Display set instance UID to match
27366
+ * @returns volumeId (or equivalent) for multi-actor viewports, undefined for single-actor
27367
+ */
27368
+ function getDataIdForViewport(viewport, displaySetInstanceUID) {
27369
+ const vp = viewport;
27370
+ if (typeof vp.getAllVolumeIds !== 'function') {
27371
+ return undefined;
27372
+ }
27373
+ const volumeIds = vp.getAllVolumeIds() || [];
27374
+ return volumeIds.length > 0 ? volumeIds.find(id => id.includes(displaySetInstanceUID)) ?? undefined : undefined;
27375
+ }
27376
+
27377
+ /***/ }
26588
27378
 
26589
27379
  }]);