diff --git a/dicom-viewer/index.html b/dicom-viewer/index.html index e4b78ea..f4f0df1 100644 --- a/dicom-viewer/index.html +++ b/dicom-viewer/index.html @@ -7,7 +7,21 @@ Vite + React + TS -
+
+ + + + + diff --git a/dicom-viewer/src/App.tsx b/dicom-viewer/src/App.tsx index 79689c8..3d63041 100644 --- a/dicom-viewer/src/App.tsx +++ b/dicom-viewer/src/App.tsx @@ -9,7 +9,6 @@ import { init as csToolsInit, addTool, ToolGroupManager, Enums as csToolsEnums, import { LengthTool, ProbeTool, ArrowAnnotateTool, RectangleROITool, EllipticalROITool, PlanarFreehandROITool, PlanarFreehandContourSegmentationTool, SculptorTool, CrosshairsTool, ReferenceLinesTool, ReferenceCursors } from "@cornerstonejs/tools" import { init as dicomImageLoaderInit } from "@cornerstonejs/dicom-image-loader" import * as cornerstoneTools from '@cornerstonejs/tools'; -import { StackScrollOutOfBoundsEvent } from 'core/src/types/EventTypes'; import { segmentation, Enums as csEnums } from '@cornerstonejs/tools'; import { voi } from "@cornerstonejs/tools/utilities" import { readDicomElementExplicit } from "dicom-parser" @@ -18,6 +17,20 @@ import { metaData } from '@cornerstonejs/core'; import * as dicomParser from "dicom-parser"; import cornerstoneDICOMImageLoader from "@cornerstonejs/dicom-image-loader" +// Extend the Window interface to include cornerstoneTools +declare global { + interface Window { + cornerstoneTools: typeof cornerstoneTools; + } +} + + +// Add this type for clarity +declare global { + interface Window { + loadDicomStackFromFiles?: (files: FileList | File[]) => void; + } +} window.cornerstoneTools = cornerstoneTools; @@ -203,7 +216,10 @@ function App() { if (url.startsWith("blob:") || url.startsWith("/")) { if (cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager) { // If load returns a promise, collect it - const result = cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager.load(url); + // @ts-ignore + const result = cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager.load( + url, + ); if (result && typeof result.then === "function") { loadPromises.push(result); } @@ -367,6 +383,21 @@ function App() { setMetaModalOpen(true); }; +// Inside your App component: +useEffect(() => { + window.loadDicomStackFromFiles = (files: FileList | File[]) => { + // Convert FileList to array if needed + const fileArray = Array.from(files); + // Your logic to handle the files and load them as a stack + // For example, call your existing handler: + handleFilesSelected({ target: { files: fileArray } } as any); + }; + // Cleanup on unmount + return () => { + window.loadDicomStackFromFiles = undefined; + }; +}, []); + const [altPressed, setAltPressed] = useState(false); useEffect(() => { @@ -472,24 +503,39 @@ function App() { const fileInputRef = useRef(null); // Handler for file input change - const handleFilesSelected = async (event: React.ChangeEvent) => { - const files = event.target.files; - if (!files || files.length === 0) return; +const handleFilesSelected = async (event: React.ChangeEvent | { target: { files: File[] | FileList } }) => { + const files = event.target.files; + if (!files || files.length === 0) return; - // Reset order by slice location when new files are loaded - setOrderBySliceLocation(false); + setOrderBySliceLocation(false); - // Convert FileList to Array and sort by name (optional, for series order) - const fileArray = Array.from(files).sort((a, b) => a.name.localeCompare(b.name)); + // Convert FileList to Array and sort by name (optional) + const fileArray = Array.from(files).sort((a, b) => a.name.localeCompare(b.name)); - // Create imageIds for each file using the cornerstone-wado-image-loader file API - // (Assumes cornerstone-wado-image-loader is configured for file loading) - const imageIds = fileArray.map( - file => `wadouri:${URL.createObjectURL(file)}` - ); + // Create imageIds for each file + const imageIds = fileArray.map( + file => `wadouri:${URL.createObjectURL(file)}` + ); + // Add as a new stack + setAvailableStacks(prev => { + const next = [...prev, imageIds]; + // Optionally, set the new stack as active in the first viewport: + setViewportImageIds(vpPrev => { + const vpNext = [...vpPrev]; + vpNext[0] = imageIds; + return vpNext; + }); + setSelectedStackIdx(selPrev => { + const selNext = [...selPrev]; + selNext[0] = next.length - 1; + return selNext; + }); + // Cache imageIds and load metadata for this stack setLoadedImageIdsAndCacheMeta(imageIds); - }; + return next; + }); +}; const handleToolChange = (mouseButton: string, toolName: string) => { @@ -862,6 +908,13 @@ function App() { updateActiveReferenceLineViewport(mainToolGroup) }, [activeViewport, referenceLinesEnabled]); + useEffect(() => { + if (fileInputRef.current) { + fileInputRef.current.setAttribute("webkitdirectory", "true"); + fileInputRef.current.setAttribute("directory", "true"); + } + }, []); + useEffect(() => { if (!orderBySliceLocation) return; @@ -1337,7 +1390,8 @@ function App() {