diff --git a/bun.lock b/bun.lock index 901f348ae97..ada82030746 100644 --- a/bun.lock +++ b/bun.lock @@ -4,6 +4,7 @@ "": { "name": "ohif-monorepo-root", "dependencies": { + "@cornerstonejs/nifti-volume-loader": "^4.10.0", "execa": "8.0.1", }, "devDependencies": { @@ -52,7 +53,7 @@ }, "addOns/externals/devDependencies": { "name": "@externals/devDependencies", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "@kitware/vtk.js": "34.15.1", @@ -137,14 +138,14 @@ }, "addOns/externals/dicom-microscopy-viewer": { "name": "@externals/dicom-microscopy-viewer", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "dicom-microscopy-viewer": "0.48.6", }, }, "extensions/cornerstone": { "name": "@ohif/extension-cornerstone", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/adapters": "4.14.6", @@ -170,8 +171,8 @@ "@cornerstonejs/codec-openjpeg": "1.3.0", "@cornerstonejs/codec-openjph": "2.4.7", "@cornerstonejs/dicom-image-loader": "4.14.6", - "@ohif/core": "3.12.0-beta.110", - "@ohif/ui": "3.12.0-beta.110", + "@ohif/core": "3.12.0-beta.111", + "@ohif/ui": "3.12.0-beta.111", "dcmjs": "0.45.0", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", @@ -183,7 +184,7 @@ }, "extensions/cornerstone-dicom-pmap": { "name": "@ohif/extension-cornerstone-dicom-pmap", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/adapters": "4.14.6", @@ -206,7 +207,7 @@ }, "extensions/cornerstone-dicom-rt": { "name": "@ohif/extension-cornerstone-dicom-rt", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "react-color": "2.19.3", @@ -226,7 +227,7 @@ }, "extensions/cornerstone-dicom-seg": { "name": "@ohif/extension-cornerstone-dicom-seg", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/adapters": "4.14.6", @@ -249,7 +250,7 @@ }, "extensions/cornerstone-dicom-sr": { "name": "@ohif/extension-cornerstone-dicom-sr", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/adapters": "4.14.6", @@ -258,10 +259,10 @@ "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.12.0-beta.110", - "@ohif/extension-cornerstone": "3.12.0-beta.110", - "@ohif/extension-measurement-tracking": "3.12.0-beta.110", - "@ohif/ui": "3.12.0-beta.110", + "@ohif/core": "3.12.0-beta.111", + "@ohif/extension-cornerstone": "3.12.0-beta.111", + "@ohif/extension-measurement-tracking": "3.12.0-beta.111", + "@ohif/ui": "3.12.0-beta.111", "dcmjs": "0.45.0", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", @@ -271,7 +272,7 @@ }, "extensions/cornerstone-dynamic-volume": { "name": "@ohif/extension-cornerstone-dynamic-volume", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/core": "4.14.6", @@ -293,16 +294,17 @@ }, "extensions/default": { "name": "@ohif/extension-default", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/calculate-suv": "1.1.0", + "@cornerstonejs/nifti-volume-loader": "4.14.6", "lodash.get": "4.4.2", "lodash.uniqby": "4.7.0", }, "peerDependencies": { - "@ohif/core": "3.12.0-beta.110", - "@ohif/i18n": "3.12.0-beta.110", + "@ohif/core": "3.12.0-beta.111", + "@ohif/i18n": "3.12.0-beta.111", "dcmjs": "0.45.0", "dicomweb-client": "0.10.4", "prop-types": "15.8.1", @@ -316,7 +318,7 @@ }, "extensions/dicom-microscopy": { "name": "@ohif/extension-dicom-microscopy", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/codec-charls": "1.2.3", @@ -341,7 +343,7 @@ }, "extensions/dicom-pdf": { "name": "@ohif/extension-dicom-pdf", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "classnames": "2.5.1", @@ -358,7 +360,7 @@ }, "extensions/dicom-video": { "name": "@ohif/extension-dicom-video", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "classnames": "2.5.1", @@ -375,20 +377,20 @@ }, "extensions/measurement-tracking": { "name": "@ohif/extension-measurement-tracking", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", - "@ohif/ui": "3.12.0-beta.110", + "@ohif/ui": "3.12.0-beta.111", "@xstate/react": "3.2.2", "xstate": "4.38.3", }, "peerDependencies": { "@cornerstonejs/core": "4.14.6", "@cornerstonejs/tools": "4.14.6", - "@ohif/core": "3.12.0-beta.110", - "@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.110", - "@ohif/extension-default": "3.12.0-beta.110", - "@ohif/ui": "3.12.0-beta.110", + "@ohif/core": "3.12.0-beta.111", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.111", + "@ohif/extension-default": "3.12.0-beta.111", + "@ohif/ui": "3.12.0-beta.111", "classnames": "2.5.1", "dcmjs": "0.45.0", "lodash.debounce": "4.0.8", @@ -401,7 +403,7 @@ }, "extensions/test-extension": { "name": "@ohif/extension-test", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "classnames": "2.5.1", @@ -418,7 +420,7 @@ }, "extensions/tmtv": { "name": "@ohif/extension-tmtv", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "classnames": "2.5.1", @@ -435,7 +437,7 @@ }, "extensions/usAnnotation": { "name": "@ohif/extension-ultrasound-pleura-bline", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/core": "4.14.6", @@ -487,7 +489,7 @@ }, "modes/basic": { "name": "@ohif/mode-basic", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", }, @@ -508,7 +510,7 @@ }, "modes/basic-dev-mode": { "name": "@ohif/mode-basic-dev-mode", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -528,7 +530,7 @@ }, "modes/basic-test-mode": { "name": "@ohif/mode-test", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -550,7 +552,7 @@ }, "modes/longitudinal": { "name": "@ohif/mode-longitudinal", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -574,7 +576,7 @@ }, "modes/microscopy": { "name": "@ohif/mode-microscopy", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -586,7 +588,7 @@ }, "modes/preclinical-4d": { "name": "@ohif/mode-preclinical-4d", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", }, @@ -605,7 +607,7 @@ }, "modes/segmentation": { "name": "@ohif/mode-segmentation", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -650,7 +652,7 @@ }, "modes/tmtv": { "name": "@ohif/mode-tmtv", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -671,7 +673,7 @@ }, "modes/usAnnotation": { "name": "@ohif/mode-ultrasound-pleura-bline", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/core": "4.14.6", @@ -710,7 +712,7 @@ }, "platform/app": { "name": "@ohif/app", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/codec-charls": "1.2.3", @@ -789,7 +791,7 @@ }, "platform/cli": { "name": "@ohif/cli", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "bin": { "ohif-cli": "src/index.js", }, @@ -813,7 +815,7 @@ }, "platform/core": { "name": "@ohif/core", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "dcmjs": "0.45.0", @@ -840,14 +842,14 @@ "@cornerstonejs/codec-openjph": "2.4.7", "@cornerstonejs/core": "4.14.6", "@cornerstonejs/dicom-image-loader": "4.14.6", - "@ohif/ui": "3.12.0-beta.110", + "@ohif/ui": "3.12.0-beta.111", "cornerstone-math": "0.1.9", "dicom-parser": "1.8.21", }, }, "platform/i18n": { "name": "@ohif/i18n", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@babel/runtime": "7.28.2", "i18next-locize-backend": "2.2.2", @@ -872,7 +874,7 @@ }, "platform/ui": { "name": "@ohif/ui", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@testing-library/react": "13.4.0", "browser-detect": "0.2.28", @@ -923,7 +925,7 @@ }, "platform/ui-next": { "name": "@ohif/ui-next", - "version": "3.12.0-beta.110", + "version": "3.12.0-beta.111", "dependencies": { "@radix-ui/react-accordion": "1.2.11", "@radix-ui/react-checkbox": "1.3.2", @@ -1251,6 +1253,8 @@ "@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@4.14.6", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "4.14.6", "@cornerstonejs/tools": "4.14.6", "@kitware/vtk.js": "34.15.1" } }, "sha512-4yHUHryMvck1v4RC6AxNhGH6Y28sP7Y71dudWCfXtYuMY2bKkBfoumQhh+IvjczJTrURT9zgg9O5krKOQHe/fQ=="], + "@cornerstonejs/nifti-volume-loader": ["@cornerstonejs/nifti-volume-loader@4.15.0", "", { "dependencies": { "nifti-reader-js": "0.6.9" }, "peerDependencies": { "@cornerstonejs/core": "4.15.0" } }, "sha512-mbpdPB8zzfJ7Gnq0cx3ka1hE9VcWKU/So9OQQqk0HrQl03yB9uIIDu9GnvvNtX+IDZN7jkZWMbYHUJu7I2+BxA=="], + "@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@4.14.6", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "4.14.6", "@cornerstonejs/tools": "4.14.6", "@kitware/vtk.js": "34.15.1" } }, "sha512-VPNaEbj2/sRv2Acn3m1NaPFECDGZJ8I9aNUC6+vg86aRbbJtDjGvqXUyqSPcW2rYGaWtnEx1RWCQhSE/5HfLOA=="], "@cornerstonejs/tools": ["@cornerstonejs/tools@4.14.6", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "4.4.2", "lodash.get": "4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "4.14.6", "@kitware/vtk.js": "34.15.1", "@types/d3-array": "3.2.1", "@types/d3-interpolate": "3.0.4", "d3-array": "3.2.4", "d3-interpolate": "3.0.1", "gl-matrix": "3.4.3" } }, "sha512-8H5cHhzc0K/H/mdwU/qpuESda/5+NIle5i2d2LjK5+AEw/qFjTi0/+ofPcFemXGmnQVgiuioZkyvTCirlqNDug=="], @@ -3947,6 +3951,8 @@ "next-themes": ["next-themes@0.3.0", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18", "react-dom": "^16.8 || ^17 || ^18" } }, "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w=="], + "nifti-reader-js": ["nifti-reader-js@0.6.9", "", { "dependencies": { "fflate": "*" } }, "sha512-oEC35cuYFR0i+KeXhaK0hJz4oSj511Vo7LmO/6iqbxzwek6XWnBi77VURlk4jsnJ86tlJNpYv6ujmSDpPP46Tw=="], + "no-case": ["no-case@3.0.4", "", { "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg=="], "node-addon-api": ["node-addon-api@3.2.1", "", {}, "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="], @@ -5365,6 +5371,8 @@ "@ohif/app/copy-webpack-plugin": ["copy-webpack-plugin@10.2.4", "", { "dependencies": { "fast-glob": "^3.2.7", "glob-parent": "^6.0.1", "globby": "^12.0.2", "normalize-path": "^3.0.0", "schema-utils": "^4.0.0", "serialize-javascript": "^6.0.0" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg=="], + "@ohif/extension-default/@cornerstonejs/nifti-volume-loader": ["@cornerstonejs/nifti-volume-loader@4.14.6", "", { "dependencies": { "nifti-reader-js": "0.6.9" }, "peerDependencies": { "@cornerstonejs/core": "4.14.6" } }, "sha512-KmLt3kP3DdwjlKMEFGb2hn0IjMP4P/WzHrkVGFFf2V6V9l3AWlJjwrlV1kFSr9vsQSesBEwWM60j9Km1jGL0qA=="], + "@ohif/extension-default/webpack": ["webpack@5.101.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.2", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-B4t+nJqytPeuZlHuIKTbalhljIFXeNRqrUGAQgTGlfOl2lXXKXw+yZu6bicycP+PUlM44CxBjCFD6aciKFT3LQ=="], "@ohif/extension-measurement-tracking/webpack": ["webpack@5.101.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.2", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-B4t+nJqytPeuZlHuIKTbalhljIFXeNRqrUGAQgTGlfOl2lXXKXw+yZu6bicycP+PUlM44CxBjCFD6aciKFT3LQ=="], diff --git a/extensions/cornerstone/src/init.tsx b/extensions/cornerstone/src/init.tsx index c8fc0ffade4..0e219c770fa 100644 --- a/extensions/cornerstone/src/init.tsx +++ b/extensions/cornerstone/src/init.tsx @@ -9,6 +9,7 @@ import { EVENTS, metaData, volumeLoader, + imageLoader, imageLoadPoolManager, getEnabledElement, Settings, @@ -164,6 +165,9 @@ export default async function init({ cornerstoneStreamingDynamicImageVolumeLoader ); + imageLoader.registerImageLoader('nifti', cornerstoneNiftiImageLoader); + + // Register strategies using the wrapper const imageLoadStrategies = { interleaveCenter: interleaveCenterLoader, diff --git a/extensions/default/package.json b/extensions/default/package.json index 349323f7571..2b4a2273190 100644 --- a/extensions/default/package.json +++ b/extensions/default/package.json @@ -49,6 +49,7 @@ "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/calculate-suv": "1.1.0", + "@cornerstonejs/nifti-volume-loader": "4.14.6", "lodash.get": "4.4.2", "lodash.uniqby": "4.7.0" } diff --git a/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx b/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx index 052d0fe2839..fc414c79936 100644 --- a/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx +++ b/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx @@ -190,9 +190,14 @@ function PanelStudyBrowser({ currentDisplaySets.forEach(async dSet => { const newImageSrcEntry = {}; const displaySet = displaySetService.getDisplaySetByUID(dSet.displaySetInstanceUID); - const imageIds = dataSource.getImageIdsForDisplaySet(dSet); - const imageId = getImageIdForThumbnail(displaySet, imageIds); + // Wait for imageIds if NIFTI is detected and imageIds are not yet loaded + let imageId; + if (displaySet.niftiURL && (!imageIds || imageIds.length === 0)) { + await displaySet.loadImageIds(); + imageIds = displaySet.imageIds; + } + imageId = getImageIdForThumbnail(displaySet, imageIds); // TODO: Is it okay that imageIds are not returned here for SR displaySets? if (displaySet?.unsupported) { diff --git a/extensions/default/src/getSopClassHandlerModule.js b/extensions/default/src/getSopClassHandlerModule.js index 902e679a984..4341c70200c 100644 --- a/extensions/default/src/getSopClassHandlerModule.js +++ b/extensions/default/src/getSopClassHandlerModule.js @@ -1,9 +1,12 @@ import { utils, classes } from '@ohif/core'; +import { createNiftiImageIdsAndCacheMetadata } from '@cornerstonejs/nifti-volume-loader'; + import i18n from '@ohif/i18n'; import { id } from './id'; import getDisplaySetMessages from './getDisplaySetMessages'; import getDisplaySetsFromUnsupportedSeries from './getDisplaySetsFromUnsupportedSeries'; import { chartHandler } from './SOPClassHandlers/chartSOPClassHandler'; +import { joinUrl, isAbsolutePathOrUrl } from './utils/urlUtils'; const { isImage, @@ -88,6 +91,37 @@ const makeDisplaySet = (instances, index) => { // set appropriate attributes to image set... const messages = getDisplaySetMessages(instances, isReconstructable, isDynamicVolume); + const { niftiPrivateTagName, niftiBaseUrl } = dataSource.getConfig?.() || {}; + let niftiURL; + const niftiPath = instance[niftiPrivateTagName]; + if (niftiPath) { + if (typeof niftiPath === 'string' && !niftiPath.includes('dedupped')) { + if (isAbsolutePathOrUrl(niftiPath)) { + niftiURL = niftiPath; + } else { + niftiURL = joinUrl(niftiBaseUrl, niftiPath); + } + } + } + + if (niftiURL) { + imageSet.setAttributes({ + niftiURL, + loadImageIds: async () => { + try { + const imageIds = await createNiftiImageIdsAndCacheMetadata({ url: niftiURL }); + imageSet.setAttributes({ imageIds }); + imageSet.setAttributes({ + numImageFrames: Array.isArray(imageIds) ? imageIds.length : 0, + }); + imageSet.setAttributes({ isReconstructable: true }); + } catch (e) { + console.error('Error loading niftiURL:', e); + } + }, + }); + } + imageSet.setAttributes({ volumeLoaderSchema, displaySetInstanceUID: imageSet.uid, // create a local alias for the imageSet UID diff --git a/extensions/default/src/utils/urlUtils.js b/extensions/default/src/utils/urlUtils.js new file mode 100644 index 00000000000..2e5fee9026c --- /dev/null +++ b/extensions/default/src/utils/urlUtils.js @@ -0,0 +1,28 @@ +/** + * Checks if a path is an absolute URL (http/https) or absolute path (starts with /) + * @param {string} path - The path to check + * @returns {boolean} True if the path is absolute + */ +export function isAbsolutePathOrUrl(path) { + if (!path || typeof path !== 'string') { + return false; + } + return path.startsWith('http://') || path.startsWith('https://') || path.startsWith('/'); +} + +/** + * Joins a base URL with a relative path + * @param {string} baseUrl - The base URL + * @param {string} path - The path to join + * @returns {string} The joined URL + */ +export function joinUrl(baseUrl, path) { + if (!baseUrl) return path || ''; + if (!path) return baseUrl; + + // Remove trailing slash from baseUrl and leading slash from path + const base = baseUrl.replace(/\/+$/, ''); + const relativePath = path.replace(/^\/+/, ''); + + return `${base}/${relativePath}`; +} diff --git a/package.json b/package.json index c699ea88994..54cbb8b8e1e 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "link-list": "npm ls --depth=0 --link=true" }, "dependencies": { + "@cornerstonejs/nifti-volume-loader": "^4.10.0", "execa": "8.0.1" }, "optionalDependencies": { diff --git a/yarn.lock b/yarn.lock index 23c59da91dd..43f30ed0dcb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1361,6 +1361,20 @@ "@itk-wasm/morphological-contour-interpolation" "1.1.0" itk-wasm "1.0.0-b.165" +"@cornerstonejs/nifti-volume-loader@4.14.6": + version "4.14.6" + resolved "https://registry.yarnpkg.com/@cornerstonejs/nifti-volume-loader/-/nifti-volume-loader-4.14.6.tgz#8d22858f48d736076bf52c8951b5d9db60c54968" + integrity sha512-KmLt3kP3DdwjlKMEFGb2hn0IjMP4P/WzHrkVGFFf2V6V9l3AWlJjwrlV1kFSr9vsQSesBEwWM60j9Km1jGL0qA== + dependencies: + nifti-reader-js "0.6.9" + +"@cornerstonejs/nifti-volume-loader@^4.10.0": + version "4.15.0" + resolved "https://registry.yarnpkg.com/@cornerstonejs/nifti-volume-loader/-/nifti-volume-loader-4.15.0.tgz#7119c14801a7222507de786475af667e3df9b8fa" + integrity sha512-mbpdPB8zzfJ7Gnq0cx3ka1hE9VcWKU/So9OQQqk0HrQl03yB9uIIDu9GnvvNtX+IDZN7jkZWMbYHUJu7I2+BxA== + dependencies: + nifti-reader-js "0.6.9" + "@cornerstonejs/polymorphic-segmentation@4.14.6": version "4.14.6" resolved "https://registry.yarnpkg.com/@cornerstonejs/polymorphic-segmentation/-/polymorphic-segmentation-4.14.6.tgz#26154ddd0b1edceca9e6136e264d2d7b34df7c4f" @@ -8833,6 +8847,11 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4: node-domexception "^1.0.0" web-streams-polyfill "^3.0.3" +fflate@*: + version "0.8.2" + resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea" + integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== + fflate@0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.7.3.tgz#288b034ff0e9c380eaa2feff48c787b8371b7fa5" @@ -13355,6 +13374,13 @@ next-themes@0.3.0: resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.3.0.tgz#b4d2a866137a67d42564b07f3a3e720e2ff3871a" integrity sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w== +nifti-reader-js@0.6.9: + version "0.6.9" + resolved "https://registry.yarnpkg.com/nifti-reader-js/-/nifti-reader-js-0.6.9.tgz#737b88eb951c6b98841ce888204a9370214b17e5" + integrity sha512-oEC35cuYFR0i+KeXhaK0hJz4oSj511Vo7LmO/6iqbxzwek6XWnBi77VURlk4jsnJ86tlJNpYv6ujmSDpPP46Tw== + dependencies: + fflate "*" + no-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"