diff --git a/bun.lockb b/bun.lockb index a189bc4..286029b 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index e9d6cbc..4929d32 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,8 @@ "private": true, "version": "0.0.0", "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" + "engines": { + "node": ">=20" }, "dependencies": { "@cornerstonejs/calculate-suv": "1.1.0", @@ -15,20 +12,20 @@ "@cornerstonejs/dicom-image-loader": "^2.1.16", "@cornerstonejs/nifti-volume-loader": "^2.1.16", "@cornerstonejs/tools": "^2.1.16", - "dcmjs": "0.33.0", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", - "dicomweb-client": "0.10.4", + "dicomweb-client": "0.11.2", "react": "^18.2.0", "react-dom": "^18.2.0", "vite-plugin-wasm": "^3.3.0" }, "devDependencies": { - "@babel/preset-react": "^7.25.9", + "@babel/preset-react": "7.26.3", "@originjs/vite-plugin-commonjs": "^1.0.3", - "@rollup/plugin-babel": "^6.0.4", + "@rollup/plugin-babel": "6.0.4", "@rollup/plugin-commonjs": "^28.0.1", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-typescript": "^12.1.1", + "@rollup/plugin-node-resolve": "15.3.1", + "@rollup/plugin-typescript": "11.1.6", "@rollup/plugin-wasm": "^6.2.2", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", @@ -43,9 +40,14 @@ "rollup-plugin-dts": "^6.1.1", "rollup-plugin-peer-deps-external": "^2.2.4", "rollup-plugin-postcss": "^4.0.2", - "rollup-plugin-terser": "^7.0.2", - "typescript": "^5.6.3", + "typescript": "5.5.4", "vite": "^5.2.0", "vite-plugin-static-copy": "^1.0.5" + }, + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" } -} +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index e0ff41d..cd689dd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,17 +21,19 @@ function App() { } running.current = true + // 4.x: init() unchanged. For 3.x-style viewport padding use init({ rendering: { useLegacyCameraFOV: true } }) await csRenderInit() await csToolsInit() - dicomImageLoaderInit({maxWebWorkers:1}) + dicomImageLoaderInit({ maxWebWorkers: 1 }) // Get Cornerstone imageIds and fetch metadata into RAM + // Same study/series UIDs; base URL from CS3D examples (old backend may have moved) const imageIds = await createImageIdsAndCacheMetaData({ StudyInstanceUID: "1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463", SeriesInstanceUID: "1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561", - wadoRsRoot: "https://d3t6nz73ql33tx.cloudfront.net/dicomweb", + wadoRsRoot: "https://d14fa38qiwhyfd.cloudfront.net/dicomweb", }) // Instantiate a rendering engine @@ -60,12 +62,11 @@ function App() { imageIds, }) - // Set the volume to load - // @ts-ignore + // Start loading the volume (streaming) volume.load() - // Set the volume on the viewport and it's default properties - viewport.setVolumes([{ volumeId}]) + // Set the volume on the viewport (4.x: setVolumes is async) + await viewport.setVolumes([{ volumeId }], true) // Render the image viewport.render() diff --git a/src/lib/createImageIdsAndCacheMetaData.ts b/src/lib/createImageIdsAndCacheMetaData.ts index cbd0b7b..4ae9fef 100644 --- a/src/lib/createImageIdsAndCacheMetaData.ts +++ b/src/lib/createImageIdsAndCacheMetaData.ts @@ -1,7 +1,6 @@ import { api } from "dicomweb-client" import cornerstoneDICOMImageLoader from "@cornerstonejs/dicom-image-loader" -/** /** * Uses dicomweb-client to fetch metadata of a study, cache it in cornerstone, * and return a list of imageIds for the frames. diff --git a/src/main.tsx b/src/main.tsx index 1be872b..8ea316e 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,9 +1,18 @@ import React from 'react' import ReactDOM from 'react-dom/client' +import { setConfiguration } from '@cornerstonejs/core'; +import { getWasmBasePath } from 'virtual:cornerstone-wasm-config'; import App from './App.tsx' import Nifti from './Nifti.tsx' import './index.css' +// Set WASM loading base path (for subpath deployment). Use plugin's value or Vite's base. +const wasmBase = getWasmBasePath() || import.meta.env.BASE_URL +if (wasmBase) { + // @ts-ignore + setConfiguration({ wasmBasePath: wasmBase }) +} + ReactDOM.createRoot(document.getElementById('root')!).render( diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 11f02fe..96b6f2a 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -1 +1,5 @@ /// + +declare module 'virtual:cornerstone-wasm-config' { + export function getWasmBasePath(): string; +} diff --git a/vite-plugin-cornerstone-wasm.ts b/vite-plugin-cornerstone-wasm.ts new file mode 100644 index 0000000..b15fa5f --- /dev/null +++ b/vite-plugin-cornerstone-wasm.ts @@ -0,0 +1,55 @@ +import type { Plugin } from 'vite'; + +const VIRTUAL_MODULE_ID = 'virtual:cornerstone-wasm-config'; +const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID; + +export interface VitePluginCornerstoneWasmOptions { + /** + * Base path (or URL prefix) for loading WASM binaries. + * When set, this is passed to Cornerstone's config so WASM URLs are resolved + * correctly when the app is served under a subpath (e.g. base '/subpath/'). + * If not set, the plugin does not inject a base path (app can use setConfiguration({ wasmBasePath: import.meta.env.BASE_URL }) instead). + */ + wasmBasePath?: string; +} + +/** + * Vite plugin that allows replacing the WASM loading path used by Cornerstone + * (e.g. @cornerstonejs/dicom-image-loader codecs). Use when the app is served + * under a subpath or you need to point WASM to a custom URL. + * + * Usage: + * 1. Add the plugin with optional wasmBasePath: + * cornerstoneWasm({ wasmBasePath: '/subpath/' }) + * 2. In your app entry (e.g. main.tsx), before loading any images: + * import { getWasmBasePath } from 'virtual:cornerstone-wasm-config'; + * import { setConfiguration } from '@cornerstonejs/core'; + * const base = getWasmBasePath(); + * if (base) setConfiguration({ wasmBasePath: base }); + * + * If you don't use the virtual module, you can instead call + * setConfiguration({ wasmBasePath: import.meta.env.BASE_URL }) for subpath support. + */ +export function vitePluginCornerstoneWasm( + options: VitePluginCornerstoneWasmOptions = {} +): Plugin { + const { wasmBasePath = '' } = options; + + return { + name: 'vite-plugin-cornerstone-wasm', + resolveId(id: string) { + if (id === VIRTUAL_MODULE_ID) return RESOLVED_VIRTUAL_MODULE_ID; + return null; + }, + load(id: string) { + if (id !== RESOLVED_VIRTUAL_MODULE_ID) return null; + return ` +export function getWasmBasePath() { + return ${JSON.stringify(wasmBasePath)}; +} +`; + }, + }; +} + +export default vitePluginCornerstoneWasm; diff --git a/vite.config.ts b/vite.config.ts index 9752594..2cbbec1 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from "vite" import react from "@vitejs/plugin-react" import { viteCommonjs } from "@originjs/vite-plugin-commonjs" +import { vitePluginCornerstoneWasm } from "./vite-plugin-cornerstone-wasm" /** * Vite configuration for the application. @@ -30,20 +31,27 @@ import { viteCommonjs } from "@originjs/vite-plugin-commonjs" * ``` */ export default defineConfig({ + base: '/subpath/', plugins: [ react(), // for dicom-parser viteCommonjs(), + // Optional: pass wasmBasePath to override WASM loading path (e.g. for subpath deployment). + // When not set, use setConfiguration({ wasmBasePath: import.meta.env.BASE_URL }) in main.tsx. + vitePluginCornerstoneWasm({ wasmBasePath: '' }), ], // seems like only required in dev mode optimizeDeps: { - exclude: ["@cornerstonejs/dicom-image-loader"], - include: ["dicom-parser"], + exclude: ['@cornerstonejs/dicom-image-loader'], + include: ['dicom-parser'], + }, + build: { + minify: false, }, worker: { - format: "es", + format: 'es', rollupOptions: { - external: ["@icr/polyseg-wasm"], + external: ['@icr/polyseg-wasm'], }, }, -}) +});