numerous more fixes
This commit is contained in:
@@ -7,8 +7,10 @@
|
|||||||
<title>Vite + React + TS</title>
|
<title>Vite + React + TS</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root" class="dicom-viewer-root" style="max-height:500px; height:500px; overflow:auto;"></div>
|
<div id="root" class="dicom-viewer-test-root" style="max-height:500px; height:500px; overflow:auto;"></div>
|
||||||
<div id="root2" class="dicom-viewer-root" style="max-height:500px; height:500px; overflow:auto;"></div>
|
<div id="root2" class="dicom-viewer-root" style="max-height:500px; height:500px; overflow:auto;"
|
||||||
|
data-images="["http://localhost:8000/media/atlas/dicom/4596ea7654b7a5074d2c3f40039c556b11fb01ae.dcm", "http://localhost:8000/media/atlas/dicom/b2fa301bc731d15be3e000402c9a79c114d17aa5.dcm", "http://localhost:8000/media/atlas/dicom/66310ef801fbecb37c5ced160c450dfd5ecc6d2d.dcm", "http://localhost:8000/media/atlas/dicom/63d2d117bf256a76a61190066f91eda6c4d67c8f.dcm", "http://localhost:8000/media/atlas/dicom/181a3575ea7fb8bf8f6449ff61e6e369a3247454.dcm", "http://localhost:8000/media/atlas/dicom/7c5c0538339266ac8897fa21c481b2f61172068e.dcm", "http://localhost:8000/media/atlas/dicom/1681410681308af7111af6644fed598fde304b96.dcm", "http://localhost:8000/media/atlas/dicom/4adcd3d7a64a9c59b530f730ce0d991bf7f27813.dcm", "http://localhost:8000/media/atlas/dicom/671e6004c7fb402a627a2282bbf304d7b4dcc12b.dcm", "http://localhost:8000/media/atlas/dicom/8d18ac56914b9de72b8aecced09654ec4b2c9bb7.dcm", "http://localhost:8000/media/atlas/dicom/74ce708a90dff0636fc4bc914fe47e8b6d9320bd.dcm", "http://localhost:8000/media/atlas/dicom/a96b6c9900570e076edf849a0ae3b52734ed2650.dcm", "http://localhost:8000/media/atlas/dicom/0183718d9336458478ec42130395d78e01492bd6.dcm", "http://localhost:8000/media/atlas/dicom/52eee968bbce5590274346eed43f1837f9daba11.dcm", "http://localhost:8000/media/atlas/dicom/961449f88197566c4643fcfd20f48ea4e810490b.dcm", "http://localhost:8000/media/atlas/dicom/1a16ad5b25e11739eadbcb8ea461072b100113a0.dcm", "http://localhost:8000/media/atlas/dicom/b89ddcc3f499059d2dedb0028a2bb1ce4abf5574.dcm", "http://localhost:8000/media/atlas/dicom/c1379c03a4e449c2fa88784d601cb766af907739.dcm", "http://localhost:8000/media/atlas/dicom/4fb532d5003c59a73f7e4248f365b1a8660ea710.dcm", "http://localhost:8000/media/atlas/dicom/e861b0de042a1f213cbccc995679aacd29c7ef53.dcm", "http://localhost:8000/media/atlas/dicom/1a854282afe264c3afff6a6e287cd646b2193a0c.dcm", "http://localhost:8000/media/atlas/dicom/f59d6d9bc33160abad3af8e2b246179b3574d884.dcm", "http://localhost:8000/media/atlas/dicom/04fadab0ed8b3f1925e601c4f9cfb792c95626c2.dcm", "http://localhost:8000/media/atlas/dicom/d45d0b12fb650e65daeeee4e5fa3b21c988597f6.dcm", "http://localhost:8000/media/atlas/dicom/e19c47b7787ac45dbc225319dfa143dc2c1fdf1f.dcm", "http://localhost:8000/media/atlas/dicom/dccabf3103a5be254a45457ce2d8d9ce9cb19a6a.dcm"]"
|
||||||
|
></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
|
||||||
<input type="file" id="dicomInput" multiple />
|
<input type="file" id="dicomInput" multiple />
|
||||||
|
|||||||
+62
-43
@@ -1,5 +1,4 @@
|
|||||||
import { useEffect, useRef, useCallback, useState } from "react"
|
import { useEffect, useRef, useCallback, useState } from "react"
|
||||||
import { createImageIds, availableImageIds } from "./lib/createImageIds.ts"
|
|
||||||
import {
|
import {
|
||||||
RenderingEngine,
|
RenderingEngine,
|
||||||
Enums,
|
Enums,
|
||||||
@@ -65,7 +64,6 @@ const { MouseBindings, KeyboardBindings } = csToolsEnums;
|
|||||||
|
|
||||||
const { IMAGE_RENDERED } = Enums.Events;
|
const { IMAGE_RENDERED } = Enums.Events;
|
||||||
|
|
||||||
const MAIN_TOOL_GROUP_ID = 'MAIN_TOOL_GROUP_ID';
|
|
||||||
|
|
||||||
// Common CT presets (add more as needed)
|
// Common CT presets (add more as needed)
|
||||||
const WINDOW_LEVEL_PRESETS = [
|
const WINDOW_LEVEL_PRESETS = [
|
||||||
@@ -115,9 +113,14 @@ const ALL_MOUSE_BINDINGS = [
|
|||||||
{ label: "Mouse Wheel + Left", value: "Wheel_Primary", mask: 524289 },
|
{ label: "Mouse Wheel + Left", value: "Wheel_Primary", mask: 524289 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
type AppProps = {
|
||||||
|
container_id?: string;
|
||||||
|
imageStacks: () => Promise<string[][]>;
|
||||||
|
// ...other props if needed
|
||||||
|
};
|
||||||
|
|
||||||
// App definintion
|
// App definintion
|
||||||
function App() {
|
function App({ container_id, imageStacks, ...props }: AppProps) {
|
||||||
const elementRef = useRef<HTMLDivElement>(null)
|
const elementRef = useRef<HTMLDivElement>(null)
|
||||||
const running = useRef(false)
|
const running = useRef(false)
|
||||||
|
|
||||||
@@ -130,7 +133,8 @@ function App() {
|
|||||||
const [openDropdownIdx, setOpenDropdownIdx] = useState<number | null>(null);
|
const [openDropdownIdx, setOpenDropdownIdx] = useState<number | null>(null);
|
||||||
|
|
||||||
const MAX_VIEWPORTS = 9;
|
const MAX_VIEWPORTS = 9;
|
||||||
const renderingEngineId = "mainRenderingEngine";
|
const renderingEngineId = "renderingEngine_" + container_id;
|
||||||
|
const MAIN_TOOL_GROUP_ID = "TOOL_GROUP_" + container_id;
|
||||||
const renderingEngineRef = useRef<any>(null);
|
const renderingEngineRef = useRef<any>(null);
|
||||||
|
|
||||||
const restoringStateRef = useRef(false);
|
const restoringStateRef = useRef(false);
|
||||||
@@ -148,7 +152,7 @@ function App() {
|
|||||||
|
|
||||||
// Load available stacks on mount
|
// Load available stacks on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
availableImageIds().then(async stacks => {
|
imageStacks().then(async stacks => {
|
||||||
// Try to extract StudyInstanceUID for each stack
|
// Try to extract StudyInstanceUID for each stack
|
||||||
const stackEntries: StackEntry[] = await Promise.all(
|
const stackEntries: StackEntry[] = await Promise.all(
|
||||||
stacks.map(async imageIds => {
|
stacks.map(async imageIds => {
|
||||||
@@ -663,7 +667,7 @@ function App() {
|
|||||||
// --- In your setup function, use the correct tool group per viewport ---
|
// --- In your setup function, use the correct tool group per viewport ---
|
||||||
let mainToolGroup = ToolGroupManager.getToolGroup(MAIN_TOOL_GROUP_ID);
|
let mainToolGroup = ToolGroupManager.getToolGroup(MAIN_TOOL_GROUP_ID);
|
||||||
if (!mainToolGroup) {
|
if (!mainToolGroup) {
|
||||||
mainToolGroup = setupTools().mainToolGroup;
|
mainToolGroup = setupTools(MAIN_TOOL_GROUP_ID).mainToolGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1065,8 +1069,9 @@ function App() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const apiKey = container_id || "default";
|
||||||
// Export the current viewer state as JSON
|
// Export the current viewer state as JSON
|
||||||
window.exportViewerState = () => {
|
window[`exportViewerState_${apiKey}`] = () => {
|
||||||
const numActive = viewportGrid.rows * viewportGrid.cols;
|
const numActive = viewportGrid.rows * viewportGrid.cols;
|
||||||
const modes = viewportModes.slice(0, numActive);
|
const modes = viewportModes.slice(0, numActive);
|
||||||
const stackIdx = selectedStackIdx.slice(0, numActive);
|
const stackIdx = selectedStackIdx.slice(0, numActive);
|
||||||
@@ -1117,7 +1122,7 @@ function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Import and restore a viewer state from JSON (using stack indices)
|
// Import and restore a viewer state from JSON (using stack indices)
|
||||||
window.importViewerState = (json: string) => {
|
window[`importViewerState_${apiKey}`] = (json: string) => {
|
||||||
try {
|
try {
|
||||||
restoringStateRef.current = true;
|
restoringStateRef.current = true;
|
||||||
const state = typeof json === "string" ? JSON.parse(json) : json;
|
const state = typeof json === "string" ? JSON.parse(json) : json;
|
||||||
@@ -1189,7 +1194,7 @@ function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Export all annotations using cornerstoneTools.annotation.state.getAllAnnotations()
|
// Export all annotations using cornerstoneTools.annotation.state.getAllAnnotations()
|
||||||
window.exportAnnotations = () => {
|
window[`exportAnnotations_${apiKey}`] = () => {
|
||||||
try {
|
try {
|
||||||
let allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations();
|
let allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations();
|
||||||
console.log("Exporting annotations:", allAnnotations);
|
console.log("Exporting annotations:", allAnnotations);
|
||||||
@@ -1221,7 +1226,7 @@ function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Import and restore all annotations using cornerstoneTools.annotation.state.restoreAnnotations()
|
// Import and restore all annotations using cornerstoneTools.annotation.state.restoreAnnotations()
|
||||||
window.importAnnotations = (json: string) => {
|
window[`importAnnotations_${apiKey}`] = (json: string) => {
|
||||||
try {
|
try {
|
||||||
const annotationData = typeof json === "string" ? JSON.parse(json) : json;
|
const annotationData = typeof json === "string" ? JSON.parse(json) : json;
|
||||||
// Remove all existing annotations if needed
|
// Remove all existing annotations if needed
|
||||||
@@ -1246,10 +1251,10 @@ function App() {
|
|||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
return () => {
|
return () => {
|
||||||
window.exportViewerState = undefined;
|
window[`exportViewerState_${apiKey}`] = undefined;
|
||||||
window.importViewerState = undefined;
|
window[`importViewerState_${apiKey}`] = undefined;
|
||||||
window.exportAnnotations = undefined;
|
window[`exportAnnotations_${apiKey}`] = undefined;
|
||||||
window.importAnnotations = undefined;
|
window[`importAnnotations_${apiKey}`] = undefined;
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
viewportGrid,
|
viewportGrid,
|
||||||
@@ -1271,8 +1276,10 @@ function App() {
|
|||||||
const referenceStackObj = availableStacks[referenceStackIdx];
|
const referenceStackObj = availableStacks[referenceStackIdx];
|
||||||
const referenceUID = referenceStackObj?.studyInstanceUID;
|
const referenceUID = referenceStackObj?.studyInstanceUID;
|
||||||
|
|
||||||
|
const numVisible = viewportGrid.rows * viewportGrid.cols;
|
||||||
const viewports = [];
|
const viewports = [];
|
||||||
for (let i = 0; i < MAX_VIEWPORTS; i++) {
|
//for (let i = 0; i < MAX_VIEWPORTS; i++) {
|
||||||
|
for (let i = 0; i < numVisible; i++) {
|
||||||
// Determine if this viewport should be visible in the current grid
|
// Determine if this viewport should be visible in the current grid
|
||||||
const isVisible = i < viewportGrid.rows * viewportGrid.cols;
|
const isVisible = i < viewportGrid.rows * viewportGrid.cols;
|
||||||
|
|
||||||
@@ -1282,7 +1289,7 @@ function App() {
|
|||||||
ref={el => (viewportRefs.current[i] = el)}
|
ref={el => (viewportRefs.current[i] = el)}
|
||||||
style={{
|
style={{
|
||||||
flex: 1,
|
flex: 1,
|
||||||
border: "1px solid #222",
|
border: isVisible ? "1px solid #222" : "none", // Only show border if visible
|
||||||
background: "#000",
|
background: "#000",
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
minHeight: 0,
|
minHeight: 0,
|
||||||
@@ -1292,8 +1299,8 @@ function App() {
|
|||||||
gridRow: Math.floor(i / viewportGrid.cols) + 1,
|
gridRow: Math.floor(i / viewportGrid.cols) + 1,
|
||||||
gridColumn: (i % viewportGrid.cols) + 1,
|
gridColumn: (i % viewportGrid.cols) + 1,
|
||||||
height: "100%",
|
height: "100%",
|
||||||
visibility: isVisible ? "visible" : "hidden",
|
//visibility: isVisible ? "visible" : "hidden",
|
||||||
pointerEvents: isVisible ? "auto" : "none",
|
//pointerEvents: isVisible ? "auto" : "none",
|
||||||
// Remove outline, use only boxShadow for indicator
|
// Remove outline, use only boxShadow for indicator
|
||||||
boxShadow: activeViewport === i
|
boxShadow: activeViewport === i
|
||||||
? "0 0 0 4px #1976d2, 0 0 16px 4px #1976d2cc"
|
? "0 0 0 4px #1976d2, 0 0 16px 4px #1976d2cc"
|
||||||
@@ -1676,20 +1683,23 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{isVisible && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
left: 8,
|
left: 0,
|
||||||
right: 8,
|
right: 0,
|
||||||
bottom: 8,
|
bottom: 8,
|
||||||
zIndex: 20,
|
zIndex: 20,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "flex-end",
|
justifyContent: "center", // Center horizontally
|
||||||
gap: "8px",
|
pointerEvents: "auto",
|
||||||
background: "rgba(34,34,34,0.85)",
|
width: "100%",
|
||||||
borderRadius: "4px",
|
// Remove background/gap if you want only the selector to be visible
|
||||||
padding: "2px 8px",
|
background: "none",
|
||||||
|
borderRadius: "0",
|
||||||
|
padding: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Custom Stack Dropdown */}
|
{/* Custom Stack Dropdown */}
|
||||||
@@ -1787,6 +1797,7 @@ function App() {
|
|||||||
const thisUID = stackObj.studyInstanceUID;
|
const thisUID = stackObj.studyInstanceUID;
|
||||||
const sameStudy = referenceUID && thisUID && referenceUID === thisUID;
|
const sameStudy = referenceUID && thisUID && referenceUID === thisUID;
|
||||||
const dotColor = sameStudy ? "#4caf50" : "#888";
|
const dotColor = sameStudy ? "#4caf50" : "#888";
|
||||||
|
const isEmpty = stackObj.imageIds.length === 0;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={idx}
|
key={idx}
|
||||||
@@ -1795,19 +1806,22 @@ function App() {
|
|||||||
style={{
|
style={{
|
||||||
padding: "6px 12px",
|
padding: "6px 12px",
|
||||||
background: selectedStackIdx[i] === idx ? "#444" : "#222",
|
background: selectedStackIdx[i] === idx ? "#444" : "#222",
|
||||||
color: "#fff",
|
color: isEmpty ? "#aaa" : "#fff",
|
||||||
cursor: "pointer",
|
cursor: isEmpty ? "not-allowed" : "pointer",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
fontWeight: selectedStackIdx[i] === idx ? "bold" : "normal",
|
fontWeight: selectedStackIdx[i] === idx ? "bold" : "normal",
|
||||||
borderBottom: "1px solid #333",
|
borderBottom: "1px solid #333",
|
||||||
userSelect: "none",
|
userSelect: "none",
|
||||||
|
opacity: isEmpty ? 0.6 : 1,
|
||||||
}}
|
}}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
onMouseDown={e => {
|
onMouseDown={e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
if (!isEmpty) {
|
||||||
handleLoadStack(i, idx);
|
handleLoadStack(i, idx);
|
||||||
setOpenDropdownIdx(null);
|
setOpenDropdownIdx(null);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{
|
<span style={{
|
||||||
@@ -1817,7 +1831,8 @@ function App() {
|
|||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
verticalAlign: "middle",
|
verticalAlign: "middle",
|
||||||
}}>●</span>
|
}}>●</span>
|
||||||
Stack {idx + 1} ({stackObj.imageIds.length} images)
|
Stack {idx + 1} ({stackObj.imageIds.length} images
|
||||||
|
{isEmpty && <span style={{ color: "#f44336", marginLeft: 6 }}>(empty)</span>})
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -1825,6 +1840,7 @@ function App() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1833,16 +1849,17 @@ function App() {
|
|||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{
|
// App root
|
||||||
position: "relative",
|
<div style={{
|
||||||
width: "99vw",
|
position: "absolute",
|
||||||
height: "100%", // <-- changed from 500px
|
inset: 0,
|
||||||
maxHeight: "100%", // <-- ensures it doesn't overflow parent
|
width: "100%",
|
||||||
padding: "12px", // <-- Add this line
|
height: "100%",
|
||||||
boxSizing: "border-box", // <-- Ensure padding doesn't shrink content
|
boxSizing: "border-box",
|
||||||
background: "#222",
|
background: "#222",
|
||||||
// overflow: "hidden",
|
padding: "12px",
|
||||||
}}>
|
overflow: "hidden",
|
||||||
|
}}>
|
||||||
<div>
|
<div>
|
||||||
{/* Side menu toggle button */}
|
{/* Side menu toggle button */}
|
||||||
<button
|
<button
|
||||||
@@ -1871,23 +1888,22 @@ function App() {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Side menu drawer */}
|
{/* Side menu drawer */}
|
||||||
|
{sideMenuOpen && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: 0,
|
top: 0,
|
||||||
left: sideMenuOpen ? 0 : -260,
|
left: 0,
|
||||||
width: 220,
|
width: 220,
|
||||||
height: "100%",
|
height: "100%",
|
||||||
background: "#222",
|
background: "#222",
|
||||||
color: "#fff",
|
color: "#fff",
|
||||||
zIndex: 199,
|
zIndex: 199,
|
||||||
boxShadow: sideMenuOpen ? "2px 0 12px rgba(0,0,0,0.3)" : "none",
|
boxShadow: "2px 0 12px rgba(0,0,0,0.3)",
|
||||||
transition: "left 0.25s cubic-bezier(.4,2,.6,1)",
|
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
padding: "24px 16px 16px 16px",
|
padding: "24px 16px 16px 16px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
overflow: "hidden", // <-- add this
|
overflow: "hidden",
|
||||||
pointerEvents: sideMenuOpen ? "auto" : "none", // <-- add this
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -2040,6 +2056,7 @@ function App() {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* Viewport grid menu at top center */}
|
{/* Viewport grid menu at top center */}
|
||||||
|
|
||||||
@@ -2096,6 +2113,7 @@ function App() {
|
|||||||
boxShadow: "0 2px 8px rgba(0,0,0,0.2)",
|
boxShadow: "0 2px 8px rgba(0,0,0,0.2)",
|
||||||
pointerEvents: gridBtnActive ? "auto" : "none", // Only clickable when active
|
pointerEvents: gridBtnActive ? "auto" : "none", // Only clickable when active
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
|
display: gridBtnActive ? "block" : "none",
|
||||||
}}
|
}}
|
||||||
onMouseLeave={() => setGridBtnActive(false)}
|
onMouseLeave={() => setGridBtnActive(false)}
|
||||||
onBlur={() => setGridBtnActive(false)}
|
onBlur={() => setGridBtnActive(false)}
|
||||||
@@ -2452,7 +2470,8 @@ function App() {
|
|||||||
|
|
||||||
export default App
|
export default App
|
||||||
|
|
||||||
function setupTools() {
|
function setupTools(MAIN_TOOL_GROUP_ID) {
|
||||||
|
console.log('Setting up tools...', MAIN_TOOL_GROUP_ID);
|
||||||
cornerstoneTools.addTool(PanTool)
|
cornerstoneTools.addTool(PanTool)
|
||||||
cornerstoneTools.addTool(WindowLevelTool)
|
cornerstoneTools.addTool(WindowLevelTool)
|
||||||
cornerstoneTools.addTool(StackScrollTool)
|
cornerstoneTools.addTool(StackScrollTool)
|
||||||
|
|||||||
@@ -20,3 +20,29 @@ html, body, #root {
|
|||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
.dicom-viewer-root, #root {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
min-height: 0;
|
||||||
|
min-width: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dicom-viewer-test-root {
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: rgb(128, 128, 128);
|
||||||
|
display: block;
|
||||||
|
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
height: 500px;
|
||||||
|
line-height: 24px;
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: auto;
|
||||||
|
text-align: start;
|
||||||
|
text-size-adjust: 100%;
|
||||||
|
unicode-bidi: isolate;
|
||||||
|
width: 1296px;
|
||||||
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
@@ -4,16 +4,57 @@ import ReactDOM from 'react-dom/client'
|
|||||||
import App from './App.tsx'
|
import App from './App.tsx'
|
||||||
import Nifti from './Nifti.tsx'
|
import Nifti from './Nifti.tsx'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
|
import { createImageIds, availableImageIds } from "./lib/createImageIds.ts"
|
||||||
|
|
||||||
//ReactDOM.createRoot(document.getElementById('root')!).render(
|
const test_containers = document.querySelectorAll(".dicom-viewer-test-root");
|
||||||
// <React.StrictMode>
|
test_containers.forEach((container) => {
|
||||||
// <App />
|
const id = container.id || '';
|
||||||
// {/* <Nifti /> */}
|
const imageStacks = () => availableImageIds();
|
||||||
// </React.StrictMode>,
|
createRoot(container).render(
|
||||||
//)
|
<App
|
||||||
|
container_id={id}
|
||||||
|
imageStacks={imageStacks}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// Render to multiple containers
|
|
||||||
const containers = document.querySelectorAll(".dicom-viewer-root");
|
const containers = document.querySelectorAll(".dicom-viewer-root");
|
||||||
containers.forEach((container) => {
|
containers.forEach((container) => {
|
||||||
createRoot(container).render(<App />);
|
const id = container.id || '';
|
||||||
|
let imageStacks;
|
||||||
|
|
||||||
|
// Helper to convert URLs to wadouri
|
||||||
|
const toWadouri = (arr: string[]) =>
|
||||||
|
arr.map(url => url.startsWith("wadouri:") ? url : `wadouri:${url}`);
|
||||||
|
|
||||||
|
// Check for data-images attribute
|
||||||
|
const dataImages = container.getAttribute('data-images');
|
||||||
|
if (dataImages) {
|
||||||
|
let parsed;
|
||||||
|
try {
|
||||||
|
parsed = JSON.parse(dataImages);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Invalid data-images JSON:", e);
|
||||||
|
parsed = [];
|
||||||
|
}
|
||||||
|
// If it's a flat list, wrap in an array to make it a stack list and convert to wadouri
|
||||||
|
if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0] === "string") {
|
||||||
|
imageStacks = () => Promise.resolve([toWadouri(parsed)]);
|
||||||
|
} else if (Array.isArray(parsed)) {
|
||||||
|
imageStacks = () => Promise.resolve(parsed.map(
|
||||||
|
stack => Array.isArray(stack) ? toWadouri(stack) : []
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
imageStacks = () => Promise.resolve([[]]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imageStacks = () => Promise.resolve([[]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
createRoot(container).render(
|
||||||
|
<App
|
||||||
|
container_id={id}
|
||||||
|
imageStacks={imageStacks}
|
||||||
|
/>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
@@ -29,13 +29,11 @@ import { viteCommonjs } from "@originjs/vite-plugin-commonjs"
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export default defineConfig({
|
export default defineConfig(({ command }) => ({
|
||||||
plugins: [
|
plugins: [
|
||||||
react(),
|
react(),
|
||||||
// for dicom-parser
|
|
||||||
viteCommonjs(),
|
viteCommonjs(),
|
||||||
],
|
],
|
||||||
// seems like only required in dev mode
|
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
exclude: ["@cornerstonejs/dicom-image-loader"],
|
exclude: ["@cornerstonejs/dicom-image-loader"],
|
||||||
include: ["dicom-parser"],
|
include: ["dicom-parser"],
|
||||||
@@ -46,4 +44,5 @@ export default defineConfig({
|
|||||||
external: ["@icr/polyseg-wasm"],
|
external: ["@icr/polyseg-wasm"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
base: command === "build" ? "/static/dv3d/" : "/",
|
||||||
|
}));
|
||||||
Reference in New Issue
Block a user