From a797cda07eb07d73e3e5f8457f5ef417cad5baab Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Thu, 7 Aug 2025 12:33:57 -0300 Subject: [PATCH 1/7] Reset visibility on series selection --- src/components/SlideViewer.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/components/SlideViewer.tsx b/src/components/SlideViewer.tsx index 80e1fae7..c33f3ad4 100644 --- a/src/components/SlideViewer.tsx +++ b/src/components/SlideViewer.tsx @@ -636,7 +636,7 @@ class SlideViewer extends React.Component { console.debug('TODO: Loading Advanced Blending Presentation State') } else if ((derivedDataset as { SOPClassUID: string }).SOPClassUID === ColorSoftcopyPresentationState) { console.debug('TODO: Loading Color Softcopy Presentation State') - } else if ((derivedDataset as { SOPClassUID: string }).SOPClassUID === GrayscaleSoftcopyPresentationState) { + } else if ((derivedDataset as { SOPClassUID: string }).SOPClassUID === GrayscaleSoftcopyPresentationState) { console.debug('TODO: Loading Grayscale Softcopy Presentation State') } else if ((derivedDataset as { SOPClassUID: string }).SOPClassUID === PseudocolorSoftcopyPresentationState) { console.debug('TODO: Loading Pseudocolor Softcopy Presentation State') @@ -2698,7 +2698,16 @@ class SlideViewer extends React.Component { } handleAnnotationGroupSelection = (value: string): void => { - this.setState({ selectedSeriesInstanceUID: value }) + // Hide all currently visible annotation groups when selection changes + this.state.visibleAnnotationGroupUIDs.forEach(annotationGroupUID => { + this.volumeViewer.hideAnnotationGroup(annotationGroupUID) + }) + + // Reset the visible annotation groups state + this.setState({ + selectedSeriesInstanceUID: value, + visibleAnnotationGroupUIDs: new Set() + }) } getSeriesDescription = (seriesInstanceUID: string): string => { From 38e381084beea4b735bedc9b5d357c1efb44fe13 Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Thu, 21 Aug 2025 17:11:07 -0300 Subject: [PATCH 2/7] Address annotation selection issues --- src/components/SlideViewer.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SlideViewer.tsx b/src/components/SlideViewer.tsx index c33f3ad4..f0f48bdc 100644 --- a/src/components/SlideViewer.tsx +++ b/src/components/SlideViewer.tsx @@ -1747,10 +1747,10 @@ class SlideViewer extends React.Component { * @param value - Code value of the coded finding that got selected * @param option - Option that got selected */ - handleAnnotationFindingSelection ( + handleAnnotationFindingSelection = ( value: string, _option: { label: React.ReactNode } - ): void { + ): void => { this.findingOptions.forEach(finding => { if (finding.CodeValue === value) { console.info(`selected finding "${finding.CodeMeaning}"`) From c508b13b354b9508595b712de4692a21b6625485 Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Fri, 22 Aug 2025 10:31:28 -0300 Subject: [PATCH 3/7] Avoid reset of annotation groups during selection --- src/components/AnnotationGroupItem.tsx | 1 + src/components/SlideViewer.tsx | 11 +---------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/components/AnnotationGroupItem.tsx b/src/components/AnnotationGroupItem.tsx index acb3d790..b54cfa60 100644 --- a/src/components/AnnotationGroupItem.tsx +++ b/src/components/AnnotationGroupItem.tsx @@ -648,6 +648,7 @@ AnnotationGroupItemState metadata, onVisibilityChange, onStyleChange, + onAnnotationGroupClick, ...otherProps } = this.props return ( diff --git a/src/components/SlideViewer.tsx b/src/components/SlideViewer.tsx index f0f48bdc..80d41e0d 100644 --- a/src/components/SlideViewer.tsx +++ b/src/components/SlideViewer.tsx @@ -2698,16 +2698,7 @@ class SlideViewer extends React.Component { } handleAnnotationGroupSelection = (value: string): void => { - // Hide all currently visible annotation groups when selection changes - this.state.visibleAnnotationGroupUIDs.forEach(annotationGroupUID => { - this.volumeViewer.hideAnnotationGroup(annotationGroupUID) - }) - - // Reset the visible annotation groups state - this.setState({ - selectedSeriesInstanceUID: value, - visibleAnnotationGroupUIDs: new Set() - }) + this.setState({ selectedSeriesInstanceUID: value }) } getSeriesDescription = (seriesInstanceUID: string): string => { From 43406ea680bab9f1f71682bd96c1af9b63d2faec Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Fri, 22 Aug 2025 10:54:59 -0300 Subject: [PATCH 4/7] Disable thumbnail rendering (pyramid) --- src/components/SlideViewer/utils/viewerUtils.ts | 1 + types/dicom-microscopy-viewer/index.d.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/SlideViewer/utils/viewerUtils.ts b/src/components/SlideViewer/utils/viewerUtils.ts index 6d16d07b..8af03c13 100644 --- a/src/components/SlideViewer/utils/viewerUtils.ts +++ b/src/components/SlideViewer/utils/viewerUtils.ts @@ -32,6 +32,7 @@ export const constructViewers = ({ clients, slide, preload }: { clientMapping: clients, metadata: slide.volumeImages, controls: ['overview', 'position'], + skipThumbnails: true, preload, errorInterceptor: (error: CustomError) => { NotificationMiddleware.onError( diff --git a/types/dicom-microscopy-viewer/index.d.ts b/types/dicom-microscopy-viewer/index.d.ts index c9b7f333..c012626f 100644 --- a/types/dicom-microscopy-viewer/index.d.ts +++ b/types/dicom-microscopy-viewer/index.d.ts @@ -14,6 +14,7 @@ import * as dcmjs from 'dcmjs' metadata: metadata.VLWholeSlideMicroscopyImage[] debug?: boolean preload?: boolean + skipThumbnails?: boolean controls: string[] annotationOptions?: object errorInterceptor?: (error: CustomError) => void From 41f461bfd2084579d30e9e51e770fdd130ef19dd Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Fri, 22 Aug 2025 11:19:47 -0300 Subject: [PATCH 5/7] Trim custom urls --- src/App.tsx | 9 +++++---- src/components/Header.tsx | 18 +++++++++++------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 187fd421..5dbc80e3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -297,17 +297,18 @@ class App extends React.Component { } handleServerSelection ({ url }: { url: string }): void { - console.info('select DICOMweb server: ', url) - if (url === '' || window.localStorage.getItem('slim_server_selection_mode') === 'default') { + const trimmedUrl = url.trim() + console.info('select DICOMweb server: ', trimmedUrl) + if (trimmedUrl === '' || window.localStorage.getItem('slim_server_selection_mode') === 'default') { this.setState({ clients: this.state.defaultClients }) return } - window.localStorage.setItem('slim_selected_server', url) + window.localStorage.setItem('slim_selected_server', trimmedUrl) const tmpClient = new DicomWebManager({ baseUri: '', settings: [{ id: 'tmp', - url, + url: trimmedUrl, read: true, write: false }], diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 3c2cf56e..dba095f7 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -74,7 +74,7 @@ interface HeaderState { class Header extends React.Component { constructor (props: HeaderProps) { super(props) - const cachedServerUrl = window.localStorage.getItem('slim_selected_server') + const cachedServerUrl = window.localStorage.getItem('slim_selected_server')?.trim() const cachedMode = window.localStorage.getItem('slim_server_selection_mode') as 'default' | 'custom' | null this.state = { @@ -132,8 +132,12 @@ class Header extends React.Component { if (url == null || url === '') { return false } + const trimmedUrl = url.trim() + if (trimmedUrl === '') { + return false + } try { - const urlObj = new URL(url) + const urlObj = new URL(trimmedUrl) return urlObj.protocol.startsWith('http') && urlObj.pathname.length > 0 } catch (TypeError) { return false @@ -318,7 +322,7 @@ class Header extends React.Component { handleServerSelectionInput = ( event: React.FormEvent ): void => { - const value = event.currentTarget.value + const value = event.currentTarget.value.trim() this.setState({ selectedServerUrl: value, isServerSelectionDisabled: !this.isValidServerUrl(value) @@ -326,7 +330,7 @@ class Header extends React.Component { } handleServerSelectionCancellation = (): void => { - const cachedServerUrl = window.localStorage.getItem('slim_selected_server') + const cachedServerUrl = window.localStorage.getItem('slim_selected_server')?.trim() this.setState({ serverSelectionMode: cachedServerUrl !== null && cachedServerUrl !== undefined && cachedServerUrl !== '' ? 'custom' : 'default', selectedServerUrl: cachedServerUrl ?? undefined, @@ -352,7 +356,7 @@ class Header extends React.Component { return } - const url = this.state.selectedServerUrl + const url = this.state.selectedServerUrl?.trim() let closeModal = false if (url != null && url !== '') { if (url.startsWith('http://') || url.startsWith('https://')) { @@ -450,7 +454,7 @@ class Header extends React.Component { const logoUrl = process.env.PUBLIC_URL + '/logo.svg' const selectedServerUrl = this.state.serverSelectionMode === 'custom' - ? this.state.selectedServerUrl + ? this.state.selectedServerUrl?.trim() : this.props.clients?.default?.baseURL ?? this.props.defaultClients?.default?.baseURL const urlInfo = selectedServerUrl != null && selectedServerUrl !== '' @@ -519,7 +523,7 @@ class Header extends React.Component { {this.state.serverSelectionMode === 'custom' && ( - + Date: Fri, 22 Aug 2025 13:01:12 -0300 Subject: [PATCH 6/7] Update dmv --- package.json | 2 +- yarn.lock | 45 +++++++++------------------------------------ 2 files changed, 10 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index d8b5750f..af75cb18 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "craco-less": "^2.0.0", "dcmjs": "^0.35.0", "detect-browser": "^5.2.1", - "dicom-microscopy-viewer": "^0.48.5", + "dicom-microscopy-viewer": "^0.48.7", "dicomweb-client": "^0.10.3", "gh-pages": "^5.0.0", "oidc-client": "^1.11.5", diff --git a/yarn.lock b/yarn.lock index af50758e..db47ab9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4255,31 +4255,6 @@ color-name@^1.1.4, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-2.0.0.tgz#03ff6b1b5aec9bb3cf1ed82400c2790dfcd01d2d" - integrity sha512-SbtvAMWvASO5TE2QP07jHBMXKafgdZz8Vrsrn96fiL+O92/FN/PLARzUW5sKt013fjAprK2d2iCn2hk2Xb5oow== - -color-parse@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/color-parse/-/color-parse-2.0.2.tgz#37b46930424924060988edf25b24e6ffb4a1dc3f" - integrity sha512-eCtOz5w5ttWIUcaKLiktF+DxZO1R9KLNY/xhbV6CkhM7sR3GhVghmt6X6yOnzeaM24po+Z9/S1apbXMwA3Iepw== - dependencies: - color-name "^2.0.0" - -color-rgba@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/color-rgba/-/color-rgba-3.0.0.tgz#77090bdcdb2951c1735e20099ddd50401675375b" - integrity sha512-PPwZYkEY3M2THEHHV6Y95sGUie77S7X8v+h1r6LSAPF3/LL2xJ8duUXSrkic31Nzc4odPwHgUbiX/XuTYzQHQg== - dependencies: - color-parse "^2.0.0" - color-space "^2.0.0" - -color-space@^2.0.0, color-space@^2.0.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/color-space/-/color-space-2.3.1.tgz#1c13ca8ee017585807e65c76de3870cdc7c4fdfb" - integrity sha512-5DJdKYwoDji3ik/i0xSn+SiwXsfwr+1FEcCMUz2GS5speGCfGSbBMOLd84SDUBOuX8y4CvdFJmOBBJuC4wp7sQ== - colord@^2.9.1: version "2.9.3" resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" @@ -4986,10 +4961,10 @@ detective@^5.2.1: defined "^1.0.0" minimist "^1.2.6" -dicom-microscopy-viewer@^0.48.5: - version "0.48.5" - resolved "https://registry.yarnpkg.com/dicom-microscopy-viewer/-/dicom-microscopy-viewer-0.48.5.tgz#425d0af54583ca4b0c466860a21eef8b07328e65" - integrity sha512-geLgEe4uz74DUYoO5fMnbGeeBju1xmjTLG9QxPdcLsWi0HTWnRGfO+0vRARJeZzR7tH/sQUswoluujUumZLOyg== +dicom-microscopy-viewer@^0.48.7: + version "0.48.7" + resolved "https://registry.yarnpkg.com/dicom-microscopy-viewer/-/dicom-microscopy-viewer-0.48.7.tgz#f40248f5e5bd0ac7c47235de3e079d5a96b6a9a9" + integrity sha512-Kmt6uQourv5HL29bnPWvy2hm49LL4fvYXhj2BTHtbCHdyLZ3LOWUxNWZR9NzuK7QoiHCA7ZPSFFnkdUfWLzF6g== dependencies: "@cornerstonejs/codec-charls" "^1.2.3" "@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2" @@ -5001,7 +4976,7 @@ dicom-microscopy-viewer@^0.48.5: dicomweb-client "^0.10.3" image-type "^4.1" mathjs "^11.2" - ol "^10.4.0" + ol "^10.6.0" uuid "^9.0" dicomicc@^0.1: @@ -9239,14 +9214,12 @@ oidc-client@^1.11.5: crypto-js "^4.0.0" serialize-javascript "^4.0.0" -ol@^10.4.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/ol/-/ol-10.4.0.tgz#b073dd2b08c3cc31fece1c904a6711e0d289d6e4" - integrity sha512-gv3voS4wgej1WVvdCz2ZIBq3lPWy8agaf0094E79piz8IGQzHiOWPs2in1pdoPmuTNvcqGqyUFG3IbxNE6n08g== +ol@^10.6.0: + version "10.6.1" + resolved "https://registry.yarnpkg.com/ol/-/ol-10.6.1.tgz#950f3914b4eec978f087b36aa74ce1e18c41ab09" + integrity sha512-xp174YOwPeLj7c7/8TCIEHQ4d41tgTDDhdv6SqNdySsql5/MaFJEJkjlsYcvOPt7xA6vrum/QG4UdJ0iCGT1cg== dependencies: "@types/rbush" "4.0.0" - color-rgba "^3.0.0" - color-space "^2.0.1" earcut "^3.0.0" geotiff "^2.1.3" pbf "4.0.1" From d2724076e3683fec994f485d0eedc7fe3b0b711e Mon Sep 17 00:00:00 2001 From: Igor Octaviano Date: Fri, 22 Aug 2025 13:08:20 -0300 Subject: [PATCH 7/7] Update test for dmv --- craco.config.js | 1 + src/__mocks__/dicomMicroscopyViewerMock.js | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 src/__mocks__/dicomMicroscopyViewerMock.js diff --git a/craco.config.js b/craco.config.js index c2b93724..a78ba169 100644 --- a/craco.config.js +++ b/craco.config.js @@ -72,6 +72,7 @@ module.exports = { 'node_modules/(?!(ol|dicom-microscopy-viewer|dicomweb-client|@cornerstonejs|dicomicc|rbush|color-rgba|color-parse|color-name|color-space|quickselect|earcut)/)' ] config.moduleNameMapper = { + 'dicom-microscopy-viewer': '/src/__mocks__/dicomMicroscopyViewerMock.js', '@cornerstonejs/codec-libjpeg-turbo-8bit/decodewasmjs': '@cornerstonejs/codec-libjpeg-turbo-8bit/dist/libjpegturbowasm_decode', '@cornerstonejs/codec-libjpeg-turbo-8bit/decodewasm': '@cornerstonejs/codec-libjpeg-turbo-8bit/dist/libjpegturbowasm_decode.wasm', '@cornerstonejs/codec-charls/decodewasmjs': '@cornerstonejs/codec-charls/dist/charlswasm_decode.js', diff --git a/src/__mocks__/dicomMicroscopyViewerMock.js b/src/__mocks__/dicomMicroscopyViewerMock.js new file mode 100644 index 00000000..b140ae53 --- /dev/null +++ b/src/__mocks__/dicomMicroscopyViewerMock.js @@ -0,0 +1,5 @@ +// Mock for dicom-microscopy-viewer to resolve Jest test issues +module.exports = { + // Add any specific exports that might be needed by the tests + // This is a basic mock that should allow tests to run +}