chore: recovery 2 (#5014)

Co-authored-by: Dan Rukas <dan.rukas@gmail.com>
Co-authored-by: Tang Cheng <45505657+tctco@users.noreply.github.com>
This commit is contained in:
Alireza 2025-06-09 10:58:06 -04:00 committed by GitHub
parent 9a4a40468f
commit 36813e0c2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
69 changed files with 1352 additions and 354 deletions

278
bun.lock
View File

@ -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=="],

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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",

View File

@ -64,7 +64,7 @@ export const fourUp = {
customViewportProps: {
hideOverlays: true,
},
syncGroups: [VOI_SYNC_GROUP, HYDRATE_SEG_SYNC_GROUP],
syncGroups: [HYDRATE_SEG_SYNC_GROUP],
},
displaySets: [
{

View File

@ -71,7 +71,7 @@ export const mprAnd3DVolumeViewport = {
customViewportProps: {
hideOverlays: true,
},
syncGroups: [VOI_SYNC_GROUP, HYDRATE_SEG_SYNC_GROUP],
syncGroups: [HYDRATE_SEG_SYNC_GROUP],
},
displaySets: [
{

View File

@ -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: [

View File

@ -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

View File

@ -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;

View File

@ -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);
}
});
};

View File

@ -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
);
}
}
}
});
}

View File

@ -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,

View File

@ -128,7 +128,9 @@ const DicomTagBrowser = ({
<div className="mb-6 flex flex-row items-start pl-1">
<div className="flex w-full flex-row items-start gap-4">
<div className="flex w-1/3 flex-col">
<span className="text-muted-foreground flex h-6 items-center text-xs">Series</span>
<span className="text-muted-foreground flex h-6 items-center pb-2 text-base">
Series
</span>
<Select
value={selectedDisplaySetInstanceUID}
onValueChange={value => onSelectChange({ value })}
@ -154,7 +156,7 @@ const DicomTagBrowser = ({
</div>
{shouldShowInstanceList && (
<div className="mx-auto mt-0.5 flex w-1/4 flex-col">
<span className="text-muted-foreground flex h-6 items-center text-xs">
<span className="text-muted-foreground flex h-6 items-center pb-2 text-base">
Instance Number ({instanceNumber} of {activeDisplaySet?.images?.length})
</span>
<Slider
@ -170,7 +172,7 @@ const DicomTagBrowser = ({
</div>
)}
<div className="ml-auto mr-1 flex w-1/3 flex-col">
<span className="text-muted-foreground flex h-6 items-center text-xs">
<span className="text-muted-foreground flex h-6 items-center pb-2 text-base">
Search metadata
</span>
<InputFilter
@ -182,7 +184,7 @@ const DicomTagBrowser = ({
placeholder="Search metadata"
className="pl-9 pr-9"
/>
<InputFilter.ClearButton />
<InputFilter.ClearButton className="text-primary mr-0.5 p-0.5" />
</InputFilter>
</div>
</div>

View File

@ -4,7 +4,7 @@ const collapsedOutsideBorderSize = 4;
const collapsedWidth = 25;
const rightPanelInitialExpandedWidth = 280;
const leftPanelInitialExpandedWidth = 292;
const leftPanelInitialExpandedWidth = 282;
const panelGroupDefinition = {
groupId: 'viewerLayoutResizablePanelGroup',

View File

@ -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,
};

View File

@ -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",

View File

@ -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',

View File

@ -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",

View File

@ -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",

View File

@ -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"

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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,
});
}
}

View File

@ -132,8 +132,9 @@ export default function ComponentShowcase() {
description="Colors and Typography"
>
<div className="text-foreground min-h-screen bg-black">
<div className="mx-auto my-4 max-w-5xl pt-6 pb-3">
<div className="grid grid-cols-1 gap-5 md:grid-cols-3">
<div className="mx-auto my-4 max-w-5xl pt-4 pb-6">
{/* Navigation cards */}
<div className="mb-8 grid grid-cols-1 gap-5 md:grid-cols-3">
<a
href="/colors-and-type"
className="focus:ring-primary block rounded-lg text-inherit no-underline hover:no-underline focus:outline-none focus:ring-2"
@ -183,9 +184,7 @@ export default function ComponentShowcase() {
</Card>
</a>
</div>
</div>
<div className="mx-auto my-4 max-w-5xl pt-4 pb-6">
<h1 className="text-foreground ml-6 mb-6 text-5xl">Colors & Typography</h1>
<ShowcaseRow
@ -249,7 +248,7 @@ export default function ComponentShowcase() {
<span className="text-foreground text-lg">foreground</span>
</div>
<div className="flex items-center space-x-3">
<div className="bg-muted-foreground border-input h-[30px] w-[30px] rounded border"></div>
<div className="bg-muted-foreground h-[30px] w-[30px] rounded"></div>
<span className="text-foreground text-lg">muted-foreground</span>
</div>
</div>
@ -258,6 +257,23 @@ export default function ComponentShowcase() {
use 'muted-foreground' to create separation and readability.
</div>
</div>
<div className="mb-8 grid grid-cols-[28%,1fr] items-start gap-x-8">
<div className="space-y-2.5">
<div className="flex items-center space-x-3">
<div className="bg-input h-[30px] w-[30px] rounded"></div>
<span className="text-foreground text-lg">input</span>
</div>
<div className="flex items-center space-x-3">
<div className="bg-neutral h-[30px] w-[30px] rounded"></div>
<span className="text-foreground text-lg">neutral</span>
</div>
</div>
<div className="text-secondary-foreground flex items-center pt-1.5 text-lg">
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
</div>
</div>
</ShowcaseRow>
<ShowcaseRow

View File

@ -3,12 +3,25 @@ import '../css/custom.css';
import Layout from '@theme/Layout';
import { TooltipProvider } from '../../../ui-next/src/components/Tooltip';
// Import all showcase components
// Navigation card components
import { Card, CardHeader, CardTitle, CardDescription } from '../../../ui-next/src/components/Card';
import { Icons } from '../../../ui-next/src/components/Icons';
/* Showcase components (alphabetical) */
import AllinOneMenuShowcase from './components/AllinOneMenuShowcase';
import ButtonShowcase from './components/ButtonShowcase';
import CheckboxShowcase from './components/CheckboxShowcase';
import CinePlayerShowcase from './components/CinePlayerShowcase';
import ComboboxShowcase from './components/ComboboxShowcase';
import DataRowShowcase from './components/DataRowShowcase';
import DialogShowcase from './components/DialogShowcase';
import DropdownMenuShowcase from './components/DropdownMenuShowcase';
import HoverCardShowcase from './components/HoverCardShowcase';
import InputShowcase from './components/InputShowcase';
import LabelShowcase from './components/LabelShowcase';
import NumericMetaShowcase from './components/NumericMetaShowcase';
import PanelSectionShowcase from './components/PanelSectionShowcase';
import PopoverShowcase from './components/PopoverShowcase';
import ScrollAreaShowcase from './components/ScrollAreaShowcase';
import SelectShowcase from './components/SelectShowcase';
import SliderShowcase from './components/SliderShowcase';
@ -17,7 +30,8 @@ import TabsShowcase from './components/TabsShowcase';
import ToastShowcase from './components/ToastShowcase';
import ToolButtonShowcase from './components/ToolButtonShowcase';
import ToolButtonListShowcase from './components/ToolButtonListShowcase';
import NumericMetaShowcase from './components/NumericMetaShowcase';
import TooltipShowcase from './components/TooltipShowcase';
/**
* Components List page that displays all available UI components
*/
@ -30,15 +44,74 @@ export default function ComponentsList() {
<TooltipProvider>
<div className="text-foreground min-h-screen bg-black">
<div className="mx-auto my-4 max-w-5xl pt-4 pb-6">
<div className="ml-6 mb-6 text-base">
<h1 className="text-foreground mb-3 text-5xl">Components</h1>
{/* Navigation cards */}
<div className="mb-8 grid grid-cols-1 gap-5 md:grid-cols-3">
<a
href="/colors-and-type"
className="focus:ring-primary block rounded-lg text-inherit no-underline hover:no-underline focus:outline-none focus:ring-2"
>
<Card className="hover:bg-primary/30 w-full transition-colors">
<CardHeader>
<CardTitle className="text-foreground text-xl">
<Icons.ColorChange className="h-12 w-12" />
Colors & Typography
</CardTitle>
<CardDescription className="text-lg">
Color Palette and Typography Guidelines
</CardDescription>
</CardHeader>
</Card>
</a>
<a
href="/components-list"
className="focus:ring-primary block rounded-lg text-inherit no-underline hover:no-underline focus:outline-none focus:ring-2"
>
<Card className="hover:bg-primary/30 w-full transition-colors">
<CardHeader>
<CardTitle className="text-foreground text-xl">
<Icons.ColorChange className="h-12 w-12" />
Components
</CardTitle>
<CardDescription className="text-lg">
Essential UI Components with Variants
</CardDescription>
</CardHeader>
</Card>
</a>
<a
href="/patterns"
className="focus:ring-primary block rounded-lg text-inherit no-underline hover:no-underline focus:outline-none focus:ring-2"
>
<Card className="hover:bg-primary/30 w-full transition-colors">
<CardHeader>
<CardTitle className="text-foreground text-xl">
<Icons.ColorChange className="h-12 w-12" />
Patterns
</CardTitle>
<CardDescription className="text-lg">
Component-Based Layout Examples
</CardDescription>
</CardHeader>
</Card>
</a>
</div>
<TabsShowcase />
<h1 className="text-foreground ml-6 mb-6 text-5xl">Components</h1>
<AllinOneMenuShowcase />
<ButtonShowcase />
<CheckboxShowcase />
<CinePlayerShowcase />
<ComboboxShowcase />
<DataRowShowcase />
<DialogShowcase />
<DropdownMenuShowcase />
<HoverCardShowcase />
<InputShowcase />
<LabelShowcase />
<NumericMetaShowcase />
<PanelSectionShowcase />
<PopoverShowcase />
<ScrollAreaShowcase />
<SelectShowcase />
<SliderShowcase />
@ -47,7 +120,7 @@ export default function ComponentsList() {
<ToastShowcase />
<ToolButtonShowcase />
<ToolButtonListShowcase />
<NumericMetaShowcase />
<TooltipShowcase />
</div>
</div>
</TooltipProvider>

View File

@ -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';
/**
* PureUI mock of the inapp 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 => <Item key={name} label={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 => (
<Item
key={p.desc}
label={p.desc}
secondaryLabel={p.wl}
/>
));
return (
<ShowcaseRow
title="All In One Menu"
description="A structured, consolidated menu designed to reduce visual clutter, helping users keep their focus on the image. It supports various UI components—including Switches, Numeric Inputs, Tabs, and Sliders—for detailed settings, as well as text-based lists for actionable items."
code={`<IconMenu icon="viewport-window-level" menuStyle={{ width: 212, maxHeight: 500 }}>
<ItemPanel label="Display">
<Item label="Display Color bar" rightIcon={<Switch disabled />} />
<DividerItem />
<SubMenu itemLabel="Color LUT" itemIcon="icon-color-lut">
<ItemPanel label="Color LUTs">
<Item label="Preview in viewport" rightIcon={<Switch disabled />} />
<DividerItem />
<Item label="Grayscale" />
<Item label="X Ray" />
<Item label="HSV" />
<Item label="Hot Iron" />
<Item label="Red Hot" />
<Item label="S PET" />
<Item label="Perfusion" />
<Item label="Rainbow" />
<Item label="SUV" />
<Item label="GE 256" />
<Item label="GE" />
<Item label="Siemens" />
</ItemPanel>
</SubMenu>
<SubMenu itemLabel="Window Presets" itemIcon="viewport-window-level">
<ItemPanel label="CT Presets">
<Item label="Soft tissue" secondaryLabel="400 / 40" />
<Item label="Lung" secondaryLabel="1500 / 600" />
<Item label="Liver" secondaryLabel="150 / 90" />
<Item label="Bone" secondaryLabel="2500 / 480" />
<Item label="Brain" secondaryLabel="80 / 40" />
</ItemPanel>
</SubMenu>
</ItemPanel>
</IconMenu>`}
>
<div className="border-input/70 relative flex h-12 items-center rounded border bg-black px-4">
<IconMenu
icon="viewport-window-level"
iconClassName="text-xl text-highlight hover:bg-primary/30 cursor-pointer rounded"
horizontalDirection={HorizontalDirection.LeftToRight}
verticalDirection={VerticalDirection.TopToBottom}
menuStyle={{ width: 212, maxHeight: 500 }}
>
<ItemPanel label="Display">
<Item
label="Display Color bar"
rightIcon={
<Switch
checked={false}
disabled
className="pointer-events-none"
/>
}
useIconSpace={false}
/>
<DividerItem />
<SubMenu itemLabel="Color LUT" itemIcon="icon-color-lut">
<ItemPanel
label="Color LUTs"
maxHeight="calc(100vh - 250px)"
className="flex flex-col"
>
<Item
label="Preview in viewport"
rightIcon={
<Switch
checked={false}
disabled
className="pointer-events-none"
/>
}
/>
<DividerItem />
{renderColorLUTItems()}
</ItemPanel>
</SubMenu>
<SubMenu itemLabel="Window Presets" itemIcon="viewport-window-level">
<ItemPanel label="CT Presets">{renderWindowPresetItems()}</ItemPanel>
</SubMenu>
</ItemPanel>
</IconMenu>
<span className="text-muted-foreground ml-3 text-sm">
Click the icon to explore an example
</span>
</div>
</ShowcaseRow>
);
}

View File

@ -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 (
<ShowcaseRow
title="Cine Player"
description="Play, pause, scrub through dynamic image series and adjust FPS."
code={`
<CinePlayer
className="w-[300px]"
isPlaying={isPlaying}
frameRate={fps}
onPlayPauseChange={setIsPlaying}
onFrameRateChange={setFps}
onClose={() => console.log('close clicked')}
/>
`}
>
<CinePlayer
className="w-[300px]"
isPlaying={isPlaying}
frameRate={fps}
onPlayPauseChange={setIsPlaying}
onFrameRateChange={setFps}
onClose={() => console.log('close clicked')}
/>
</ShowcaseRow>
);
}

View File

@ -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 (
<ShowcaseRow
title="Combobox"
description="Searchable dropdown built with Command + Popover primitives."
code={`
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 }));
<Combobox data={modalities} placeholder="Modality" />
`}
>
<Combobox
data={modalities}
placeholder="Modality"
/>
</ShowcaseRow>
);
}

View File

@ -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 Radixbased dialog.
*/
export default function DialogShowcase() {
return (
<ShowcaseRow
title="Dialog"
description="Modal dialog with header, content, and footer actions."
code={`
<Dialog>
<DialogTrigger asChild>
<Button variant="default">Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Example Dialog</DialogTitle>
</DialogHeader>
<DialogDescription>
This is a short message inside the dialog.
</DialogDescription>
<DialogFooter>
<DialogClose asChild>
<Button variant="secondary" className="min-w-[60px]">Cancel</Button>
</DialogClose>
<DialogClose asChild>
<Button variant="default" className="min-w-[60px]">OK</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
`}
>
<Dialog>
<DialogTrigger asChild>
<Button variant="default">Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Example Dialog</DialogTitle>
</DialogHeader>
<DialogDescription>This is a short message inside the dialog.</DialogDescription>
<DialogFooter>
<DialogClose asChild>
<Button
variant="secondary"
className="min-w-[60px]"
>
Cancel
</Button>
</DialogClose>
<DialogClose asChild>
<Button
variant="default"
className="min-w-[60px]"
>
OK
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
</ShowcaseRow>
);
}

View File

@ -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 (
<ShowcaseRow
title="Hover Card"
description="Lightweight, nonmodal surface that appears on hover or focus."
code={`
<HoverCard>
<HoverCardTrigger asChild>
<Button variant="link">Hover me</Button>
</HoverCardTrigger>
<HoverCardContent>
<p className="text-sm">Hello there! I'm a hover card.</p>
</HoverCardContent>
</HoverCard>
`}
>
<HoverCard>
<HoverCardTrigger asChild>
<Button variant="link">Hover me</Button>
</HoverCardTrigger>
<HoverCardContent>
<p className="text-sm">Hello there! I'm a hover card.</p>
</HoverCardContent>
</HoverCard>
</ShowcaseRow>
);
}

View File

@ -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 (
<ShowcaseRow
title="Label"
description="Labels clarify the purpose of controls and improve accessibility."
code={`
<Switch defaultChecked id="preview-switch" />
<Label htmlFor="preview-switch" className="ml-2">
Preview edits before creating
</Label>
`}
>
<div className="flex items-center gap-2">
<Switch defaultChecked id="preview-switch" />
<Label htmlFor="preview-switch">Preview edits before creating</Label>
</div>
</ShowcaseRow>
);
}

View File

@ -45,7 +45,7 @@ export default function NumericShowcase() {
<Numeric.NumberInput className="w-32 text-center" />
</Numeric.Container>`}
>
<div className="bg-popover flex w-[300px] flex-col space-y-4 rounded p-4">
<div className="bg-muted flex w-[300px] flex-col space-y-4 rounded p-4">
<Numeric.Container
mode="number"
min={0}
@ -136,7 +136,7 @@ const [dimensionGroupNumber, setDimensionGroupNumber] = useState(1);
</div>
</Numeric.Container>`}
>
<div className="bg-popover flex w-[300px] flex-col space-y-4 rounded p-4">
<div className="bg-muted flex w-[300px] flex-col space-y-4 rounded p-4">
<Numeric.Container
mode="stepper"
value={dimensionGroupNumber}
@ -229,7 +229,7 @@ const [controlledValue, setControlledValue] = useState(0);
<Numeric.SingleRange showNumberInput />
</Numeric.Container>`}
>
<div className="bg-popover flex w-[300px] flex-col space-y-4 rounded p-4">
<div className="bg-muted flex w-[300px] flex-col space-y-4 rounded p-4">
<Numeric.Container
mode="singleRange"
min={0}
@ -314,7 +314,7 @@ const [controlledValues, setControlledValues] = useState<[number, number]>([0, 1
<Numeric.DoubleRange />
</Numeric.Container>`}
>
<div className="bg-popover flex w-[300px] flex-col space-y-4 rounded p-4">
<div className="bg-muted flex w-[300px] flex-col space-y-4 rounded p-4">
<Numeric.Container
mode="doubleRange"
min={0}
@ -407,7 +407,7 @@ const [controlledValues, setControlledValues] = useState<[number, number]>([0, 1
<Numeric.DoubleRange showNumberInputs />
</Numeric.Container>`}
>
<div className="bg-popover flex w-[300px] flex-col space-y-4 rounded p-4">
<div className="bg-muted flex w-[300px] flex-col space-y-4 rounded p-4">
<Numeric.Container
mode="number"
min={0}

View File

@ -0,0 +1,52 @@
// docs/src/pages/components/PanelSectionShowcase.tsx
import React from 'react';
import { PanelSection } from '../../../../ui-next/src/components/PanelSection/PanelSection';
import { Button } from '../../../../ui-next/src/components/Button';
import ShowcaseRow from './ShowcaseRow';
/**
* PanelSectionShowcase displays an expandable/collapsible panel section.
*/
export default function PanelSectionShowcase() {
return (
<ShowcaseRow
title="Panel Section"
description="Collapsible panels for grouping controls or metadata inside sidepanels."
code={`
<PanelSection defaultOpen>
<PanelSection.Header>Series Information</PanelSection.Header>
<PanelSection.Content>
<div className="p-2 space-y-1 text-sm text-muted-foreground">
<div>Images: 120</div>
<div>Modality: MR</div>
<div>Body Part: Brain</div>
<Button variant="link" size="xs">Load another series</Button>
</div>
</PanelSection.Content>
</PanelSection>
`}
>
<PanelSection
defaultOpen
className="bg-muted w-[280px]"
>
<PanelSection.Header className="bg-popover">Series Information</PanelSection.Header>
<PanelSection.Content>
<div className="text-muted-foreground space-y-1 p-2 text-sm">
<div className="pl-2">
<div>Images: 120</div>
<div>Modality: MR</div>
<div>Body Part: Brain</div>
</div>
<Button
variant="ghost"
size="sm"
>
Load more information
</Button>
</div>
</PanelSection.Content>
</PanelSection>
</ShowcaseRow>
);
}

View File

@ -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 (
<ShowcaseRow
title="Popover"
description="Transient panel that appears over content—great for small forms, extra details, or actions."
code={`
<Popover>
<PopoverTrigger asChild>
<Button variant="default">Open Popover</Button>
</PopoverTrigger>
<PopoverContent className="w-60">
<p className="text-sm">
Hello! Im a Popover. Click outside or press Esc to close me.
</p>
</PopoverContent>
</Popover>
`}
>
<Popover>
<PopoverTrigger asChild>
<Button variant="default">Open Popover</Button>
</PopoverTrigger>
<PopoverContent className="w-60">
<p className="text-sm">
Hello! Im a Popover. Click outside or press&nbsp;Esc to close me.
</p>
</PopoverContent>
</Popover>
</ShowcaseRow>
);
}

View File

@ -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={`
<Tabs className="w-[400px]" onValueChange={newValue => console.log(newValue)}>
<Tabs defaultValue="circle" className="w-[400px]" onValueChange={newValue => console.log(newValue)}>
<TabsList>
<TabsTrigger value="circle">Circle</TabsTrigger>
<Separator orientation="vertical" />
@ -24,6 +24,7 @@ export default function TabsShowcase() {
`}
>
<Tabs
defaultValue="circle"
className="w-[400px]"
onValueChange={newValue => console.log(newValue)}
>
@ -37,4 +38,4 @@ export default function TabsShowcase() {
</Tabs>
</ShowcaseRow>
);
}
}

View File

@ -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 (
<ShowcaseRow
title="Tooltip"
description="Tooltips reveal helper text when users hover, focus, or tap an element."
code={`
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">?</Button>
</TooltipTrigger>
<TooltipContent>
Tooltip content
</TooltipContent>
</Tooltip>
`}
>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">?</Button>
</TooltipTrigger>
<TooltipContent>
Tooltip content
</TooltipContent>
</Tooltip>
</ShowcaseRow>
);
}

View File

@ -27,8 +27,9 @@ export default function ComponentShowcase() {
description="Patterns and example layouts"
>
<div className="text-foreground min-h-screen bg-black">
<div className="mx-auto my-4 max-w-5xl pt-6 pb-3">
<div className="grid grid-cols-1 gap-5 md:grid-cols-3">
<div className="mx-auto my-4 max-w-5xl pt-4 pb-6">
{/* Navigation cards */}
<div className="mb-8 grid grid-cols-1 gap-5 md:grid-cols-3">
<a
href="/colors-and-type"
className="focus:ring-primary block rounded-lg text-inherit no-underline hover:no-underline focus:outline-none focus:ring-2"
@ -78,9 +79,7 @@ export default function ComponentShowcase() {
</Card>
</a>
</div>
</div>
<div className="mx-auto my-4 max-w-5xl pt-4 pb-6">
<h1 className="text-foreground ml-6 mb-6 text-5xl">Patterns</h1>
<ShowcaseRow

View File

@ -3,7 +3,7 @@ import React from 'react';
const DividerItem = () => {
return (
<div className="flex h-3.5 shrink-0 items-center px-2">
<div className="bg-primary-dark h-[2px] w-full"></div>
<div className="bg-background h-[2px] w-full"></div>
</div>
);
};

View File

@ -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 = ({
<ReactErrorBoundary
fallbackRender={props => (
<FallbackComponent
error={props.error}
{...props}
context={context}
resetErrorBoundary={props.resetErrorBoundary}
/>
)}
onReset={onResetHandler}

View File

@ -14,7 +14,7 @@ export const Clear = (props: IconProps) => (
fillRule="evenodd"
>
<circle
fill="#0944B3"
fill="currentColor"
cx="9.5"
cy="9.5"
r="9.5"

View File

@ -6,8 +6,8 @@ export const PowerOff = (props: IconProps) => (
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}
>

View File

@ -67,9 +67,10 @@ function ViewportPane({
{/* Border overlay */}
<div
className={classNames('pointer-events-none absolute inset-0', {
'border-highlight rounded-md border': isActive,
'group-hover:border-highlight/50 rounded-md border border-transparent': !isActive,
className={classNames('pointer-events-none absolute inset-0 rounded-md border', {
'border-highlight': isActive,
'group-hover/pane:border-highlight/50 border-transparent': !isActive,
'!border-secondary-light border-dashed': isHighlighted,
})}
/>
</div>

View File

@ -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<DialogProviderProps> = ({
service = null,
}) => {
const [dialogs, setDialogs] = useState<(ManagedDialogProps & { id: string })[]>([]);
const dialogRefs = useRef<Map<string, ManagedDialogRef>>(new Map());
const show = useCallback((options: ManagedDialogProps) => {
const id = options.id;
@ -44,22 +54,32 @@ const DialogProvider: React.FC<DialogProviderProps> = ({
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<DialogProviderProps> = ({
{dialogs.map(dialog => (
<RenderedDialog
key={dialog.id}
ref={(ref: ManagedDialogRef) => {
if (ref) {
dialogRefs.current.set(dialog.id, ref);
}
}}
onClose={hide}
isOpen={true}
{...dialog}

View File

@ -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<ManagedDialogProps> = ({
id,
isOpen,
title,
content: DialogContentComponent,
contentProps,
isDraggable,
shouldCloseOnEsc = false,
shouldCloseOnOverlayClick = false,
showOverlay = true,
defaultPosition,
onClose,
unstyled,
containerClassName,
}) => {
return (
<Dialog
open={isOpen}
modal={false} // keep modal behavior off for independent windows
onOpenChange={open => {
if (!open) {
onClose(id);
}
}}
isDraggable={isDraggable}
shouldCloseOnEsc={shouldCloseOnEsc}
shouldCloseOnOverlayClick={shouldCloseOnOverlayClick}
showOverlay={showOverlay}
>
<DialogContent
className={cn(unstyled ? 'p-0' : '', containerClassName)}
unstyled={unstyled}
style={{
...(defaultPosition
? {
position: 'fixed',
left: `${defaultPosition.x}px`,
top: `${defaultPosition.y}px`,
transform: 'translate(0, 0)',
margin: 0,
animation: 'none',
}
: {}),
export interface ManagedDialogRef {
updatePosition: (position: { x: number; y: number }) => void;
}
const ManagedDialog = forwardRef<ManagedDialogRef, ManagedDialogProps>(
(
{
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 (
<Dialog
open={isOpen}
modal={false} // keep modal behavior off for independent windows
onOpenChange={open => {
if (!open) {
onClose(id);
}
}}
isDraggable={isDraggable}
shouldCloseOnEsc={shouldCloseOnEsc}
shouldCloseOnOverlayClick={shouldCloseOnOverlayClick}
showOverlay={showOverlay}
>
{!unstyled && <DialogHeader>{title && <DialogTitle>{title}</DialogTitle>}</DialogHeader>}
<DialogContentComponent
{...contentProps}
hide={() => onClose(id)}
/>
</DialogContent>
</Dialog>
);
};
<DialogContent
className={cn(unstyled ? 'p-0' : '', containerClassName)}
unstyled={unstyled}
style={{
...(currentPosition
? {
position: 'fixed',
left: `${currentPosition.x}px`,
top: `${currentPosition.y}px`,
transform: 'translate(0, 0)',
margin: 0,
animation: 'none',
}
: {}),
}}
>
{!unstyled && <DialogHeader>{title && <DialogTitle>{title}</DialogTitle>}</DialogHeader>}
<DialogContentComponent
{...contentProps}
hide={() => onClose(id)}
/>
</DialogContent>
</Dialog>
);
}
);
ManagedDialog.displayName = 'ManagedDialog';
export default ManagedDialog;

View File

@ -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<Record<string, NotificationCacheEntry>>({});
// 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 (
<NotificationContext.Provider value={{ show, hide, hideAll }}>
<NotificationContext.Provider value={{ show, hide, hideAll, getNotificationCache }}>
<Toaster position="bottom-right" />
{children}
</NotificationContext.Provider>
@ -71,6 +206,8 @@ const NotificationProvider = ({ children, service }) => {
NotificationProvider.propTypes = {
children: PropTypes.node.isRequired,
service: PropTypes.object,
deduplicationInterval: PropTypes.number,
};
export const withNotification = Component => {

View File

@ -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,
},
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 KiB

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 KiB

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 KiB

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 KiB

After

Width:  |  Height:  |  Size: 297 KiB

View File

@ -1 +1 @@
3.10.1
3.10.2

View File

@ -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"