diff --git a/bun.lock b/bun.lock
index 8c7f794fc..1473179ba 100644
--- a/bun.lock
+++ b/bun.lock
@@ -33,7 +33,7 @@
},
"addOns/externals/devDependencies": {
"name": "@externals/devDependencies",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@kitware/vtk.js": "32.12.0",
@@ -120,22 +120,22 @@
},
"addOns/externals/dicom-microscopy-viewer": {
"name": "@externals/dicom-microscopy-viewer",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"dicom-microscopy-viewer": "^0.46.1",
},
},
"extensions/cornerstone": {
"name": "@ohif/extension-cornerstone",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^3.10.1",
- "@cornerstonejs/ai": "^3.10.1",
- "@cornerstonejs/core": "^3.10.1",
- "@cornerstonejs/labelmap-interpolation": "^3.10.1",
- "@cornerstonejs/polymorphic-segmentation": "^3.10.1",
- "@cornerstonejs/tools": "^3.10.1",
+ "@cornerstonejs/adapters": "^3.11.6",
+ "@cornerstonejs/ai": "^3.11.6",
+ "@cornerstonejs/core": "^3.11.6",
+ "@cornerstonejs/labelmap-interpolation": "^3.11.6",
+ "@cornerstonejs/polymorphic-segmentation": "^3.11.6",
+ "@cornerstonejs/tools": "^3.11.6",
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
"@kitware/vtk.js": "32.12.0",
"html2canvas": "^1.4.1",
@@ -152,9 +152,9 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
- "@cornerstonejs/dicom-image-loader": "^3.10.1",
- "@ohif/core": "3.10.0",
- "@ohif/ui": "3.10.0",
+ "@cornerstonejs/dicom-image-loader": "^3.11.6",
+ "@ohif/core": "3.10.1",
+ "@ohif/ui": "3.10.1",
"dcmjs": "*",
"dicom-parser": "^1.8.21",
"hammerjs": "^2.0.8",
@@ -166,19 +166,19 @@
},
"extensions/cornerstone-dicom-pmap": {
"name": "@ohif/extension-cornerstone-dicom-pmap",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^3.10.1",
- "@cornerstonejs/core": "^3.10.1",
+ "@cornerstonejs/adapters": "^3.11.6",
+ "@cornerstonejs/core": "^3.11.6",
"@kitware/vtk.js": "32.12.0",
"react-color": "^2.19.3",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/extension-cornerstone": "3.10.0",
- "@ohif/extension-default": "3.10.0",
- "@ohif/i18n": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/extension-cornerstone": "3.10.1",
+ "@ohif/extension-default": "3.10.1",
+ "@ohif/i18n": "3.10.1",
"prop-types": "^15.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -189,16 +189,16 @@
},
"extensions/cornerstone-dicom-rt": {
"name": "@ohif/extension-cornerstone-dicom-rt",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"react-color": "^2.19.3",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/extension-cornerstone": "3.10.0",
- "@ohif/extension-default": "3.10.0",
- "@ohif/i18n": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/extension-cornerstone": "3.10.1",
+ "@ohif/extension-default": "3.10.1",
+ "@ohif/i18n": "3.10.1",
"prop-types": "^15.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -209,19 +209,19 @@
},
"extensions/cornerstone-dicom-seg": {
"name": "@ohif/extension-cornerstone-dicom-seg",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^3.10.1",
- "@cornerstonejs/core": "^3.10.1",
+ "@cornerstonejs/adapters": "^3.11.6",
+ "@cornerstonejs/core": "^3.11.6",
"@kitware/vtk.js": "32.12.0",
"react-color": "^2.19.3",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/extension-cornerstone": "3.10.0",
- "@ohif/extension-default": "3.10.0",
- "@ohif/i18n": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/extension-cornerstone": "3.10.1",
+ "@ohif/extension-default": "3.10.1",
+ "@ohif/i18n": "3.10.1",
"prop-types": "^15.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -232,19 +232,19 @@
},
"extensions/cornerstone-dicom-sr": {
"name": "@ohif/extension-cornerstone-dicom-sr",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^3.10.1",
- "@cornerstonejs/core": "^3.10.1",
- "@cornerstonejs/tools": "^3.10.1",
+ "@cornerstonejs/adapters": "^3.11.6",
+ "@cornerstonejs/core": "^3.11.6",
+ "@cornerstonejs/tools": "^3.11.6",
"classnames": "^2.3.2",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/extension-cornerstone": "3.10.0",
- "@ohif/extension-measurement-tracking": "3.10.0",
- "@ohif/ui": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/extension-cornerstone": "3.10.1",
+ "@ohif/extension-measurement-tracking": "3.10.1",
+ "@ohif/ui": "3.10.1",
"dcmjs": "*",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
@@ -254,19 +254,19 @@
},
"extensions/cornerstone-dynamic-volume": {
"name": "@ohif/extension-cornerstone-dynamic-volume",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/core": "^3.10.1",
- "@cornerstonejs/tools": "^3.10.1",
+ "@cornerstonejs/core": "^3.11.6",
+ "@cornerstonejs/tools": "^3.11.6",
"classnames": "^2.3.2",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/extension-cornerstone": "3.10.0",
- "@ohif/extension-default": "3.10.0",
- "@ohif/i18n": "3.10.0",
- "@ohif/ui": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/extension-cornerstone": "3.10.1",
+ "@ohif/extension-default": "3.10.1",
+ "@ohif/i18n": "3.10.1",
+ "@ohif/ui": "3.10.1",
"dcmjs": "*",
"dicom-parser": "^1.8.21",
"hammerjs": "^2.0.8",
@@ -276,7 +276,7 @@
},
"extensions/default": {
"name": "@ohif/extension-default",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/calculate-suv": "^1.1.0",
@@ -284,8 +284,8 @@
"lodash.uniqby": "^4.7.0",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/i18n": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/i18n": "3.10.1",
"dcmjs": "*",
"dicomweb-client": "^0.10.4",
"prop-types": "^15.6.2",
@@ -299,7 +299,7 @@
},
"extensions/dicom-microscopy": {
"name": "@ohif/extension-dicom-microscopy",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/codec-charls": "^1.2.3",
@@ -310,10 +310,10 @@
"mathjs": "^12.4.2",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/extension-default": "3.10.0",
- "@ohif/i18n": "3.10.0",
- "@ohif/ui": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/extension-default": "3.10.1",
+ "@ohif/i18n": "3.10.1",
+ "@ohif/ui": "3.10.1",
"prop-types": "^15.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -324,14 +324,14 @@
},
"extensions/dicom-pdf": {
"name": "@ohif/extension-dicom-pdf",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"classnames": "^2.3.2",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/ui": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/ui": "3.10.1",
"dcmjs": "*",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
@@ -341,14 +341,14 @@
},
"extensions/dicom-video": {
"name": "@ohif/extension-dicom-video",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"classnames": "^2.3.2",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/ui": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/ui": "3.10.1",
"dcmjs": "*",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
@@ -358,20 +358,20 @@
},
"extensions/measurement-tracking": {
"name": "@ohif/extension-measurement-tracking",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@ohif/ui": "3.10.0",
+ "@ohif/ui": "3.10.1",
"@xstate/react": "^3.2.2",
"xstate": "^4.10.0",
},
"peerDependencies": {
- "@cornerstonejs/core": "^3.10.1",
- "@cornerstonejs/tools": "^3.10.1",
- "@ohif/core": "3.10.0",
- "@ohif/extension-cornerstone-dicom-sr": "3.10.0",
- "@ohif/extension-default": "3.10.0",
- "@ohif/ui": "3.10.0",
+ "@cornerstonejs/core": "^3.11.6",
+ "@cornerstonejs/tools": "^3.11.6",
+ "@ohif/core": "3.10.1",
+ "@ohif/extension-cornerstone-dicom-sr": "3.10.1",
+ "@ohif/extension-default": "3.10.1",
+ "@ohif/ui": "3.10.1",
"classnames": "^2.3.2",
"dcmjs": "*",
"lodash.debounce": "^4.0.8",
@@ -384,14 +384,14 @@
},
"extensions/test-extension": {
"name": "@ohif/extension-test",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"classnames": "^2.3.2",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/ui": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/ui": "3.10.1",
"dcmjs": "0.38.0",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
@@ -401,7 +401,7 @@
},
"extensions/tmtv": {
"name": "@ohif/extension-tmtv",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"classnames": "^2.3.2",
@@ -418,7 +418,7 @@
},
"modes/basic-dev-mode": {
"name": "@ohif/mode-basic-dev-mode",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next": "^17.0.3",
@@ -428,17 +428,17 @@
"webpack-merge": "^5.7.3",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/extension-cornerstone": "3.10.0",
- "@ohif/extension-cornerstone-dicom-sr": "3.10.0",
- "@ohif/extension-default": "3.10.0",
- "@ohif/extension-dicom-pdf": "3.10.0",
- "@ohif/extension-dicom-video": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/extension-cornerstone": "3.10.1",
+ "@ohif/extension-cornerstone-dicom-sr": "3.10.1",
+ "@ohif/extension-default": "3.10.1",
+ "@ohif/extension-dicom-pdf": "3.10.1",
+ "@ohif/extension-dicom-video": "3.10.1",
},
},
"modes/basic-test-mode": {
"name": "@ohif/mode-test",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next": "^17.0.3",
@@ -448,19 +448,19 @@
"webpack-merge": "^5.7.3",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/extension-cornerstone": "3.10.0",
- "@ohif/extension-cornerstone-dicom-sr": "3.10.0",
- "@ohif/extension-default": "3.10.0",
- "@ohif/extension-dicom-pdf": "3.10.0",
- "@ohif/extension-dicom-video": "3.10.0",
- "@ohif/extension-measurement-tracking": "3.10.0",
- "@ohif/extension-test": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/extension-cornerstone": "3.10.1",
+ "@ohif/extension-cornerstone-dicom-sr": "3.10.1",
+ "@ohif/extension-default": "3.10.1",
+ "@ohif/extension-dicom-pdf": "3.10.1",
+ "@ohif/extension-dicom-video": "3.10.1",
+ "@ohif/extension-measurement-tracking": "3.10.1",
+ "@ohif/extension-test": "3.10.1",
},
},
"modes/longitudinal": {
"name": "@ohif/mode-longitudinal",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next": "^17.0.3",
@@ -470,32 +470,32 @@
"webpack-merge": "^5.7.3",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/extension-cornerstone": "3.10.0",
- "@ohif/extension-cornerstone-dicom-rt": "3.10.0",
- "@ohif/extension-cornerstone-dicom-seg": "3.10.0",
- "@ohif/extension-cornerstone-dicom-sr": "3.10.0",
- "@ohif/extension-default": "3.10.0",
- "@ohif/extension-dicom-pdf": "3.10.0",
- "@ohif/extension-dicom-video": "3.10.0",
- "@ohif/extension-measurement-tracking": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/extension-cornerstone": "3.10.1",
+ "@ohif/extension-cornerstone-dicom-rt": "3.10.1",
+ "@ohif/extension-cornerstone-dicom-seg": "3.10.1",
+ "@ohif/extension-cornerstone-dicom-sr": "3.10.1",
+ "@ohif/extension-default": "3.10.1",
+ "@ohif/extension-dicom-pdf": "3.10.1",
+ "@ohif/extension-dicom-video": "3.10.1",
+ "@ohif/extension-measurement-tracking": "3.10.1",
},
},
"modes/microscopy": {
"name": "@ohif/mode-microscopy",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next": "^17.0.3",
},
"peerDependencies": {
- "@ohif/core": "3.10.0",
- "@ohif/extension-dicom-microscopy": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/extension-dicom-microscopy": "3.10.1",
},
},
"modes/preclinical-4d": {
"name": "@ohif/mode-preclinical-4d",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
},
@@ -514,7 +514,7 @@
},
"modes/segmentation": {
"name": "@ohif/mode-segmentation",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next": "^17.0.3",
@@ -558,7 +558,7 @@
},
"modes/tmtv": {
"name": "@ohif/mode-tmtv",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next": "^17.0.3",
@@ -579,32 +579,32 @@
},
"platform/app": {
"name": "@ohif/app",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/codec-charls": "^1.2.3",
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
- "@cornerstonejs/dicom-image-loader": "^3.10.1",
+ "@cornerstonejs/dicom-image-loader": "^3.11.6",
"@emotion/serialize": "^1.1.3",
- "@ohif/core": "3.10.0",
- "@ohif/extension-cornerstone": "3.10.0",
- "@ohif/extension-cornerstone-dicom-rt": "3.10.0",
- "@ohif/extension-cornerstone-dicom-seg": "3.10.0",
- "@ohif/extension-cornerstone-dicom-sr": "3.10.0",
- "@ohif/extension-default": "3.10.0",
- "@ohif/extension-dicom-microscopy": "3.10.0",
- "@ohif/extension-dicom-pdf": "3.10.0",
- "@ohif/extension-dicom-video": "3.10.0",
- "@ohif/extension-test": "3.10.0",
- "@ohif/i18n": "3.10.0",
- "@ohif/mode-basic-dev-mode": "3.10.0",
- "@ohif/mode-longitudinal": "3.10.0",
- "@ohif/mode-microscopy": "3.10.0",
- "@ohif/mode-test": "3.10.0",
- "@ohif/ui": "3.10.0",
- "@ohif/ui-next": "3.10.0",
+ "@ohif/core": "3.10.1",
+ "@ohif/extension-cornerstone": "3.10.1",
+ "@ohif/extension-cornerstone-dicom-rt": "3.10.1",
+ "@ohif/extension-cornerstone-dicom-seg": "3.10.1",
+ "@ohif/extension-cornerstone-dicom-sr": "3.10.1",
+ "@ohif/extension-default": "3.10.1",
+ "@ohif/extension-dicom-microscopy": "3.10.1",
+ "@ohif/extension-dicom-pdf": "3.10.1",
+ "@ohif/extension-dicom-video": "3.10.1",
+ "@ohif/extension-test": "3.10.1",
+ "@ohif/i18n": "3.10.1",
+ "@ohif/mode-basic-dev-mode": "3.10.1",
+ "@ohif/mode-longitudinal": "3.10.1",
+ "@ohif/mode-microscopy": "3.10.1",
+ "@ohif/mode-test": "3.10.1",
+ "@ohif/ui": "3.10.1",
+ "@ohif/ui-next": "3.10.1",
"@svgr/webpack": "^8.1.0",
"@types/react": "^18.3.3",
"classnames": "^2.3.2",
@@ -645,7 +645,7 @@
},
"platform/cli": {
"name": "@ohif/cli",
- "version": "3.10.0",
+ "version": "3.10.1",
"bin": {
"ohif-cli": "src/index.js",
},
@@ -669,7 +669,7 @@
},
"platform/core": {
"name": "@ohif/core",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"dcmjs": "*",
@@ -694,16 +694,16 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
- "@cornerstonejs/core": "^3.10.1",
- "@cornerstonejs/dicom-image-loader": "^3.10.1",
- "@ohif/ui": "3.10.0",
+ "@cornerstonejs/core": "^3.11.6",
+ "@cornerstonejs/dicom-image-loader": "^3.11.6",
+ "@ohif/ui": "3.10.1",
"cornerstone-math": "0.1.9",
"dicom-parser": "^1.8.21",
},
},
"platform/i18n": {
"name": "@ohif/i18n",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@babel/runtime": "^7.20.13",
"i18next-locize-backend": "^2.0.0",
@@ -728,7 +728,7 @@
},
"platform/ui": {
"name": "@ohif/ui",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@testing-library/react": "^13.1.0",
"browser-detect": "^0.2.28",
@@ -788,7 +788,7 @@
},
"platform/ui-next": {
"name": "@ohif/ui-next",
- "version": "3.10.0",
+ "version": "3.10.1",
"dependencies": {
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-checkbox": "^1.1.1",
@@ -1099,9 +1099,9 @@
"@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="],
- "@cornerstonejs/adapters": ["@cornerstonejs/adapters@3.10.1", "", { "dependencies": { "@babel/runtime-corejs2": "^7.17.8", "buffer": "^6.0.3", "dcmjs": "^0.29.8", "gl-matrix": "^3.4.3", "ndarray": "^1.0.19" }, "peerDependencies": { "@cornerstonejs/core": "^3.10.1", "@cornerstonejs/tools": "^3.10.1" } }, "sha512-uKic+EyF8SguEM9ahZK96r1oWetjO2GCCEM1oO15wl71iHVXFbW8lUYD1uqXtNW4fZet3X9rEke/te1zNVafxA=="],
+ "@cornerstonejs/adapters": ["@cornerstonejs/adapters@3.11.7", "", { "dependencies": { "@babel/runtime-corejs2": "^7.17.8", "buffer": "^6.0.3", "dcmjs": "^0.29.8", "gl-matrix": "^3.4.3", "ndarray": "^1.0.19" }, "peerDependencies": { "@cornerstonejs/core": "^3.11.7", "@cornerstonejs/tools": "^3.11.7" } }, "sha512-21ewTLHbwHcoALMqXQMuoZTHnAJ4EWuMtLvCHXL43BfVerzDXjLTuPuOeyg2QP4V4krevKH/oyOF04deHWHpsA=="],
- "@cornerstonejs/ai": ["@cornerstonejs/ai@3.10.1", "", { "dependencies": { "@babel/runtime-corejs2": "^7.17.8", "buffer": "^6.0.3", "dcmjs": "^0.29.8", "gl-matrix": "^3.4.3", "lodash.clonedeep": "^4.5.0", "ndarray": "^1.0.19", "onnxruntime-common": "1.17.1", "onnxruntime-web": "1.17.1" }, "peerDependencies": { "@cornerstonejs/core": "^3.10.1", "@cornerstonejs/tools": "^3.10.1" } }, "sha512-yEALz91yZCJdrRhk2R5Vn1yFefy07FL9g3ff4BgL19nTnGIu/3yUkVR9PY5js8T6E56GCFWxs9meWdeiCuRAcg=="],
+ "@cornerstonejs/ai": ["@cornerstonejs/ai@3.11.7", "", { "dependencies": { "@babel/runtime-corejs2": "^7.17.8", "buffer": "^6.0.3", "dcmjs": "^0.29.8", "gl-matrix": "^3.4.3", "lodash.clonedeep": "^4.5.0", "ndarray": "^1.0.19", "onnxruntime-common": "1.17.1", "onnxruntime-web": "1.17.1" }, "peerDependencies": { "@cornerstonejs/core": "^3.11.7", "@cornerstonejs/tools": "^3.11.7" } }, "sha512-oVJzZNwyjh6UTzvJanYIDjXKcxvT4u9cOLuvzt3pBGbdaeq5Xn/MlxMi90EBvLIuUGgxT/u9MDu4xpBJLVJalw=="],
"@cornerstonejs/calculate-suv": ["@cornerstonejs/calculate-suv@1.1.0", "", {}, "sha512-Q9XraiDJif9aMFArD2iEuDO/HXbcRVCqB7KfaHgDrdTTjgDFovS91Psbdim7crypRSvE6dh/+HKeFNHdvNkA6w=="],
@@ -1113,15 +1113,15 @@
"@cornerstonejs/codec-openjph": ["@cornerstonejs/codec-openjph@2.4.7", "", {}, "sha512-qvP4q4JDib7mi9r7LqKOwqz7YZ8gjtDX4ZCezeYf8+eb7MBXCz5uXAMeVF3yz9Axw4XiIMdB/pqXkm8tqCl13w=="],
- "@cornerstonejs/core": ["@cornerstonejs/core@3.10.1", "", { "dependencies": { "@kitware/vtk.js": "32.12.1", "comlink": "^4.4.1", "gl-matrix": "^3.4.3", "loglevel": "^1.9.2" } }, "sha512-DMlE98A5Oa81G9K5J4ZD1917SbJYteh+j4oJTY60KHAI7p0GoGQRV/ZcxaL+FS8mWng/qejVss9w4fLoLJcErg=="],
+ "@cornerstonejs/core": ["@cornerstonejs/core@3.11.7", "", { "dependencies": { "@kitware/vtk.js": "32.12.1", "comlink": "^4.4.1", "gl-matrix": "^3.4.3", "loglevel": "^1.9.2" } }, "sha512-mjWC603R3hGE8NuYxU89nt3TsRoROci5MC0FIIpLNrdPedizdz8F5PYzUCxdHREIrfy4nDyvPmfK1tdGDqvDtQ=="],
- "@cornerstonejs/dicom-image-loader": ["@cornerstonejs/dicom-image-loader@3.10.1", "", { "dependencies": { "@cornerstonejs/codec-charls": "^1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.5", "comlink": "^4.4.1", "dicom-parser": "^1.8.9", "jpeg-lossless-decoder-js": "^2.1.0", "pako": "^2.0.4", "uuid": "^9.0.0" }, "peerDependencies": { "@cornerstonejs/core": "^3.10.1" } }, "sha512-w/55TI05KV3mP/RtL0ds+jfh19KlBZajJDgXystV489duvqJJUH27C+eZyrGFqbA3a+EDv4GYQa4jw/MDkGGqA=="],
+ "@cornerstonejs/dicom-image-loader": ["@cornerstonejs/dicom-image-loader@3.11.7", "", { "dependencies": { "@cornerstonejs/codec-charls": "^1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.5", "comlink": "^4.4.1", "dicom-parser": "^1.8.9", "jpeg-lossless-decoder-js": "^2.1.0", "pako": "^2.0.4", "uuid": "^9.0.0" }, "peerDependencies": { "@cornerstonejs/core": "^3.11.7" } }, "sha512-UzFD/uDnNN3VNuuIq+oWLXRP8Vuxy/n3ZSL4ZxFWuAyQmgM/9CGlJhKmXrmyGXzFnHFnI2hDTuvg54/A2NqMBg=="],
- "@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@3.10.1", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "^3.10.1", "@cornerstonejs/tools": "^3.10.1", "@kitware/vtk.js": "32.12.1" } }, "sha512-hcAR83rWA6lBYKXgYOWR/3ofhV3mqh4RRLbj8QnS9e//qtmF+UxoZ4I0125ihPkCjFqFOWuYgn42BnRZNhvjxA=="],
+ "@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@3.11.7", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "^3.11.7", "@cornerstonejs/tools": "^3.11.7", "@kitware/vtk.js": "32.12.1" } }, "sha512-y7+E98bLhRzaIN9v7CtUS+Pqf9yJYI4vukz7gd2p2524/5iPvTqz1v3nfV3jUCZHU+FS27zcN0LNtvlbnPfZRw=="],
- "@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@3.10.1", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "^3.10.1", "@cornerstonejs/tools": "^3.10.1", "@kitware/vtk.js": "32.12.1" } }, "sha512-HErrnX7kDs5CVfcuPW5pyWw3lJovqqqKiIfPJdEmZNEM4CVSv9pNpYIbSX8uv42zWq+biQFkP4TwYw12q/r7Og=="],
+ "@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@3.11.7", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "^3.11.7", "@cornerstonejs/tools": "^3.11.7", "@kitware/vtk.js": "32.12.1" } }, "sha512-ZZysECCUlEp3L33OJZ6ObGq35OyFTa0fo9+D+Tfv32HcRbrfTRTlcrWU7uLGNo3WBolX/zk0pCubRQjT8TbQiQ=="],
- "@cornerstonejs/tools": ["@cornerstonejs/tools@3.10.1", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "^4.4.1", "lodash.get": "^4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "^3.10.1", "@kitware/vtk.js": "32.12.1", "@types/d3-array": "^3.0.4", "@types/d3-interpolate": "^3.0.1", "d3-array": "^3.2.3", "d3-interpolate": "^3.0.1", "gl-matrix": "^3.4.3" } }, "sha512-L1z8JiDwgbSwz040BSV5KBMMEe7IZyS4xYVhbn6cpfzCxSfevp53EkUZMhLl4bFcYyRPgXu5/KJO6f9wGFhEvA=="],
+ "@cornerstonejs/tools": ["@cornerstonejs/tools@3.11.7", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "^4.4.1", "lodash.get": "^4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "^3.11.7", "@kitware/vtk.js": "32.12.1", "@types/d3-array": "^3.0.4", "@types/d3-interpolate": "^3.0.1", "d3-array": "^3.2.3", "d3-interpolate": "^3.0.1", "gl-matrix": "^3.4.3" } }, "sha512-Lo2/mp2J/PDqA/xZu/krQGryCXaDuqj77F9w/wWCjme3NOqrZfhiq/FYEPRsze8ejhXo5A9ChgVQcEeqNyhPkQ=="],
"@csstools/postcss-cascade-layers": ["@csstools/postcss-cascade-layers@1.1.1", "", { "dependencies": { "@csstools/selector-specificity": "^2.0.2", "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.2" } }, "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA=="],
diff --git a/extensions/cornerstone-dicom-pmap/package.json b/extensions/cornerstone-dicom-pmap/package.json
index f2743a0e2..e878c01d3 100644
--- a/extensions/cornerstone-dicom-pmap/package.json
+++ b/extensions/cornerstone-dicom-pmap/package.json
@@ -46,8 +46,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^3.10.1",
- "@cornerstonejs/core": "^3.10.1",
+ "@cornerstonejs/adapters": "^3.11.6",
+ "@cornerstonejs/core": "^3.11.6",
"@kitware/vtk.js": "32.12.0",
"react-color": "^2.19.3"
}
diff --git a/extensions/cornerstone-dicom-seg/package.json b/extensions/cornerstone-dicom-seg/package.json
index dd295e61c..9e6e22712 100644
--- a/extensions/cornerstone-dicom-seg/package.json
+++ b/extensions/cornerstone-dicom-seg/package.json
@@ -46,8 +46,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^3.10.1",
- "@cornerstonejs/core": "^3.10.1",
+ "@cornerstonejs/adapters": "^3.11.6",
+ "@cornerstonejs/core": "^3.11.6",
"@kitware/vtk.js": "32.12.0",
"react-color": "^2.19.3"
}
diff --git a/extensions/cornerstone-dicom-sr/package.json b/extensions/cornerstone-dicom-sr/package.json
index c0921b9d8..e14c57a27 100644
--- a/extensions/cornerstone-dicom-sr/package.json
+++ b/extensions/cornerstone-dicom-sr/package.json
@@ -46,9 +46,9 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^3.10.1",
- "@cornerstonejs/core": "^3.10.1",
- "@cornerstonejs/tools": "^3.10.1",
+ "@cornerstonejs/adapters": "^3.11.6",
+ "@cornerstonejs/core": "^3.11.6",
+ "@cornerstonejs/tools": "^3.11.6",
"classnames": "^2.3.2"
}
}
diff --git a/extensions/cornerstone-dynamic-volume/package.json b/extensions/cornerstone-dynamic-volume/package.json
index c1ef9363a..028c00882 100644
--- a/extensions/cornerstone-dynamic-volume/package.json
+++ b/extensions/cornerstone-dynamic-volume/package.json
@@ -42,8 +42,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/core": "^3.10.1",
- "@cornerstonejs/tools": "^3.10.1",
+ "@cornerstonejs/core": "^3.11.6",
+ "@cornerstonejs/tools": "^3.11.6",
"classnames": "^2.3.2"
}
}
diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json
index 255cfc40c..363cbf17f 100644
--- a/extensions/cornerstone/package.json
+++ b/extensions/cornerstone/package.json
@@ -38,7 +38,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
- "@cornerstonejs/dicom-image-loader": "^3.10.1",
+ "@cornerstonejs/dicom-image-loader": "^3.11.6",
"@ohif/core": "3.10.1",
"@ohif/ui": "3.10.1",
"dcmjs": "*",
@@ -51,12 +51,12 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
- "@cornerstonejs/adapters": "^3.10.1",
- "@cornerstonejs/ai": "^3.10.1",
- "@cornerstonejs/core": "^3.10.1",
- "@cornerstonejs/labelmap-interpolation": "^3.10.1",
- "@cornerstonejs/polymorphic-segmentation": "^3.10.1",
- "@cornerstonejs/tools": "^3.10.1",
+ "@cornerstonejs/adapters": "^3.11.6",
+ "@cornerstonejs/ai": "^3.11.6",
+ "@cornerstonejs/core": "^3.11.6",
+ "@cornerstonejs/labelmap-interpolation": "^3.11.6",
+ "@cornerstonejs/polymorphic-segmentation": "^3.11.6",
+ "@cornerstonejs/tools": "^3.11.6",
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
"@kitware/vtk.js": "32.12.0",
"html2canvas": "^1.4.1",
diff --git a/extensions/cornerstone/src/hps/fourUp.ts b/extensions/cornerstone/src/hps/fourUp.ts
index 9a72b049d..11facdf9a 100644
--- a/extensions/cornerstone/src/hps/fourUp.ts
+++ b/extensions/cornerstone/src/hps/fourUp.ts
@@ -64,7 +64,7 @@ export const fourUp = {
customViewportProps: {
hideOverlays: true,
},
- syncGroups: [VOI_SYNC_GROUP, HYDRATE_SEG_SYNC_GROUP],
+ syncGroups: [HYDRATE_SEG_SYNC_GROUP],
},
displaySets: [
{
diff --git a/extensions/cornerstone/src/hps/mprAnd3DVolumeViewport.ts b/extensions/cornerstone/src/hps/mprAnd3DVolumeViewport.ts
index 002600f27..c26a6d5ee 100644
--- a/extensions/cornerstone/src/hps/mprAnd3DVolumeViewport.ts
+++ b/extensions/cornerstone/src/hps/mprAnd3DVolumeViewport.ts
@@ -71,7 +71,7 @@ export const mprAnd3DVolumeViewport = {
customViewportProps: {
hideOverlays: true,
},
- syncGroups: [VOI_SYNC_GROUP, HYDRATE_SEG_SYNC_GROUP],
+ syncGroups: [HYDRATE_SEG_SYNC_GROUP],
},
displaySets: [
{
diff --git a/extensions/cornerstone/src/hps/only3D.ts b/extensions/cornerstone/src/hps/only3D.ts
index 6e5782ebe..36f3534ef 100644
--- a/extensions/cornerstone/src/hps/only3D.ts
+++ b/extensions/cornerstone/src/hps/only3D.ts
@@ -47,7 +47,7 @@ export const only3D = {
orientation: 'coronal',
customViewportProps: {
hideOverlays: true,
- syncGroups: [VOI_SYNC_GROUP, HYDRATE_SEG_SYNC_GROUP],
+ syncGroups: [HYDRATE_SEG_SYNC_GROUP],
},
},
displaySets: [
diff --git a/extensions/cornerstone/src/init.tsx b/extensions/cornerstone/src/init.tsx
index e116c6134..be882bdc2 100644
--- a/extensions/cornerstone/src/init.tsx
+++ b/extensions/cornerstone/src/init.tsx
@@ -43,6 +43,7 @@ import {
setupSegmentationDataModifiedHandler,
setupSegmentationModifiedHandler,
} from './utils/segmentationHandlers';
+import { initializeWebWorkerProgressHandler } from './utils/initWebWorkerProgressHandler';
const { registerColormap } = csUtilities.colormap;
@@ -289,10 +290,16 @@ export default async function init({
eventTarget.addEventListenerDebounced(
EVENTS.ERROR_EVENT,
({ detail }) => {
+ // Create a stable ID for deduplication based on error type and message
+ const errorId = `cornerstone-error-${detail.type}-${detail.message.substring(0, 50)}`;
+
uiNotificationService.show({
title: detail.type,
message: detail.message,
type: 'error',
+ id: errorId,
+ allowDuplicates: false, // Prevent duplicate error notifications
+ deduplicationInterval: 30000, // 30 seconds deduplication window
});
},
100
@@ -309,40 +316,6 @@ export default async function init({
return { unsubscriptions };
}
-function initializeWebWorkerProgressHandler(uiNotificationService) {
- const activeToasts = new Map();
-
- // eventTarget.addEventListener(EVENTS.WEB_WORKER_PROGRESS, ({ detail }) => {
- // const { progress, type, id } = detail;
-
- // const cacheKey = `${type}-${id}`;
- // if (progress === 0 && !activeToasts.has(cacheKey)) {
- // const progressPromise = new Promise((resolve, reject) => {
- // activeToasts.set(cacheKey, { resolve, reject });
- // });
-
- // uiNotificationService.show({
- // id: cacheKey,
- // title: `${type}`,
- // message: `${type}: ${progress}%`,
- // autoClose: false,
- // promise: progressPromise,
- // promiseMessages: {
- // loading: `Computing...`,
- // success: `Completed successfully`,
- // error: 'Web Worker failed',
- // },
- // });
- // } else {
- // if (progress === 100) {
- // const { resolve } = activeToasts.get(cacheKey);
- // resolve({ progress, type });
- // activeToasts.delete(cacheKey);
- // }
- // }
- // });
-}
-
/**
* Creates a wrapped image load strategy with metadata handling
* @param strategyFn - The image loading strategy function to wrap
diff --git a/extensions/cornerstone/src/initCornerstoneTools.js b/extensions/cornerstone/src/initCornerstoneTools.js
index d6627408b..2837496bb 100644
--- a/extensions/cornerstone/src/initCornerstoneTools.js
+++ b/extensions/cornerstone/src/initCornerstoneTools.js
@@ -48,6 +48,8 @@ import ImageOverlayViewerTool from './tools/ImageOverlayViewerTool';
export default function initCornerstoneTools(configuration = {}) {
CrosshairsTool.isAnnotation = false;
+ LabelmapSlicePropagationTool.isAnnotation = false;
+ MarkerLabelmapTool.isAnnotation = false;
ReferenceLinesTool.isAnnotation = false;
AdvancedMagnifyTool.isAnnotation = false;
PlanarFreehandContourSegmentationTool.isAnnotation = false;
diff --git a/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx b/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx
index 2bbbda1dc..3f32c623b 100644
--- a/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx
+++ b/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx
@@ -180,15 +180,14 @@ const CornerstoneViewportDownloadForm = ({
const toolGroup = ToolGroupManager.getToolGroupForViewport(activeViewportId, renderingEngineId);
toolGroup.addViewport(downloadViewportId, renderingEngineId);
- Object.keys(toolGroup.getToolInstances()).forEach(toolName => {
- if (show && toolName !== 'Crosshairs') {
- try {
- toolGroup.setToolEnabled(toolName);
- } catch (error) {
- console.debug('Error enabling tool:', error);
- }
+ const toolInstances = toolGroup.getToolInstances();
+ const toolInstancesArray = Object.values(toolInstances);
+
+ toolInstancesArray.forEach(toolInstance => {
+ if (show && toolInstance.constructor.isAnnotation !== false) {
+ toolGroup.setToolEnabled(toolInstance.toolName);
} else {
- toolGroup.setToolDisabled(toolName);
+ toolGroup.setToolDisabled(toolInstance.toolName);
}
});
};
diff --git a/extensions/cornerstone/src/utils/initWebWorkerProgressHandler.ts b/extensions/cornerstone/src/utils/initWebWorkerProgressHandler.ts
new file mode 100644
index 000000000..a57404fef
--- /dev/null
+++ b/extensions/cornerstone/src/utils/initWebWorkerProgressHandler.ts
@@ -0,0 +1,110 @@
+import { eventTarget, EVENTS } from '@cornerstonejs/core';
+import * as cornerstoneTools from '@cornerstonejs/tools';
+
+/**
+ * Initializes a handler for web worker progress events.
+ * Tracks active worker tasks and shows notifications for their progress.
+ *
+ * @param uiNotificationService - The UI notification service for showing progress notifications
+ */
+export function initializeWebWorkerProgressHandler(uiNotificationService: any) {
+ // Use a single map to track all active worker tasks
+ const activeWorkerTasks = new Map();
+
+ // Create a normalized task key that doesn't include the random ID
+ // This helps us identify and deduplicate the same type of task
+ const getNormalizedTaskKey = (type: string) => {
+ return `worker-task-${type.toLowerCase().replace(/\s+/g, '-')}`;
+ };
+
+ eventTarget.addEventListener(EVENTS.WEB_WORKER_PROGRESS, ({ detail }) => {
+ let normalizedKey: string | undefined;
+ let shouldCleanup = false;
+
+ try {
+ const { progress, type, id } = detail;
+
+ // Skip notifications for compute statistics
+ if (type === cornerstoneTools.Enums.WorkerTypes.COMPUTE_STATISTICS) {
+ return;
+ }
+
+ normalizedKey = getNormalizedTaskKey(type);
+
+ if (progress === 0) {
+ // Check if we're already tracking a task of this type
+ if (!activeWorkerTasks.has(normalizedKey)) {
+ const progressPromise = new Promise((resolve, reject) => {
+ try {
+ activeWorkerTasks.set(normalizedKey, {
+ resolve,
+ reject,
+ originalId: id,
+ type,
+ });
+ } catch (error) {
+ console.error(`Error setting active worker task for type "${type}":`, error);
+ reject(error);
+ throw error; // Re-throw to trigger outer catch and cleanup
+ }
+ });
+
+ try {
+ uiNotificationService.show({
+ id: normalizedKey, // Use the normalized key as ID for better deduplication
+ title: `${type}`,
+ message: `Computing...`,
+ autoClose: false,
+ allowDuplicates: false,
+ deduplicationInterval: 60000, // 60 seconds - prevent frequent notifications of same type
+ promise: progressPromise,
+ promiseMessages: {
+ loading: `Computing...`,
+ success: `Completed successfully`,
+ error: 'Web Worker failed',
+ },
+ });
+ } catch (error) {
+ console.error(`Error showing web worker notification for type "${type}":`, error);
+ shouldCleanup = true;
+ throw error;
+ }
+ } else {
+ // Already tracking this type of task, just let it continue
+ console.debug(`Already tracking a "${type}" task, skipping duplicate notification`);
+ }
+ }
+ // Task completed
+ else if (progress === 100) {
+ // Check if we have this task type in our tracking map
+ const taskData = activeWorkerTasks.get(normalizedKey);
+
+ if (taskData) {
+ // Resolve the promise to update the notification
+ const { resolve } = taskData;
+ resolve({ progress, type });
+
+ // Mark for cleanup
+ shouldCleanup = true;
+
+ console.debug(`Worker task "${type}" completed successfully`);
+ }
+ }
+ } catch (error) {
+ console.error(`Error in web worker progress handler for type "${detail?.type}":`, error);
+ shouldCleanup = true;
+ } finally {
+ // Clean up if needed
+ if (shouldCleanup && normalizedKey) {
+ try {
+ activeWorkerTasks.delete(normalizedKey);
+ } catch (cleanupError) {
+ console.error(
+ `Error cleaning up active worker task for type "${detail?.type}":`,
+ cleanupError
+ );
+ }
+ }
+ }
+ });
+}
diff --git a/extensions/default/src/CustomizableContextMenu/ContextMenuController.tsx b/extensions/default/src/CustomizableContextMenu/ContextMenuController.tsx
index 183fd417d..2827790c0 100644
--- a/extensions/default/src/CustomizableContextMenu/ContextMenuController.tsx
+++ b/extensions/default/src/CustomizableContextMenu/ContextMenuController.tsx
@@ -80,6 +80,7 @@ export default class ContextMenuController {
this.services.uiDialogService.hide('context-menu');
this.services.uiDialogService.show({
id: 'context-menu',
+ showOverlay: false,
defaultPosition: ContextMenuController._getDefaultPosition(
defaultPointsPosition,
event?.detail || event,
diff --git a/extensions/default/src/DicomTagBrowser/DicomTagBrowser.tsx b/extensions/default/src/DicomTagBrowser/DicomTagBrowser.tsx
index d69fc3a1c..f1ee6a889 100644
--- a/extensions/default/src/DicomTagBrowser/DicomTagBrowser.tsx
+++ b/extensions/default/src/DicomTagBrowser/DicomTagBrowser.tsx
@@ -128,7 +128,9 @@ const DicomTagBrowser = ({
- Series
+
+ Series
+
onSelectChange({ value })}
@@ -154,7 +156,7 @@ const DicomTagBrowser = ({
{shouldShowInstanceList && (
-
+
Instance Number ({instanceNumber} of {activeDisplaySet?.images?.length})
)}
-
+
Search metadata
-
+
diff --git a/extensions/default/src/ViewerLayout/constants/panels.ts b/extensions/default/src/ViewerLayout/constants/panels.ts
index 2c6684032..3219e6c51 100644
--- a/extensions/default/src/ViewerLayout/constants/panels.ts
+++ b/extensions/default/src/ViewerLayout/constants/panels.ts
@@ -4,7 +4,7 @@ const collapsedOutsideBorderSize = 4;
const collapsedWidth = 25;
const rightPanelInitialExpandedWidth = 280;
-const leftPanelInitialExpandedWidth = 292;
+const leftPanelInitialExpandedWidth = 282;
const panelGroupDefinition = {
groupId: 'viewerLayoutResizablePanelGroup',
diff --git a/extensions/default/src/index.ts b/extensions/default/src/index.ts
index f11d3d237..133d5878e 100644
--- a/extensions/default/src/index.ts
+++ b/extensions/default/src/index.ts
@@ -37,6 +37,8 @@ import * as utils from './utils';
import { Toolbox } from './utils';
import MoreDropdownMenu from './Components/MoreDropdownMenu';
import requestDisplaySetCreationForStudy from './Panels/requestDisplaySetCreationForStudy';
+import { Toolbar } from './Toolbar/Toolbar';
+
const defaultExtension: Types.Extensions.Extension = {
/**
* Only required property. Should be a unique value across all extensions.
@@ -103,4 +105,5 @@ export {
requestDisplaySetCreationForStudy,
callInputDialog,
createReportDialogPrompt,
+ Toolbar,
};
diff --git a/extensions/measurement-tracking/package.json b/extensions/measurement-tracking/package.json
index ed5fee6d4..22651c1ba 100644
--- a/extensions/measurement-tracking/package.json
+++ b/extensions/measurement-tracking/package.json
@@ -32,8 +32,8 @@
"start": "yarn run dev"
},
"peerDependencies": {
- "@cornerstonejs/core": "^3.10.1",
- "@cornerstonejs/tools": "^3.10.1",
+ "@cornerstonejs/core": "^3.11.6",
+ "@cornerstonejs/tools": "^3.11.6",
"@ohif/core": "3.10.1",
"@ohif/extension-cornerstone-dicom-sr": "3.10.1",
"@ohif/extension-default": "3.10.1",
diff --git a/modes/longitudinal/src/toolbarButtons.ts b/modes/longitudinal/src/toolbarButtons.ts
index 48f38a2d3..14c1e9c10 100644
--- a/modes/longitudinal/src/toolbarButtons.ts
+++ b/modes/longitudinal/src/toolbarButtons.ts
@@ -120,8 +120,8 @@ const toolbarButtons: Button[] = [
tooltip: 'Show Reference Lines',
commands: 'toggleEnabledDisabledToolbar',
listeners: {
- [ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: callbacks('ReferenceLinesTool'),
- [ViewportGridService.EVENTS.VIEWPORTS_READY]: callbacks('ReferenceLinesTool'),
+ [ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: callbacks('ReferenceLines'),
+ [ViewportGridService.EVENTS.VIEWPORTS_READY]: callbacks('ReferenceLines'),
},
evaluate: [
'evaluate.cornerstoneTool.toggle',
diff --git a/package.json b/package.json
index 33b36f9be..bc131261d 100644
--- a/package.json
+++ b/package.json
@@ -56,6 +56,7 @@
"test:e2e": "lerna run test:e2e --stream",
"test:e2e:ci": "TEST_ENV=true npx playwright test",
"test:e2e:ui": "TEST_ENV=true npx playwright test --ui",
+ "test:e2e:update": "TEST_ENV=true npx playwright test --update-snapshots",
"test:e2e:headed": "TEST_ENV=true npx playwright test --headed",
"test:e2e:debug": "TEST_ENV=true npx playwright test --debug",
"test:e2e:dist": "lerna run test:e2e:dist --stream",
diff --git a/platform/app/package.json b/platform/app/package.json
index da67d758a..03fcf1ffe 100644
--- a/platform/app/package.json
+++ b/platform/app/package.json
@@ -53,7 +53,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
- "@cornerstonejs/dicom-image-loader": "^3.10.1",
+ "@cornerstonejs/dicom-image-loader": "^3.11.6",
"@emotion/serialize": "^1.1.3",
"@ohif/core": "3.10.1",
"@ohif/extension-cornerstone": "3.10.1",
diff --git a/platform/core/package.json b/platform/core/package.json
index 0edd94c85..8ea0b8e45 100644
--- a/platform/core/package.json
+++ b/platform/core/package.json
@@ -37,8 +37,8 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
- "@cornerstonejs/core": "^3.10.1",
- "@cornerstonejs/dicom-image-loader": "^3.10.1",
+ "@cornerstonejs/core": "^3.11.6",
+ "@cornerstonejs/dicom-image-loader": "^3.11.6",
"@ohif/ui": "3.10.1",
"cornerstone-math": "0.1.9",
"dicom-parser": "^1.8.21"
diff --git a/platform/core/src/classes/MetadataProvider.ts b/platform/core/src/classes/MetadataProvider.ts
index 7ee88898f..ce55c4f05 100644
--- a/platform/core/src/classes/MetadataProvider.ts
+++ b/platform/core/src/classes/MetadataProvider.ts
@@ -215,7 +215,10 @@ class MetadataProvider {
break;
case WADO_IMAGE_LOADER_TAGS.MODALITY_LUT_MODULE:
const { RescaleIntercept, RescaleSlope } = instance;
- if (RescaleIntercept === undefined || RescaleSlope === undefined) {
+ // Early return if RescaleIntercept or RescaleSlope are not
+ // present (undefined) or explicitly set to null. We use loose
+ // equality in this case to check for *null* or *undefined*.
+ if (RescaleIntercept == null || RescaleSlope == null) {
return;
}
diff --git a/platform/core/src/services/UIDialogService/UIDialogService.ts b/platform/core/src/services/UIDialogService/UIDialogService.ts
index 2f291d331..15f50c2e1 100644
--- a/platform/core/src/services/UIDialogService/UIDialogService.ts
+++ b/platform/core/src/services/UIDialogService/UIDialogService.ts
@@ -14,6 +14,8 @@ const serviceImplementation = {
console.warn('isEmpty() NOT IMPLEMENTED');
return true;
},
+ _updatePosition: (id: string, position: { x: number; y: number }) =>
+ console.warn('updatePosition() NOT IMPLEMENTED'),
_customComponent: null,
};
@@ -63,6 +65,16 @@ class UIDialogService {
return serviceImplementation._isEmpty();
}
+ /**
+ * Update the position of a specific dialog by id
+ *
+ * @param {string} id - The dialog id to update
+ * @param {{ x: number; y: number }} position - The new position
+ */
+ updatePosition(id: string, position: { x: number; y: number }): void {
+ return serviceImplementation._updatePosition(id, position);
+ }
+
/**
* This provides flexibility in customizing the Modal's default component
*
@@ -75,7 +87,14 @@ class UIDialogService {
/**
* Set the service implementation
*/
- setServiceImplementation({ show, hide, hideAll, isEmpty, customComponent }: any): void {
+ setServiceImplementation({
+ show,
+ hide,
+ hideAll,
+ isEmpty,
+ updatePosition,
+ customComponent,
+ }: any): void {
if (show) {
serviceImplementation._show = show;
}
@@ -88,6 +107,9 @@ class UIDialogService {
if (isEmpty) {
serviceImplementation._isEmpty = isEmpty;
}
+ if (updatePosition) {
+ serviceImplementation._updatePosition = updatePosition;
+ }
if (customComponent) {
serviceImplementation._customComponent = customComponent;
}
diff --git a/platform/core/src/services/UINotificationService/index.ts b/platform/core/src/services/UINotificationService/index.ts
index f1f565d81..eada24005 100644
--- a/platform/core/src/services/UINotificationService/index.ts
+++ b/platform/core/src/services/UINotificationService/index.ts
@@ -60,17 +60,24 @@ class UINotificationService {
* @param {string} [notification.promiseMessages.loading] - Message to show while promise is pending
* @param {string | function} [notification.promiseMessages.success] - Message to show when promise resolves
* @param {string | function} [notification.promiseMessages.error] - Message to show when promise rejects
+ * @param {object} [notification.action] - Action button configuration
+ * @param {string} notification.action.label - The label for the action button
+ * @param {function} notification.action.onClick - The function to call when the action button is clicked
* @returns {string} id - The ID of the created notification
*/
show({
title,
message,
- duration = 5000,
+ duration = 2000,
position = 'bottom-right',
type = 'info',
autoClose = true,
promise,
promiseMessages,
+ id,
+ allowDuplicates = false,
+ deduplicationInterval = 30000,
+ action,
}: {
title: string;
message: string | ((data?: any) => string);
@@ -90,6 +97,13 @@ class UINotificationService {
success?: string | ((data: any) => string);
error?: string | ((error: any) => string);
};
+ id?: string;
+ allowDuplicates?: boolean;
+ deduplicationInterval?: number;
+ action?: {
+ label: string;
+ onClick: () => void;
+ };
}): string {
if (promise && promiseMessages) {
const loadingId = serviceImplementation._show({
@@ -98,6 +112,9 @@ class UINotificationService {
type: 'loading',
autoClose: false,
position,
+ id: id ? `${id}-loading` : undefined,
+ allowDuplicates,
+ deduplicationInterval,
});
promise.then(
@@ -114,6 +131,10 @@ class UINotificationService {
duration,
position,
autoClose,
+ id: id ? `${id}-success` : undefined,
+ allowDuplicates,
+ deduplicationInterval,
+ action,
});
this.hide(loadingId);
},
@@ -130,6 +151,10 @@ class UINotificationService {
duration,
position,
autoClose,
+ id: id ? `${id}-error` : undefined,
+ allowDuplicates,
+ deduplicationInterval,
+ action,
});
this.hide(loadingId);
}
@@ -145,6 +170,10 @@ class UINotificationService {
position,
type,
autoClose,
+ id,
+ allowDuplicates,
+ deduplicationInterval,
+ action,
});
}
}
diff --git a/platform/docs/src/pages/colors-and-type.tsx b/platform/docs/src/pages/colors-and-type.tsx
index 8aad79e03..089d30f57 100644
--- a/platform/docs/src/pages/colors-and-type.tsx
+++ b/platform/docs/src/pages/colors-and-type.tsx
@@ -132,8 +132,9 @@ export default function ComponentShowcase() {
description="Colors and Typography"
>
-
-
+
+ {/* Navigation cards */}
+
-
-
Colors & Typography
foreground
@@ -258,6 +257,23 @@ export default function ComponentShowcase() {
use 'muted-foreground' to create separation and readability.
+
+
+
+
+ Used for borders and UI elements. 'neutral' is typically used at 50% opacity for
+ elements such as scrollbars and will work over light and dark backgrounds
+
+
-
-
Components
+ {/* Navigation cards */}
+
-
+
+
Components
+
+
+
+
+
+
+
+
+
+
@@ -47,7 +120,7 @@ export default function ComponentsList() {
-
+
diff --git a/platform/docs/src/pages/components/AllinOneMenuShowcase.tsx b/platform/docs/src/pages/components/AllinOneMenuShowcase.tsx
new file mode 100644
index 000000000..f479b5d9d
--- /dev/null
+++ b/platform/docs/src/pages/components/AllinOneMenuShowcase.tsx
@@ -0,0 +1,142 @@
+import React from 'react';
+import AllInOneMenu, {
+ IconMenu,
+ SubMenu,
+ ItemPanel,
+ Item,
+ DividerItem,
+ HorizontalDirection,
+ VerticalDirection,
+} from '../../../../ui-next/src/components/AllInOneMenu';
+import { Switch } from '../../../../ui-next/src/components/Switch';
+import ShowcaseRow from './ShowcaseRow';
+
+/**
+ * Pure‑UI mock of the in‑app Window/Level menu.
+ * Clickable, but all actions are inert.
+ */
+export default function AllinOneMenuShowcase() {
+ /* Helpers to build common static items so the JSX stays concise */
+ const renderColorLUTItems = () =>
+ [
+ 'Grayscale',
+ 'X Ray',
+ 'HSV',
+ 'Hot Iron',
+ 'Red Hot',
+ 'S PET',
+ 'Perfusion',
+ 'Rainbow',
+ 'SUV',
+ 'GE 256',
+ 'GE',
+ 'Siemens',
+ ].map(name =>
);
+
+ const renderWindowPresetItems = () =>
+ [
+ { desc: 'Soft tissue', wl: '400 / 40' },
+ { desc: 'Lung', wl: '1500 / -600' },
+ { desc: 'Liver', wl: '150 / 90' },
+ { desc: 'Bone', wl: '2500 / 480' },
+ { desc: 'Brain', wl: '80 / 40' },
+ ].map(p => (
+
+ ));
+
+ return (
+
+
+ } />
+
+
+
+ } />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`}
+ >
+
+
+
+
+ }
+ useIconSpace={false}
+ />
+
+
+
+
+ }
+ />
+
+ {renderColorLUTItems()}
+
+
+
+ {renderWindowPresetItems()}
+
+
+
+
+
+ Click the icon to explore an example
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/platform/docs/src/pages/components/CinePlayerShowcase.tsx b/platform/docs/src/pages/components/CinePlayerShowcase.tsx
new file mode 100644
index 000000000..dc07be265
--- /dev/null
+++ b/platform/docs/src/pages/components/CinePlayerShowcase.tsx
@@ -0,0 +1,37 @@
+import React, { useState } from 'react';
+import CinePlayer from '../../../../ui-next/src/components/CinePlayer/CinePlayer';
+import ShowcaseRow from './ShowcaseRow';
+
+/**
+ * CinePlayerShowcase displays a playable/pausable cine player with FPS control.
+ */
+export default function CinePlayerShowcase() {
+ const [isPlaying, setIsPlaying] = useState(false);
+ const [fps, setFps] = useState(24);
+
+ return (
+
console.log('close clicked')}
+/>
+ `}
+ >
+ console.log('close clicked')}
+ />
+
+ );
+}
diff --git a/platform/docs/src/pages/components/ComboboxShowcase.tsx b/platform/docs/src/pages/components/ComboboxShowcase.tsx
new file mode 100644
index 000000000..812d9131c
--- /dev/null
+++ b/platform/docs/src/pages/components/ComboboxShowcase.tsx
@@ -0,0 +1,105 @@
+import React from 'react';
+import { Combobox } from '../../../../ui-next/src/components/Combobox/Combobox';
+import ShowcaseRow from './ShowcaseRow';
+
+/**
+ * ComboboxShowcase demonstrates the searchable Combobox component with a
+ * comprehensive “Modality” list.
+ */
+export default function ComboboxShowcase() {
+ const modalities = [
+ 'AR',
+ 'ASMT',
+ 'AU',
+ 'BDUS',
+ 'BI',
+ 'BMD',
+ 'CR',
+ 'CT',
+ 'CTPROTOCOL',
+ 'DG',
+ 'DOC',
+ 'DX',
+ 'ECG',
+ 'EPS',
+ 'ES',
+ 'FID',
+ 'GM',
+ 'HC',
+ 'HD',
+ 'IO',
+ 'IOL',
+ 'IVOCT',
+ 'IVUS',
+ 'KER',
+ 'KO',
+ 'LEN',
+ 'LS',
+ 'MG',
+ 'MR',
+ 'M3D',
+ 'NM',
+ 'OAM',
+ 'OCT',
+ 'OP',
+ 'OPM',
+ 'OPT',
+ 'OPTBSV',
+ 'OPTENF',
+ 'OPV',
+ 'OSS',
+ 'OT',
+ 'PLAN',
+ 'PR',
+ 'PT',
+ 'PX',
+ 'REG',
+ 'RESP',
+ 'RF',
+ 'RG',
+ 'RTDOSE',
+ 'RTIMAGE',
+ 'RTINTENT',
+ 'RTPLAN',
+ 'RTRAD',
+ 'RTRECORD',
+ 'RTSEGANN',
+ 'RTSTRUCT',
+ 'RWV',
+ 'SEG',
+ 'SM',
+ 'SMR',
+ 'SR',
+ 'SRF',
+ 'STAIN',
+ 'TEXTUREMAP',
+ 'TG',
+ 'US',
+ 'VA',
+ 'XA',
+ 'XC',
+ ].map(m => ({ value: m, label: m }));
+
+ return (
+
({ value: m, label: m }));
+
+
+ `}
+ >
+
+
+ );
+}
diff --git a/platform/docs/src/pages/components/DialogShowcase.tsx b/platform/docs/src/pages/components/DialogShowcase.tsx
new file mode 100644
index 000000000..cb5d29b95
--- /dev/null
+++ b/platform/docs/src/pages/components/DialogShowcase.tsx
@@ -0,0 +1,82 @@
+import React from 'react';
+import {
+ Dialog,
+ DialogTrigger,
+ DialogContent,
+ DialogHeader,
+ DialogFooter,
+ DialogTitle,
+ DialogDescription,
+ DialogClose,
+} from '../../../../ui-next/src/components/Dialog/Dialog';
+import { Button } from '../../../../ui-next/src/components/Button';
+import ShowcaseRow from './ShowcaseRow';
+
+/**
+ * DialogShowcase demonstrates a simple Radix‑based dialog.
+ */
+export default function DialogShowcase() {
+ return (
+
+
+ Open Dialog
+
+
+
+ Example Dialog
+
+
+ This is a short message inside the dialog.
+
+
+
+ Cancel
+
+
+ OK
+
+
+
+
+ `}
+ >
+
+
+ Open Dialog
+
+
+
+
+ Example Dialog
+
+
+ This is a short message inside the dialog.
+
+
+
+
+ Cancel
+
+
+
+
+
+ OK
+
+
+
+
+
+
+ );
+}
diff --git a/platform/docs/src/pages/components/HoverCardShowcase.tsx b/platform/docs/src/pages/components/HoverCardShowcase.tsx
new file mode 100644
index 000000000..8dbf713f2
--- /dev/null
+++ b/platform/docs/src/pages/components/HoverCardShowcase.tsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import {
+ HoverCard,
+ HoverCardTrigger,
+ HoverCardContent,
+} from '../../../../ui-next/src/components/HoverCard/HoverCard';
+import { Button } from '../../../../ui-next/src/components/Button';
+import ShowcaseRow from './ShowcaseRow';
+
+/**
+ * HoverCardShowcase demonstrates a Hover Card that appears on pointer hover.
+ */
+export default function HoverCardShowcase() {
+ return (
+
+
+ Hover me
+
+
+ Hello there! I'm a hover card.
+
+
+ `}
+ >
+
+
+ Hover me
+
+
+ Hello there! I'm a hover card.
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/platform/docs/src/pages/components/LabelShowcase.tsx b/platform/docs/src/pages/components/LabelShowcase.tsx
new file mode 100644
index 000000000..7b764b943
--- /dev/null
+++ b/platform/docs/src/pages/components/LabelShowcase.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import { Label } from '../../../../ui-next/src/components/Label/Label';
+import { Switch } from '../../../../ui-next/src/components/Switch';
+import ShowcaseRow from './ShowcaseRow';
+
+/**
+ * LabelShowcase pairs a Label with a Switch inline.
+ */
+export default function LabelShowcase() {
+ return (
+
+
+ Preview edits before creating
+
+ `}
+ >
+
+
+ Preview edits before creating
+
+
+ );
+}
\ No newline at end of file
diff --git a/platform/docs/src/pages/components/NumericMetaShowcase.tsx b/platform/docs/src/pages/components/NumericMetaShowcase.tsx
index 3ee19cf42..ba2bf180c 100644
--- a/platform/docs/src/pages/components/NumericMetaShowcase.tsx
+++ b/platform/docs/src/pages/components/NumericMetaShowcase.tsx
@@ -45,7 +45,7 @@ export default function NumericShowcase() {
`}
>
-
+
`}
>
-
+
`}
>
-
+
([0, 1
`}
>
-
+
([0, 1
`}
>
-
+
+ Series Information
+
+
+
Images: 120
+
Modality: MR
+
Body Part: Brain
+
Load another series
+
+
+
+ `}
+ >
+
+ Series Information
+
+
+
+
Images: 120
+
Modality: MR
+
Body Part: Brain
+
+
+ Load more information
+
+
+
+
+
+ );
+}
diff --git a/platform/docs/src/pages/components/PopoverShowcase.tsx b/platform/docs/src/pages/components/PopoverShowcase.tsx
new file mode 100644
index 000000000..eb86c0901
--- /dev/null
+++ b/platform/docs/src/pages/components/PopoverShowcase.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import {
+ Popover,
+ PopoverTrigger,
+ PopoverContent,
+} from '../../../../ui-next/src/components/Popover/Popover';
+import { Button } from '../../../../ui-next/src/components/Button';
+import ShowcaseRow from './ShowcaseRow';
+
+/**
+ * PopoverShowcase presents a simple Popover that appears after clicking a button.
+ */
+export default function PopoverShowcase() {
+ return (
+
+
+ Open Popover
+
+
+
+ Hello! I’m a Popover. Click outside or press Esc to close me.
+
+
+
+ `}
+ >
+
+
+ Open Popover
+
+
+
+ Hello! I’m a Popover. Click outside or press Esc to close me.
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/platform/docs/src/pages/components/TabsShowcase.tsx b/platform/docs/src/pages/components/TabsShowcase.tsx
index e88750339..82b775c3e 100644
--- a/platform/docs/src/pages/components/TabsShowcase.tsx
+++ b/platform/docs/src/pages/components/TabsShowcase.tsx
@@ -12,7 +12,7 @@ export default function TabsShowcase() {
title="Tabs"
description="Tabs (or segmented controls) can be used to provide navigation options or allow users to switch between multiple options (e.g., tool settings) "
code={`
- console.log(newValue)}>
+ console.log(newValue)}>
Circle
@@ -24,6 +24,7 @@ export default function TabsShowcase() {
`}
>
console.log(newValue)}
>
@@ -37,4 +38,4 @@ export default function TabsShowcase() {
);
-}
+}
\ No newline at end of file
diff --git a/platform/docs/src/pages/components/TooltipShowcase.tsx b/platform/docs/src/pages/components/TooltipShowcase.tsx
new file mode 100644
index 000000000..c9ad13651
--- /dev/null
+++ b/platform/docs/src/pages/components/TooltipShowcase.tsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import { Tooltip, TooltipTrigger, TooltipContent } from '../../../../ui-next/src/components/Tooltip';
+import { Button } from '../../../../ui-next/src/components/Button';
+import ShowcaseRow from './ShowcaseRow';
+
+/**
+ * TooltipShowcase component displays Tooltip variants and examples
+ */
+export default function TooltipShowcase() {
+ return (
+
+
+ ?
+
+
+ Tooltip content
+
+
+ `}
+ >
+
+
+ ?
+
+
+ Tooltip content
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/platform/docs/src/pages/patterns.tsx b/platform/docs/src/pages/patterns.tsx
index 309995764..12d5f2836 100644
--- a/platform/docs/src/pages/patterns.tsx
+++ b/platform/docs/src/pages/patterns.tsx
@@ -27,8 +27,9 @@ export default function ComponentShowcase() {
description="Patterns and example layouts"
>
-
-
+
+ {/* Navigation cards */}
+
-
-
Patterns
{
return (
);
};
diff --git a/platform/ui-next/src/components/Errorboundary/ErrorBoundary.tsx b/platform/ui-next/src/components/Errorboundary/ErrorBoundary.tsx
index 0ad5c2bfd..4fc51cf7c 100644
--- a/platform/ui-next/src/components/Errorboundary/ErrorBoundary.tsx
+++ b/platform/ui-next/src/components/Errorboundary/ErrorBoundary.tsx
@@ -1,10 +1,10 @@
import React, { useState, useEffect } from 'react';
-import { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';
+import { ErrorBoundary as ReactErrorBoundary, FallbackProps } from 'react-error-boundary';
import { useTranslation } from 'react-i18next';
-import { toast } from 'sonner';
import { Dialog, DialogContent } from '../Dialog/Dialog';
import { ScrollArea } from '../ScrollArea/ScrollArea';
import { Button } from '../Button/Button';
+import { useNotification } from '../../contextProviders';
const isProduction = process.env.NODE_ENV === 'production';
@@ -112,7 +112,7 @@ interface ErrorBoundaryError extends Error {
stack?: string;
}
-interface DefaultFallbackProps {
+interface DefaultFallbackProps extends FallbackProps {
error: ErrorBoundaryError;
context: string;
resetErrorBoundary: () => void;
@@ -135,6 +135,8 @@ const DefaultFallback = ({
}: DefaultFallbackProps) => {
const { t } = useTranslation('ErrorBoundary');
const [showDetails, setShowDetails] = useState(false);
+ const { show } = useNotification();
+
const title = `${t('Something went wrong')}${!isProduction && ` ${t('in')} ${context}`}.`;
const subtitle = t('Sorry, something went wrong there. Try again.');
@@ -143,20 +145,32 @@ const DefaultFallback = ({
const copyErrorToClipboard = () => {
if (code) {
navigator.clipboard.writeText(code);
- toast.success(t('Error copied to clipboard'));
+ show({
+ title: t('Success'),
+ message: t('Error copied to clipboard'),
+ type: 'success',
+ duration: 3000,
+ });
}
};
useEffect(() => {
- toast.error(title, {
- description: subtitle,
+ // Use a stable ID based on error message to support deduplication
+ const errorId = `error-${errorTitle || error.message}`;
+
+ // We don't need to track shown state - instead rely on the notification deduplication system
+ show({
+ title,
+ message: subtitle,
+ type: 'error',
+ duration: 0,
+ id: errorId,
action: {
label: t('Show Details'),
onClick: () => setShowDetails(true),
},
- duration: 0,
});
- }, [error, subtitle, t, title]);
+ }, [error, errorTitle, subtitle, t, title, show]);
if (isProduction) {
return null;
@@ -286,9 +300,8 @@ const ErrorBoundary = ({
(
)}
onReset={onResetHandler}
diff --git a/platform/ui-next/src/components/Icons/Sources/Clear.tsx b/platform/ui-next/src/components/Icons/Sources/Clear.tsx
index bceffb901..fea7ec869 100644
--- a/platform/ui-next/src/components/Icons/Sources/Clear.tsx
+++ b/platform/ui-next/src/components/Icons/Sources/Clear.tsx
@@ -14,7 +14,7 @@ export const Clear = (props: IconProps) => (
fillRule="evenodd"
>
(
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 28"
aria-labelledby="title"
- width="1em"
- height="1em"
+ width="28px"
+ height="28px"
fill="currentColor"
{...props}
>
diff --git a/platform/ui-next/src/components/Viewport/ViewportPane.tsx b/platform/ui-next/src/components/Viewport/ViewportPane.tsx
index e5c6f4b91..2610ad241 100644
--- a/platform/ui-next/src/components/Viewport/ViewportPane.tsx
+++ b/platform/ui-next/src/components/Viewport/ViewportPane.tsx
@@ -67,9 +67,10 @@ function ViewportPane({
{/* Border overlay */}
diff --git a/platform/ui-next/src/contextProviders/DialogProvider.tsx b/platform/ui-next/src/contextProviders/DialogProvider.tsx
index 940aed9b5..442cd7284 100644
--- a/platform/ui-next/src/contextProviders/DialogProvider.tsx
+++ b/platform/ui-next/src/contextProviders/DialogProvider.tsx
@@ -1,11 +1,20 @@
-import React, { useState, createContext, useContext, useCallback, useEffect, useMemo } from 'react';
-import ManagedDialog, { ManagedDialogProps } from './ManagedDialog';
+import React, {
+ useState,
+ createContext,
+ useContext,
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+} from 'react';
+import ManagedDialog, { ManagedDialogProps, ManagedDialogRef } from './ManagedDialog';
interface DialogContextType {
show: (options: ManagedDialogProps) => string;
hide: (id: string) => void;
hideAll: () => void;
isEmpty: () => boolean;
+ updatePosition: (id: string, position: { x: number; y: number }) => void;
}
interface DialogService {
@@ -35,6 +44,7 @@ const DialogProvider: React.FC
= ({
service = null,
}) => {
const [dialogs, setDialogs] = useState<(ManagedDialogProps & { id: string })[]>([]);
+ const dialogRefs = useRef>(new Map());
const show = useCallback((options: ManagedDialogProps) => {
const id = options.id;
@@ -44,22 +54,32 @@ const DialogProvider: React.FC = ({
const hide = useCallback((id: string) => {
setDialogs(prev => prev.filter(dialog => dialog.id !== id));
+ dialogRefs.current.delete(id);
}, []);
const hideAll = useCallback(() => {
setDialogs([]);
+ dialogRefs.current.clear();
}, []);
const isEmpty = useCallback(() => dialogs.length === 0, [dialogs]);
+ const updatePosition = useCallback((id: string, position: { x: number; y: number }) => {
+ const dialogRef = dialogRefs.current.get(id);
+ if (dialogRef) {
+ dialogRef.updatePosition(position);
+ }
+ }, []);
+
const contextValue = useMemo(
() => ({
show,
hide,
hideAll,
isEmpty,
+ updatePosition,
}),
- [show, hide, hideAll, isEmpty]
+ [show, hide, hideAll, isEmpty, updatePosition]
);
useEffect(() => {
@@ -76,6 +96,11 @@ const DialogProvider: React.FC = ({
{dialogs.map(dialog => (
{
+ if (ref) {
+ dialogRefs.current.set(dialog.id, ref);
+ }
+ }}
onClose={hide}
isOpen={true}
{...dialog}
diff --git a/platform/ui-next/src/contextProviders/ManagedDialog.tsx b/platform/ui-next/src/contextProviders/ManagedDialog.tsx
index 1882b9de9..9bd0cf722 100644
--- a/platform/ui-next/src/contextProviders/ManagedDialog.tsx
+++ b/platform/ui-next/src/contextProviders/ManagedDialog.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useState, useEffect, useImperativeHandle, forwardRef } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../components/Dialog/Dialog';
import { cn } from '../lib/utils';
@@ -19,59 +19,85 @@ export interface ManagedDialogProps {
containerClassName?: string;
}
-const ManagedDialog: React.FC = ({
- id,
- isOpen,
- title,
- content: DialogContentComponent,
- contentProps,
- isDraggable,
- shouldCloseOnEsc = false,
- shouldCloseOnOverlayClick = false,
- showOverlay = true,
- defaultPosition,
- onClose,
- unstyled,
- containerClassName,
-}) => {
- return (
- {
- if (!open) {
- onClose(id);
- }
- }}
- isDraggable={isDraggable}
- shouldCloseOnEsc={shouldCloseOnEsc}
- shouldCloseOnOverlayClick={shouldCloseOnOverlayClick}
- showOverlay={showOverlay}
- >
- void;
+}
+
+const ManagedDialog = forwardRef(
+ (
+ {
+ id,
+ isOpen,
+ title,
+ content: DialogContentComponent,
+ contentProps,
+ isDraggable,
+ shouldCloseOnEsc = false,
+ shouldCloseOnOverlayClick = false,
+ showOverlay = true,
+ defaultPosition,
+ onClose,
+ unstyled,
+ containerClassName,
+ },
+ ref
+ ) => {
+ const [currentPosition, setCurrentPosition] = useState(defaultPosition);
+
+ useImperativeHandle(
+ ref,
+ () => ({
+ updatePosition: (position: { x: number; y: number }) => {
+ setCurrentPosition(position);
+ },
+ }),
+ []
+ );
+
+ useEffect(() => {
+ setCurrentPosition(defaultPosition);
+ }, [defaultPosition]);
+ return (
+ {
+ if (!open) {
+ onClose(id);
+ }
}}
+ isDraggable={isDraggable}
+ shouldCloseOnEsc={shouldCloseOnEsc}
+ shouldCloseOnOverlayClick={shouldCloseOnOverlayClick}
+ showOverlay={showOverlay}
>
- {!unstyled && {title && {title} } }
- onClose(id)}
- />
-
-
- );
-};
+
+ {!unstyled && {title && {title} } }
+ onClose(id)}
+ />
+
+
+ );
+ }
+);
+
+ManagedDialog.displayName = 'ManagedDialog';
export default ManagedDialog;
diff --git a/platform/ui-next/src/contextProviders/NotificationProvider.tsx b/platform/ui-next/src/contextProviders/NotificationProvider.tsx
index 09e526245..e824a4036 100644
--- a/platform/ui-next/src/contextProviders/NotificationProvider.tsx
+++ b/platform/ui-next/src/contextProviders/NotificationProvider.tsx
@@ -1,4 +1,4 @@
-import React, { createContext, useContext, useCallback, useEffect, ReactNode } from 'react';
+import React, { createContext, useContext, useCallback, useEffect, ReactNode, useRef } from 'react';
import PropTypes from 'prop-types';
import { Toaster, toast } from '../components';
@@ -6,7 +6,23 @@ const NotificationContext = createContext(null);
export const useNotification = () => useContext(NotificationContext);
-const NotificationProvider = ({ children, service }) => {
+// Notification deduplication cache
+interface NotificationCacheEntry {
+ timestamp: number;
+ id: string;
+}
+
+interface NotificationProviderProps {
+ children: ReactNode;
+ service?: any;
+ deduplicationInterval?: number;
+}
+
+const NotificationProvider = ({
+ children,
+ service,
+ deduplicationInterval = 10000, // Default to 10 seconds
+}: NotificationProviderProps) => {
const DEFAULT_OPTIONS = {
title: '',
message: '',
@@ -15,39 +31,138 @@ const NotificationProvider = ({ children, service }) => {
type: 'info', // info, success, error
};
+ // Cache for recent notifications to prevent duplicates
+ // Structure: { [title_message_type]: { timestamp, id } }
+ const recentNotificationsRef = useRef>({});
+
+ // Use the configurable deduplication interval from props
+
const show = useCallback(options => {
- const { title, message, duration, position, type, promise } = {
+ const {
+ title,
+ message,
+ duration,
+ position,
+ type,
+ promise,
+ allowDuplicates = false,
+ deduplicationInterval: optionsDeduplicationInterval,
+ action,
+ } = {
...DEFAULT_OPTIONS,
...options,
};
+ // Use the provider's deduplicationInterval by default, but allow it to be overridden per notification
+ const notificationDeduplicationInterval = optionsDeduplicationInterval || deduplicationInterval;
+
if (promise) {
return toast.promise(promise, {
loading: title || 'Loading...',
- success: (data: unknown) => ({
- title: title || 'Success',
- description: typeof message === 'function' ? message(data) : message,
- }),
- error: (err: unknown) => ({
- title: title || 'Error',
- description: typeof message === 'function' ? message(err) : message,
- }),
+ success: (data: unknown) => {
+ const description = typeof message === 'function' ? message(data) : message;
+ return {
+ title: title || 'Success',
+ description,
+ };
+ },
+ error: (err: unknown) => {
+ const description = typeof message === 'function' ? message(err) : message;
+ return {
+ title: title || 'Error',
+ description,
+ };
+ },
});
}
- return toast[type](title, {
+ // Create a cache key from notification properties
+ const messageStr = typeof message === 'function' ? 'function' : message;
+ const cacheKey = `${title}_${messageStr}_${type}`;
+
+ // Handle deduplication
+ if (!allowDuplicates && type === 'error') {
+ const now = Date.now();
+ const cachedNotification = recentNotificationsRef.current[cacheKey];
+
+ // First check if we've shown this notification recently
+ if (cachedNotification) {
+ const timeSinceLastShown = now - cachedNotification.timestamp;
+
+ // If it's been shown recently and within the deduplication interval,
+ // don't show it again
+ if (timeSinceLastShown < notificationDeduplicationInterval) {
+ console.debug(
+ `Prevented duplicate notification: "${title}" (${timeSinceLastShown}ms < ${notificationDeduplicationInterval}ms)`
+ );
+ // Return the existing notification ID
+ return cachedNotification.id;
+ }
+
+ // If it's been shown before but outside the deduplication interval,
+ // dismiss the old notification first and allow showing a new one
+ console.debug(
+ `Showing notification again after interval: "${title}" (${timeSinceLastShown}ms >= ${notificationDeduplicationInterval}ms)`
+ );
+ toast.dismiss(cachedNotification.id);
+ }
+ }
+
+ // Show the notification with action if provided
+ const toastOptions = {
duration,
position,
description: message,
- });
+ id: options.id, // Use provided ID if available
+ };
+
+ // Add action button if provided
+ if (action && action.label && typeof action.onClick === 'function') {
+ toastOptions.action = {
+ label: action.label,
+ onClick: action.onClick,
+ };
+ }
+
+ const id = toast[type](title, toastOptions);
+
+ // Cache this notification for deduplication if it's an error
+ if (type === 'error') {
+ const timestamp = Date.now();
+ recentNotificationsRef.current[cacheKey] = {
+ timestamp,
+ id,
+ };
+
+ console.debug(
+ `Stored notification in cache: "${title}" (id: ${id}, timestamp: ${timestamp})`
+ );
+
+ // We no longer auto-delete the cache entry after duration
+ // Instead, we keep it to track when the notification was last shown
+ // The entry will be checked against the deduplication interval
+ }
+
+ return id;
}, []);
const hide = useCallback(id => {
toast.dismiss(id);
+
+ // Remove from cache if present
+ const cacheEntries = Object.entries(recentNotificationsRef.current);
+ for (const [key, entry] of cacheEntries) {
+ if (entry.id === id) {
+ delete recentNotificationsRef.current[key];
+ break;
+ }
+ }
}, []);
const hideAll = useCallback(() => {
toast.dismiss();
+ // Clear notification cache
+ recentNotificationsRef.current = {};
}, []);
/**
@@ -61,8 +176,28 @@ const NotificationProvider = ({ children, service }) => {
}
}, [service, hide, show]);
+ // Debug function to get the current cache (for development use)
+ const getNotificationCache = useCallback(() => {
+ const cache = { ...recentNotificationsRef.current };
+
+ // Add human-readable timestamps and time since showing
+ const now = Date.now();
+ const enhancedCache = Object.entries(cache).reduce((result, [key, entry]) => {
+ const timeSince = now - entry.timestamp;
+ result[key] = {
+ ...entry,
+ date: new Date(entry.timestamp).toISOString(),
+ timeSinceMs: timeSince,
+ timeSinceStr: `${Math.floor(timeSince / 1000)}s ago`,
+ };
+ return result;
+ }, {});
+
+ return enhancedCache;
+ }, []);
+
return (
-
+
{children}
@@ -71,6 +206,8 @@ const NotificationProvider = ({ children, service }) => {
NotificationProvider.propTypes = {
children: PropTypes.node.isRequired,
+ service: PropTypes.object,
+ deduplicationInterval: PropTypes.number,
};
export const withNotification = Component => {
diff --git a/rsbuild.config.ts b/rsbuild.config.ts
index fa8583a77..486b42d45 100644
--- a/rsbuild.config.ts
+++ b/rsbuild.config.ts
@@ -104,7 +104,7 @@ export default defineConfig({
// Copy public directory except config and html-templates
{
from: path.resolve(__dirname, 'node_modules/onnxruntime-web/dist'),
- to: DIST_DIR,
+ to: `${DIST_DIR}/ort`,
force: true,
},
{
diff --git a/tests/screenshots/chromium/3DFourUp.spec.ts/threeDFourUpDisplayedCorrectly.png b/tests/screenshots/chromium/3DFourUp.spec.ts/threeDFourUpDisplayedCorrectly.png
index d1164c47c..cc0000a48 100644
Binary files a/tests/screenshots/chromium/3DFourUp.spec.ts/threeDFourUpDisplayedCorrectly.png and b/tests/screenshots/chromium/3DFourUp.spec.ts/threeDFourUpDisplayedCorrectly.png differ
diff --git a/tests/screenshots/chromium/3DMain.spec.ts/threeDMainDisplayedCorrectly.png b/tests/screenshots/chromium/3DMain.spec.ts/threeDMainDisplayedCorrectly.png
index a6443b66c..705f91bdd 100644
Binary files a/tests/screenshots/chromium/3DMain.spec.ts/threeDMainDisplayedCorrectly.png and b/tests/screenshots/chromium/3DMain.spec.ts/threeDMainDisplayedCorrectly.png differ
diff --git a/tests/screenshots/chromium/AxialPrimary.spec.ts/axialPrimaryDisplayedCorrectly.png b/tests/screenshots/chromium/AxialPrimary.spec.ts/axialPrimaryDisplayedCorrectly.png
index 3a35aa4d8..7fdf074ee 100644
Binary files a/tests/screenshots/chromium/AxialPrimary.spec.ts/axialPrimaryDisplayedCorrectly.png and b/tests/screenshots/chromium/AxialPrimary.spec.ts/axialPrimaryDisplayedCorrectly.png differ
diff --git a/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsNewDisplayset.png b/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsNewDisplayset.png
index bea8498b3..a839ee0af 100644
Binary files a/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsNewDisplayset.png and b/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsNewDisplayset.png differ
diff --git a/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsRendered.png b/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsRendered.png
index 9edb32170..6e7ea321f 100644
Binary files a/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsRendered.png and b/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsRendered.png differ
diff --git a/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsResetToolbar.png b/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsResetToolbar.png
index ebd8e0add..4d55b61fd 100644
Binary files a/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsResetToolbar.png and b/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsResetToolbar.png differ
diff --git a/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsRotated.png b/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsRotated.png
index 5018a5e22..bc6bb78f2 100644
Binary files a/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsRotated.png and b/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsRotated.png differ
diff --git a/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsSlabThickness.png b/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsSlabThickness.png
index b566310be..faa0a920d 100644
Binary files a/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsSlabThickness.png and b/tests/screenshots/chromium/Crosshairs.spec.ts/crosshairsSlabThickness.png differ
diff --git a/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-changeSeriesInMPR.png b/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-changeSeriesInMPR.png
index 6bdfabf6a..397ea64e2 100644
Binary files a/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-changeSeriesInMPR.png and b/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-changeSeriesInMPR.png differ
diff --git a/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-initialDraw.png b/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-initialDraw.png
index 81cf3e160..8d6dcbbfd 100644
Binary files a/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-initialDraw.png and b/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-initialDraw.png differ
diff --git a/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-jumpInMPR.png b/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-jumpInMPR.png
index 2e0c4766a..605a57186 100644
Binary files a/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-jumpInMPR.png and b/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-jumpInMPR.png differ
diff --git a/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-jumpToMeasurementAfterSeriesChange.png b/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-jumpToMeasurementAfterSeriesChange.png
index 2e0c4766a..605a57186 100644
Binary files a/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-jumpToMeasurementAfterSeriesChange.png and b/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-jumpToMeasurementAfterSeriesChange.png differ
diff --git a/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-jumpToMeasurementStack.png b/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-jumpToMeasurementStack.png
index 53d149250..434b76ba7 100644
Binary files a/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-jumpToMeasurementStack.png and b/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-jumpToMeasurementStack.png differ
diff --git a/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-scrollAway.png b/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-scrollAway.png
index 3341d7725..20a32fd67 100644
Binary files a/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-scrollAway.png and b/tests/screenshots/chromium/JumpToMeasurementMPR.spec.ts/jumpToMeasurementMPR-scrollAway.png differ
diff --git a/tests/screenshots/chromium/MPR.spec.ts/mprDisplayedCorrectly.png b/tests/screenshots/chromium/MPR.spec.ts/mprDisplayedCorrectly.png
index 61901ca2f..624a83976 100644
Binary files a/tests/screenshots/chromium/MPR.spec.ts/mprDisplayedCorrectly.png and b/tests/screenshots/chromium/MPR.spec.ts/mprDisplayedCorrectly.png differ
diff --git a/tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSEG.png b/tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSEG.png
index 67c8f700c..8dd00f16a 100644
Binary files a/tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSEG.png and b/tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSEG.png differ
diff --git a/tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSegHydrated.png b/tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSegHydrated.png
index 4343542ad..6918e4b21 100644
Binary files a/tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSegHydrated.png and b/tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSegHydrated.png differ
diff --git a/tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSegHydratedAfterLayoutChange.png b/tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSegHydratedAfterLayoutChange.png
index 4142f8e84..2d6a61bbe 100644
Binary files a/tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSegHydratedAfterLayoutChange.png and b/tests/screenshots/chromium/SEGHydrationFromMPR.spec.ts/mprAfterSegHydratedAfterLayoutChange.png differ
diff --git a/tests/screenshots/chromium/SEGHydrationThenMPR.spec.ts/segPostHydrationMPRAxialPrimary.png b/tests/screenshots/chromium/SEGHydrationThenMPR.spec.ts/segPostHydrationMPRAxialPrimary.png
index 293f5c3e9..cc0118697 100644
Binary files a/tests/screenshots/chromium/SEGHydrationThenMPR.spec.ts/segPostHydrationMPRAxialPrimary.png and b/tests/screenshots/chromium/SEGHydrationThenMPR.spec.ts/segPostHydrationMPRAxialPrimary.png differ
diff --git a/version.txt b/version.txt
index a8819280c..7b59a5caa 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-3.10.1
\ No newline at end of file
+3.10.2
diff --git a/yarn.lock b/yarn.lock
index ff259a81d..22313ef84 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1393,10 +1393,10 @@
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
-"@cornerstonejs/adapters@^3.10.1":
- version "3.10.1"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-3.10.1.tgz#a03c21d42c0f54b42b8886df54dec4823b867bd7"
- integrity sha512-uKic+EyF8SguEM9ahZK96r1oWetjO2GCCEM1oO15wl71iHVXFbW8lUYD1uqXtNW4fZet3X9rEke/te1zNVafxA==
+"@cornerstonejs/adapters@^3.11.6":
+ version "3.11.6"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-3.11.6.tgz#a4ec9e2206e1956b2a1f524e08737a994ddd7f82"
+ integrity sha512-EYLkBXfXF6UcmcZPitIkDNe02HdKykiFDnG9CtXPJkTSqFfCZnkF4Mqe4WN/husc9wia6BUMlESQRBYEgBithA==
dependencies:
"@babel/runtime-corejs2" "^7.17.8"
buffer "^6.0.3"
@@ -1404,10 +1404,10 @@
gl-matrix "^3.4.3"
ndarray "^1.0.19"
-"@cornerstonejs/ai@^3.10.1":
- version "3.10.1"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/ai/-/ai-3.10.1.tgz#795ca5e69d94a7b868a765484d2a9b5ed943ef33"
- integrity sha512-yEALz91yZCJdrRhk2R5Vn1yFefy07FL9g3ff4BgL19nTnGIu/3yUkVR9PY5js8T6E56GCFWxs9meWdeiCuRAcg==
+"@cornerstonejs/ai@^3.11.6":
+ version "3.11.6"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/ai/-/ai-3.11.6.tgz#146bed97e452eb941013e437beb8b3416e412252"
+ integrity sha512-F17IDzbn1PC2Zt1S2RS4OaLqO5ccC71Mmk2/9tU3wQS7g7qmyh8JzyB3B6t3iHkSlv20p3HNh8MqSug7zHOcrg==
dependencies:
"@babel/runtime-corejs2" "^7.17.8"
buffer "^6.0.3"
@@ -1443,20 +1443,20 @@
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.5.tgz#8690b61a86fa53ef38a70eee9d665a79229517c0"
integrity sha512-MZCUy8VG0VG5Nl1l58+g+kH3LujAzLYTfJqkwpWI2gjSrGXnP6lgwyy4GmPRZWVoS40/B1LDNALK905cNWm+sg==
-"@cornerstonejs/core@^3.10.1":
- version "3.10.1"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-3.10.1.tgz#074dd6412241ccb208b8615bb6a50db7527b0272"
- integrity sha512-DMlE98A5Oa81G9K5J4ZD1917SbJYteh+j4oJTY60KHAI7p0GoGQRV/ZcxaL+FS8mWng/qejVss9w4fLoLJcErg==
+"@cornerstonejs/core@^3.11.6":
+ version "3.11.6"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-3.11.6.tgz#6d4192981af13911705914d6f5f0bd48303fc8a4"
+ integrity sha512-8IlnDj7MZV/pfbtdF5gdpL53hLkriBgqcTY4hBIY4iW1A1plmwADsbVXzgyXl3Bi63aovmJhiQZ1VtQx0TzZGQ==
dependencies:
"@kitware/vtk.js" "32.12.1"
comlink "^4.4.1"
gl-matrix "^3.4.3"
loglevel "^1.9.2"
-"@cornerstonejs/dicom-image-loader@^3.10.1":
- version "3.10.1"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-3.10.1.tgz#93744c03101135aa51324bce043ea1e40544ec5b"
- integrity sha512-w/55TI05KV3mP/RtL0ds+jfh19KlBZajJDgXystV489duvqJJUH27C+eZyrGFqbA3a+EDv4GYQa4jw/MDkGGqA==
+"@cornerstonejs/dicom-image-loader@^3.11.6":
+ version "3.11.6"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-3.11.6.tgz#7abebb1b091fad6401a96df47bea08b38ba81fbf"
+ integrity sha512-SJZ/LAw2gIyxzyQTEFWqBtyZELakqjcHOJnFxhkEmmpuJdpX4tgtV4k+6fnbKTg9OhybyP3prouMVPj5myjCZA==
dependencies:
"@cornerstonejs/codec-charls" "^1.2.3"
"@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2"
@@ -1468,25 +1468,25 @@
pako "^2.0.4"
uuid "^9.0.0"
-"@cornerstonejs/labelmap-interpolation@^3.10.1":
- version "3.10.1"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/labelmap-interpolation/-/labelmap-interpolation-3.10.1.tgz#fe5a6031ffabf916e70f1cc2edb2b45fc0ef5eee"
- integrity sha512-hcAR83rWA6lBYKXgYOWR/3ofhV3mqh4RRLbj8QnS9e//qtmF+UxoZ4I0125ihPkCjFqFOWuYgn42BnRZNhvjxA==
+"@cornerstonejs/labelmap-interpolation@^3.11.6":
+ version "3.11.6"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/labelmap-interpolation/-/labelmap-interpolation-3.11.6.tgz#1969c44400067fa67eb6467c4adfb831149e85e8"
+ integrity sha512-N55MxFxlRNXHVKpsJgeO8Z2aEURINxdmyPvGODGgYjZSOXZ+iR12Aly/iZHW9d/crq4GU440d5B6EmEZ6iuW8w==
dependencies:
"@itk-wasm/morphological-contour-interpolation" "1.1.0"
itk-wasm "1.0.0-b.165"
-"@cornerstonejs/polymorphic-segmentation@^3.10.1":
- version "3.10.1"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/polymorphic-segmentation/-/polymorphic-segmentation-3.10.1.tgz#2ea4999bf10d4eb53a5fa2caca8ad4336ed9c9a9"
- integrity sha512-HErrnX7kDs5CVfcuPW5pyWw3lJovqqqKiIfPJdEmZNEM4CVSv9pNpYIbSX8uv42zWq+biQFkP4TwYw12q/r7Og==
+"@cornerstonejs/polymorphic-segmentation@^3.11.6":
+ version "3.11.6"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/polymorphic-segmentation/-/polymorphic-segmentation-3.11.6.tgz#12a749443cdc2ea94a57f20fdae2c1377b6f0704"
+ integrity sha512-dk+G12MqqmKSd2Rz8gHo1piEDAeijBcz1EAdF+50UQlUEds1fczHwFkMrh9mlHc9treyHi5+QEIr2MKRmwaJIw==
dependencies:
"@icr/polyseg-wasm" "0.4.0"
-"@cornerstonejs/tools@^3.10.1":
- version "3.10.1"
- resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-3.10.1.tgz#8a49c5f61eeb619f42247bd422100e8d5b09af18"
- integrity sha512-L1z8JiDwgbSwz040BSV5KBMMEe7IZyS4xYVhbn6cpfzCxSfevp53EkUZMhLl4bFcYyRPgXu5/KJO6f9wGFhEvA==
+"@cornerstonejs/tools@^3.11.6":
+ version "3.11.6"
+ resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-3.11.6.tgz#63c051410d83c8ea5f68bb62b7691395e0890704"
+ integrity sha512-Sp1QkEaA952GgZikTNzyj125FzpYtVAYbeqCOn0E3WS7K1yw2XPuRbSCTRkRcV/J66ZoeKl4KgZVrhDlxJGAnw==
dependencies:
"@types/offscreencanvas" "2019.7.3"
comlink "^4.4.1"