Compare commits

..
388 Commits
Author SHA1 Message Date
ohif-bot db16341a4f chore(version): Update package versions [skip ci] 2026-05-18 13:39:27 +00:00
ohif-bot 0fcf3a9dc1 chore(version): version.json [skip ci] 2026-05-18 13:39:19 +00:00
Joe Boccanfuso 75a24a3393 fix(security): Update various dependencies to fix security vulnerabilities. Release 3.12.2 (#6024) 2026-05-18 09:32:40 -04:00
ohif-bot 104de8d833 chore(version): version.json [skip ci] 2026-05-08 18:51:36 +00:00
Alireza 647fd025cf fix(release): make publish-version.mjs idempotent for re-runs
Skip the peer-dep commit when nothing is staged and skip the lerna
version step when the workspace is already at the target version.
Allows the release pipeline to be re-triggered after a partial run.
2026-05-08 14:42:57 -04:00
Alireza 3fc8706126 chore(version): Update package versions [skip ci] 2026-05-08 14:37:54 -04:00
ohif-bot 539ff44f47 chore(version): version.json [skip ci] 2026-05-07 23:52:54 +00:00
Joe Boccanfuso 146897f216 chore: release 3.12.1 (#6003) 2026-05-07 19:45:13 -04:00
Joe Boccanfuso a264413124 fix(security): Patch axios security vulnerabilities. (#5997)
Patch axios security vulnerabilities.
2026-05-06 13:37:37 -04:00
Joe Boccanfuso 838f51957e fix(configuration): Harden dynamic datasource URL trust boundaries and credential handling. 3.12 (#5978) 2026-04-29 14:38:55 -04:00
Joe Boccanfuso bab20143fd fix(security): Update axios and protobufjs to fix security vulnerabilities. (#5975) 2026-04-23 14:15:16 -04:00
Joe Boccanfuso 946735bf90 fix(security):Update yarn.lock that was missed in PR #5937. (#5939) 2026-04-03 13:16:47 -04:00
Joe Boccanfuso a5be4c3dce fix(security): update dependencies to fix security vulnerabilities (#5937) 2026-04-02 18:06:35 -04:00
Joe Boccanfuso 171ea857cf fix(security): Bump flattened version to address CVE-2026-32141 for release 3.12 (#5898) 2026-03-13 15:25:55 -04:00
Joe Boccanfuso 9754fc1e92 fix(security): Bump tar version to address CVE-2026-31802 in release/3.12 (#5896) 2026-03-13 08:49:13 -04:00
Joe Boccanfuso 6772deb4c6 fix(security): Bump svgo and tar to fix vulnerabilities in release 3.12 (#5881) 2026-03-06 08:26:16 -05:00
Joe Boccanfuso 1d0a8744b2 fix(security): Address various security vulnerabilities in the release/3.12 branch (#5872) 2026-03-04 14:54:17 -05:00
Joe Boccanfuso 555c87f1c5 chore(security): Updates for running bun audit during CI and dependabot PR version update settings (#5836)
chore(security): Update circleci config to only run bun audit for lockfile changes.

Disabled all dependabot pull requests for bun and npm version updates.
2026-02-23 16:26:49 -05:00
Joe Boccanfuso 51d267e707 fix(security): CVE-2026-27212 and CVE-2026-26996 addressed for release/3.12 (#5831)
* fix(security):  CVE-2026-27212 patched.
Various dependency updates as a result of CVE-2026-26996.
Ultimately CVE-2026-26996 was ignored because it is only exposed in itk-wasm via CLI and OHIF's other use of minimatch is limited to build/dev environments.

* Switched to eslint 9.39.3 for compatibility with .eslintrc.json.
2026-02-23 14:51:42 -05:00
Joe Boccanfuso c5c18f9516 fix(security): Bump tar to 7.5.9 and lerna to 9.0.4 to fix CVE-2026-26960. (#5825)
* fix(security): Bump tar to 7.5.9 and lerna to 9.0.4 to fix CVE-2026-26960.
Bump sharp to 0.34.5 to fix tar-fs vulnerabilities.

* Update node version to 20.19.0 in circleci config. Needed for lerna and cypress tests.

* Now installing bun for cypress tests in circleci config.

* Use node version 20.19.0 in netlify config.
2026-02-18 16:52:57 -05:00
Joe Boccanfuso 213a0974bb chore(deps): bump webpack from 5.95.0 to 5.105.0 to address CVE-2025-68458 and CVE-2025-68157 (#5822) 2026-02-18 12:31:13 -05:00
0d882a425d fix: Better handle octet stream case (#5789) (#5804)
* Better handle octet stream case

* Use data source config stack options

* Use immutability helper

---------

Co-authored-by: Igor Octaviano <igoroctaviano@gmail.com>
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2026-02-13 12:04:05 -05:00
Joe Boccanfuso 49ea3ac82b chore(deps): bump diff to 5.2.2 to path CVE-2026-24001 (#5812) 2026-02-11 09:39:32 -05:00
Joe Boccanfuso 99736b6437 chore(deps): bump lodash and lodash-es from 4.17.21 to 4.17.23 to address CVE-2025-13465 (#5803) 2026-02-10 14:28:08 -05:00
Joe Boccanfuso 7fb67d2396 fix(security): Use a resolution for axios to address CVE-2026-25639. (#5794) 2026-02-09 16:44:12 -05:00
ohif-bot 04b121c1ba chore(version): Update package versions [skip ci] 2026-02-06 17:45:00 +00:00
ohif-bot 9782980a7a chore(version): version.json [skip ci] 2026-02-06 17:44:53 +00:00
Joe Boccanfuso 27b1afe040 chore: release 3.12.0 2026-02-06 10:03:53 -05:00
ohif-bot 891d6570c1 chore(version): Update package versions [skip ci] 2026-02-05 22:02:12 +00:00
ohif-bot 48f8e806c3 chore(version): version.json [skip ci] 2026-02-05 22:02:05 +00:00
Joe Boccanfuso 45739b31a2 fix(jump-to-contour-segment): For contours like those from RSTRUCTs, use generic jumpToSegmentCenter if a segment center is available. (#5785) 2026-02-05 16:57:15 -05:00
ohif-bot 54ff608605 chore(version): Update package versions [skip ci] 2026-02-05 16:55:14 +00:00
ohif-bot 710fefe3e8 chore(version): version.json [skip ci] 2026-02-05 16:55:06 +00:00
Bill Wallace 48f2f6fb13 fix(HistoryMemo): Segmentation delete wasn't remembered (#5775)
Will address a few more comments in a separate PR released after this one into 3.13

* fix(HistoryMemo): Segmentation delete wasn't remembered

* Temporary add of package.json linking

* fix: Use newer dcmjs version and delete segmentation with history cs3d
2026-02-05 11:49:49 -05:00
ohif-bot 3b9d3babf9 chore(version): Update package versions [skip ci] 2026-02-04 21:17:59 +00:00
ohif-bot e71a20334a chore(version): version.json [skip ci] 2026-02-04 21:17:50 +00:00
Joe Boccanfuso 512aa3b07f fix(jump-to-label-map): Use undefined for viewportId for arrow navigation. (#5774)
* Update to CS3D 4.15.28.
2026-02-04 16:12:41 -05:00
ohif-bot c40ebd2124 chore(version): Update package versions [skip ci] 2026-01-30 18:57:26 +00:00
ohif-bot ceb61fce15 chore(version): version.json [skip ci] 2026-01-30 18:57:20 +00:00
Joe Boccanfuso 9a97095d5c chore(deps): Update cornerstonejs dependencies to 4.15.24. (#5764) 2026-01-30 13:53:28 -05:00
ohif-bot a448bafbc5 chore(version): Update package versions [skip ci] 2026-01-30 16:22:51 +00:00
ohif-bot 2da384ccbc chore(version): version.json [skip ci] 2026-01-30 16:22:44 +00:00
Joe Boccanfuso 02423c5bf3 fix(segmentation): Use jumpToSegmentNext instead of jumpToSegmentCenter for arrow navigation. (#5759)
* fix(segmentation): Use jumpToSegmentNext instead of jumpToSegmentCenter for arrow navigation.

* Fixed unit tests.
2026-01-30 11:18:01 -05:00
Bill Wallace 5807889f79 Fix/duplicate redundant log measurement mapping (#5760)
* Remove all but one log message for mappings.

* fix: NPE when no segmentations list returned.
2026-01-30 11:16:37 -05:00
ohif-bot a4c99e6926 chore(version): Update package versions [skip ci] 2026-01-28 19:06:09 +00:00
ohif-bot f4ac95f96f chore(version): version.json [skip ci] 2026-01-28 19:06:03 +00:00
Joe Boccanfuso 76199884de chore(security): Ignore node-tar vulnerability. (#5753) 2026-01-28 14:02:08 -05:00
ohif-bot 64cd189cce chore(version): Update package versions [skip ci] 2026-01-27 15:18:13 +00:00
ohif-bot 8f13d92fa8 chore(version): version.json [skip ci] 2026-01-27 15:18:06 +00:00
6dedf5573e fix: Handle optional ContentSequence for NUM content items (#5703)
* fix: SR validation

* Adding links to cs3d

* Update to dcmjs 0.48.0

* Update package files to include updated adapter

* Update package files

---------

Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
Co-authored-by: arul-trenser <arul.mozhi@trenser.com>
2026-01-27 10:13:55 -05:00
ohif-bot b69abf25ee chore(version): Update package versions [skip ci] 2026-01-27 13:51:49 +00:00
ohif-bot 891012397b chore(version): version.json [skip ci] 2026-01-27 13:51:42 +00:00
Joe BoccanfusoandBill Wallace 06612fe177 fix(TMTV): Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 (#5735)
* Updated screenshots for failing tests due to FOV improvements.

---------

Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2026-01-27 08:47:29 -05:00
ohif-bot 0321361e2c chore(version): Update package versions [skip ci] 2026-01-22 19:16:07 +00:00
ohif-bot 1bc0dece64 chore(version): version.json [skip ci] 2026-01-22 19:15:58 +00:00
Bill Wallace 0fd517d6ab fix: Navigate to contour should work (#5718)
* fix: Navigate to contour should work

* Switched back to jump to segment center for hte unit tests
2026-01-22 14:10:40 -05:00
ohif-bot b2f6e1f237 chore(version): Update package versions [skip ci] 2026-01-22 03:03:33 +00:00
ohif-bot dbb7873238 chore(version): version.json [skip ci] 2026-01-22 03:03:26 +00:00
afe184ec78 fix: the null value check issue in image spacing calculation (#5726)
Co-authored-by: 顾争 <zheng.gu-rd@djhealthunion.com.cn>
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2026-01-21 21:59:20 -05:00
ohif-bot 3cfddc1f64 chore(version): Update package versions [skip ci] 2026-01-21 18:07:03 +00:00
ohif-bot 1142cdbb46 chore(version): version.json [skip ci] 2026-01-21 18:06:56 +00:00
Joe Boccanfuso 320f0a7463 fix(security): Ignore CVE-2026-23745, CVE-2026-23950 and CVE-2024-28863 because they are limited to build/dev environments. (#5733)
The tar vulnerabilities affect build-time tools (lerna) and transitive dependencies, but not runtime data flow from Cornerstone3D to its dependencies.
2026-01-21 13:02:40 -05:00
ohif-bot 16267d9ddd chore(version): Update package versions [skip ci] 2026-01-16 02:28:46 +00:00
ohif-bot 88e4a9fb7b chore(version): version.json [skip ci] 2026-01-16 02:28:40 +00:00
Joe Boccanfuso 4a1f509865 fix(microscopy): Update microscope to 0.48.17 to fix a measurement exception. (#5710)
* Added documentation for locally linking dicom-microscopy-viewer.
2026-01-15 21:24:31 -05:00
ohif-bot c2efa5701e chore(version): Update package versions [skip ci] 2026-01-14 21:55:47 +00:00
ohif-bot 7f1c2910eb chore(version): version.json [skip ci] 2026-01-14 21:55:38 +00:00
Joe Boccanfuso eb6b814fc9 fix(window-level-action-menu): Window level menu no longer needs two clicks to open after it is used (#5711)
The window level menu's logic for when it is opened and closed now also runs when the AllInOneMenu triggers the open or close.
Using refs to store the latest function references in the AllInOneMenu component to avoid triggering a useEffect when the functions change.
Removed an unused element prop from the WindowLevelActionMenu component.
2026-01-14 16:50:24 -05:00
Joe Boccanfuso 612ced0ea1 chore(deps): update CS3D to 4.15.9 to fix OHIF issues 5656 and 5639. (#5714) 2026-01-14 16:48:55 -05:00
ohif-bot 0995616567 chore(version): Update package versions [skip ci] 2026-01-13 21:02:21 +00:00
ohif-bot 210ebf26f6 chore(version): version.json [skip ci] 2026-01-13 21:02:14 +00:00
Joe Boccanfuso 82e6a18735 fix(segmentation): List surface representations in the segmentation table for 3D views. (#5700)
This was done by using an array of representation types instead of a single type for a panel.
The first element of the array is the primary type of the panel, and the rest are secondary types
that can also be displayed in the panel.

PR feedback:
- added test to check number of segments in side panel for 3D only view
- fixed jumping to segment in 3D only view
- fixed exception when adding contour segment in 3D only view
2026-01-13 15:57:44 -05:00
ohif-bot 83c9ce0f5f chore(version): Update package versions [skip ci] 2026-01-12 12:33:06 +00:00
ohif-bot e06b1786e0 chore(version): version.json [skip ci] 2026-01-12 12:32:59 +00:00
Joe Boccanfuso aba4855276 fix(segmentation): Update cornerstone dependencies to fix a segmentation exception after capture. (#5701) 2026-01-12 07:29:04 -05:00
ohif-bot 6f847cb053 chore(version): Update package versions [skip ci] 2026-01-09 19:57:27 +00:00
ohif-bot b55f4985e0 chore(version): version.json [skip ci] 2026-01-09 19:57:20 +00:00
dependabot[bot] 00d04162b2 chore(deps): bump react-router from 6.30.1 to 6.30.3 (#5704) 2026-01-09 14:53:01 -05:00
ohif-bot 33575b769e chore(version): Update package versions [skip ci] 2026-01-08 19:40:28 +00:00
ohif-bot 36581696c9 chore(version): version.json [skip ci] 2026-01-08 19:40:21 +00:00
Joe Boccanfuso 6429a5518a fix(DicomTagBrowser): Check for null metadata. (#5696) 2026-01-08 14:35:45 -05:00
ohif-bot c34ede8570 chore(version): Update package versions [skip ci] 2026-01-07 21:18:36 +00:00
ohif-bot 0474168b44 chore(version): version.json [skip ci] 2026-01-07 21:18:29 +00:00
Joe Boccanfuso ecc74a53e0 fix(segmentation): Use active segmentation for navigation. (#5692) 2026-01-07 16:14:20 -05:00
ohif-bot 18636734ec chore(version): Update package versions [skip ci] 2026-01-06 20:32:12 +00:00
ohif-bot 0cf3b0b335 chore(version): version.json [skip ci] 2026-01-06 20:32:05 +00:00
Joe Boccanfuso 38a8406a66 fix(capture): Ensure download filename has the correct extension. (#5691) 2026-01-06 15:27:44 -05:00
ohif-bot a51552a2e7 chore(version): Update package versions [skip ci] 2026-01-06 13:14:33 +00:00
ohif-bot 7386d1fc01 chore(version): version.json [skip ci] 2026-01-06 13:14:27 +00:00
AlirezaandBill Wallace a74648f8ec fix: NM multiframe with TimeSlotVector (#5666)
* fix(ViewportWindowLevel): add null checks for volumeActor in opacity functions

* refactor(ViewportWindowLevel): register histogram worker at module load and optimize scalar data retrieval

* fix(Preclinical4D): add null check for viewportId and set frame rate for cine playback

* fix(getSopClassHandlerModule): handle multiframe dynamic volumes by utilizing metaData for instance retrieval

---------

Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2026-01-06 08:10:01 -05:00
ohif-bot fec661318c chore(version): Update package versions [skip ci] 2026-01-05 18:56:32 +00:00
ohif-bot 09c8af50b7 chore(version): version.json [skip ci] 2026-01-05 18:56:24 +00:00
Joe Boccanfuso 6931c6b7fa chore(deps): update cs3d dependencies (#5688) 2026-01-05 13:51:23 -05:00
ohif-bot 233f73b7eb chore(version): Update package versions [skip ci] 2026-01-05 17:28:26 +00:00
ohif-bot 017c7dff27 chore(version): version.json [skip ci] 2026-01-05 17:28:19 +00:00
Joe Boccanfuso ca364a3af7 fix(security): update qs package to fix vulnerability CVE-2025-15284 (#5686)
fixes: qs's arrayLimit bypass in its bracket notation allows DoS via memory exhaustion
2026-01-05 12:23:34 -05:00
ohif-bot 50f6b52bbd chore(version): Update package versions [skip ci] 2025-12-19 19:50:23 +00:00
ohif-bot 3b34296d2b chore(version): version.json [skip ci] 2025-12-19 19:50:16 +00:00
Bill Wallace 635de2bfcf Allow doc launch (#5670) 2025-12-19 14:46:08 -05:00
ohif-bot 8626906b0e chore(version): Update package versions [skip ci] 2025-12-19 18:02:00 +00:00
ohif-bot 6a185bfb6e chore(version): version.json [skip ci] 2025-12-19 18:01:52 +00:00
Belbin-GKandBelbin GK cd253f20bd fix: pass metadata to viewport orientation matching (#5667)
Co-authored-by: Belbin GK <belbin.kunjumon@prenuvo.com>
2025-12-19 12:56:59 -05:00
ohif-bot e7bc688290 chore(version): Update package versions [skip ci] 2025-12-18 18:39:51 +00:00
ohif-bot 21ab058fdb chore(version): version.json [skip ci] 2025-12-18 18:39:44 +00:00
Joe Boccanfuso 3440f66ebd fix(segmentation): Update cornerstone dependencies to fix segment visibility issue. (#5664) 2025-12-18 13:35:29 -05:00
ohif-bot f8f09c2c68 chore(version): Update package versions [skip ci] 2025-12-18 17:06:35 +00:00
ohif-bot 68cf1bb81e chore(version): version.json [skip ci] 2025-12-18 17:06:30 +00:00
658994c854 fix: prevent annotation from appearing in active viewport when switching series with different Frame of Reference UID (#5630)
* fix: prevent annotation from appearing in active viewport when switching series with different Frame of Reference UID

* refactor: extract viewport update strategies

* Update version and use nearest orientation viewport

---------

Co-authored-by: trenser-belbin <belbin.kunjumon@prenuvo.com>
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2025-12-18 12:02:52 -05:00
ohif-bot 0af4c2ae47 chore(version): Update package versions [skip ci] 2025-12-18 14:38:06 +00:00
ohif-bot fad77aa188 chore(version): version.json [skip ci] 2025-12-18 14:37:59 +00:00
Joe Boccanfuso f3beb8e711 fix(segmentation): When setting the segmentation style and the representation type is not provided, apply the style to all representation types. (#5662) 2025-12-18 09:33:54 -05:00
ohif-bot fcfc2b4eb8 chore(version): Update package versions [skip ci] 2025-12-17 21:57:16 +00:00
ohif-bot f296247cc1 chore(version): version.json [skip ci] 2025-12-17 21:57:09 +00:00
ef98516b4f fix: Jump to measurement from measurement panel after rotating viewport (#5090)
Requested issues were resolved.  Approved this and am merging.

* sort the viewports based on the closest orientation as that of measurement

* fix: Separate out function for finding viewport alignment

---------

Co-authored-by: Arul Mozhi J <arul.mozhi@trenser.com>
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2025-12-17 16:52:45 -05:00
ohif-bot df40ffbf12 chore(version): Update package versions [skip ci] 2025-12-16 19:05:52 +00:00
ohif-bot 5fc1c44b1e chore(version): version.json [skip ci] 2025-12-16 19:05:45 +00:00
dxlin b2c469d087 fix/RTStructInlineBinaryContourData (#5611)
* -check for ContourData still in InlineBinary format and perform conversion
2025-12-16 14:01:15 -05:00
ohif-bot 42168442b9 chore(version): Update package versions [skip ci] 2025-12-16 02:19:36 +00:00
ohif-bot f01be6aedc chore(version): version.json [skip ci] 2025-12-16 02:19:29 +00:00
Alireza 2de81efa7e fix: preclinical mode not working (#5631)
* fix(ViewportWindowLevel): add null checks for volumeActor in opacity functions

* refactor(ViewportWindowLevel): register histogram worker at module load and optimize scalar data retrieval

* fix(Preclinical4D): add null check for viewportId and set frame rate for cine playback
2025-12-15 21:15:11 -05:00
ohif-bot 84d0d8866b chore(version): Update package versions [skip ci] 2025-12-15 13:54:44 +00:00
ohif-bot 7056148ada chore(version): version.json [skip ci] 2025-12-15 13:54:36 +00:00
Devu-trenserandBill Wallace 6c628e0937 fix: seg sync for data without FOR (#5565)
* fix: seg sync for data without FOR

* test commit to trigger deploy re-run

* test commit to trigger deploy re-run

* Revert "test commit to trigger deploy re-run"

This reverts commit 42c532c6f404798e39e3d137fff7c1e7569956ad.

* chore: comments updated

* review commnets

---------

Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2025-12-15 08:49:43 -05:00
ohif-bot 31ac27d5a0 chore(version): Update package versions [skip ci] 2025-12-12 16:51:13 +00:00
ohif-bot 46d5bc6cf6 chore(version): version.json [skip ci] 2025-12-12 16:51:06 +00:00
Vinícius Alves de Faria Resende d917e74e68 tests(Playwright): Full implementation of Page Object Models (POMs) to Playwright tests (#5608) 2025-12-12 11:46:45 -05:00
ohif-bot 7b12dcf048 chore(version): Update package versions [skip ci] 2025-12-12 15:51:24 +00:00
ohif-bot 062d18c950 chore(version): version.json [skip ci] 2025-12-12 15:51:16 +00:00
Martin Bellehumeur, M. Eng. 181c0f32c5 chore: update French i18n (#5561) 2025-12-12 10:46:56 -05:00
nithin-trenser ba140a05f7 fix: Console error on loading SR report (#5600)
* fixed console error

* Added check for SCOORD3D
2025-12-11 09:36:39 -05:00
ohif-bot 88188ad0c4 chore(version): Update package versions [skip ci] 2025-12-10 21:43:24 +00:00
ohif-bot 3609b162be chore(version): version.json [skip ci] 2025-12-10 21:43:17 +00:00
Dan Rukas eef755ef05 fix(routes): display 404 feedback page for unmatched URLs (#5627)
* Added 404 feedback and design

* restore message and showGoBackButton props
2025-12-10 16:39:17 -05:00
Dan Rukas c0f39aeb51 fix(DataSource): migrate configuration dialog to ui-next components (#5626)
* Fix to show data source on study list

* Update Button in Dialog select rows

* Updated legacy colors, small design updates

* Update dialog sizing
2025-12-10 16:38:25 -05:00
ohif-bot 3a5def6eb7 chore(version): Update package versions [skip ci] 2025-12-09 22:24:37 +00:00
ohif-bot 7e148ef197 chore(version): version.json [skip ci] 2025-12-09 22:24:30 +00:00
Ghadeer Albattarni 8dde2237a2 fix(seg-viewport): add guard for missing reference display set handler to prevent viewport crash (#5618) 2025-12-09 17:20:11 -05:00
ohif-bot 442ee68599 chore(version): Update package versions [skip ci] 2025-12-05 13:11:03 +00:00
ohif-bot dc91c07fd5 chore(version): version.json [skip ci] 2025-12-05 13:10:56 +00:00
Belbin-GKandtrenser-belbin c4e5a4616d feat(notification): add custom notification component support (#5605)
* feat: add custom notification component support

* update the documentation of UINotificationService to support custom component integration

---------

Co-authored-by: trenser-belbin <belbin.kunjumon@prenuvo.com>
2025-12-05 08:06:32 -05:00
ohif-bot fd86a9e3e8 chore(version): Update package versions [skip ci] 2025-12-04 17:30:48 +00:00
ohif-bot b85141ed2f chore(version): version.json [skip ci] 2025-12-04 17:30:39 +00:00
Bill Wallace 77f9f8e1c4 fix: Initial sort not consistent (#5224)
* fix: Initial sort not consistent

* Add better sort criteria

* Remove sorting changes

* Start replacing screen shots

* Update bun lock for version

* Leave default sort for test mode

* fix: Add consistent sorting for same series splits

* PR comments

* docs: add notes for series/display set sort

* Updated example docs

* Update sortVector to be compareSameStudy

* Add unit test

* PR comments
2025-12-04 12:26:21 -05:00
ohif-bot 14bc99124d chore(version): Update package versions [skip ci] 2025-12-03 22:37:56 +00:00
ohif-bot d1388ac9c8 chore(version): version.json [skip ci] 2025-12-03 22:37:49 +00:00
Dan Rukas 6566354986 fix(clipboard): Adds tooltip clipboard icon feedback when copy fails (#5609)
* Added missing icon feedback

* fixed colors
2025-12-03 17:33:37 -05:00
ohif-bot ff5ead6284 chore(version): Update package versions [skip ci] 2025-12-03 20:40:56 +00:00
ohif-bot 58c2cc70cf chore(version): version.json [skip ci] 2025-12-03 20:40:48 +00:00
Belbin-GKandtrenser-belbin 1aa4431121 fix: skip VOI sync for empty viewports (#5599)
Co-authored-by: trenser-belbin <belbin.kunjumon@prenuvo.com>
2025-12-03 15:36:30 -05:00
ohif-bot 4e5d5a7701 chore(version): Update package versions [skip ci] 2025-12-02 18:05:56 +00:00
ohif-bot 6b18aa085d chore(version): version.json [skip ci] 2025-12-02 18:05:49 +00:00
dependabot[bot]andJoe Boccanfuso 740dd7fe5f chore(deps): bump node-forge from 1.3.1 to 1.3.2 in /platform/docs (#5592)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Joe Boccanfuso <joe.boccanfuso@radicalimaging.com>
2025-12-02 13:00:17 -05:00
ohif-bot 5b75a6a56a chore(version): Update package versions [skip ci] 2025-12-02 14:53:29 +00:00
ohif-bot 3dba8dbc9f chore(version): version.json [skip ci] 2025-12-02 14:53:23 +00:00
Bill Wallace 4471416c30 fix: sculptor tool fixes (#5595)
Note: FOSSA check is invalid, but we don't have the ignore FOSSA version included.

* fix: sculptor tool fixes

* Add yarn

* update vtk.js version as well to match
2025-12-02 09:49:09 -05:00
ohif-bot a7229bb022 chore(version): Update package versions [skip ci] 2025-12-02 14:13:17 +00:00
ohif-bot 55ea706768 chore(version): version.json [skip ci] 2025-12-02 14:13:09 +00:00
dependabot[bot]andJoe Boccanfuso 6d6ce0ddae chore(deps): bump mdast-util-to-hast from 13.2.0 to 13.2.1 in /platform/docs (#5602)
* Fix node-forge security alerts as well.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Joe Boccanfuso <joe.boccanfuso@radicalimaging.com>
2025-12-02 09:08:43 -05:00
ohif-bot 0f26588c4b chore(version): Update package versions [skip ci] 2025-11-25 14:17:18 +00:00
ohif-bot 446f58eb76 chore(version): version.json [skip ci] 2025-11-25 14:17:11 +00:00
Pedro Köhler 411553ee91 feat: tooltips for tool settings (#5586) 2025-11-25 09:13:01 -05:00
ohif-bot e0d74a6674 chore(version): Update package versions [skip ci] 2025-11-24 18:14:22 +00:00
ohif-bot 7d0a3e6ce7 chore(version): version.json [skip ci] 2025-11-24 18:14:14 +00:00
Alireza 29cfb2c299 docs(docs): Correct grammar in README.md for clarity (#5584) 2025-11-24 13:09:50 -05:00
Joe Boccanfuso 94b0358f18 fix(docs): Empty commit as an attempt to fix the docs build. (#5582) 2025-11-24 09:46:21 -05:00
ohif-bot 173f42010b chore(version): Update package versions [skip ci] 2025-11-21 18:11:46 +00:00
ohif-bot c5a51607c9 chore(version): version.json [skip ci] 2025-11-21 18:11:39 +00:00
Bill Wallace 57205703bb Fix contour annotations RTSS saving (#5545)
* [WIP] Fix contour annotations RTSS saving

* fix: Export image sop image reference provider

* Fixes for download with filename

* Updates to fix metadata issues with CS3D

* Updates to save menu

* Fixing some additional re-save metadata

* Add support for saving with predecessor image sequence

* PR comment fixes

* fix: Load RTSS after save

* fix segmentation save

* fix load of mixed seg/rt studies

* Use frame module to get information on the frame shown

* Fix study browser to use instances.length when numImageFrames not present

* fix: Load of display set split from multiple non-FOR referenes

* Fix RT and SEG applying to wrong series because of using
referenced series sequence without checkout sop instances

* Fix save seg

* Update to released CS3D versions

* test: Add revokeObjectURL to test download blob

* Add hard dependency on mode-basic to fix build order issues

* fix: build

* Try to fix build segmentation

* PR comments

* PR comment update
2025-11-21 13:07:26 -05:00
ohif-bot 9c5043ca2c chore(version): Update package versions [skip ci] 2025-11-21 14:47:02 +00:00
ohif-bot 9899d3f623 chore(version): version.json [skip ci] 2025-11-21 14:46:55 +00:00
Dan Rukas 62d6bf437b fix(DicomUpload): Switch to use ui-next components and colors (#5576)
* Replaced legacy Button

* Updated Dialog width

* Restore text and spacing fixes

* Updated drop zone styling

* Migrated to ui-next colors
2025-11-21 09:42:14 -05:00
ohif-bot 4dc8debb02 chore(version): Update package versions [skip ci] 2025-11-20 14:33:45 +00:00
ohif-bot f72c47568b chore(version): version.json [skip ci] 2025-11-20 14:33:38 +00:00
Vinícius Alves de Faria Resende bdad2264ed tests(Playwright): Introduction of Page Object Models (POMs) to Playwright tests (#5557) 2025-11-20 09:29:14 -05:00
ohif-bot 1c991037ee chore(version): Update package versions [skip ci] 2025-11-20 01:08:59 +00:00
ohif-bot 44eafe16c1 chore(version): version.json [skip ci] 2025-11-20 01:08:52 +00:00
9ca02b4071 fix: Remove dead code and fix rectangle roi rehydration (#5490)
* Remove dead code and fix rectangle roi rehydration

* Revert bun.lock

* Remove hardcoded modality

* Add e2e tests

* Update screewnshots

* Adjustments to tests

* disabvle test for now

* Add test again

* Use ohif data soure

* Revert changes

* Test

* Update testdata to include point

* Improve tests

* Update screenshot

---------

Co-authored-by: Andrey Fedorov <andrey.fedorov@gmail.com>
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2025-11-19 20:04:23 -05:00
ohif-bot 0be3b950b7 chore(version): Update package versions [skip ci] 2025-11-19 23:00:57 +00:00
ohif-bot 186e9caeaa chore(version): version.json [skip ci] 2025-11-19 23:00:48 +00:00
Joe Boccanfuso 5c17c262ea fix(SegmentationStyle): Fix inactive contour visibility and styling. (#5563)
* Opacity, fill and outline properties for active and inactive contours
are now consistent with those for label map segmentations.
The properties for inactive segmentations are applied to BOTH
inactive contours and label maps.

* Activate the selected segmentation for a type when the alpha, outline width, or fill/outline visibility is changed.
2025-11-19 17:55:53 -05:00
dependabot[bot]andJoe Boccanfuso 62797acd9a chore(deps): bump glob from 10.4.5 to 10.5.0 (#5572)
Ignore CVE-2025-64756 (aka GHSA-5j98-mcp5-4vw2) in bun audit.

Co-authored-by: Joe Boccanfuso <joe.boccanfuso@radicalimaging.com>
2025-11-19 08:43:58 -05:00
ohif-bot b15c002f60 chore(version): Update package versions [skip ci] 2025-11-13 12:17:23 +00:00
ohif-bot 9b4e66c868 chore(version): version.json [skip ci] 2025-11-13 12:17:16 +00:00
Joe Boccanfuso 02f6744dab fix(segmentation): Allow for manually added label map and contour segmentations to be used as viewport data overlays. (#5562) 2025-11-13 07:12:28 -05:00
ohif-bot 5c44fe06dc chore(version): Update package versions [skip ci] 2025-11-11 13:24:22 +00:00
ohif-bot b71952ec86 chore(version): version.json [skip ci] 2025-11-11 13:24:16 +00:00
Joe Boccanfuso 834ae3c762 fix(interpolation): Auto accept interpolation when the interpolation process is completed. (#5555)
* Update Cornerstone3D packages to 4.9.1.
2025-11-11 08:19:45 -05:00
ohif-bot 79fab8d70d chore(version): Update package versions [skip ci] 2025-11-10 21:14:05 +00:00
ohif-bot ace476649f chore(version): version.json [skip ci] 2025-11-10 21:13:57 +00:00
58aa0d1eaa fix(dev): update webpack proxy config syntax for Orthanc dev mode (#5531)
* fix(dev): update webpack proxy config syntax for Orthanc dev mode

* Add datasources and rename dicomweb to orthancProxy datasource

---------

Co-authored-by: Nilesh Gautam <nilesh.gautam@radpretation.ai>
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2025-11-10 16:08:50 -05:00
ohif-bot bb45cb24b5 chore(version): Update package versions [skip ci] 2025-11-06 13:51:02 +00:00
ohif-bot 2a07964002 chore(version): version.json [skip ci] 2025-11-06 13:50:55 +00:00
Devu-trenser 16233d980f feat(metadata): metadata access improvement (#5292)
* feat(metadata): metadata access improvement

* feat: Make a few fields commonly used at the instance level inherit multiframe
2025-11-06 08:46:01 -05:00
ohif-bot 9413219206 chore(version): Update package versions [skip ci] 2025-11-05 17:05:25 +00:00
ohif-bot 14d62ce180 chore(version): version.json [skip ci] 2025-11-05 17:05:18 +00:00
Joe Boccanfuso a0556b08be fix(segmentation): When synchronizing a segmentation representation, use the type of the source viewport segmentation to determine the type of the target viewport segmentation (#5546) 2025-11-05 12:00:41 -05:00
ohif-bot bee60a5482 chore(version): Update package versions [skip ci] 2025-11-05 15:39:24 +00:00
ohif-bot 5c496c7f02 chore(version): version.json [skip ci] 2025-11-05 15:39:17 +00:00
Vinícius Alves de Faria Resende 29bd87c8c0 fix(SegmentationTools): [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool (#5535) 2025-11-05 10:34:41 -05:00
ohif-bot 0c8ab01d34 chore(version): Update package versions [skip ci] 2025-11-05 15:16:28 +00:00
ohif-bot f15887c9bf chore(version): version.json [skip ci] 2025-11-05 15:16:20 +00:00
Abhijith Sb 2481208f31 Fix : Problems related to measurement operation undo (#5541)
fixed the console error while undoing a removed annotation
2025-11-05 10:11:26 -05:00
ohif-bot 74c92fa601 chore(version): Update package versions [skip ci] 2025-10-30 20:09:19 +00:00
ohif-bot beb6a73bc7 chore(version): version.json [skip ci] 2025-10-30 20:09:11 +00:00
Joe Boccanfuso 000e42e9a6 fix(security): Exact versioning and docs dependabot alerts (#5536) 2025-10-30 16:04:19 -04:00
ohif-bot 9ad7623a25 chore(version): Update package versions [skip ci] 2025-10-29 18:22:19 +00:00
ohif-bot eb564aec5e chore(version): version.json [skip ci] 2025-10-29 18:22:11 +00:00
35bfc9b0cc feature(segmentation): Add contour tools and utilities in a side panel (#5517)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Dan Rukas <dan.rukas@gmail.com>
2025-10-29 14:15:52 -04:00
ohif-bot ab440f086a chore(version): Update package versions [skip ci] 2025-10-28 12:57:15 +00:00
ohif-bot 1d33bcf229 chore(version): version.json [skip ci] 2025-10-28 12:57:08 +00:00
Vinícius Alves de Faria Resende 76f7d4e23a fix(3DSegmentation): [Bug] The viewports become blank when loading the seg file in advanced layout after closing the seg file from any other advanced layout (#5505) 2025-10-28 08:52:12 -04:00
ohif-bot afa9131da1 chore(version): Update package versions [skip ci] 2025-10-27 19:21:49 +00:00
ohif-bot 5b5bca5e91 chore(version): version.json [skip ci] 2025-10-27 19:21:42 +00:00
Joe Boccanfuso 1f11c83006 fix(security): Addressed dependabot alert CVE-2025-59288 concerning playwright (#5527) 2025-10-27 15:16:43 -04:00
ohif-bot 1dd4f60034 chore(version): Update package versions [skip ci] 2025-10-24 20:53:28 +00:00
ohif-bot 2013c87ddb chore(version): version.json [skip ci] 2025-10-24 20:53:21 +00:00
Joe Boccanfuso 1009c60911 fix(security): For bun, enforce frozen lockfile. For yarn, strongly suggest using frozen lockfile. (#5508)
* Updated docs for security information.
2025-10-24 16:48:35 -04:00
ohif-bot b517944cb5 chore(version): Update package versions [skip ci] 2025-10-23 13:17:02 +00:00
ohif-bot 67d6622ae4 chore(version): version.json [skip ci] 2025-10-23 13:16:54 +00:00
Ibrahim a1544432db feat(jpeg2000): Add 16-bit RGB support to JPEG2000 decoder (#5519) 2025-10-23 09:11:54 -04:00
ohif-bot e0e1763472 chore(version): Update package versions [skip ci] 2025-10-22 16:18:31 +00:00
ohif-bot b844b56839 chore(version): version.json [skip ci] 2025-10-22 16:18:25 +00:00
k3motandTom Avitan 8b9445c442 fix: Fixed the tooltip text of the window button, it was mistakenly written in Chinese in the en-us json (#5513)
Fixed the tooltip text of the window button, it was mistakenly written in Chinese in the en-US json

Co-authored-by: Tom Avitan <tom.avitan@aidoc.com>
2025-10-22 12:14:00 -04:00
ohif-bot 8193311841 chore(version): Update package versions [skip ci] 2025-10-22 15:00:24 +00:00
ohif-bot c83710fa78 chore(version): version.json [skip ci] 2025-10-22 15:00:18 +00:00
Joe Boccanfuso e07a87dcae chore(workflow): Run playwright tests for release branch PRs. (#5515) 2025-10-22 10:55:46 -04:00
ohif-bot 2bc0647861 chore(version): Update package versions [skip ci] 2025-10-21 16:03:13 +00:00
ohif-bot 2a01dcce29 chore(version): version.json [skip ci] 2025-10-21 16:03:06 +00:00
Joe Boccanfuso 1df4f843fe fix(segmentation): Fixed the manual scrolling of the segments list and implemented automatic scrolling to the active segment. (#5510)
The height of the list container is now calculated based on a parent div instead of itself.
DataRow component now has a forward ref to allow for automatic scrolling.

Added better detection to the useDynamicMaxHeight hook for when to recalculate the max height using intersection observers.

The useEffect for scrolling a segment into view used to be excessively called to the point
where it was auto scrolling after the user manually scrolled. The auto scroll useEffect is
now dependent on the segment index changing which should only happen once per active segment change.
2025-10-21 11:58:23 -04:00
ohif-bot cf0b561a49 chore(version): Update package versions [skip ci] 2025-10-20 17:12:13 +00:00
ohif-bot f1675559ee chore(version): version.json [skip ci] 2025-10-20 17:12:07 +00:00
Ibrahim ffd9ec3273 fix(rendering): Fix palette color LUT conversion causing black images (#5509)
The palette color LUT was incorrectly handling 16 bit entries, and didn't work for both array buffers and typed arrays.
2025-10-20 13:07:39 -04:00
ohif-bot 66e6d73ad6 chore(version): Update package versions [skip ci] 2025-10-20 15:28:17 +00:00
ohif-bot 07c45d0364 chore(version): version.json [skip ci] 2025-10-20 15:28:10 +00:00
Bill Wallace 503780247c fix: Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data (#5506)
* fix: [#2404] by passing the right sop uid map

* Update PR with requried CS3D fixes

* Update to released versions - had wrong release number
2025-10-20 11:23:26 -04:00
ohif-bot 82c4a0a8d0 chore(version): Update package versions [skip ci] 2025-10-18 02:18:57 +00:00
ohif-bot 6a523ff78d chore(version): version.json [skip ci] 2025-10-18 02:18:51 +00:00
Joe Boccanfuso 170e860aa1 fix(segmentation): Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. (#5503)
* fix(segmentation): Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true.

* PR feedback.

* PR feedback.

* Added tests for disableEditing and utilities for simulating drag operations.

* Update bun.lock file after merging master.
2025-10-17 22:14:31 -04:00
ohif-bot 3e1d50c8d2 chore(version): Update package versions [skip ci] 2025-10-17 12:19:47 +00:00
ohif-bot 05b80305dc chore(version): version.json [skip ci] 2025-10-17 12:19:40 +00:00
Joe Boccanfuso 4620cc3acf fix(ErrorBoundary): Allow for details to be shown in production. (#5504)
Added showErrorDetails to config to allow for details to be shown in various runtime environments.
2025-10-17 08:15:13 -04:00
ohif-bot cf8f4aa120 chore(version): Update package versions [skip ci] 2025-10-17 01:59:53 +00:00
ohif-bot f75273ece6 chore(version): version.json [skip ci] 2025-10-17 01:59:47 +00:00
Luis Miguel SantosandBill Wallace 42aa2df626 fix 5323 unexpected series list filter change from all to recent (#5337)
* fix to unexpected change from All to recent browser study lists because of the search order for the presence of the study in the tab groups and subsequent reset of the active tab state.
Minor comment in the study browser header.

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>

* chore Added type annotations and cleaned up stylistically.

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>

* Tighten up a bit of the code to make it easier to follow

* Fix warnings

---------

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>

Confirm that Alireza's requested change was included.
2025-10-16 21:55:31 -04:00
ohif-bot be6f3db1ce chore(version): Update package versions [skip ci] 2025-10-17 00:07:14 +00:00
ohif-bot c41dbc8236 chore(version): version.json [skip ci] 2025-10-17 00:07:08 +00:00
53694da216 [Ready] fix 5288 removal of accept header options in retrieve metadata (#5293)
* chore: Minor adjustment to generateAcceptHeader function signature to reflect expected return type.

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>

* fix: Added switch to skip the Accept header generation when requesting metadata. WADO metadata request is more likely to return JSON and some VNAs do not like the extra options in the Accept header. Also, passing an empty array is not sufficient because somewhere we still include a comma that breaks the Accept header. It's better to omit it for the metadata request only.

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>

* chore: Added new HeadersInterface interface type docstrings.

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>

* chore: Refactored getAuthorizationHeader() function signature so it is typed checked. Of course, I upgraded the module to a TypeScript module.
Moved the request header interfaces into its own TypeScript module (RequestHeaders.ts) in the core types.

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>

* chore: Refactored the user logic to include a TypeScript interface. As a result, upgraded the source file to TypeScript.
Removed the User interface from RequestHeaders module and moved them to the user module.

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>

* chore: Updated function signatures, user import, and confirmed unit tests are passing.

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>

* chore: Minor stylistic adjustment to getAuthorizationHeader.test.ts

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>

* chore: Added missing comments.

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>

* chore adjusted generateWadoHeader parameter's name and added comment about the expected default header.

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>

* Just tweaking the interface a bit to be more consistent.

* fix authorization header signature change

---------

Signed-off-by: Luis M. Santos <luis.santos2@nih.gov>
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
Co-authored-by: Alireza <ar.sedghi@gmail.com>
2025-10-16 20:01:34 -04:00
ohif-bot 9d68b6ef31 chore(version): Update package versions [skip ci] 2025-10-15 14:51:20 +00:00
ohif-bot 2300c70314 chore(version): version.json [skip ci] 2025-10-15 14:51:13 +00:00
Joe Boccanfuso 86e924fb88 fix(docker): Use bun version 1.2.23 for docker build. (#5501) 2025-10-15 10:46:47 -04:00
ohif-bot e988216b2b chore(version): Update package versions [skip ci] 2025-10-15 13:40:22 +00:00
ohif-bot 23c8fdca00 chore(version): version.json [skip ci] 2025-10-15 13:40:14 +00:00
Devu-trenser faea09e7ef fix: Segmentation not displayed and unable to draw segments in volume viewport after converting Labelmap to Surface (#5488)
* fix: unable to draw segment after 3d layout

* comment update

---------
2025-10-15 09:35:26 -04:00
ohif-bot 05ecdfe512 chore(version): Update package versions [skip ci] 2025-10-15 11:37:08 +00:00
ohif-bot 0b6c38e5e0 chore(version): version.json [skip ci] 2025-10-15 11:37:01 +00:00
Pedro Köhler 3137aed017 fix(segmentationService): prevent no representation crash (#5495) 2025-10-15 07:32:27 -04:00
ohif-bot a1c6b5f3ec chore(version): Update package versions [skip ci] 2025-10-15 04:13:38 +00:00
ohif-bot be13e7506a chore(version): version.json [skip ci] 2025-10-15 04:13:32 +00:00
Joe Boccanfuso 0d2f4e715a fix(docs): Set exact version for glob. (#5497) 2025-10-15 00:08:57 -04:00
ohif-bot 37231a5ffe chore(version): Update package versions [skip ci] 2025-10-15 03:11:55 +00:00
ohif-bot c53ee335ee chore(version): version.json [skip ci] 2025-10-15 03:11:48 +00:00
Joe Boccanfuso fbd9371884 fix(security): Run bun audit before bun install. (#5496) 2025-10-14 23:07:00 -04:00
ohif-bot 5d57c05217 chore(version): Update package versions [skip ci] 2025-10-14 20:28:31 +00:00
ohif-bot ec38af8c06 chore(version): version.json [skip ci] 2025-10-14 20:28:24 +00:00
Joe Boccanfuso c7d2017f08 fix(security): Use exact versioning for dependencies in package.json files. (#5494)
Added bun audit security check in build process.
Removed bun caching.
2025-10-14 16:23:36 -04:00
ohif-bot 3d393df45d chore(version): Update package versions [skip ci] 2025-10-14 17:10:35 +00:00
ohif-bot 2f94e27d3b chore(version): version.json [skip ci] 2025-10-14 17:10:28 +00:00
Ujjwal Kumar Baranwal e9b57d5731 Update Dockerfile (#5486) 2025-10-14 13:06:01 -04:00
ohif-bot ff4ffbe975 chore(version): Update package versions [skip ci] 2025-10-14 17:00:02 +00:00
ohif-bot 458311252b chore(version): version.json [skip ci] 2025-10-14 16:59:55 +00:00
Joe Boccanfuso 3fe67d5c98 fix(bun): Use fixed bun version (#5493) 2025-10-14 12:55:32 -04:00
ohif-bot 87f78069e7 chore(version): Update package versions [skip ci] 2025-10-10 13:19:03 +00:00
ohif-bot f8f7a175c4 chore(version): version.json [skip ci] 2025-10-10 13:18:56 +00:00
Ujjwal Kumar Baranwal 41c028781b fix: Update Dockerfile (#5484)
* Update Dockerfile

* Update Dockerfile
2025-10-10 09:14:11 -04:00
ohif-bot f6cf4d127d chore(version): Update package versions [skip ci] 2025-10-09 20:56:01 +00:00
ohif-bot 131fb57cc9 chore(version): version.json [skip ci] 2025-10-09 20:55:54 +00:00
Joe Boccanfuso efe97cd01d chore(deps): Update cornerstone3D dependencies. (#5479) 2025-10-09 16:51:34 -04:00
ohif-bot 17e1641226 chore(version): Update package versions [skip ci] 2025-10-08 14:37:01 +00:00
ohif-bot be1b44a4ba chore(version): version.json [skip ci] 2025-10-08 14:36:52 +00:00
Joe Boccanfuso 84f27593a0 chore(deps): Update Cornerstone 3D to 4.5.3. (#5473) 2025-10-08 10:30:27 -04:00
ohif-bot 2af95988df chore(version): Update package versions [skip ci] 2025-10-07 23:24:02 +00:00
ohif-bot e2901eb30f chore(version): version.json [skip ci] 2025-10-07 23:23:54 +00:00
Joe Boccanfuso 377d5e2fb6 chore(deps): Update cornerstone dependencies. (#5470) 2025-10-07 19:18:10 -04:00
ohif-bot d3adeec109 chore(version): Update package versions [skip ci] 2025-10-07 21:45:41 +00:00
ohif-bot fc3aec7a79 chore(version): version.json [skip ci] 2025-10-07 21:45:34 +00:00
Devu-trenserandVinicius Faria 78df8ff03a fix(segmentation): Segmentation not displayed and unable to draw segments when switching back to volume/stack viewport from 3D viewport (#5430)
Co-authored-by: Vinicius Faria <viniciusfariaresende@gmail.com>
2025-10-07 18:40:32 -03:00
ohif-bot c4902f2b9e chore(version): Update package versions [skip ci] 2025-10-07 18:35:26 +00:00
ohif-bot 1423ecc9d8 chore(version): version.json [skip ci] 2025-10-07 18:35:16 +00:00
devin-ai-integration[bot]andJoe Boccanfuso 5040c947d2 fix: validation for Percentage of Max SUV input field in TMTV module (#5417)
Co-authored-by: Joe Boccanfuso <joe.boccanfuso@radicalimaging.com>
2025-10-07 14:29:07 -04:00
ohif-bot c05698c5ec chore(version): Update package versions [skip ci] 2025-10-07 17:29:12 +00:00
ohif-bot 3dd18a453a chore(version): version.json [skip ci] 2025-10-07 17:29:04 +00:00
Vinícius Alves de Faria Resende 2c6ce21c8f fix(Segmentation): [Bug #5420] Segmentation color resets after using toggleOneUp (#5465) 2025-10-07 13:23:26 -04:00
ohif-bot 0803dce328 chore(version): Update package versions [skip ci] 2025-10-07 15:48:25 +00:00
ohif-bot 990ae853b0 chore(version): version.json [skip ci] 2025-10-07 15:48:17 +00:00
Vinícius Alves de Faria Resende 3c421f124e test(CornerstoneUtils): Adding unit tests for most of Cornerstone Extension utilities (#5454) 2025-10-07 11:42:47 -04:00
ohif-bot 44beac799f chore(version): Update package versions [skip ci] 2025-10-07 13:15:00 +00:00
ohif-bot 3b4b59c32b chore(version): version.json [skip ci] 2025-10-07 13:14:53 +00:00
Pedro Köhler 7f12f398fb fix(measurementTrackingMachine): remove unreachable states (#5460) 2025-10-07 09:09:51 -04:00
ohif-bot 891b752e69 chore(version): Update package versions [skip ci] 2025-10-07 12:59:00 +00:00
ohif-bot 42bf9c1484 chore(version): version.json [skip ci] 2025-10-07 12:58:52 +00:00
Pedro Köhler 4d04dd5651 fix(toolbar): prevent duplicate command execution on repeated interactions (#5456) 2025-10-07 08:52:56 -04:00
ohif-bot 9e79e76bab chore(version): Update package versions [skip ci] 2025-10-06 13:44:50 +00:00
ohif-bot a58371e83b chore(version): version.json [skip ci] 2025-10-06 13:44:43 +00:00
d2ae13b03f fix: viewport ready event firing logic (#5462)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-06 09:39:56 -04:00
ohif-bot c9b687900e chore(version): Update package versions [skip ci] 2025-10-06 13:35:37 +00:00
ohif-bot 282633f907 chore(version): version.json [skip ci] 2025-10-06 13:35:30 +00:00
Pedro Köhler e2964bf669 fix(router): opt in to React Router v7 future flags (#5461) 2025-10-06 09:30:36 -04:00
ohif-bot fae3b25b7b chore(version): Update package versions [skip ci] 2025-10-06 13:01:53 +00:00
ohif-bot 64ee6d529f chore(version): version.json [skip ci] 2025-10-06 13:01:45 +00:00
Sofien-Sellami 792db26ff1 fix: Safely handle missing active viewport (#5448) 2025-10-06 08:56:31 -04:00
ohif-bot 60633a8f3f chore(version): Update package versions [skip ci] 2025-10-06 12:56:17 +00:00
ohif-bot 07eeb8e5fe chore(version): version.json [skip ci] 2025-10-06 12:56:08 +00:00
Igor Octaviano 851e74d7b8 feat(MeasurementService): add rendering of unmapped measurements (#5416) 2025-10-06 08:50:17 -04:00
ohif-bot 639863a8ef chore(version): Update package versions [skip ci] 2025-10-03 20:36:59 +00:00
ohif-bot a00f610b2d chore(version): version.json [skip ci] 2025-10-03 20:36:52 +00:00
AlirezaandCursor Agent 9e951e8800 fix: duplicate segmentation entries on layout switch (#5458)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-10-03 16:31:25 -04:00
ohif-bot 71c16615d1 chore(version): Update package versions [skip ci] 2025-10-03 16:32:53 +00:00
ohif-bot b1fc82315c chore(version): version.json [skip ci] 2025-10-03 16:32:46 +00:00
AlirezaandCursor Agent 6a1df9de4a fix: study browser mode for multiple studies (#5457)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-10-03 12:28:05 -04:00
ohif-bot 06445dcaa2 chore(version): Update package versions [skip ci] 2025-10-02 21:10:24 +00:00
ohif-bot a344183355 chore(version): version.json [skip ci] 2025-10-02 21:10:17 +00:00
ZZzzaaKK 2f28a0deab fix(studyBrowserCustomization): fix thumbnail menu item actions in study browser (#5283) 2025-10-02 17:05:45 -04:00
ohif-bot e04310a02d chore(version): Update package versions [skip ci] 2025-10-01 18:37:20 +00:00
ohif-bot 4efb7efd0e chore(version): version.json [skip ci] 2025-10-01 18:37:11 +00:00
Bill WallaceandIbrahim 9ad0d7fc8c feat: Add inherit from basic/longitudinal modes (#5435)
Co-authored-by: Ibrahim <ibrahim.mdev@gmail.com>
2025-10-01 14:31:26 -04:00
ohif-bot 71d21ee184 chore(version): Update package versions [skip ci] 2025-09-30 22:34:04 +00:00
ohif-bot 9677dfdb90 chore(version): version.json [skip ci] 2025-09-30 22:33:57 +00:00
Smoothengineer 5b361fb289 docs: grammar: change "it's" to "its" (#5451) 2025-09-30 18:29:00 -04:00
ohif-bot d9c5b840a5 chore(version): Update package versions [skip ci] 2025-09-30 19:12:30 +00:00
ohif-bot ec3a70b5a7 chore(version): version.json [skip ci] 2025-09-30 19:12:18 +00:00
Joe Boccanfuso a46b00f76b chore(deps): Update cornerstone3D to 4.3.14. (#5450) 2025-09-30 15:07:13 -04:00
ohif-bot f0e9865ca1 chore(version): Update package versions [skip ci] 2025-09-30 17:55:16 +00:00
ohif-bot 0d37705435 chore(version): version.json [skip ci] 2025-09-30 17:55:07 +00:00
Vinícius Alves de Faria Resende 12ac509c40 test(SegmentationService): Adding 98.63% of test line coverage to Segmentation Service through unit tests (#5421) 2025-09-30 13:49:13 -04:00
ohif-bot 9353a840ee chore(version): Update package versions [skip ci] 2025-09-29 20:01:26 +00:00
ohif-bot 8030577fb2 chore(version): version.json [skip ci] 2025-09-29 20:01:18 +00:00
Joe Boccanfuso dd86a8eabd fix(segmentation): Restored segment label tool in longitudinal mode. (#5443) 2025-09-29 15:56:11 -04:00
ohif-bot 47164463cc chore(version): Update package versions [skip ci] 2025-09-29 16:53:02 +00:00
ohif-bot 00b74664f9 chore(version): version.json [skip ci] 2025-09-29 16:52:54 +00:00
Dan RukasandJoe Boccanfuso c91752d575 ui(settings): Moved Segment Label Display from tools to settings panel (#5422)
Co-authored-by: Joe Boccanfuso <joe.boccanfuso@radicalimaging.com>
2025-09-29 12:47:45 -04:00
ohif-bot 41d4398342 chore(version): Update package versions [skip ci] 2025-09-29 02:32:36 +00:00
ohif-bot f0a5795b68 chore(version): version.json [skip ci] 2025-09-29 02:32:30 +00:00
Joe Boccanfuso 80f314a422 fix(security): Removed dependency on tar-fs by removing dependencies on storybook and sharp. (#5438) 2025-09-28 22:28:06 -04:00
ohif-bot a6408d6033 chore(version): Update package versions [skip ci] 2025-09-26 14:10:20 +00:00
ohif-bot 5332d3e5f5 chore(version): version.json [skip ci] 2025-09-26 14:10:12 +00:00
Joe Boccanfuso d41eca4ed5 chore(deps): update cornerstone to 4.3.10 (#5436) 2025-09-26 10:04:30 -04:00
ohif-bot a5ddcf43de chore(version): Update package versions [skip ci] 2025-09-25 18:12:56 +00:00
ohif-bot bd19ab983d chore(version): version.json [skip ci] 2025-09-25 18:12:49 +00:00
Igor Octaviano d88a341884 fix: Improve rt handling for bulkdata (#5415) 2025-09-25 14:07:35 -04:00
ohif-bot 6db50d26b2 chore(version): Update package versions [skip ci] 2025-09-25 14:26:37 +00:00
ohif-bot 3906c754f1 chore(version): version.json [skip ci] 2025-09-25 14:26:30 +00:00
毛知劲 | LIS/PACS/RIS 产品负责人 7783d0f558 fix(i18n): Add and update i18n Translation for Toolbar Tool Names (#5392) 2025-09-25 10:22:02 -04:00
ohif-bot add69e5e4f chore(version): Update package versions [skip ci] 2025-09-22 02:53:26 +00:00
ohif-bot d32edcca45 chore(version): version.json [skip ci] 2025-09-22 02:53:19 +00:00
Vinícius Alves de Faria Resende 69dbe27784 feat(Segmentation): Segmentation highlight animation function selection (#5401) 2025-09-21 22:48:35 -04:00
ohif-bot de201d567f chore(version): Update package versions [skip ci] 2025-09-19 13:30:27 +00:00
ohif-bot 26a042d8b9 chore(version): version.json [skip ci] 2025-09-19 13:30:20 +00:00
Vinícius Alves de Faria Resende 74b82bd934 fix(SegmentationService): _getSegmentCenter wrongly handling seg center availability (#5396) 2025-09-19 09:25:32 -04:00
ohif-bot a7576e07a7 chore(version): Update package versions [skip ci] 2025-09-17 19:40:05 +00:00
ohif-bot 66b2f16c9e chore(version): version.json [skip ci] 2025-09-17 19:39:58 +00:00
Joe Boccanfuso cf10e3cf67 chore(tests): update screenshots (#5413) 2025-09-17 15:35:13 -04:00
ohif-bot 3ea981b40c chore(version): Update package versions [skip ci] 2025-09-17 16:07:53 +00:00
ohif-bot e1ce9c63ee chore(version): version.json [skip ci] 2025-09-17 16:07:46 +00:00
dependabot[bot]andJoe Boccanfuso 06073af035 chore(deps): bump axios from 1.11.0 to 1.12.0 (#5412)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Joe Boccanfuso <joe.boccanfuso@radicalimaging.com>
2025-09-17 12:02:33 -04:00
ohif-bot 19f7bba5d8 chore(version): Update package versions [skip ci] 2025-09-16 19:56:57 +00:00
ohif-bot 5f7045eb09 chore(version): version.json [skip ci] 2025-09-16 19:56:51 +00:00
dependabot[bot]andJoe Boccanfuso 8610c862a9 chore(deps): bump axios from 1.11.0 to 1.12.0 (#5402)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Joe Boccanfuso <joe.boccanfuso@radicalimaging.com>
2025-09-16 15:52:13 -04:00
ohif-bot 8c8a4c2222 chore(version): Update package versions [skip ci] 2025-09-10 16:44:52 +00:00
ohif-bot 57cc0b0ae3 chore(version): version.json [skip ci] 2025-09-10 16:44:45 +00:00
Vinícius Alves de Faria Resende 56c5828b02 fix(ErrorBoundary): Fixing console errors and Improving error logging for better developer experience (#5378) 2025-09-10 12:40:09 -04:00
ohif-bot f6d05bc21c chore(version): Update package versions [skip ci] 2025-09-05 12:39:54 +00:00
ohif-bot a5d72a130a chore(version): version.json [skip ci] 2025-09-05 12:39:47 +00:00
Vinícius Alves de Faria Resende d6ea179ac4 fix(cornerstone-dicom-rt): adding caching usage to cornerstone-dicom-rt SOP Class Handler (#5387) 2025-09-05 08:35:16 -04:00
ohif-bot 7de1c78fc4 chore(version): Update package versions [skip ci] 2025-08-29 13:16:02 +00:00
ohif-bot 5f8f559426 chore(version): version.json [skip ci] 2025-08-29 13:15:55 +00:00
Omar Hamad 79473c2ea4 fix(oidc): resolve navigation throttling and blank screen during sign-in/sign-out with Keycloak + dcm4chee + Traefik (#5373) 2025-08-29 09:11:09 -04:00
ohif-bot ecf91bdcd4 chore(version): Update package versions [skip ci] 2025-08-29 12:22:34 +00:00
ohif-bot 2df818c8cd chore(version): version.json [skip ci] 2025-08-29 12:22:27 +00:00
이양구andBill Wallace 0f6f2bb432 fix: routerBasename declaration location (#5347)
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2025-08-29 08:17:38 -04:00
ohif-bot 9f2835be2d chore(version): Update package versions [skip ci] 2025-08-28 19:12:46 +00:00
ohif-bot 01a1265291 chore(version): version.json [skip ci] 2025-08-28 19:12:40 +00:00
Copilotandsedghi 83ee20d249 Replace individual system info fields with envinfo command in bug report template (#5368)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sedghi <7490180+sedghi@users.noreply.github.com>
2025-08-28 15:08:02 -04:00
ohif-bot a0a571cb4b chore(version): Update package versions [skip ci] 2025-08-28 18:04:29 +00:00
ohif-bot d45fd931b3 chore(version): version.json [skip ci] 2025-08-28 18:04:23 +00:00
Alireza fc0e7f8572 upgrade cs3d to 4.0 (#5371) 2025-08-28 13:59:26 -04:00
ohif-bot cb9c099f36 chore(version): Update package versions [skip ci] 2025-08-28 17:02:31 +00:00
ohif-bot ccb8a0dcc3 chore(version): version.json [skip ci] 2025-08-28 17:02:24 +00:00
Alireza 6a1838bf0d fix/migration 3p11 (#5370) 2025-08-28 12:57:45 -04:00
1128 changed files with 70709 additions and 16398 deletions

No files matched your search

+60 -12
View File
@@ -6,7 +6,7 @@ orbs:
defaults: &defaults
docker:
- image: cimg/node:20.18.1
- image: cimg/node:20.19.0
environment:
TERM: xterm
QUICK_BUILD: true
@@ -15,22 +15,13 @@ defaults: &defaults
commands:
install_bun:
steps:
- restore_cache:
keys:
- bun-cache-v2-{{ arch }}-latest
- run:
name: Install Bun
command: |
if [ ! -d "$HOME/.bun" ]; then
curl -fsSL https://bun.sh/install | bash
fi
curl -fsSL https://bun.sh/install | bash -s "bun-v1.2.23"
echo 'export BUN_INSTALL="$HOME/.bun"' >> $BASH_ENV
echo 'export PATH="$BUN_INSTALL/bin:$PATH"' >> $BASH_ENV
source $BASH_ENV
- save_cache:
key: bun-cache-v2-{{ arch }}-latest
paths:
- ~/.bun
jobs:
UNIT_TESTS:
@@ -121,6 +112,62 @@ jobs:
- checkout
- attach_workspace:
at: ~/repo
# SECURITY AUDIT - only when bun.lock has changed
- run:
name: 'Security Audit - High Risk Vulnerabilities'
command: |
git fetch origin master 2>/dev/null || true
BASE_REF=$(git merge-base HEAD origin/master 2>/dev/null)
if [[ -z "$BASE_REF" ]]; then
echo "Could not determine base ref (e.g. shallow clone or no origin/master), skipping security audit."
exit 0
fi
CHANGED_FILES=$(git diff --name-only origin/master...HEAD 2>/dev/null || echo "")
if ! echo "$CHANGED_FILES" | grep -qx 'bun.lock'; then
echo "⏭️ bun.lock unchanged - skipping security audit."
exit 0
fi
echo "🔍 bun.lock changed - running bun audit for security vulnerabilities..."
echo "Checking for HIGH-RISK vulnerabilities..."
# Define ignored vulnerabilities with comments
IGNORED_VULNS=(
"GHSA-3ppc-4f35-3m26" # CVE-2026-26996 - minimatch via itk-wasm and glob is safe because it does NOT use the CLI
# CVE-2026-26996 - minimatch via other packages are strictly for building and CI/CD purposes; no user supplied expressions are passed to minimatch
"GHSA-7r86-cg39-jmmj" # CVE-2026-27903 - minimatch same as above
"GHSA-23c5-xmqv-rm74" # CVE-2026-27904 - minimatch same as above
"GHSA-c2c7-rcm5-vvqj" # CVE-2026-33671 - picomatch is generally used for development and CI/CD purposes
)
# Build ignore flags
IGNORE_FLAGS=""
for vuln in "${IGNORED_VULNS[@]}"; do
IGNORE_FLAGS="$IGNORE_FLAGS --ignore=$vuln"
done
if bun audit $IGNORE_FLAGS --audit-level high; then
echo "✅ No high-risk vulnerabilities found"
echo "🎉 Security audit passed!"
else
echo ""
echo "❌ HIGH-RISK VULNERABILITIES DETECTED!"
echo "======================================"
echo ""
echo "🔧 To fix these issues:"
echo " 1. Run: bun audit"
echo " 2. Review the vulnerability details"
echo " 3. Update affected packages to secure versions"
echo " 4. Test your changes"
echo " 5. Re-run: bun audit --audit-level high"
echo ""
echo "📋 Full audit report:"
bun audit $IGNORE_FLAGS --audit-level low || true
echo ""
echo "❌ This build cannot proceed until high-risk vulnerabilities are resolved."
exit 1
fi
- run:
name: Install Dependencies
command: bun install --frozen-lockfile
@@ -339,6 +386,7 @@ jobs:
resource_class: large
parallelism: 8
steps:
- install_bun
- run:
name: Install System Dependencies
command: |
@@ -352,7 +400,7 @@ jobs:
name: Export Display Variable
command: export DISPLAY=:99
- cypress/install:
install-command: yarn install --no-save
install-command: yarn install --frozen-lockfile --no-save
- cypress/run-tests:
cypress-command: |
npx wait-on@latest http://localhost:3000 && cd platform/app && npx cypress run --record --parallel
+4
View File
@@ -2,6 +2,8 @@ version: 2
enable-beta-ecosystems: true
updates:
- package-ecosystem: 'bun'
# Disable all pull requests for bun version updates.
open-pull-requests-limit: 0
directory: '/'
schedule:
interval: 'daily'
@@ -10,6 +12,8 @@ updates:
prefix: 'chore'
include: 'scope'
- package-ecosystem: 'npm'
# Disable all pull requests for npm version updates.
open-pull-requests-limit: 0
directory: '/'
schedule:
interval: 'daily'
+22 -21
View File
@@ -51,28 +51,29 @@ body:
validations:
required: true
- type: input
id: os
- type: textarea
id: system_info
attributes:
label: 'OS'
description: 'Your operating system.'
placeholder: 'e.g., Windows 10, macOS 10.15.4'
validations:
required: true
- type: input
id: node-version
attributes:
label: 'Node version'
description: 'Your Node.js version.'
placeholder: 'e.g., 20.18.1'
validations:
required: true
- type: input
id: browser
attributes:
label: 'Browser'
description: 'Your browser.'
placeholder: 'e.g., Chrome 83.0.4103.116, Firefox 77.0.1, Safari 13.1.1'
label: 'System Information'
description: 'Please run the following command in your terminal and paste the output:'
placeholder: |
Run: npx envinfo --system --binaries --browsers
Then paste the output here. It should look something like:
System:
OS: Windows 10 10.0.19042
CPU: (8) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Memory: 15.89 GB / 31.74 GB
Shell: 1.0.0 - C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe
Binaries:
Node: 20.18.1 - C:\Program Files\nodejs\node.EXE
Yarn: 1.22.22 - C:\Users\user\AppData\Roaming\npm\yarn.CMD
npm: 10.8.2 - C:\Program Files\nodejs\npm.CMD
Browsers:
Chrome: 83.0.4103.116
Edge: Spartan (44.19041.1266.0), Chromium (83.0.478.58)
Firefox: 77.0.1
validations:
required: true
+2
View File
@@ -23,6 +23,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.23
- uses: actions/setup-node@v4
with:
node-version: 20 # Or your desired Node version
+5 -1
View File
@@ -1,7 +1,7 @@
name: Playwright Tests
on:
pull_request:
branches: [master]
branches: [master, release/*]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -18,9 +18,13 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.23
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install Yarn
run: npm install -g yarn@1.22.22
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Install Playwright browsers
+1 -1
View File
@@ -19,7 +19,7 @@ echo 'Web application built and copied'
# Build && Move Docusaurus Output (for the docs themselves)
cd platform/docs
yarn install
yarn install --frozen-lockfile
yarn run build
cd ../..
mkdir -p ./.netlify/www/docs
+1 -1
View File
@@ -10,6 +10,6 @@
"deploy": "netlify deploy --prod --dir ./../platform/app/dist"
},
"devDependencies": {
"netlify-cli": "^2.21.0"
"netlify-cli": "2.21.0"
}
}
+1220
View File
File diff suppressed because it is too large. Load diff
+3 -2
View File
@@ -49,7 +49,7 @@ RUN apt-get update && apt-get install -y build-essential python3
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
RUN npm install -g bun
RUN npm install -g bun@1.2.23
RUN npm install -g lerna@7.4.2
ENV PATH=/usr/src/app/node_modules/.bin:$PATH
@@ -60,6 +60,7 @@ COPY --parents ./addOns/package.json ./addOns/*/*/package.json ./extensions/*/pa
RUN bun pm cache rm
RUN bun install
RUN bun add ajv@8.12.0
# Copy the local directory
COPY --link --exclude=yarn.lock --exclude=package.json --exclude=Dockerfile . .
@@ -98,7 +99,7 @@ COPY --from=builder /usr/src/app/platform/app/dist/dicom-microscopy-viewer /usr/
# In entrypoint.sh, app-config.js might be overwritten, so chmod it to be writeable.
# The nginx user cannot chmod it, so change to root.
USER root
RUN chown -R nginx:nginx /usr/share/nginx/html && chmod -R 666 /usr/share/nginx/html
RUN chown -R nginx:nginx /usr/share/nginx/html && chmod -R 777 /usr/share/nginx/html
USER nginx
ENTRYPOINT ["/usr/src/entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
+11 -4
View File
@@ -53,7 +53,7 @@ provided by the <a href="https://ohif.org/">Open Health Imaging Foundation (OHIF
## About
The OHIF Viewer can retrieve
and load images from most sources and formats; render sets in 2D, 3D, and
and load images from most sources and formats, render sets in 2D, 3D, and
reconstructed representations; allows for the manipulation, annotation, and
serialization of observations; supports internationalization, OpenID Connect,
offline use, hotkeys, and many more features.
@@ -76,7 +76,7 @@ contributions of individuals, research groups, and commercial organizations.
After more than 8-years of integrating with many companies and organizations,
The OHIF Viewer has been rebuilt from the ground up to better address the
varying workflow and configuration needs of its many users. All of the Viewer's
core features are built using it's own extension system. The same extensibility
core features are built using its own extension system. The same extensibility
that allows us to offer:
- 2D and 3D medical image viewing
@@ -147,7 +147,14 @@ Here is a schematic representation of our development workflow:
3. Navigate to the cloned project's directory
4. Add this repo as a `remote` named `upstream`
- `git remote add upstream https://github.com/OHIF/Viewers.git`
5. `yarn install` to restore dependencies and link projects
5. `yarn install --frozen-lockfile` to restore dependencies and link projects
:::danger
In general run `yarn install` with the `--frozen-lockfile` flag to help avoid
supply chain attacks by enforcing reproducible dependencies. That is, if the
`yarn.lock` file is clean and does NOT reference compromised packages, then
no compromised packages should land on your machine by using this flag.
:::
#### To Develop
@@ -158,7 +165,7 @@ _From this repository's root directory:_
yarn config set workspaces-experimental true
# Restore dependencies
yarn install
yarn install --frozen-lockfile
```
## Commands
+969
View File
@@ -3,6 +3,975 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.12.2](https://github.com/OHIF/Viewers/compare/v3.12.1...v3.12.2) (2026-05-18)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.135...v3.12.0) (2026-02-06)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.135](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.134...v3.12.0-beta.135) (2026-02-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.133](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.132...v3.12.0-beta.133) (2026-02-04)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.132](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.131...v3.12.0-beta.132) (2026-01-30)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.131](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.130...v3.12.0-beta.131) (2026-01-30)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.130](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.129...v3.12.0-beta.130) (2026-01-28)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.129](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.128...v3.12.0-beta.129) (2026-01-27)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.127](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.126...v3.12.0-beta.127) (2026-01-22)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.126](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.125...v3.12.0-beta.126) (2026-01-22)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.125](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.124...v3.12.0-beta.125) (2026-01-21)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.124](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.123...v3.12.0-beta.124) (2026-01-16)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.123](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.122...v3.12.0-beta.123) (2026-01-14)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.122](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.121...v3.12.0-beta.122) (2026-01-13)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.121](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.120...v3.12.0-beta.121) (2026-01-12)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.120](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.119...v3.12.0-beta.120) (2026-01-09)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.119](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.118...v3.12.0-beta.119) (2026-01-08)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.118](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.117...v3.12.0-beta.118) (2026-01-07)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.117](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.116...v3.12.0-beta.117) (2026-01-06)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.116](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.115...v3.12.0-beta.116) (2026-01-06)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.115](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.114...v3.12.0-beta.115) (2026-01-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.114](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.113...v3.12.0-beta.114) (2026-01-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.113](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.112...v3.12.0-beta.113) (2025-12-19)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.112](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.111...v3.12.0-beta.112) (2025-12-19)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.110...v3.12.0-beta.111) (2025-12-18)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.109...v3.12.0-beta.110) (2025-12-18)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.108...v3.12.0-beta.109) (2025-12-18)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.107...v3.12.0-beta.108) (2025-12-17)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.107](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.106...v3.12.0-beta.107) (2025-12-16)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.104...v3.12.0-beta.105) (2025-12-15)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.104](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.103...v3.12.0-beta.104) (2025-12-12)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.103](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.102...v3.12.0-beta.103) (2025-12-12)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.101...v3.12.0-beta.102) (2025-12-10)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.100...v3.12.0-beta.101) (2025-12-09)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.99...v3.12.0-beta.100) (2025-12-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.98...v3.12.0-beta.99) (2025-12-04)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.97...v3.12.0-beta.98) (2025-12-03)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
### Bug Fixes
* sculptor tool fixes ([#5595](https://github.com/OHIF/Viewers/issues/5595)) ([4471416](https://github.com/OHIF/Viewers/commit/4471416c30bf80d9f6766660d103f28c83ba7bb3))
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.61](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.60...v3.12.0-beta.61) (2025-10-15)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.60](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.59...v3.12.0-beta.60) (2025-10-15)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.59](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.58...v3.12.0-beta.59) (2025-10-14)
### Bug Fixes
* **security:** Use exact versioning for dependencies in package.json files. ([#5494](https://github.com/OHIF/Viewers/issues/5494)) ([c7d2017](https://github.com/OHIF/Viewers/commit/c7d2017f081c5803a70bab6c75bba0c975028125))
# [3.12.0-beta.58](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.57...v3.12.0-beta.58) (2025-10-14)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.57](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.56...v3.12.0-beta.57) (2025-10-14)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.56](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.55...v3.12.0-beta.56) (2025-10-10)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.55](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.54...v3.12.0-beta.55) (2025-10-09)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.54](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.53...v3.12.0-beta.54) (2025-10-08)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.53](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.52...v3.12.0-beta.53) (2025-10-07)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.52](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.51...v3.12.0-beta.52) (2025-10-07)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.51](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.50...v3.12.0-beta.51) (2025-10-07)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.50](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.49...v3.12.0-beta.50) (2025-10-07)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.49](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.48...v3.12.0-beta.49) (2025-10-07)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.48](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.47...v3.12.0-beta.48) (2025-10-07)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.47](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.46...v3.12.0-beta.47) (2025-10-07)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.46](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.45...v3.12.0-beta.46) (2025-10-06)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.45](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.44...v3.12.0-beta.45) (2025-10-06)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.44](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.43...v3.12.0-beta.44) (2025-10-06)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.43](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.42...v3.12.0-beta.43) (2025-10-06)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.42](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.41...v3.12.0-beta.42) (2025-10-03)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.41](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.40...v3.12.0-beta.41) (2025-10-03)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.40](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.39...v3.12.0-beta.40) (2025-10-02)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.39](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.38...v3.12.0-beta.39) (2025-10-01)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.38](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.37...v3.12.0-beta.38) (2025-09-30)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.37](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.36...v3.12.0-beta.37) (2025-09-30)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.36](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.35...v3.12.0-beta.36) (2025-09-30)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.35](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.34...v3.12.0-beta.35) (2025-09-29)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.34](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.33...v3.12.0-beta.34) (2025-09-29)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.33](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.32...v3.12.0-beta.33) (2025-09-29)
### Bug Fixes
* **security:** Removed dependency on tar-fs by removing dependencies on storybook and sharp. ([#5438](https://github.com/OHIF/Viewers/issues/5438)) ([80f314a](https://github.com/OHIF/Viewers/commit/80f314a422ec45fff6cdd5869f4293a110d00125))
# [3.12.0-beta.32](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.31...v3.12.0-beta.32) (2025-09-26)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.31](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.30...v3.12.0-beta.31) (2025-09-25)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.29](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.28...v3.12.0-beta.29) (2025-09-22)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.28](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.27...v3.12.0-beta.28) (2025-09-19)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.27](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.26...v3.12.0-beta.27) (2025-09-17)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.25...v3.12.0-beta.26) (2025-09-17)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.24...v3.12.0-beta.25) (2025-09-16)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.23...v3.12.0-beta.24) (2025-09-10)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.22...v3.12.0-beta.23) (2025-09-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.21...v3.12.0-beta.22) (2025-08-29)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.20...v3.12.0-beta.21) (2025-08-29)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.19...v3.12.0-beta.20) (2025-08-28)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.18...v3.12.0-beta.19) (2025-08-28)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.17...v3.12.0-beta.18) (2025-08-28)
**Note:** Version bump only for package @externals/devDependencies
# [3.12.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.16...v3.12.0-beta.17) (2025-08-28)
**Note:** Version bump only for package @externals/devDependencies
+65 -69
View File
@@ -1,7 +1,7 @@
{
"name": "@externals/devDependencies",
"description": "External dev dependencies - put dev build dependencies here",
"version": "3.12.0-beta.17",
"version": "3.12.2",
"license": "MIT",
"private": true,
"engines": {
@@ -9,85 +9,81 @@
"yarn": ">=1.19.1"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@kitware/vtk.js": "32.12.0",
"clsx": "^2.1.1",
"core-js": "^3.2.1",
"moment": "^2.30.1"
"@babel/runtime": "7.28.2",
"@kitware/vtk.js": "34.15.1",
"clsx": "2.1.1",
"core-js": "3.45.1",
"moment": "2.30.1"
},
"peerDependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react": "18.3.1",
"react-dom": "18.3.1"
},
"devDependencies": {
"@babel/eslint-parser": "7.28.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
"@rsbuild/core": "^1.3.16",
"@rsbuild/plugin-node-polyfill": "^1.3.0",
"@rsbuild/plugin-react": "^1.3.1",
"@svgr/webpack": "^8.1.0",
"@swc/helpers": "^0.5.15",
"@types/jest": "^27.5.0",
"@typescript-eslint/eslint-plugin": "^6.3.0",
"@typescript-eslint/parser": "^6.3.0",
"autoprefixer": "^10.4.4",
"babel-loader": "^8.2.4",
"babel-plugin-module-resolver": "^5.0.0",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^9.0.1",
"cross-env": "^5.2.0",
"css-loader": "^6.8.1",
"dotenv": "^8.1.0",
"eslint": "^8.39.0",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.17",
"@rsbuild/core": "1.5.1",
"@rsbuild/plugin-node-polyfill": "1.4.2",
"@rsbuild/plugin-react": "1.4.0",
"@svgr/webpack": "8.1.0",
"@swc/helpers": "0.5.17",
"@types/jest": "27.5.2",
"@typescript-eslint/eslint-plugin": "8.56.0",
"@typescript-eslint/parser": "8.56.0",
"autoprefixer": "10.4.21",
"babel-loader": "8.4.1",
"clean-webpack-plugin": "3.0.0",
"copy-webpack-plugin": "9.1.0",
"cross-env": "7.0.3",
"css-loader": "6.11.0",
"dotenv": "8.6.0",
"eslint": "9.39.3",
"eslint-config-prettier": "7.2.0",
"eslint-config-react-app": "^6.0.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-flowtype": "^7.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-node": "^11.1.0",
"eslint-config-react-app": "7.0.1",
"eslint-plugin-cypress": "2.15.2",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-prettier": "5.5.1",
"eslint-plugin-promise": "^5.2.0",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.4.0",
"eslint-plugin-tsdoc": "^0.2.11",
"eslint-webpack-plugin": "^2.5.3",
"execa": "^8.0.1",
"extract-css-chunks-webpack-plugin": "^4.5.4",
"html-webpack-plugin": "^5.3.2",
"husky": "^3.0.0",
"jest": "^29.5.0",
"jest-canvas-mock": "^2.1.0",
"jest-environment-jsdom": "^29.5.0",
"jest-junit": "^6.4.0",
"lerna": "^7.2.0",
"lint-staged": "^9.0.2",
"mini-css-extract-plugin": "^2.1.0",
"optimize-css-assets-webpack-plugin": "^6.0.1",
"postcss": "^8.3.5",
"postcss-import": "^14.0.2",
"postcss-loader": "^6.1.1",
"postcss-preset-env": "^7.4.3",
"eslint-plugin-promise": "7.2.1",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-react-hooks": "7.0.1",
"eslint-plugin-tsdoc": "0.2.17",
"execa": "8.0.1",
"extract-css-chunks-webpack-plugin": "4.10.0",
"html-webpack-plugin": "5.6.3",
"husky": "3.1.0",
"jest": "29.7.0",
"jest-canvas-mock": "2.5.2",
"jest-environment-jsdom": "29.7.0",
"jest-junit": "6.4.0",
"lerna": "9.0.4",
"lint-staged": "9.5.0",
"mini-css-extract-plugin": "2.9.2",
"optimize-css-assets-webpack-plugin": "6.0.1",
"postcss": "8.5.6",
"postcss-import": "14.1.0",
"postcss-loader": "6.2.1",
"postcss-preset-env": "7.8.3",
"prettier": "3.6.2",
"prettier-plugin-tailwindcss": "0.6.9",
"react-refresh": "^0.14.2",
"semver": "^7.5.1",
"serve": "^14.2.4",
"shader-loader": "^1.3.1",
"shx": "^0.3.3",
"source-map-loader": "^4.0.1",
"style-loader": "^1.0.0",
"terser-webpack-plugin": "^5.1.4",
"react-refresh": "0.14.2",
"semver": "7.7.2",
"serve": "14.2.5",
"shader-loader": "1.3.1",
"shx": "0.3.4",
"source-map-loader": "4.0.2",
"style-loader": "1.3.0",
"terser-webpack-plugin": "5.3.14",
"typescript": "5.5.4",
"unused-webpack-plugin": "2.4.0",
"webpack": "^5.94.0",
"webpack-bundle-analyzer": "^4.8.0",
"webpack-cli": "^5.0.1",
"webpack": "5.105.0",
"webpack-bundle-analyzer": "4.10.2",
"webpack-cli": "5.1.4",
"webpack-dev-server": "5.2.2",
"webpack-hot-middleware": "^2.25.0",
"webpack-merge": "^5.7.3",
"workbox-webpack-plugin": "^6.1.5",
"worker-loader": "^3.0.8"
"webpack-hot-middleware": "2.26.1",
"webpack-merge": "5.10.0",
"workbox-webpack-plugin": "6.6.1",
"worker-loader": "3.0.8"
},
"scripts": {
"build": "Included as direct dependency"
+966
View File
@@ -3,6 +3,972 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.12.2](https://github.com/OHIF/Viewers/compare/v3.12.1...v3.12.2) (2026-05-18)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.135...v3.12.0) (2026-02-06)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.135](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.134...v3.12.0-beta.135) (2026-02-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.133](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.132...v3.12.0-beta.133) (2026-02-04)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.132](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.131...v3.12.0-beta.132) (2026-01-30)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.131](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.130...v3.12.0-beta.131) (2026-01-30)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.130](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.129...v3.12.0-beta.130) (2026-01-28)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.129](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.128...v3.12.0-beta.129) (2026-01-27)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.127](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.126...v3.12.0-beta.127) (2026-01-22)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.126](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.125...v3.12.0-beta.126) (2026-01-22)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.125](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.124...v3.12.0-beta.125) (2026-01-21)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.124](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.123...v3.12.0-beta.124) (2026-01-16)
### Bug Fixes
* **microscopy:** Update microscope to 0.48.17 to fix a measurement exception. ([#5710](https://github.com/OHIF/Viewers/issues/5710)) ([4a1f509](https://github.com/OHIF/Viewers/commit/4a1f5098654dc7afef4c135f2e2198a0e785f4b9))
# [3.12.0-beta.123](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.122...v3.12.0-beta.123) (2026-01-14)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.122](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.121...v3.12.0-beta.122) (2026-01-13)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.121](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.120...v3.12.0-beta.121) (2026-01-12)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.120](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.119...v3.12.0-beta.120) (2026-01-09)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.119](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.118...v3.12.0-beta.119) (2026-01-08)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.118](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.117...v3.12.0-beta.118) (2026-01-07)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.117](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.116...v3.12.0-beta.117) (2026-01-06)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.116](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.115...v3.12.0-beta.116) (2026-01-06)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.115](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.114...v3.12.0-beta.115) (2026-01-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.114](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.113...v3.12.0-beta.114) (2026-01-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.113](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.112...v3.12.0-beta.113) (2025-12-19)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.112](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.111...v3.12.0-beta.112) (2025-12-19)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.110...v3.12.0-beta.111) (2025-12-18)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.109...v3.12.0-beta.110) (2025-12-18)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.108...v3.12.0-beta.109) (2025-12-18)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.107...v3.12.0-beta.108) (2025-12-17)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.107](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.106...v3.12.0-beta.107) (2025-12-16)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.104...v3.12.0-beta.105) (2025-12-15)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.104](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.103...v3.12.0-beta.104) (2025-12-12)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.103](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.102...v3.12.0-beta.103) (2025-12-12)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.101...v3.12.0-beta.102) (2025-12-10)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.100...v3.12.0-beta.101) (2025-12-09)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.99...v3.12.0-beta.100) (2025-12-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.98...v3.12.0-beta.99) (2025-12-04)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.97...v3.12.0-beta.98) (2025-12-03)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.61](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.60...v3.12.0-beta.61) (2025-10-15)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.60](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.59...v3.12.0-beta.60) (2025-10-15)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.59](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.58...v3.12.0-beta.59) (2025-10-14)
### Bug Fixes
* **security:** Use exact versioning for dependencies in package.json files. ([#5494](https://github.com/OHIF/Viewers/issues/5494)) ([c7d2017](https://github.com/OHIF/Viewers/commit/c7d2017f081c5803a70bab6c75bba0c975028125))
# [3.12.0-beta.58](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.57...v3.12.0-beta.58) (2025-10-14)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.57](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.56...v3.12.0-beta.57) (2025-10-14)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.56](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.55...v3.12.0-beta.56) (2025-10-10)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.55](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.54...v3.12.0-beta.55) (2025-10-09)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.54](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.53...v3.12.0-beta.54) (2025-10-08)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.53](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.52...v3.12.0-beta.53) (2025-10-07)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.52](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.51...v3.12.0-beta.52) (2025-10-07)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.51](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.50...v3.12.0-beta.51) (2025-10-07)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.50](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.49...v3.12.0-beta.50) (2025-10-07)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.49](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.48...v3.12.0-beta.49) (2025-10-07)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.48](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.47...v3.12.0-beta.48) (2025-10-07)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.47](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.46...v3.12.0-beta.47) (2025-10-07)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.46](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.45...v3.12.0-beta.46) (2025-10-06)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.45](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.44...v3.12.0-beta.45) (2025-10-06)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.44](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.43...v3.12.0-beta.44) (2025-10-06)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.43](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.42...v3.12.0-beta.43) (2025-10-06)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.42](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.41...v3.12.0-beta.42) (2025-10-03)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.41](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.40...v3.12.0-beta.41) (2025-10-03)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.40](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.39...v3.12.0-beta.40) (2025-10-02)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.39](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.38...v3.12.0-beta.39) (2025-10-01)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.38](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.37...v3.12.0-beta.38) (2025-09-30)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.37](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.36...v3.12.0-beta.37) (2025-09-30)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.36](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.35...v3.12.0-beta.36) (2025-09-30)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.35](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.34...v3.12.0-beta.35) (2025-09-29)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.34](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.33...v3.12.0-beta.34) (2025-09-29)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.33](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.32...v3.12.0-beta.33) (2025-09-29)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.32](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.31...v3.12.0-beta.32) (2025-09-26)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.31](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.30...v3.12.0-beta.31) (2025-09-25)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.29](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.28...v3.12.0-beta.29) (2025-09-22)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.28](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.27...v3.12.0-beta.28) (2025-09-19)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.27](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.26...v3.12.0-beta.27) (2025-09-17)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.25...v3.12.0-beta.26) (2025-09-17)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.24...v3.12.0-beta.25) (2025-09-16)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.23...v3.12.0-beta.24) (2025-09-10)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.22...v3.12.0-beta.23) (2025-09-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.21...v3.12.0-beta.22) (2025-08-29)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.20...v3.12.0-beta.21) (2025-08-29)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.19...v3.12.0-beta.20) (2025-08-28)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.18...v3.12.0-beta.19) (2025-08-28)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.17...v3.12.0-beta.18) (2025-08-28)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.12.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.16...v3.12.0-beta.17) (2025-08-28)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
+2 -2
View File
@@ -1,9 +1,9 @@
{
"name": "@externals/dicom-microscopy-viewer",
"description": "External reference to dicom-microscopy-viewer",
"version": "3.12.0-beta.17",
"version": "3.12.2",
"license": "MIT",
"dependencies": {
"dicom-microscopy-viewer": "^0.48.6"
"dicom-microscopy-viewer": "0.48.17"
}
}
+20 -21
View File
@@ -22,30 +22,29 @@
},
"devDependencies": {
"@babel/core": "7.28.0",
"@babel/plugin-transform-class-properties": "^7.27.1",
"@babel/plugin-transform-object-rest-spread": "^7.28.0",
"@babel/plugin-transform-private-methods": "^7.27.1",
"@babel/plugin-transform-private-property-in-object": "^7.27.1",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-arrow-functions": "^7.16.7",
"@babel/plugin-transform-regenerator": "^7.16.7",
"@babel/plugin-transform-class-properties": "7.27.1",
"@babel/plugin-transform-object-rest-spread": "7.28.0",
"@babel/plugin-transform-private-methods": "7.27.1",
"@babel/plugin-transform-private-property-in-object": "7.27.1",
"@babel/plugin-syntax-dynamic-import": "7.8.3",
"@babel/plugin-transform-arrow-functions": "7.27.1",
"@babel/plugin-transform-regenerator": "7.28.1",
"@babel/plugin-transform-runtime": "7.28.0",
"@babel/plugin-transform-typescript": "^7.28.0",
"@babel/preset-env": "7.28.0",
"@babel/preset-react": "^7.27.1",
"@babel/preset-typescript": "^7.27.1"
"@babel/plugin-transform-typescript": "7.28.0",
"@babel/preset-env": "7.29.5",
"@babel/preset-react": "7.27.1",
"@babel/preset-typescript": "7.27.1"
},
"resolutions": {
"**/@babel/runtime": "^7.20.13",
"**/@babel/runtime": "7.28.2",
"commander": "8.3.0",
"dcmjs": "0.43.1",
"dicomweb-client": ">=0.10.4",
"nth-check": "^2.1.1",
"trim-newlines": "^5.0.0",
"glob-parent": "^6.0.2",
"trim": "^1.0.0",
"package-json": "^8.1.0",
"typescript": "5.5.4",
"sharp": "^0.32.6"
"dcmjs": "0.49.4",
"dicomweb-client": "0.10.4",
"nth-check": "2.1.1",
"trim-newlines": "5.0.0",
"glob-parent": "6.0.2",
"trim": "1.0.1",
"package-json": "8.1.1",
"typescript": "5.5.4"
}
}
+1
View File
@@ -35,6 +35,7 @@ module.exports = {
'@babel/plugin-transform-typescript',
'@babel/plugin-transform-class-static-block',
'@babel/plugin-transform-for-of',
['babel-plugin-transform-import-meta', { module: 'ES6' }],
],
},
production: {
+2513 -2456
View File
File diff suppressed because it is too large. Load diff
+2
View File
@@ -0,0 +1,2 @@
[install]
frozenLockfile = true
+2
View File
@@ -0,0 +1,2 @@
[install]
frozenLockfile = false
+1 -1
View File
@@ -1 +1 @@
d91cc230e5231ca32ee28736f661a1497f7834c5
75a24a33935bda8779da30ef9b725b387d80723d
-118
View File
@@ -1,118 +0,0 @@
diff --git a/extensions/cornerstone/src/components/ViewportOrientationMenu/ViewportOrientationMenu.tsx b/extensions/cornerstone/src/components/ViewportOrientationMenu/ViewportOrientationMenu.tsx
index 6c1e4f8f6..56dac15b5 100644
--- a/extensions/cornerstone/src/components/ViewportOrientationMenu/ViewportOrientationMenu.tsx
+++ b/extensions/cornerstone/src/components/ViewportOrientationMenu/ViewportOrientationMenu.tsx
@@ -156,65 +156,65 @@ function ViewportOrientationMenu({
>
<Button
variant="ghost"
- className="flex h-[28px] flex-shrink-0 items-center justify-start self-stretch"
+ className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
onClick={() => handleOrientationChange('axial')}
>
- <span className="mr-0 flex w-4 items-center justify-center">
+ <div className="mr-1 flex w-6 items-center justify-start">
{currentOrientation === 'axial' ? (
- <Icons.Checked className="text-primary h-5 w-5" />
+ <Icons.Checked className="text-primary h-6 w-6" />
) : null}
- </span>
- <span className="flex-1 text-left">Axial</span>
+ </div>
+ <div className="flex-1 text-left">Axial</div>
</Button>
<Button
variant="ghost"
- className="flex h-[28px] flex-shrink-0 items-center justify-start self-stretch"
+ className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
onClick={() => handleOrientationChange('sagittal')}
>
- <span className="mr-0 flex w-4 items-center justify-center">
+ <div className="mr-1 flex w-6 items-center justify-start">
{currentOrientation === 'sagittal' ? (
- <Icons.Checked className="text-primary h-5 w-5" />
+ <Icons.Checked className="text-primary h-6 w-6" />
) : null}
- </span>
- <span className="flex-1 text-left">Sagittal</span>
+ </div>
+ <div className="flex-1 text-left">Sagittal</div>
</Button>
<Button
variant="ghost"
- className="flex h-[28px] flex-shrink-0 items-center justify-start self-stretch"
+ className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
onClick={() => handleOrientationChange('coronal')}
>
- <span className="mr-0 flex w-4 items-center justify-center">
+ <div className="mr-1 flex w-6 items-center justify-start">
{currentOrientation === 'coronal' ? (
- <Icons.Checked className="text-primary h-5 w-5" />
+ <Icons.Checked className="text-primary h-6 w-6" />
) : null}
- </span>
- <span className="flex-1 text-left">Coronal</span>
+ </div>
+ <div className="flex-1 text-left">Coronal</div>
</Button>
<Button
variant="ghost"
- className="flex h-[28px] flex-shrink-0 items-center justify-start self-stretch"
+ className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
onClick={() => handleOrientationChange('acquisition')}
>
- <span className="mr-0 flex w-4 items-center justify-center">
+ <div className="mr-1 flex w-6 items-center justify-start">
{currentOrientation === 'acquisition' ? (
- <Icons.Checked className="text-primary h-5 w-5" />
+ <Icons.Checked className="text-primary h-6 w-6" />
) : null}
- </span>
- <span className="flex-1 text-left">Acquisition</span>
+ </div>
+ <div className="flex-1 text-left">Acquisition</div>
</Button>
{/* Divider */}
- <div className="my-2 border-t border-white/20" />
+ <div className="mx-1 my-2 border-t border-white/20" />
<Button
variant="ghost"
- className="flex h-[28px] flex-shrink-0 items-center justify-start self-stretch"
+ className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
onClick={() => handleOrientationChange('reformat')}
>
- <span className="mr-0 flex w-4 items-center justify-center">
+ <div className="mr-1 flex w-6 items-center justify-start">
{currentOrientation === 'reformat' ? (
- <Icons.Checked className="text-primary h-5 w-5" />
+ <Icons.Checked className="text-primary h-6 w-6" />
) : null}
- </span>
- <span className="flex-1 text-left">Reformat</span>
+ </div>
+ <div className="flex-1 text-left">Reformat</div>
</Button>
</PopoverContent>
</Popover>
diff --git a/platform/ui-next/src/components/Icons/Sources/Checked.tsx b/platform/ui-next/src/components/Icons/Sources/Checked.tsx
index 83c8fc57e..c88b75e0d 100644
--- a/platform/ui-next/src/components/Icons/Sources/Checked.tsx
+++ b/platform/ui-next/src/components/Icons/Sources/Checked.tsx
@@ -4,14 +4,14 @@ import type { IconProps } from '../types';
export const Checked = (props: IconProps) => (
<svg
{...props}
- width="24"
+ width="20"
height="24"
- viewBox="0 0 24 24"
+ viewBox="0 0 20 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
- d="M16 9L11.3333 15L8 12.4737"
+ d="M14 9L9.3333 15L6 12.4737"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
File diff suppressed because it is too large. Load diff
+15 -16
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-pmap",
"version": "3.12.0-beta.17",
"version": "3.12.2",
"description": "DICOM Parametric Map read workflow",
"author": "OHIF",
"license": "MIT",
@@ -33,22 +33,21 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.12.0-beta.17",
"@ohif/extension-cornerstone": "3.12.0-beta.17",
"@ohif/extension-default": "3.12.0-beta.17",
"@ohif/i18n": "3.12.0-beta.17",
"prop-types": "^15.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^12.2.2",
"react-router": "^6.8.1",
"react-router-dom": "^6.8.1"
"@ohif/core": "3.12.2",
"@ohif/extension-cornerstone": "3.12.2",
"@ohif/extension-default": "3.12.2",
"@ohif/i18n": "3.12.2",
"prop-types": "15.8.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-i18next": "12.3.1",
"react-router": "6.30.3",
"react-router-dom": "6.30.3"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.32.12",
"@cornerstonejs/core": "^3.32.12",
"@kitware/vtk.js": "32.12.0",
"react-color": "^2.19.3"
"@babel/runtime": "7.28.2",
"@cornerstonejs/adapters": "4.15.29",
"@cornerstonejs/core": "4.15.29",
"@kitware/vtk.js": "34.15.1"
}
}
@@ -3,6 +3,975 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.12.2](https://github.com/OHIF/Viewers/compare/v3.12.1...v3.12.2) (2026-05-18)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.135...v3.12.0) (2026-02-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.135](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.134...v3.12.0-beta.135) (2026-02-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.133](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.132...v3.12.0-beta.133) (2026-02-04)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.132](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.131...v3.12.0-beta.132) (2026-01-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.131](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.130...v3.12.0-beta.131) (2026-01-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.130](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.129...v3.12.0-beta.130) (2026-01-28)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.129](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.128...v3.12.0-beta.129) (2026-01-27)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.127](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.126...v3.12.0-beta.127) (2026-01-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.126](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.125...v3.12.0-beta.126) (2026-01-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.125](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.124...v3.12.0-beta.125) (2026-01-21)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.124](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.123...v3.12.0-beta.124) (2026-01-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.123](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.122...v3.12.0-beta.123) (2026-01-14)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.122](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.121...v3.12.0-beta.122) (2026-01-13)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.121](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.120...v3.12.0-beta.121) (2026-01-12)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.120](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.119...v3.12.0-beta.120) (2026-01-09)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.119](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.118...v3.12.0-beta.119) (2026-01-08)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.118](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.117...v3.12.0-beta.118) (2026-01-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.117](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.116...v3.12.0-beta.117) (2026-01-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.116](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.115...v3.12.0-beta.116) (2026-01-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.115](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.114...v3.12.0-beta.115) (2026-01-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.114](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.113...v3.12.0-beta.114) (2026-01-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.113](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.112...v3.12.0-beta.113) (2025-12-19)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.112](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.111...v3.12.0-beta.112) (2025-12-19)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.110...v3.12.0-beta.111) (2025-12-18)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.109...v3.12.0-beta.110) (2025-12-18)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.108...v3.12.0-beta.109) (2025-12-18)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.107...v3.12.0-beta.108) (2025-12-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.107](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.106...v3.12.0-beta.107) (2025-12-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.104...v3.12.0-beta.105) (2025-12-15)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.104](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.103...v3.12.0-beta.104) (2025-12-12)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.103](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.102...v3.12.0-beta.103) (2025-12-12)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.101...v3.12.0-beta.102) (2025-12-10)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.100...v3.12.0-beta.101) (2025-12-09)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.99...v3.12.0-beta.100) (2025-12-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.98...v3.12.0-beta.99) (2025-12-04)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.97...v3.12.0-beta.98) (2025-12-03)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.61](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.60...v3.12.0-beta.61) (2025-10-15)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.60](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.59...v3.12.0-beta.60) (2025-10-15)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.59](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.58...v3.12.0-beta.59) (2025-10-14)
### Bug Fixes
* **security:** Use exact versioning for dependencies in package.json files. ([#5494](https://github.com/OHIF/Viewers/issues/5494)) ([c7d2017](https://github.com/OHIF/Viewers/commit/c7d2017f081c5803a70bab6c75bba0c975028125))
# [3.12.0-beta.58](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.57...v3.12.0-beta.58) (2025-10-14)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.57](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.56...v3.12.0-beta.57) (2025-10-14)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.56](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.55...v3.12.0-beta.56) (2025-10-10)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.55](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.54...v3.12.0-beta.55) (2025-10-09)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.54](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.53...v3.12.0-beta.54) (2025-10-08)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.53](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.52...v3.12.0-beta.53) (2025-10-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.52](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.51...v3.12.0-beta.52) (2025-10-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.51](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.50...v3.12.0-beta.51) (2025-10-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.50](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.49...v3.12.0-beta.50) (2025-10-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.49](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.48...v3.12.0-beta.49) (2025-10-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.48](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.47...v3.12.0-beta.48) (2025-10-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.47](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.46...v3.12.0-beta.47) (2025-10-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.46](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.45...v3.12.0-beta.46) (2025-10-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.45](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.44...v3.12.0-beta.45) (2025-10-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.44](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.43...v3.12.0-beta.44) (2025-10-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.43](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.42...v3.12.0-beta.43) (2025-10-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.42](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.41...v3.12.0-beta.42) (2025-10-03)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.41](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.40...v3.12.0-beta.41) (2025-10-03)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.40](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.39...v3.12.0-beta.40) (2025-10-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.39](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.38...v3.12.0-beta.39) (2025-10-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.38](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.37...v3.12.0-beta.38) (2025-09-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.37](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.36...v3.12.0-beta.37) (2025-09-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.36](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.35...v3.12.0-beta.36) (2025-09-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.35](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.34...v3.12.0-beta.35) (2025-09-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.34](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.33...v3.12.0-beta.34) (2025-09-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.33](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.32...v3.12.0-beta.33) (2025-09-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.32](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.31...v3.12.0-beta.32) (2025-09-26)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.31](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.30...v3.12.0-beta.31) (2025-09-25)
### Bug Fixes
* Improve rt handling for bulkdata ([#5415](https://github.com/OHIF/Viewers/issues/5415)) ([d88a341](https://github.com/OHIF/Viewers/commit/d88a341884f7361865bc10159c1a82633667ab5b))
# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.29](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.28...v3.12.0-beta.29) (2025-09-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.28](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.27...v3.12.0-beta.28) (2025-09-19)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.27](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.26...v3.12.0-beta.27) (2025-09-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.25...v3.12.0-beta.26) (2025-09-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.24...v3.12.0-beta.25) (2025-09-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.23...v3.12.0-beta.24) (2025-09-10)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.22...v3.12.0-beta.23) (2025-09-05)
### Bug Fixes
* **cornerstone-dicom-rt:** adding caching usage to cornerstone-dicom-rt SOP Class Handler ([#5387](https://github.com/OHIF/Viewers/issues/5387)) ([d6ea179](https://github.com/OHIF/Viewers/commit/d6ea179ac43a47d4011b8dcaf59d79d99382d6c8))
# [3.12.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.21...v3.12.0-beta.22) (2025-08-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.20...v3.12.0-beta.21) (2025-08-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.19...v3.12.0-beta.20) (2025-08-28)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.18...v3.12.0-beta.19) (2025-08-28)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.17...v3.12.0-beta.18) (2025-08-28)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.12.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.16...v3.12.0-beta.17) (2025-08-28)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
+12 -13
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-rt",
"version": "3.12.0-beta.17",
"version": "3.12.2",
"description": "DICOM RT read workflow",
"author": "OHIF",
"license": "MIT",
@@ -33,19 +33,18 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.12.0-beta.17",
"@ohif/extension-cornerstone": "3.12.0-beta.17",
"@ohif/extension-default": "3.12.0-beta.17",
"@ohif/i18n": "3.12.0-beta.17",
"prop-types": "^15.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^10.11.0",
"react-router": "^6.23.1",
"react-router-dom": "^6.23.1"
"@ohif/core": "3.12.2",
"@ohif/extension-cornerstone": "3.12.2",
"@ohif/extension-default": "3.12.2",
"@ohif/i18n": "3.12.2",
"prop-types": "15.8.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-i18next": "12.3.1",
"react-router": "6.30.3",
"react-router-dom": "6.30.3"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"react-color": "^2.19.3"
"@babel/runtime": "7.28.2"
}
}
@@ -4,7 +4,11 @@ import i18n from '@ohif/i18n';
import { SOPClassHandlerId } from './id';
import loadRTStruct from './loadRTStruct';
const sopClassUids = ['1.2.840.10008.5.1.4.1.1.481.3'];
const { sopClassDictionary } = utils;
const sopClassUids = [sopClassDictionary.RTStructureSetStorage];
const cachedRTStructsSEG = new Set<string>();
const loadPromises = {};
@@ -13,19 +17,25 @@ function _getDisplaySetsFromSeries(
servicesManager: AppTypes.ServicesManager,
extensionManager
) {
const instance = instances[0];
utils.sortStudyInstances(instances);
// Choose the LAST instance in the list as the most recently created one.
const instance = instances[instances.length - 1];
const {
StudyInstanceUID,
SeriesInstanceUID,
SOPInstanceUID,
SeriesDescription,
SeriesDescription = '',
SeriesNumber,
SeriesDate,
SeriesTime,
StructureSetDate,
StructureSetTime,
SOPClassUID,
wadoRoot,
wadoUri,
wadoUriRoot,
imageId: predecessorImageId,
} = instance;
const displaySet = {
@@ -35,7 +45,13 @@ function _getDisplaySetsFromSeries(
displaySetInstanceUID: utils.guid(),
SeriesDescription,
SeriesNumber,
SeriesDate,
/**
* The "SeriesDate" for a display set is really the display set date, which
* should be the date of the instance being used, which will be the structure
* set date in this case.
*/
SeriesDate: StructureSetDate || SeriesDate,
SeriesTime: StructureSetTime || SeriesTime,
SOPInstanceUID,
SeriesInstanceUID,
StudyInstanceUID,
@@ -50,6 +66,8 @@ function _getDisplaySetsFromSeries(
structureSet: null,
sopClassUids,
instance,
instances,
predecessorImageId,
wadoRoot,
wadoUriRoot,
wadoUri,
@@ -58,7 +76,10 @@ function _getDisplaySetsFromSeries(
};
let referencedSeriesSequence = instance.ReferencedSeriesSequence;
if (instance.ReferencedFrameOfReferenceSequence && !instance.ReferencedSeriesSequence) {
if (
instance.ReferencedFrameOfReferenceSequence?.RTReferencedStudySequence &&
!instance.ReferencedSeriesSequence
) {
instance.ReferencedSeriesSequence = _deriveReferencedSeriesSequenceFromFrameOfReferenceSequence(
instance.ReferencedFrameOfReferenceSequence
);
@@ -66,7 +87,8 @@ function _getDisplaySetsFromSeries(
}
if (!referencedSeriesSequence) {
throw new Error('ReferencedSeriesSequence is missing for the RTSTRUCT');
console.error('ReferencedSeriesSequence is missing for the RTSTRUCT');
return;
}
const referencedSeries = referencedSeriesSequence[0];
@@ -75,9 +97,13 @@ function _getDisplaySetsFromSeries(
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
const { displaySetService } = servicesManager.services;
const referencedDisplaySets = displaySetService.getDisplaySetsForSeries(
displaySet.referencedSeriesInstanceUID
);
const referencedDisplaySets =
displaySetService.getDisplaySetsForReferences(referencedSeriesSequence);
if (referencedDisplaySets?.length > 1) {
console.warn(
'Reference applies to more than 1 display set for Contours, applying only to first display set'
);
}
if (!referencedDisplaySets || referencedDisplaySets.length === 0) {
// Instead of throwing error, subscribe to display sets added
@@ -93,7 +119,7 @@ function _getDisplaySetsFromSeries(
}
);
} else {
const referencedDisplaySet = referencedDisplaySets[0];
const [referencedDisplaySet] = referencedDisplaySets;
displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
displaySet.isReconstructable = referencedDisplaySet.isReconstructable;
}
@@ -113,39 +139,45 @@ function _load(
) {
const { SOPInstanceUID } = rtDisplaySet;
const { segmentationService } = servicesManager.services;
if (
(rtDisplaySet.loading || rtDisplaySet.isLoaded) &&
loadPromises[SOPInstanceUID] &&
_segmentationExistsInCache(rtDisplaySet, segmentationService)
cachedRTStructsSEG.has(rtDisplaySet.displaySetInstanceUID)
) {
return loadPromises[SOPInstanceUID];
}
rtDisplaySet.loading = true;
const { unsubscribe } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
(evt: { rtDisplaySet: { displaySetInstanceUID: string } }) => {
if (evt.rtDisplaySet?.displaySetInstanceUID === rtDisplaySet.displaySetInstanceUID) {
cachedRTStructsSEG.add(rtDisplaySet.displaySetInstanceUID);
unsubscribe();
}
}
);
// We don't want to fire multiple loads, so we'll wait for the first to finish
// and also return the same promise to any other callers.
loadPromises[SOPInstanceUID] = new Promise(async (resolve, reject) => {
if (!rtDisplaySet.structureSet) {
const structureSet = await loadRTStruct(extensionManager, rtDisplaySet, headers);
loadPromises[SOPInstanceUID] = new Promise<void>(async (resolve, reject) => {
try {
if (!rtDisplaySet.structureSet) {
const structureSet = await loadRTStruct(extensionManager, rtDisplaySet, headers);
rtDisplaySet.structureSet = structureSet;
}
rtDisplaySet.structureSet = structureSet;
}
if (createSegmentation) {
await segmentationService.createSegmentationForRTDisplaySet(rtDisplaySet);
}
if (createSegmentation) {
segmentationService
.createSegmentationForRTDisplaySet(rtDisplaySet)
.then(() => {
rtDisplaySet.loading = false;
resolve();
})
.catch(error => {
rtDisplaySet.loading = false;
reject(error);
});
} else {
rtDisplaySet.loading = false;
resolve();
} catch (error) {
reject(error);
} finally {
rtDisplaySet.loading = false;
}
});
@@ -187,11 +219,6 @@ function _deriveReferencedSeriesSequenceFromFrameOfReferenceSequence(
return ReferencedSeriesSequence;
}
function _segmentationExistsInCache() {
// Todo: fix this
return false;
}
function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) {
const { servicesManager, extensionManager } = params;
@@ -2,9 +2,34 @@ import dcmjs from 'dcmjs';
const { DicomMessage, DicomMetaDictionary } = dcmjs.data;
const dicomlab2RGB = dcmjs.data.Colors.dicomlab2RGB;
async function checkAndLoadContourData(instance, datasource) {
/**
* Checks and loads contour data for RT Structure Set, handling both inline and bulk data URIs.
* Processes ROIContourSequence to extract contour data and resolve any bulk data references.
*
* @async
* @function checkAndLoadContourData
* @param {Object} params - Parameters object
* @param {Object} params.instance - Initial RT Structure instance
* @param {Object} params.dataSource - Data source for retrieving bulk data
* @param {Object} params.extensionManager - OHIF extension manager
* @param {Object} params.rtStructDisplaySet - RT Structure display set
* @param {Object} params.headers - HTTP headers for requests
* @returns {Promise<Object>} Promise that resolves to the processed RT Structure instance with loaded contour data
* @throws {Promise<string>} Rejects with error message if instance is invalid or data retrieval fails
*/
async function checkAndLoadContourData({
instance: initialInstance,
dataSource,
extensionManager,
rtStructDisplaySet,
headers,
}) {
let instance = initialInstance;
if (!instance || !instance.ROIContourSequence) {
return Promise.reject('Invalid instance object or ROIContourSequence');
instance = await getRTStructInstance({ extensionManager, rtStructDisplaySet, headers });
if (!instance || !instance.ROIContourSequence) {
return Promise.reject('Invalid instance object or ROIContourSequence');
}
}
const promisesMap = new Map();
@@ -30,11 +55,11 @@ async function checkAndLoadContourData(instance, datasource) {
} else if (contourData && contourData.BulkDataURI) {
const bulkDataURI = contourData.BulkDataURI;
if (!datasource || !datasource.retrieve || !datasource.retrieve.bulkDataURI) {
return Promise.reject('Invalid datasource object or retrieve function');
if (!dataSource || !dataSource.retrieve || !dataSource.retrieve.bulkDataURI) {
return Promise.reject('Invalid dataSource object or retrieve function');
}
const bulkDataPromise = datasource.retrieve.bulkDataURI({
const bulkDataPromise = dataSource.retrieve.bulkDataURI({
BulkDataURI: bulkDataURI,
StudyInstanceUID: instance.StudyInstanceUID,
SeriesInstanceUID: instance.SeriesInstanceUID,
@@ -44,6 +69,40 @@ async function checkAndLoadContourData(instance, datasource) {
promisesMap.has(referencedROINumber)
? promisesMap.get(referencedROINumber).push(bulkDataPromise)
: promisesMap.set(referencedROINumber, [bulkDataPromise]);
} else if (contourData && contourData.InlineBinary) {
// Contour data is still in binary format, conversion needed
const base64String = contourData.InlineBinary;
const decodedText = atob(base64String);
const rawValues = decodedText.split('\\');
const result = [];
// Ensure strictly that we have a full set of 3 coordinates
if (rawValues.length % 3 !== 0) {
return Promise.reject('ContourData raw values not divisible by 3');
}
for (let i = 0; i < rawValues.length; i += 3) {
if (i + 2 < rawValues.length) {
const x = parseFloat(rawValues[i]);
const y = parseFloat(rawValues[i + 1]);
const z = parseFloat(rawValues[i + 2]);
// Only push if all three are valid numbers (filters out trailing empty splits)
if (!isNaN(x) && !isNaN(y) && !isNaN(z)) {
result.push(x);
result.push(y);
result.push(z);
} else {
return Promise.reject('Error parsing contourData from InlineBinary format');
}
}
}
promisesMap.has(referencedROINumber)
? promisesMap.get(referencedROINumber).push(result)
: promisesMap.set(referencedROINumber, [result]);
} else {
return Promise.reject(`Invalid ContourData: ${contourData}`);
}
@@ -64,9 +123,12 @@ async function checkAndLoadContourData(instance, datasource) {
ROIContour.ContourSequence.forEach((Contour, index) => {
const promise = resolvedPromises[index];
if (promise.status === 'fulfilled') {
if (Array.isArray(promise.value) && promise.value.every(Number.isFinite)) {
if (
Array.isArray(promise.value) &&
promise.value.every(it => Number.isFinite(Number(it)))
) {
// If promise.value is already an array of numbers, use it directly
Contour.ContourData = promise.value;
Contour.ContourData = promise.value.map(Number);
} else {
// If the resolved promise value is a byte array (Blob), it needs to be decoded
const uint8Array = new Uint8Array(promise.value);
@@ -87,35 +149,74 @@ async function checkAndLoadContourData(instance, datasource) {
console.error(error);
}
});
return instance;
}
export default async function loadRTStruct(extensionManager, rtStructDisplaySet, headers) {
/**
* Retrieves and parses RT Structure Set instance from DICOM data.
* Uses the cornerstone utility module to load DICOM data and converts it to a naturalized dataset.
*
* @async
* @function getRTStructInstance
* @param {Object} params - Parameters object
* @param {Object} params.extensionManager - OHIF extension manager
* @param {Object} params.rtStructDisplaySet - RT Structure display set
* @param {Object} params.headers - HTTP headers for requests
* @returns {Promise<Object>} Promise that resolves to the parsed RT Structure dataset
*/
const getRTStructInstance = async ({ extensionManager, rtStructDisplaySet, headers }) => {
const utilityModule = extensionManager.getModuleEntry(
'@ohif/extension-cornerstone.utilityModule.common'
);
const { dicomLoaderService } = utilityModule.exports;
const segArrayBuffer = await dicomLoaderService.findDicomDataPromise(
rtStructDisplaySet,
null,
headers
);
const dicomData = DicomMessage.readFile(segArrayBuffer);
const rtStructDataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
rtStructDataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
return rtStructDataset;
};
/**
* Main function to load and process RT Structure Set data.
* Creates a structure set object with ROI contours, metadata, and visualization properties.
* Handles both bulk data URI and inline contour data scenarios.
*
* @async
* @function loadRTStruct
* @param {Object} extensionManager - OHIF extension manager
* @param {Object} rtStructDisplaySet - RT Structure display set to process
* @param {Object} headers - HTTP headers for data requests
* @returns {Promise<Object>} Promise that resolves to a structure set object containing:
* - StructureSetLabel: Label of the structure set
* - SeriesInstanceUID: Series instance UID
* - ROIContours: Array of ROI contour data with points and metadata
* - visible: Visibility state
* - ReferencedSOPInstanceUIDsSet: Set of referenced SOP instance UIDs
*/
export default async function loadRTStruct(extensionManager, rtStructDisplaySet, headers) {
const dataSource = extensionManager.getActiveDataSource()[0];
const { bulkDataURI } = dataSource.getConfig?.() || {};
const { dicomLoaderService } = utilityModule.exports;
// Set here is loading is asynchronous.
// If this function throws its set back to false.
rtStructDisplaySet.isLoaded = true;
let instance = rtStructDisplaySet.instance;
if (!bulkDataURI || !bulkDataURI.enabled) {
const segArrayBuffer = await dicomLoaderService.findDicomDataPromise(
rtStructDisplaySet,
null,
headers
);
const dicomData = DicomMessage.readFile(segArrayBuffer);
const rtStructDataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
rtStructDataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
instance = rtStructDataset;
instance = await getRTStructInstance({ extensionManager, rtStructDisplaySet, headers });
} else {
await checkAndLoadContourData(instance, dataSource);
instance = await checkAndLoadContourData({
instance,
dataSource,
extensionManager,
rtStructDisplaySet,
headers,
});
}
const { StructureSetROISequence, ROIContourSequence, RTROIObservationsSequence } = instance;
@@ -132,21 +233,16 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet,
for (let i = 0; i < ROIContourSequence.length; i++) {
const ROIContour = ROIContourSequence[i];
const { ContourSequence } = ROIContour;
if (!ContourSequence) {
continue;
}
const isSupported = false;
const ContourSequenceArray = _toArray(ContourSequence);
const contourPoints = [];
for (let c = 0; c < ContourSequenceArray.length; c++) {
for (const ContourSequenceItem of ContourSequenceArray) {
const { ContourData, NumberOfContourPoints, ContourGeometricType, ContourImageSequence } =
ContourSequenceArray[c];
let isSupported = false;
ContourSequenceItem;
const points = [];
for (let p = 0; p < NumberOfContourPoints * 3; p += 3) {
@@ -157,22 +253,18 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet,
});
}
switch (ContourGeometricType) {
case 'CLOSED_PLANAR':
case 'OPEN_PLANAR':
case 'POINT':
isSupported = true;
break;
default:
continue;
}
const supportedContourTypesMap = new Map([
['CLOSED_PLANAR', false],
['OPEN_NONPLANAR', false],
['OPEN_PLANAR', false],
['POINT', true],
]);
contourPoints.push({
numberOfPoints: NumberOfContourPoints,
points,
type: ContourGeometricType,
isSupported,
isSupported: supportedContourTypesMap.get(ContourGeometricType) ?? false,
});
if (ContourImageSequence?.ReferencedSOPInstanceUID) {
@@ -187,20 +279,30 @@ export default async function loadRTStruct(extensionManager, rtStructDisplaySet,
StructureSetROISequence,
RTROIObservationsSequence,
ROIContour,
contourPoints,
isSupported
contourPoints
);
}
return structureSet;
}
/**
* Sets metadata for ROI contour data and adds it to the structure set.
* Extracts ROI information from StructureSetROISequence and RTROIObservationsSequence,
* then creates a complete ROI contour data object with visualization properties.
*
* @function _setROIContourMetadata
* @param {Object} structureSet - The structure set object to add ROI contour to
* @param {Array} StructureSetROISequence - Array of structure set ROI definitions
* @param {Array} RTROIObservationsSequence - Array of RT ROI observations
* @param {Object} ROIContour - ROI contour object containing contour data
* @param {Array} contourPoints - Array of processed contour points
*/
function _setROIContourMetadata(
structureSet,
StructureSetROISequence,
RTROIObservationsSequence,
ROIContour,
contourPoints,
isSupported
contourPoints
) {
const StructureSetROI = StructureSetROISequence.find(
structureSetROI => structureSetROI.ROINumber === ROIContour.ReferencedROINumber
@@ -211,9 +313,9 @@ function _setROIContourMetadata(
ROIName: StructureSetROI.ROIName,
ROIGenerationAlgorithm: StructureSetROI.ROIGenerationAlgorithm,
ROIDescription: StructureSetROI.ROIDescription,
isSupported,
contourPoints,
visible: true,
colorArray: [],
};
_setROIContourDataColor(ROIContour, ROIContourData);
@@ -230,6 +332,14 @@ function _setROIContourMetadata(
structureSet.ROIContours.push(ROIContourData);
}
/**
* Sets the display color for ROI contour data.
* Uses ROIDisplayColor if available, otherwise converts RecommendedDisplayCIELabValue to RGB.
*
* @function _setROIContourDataColor
* @param {Object} ROIContour - ROI contour object containing color information
* @param {Object} ROIContourData - ROI contour data object to set color on
*/
function _setROIContourDataColor(ROIContour, ROIContourData) {
let { ROIDisplayColor, RecommendedDisplayCIELabValue } = ROIContour;
@@ -243,6 +353,15 @@ function _setROIContourDataColor(ROIContour, ROIContourData) {
}
}
/**
* Sets RT ROI observations metadata for ROI contour data.
* Finds matching RTROIObservations by ROINumber and adds observation details to contour data.
*
* @function _setROIContourRTROIObservations
* @param {Object} ROIContourData - ROI contour data object to add observations to
* @param {Array} RTROIObservationsSequence - Array of RT ROI observations
* @param {number} ROINumber - ROI number to match observations
*/
function _setROIContourRTROIObservations(ROIContourData, RTROIObservationsSequence, ROINumber) {
const RTROIObservations = RTROIObservationsSequence.find(
RTROIObservations => RTROIObservations.ReferencedROINumber === ROINumber
@@ -262,6 +381,14 @@ function _setROIContourRTROIObservations(ROIContourData, RTROIObservationsSequen
}
}
/**
* Converts a single object or array to an array.
* Utility function to ensure consistent array handling for DICOM sequences.
*
* @function _toArray
* @param {*} objOrArray - Object or array to convert
* @returns {Array} Array containing the input (if already array) or wrapped in array
*/
function _toArray(objOrArray) {
return Array.isArray(objOrArray) ? objOrArray : [objOrArray];
}
@@ -103,7 +103,7 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
const { unsubscribe } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
evt => {
if (evt.rtDisplaySet.displaySetInstanceUID === rtDisplaySet.displaySetInstanceUID) {
if (evt.rtDisplaySet?.displaySetInstanceUID === rtDisplaySet.displaySetInstanceUID) {
setRtIsLoading(false);
}
File diff suppressed because it is too large. Load diff
+15 -16
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-seg",
"version": "3.12.0-beta.17",
"version": "3.12.2",
"description": "DICOM SEG read workflow",
"author": "OHIF",
"license": "MIT",
@@ -33,22 +33,21 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.12.0-beta.17",
"@ohif/extension-cornerstone": "3.12.0-beta.17",
"@ohif/extension-default": "3.12.0-beta.17",
"@ohif/i18n": "3.12.0-beta.17",
"prop-types": "^15.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^12.2.2",
"react-router": "^6.23.1",
"react-router-dom": "^6.23.1"
"@ohif/core": "3.12.2",
"@ohif/extension-cornerstone": "3.12.2",
"@ohif/extension-default": "3.12.2",
"@ohif/i18n": "3.12.2",
"prop-types": "15.8.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-i18next": "12.3.1",
"react-router": "6.30.3",
"react-router-dom": "6.30.3"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.32.12",
"@cornerstonejs/core": "^3.32.12",
"@kitware/vtk.js": "32.12.0",
"react-color": "^2.19.3"
"@babel/runtime": "7.28.2",
"@cornerstonejs/adapters": "4.15.29",
"@cornerstonejs/core": "4.15.29",
"@kitware/vtk.js": "34.15.1"
}
}
@@ -1,15 +1,13 @@
import dcmjs from 'dcmjs';
import { classes, Types } from '@ohif/core';
import { classes, Types, utils } from '@ohif/core';
import { cache, metaData } from '@cornerstonejs/core';
import { segmentation as cornerstoneToolsSegmentation } from '@cornerstonejs/tools';
import { adaptersRT, helpers, adaptersSEG } from '@cornerstonejs/adapters';
import { createReportDialogPrompt } from '@ohif/extension-default';
import { createReportDialogPrompt, useUIStateStore } from '@ohif/extension-default';
import { DicomMetadataStore } from '@ohif/core';
import PROMPT_RESPONSES from '../../default/src/utils/_shared/PROMPT_RESPONSES';
const { datasetToBlob } = dcmjs.data;
const getTargetViewport = ({ viewportId, viewportGridService }) => {
const { viewports, activeViewportId } = viewportGridService.getState();
const targetViewportId = viewportId || activeViewportId;
@@ -27,7 +25,7 @@ const {
const {
Cornerstone3D: {
RTSS: { generateRTSSFromSegmentations },
RTSS: { generateRTSSFromRepresentation },
},
} = adaptersRT;
@@ -37,7 +35,7 @@ const commandsModule = ({
servicesManager,
extensionManager,
}: Types.Extensions.ExtensionParams): Types.Extensions.CommandsModule => {
const { segmentationService, displaySetService, viewportGridService, toolGroupService } =
const { segmentationService, displaySetService, viewportGridService } =
servicesManager.services as AppTypes.Services;
const actions = {
@@ -92,6 +90,7 @@ const commandsModule = ({
*/
generateSegmentation: ({ segmentationId, options = {} }) => {
const segmentation = cornerstoneToolsSegmentation.state.getSegmentation(segmentationId);
const predecessorImageId = options.predecessorImageId ?? segmentation.predecessorImageId;
const { imageIds } = segmentation.representationData.Labelmap;
@@ -173,12 +172,10 @@ const commandsModule = ({
labelmap3D.metadata[segmentIndex] = segmentMetadata;
});
const generatedSegmentation = generateSegmentation(
referencedImages,
labelmap3D,
metaData,
options
);
const generatedSegmentation = generateSegmentation(referencedImages, labelmap3D, metaData, {
predecessorImageId,
...options,
});
return generatedSegmentation;
},
@@ -212,24 +209,28 @@ const commandsModule = ({
* @returns {Object|void} Returns the naturalized report if successfully stored,
* otherwise throws an error.
*/
storeSegmentation: async ({ segmentationId, dataSource }) => {
storeSegmentation: async ({ segmentationId, dataSource, modality = 'SEG' }) => {
const segmentation = segmentationService.getSegmentation(segmentationId);
if (!segmentation) {
throw new Error('No segmentation found');
}
const { label } = segmentation;
const { label, predecessorImageId } = segmentation;
const defaultDataSource = dataSource ?? extensionManager.getActiveDataSource()[0];
const {
value: reportName,
dataSourceName: selectedDataSource,
series,
priorSeriesNumber,
action,
} = await createReportDialogPrompt({
servicesManager,
extensionManager,
predecessorImageId,
title: 'Store Segmentation',
modality,
});
if (action === PROMPT_RESPONSES.CREATE_REPORT) {
@@ -238,12 +239,18 @@ const commandsModule = ({
? extensionManager.getDataSources(selectedDataSource)[0]
: defaultDataSource;
const generatedData = actions.generateSegmentation({
const args = {
segmentationId,
options: {
SeriesDescription: reportName || label || 'Research Derived Series',
SeriesDescription: series ? undefined : reportName || label || 'Contour Series',
SeriesNumber: series ? undefined : 1 + priorSeriesNumber,
predecessorImageId: series,
},
});
};
const generatedDataAsync =
(modality === 'SEG' && actions.generateSegmentation(args)) ||
(modality === 'RTSTRUCT' && actions.generateContour(args));
const generatedData = await generatedDataAsync;
if (!generatedData || !generatedData.dataset) {
throw new Error('Error during segmentation generation');
@@ -270,14 +277,9 @@ const commandsModule = ({
}
}
},
/**
* Converts segmentations into RTSS for download.
* This sample function retrieves all segentations and passes to
* cornerstone tool adapter to convert to DICOM RTSS format. It then
* converts dataset to downloadable blob.
*
*/
downloadRTSS: async ({ segmentationId }) => {
generateContour: async args => {
const { segmentationId, options } = args;
const segmentations = segmentationService.getSegmentation(segmentationId);
// inject colors to the segmentIndex
@@ -287,45 +289,54 @@ const commandsModule = ({
segment.color = segmentationService.getSegmentColor(
firstRepresentation.viewportId,
segmentationId,
segmentIndex
Number(segmentIndex)
);
});
const predecessorImageId = options?.predecessorImageId ?? segmentations.predecessorImageId;
const dataset = await generateRTSSFromRepresentation(segmentations, {
predecessorImageId,
...options,
});
return { dataset };
},
const RTSS = await generateRTSSFromSegmentations(
segmentations,
classes.MetadataProvider,
DicomMetadataStore
);
/**
* Downloads an RTSS instance from a segmentation or contour
* representation.
*/
downloadRTSS: async args => {
const { dataset } = await actions.generateContour(args);
const { InstanceNumber: instanceNumber = 1, SeriesInstanceUID: seriesUID } = dataset;
try {
const reportBlob = datasetToBlob(RTSS);
//Create a URL for the binary.
const objectUrl = URL.createObjectURL(reportBlob);
window.location.assign(objectUrl);
const filename = `rtss-${seriesUID}-${instanceNumber}.dcm`;
downloadDICOMData(dataset, filename);
} catch (e) {
console.warn(e);
}
},
toggleActiveSegmentationUtility: ({ itemId: buttonId }) => {
const { uiState, setUIState } = useUIStateStore.getState();
const isButtonActive = uiState['activeSegmentationUtility'] === buttonId;
console.log('toggleActiveSegmentationUtility', isButtonActive, buttonId);
// if the button is active, clear the active segmentation utility
if (isButtonActive) {
setUIState('activeSegmentationUtility', null);
} else {
setUIState('activeSegmentationUtility', buttonId);
}
},
};
const definitions = {
loadSegmentationsForViewport: {
commandFn: actions.loadSegmentationsForViewport,
},
generateSegmentation: {
commandFn: actions.generateSegmentation,
},
downloadSegmentation: {
commandFn: actions.downloadSegmentation,
},
storeSegmentation: {
commandFn: actions.storeSegmentation,
},
downloadRTSS: {
commandFn: actions.downloadRTSS,
},
loadSegmentationsForViewport: actions.loadSegmentationsForViewport,
generateSegmentation: actions.generateSegmentation,
downloadSegmentation: actions.downloadSegmentation,
storeSegmentation: actions.storeSegmentation,
downloadRTSS: actions.downloadRTSS,
toggleActiveSegmentationUtility: actions.toggleActiveSegmentationUtility,
};
return {
@@ -0,0 +1,246 @@
import React, { useCallback, useEffect, useState } from 'react';
import { useRunCommand, useSystem } from '@ohif/core';
import { useActiveViewportSegmentationRepresentations } from '@ohif/extension-cornerstone';
import {
Button,
cn,
Input,
Label,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
Separator,
Switch,
Tabs,
TabsList,
TabsTrigger,
} from '@ohif/ui-next';
import { Icons } from '@ohif/ui-next';
import { contourSegmentation } from '@cornerstonejs/tools/utilities';
import { useTranslation } from 'react-i18next';
import { Segment } from '@cornerstonejs/tools/types';
const { LogicalOperation } = contourSegmentation;
const options = [
{
value: 'merge',
logicalOperation: LogicalOperation.Union,
label: 'Merge',
icon: 'actions-combine-merge',
helperIcon: 'helper-combine-merge',
},
{
value: 'intersect',
logicalOperation: LogicalOperation.Intersect,
label: 'Intersect',
icon: 'actions-combine-intersect',
helperIcon: 'helper-combine-intersect',
},
{
value: 'subtract',
logicalOperation: LogicalOperation.Subtract,
label: 'Subtract',
icon: 'actions-combine-subtract',
helperIcon: 'helper-combine-subtract',
},
];
// Shared component for segment selection
function SegmentSelector({
label,
value,
onValueChange,
segments,
placeholder = 'Select a segment',
}: {
label: string;
value: string;
onValueChange: (value: string) => void;
segments: Segment[];
placeholder?: string;
}) {
const { t } = useTranslation('SegmentationPanel');
return (
<div className="flex justify-between gap-6">
<div>{label}</div>
<Select
key={`select-segment-${label}`}
onValueChange={onValueChange}
value={value}
>
<SelectTrigger className="overflow-hidden">
<SelectValue placeholder={t(placeholder)} />
</SelectTrigger>
<SelectContent>
{segments.map(segment => (
<SelectItem
key={segment.segmentIndex}
value={segment.segmentIndex.toString()}
>
{segment.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
}
function LogicalContourOperationOptions() {
const { servicesManager } = useSystem();
const { segmentationService } = servicesManager.services;
const { t } = useTranslation('SegmentationPanel');
const { segmentationsWithRepresentations } = useActiveViewportSegmentationRepresentations();
const activeRepresentation = segmentationsWithRepresentations?.find(
({ representation }) => representation?.active
);
const segments = activeRepresentation
? Object.values(activeRepresentation.segmentation.segments)
: [];
// Calculate the next available segment index
const nextSegmentIndex = activeRepresentation
? segmentationService.getNextAvailableSegmentIndex(
activeRepresentation.segmentation.segmentationId
)
: 1;
const activeSegment = segments.find(segment => segment.active);
const activeSegmentIndex = activeSegment?.segmentIndex || 0;
const [operation, setOperation] = useState(options[0]);
const [segmentA, setSegmentA] = useState<string>(activeSegmentIndex?.toString() || '');
const [segmentB, setSegmentB] = useState<string>('');
const [createNewSegment, setCreateNewSegment] = useState<boolean>(false);
const [newSegmentName, setNewSegmentName] = useState<string>('');
useEffect(() => {
setSegmentA(activeSegmentIndex?.toString() || null);
}, [activeSegmentIndex]);
useEffect(() => {
setNewSegmentName(`Segment ${nextSegmentIndex}`);
}, [nextSegmentIndex]);
const runCommand = useRunCommand();
const applyLogicalContourOperation = useCallback(() => {
let resultSegmentIndex = segmentA;
if (createNewSegment) {
resultSegmentIndex = nextSegmentIndex.toString();
runCommand('addSegment', {
segmentationId: activeRepresentation.segmentation.segmentationId,
config: {
label: newSegmentName,
segmentIndex: nextSegmentIndex,
},
});
}
runCommand('applyLogicalContourOperation', {
segmentAInfo: {
segmentationId: activeRepresentation.segmentation.segmentationId,
segmentIndex: parseInt(segmentA),
},
segmentBInfo: {
segmentationId: activeRepresentation.segmentation.segmentationId,
segmentIndex: parseInt(segmentB),
},
resultSegmentInfo: {
segmentationId: activeRepresentation.segmentation.segmentationId,
segmentIndex: parseInt(resultSegmentIndex),
},
logicalOperation: operation.logicalOperation,
});
}, [
activeRepresentation?.segmentation?.segmentationId,
createNewSegment,
newSegmentName,
nextSegmentIndex,
operation.logicalOperation,
runCommand,
segmentA,
segmentB,
]);
return (
<div className="flex w-[245px] flex-col gap-4">
<div className="flex items-start justify-between">
<div className="flex w-auto flex-col items-center gap-2 text-base font-normal leading-none">
<Tabs value={operation.value}>
<TabsList className="inline-flex space-x-1">
{options.map(option => {
const { value, icon } = option;
return (
<TabsTrigger
value={value}
key={`logical-contour-operation-${value}`}
onClick={() => setOperation(option)}
>
<Icons.ByName name={icon}></Icons.ByName>
</TabsTrigger>
);
})}
</TabsList>
</Tabs>
<div>{t(operation.label)}</div>
</div>
<div className="bg-primary-dark flex h-[62px] w-[88px] items-center justify-center rounded-lg">
<Icons.ByName name={operation.helperIcon}></Icons.ByName>
</div>
</div>
<SegmentSelector
label="A"
value={segmentA}
onValueChange={setSegmentA}
segments={segments}
/>
<SegmentSelector
label="B"
value={segmentB}
onValueChange={setSegmentB}
segments={segments}
/>
<div className="flex justify-end pl-[34px]">
<Button
className="border-primary/60 grow border"
variant="ghost"
onClick={() => {
applyLogicalContourOperation();
}}
>
{t(operation.label)}
</Button>
</div>
<Separator className="bg-input mt-2 h-[1px]" />
<div className="flex flex-col gap-2">
<div className="flex items-center justify-start gap-2">
<Switch
id="logical-contour-operations-create-new-segment-switch"
onCheckedChange={setCreateNewSegment}
></Switch>
<Label htmlFor="logical-contour-operations-create-new-segment-switch">
{t('Create a new segment')}
</Label>
</div>
<div className="pl-9">
<Input
className={cn(createNewSegment ? 'visible' : 'hidden')}
disabled={!createNewSegment}
id="logical-contour-operations-create-new-segment-input"
type="text"
placeholder={t('New segment name')}
value={newSegmentName}
onChange={e => setNewSegmentName(e.target.value)}
/>
</div>
</div>
</div>
);
}
export default LogicalContourOperationOptions;
@@ -0,0 +1,73 @@
import React, { useState } from 'react';
import { Button, Input, Label, Separator } from '@ohif/ui-next';
import { useRunCommand } from '@ohif/core';
import { useTranslation } from 'react-i18next';
function SimplifyContourOptions() {
const [areaThreshold, setAreaThreshold] = useState(10);
const runCommand = useRunCommand();
const { t } = useTranslation('SegmentationPanel');
return (
<div className="flex w-auto w-[252px] flex-col gap-[8px] text-base font-normal leading-none">
<div className="flex w-auto flex-col gap-[10px] text-base font-normal leading-none">
<div>{t('Fill contour holes')}</div>
<Button
className="border-primary/60 border"
variant="ghost"
onClick={() => {
runCommand('removeContourHoles');
}}
>
{t('Fill Holes')}
</Button>
<Separator className="bg-input mt-[20px] h-[1px]" />
</div>
<div className="flex w-auto flex-col gap-[10px] text-base font-normal leading-none">
<div>{t('Remove Small Contours')}</div>
<div className="flex items-center gap-2 self-end">
<Label
htmlFor="simplify-contour-options"
className="text-muted-foreground"
>
{t('Area Threshold')}
</Label>
<Input
id="simplify-contour-options"
className="w-20"
type="number"
value={areaThreshold}
onChange={e => setAreaThreshold(Number(e.target.value))}
/>
</div>
<Button
className="border-primary/60 border"
variant="ghost"
onClick={() => {
runCommand('removeSmallContours', {
areaThreshold,
});
}}
>
{t('Remove Small Contours')}
</Button>
<Separator className="bg-input mt-[20px] h-[1px]" />
</div>
<div className="flex w-auto flex-col gap-[10px] text-base font-normal leading-none">
<div>{t('Create New Segment from Holes')}</div>
<Button
className="border-primary/60 border"
variant="ghost"
onClick={() => {
runCommand('convertContourHoles');
}}
>
{t('Create New Segment')}
</Button>
</div>
</div>
);
}
export default SimplifyContourOptions;
@@ -0,0 +1,41 @@
import React from 'react';
import { Button, Separator } from '@ohif/ui-next';
import { useRunCommand } from '@ohif/core';
import { useTranslation } from 'react-i18next';
function SmoothContoursOptions() {
const runCommand = useRunCommand();
const { t } = useTranslation('SegmentationPanel');
return (
<div className="flex w-auto w-[245px] flex-col gap-[8px] text-base font-normal leading-none">
<div className="flex w-auto flex-col gap-[10px] text-base font-normal leading-none">
<div>{t('Smooth all edges')}</div>
<Button
className="border-primary/60 border"
variant="ghost"
onClick={() => {
runCommand('smoothContours');
}}
>
{t('Smooth Edges')}
</Button>
<Separator className="bg-input mt-[20px] h-[1px]" />
</div>
<div className="flex w-auto flex-col gap-[10px] text-base font-normal leading-none">
<div>{t('Remove extra points')}</div>
<Button
className="border-primary/60 border"
variant="ghost"
onClick={() => {
runCommand('decimateContours');
}}
>
{t('Remove Points')}
</Button>
</div>
</div>
);
}
export default SmoothContoursOptions;
@@ -16,19 +16,24 @@ function _getDisplaySetsFromSeries(
servicesManager: AppTypes.ServicesManager,
extensionManager
) {
const instance = instances[0];
utils.sortStudyInstances(instances);
// Choose the LAST instance in the list as the most recently created one.
const instance = instances[instances.length - 1];
const {
StudyInstanceUID,
SeriesInstanceUID,
SOPInstanceUID,
SeriesDescription,
SeriesDescription = '',
SeriesNumber,
SeriesDate,
StructureSetDate,
SOPClassUID,
wadoRoot,
wadoUri,
wadoUriRoot,
imageId: predecessorImageId,
} = instance;
const displaySet = {
@@ -38,7 +43,7 @@ function _getDisplaySetsFromSeries(
displaySetInstanceUID: utils.guid(),
SeriesDescription,
SeriesNumber,
SeriesDate,
SeriesDate: SeriesDate || StructureSetDate || '',
SOPInstanceUID,
SeriesInstanceUID,
StudyInstanceUID,
@@ -53,6 +58,7 @@ function _getDisplaySetsFromSeries(
segments: {},
sopClassUids,
instance,
predecessorImageId,
instances: [instance],
wadoRoot,
wadoUriRoot,
@@ -73,10 +79,16 @@ function _getDisplaySetsFromSeries(
displaySet.referencedImages = instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
const { displaySetService } = servicesManager.services;
const referencedDisplaySets = displaySetService.getDisplaySetsForSeries(
displaySet.referencedSeriesInstanceUID
const referencedDisplaySets = displaySetService.getDisplaySetsForReferences(
instance.ReferencedSeriesSequence
);
if (referencedDisplaySets?.length > 1) {
console.warn(
'Segmentation does not currently handle references to multiple series, defaulting to first series'
);
}
const referencedDisplaySet = referencedDisplaySets[0];
if (!referencedDisplaySet) {
@@ -1,6 +1,35 @@
import { utilities as cstUtils } from '@cornerstonejs/tools';
import i18n from '@ohif/i18n';
import { useUIStateStore } from '@ohif/extension-default';
import LogicalContourOperationsOptions from './components/LogicalContourOperationsOptions';
import SimplifyContourOptions from './components/SimplifyContourOptions';
import SmoothContoursOptions from './components/SmoothContoursOptions';
export function getToolbarModule({ servicesManager }: withAppTypes) {
const { segmentationService, toolbarService, toolGroupService } = servicesManager.services;
return [
{
name: 'cornerstone.SimplifyContourOptions',
defaultComponent: SimplifyContourOptions,
},
{
name: 'cornerstone.LogicalContourOperationsOptions',
defaultComponent: LogicalContourOperationsOptions,
},
{
name: 'cornerstone.SmoothContoursOptions',
defaultComponent: SmoothContoursOptions,
},
{
name: 'cornerstone.isActiveSegmentationUtility',
evaluate: ({ button }) => {
const { uiState } = useUIStateStore.getState();
return {
isActive: uiState[`activeSegmentationUtility`] === button.id,
};
},
},
{
name: 'evaluate.cornerstone.hasSegmentation',
evaluate: ({ viewportId }) => {
@@ -10,6 +39,30 @@ export function getToolbarModule({ servicesManager }: withAppTypes) {
};
},
},
{
name: 'evaluate.cornerstone.hasSegmentationOfType',
evaluate: ({ viewportId, segmentationRepresentationType }) => {
const segmentations = segmentationService.getSegmentationRepresentations(viewportId);
if (!segmentations?.length) {
return {
disabled: true,
disabledText: i18n.t('SegmentationPanel:No segmentations available'),
};
}
if (
!segmentations.some(segmentation =>
Boolean(segmentation.type === segmentationRepresentationType)
)
) {
return {
disabled: true,
disabledText: `No ${segmentationRepresentationType} segmentations available`,
};
}
},
},
{
name: 'evaluate.cornerstone.segmentation',
evaluate: ({ viewportId, button, toolNames, disabledText }) => {
@@ -21,7 +74,7 @@ export function getToolbarModule({ servicesManager }: withAppTypes) {
if (!segmentations?.length) {
return {
disabled: true,
disabledText: disabledText ?? 'No segmentations available',
disabledText: disabledText ?? i18n.t('SegmentationPanel:No segmentations available'),
};
}
@@ -29,7 +82,7 @@ export function getToolbarModule({ servicesManager }: withAppTypes) {
if (!Object.keys(activeSegmentation.segments).length) {
return {
disabled: true,
disabledText: 'Add segment to enable this tool',
disabledText: i18n.t('SegmentationPanel:Add segment to enable this tool'),
};
}
@@ -38,7 +91,7 @@ export function getToolbarModule({ servicesManager }: withAppTypes) {
if (!toolGroup) {
return {
disabled: true,
disabledText: disabledText ?? 'Not available on the current viewport',
disabledText: disabledText ?? i18n.t('SegmentationPanel:Not available on the current viewport'),
};
}
@@ -54,7 +107,7 @@ export function getToolbarModule({ servicesManager }: withAppTypes) {
if (!toolGroup.hasTool(toolName) && !toolNames) {
return {
disabled: true,
disabledText: disabledText ?? 'Not available on the current viewport',
disabledText: disabledText ?? i18n.t('SegmentationPanel:Not available on the current viewport'),
};
}
@@ -68,5 +121,23 @@ export function getToolbarModule({ servicesManager }: withAppTypes) {
};
},
},
{
name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius',
evaluate: ({ button, radiusOptionId }) => {
const toolGroupIds = toolGroupService.getToolGroupIds();
if (!toolGroupIds?.length) {
return;
}
for (const toolGroupId of toolGroupIds) {
const brushSize = cstUtils.segmentation.getBrushSizeForToolGroup(toolGroupId);
if (brushSize) {
const option = toolbarService.getOptionById(button, radiusOptionId);
option.value = brushSize;
}
}
},
},
];
}
@@ -60,15 +60,26 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
// In such cases, we attempt to handle this scenario gracefully by
// invoking a custom handler. Ideally, if a user tries to launch a series that isn't viewable,
// (eg.: we can prompt them with an explanation and provide a link to the full study).
// Additional guard: If no customization handler is registered for missing
// referenced display sets, skip SEG rendering to avoid a viewport crash.
if (!referencedDisplaySetInstanceUID) {
const missingReferenceDisplaySetHandler = customizationService.getCustomization(
'missingReferenceDisplaySetHandler'
);
const { handled } = missingReferenceDisplaySetHandler();
if (handled) {
if (typeof missingReferenceDisplaySetHandler === 'function') {
const { handled } = missingReferenceDisplaySetHandler();
if (handled) {
return;
}
} else {
console.log(
"No customization 'missingReferenceDisplaySetHandler' registered. Skipping SEG rendering."
);
return;
}
}
const referencedDisplaySet = displaySetService.getDisplaySetByUID(
referencedDisplaySetInstanceUID
);
@@ -134,7 +145,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
const { unsubscribe } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
evt => {
if (evt.segDisplaySet.displaySetInstanceUID === segDisplaySet.displaySetInstanceUID) {
if (evt.segDisplaySet?.displaySetInstanceUID === segDisplaySet?.displaySetInstanceUID) {
setSegIsLoading(false);
}
File diff suppressed because it is too large. Load diff
+15 -15
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-sr",
"version": "3.12.0-beta.17",
"version": "3.12.2",
"description": "OHIF extension for an SR Cornerstone Viewport",
"author": "OHIF",
"license": "MIT",
@@ -34,21 +34,21 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.12.0-beta.17",
"@ohif/extension-cornerstone": "3.12.0-beta.17",
"@ohif/extension-measurement-tracking": "3.12.0-beta.17",
"@ohif/ui": "3.12.0-beta.17",
"dcmjs": "0.43.1",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
"react": "^18.3.1"
"@ohif/core": "3.12.2",
"@ohif/extension-cornerstone": "3.12.2",
"@ohif/extension-measurement-tracking": "3.12.2",
"@ohif/ui": "3.12.2",
"dcmjs": "0.49.4",
"dicom-parser": "1.8.21",
"hammerjs": "2.0.8",
"prop-types": "15.8.1",
"react": "18.3.1"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.32.12",
"@cornerstonejs/core": "^3.32.12",
"@cornerstonejs/tools": "^3.32.12",
"classnames": "^2.3.2"
"@babel/runtime": "7.28.2",
"@cornerstonejs/adapters": "4.15.29",
"@cornerstonejs/core": "4.15.29",
"@cornerstonejs/tools": "4.15.29",
"classnames": "2.5.1"
}
}
@@ -1,12 +1,14 @@
import { metaData, utilities } from '@cornerstonejs/core';
import { metaData } from '@cornerstonejs/core';
import OHIF, { DicomMetadataStore } from '@ohif/core';
import OHIF, { DicomMetadataStore, utils } from '@ohif/core';
import dcmjs from 'dcmjs';
import { adaptersSR } from '@cornerstonejs/adapters';
import getFilteredCornerstoneToolState from './utils/getFilteredCornerstoneToolState';
import hydrateStructuredReport from './utils/hydrateStructuredReport';
const { downloadBlob } = utils;
const { MeasurementReport } = adaptersSR.Cornerstone3D;
const { log } = OHIF;
@@ -42,8 +44,6 @@ const _generateReport = (measurementData, additionalFindingTypes, options: Optio
dataset.SpecificCharacterSet = 'ISO_IR 192';
}
dataset.InstanceNumber = options.InstanceNumber ?? 1;
return dataset;
};
@@ -85,8 +85,7 @@ const commandsModule = (props: withAppTypes) => {
const reportBlob = dcmjs.data.datasetToBlob(srDataset);
//Create a URL for the binary.
const objectUrl = URL.createObjectURL(reportBlob);
window.location.assign(objectUrl);
downloadBlob(reportBlob, { filename: 'dicom-sr.dcm' });
},
/**
@@ -22,7 +22,7 @@ const {
CodeScheme: Cornerstone3DCodeScheme,
} = adaptersSR.Cornerstone3D;
type InstanceMetadata = Types.InstanceMetadata;
type InstanceMetadata = OhifTypes.InstanceMetadata;
/**
* TODO
@@ -69,9 +69,10 @@ function addInstances(instances: InstanceMetadata[], _displaySetService: Display
* DICOM SR SOP Class Handler
* For all referenced images in the TID 1500/300 sections, add an image to the
* display.
* @param instances is a set of instances all from the same series
* @param servicesManager is the services that can be used for creating
* @returns The list of display sets created for the given instances object
* @param {InstanceMetadata[]} instances - A set of instances all from the same series
* @param {AppTypes.ServicesManager} servicesManager - The services that can be used for creating
* @param {AppTypes.ExtensionManager} extensionManager - The extension manager
* @returns {Types.DisplaySet[]} The list of display sets created for the given instances object
*/
function _getDisplaySetsFromSeries(
instances,
@@ -99,6 +100,7 @@ function _getDisplaySetsFromSeries(
SeriesTime,
ConceptNameCodeSequence,
SOPClassUID,
imageId: predecessorImageId,
} = instance;
validateSameStudyUID(instance.StudyInstanceUID, instances);
@@ -127,6 +129,7 @@ function _getDisplaySetsFromSeries(
isImagingMeasurementReport,
sopClassUids,
instance,
predecessorImageId,
addInstances,
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('SR')}`,
};
@@ -143,7 +146,7 @@ function _getDisplaySetsFromSeries(
* @param extensionManager - The extension manager containing data sources.
*/
async function _load(
srDisplaySet: Types.DisplaySet,
srDisplaySet: OhifTypes.DisplaySet,
servicesManager: AppTypes.ServicesManager,
extensionManager: AppTypes.ExtensionManager
) {
@@ -183,6 +186,10 @@ async function _load(
srDisplaySet.referencedImages = [];
srDisplaySet.measurements = [];
}
const { predecessorImageId } = srDisplaySet;
for (const measurement of srDisplaySet.measurements) {
measurement.predecessorImageId = predecessorImageId;
}
const mappings = measurementService.getSourceMappings(
CORNERSTONE_3D_TOOLS_SOURCE_NAME,
@@ -227,17 +234,9 @@ function _measurementBelongsToDisplaySet({ measurement, displaySet }) {
);
}
/**
* Checks if measurements can be added to a display set.
*
* @param srDisplaySet - The source display set containing measurements.
* @param newDisplaySet - The new display set to check if measurements can be added.
* @param dataSource - The data source used to retrieve image IDs.
* @param servicesManager - The services manager.
*/
function _checkIfCanAddMeasurementsToDisplaySet(
srDisplaySet,
newDisplaySet,
srDisplaySet: OhifTypes.DisplaySet,
newDisplaySet: OhifTypes.DisplaySet,
dataSource,
servicesManager: AppTypes.ServicesManager
) {
@@ -289,9 +288,7 @@ function _checkIfCanAddMeasurementsToDisplaySet(
is3DMeasurement &&
_measurementBelongsToDisplaySet({ measurement, displaySet: newDisplaySet })
) {
_measurementBelongsToDisplaySet({ measurement, displaySet: newDisplaySet })
addSRAnnotation(measurement, null, null);
addSRAnnotation({ measurement, displaySet: newDisplaySet });
measurement.loaded = true;
measurement.displaySetInstanceUID = newDisplaySet.displaySetInstanceUID;
unloadedMeasurements.splice(j, 1);
@@ -312,15 +309,12 @@ function _checkIfCanAddMeasurementsToDisplaySet(
imageId &&
_measurementReferencesSOPInstanceUID(measurement, ReferencedSOPInstanceUID, frame)
) {
addSRAnnotation(measurement, imageId, frame);
// Update measurement properties
addSRAnnotation({ measurement, imageId, frameNumber: frame, displaySet: newDisplaySet });
measurement.loaded = true;
measurement.imageId = imageId;
measurement.displaySetInstanceUID = newDisplaySet.displaySetInstanceUID;
measurement.ReferencedSOPInstanceUID = ReferencedSOPInstanceUID;
measurement.frameNumber = frame;
unloadedMeasurements.splice(j, 1);
}
}
@@ -328,10 +322,10 @@ function _checkIfCanAddMeasurementsToDisplaySet(
/**
* Checks if a measurement references a specific SOP Instance UID.
* @param measurement - The measurement object.
* @param SOPInstanceUID - The SOP Instance UID to check against.
* @param frameNumber - The frame number to check against (optional).
* @returns True if the measurement references the specified SOP Instance UID, false otherwise.
* @param {any} measurement - The measurement object.
* @param {string} sopInstanceUID - The SOP Instance UID to check against.
* @param {number} frameNumber - The frame number to check against (optional).
* @returns {boolean} True if the measurement references the specified SOP Instance UID, false otherwise.
*/
function _measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frameNumber) {
const { coords } = measurement;
@@ -363,10 +357,8 @@ function _measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frame
/**
* Retrieves the SOP class handler module.
*
* @param {Object} options - The options for retrieving the SOP class handler module.
* @param {Object} options.servicesManager - The services manager.
* @param {Object} options.extensionManager - The extension manager.
* @returns {Array} An array containing the SOP class handler module.
* @param {OhifTypes.Extensions.ExtensionParams} params - The extension parameters.
* @returns {Array} An array containing the SOP class handler modules.
*/
function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) {
const { servicesManager, extensionManager } = params;
@@ -390,8 +382,8 @@ function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams)
/**
* Retrieves the measurements from the ImagingMeasurementReportContentSequence.
*
* @param {Array} ImagingMeasurementReportContentSequence - The ImagingMeasurementReportContentSequence array.
* @returns {Array} - The array of measurements.
* @param {any[]} imagingMeasurementReportContentSequence - The ImagingMeasurementReportContentSequence array.
* @returns {any[]} The array of measurements.
*/
function _getMeasurements(ImagingMeasurementReportContentSequence) {
const ImagingMeasurements = ImagingMeasurementReportContentSequence.find(
@@ -429,8 +421,8 @@ function _getMeasurements(ImagingMeasurementReportContentSequence) {
/**
* Retrieves merged content sequences by tracking unique identifiers.
*
* @param {Array} MeasurementGroups - The measurement groups.
* @returns {Object} - The merged content sequences by tracking unique identifiers.
* @param {any[]} measurementGroups - The measurement groups.
* @returns {Object} The merged content sequences by tracking unique identifiers.
*/
function _getMergedContentSequencesByTrackingUniqueIdentifiers(MeasurementGroups) {
const mergedContentSequencesByTrackingUniqueIdentifiers = {};
@@ -477,8 +469,8 @@ function _getMergedContentSequencesByTrackingUniqueIdentifiers(MeasurementGroups
* it calls the _processTID1410Measurement function.
* Otherwise, it calls the _processNonGeometricallyDefinedMeasurement function.
*
* @param {Array<Object>} mergedContentSequence - The merged content sequence to process.
* @returns {any} - The processed measurement result.
* @param {any[]} mergedContentSequence - The merged content sequence to process.
* @returns {any} The processed measurement result.
*/
function _processMeasurement(mergedContentSequence) {
if (mergedContentSequence.some(group => isScoordOr3d(group) && !isTextPosition(group))) {
@@ -493,8 +485,8 @@ function _processMeasurement(mergedContentSequence) {
* TID 1410 style measurements have a SCOORD or SCOORD3D at the top level,
* and non-geometric representations where each NUM has "INFERRED FROM" SCOORD/SCOORD3D.
*
* @param mergedContentSequence - The merged content sequence containing the measurements.
* @returns The measurement object containing the loaded status, labels, coordinates, tracking unique identifier, and tracking identifier.
* @param {any[]} mergedContentSequence - The merged content sequence containing the measurements.
* @returns {any} The measurement object containing the loaded status, labels, coordinates, tracking unique identifier, and tracking identifier.
*/
function _processTID1410Measurement(mergedContentSequence) {
// Need to deal with TID 1410 style measurements, which will have a SCOORD or SCOORD3D at the top level,
@@ -567,8 +559,8 @@ function _processTID1410Measurement(mergedContentSequence) {
/**
* Processes the non-geometrically defined measurement from the merged content sequence.
*
* @param mergedContentSequence The merged content sequence containing the measurement data.
* @returns The processed measurement object.
* @param {any[]} mergedContentSequence The merged content sequence containing the measurement data.
* @returns {any} The processed measurement object.
*/
function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
const NUMContentItems = mergedContentSequence.filter(group => group.ValueType === 'NUM');
@@ -645,15 +637,20 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
NUMContentItems.forEach(item => {
const { ConceptNameCodeSequence, ContentSequence, MeasuredValueSequence } = item;
const { ValueType } = ContentSequence;
if (!ValueType === 'SCOORD') {
console.warn(`Graphic ${ValueType} not currently supported, skipping annotation.`);
return;
}
// Handle spatial reference ONLY if ContentSequence exists
if (ContentSequence) {
const { ValueType } = ContentSequence;
const coords = _getCoordsFromSCOORDOrSCOORD3D(ContentSequence);
if (coords) {
measurement.coords.push(coords);
if (ValueType !== 'SCOORD' && ValueType !== 'SCOORD3D') {
console.warn(`Graphic ${ValueType} not currently supported, skipping annotation.`);
return;
}
const coords = _getCoordsFromSCOORDOrSCOORD3D(ContentSequence);
if (coords) {
measurement.coords.push(coords);
}
}
if (MeasuredValueSequence) {
@@ -668,8 +665,8 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
/**
* Extracts coordinates from a graphic item of type SCOORD or SCOORD3D.
* @param {object} graphicItem - The graphic item containing the coordinates.
* @returns {object} - The extracted coordinates.
* @param {any} graphicItem - The graphic item containing the coordinates.
* @returns {any} The extracted coordinates.
*/
const _getCoordsFromSCOORDOrSCOORD3D = graphicItem => {
const { ValueType, GraphicType, GraphicData } = graphicItem;
@@ -683,9 +680,9 @@ const _getCoordsFromSCOORDOrSCOORD3D = graphicItem => {
/**
* Retrieves the label and value from the provided ConceptNameCodeSequence and MeasuredValueSequence.
* @param {Object} ConceptNameCodeSequence - The ConceptNameCodeSequence object.
* @param {Object} MeasuredValueSequence - The MeasuredValueSequence object.
* @returns {Object} - An object containing the label and value.
* @param {any} conceptNameCodeSequence - The ConceptNameCodeSequence object.
* @param {any} measuredValueSequence - The MeasuredValueSequence object.
* @returns {Object} An object containing the label and value.
* The label represents the CodeMeaning from the ConceptNameCodeSequence.
* The value represents the formatted NumericValue and CodeValue from the MeasuredValueSequence.
* Example: { label: 'Long Axis', value: '31.00 mm' }
@@ -704,8 +701,8 @@ function _getLabelFromMeasuredValueSequence(ConceptNameCodeSequence, MeasuredVal
/**
* Retrieves a list of referenced images from the Imaging Measurement Report Content Sequence.
*
* @param {Array} ImagingMeasurementReportContentSequence - The Imaging Measurement Report Content Sequence.
* @returns {Array} - The list of referenced images.
* @param {any[]} imagingMeasurementReportContentSequence - The Imaging Measurement Report Content Sequence.
* @returns {any[]} The list of referenced images.
*/
function _getReferencedImagesList(ImagingMeasurementReportContentSequence) {
const ImageLibrary = ImagingMeasurementReportContentSequence.find(
@@ -752,7 +749,7 @@ function _getReferencedImagesList(ImagingMeasurementReportContentSequence) {
* Otherwise, the sequence is wrapped in an array and returned.
*
* @param {any} sequence - The DICOM sequence to convert.
* @returns {any[]} - The converted array.
* @returns {any[]} The converted array.
*/
function _getSequenceAsArray(sequence) {
if (!sequence) {
+2 -25
View File
@@ -9,18 +9,13 @@ import {
LengthTool,
PlanarFreehandROITool,
RectangleROITool,
utilities as csToolsUtils,
} from '@cornerstonejs/tools';
import { Types, MeasurementService } from '@ohif/core';
import { Enums as CSExtensionEnums } from '@ohif/extension-cornerstone';
import { Types } from '@ohif/core';
import DICOMSRDisplayTool from './tools/DICOMSRDisplayTool';
import SCOORD3DPointTool from './tools/SCOORD3DPointTool';
import SRSCOOR3DProbeMapper from './utils/SRSCOOR3DProbeMapper';
import addToolInstance from './utils/addToolInstance';
import toolNames from './tools/toolNames';
const { CORNERSTONE_3D_TOOLS_SOURCE_NAME, CORNERSTONE_3D_TOOLS_SOURCE_VERSION } = CSExtensionEnums;
/**
* @param {object} configuration
*/
@@ -28,8 +23,6 @@ export default function init({
configuration = {},
servicesManager,
}: Types.Extensions.ExtensionParams): void {
const { measurementService, cornerstoneViewportService } = servicesManager.services;
addToolInstance(toolNames.DICOMSRDisplay, DICOMSRDisplayTool);
addToolInstance(toolNames.SRLength, LengthTool);
addToolInstance(toolNames.SRBidirectional, BidirectionalTool);
@@ -39,26 +32,10 @@ export default function init({
addToolInstance(toolNames.SRAngle, AngleTool);
addToolInstance(toolNames.SRPlanarFreehandROI, PlanarFreehandROITool);
addToolInstance(toolNames.SRRectangleROI, RectangleROITool);
addToolInstance(toolNames.SRSCOORD3DPoint, SCOORD3DPointTool);
// TODO - fix the SR display of Cobb Angle, as it joins the two lines
addToolInstance(toolNames.SRCobbAngle, CobbAngleTool);
const csTools3DVer1MeasurementSource = measurementService.getSource(
CORNERSTONE_3D_TOOLS_SOURCE_NAME,
CORNERSTONE_3D_TOOLS_SOURCE_VERSION
);
const { POINT } = measurementService.VALUE_TYPES;
measurementService.addMapping(
csTools3DVer1MeasurementSource,
'SRSCOORD3DPoint',
POINT,
SRSCOOR3DProbeMapper.toAnnotation,
SRSCOOR3DProbeMapper.toMeasurement.bind(null, { servicesManager })
);
// Modify annotation tools to use dashed lines on SR
const dashedLine = {
lineDash: '4,4',
@@ -1,203 +0,0 @@
import { type Types } from '@cornerstonejs/core';
import {
annotation,
drawing,
utilities,
Types as cs3DToolsTypes,
AnnotationDisplayTool,
} from '@cornerstonejs/tools';
import toolNames from './toolNames';
import { Annotation } from '@cornerstonejs/tools/dist/types/types';
export default class SCOORD3DPointTool extends AnnotationDisplayTool {
static toolName = toolNames.SRSCOORD3DPoint;
constructor(
toolProps = {},
defaultToolProps = {
configuration: {},
}
) {
super(toolProps, defaultToolProps);
}
_getTextBoxLinesFromLabels(labels) {
// TODO -> max 5 for now (label + shortAxis + longAxis), need a generic solution for this!
const labelLength = Math.min(labels.length, 5);
const lines = [];
return lines;
}
// This tool should not inherit from AnnotationTool and we should not need
// to add the following lines.
isPointNearTool = () => null;
getHandleNearImagePoint = () => null;
renderAnnotation = (enabledElement: Types.IEnabledElement, svgDrawingHelper: any): void => {
const { viewport } = enabledElement;
const { element } = viewport;
const annotations = annotation.state.getAnnotations(this.getToolName(), element);
// Todo: We don't need this anymore, filtering happens in triggerAnnotationRender
if (!annotations?.length) {
return;
}
// Filter toolData to only render the data for the active SR.
const filteredAnnotations = annotations;
if (!viewport._actors?.size) {
return;
}
const styleSpecifier: cs3DToolsTypes.AnnotationStyle.StyleSpecifier = {
toolGroupId: this.toolGroupId,
toolName: this.getToolName(),
viewportId: enabledElement.viewport.id,
};
for (let i = 0; i < filteredAnnotations.length; i++) {
const annotation = filteredAnnotations[i];
const annotationUID = annotation.annotationUID;
const { renderableData } = annotation.data;
const { POINT: points } = renderableData;
styleSpecifier.annotationUID = annotationUID;
const lineWidth = this.getStyle('lineWidth', styleSpecifier, annotation);
const lineDash = this.getStyle('lineDash', styleSpecifier, annotation);
const color = this.getStyle('color', styleSpecifier, annotation);
const options = {
color,
lineDash,
lineWidth,
};
const point = points[0][0];
// check if viewport can render it
const viewable = viewport.isReferenceViewable(
{ FrameOfReferenceUID: annotation.metadata.FrameOfReferenceUID, cameraFocalPoint: point },
{ asNearbyProjection: true }
);
if (!viewable) {
continue;
}
// render the point
const arrowPointCanvas = viewport.worldToCanvas(point);
// Todo: configure this
const arrowEndCanvas = [arrowPointCanvas[0] + 20, arrowPointCanvas[1] + 20];
const canvasCoordinates = [arrowPointCanvas, arrowEndCanvas];
drawing.drawArrow(
svgDrawingHelper,
annotationUID,
'1',
canvasCoordinates[1],
canvasCoordinates[0],
{
color: options.color,
width: options.lineWidth,
}
);
this.renderTextBox(
svgDrawingHelper,
viewport,
canvasCoordinates,
annotation,
styleSpecifier,
options
);
}
};
renderTextBox(
svgDrawingHelper,
viewport,
canvasCoordinates,
annotation,
styleSpecifier,
options = {}
) {
if (!canvasCoordinates || !annotation) {
return;
}
const { annotationUID, data = {} } = annotation;
const { labels } = data;
const textLines = [];
for (const label of labels) {
// make this generic
// fix this
if (label.label === '363698007') {
textLines.push(`Finding Site: ${label.value}`);
}
}
const { color } = options;
const adaptedCanvasCoordinates = canvasCoordinates;
// adapt coordinates if there is an adapter
const canvasTextBoxCoords = utilities.drawing.getTextBoxCoordsCanvas(adaptedCanvasCoordinates);
if (!annotation.data?.handles?.textBox?.worldPosition) {
annotation.data.handles.textBox.worldPosition = viewport.canvasToWorld(canvasTextBoxCoords);
}
const textBoxPosition = viewport.worldToCanvas(annotation.data.handles.textBox.worldPosition);
const textBoxUID = '1';
const textBoxOptions = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
const boundingBox = drawing.drawLinkedTextBox(
svgDrawingHelper,
annotationUID,
textBoxUID,
textLines,
textBoxPosition,
canvasCoordinates,
{},
{
...textBoxOptions,
color,
}
);
const { x: left, y: top, width, height } = boundingBox;
annotation.data.handles.textBox.worldBoundingBox = {
topLeft: viewport.canvasToWorld([left, top]),
topRight: viewport.canvasToWorld([left + width, top]),
bottomLeft: viewport.canvasToWorld([left, top + height]),
bottomRight: viewport.canvasToWorld([left + width, top + height]),
};
}
public getLinkedTextBoxStyle(
specifications: cs3DToolsTypes.AnnotationStyle.StyleSpecifier,
annotation?: Annotation
): Record<string, unknown> {
// Todo: this function can be used to set different styles for different toolMode
// for the textBox.
return {
visibility: this.getStyle('textBoxVisibility', specifications, annotation),
fontFamily: this.getStyle('textBoxFontFamily', specifications, annotation),
fontSize: this.getStyle('textBoxFontSize', specifications, annotation),
color: this.getStyle('textBoxColor', specifications, annotation),
shadow: this.getStyle('textBoxShadow', specifications, annotation),
background: this.getStyle('textBoxBackground', specifications, annotation),
lineWidth: this.getStyle('textBoxLinkLineWidth', specifications, annotation),
lineDash: this.getStyle('textBoxLinkLineDash', specifications, annotation),
};
}
}
@@ -9,7 +9,6 @@ const toolNames = {
SRCobbAngle: 'SRCobbAngle',
SRRectangleROI: 'SRRectangleROI',
SRPlanarFreehandROI: 'SRPlanarFreehandROI',
SRSCOORD3DPoint: 'SRSCOORD3DPoint',
};
export default toolNames;
@@ -1,71 +0,0 @@
const SRSCOOR3DProbe = {
toAnnotation: measurement => {},
/**
* Maps cornerstone annotation event data to measurement service format.
*
* @param {Object} cornerstone Cornerstone event data
* @return {Measurement} Measurement instance
*/
toMeasurement: ({ servicesManager, getValueTypeFromToolType }, csToolsEventDetail) => {
const { displaySetService } = servicesManager.services;
const { annotation } = csToolsEventDetail;
const { metadata, data, annotationUID } = annotation;
if (!metadata || !data) {
console.warn('Probe tool: Missing metadata or data');
return null;
}
const { toolName, FrameOfReferenceUID } = metadata;
const { points } = data.handles;
const displaySets = displaySetService
.getActiveDisplaySets()
.filter(ds => ds.FrameOfReferenceUID === FrameOfReferenceUID);
const displaySet = displaySets.filter(ds => ds.isReconstructable)[0] || displaySets[0];
const { StudyInstanceUID: referenceStudyUID, SeriesInstanceUID: referenceSeriesUID } =
displaySets[0] || {};
const displayText = getDisplayText(annotation);
return {
uid: annotationUID,
points,
metadata,
referenceStudyUID,
referenceSeriesUID,
displaySetInstanceUID: displaySet?.displaySetInstanceUID,
toolName: metadata.toolName,
label: data.label,
displayText: displayText,
data: data.cachedStats,
type: getValueTypeFromToolType?.(toolName) ?? null,
};
},
};
function getDisplayText(annotation) {
const { data } = annotation;
if (!data) {
return [''];
}
const { labels } = data;
const displayText = [];
for (const label of labels) {
// make this generic
if (label.label === '33636980076') {
displayText.push(`Finding Site: ${label.value}`);
}
}
return {
primary: displayText,
secondary: [],
};
}
export default SRSCOOR3DProbe;
@@ -7,8 +7,53 @@ import toolNames from '../tools/toolNames';
const { MeasurementReport } = adaptersSR.Cornerstone3D;
export default function addSRAnnotation(measurement, imageId, frameNumber) {
/**
* Adds a DICOM SR (Structured Report) annotation to the annotation manager.
* This function processes measurement data from DICOM SR and converts it into
* a format suitable for display in the Cornerstone3D viewer.
*
* @param {Object} params - The parameters object
* @param {Object} params.measurement - The DICOM SR measurement data containing coordinates, labels, and metadata
* @param {Array} params.measurement.coords - Array of coordinate objects with GraphicType, ValueType, and other properties
* @param {string} params.measurement.TrackingUniqueIdentifier - Unique identifier for the measurement
* @param {string} params.measurement.TrackingIdentifier - Tracking identifier for adapter lookup
* @param {Array} [params.measurement.labels] - Optional array of label objects
* @param {string} [params.measurement.displayText] - Optional display text for the annotation
* @param {Object} [params.measurement.textBox] - Optional text box configuration
* @param {string|null} [params.imageId] - Optional image ID for the referenced image (defaults to null)
* @param {number|null} [params.frameNumber] - Optional frame number for multi-frame images (defaults to null)
* @param {Object} params.displaySet - The display set containing the image
* @param {string} params.displaySet.displaySetInstanceUID - Unique identifier for the display set
* @returns {void}
*
* @example
* ```typescript
* addSRAnnotation({
* measurement: {
* TrackingUniqueIdentifier: '1.2.3.4.5',
* TrackingIdentifier: 'POINT',
* coords: [{
* GraphicType: 'POINT',
* ValueType: 'SCOORD',
* // ... other coordinate properties
* }],
* labels: [{ value: 'Measurement Point' }],
* displayText: 'Point measurement'
* },
* imageId: 'wadouri:file://path/to/image.dcm', // Optional
* frameNumber: 0, // Optional
* displaySet: { displaySetInstanceUID: '1.2.3.4' }
* });
* ```
*/
export default function addSRAnnotation({ measurement, imageId = null, frameNumber = null, displaySet }) {
/** @type {string} The tool name to use for the annotation, defaults to DICOMSRDisplay */
let toolName = toolNames.DICOMSRDisplay;
/**
* @type {Object} Renderable data organized by graphic type
* Groups coordinate data by GraphicType for efficient rendering
*/
const renderableData = measurement.coords.reduce((acc, coordProps) => {
acc[coordProps.GraphicType] = acc[coordProps.GraphicType] || [];
acc[coordProps.GraphicType].push(getRenderableData({ ...coordProps, imageId }));
@@ -19,43 +64,49 @@ export default function addSRAnnotation(measurement, imageId, frameNumber) {
const { ValueType: valueType, GraphicType: graphicType } = measurement.coords[0];
const graphicTypePoints = renderableData[graphicType];
/** TODO: Read the tool name from the DICOM SR identification type in the future. */
/**
* TODO: Read the tool name from the DICOM SR identification type in the future.
*/
let frameOfReferenceUID = null;
let planeRestriction = null;
/**
* Store the view reference for use in initial navigation
*/
if (imageId) {
const imagePlaneModule = metaData.get('imagePlaneModule', imageId);
frameOfReferenceUID = imagePlaneModule?.frameOfReferenceUID;
}
/**
* Store the view reference for use in initial navigation
*/
if (valueType === 'SCOORD3D') {
const adapter = MeasurementReport.getAdapterForTrackingIdentifier(
measurement.TrackingIdentifier
);
if (!adapter) {
toolName = toolNames.SRSCOORD3DPoint;
}
// get the ReferencedFrameOfReferenceUID from the measurement
frameOfReferenceUID = measurement.coords[0].ReferencedFrameOfReferenceSequence;
planeRestriction = {
FrameOfReferenceUID: frameOfReferenceUID,
point: graphicTypePoints[0][0],
};
}
// Store the view reference for use in initial navigation
/**
* Store the view reference for use in initial navigation
*/
measurement.viewReference = {
planeRestriction,
FrameOfReferenceUID: frameOfReferenceUID,
referencedImageId: imageId,
};
/**
* @type {Types.Annotation} The annotation object to be added to the annotation manager
* Contains all necessary metadata and data for rendering the DICOM SR measurement
*/
const SRAnnotation: Types.Annotation = {
annotationUID: TrackingUniqueIdentifier,
highlighted: false,
isLocked: false,
isPreview: toolName === toolNames.DICOMSRDisplay,
invalidated: false,
metadata: {
toolName,
@@ -64,6 +115,7 @@ export default function addSRAnnotation(measurement, imageId, frameNumber) {
graphicType,
FrameOfReferenceUID: frameOfReferenceUID,
referencedImageId: imageId,
displaySetInstanceUID: displaySet.displaySetInstanceUID,
},
data: {
label: measurement.labels?.[0]?.value || undefined,
@@ -81,8 +133,11 @@ export default function addSRAnnotation(measurement, imageId, frameNumber) {
};
/**
* const annotationManager = annotation.annotationState.getAnnotationManager();
* was not triggering annotation_added events.
* Add the annotation to the annotation state manager.
* Note: Using annotation.state.addAnnotation() instead of annotationManager.addAnnotation()
* because the latter was not triggering annotation_added events properly.
*
* @param {Types.Annotation} SRAnnotation - The annotation to add
*/
annotation.state.addAnnotation(SRAnnotation);
}
@@ -54,6 +54,11 @@ export default function hydrateStructuredReport(
const disableEditing = customizationService.getCustomization('panelMeasurement.disableEditing');
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
const {
StudyInstanceUID: studyUID,
SeriesInstanceUID: seriesUID,
instance: { SOPInstanceUID: sopUID },
} = displaySet;
// TODO -> We should define a strict versioning somewhere.
const mappings = measurementService.getSourceMappings(
@@ -67,24 +72,16 @@ export default function hydrateStructuredReport(
);
}
const instance = DicomMetadataStore.getInstance(
displaySet.StudyInstanceUID,
displaySet.SeriesInstanceUID,
displaySet.SOPInstanceUID
);
const instance = DicomMetadataStore.getInstance(studyUID, seriesUID, sopUID);
const sopInstanceUIDToImageId = {};
const imageIdsForToolState = {};
displaySet.measurements.forEach(measurement => {
const { ReferencedSOPInstanceUID, imageId, frameNumber } = measurement;
const { ReferencedSOPInstanceUID, imageId, frameNumber = 1 } = measurement;
const key = `${ReferencedSOPInstanceUID}:${frameNumber}`;
if (!sopInstanceUIDToImageId[ReferencedSOPInstanceUID]) {
sopInstanceUIDToImageId[ReferencedSOPInstanceUID] = imageId;
imageIdsForToolState[ReferencedSOPInstanceUID] = [];
}
if (!imageIdsForToolState[ReferencedSOPInstanceUID][frameNumber]) {
imageIdsForToolState[ReferencedSOPInstanceUID][frameNumber] = imageId;
if (!sopInstanceUIDToImageId[key]) {
sopInstanceUIDToImageId[key] = imageId;
}
});
@@ -133,10 +130,8 @@ export default function hydrateStructuredReport(
// dcmjs and Cornerstone3D has structural defect in supporting multi-frame
// files, and looking up the imageId from sopInstanceUIDToImageId results
// in the wrong value.
const frameNumber = (toolData.annotation.data && toolData.annotation.data.frameNumber) || 1;
const imageId =
imageIdsForToolState[toolData.sopInstanceUid][frameNumber] ||
sopInstanceUIDToImageId[toolData.sopInstanceUid];
const frameNumber = toolData.annotation.data?.frameNumber || 1;
const imageId = sopInstanceUIDToImageId[`${toolData.sopInstanceUid}:${frameNumber}`];
if (!imageIds.includes(imageId)) {
imageIds.push(imageId);
@@ -175,9 +170,7 @@ export default function hydrateStructuredReport(
// files, and looking up the imageId from sopInstanceUIDToImageId results
// in the wrong value.
const frameNumber = (toolData.annotation.data && toolData.annotation.data.frameNumber) || 1;
const imageId =
imageIdsForToolState[toolData.sopInstanceUid][frameNumber] ||
sopInstanceUIDToImageId[toolData.sopInstanceUid];
const imageId = sopInstanceUIDToImageId[`${toolData.sopInstanceUid}:${frameNumber}`];
if (!imageId) {
return getReferenceData3D(toolData, servicesManager);
@@ -208,6 +201,7 @@ export default function hydrateStructuredReport(
const annotation = {
annotationUID: toolData.annotation.annotationUID,
data: toolData.annotation.data,
predecessorImageId: toolData.predecessorImageId,
metadata: {
...referenceData,
toolName: annotationType,
File diff suppressed because it is too large. Load diff
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dynamic-volume",
"version": "3.12.0-beta.17",
"version": "3.12.2",
"description": "OHIF extension for 4D volumes data",
"author": "OHIF",
"license": "MIT",
@@ -29,21 +29,21 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.12.0-beta.17",
"@ohif/extension-cornerstone": "3.12.0-beta.17",
"@ohif/extension-default": "3.12.0-beta.17",
"@ohif/i18n": "3.12.0-beta.17",
"@ohif/ui": "3.12.0-beta.17",
"dcmjs": "0.43.1",
"dicom-parser": "^1.8.21",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
"react": "^18.3.1"
"@ohif/core": "3.12.2",
"@ohif/extension-cornerstone": "3.12.2",
"@ohif/extension-default": "3.12.2",
"@ohif/i18n": "3.12.2",
"@ohif/ui": "3.12.2",
"dcmjs": "0.49.4",
"dicom-parser": "1.8.21",
"hammerjs": "2.0.8",
"prop-types": "15.8.1",
"react": "18.3.1"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/core": "^3.32.12",
"@cornerstonejs/tools": "^3.32.12",
"classnames": "^2.3.2"
"@babel/runtime": "7.28.2",
"@cornerstonejs/core": "4.15.29",
"@cornerstonejs/tools": "4.15.29",
"classnames": "2.5.1"
}
}
@@ -1,8 +1,10 @@
import * as importedActions from './actions';
import { utilities, Enums } from '@cornerstonejs/tools';
import { cache } from '@cornerstonejs/core';
import { utils } from '@ohif/core';
const LABELMAP = Enums.SegmentationRepresentations.Labelmap;
import * as importedActions from './actions';
const { downloadCsv } = utils;
const commandsModule = ({ commandsManager, servicesManager }: withAppTypes) => {
const services = servicesManager.services;
@@ -37,7 +39,7 @@ const commandsModule = ({ commandsManager, servicesManager }: withAppTypes) => {
});
return computedDisplaySets;
},
exportTimeReportCSV: ({ segmentations, config, options, summaryStats }) => {
exportTimeReportCSV: ({ segmentations, summaryStats }) => {
const dynamic4DDisplaySet = actions.getDynamic4DDisplaySet();
const volumeId = dynamic4DDisplaySet?.displaySetInstanceUID;
@@ -203,15 +205,7 @@ const commandsModule = ({ commandsManager, servicesManager }: withAppTypes) => {
// Generate filename and trigger download
const filename = `${instance.PatientID}.csv`;
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', filename);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
downloadCsv(csvContent, { filename });
},
swapDynamicWithComputedDisplaySet: ({ displaySet }) => {
const computedDisplaySet = displaySet;
@@ -22,7 +22,7 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager, co
<>
<Toolbox
buttonSectionId={toolbarService.sections.dynamicToolbox}
title="Threshold Tools"
title="Buttons:Threshold Tools"
/>
<PanelSegmentation
servicesManager={servicesManager}
File diff suppressed because it is too large. Load diff
+6 -2
View File
@@ -1,9 +1,13 @@
const base = require('../../jest.config.base.js');
const pkg = require('./package');
module.exports = {
...base,
displayName: pkg.name,
moduleNameMapper: {
...base.moduleNameMapper,
'@ohif/(.*)': '<rootDir>/../../platform/$1/src',
'^@cornerstonejs/([^/]+)/(.*)$': '<rootDir>/../../node_modules/@cornerstonejs/$1/dist/esm/$2',
'^@cornerstonejs/([^/]+)$': '<rootDir>/../../node_modules/@cornerstonejs/$1/dist/esm',
},
// rootDir: "../.."
// testMatch: [
// //`<rootDir>/platform/${pack.name}/**/*.spec.js`
+35 -32
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
"version": "3.12.0-beta.17",
"version": "3.12.2",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
@@ -31,41 +31,44 @@
"dev:cornerstone": "yarn run dev",
"build": "cross-env NODE_ENV=production webpack --progress --config .webpack/webpack.prod.js",
"build:package-1": "yarn run build",
"start": "yarn run dev"
"start": "yarn run dev",
"test:unit": "jest --watchAll",
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@cornerstonejs/codec-charls": "^1.2.3",
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
"@cornerstonejs/dicom-image-loader": "^3.32.12",
"@ohif/core": "3.12.0-beta.17",
"@ohif/ui": "3.12.0-beta.17",
"dcmjs": "0.43.1",
"dicom-parser": "^1.8.21",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-resize-detector": "^10.0.1"
"@cornerstonejs/codec-charls": "1.2.3",
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
"@cornerstonejs/codec-openjpeg": "1.3.0",
"@cornerstonejs/codec-openjph": "2.4.7",
"@cornerstonejs/dicom-image-loader": "4.15.29",
"@ohif/core": "3.12.2",
"@ohif/ui": "3.12.2",
"dcmjs": "0.49.4",
"dicom-parser": "1.8.21",
"hammerjs": "2.0.8",
"prop-types": "15.8.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-resize-detector": "10.0.1"
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.32.12",
"@cornerstonejs/ai": "^3.32.12",
"@cornerstonejs/core": "^3.32.12",
"@cornerstonejs/labelmap-interpolation": "^3.32.12",
"@cornerstonejs/polymorphic-segmentation": "^3.32.12",
"@cornerstonejs/tools": "^3.32.12",
"@babel/runtime": "7.28.2",
"@cornerstonejs/adapters": "4.15.29",
"@cornerstonejs/ai": "4.15.29",
"@cornerstonejs/core": "4.15.29",
"@cornerstonejs/labelmap-interpolation": "4.15.29",
"@cornerstonejs/polymorphic-segmentation": "4.15.29",
"@cornerstonejs/tools": "4.15.29",
"@icr/polyseg-wasm": "0.4.0",
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
"@kitware/vtk.js": "32.12.0",
"html2canvas": "^1.4.1",
"lodash.compact": "^3.0.1",
"lodash.debounce": "^4.0.8",
"lodash.flatten": "^4.4.0",
"lodash.merge": "^4.6.2",
"lodash.zip": "^4.2.0",
"shader-loader": "^1.3.1",
"worker-loader": "^3.0.8"
"@kitware/vtk.js": "34.15.1",
"html2canvas": "1.4.1",
"immutability-helper": "3.1.1",
"lodash.compact": "3.0.1",
"lodash.debounce": "4.0.8",
"lodash.flatten": "4.4.0",
"lodash.zip": "4.2.0",
"shader-loader": "1.3.1",
"worker-loader": "3.0.8"
}
}
@@ -4,9 +4,9 @@ import PropTypes from 'prop-types';
import { metaData, Enums, utilities, eventTarget } from '@cornerstonejs/core';
import { Enums as csToolsEnums, UltrasoundPleuraBLineTool } from '@cornerstonejs/tools';
import type { ImageSliceData } from '@cornerstonejs/core/types';
import { ViewportOverlay } from '@ohif/ui-next';
import { ViewportOverlay, formatDICOMDate } from '@ohif/ui-next';
import type { InstanceMetadata } from '@ohif/core/src/types';
import { formatDICOMDate, formatDICOMTime, formatNumberPrecision } from './utils';
import { formatDICOMTime, formatNumberPrecision } from './utils';
import { utils } from '@ohif/core';
import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';
@@ -1,5 +1,7 @@
import moment from 'moment';
import i18n from 'i18next';
import { metaData } from '@cornerstonejs/core';
import { formatDICOMDate } from '@ohif/ui-next';
/**
* Checks if value is valid.
@@ -24,17 +26,6 @@ export function formatNumberPrecision(number, precision = 0) {
}
}
/**
* Formats DICOM date.
*
* @param {string} date
* @param {string} strFormat
* @returns {string} formatted date.
*/
export function formatDICOMDate(date, strFormat = 'MMM D, YYYY') {
return moment(date, 'YYYYMMDD').format(strFormat);
}
/**
* DICOM Time is stored as HHmmss.SSS, where:
* HH 24 hour time:
@@ -71,3 +62,5 @@ export function getCompression(imageId) {
return 'Lossless / Uncompressed';
}
export { formatDICOMDate };
+409 -162
View File
@@ -14,13 +14,18 @@ import {
utilities as cstUtils,
annotation,
Types as ToolTypes,
SplineContourSegmentationTool,
} from '@cornerstonejs/tools';
import {
SegmentInfo,
LogicalOperation,
OperatorOptions,
} from '@cornerstonejs/tools/utilities/contourSegmentation/logicalOperators';
import * as cornerstoneTools from '@cornerstonejs/tools';
import * as labelmapInterpolation from '@cornerstonejs/labelmap-interpolation';
import { ONNXSegmentationController } from '@cornerstonejs/ai';
import { Types as OhifTypes, utils } from '@ohif/core';
import i18n from '@ohif/i18n';
import {
callInputDialogAutoComplete,
createReportAsync,
@@ -33,7 +38,11 @@ import { getFirstAnnotationSelected } from './utils/measurementServiceMappings/u
import { getViewportEnabledElement } from './utils/getViewportEnabledElement';
import getActiveViewportEnabledElement from './utils/getActiveViewportEnabledElement';
import toggleVOISliceSync from './utils/toggleVOISliceSync';
import { usePositionPresentationStore, useSegmentationPresentationStore } from './stores';
import {
usePositionPresentationStore,
useSegmentationPresentationStore,
useSelectedSegmentationsForViewportStore,
} from './stores';
import { toolNames } from './initCornerstoneTools';
import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownloadForm';
import { updateSegmentBidirectionalStats } from './utils/updateSegmentationStats';
@@ -42,6 +51,12 @@ import { getUpdatedViewportsForSegmentation } from './utils/hydrationUtils';
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
import { isMeasurementWithinViewport } from './utils/isMeasurementWithinViewport';
import { getCenterExtent } from './utils/getCenterExtent';
import { EasingFunctionEnum } from './utils/transitions';
import { createSegmentationForViewport } from './utils/createSegmentationForViewport';
import { utilities as segmentationUtilities } from '@cornerstonejs/tools/segmentation';
import i18n from '@ohif/i18n';
const { add, intersect, subtract, copy } = cstUtils.contourSegmentation;
const { DefaultHistoryMemo } = csUtils.HistoryMemo;
const toggleSyncFunctions = {
@@ -105,6 +120,7 @@ function commandsModule({
}: OhifTypes.Extensions.ExtensionParams): OhifTypes.Extensions.CommandsModule {
const {
viewportGridService,
toolbarService,
toolGroupService,
cineService,
uiDialogService,
@@ -144,6 +160,50 @@ function commandsModule({
};
}
function _handleBrushSizeAction(action: 'increase' | 'decrease') {
const toolGroupIds = toolGroupService.getToolGroupIds();
if (!toolGroupIds?.length) {
return;
}
for (const toolGroupId of toolGroupIds) {
const brushSize = segmentationUtils.getBrushSizeForToolGroup(toolGroupId);
const newBrushSize = action === 'increase' ? brushSize + 3 : brushSize - 3;
if (brushSize) {
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, newBrushSize);
toolbarService.refreshToolbarState({ toolGroupId });
}
}
}
/**
* Creates a command function that sets a style property for segmentation types.
* If type is provided, sets the property for that type only.
* If type is not provided, sets the property for both Labelmap and Contour types.
* @param propertyName - The name of the style property to set
* @returns A command function that takes { type, value }
*/
const createSetStyleCommand = (propertyName: string) => {
return ({ type, value }) => {
const { segmentationService } = servicesManager.services;
if (type) {
segmentationService.setStyle({ type }, { [propertyName]: value });
} else {
segmentationService.setStyle(
{ type: SegmentationRepresentations.Labelmap },
{ [propertyName]: value }
);
segmentationService.setStyle(
{ type: SegmentationRepresentations.Contour },
{ [propertyName]: value }
);
}
};
};
const actions = {
jumpToMeasurementViewport: ({ annotationUID, measurement }) => {
cornerstoneTools.annotation.selection.setAnnotationSelected(annotationUID, true);
@@ -235,16 +295,28 @@ function commandsModule({
return;
}
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
if (!viewport) {
return;
}
if (displaySet.isOverlayDisplaySet) {
// update the previously stored segmentationPresentation with the new viewportId
// presentation so that when we put the referencedDisplaySet back in the viewport
// it will have the correct segmentation representation hydrated
const segmentationType =
// Todo: check if PMAP modality should be handled such as SEG
displaySet.Modality !== 'SEG'
? SegmentationRepresentations.Contour
: viewport.type === CoreEnums.ViewportType.VOLUME_3D
? SegmentationRepresentations.Surface
: SegmentationRepresentations.Labelmap;
commandsManager.runCommand('updateStoredSegmentationPresentation', {
displaySet,
type:
displaySet.Modality === 'SEG'
? SegmentationRepresentations.Labelmap
: SegmentationRepresentations.Contour,
type: segmentationType,
});
}
@@ -265,10 +337,35 @@ function commandsModule({
referencedDisplaySetInstanceUID
);
storePositionPresentation(referencedDisplaySet);
return commandsManager.runCommand('loadSegmentationDisplaySetsForViewport', {
const results = commandsManager.runCommand('loadSegmentationDisplaySetsForViewport', {
viewportId,
displaySetInstanceUIDs: [referencedDisplaySet.displaySetInstanceUID],
});
const disableEditing = customizationService.getCustomization(
'panelSegmentation.disableEditing'
);
if (disableEditing) {
const segmentationRepresentations = segmentationService.getSegmentationRepresentations(
viewportId,
{
segmentationId: displaySet.displaySetInstanceUID,
}
);
segmentationRepresentations.forEach(representation => {
const segmentIndices = Object.keys(representation.segments);
segmentIndices.forEach(segmentIndex => {
segmentationService.setSegmentLocked(
representation.segmentationId,
parseInt(segmentIndex),
true
);
});
});
}
return results;
} else if (displaySet.Modality === 'SR') {
const results = commandsManager.runCommand('hydrateStructuredReport', {
displaySetInstanceUID: displaySet.displaySetInstanceUID,
@@ -517,8 +614,8 @@ function commandsModule({
if (!labelConfig) {
const label = await callInputDialog({
uiDialogService,
title: 'Edit Measurement Label',
placeholder: measurement.label || 'Enter new label',
title: i18n.t('Tools:Edit Measurement Label'),
placeholder: measurement.label || i18n.t('Tools:Enter new label'),
defaultValue: measurement.label,
});
@@ -732,8 +829,8 @@ function commandsModule({
if (!labelConfig) {
const label = await callInputDialog({
uiDialogService,
title: 'Edit Arrow Text',
placeholder: data?.data?.label || 'Enter new text',
title: i18n.t('Tools:Edit Arrow Text'),
placeholder: data?.data?.label || i18n.t('Tools:Enter new text'),
defaultValue: data?.data?.label || '',
});
@@ -925,17 +1022,21 @@ function commandsModule({
}
}
},
setToolActiveToolbar: ({ value, itemId, toolName, toolGroupIds = [] }) => {
setToolActiveToolbar: ({ value, itemId, toolName, toolGroupIds = [], bindings }) => {
// Sometimes it is passed as value (tools with options), sometimes as itemId (toolbar buttons)
toolName = toolName || itemId || value;
toolGroupIds = toolGroupIds.length ? toolGroupIds : toolGroupService.getToolGroupIds();
toolGroupIds.forEach(toolGroupId => {
actions.setToolActive({ toolName, toolGroupId });
actions.setToolActive({ toolName, toolGroupId, bindings });
});
},
setToolActive: ({ toolName, toolGroupId = null }) => {
setToolActive: ({
toolName,
toolGroupId = null,
bindings = [{ mouseButton: Enums.MouseBindings.Primary }],
}) => {
const { viewports } = viewportGridService.getState();
if (!viewports.size) {
@@ -963,11 +1064,7 @@ function commandsModule({
// Set the new toolName to be active
toolGroup.setToolActive(toolName, {
bindings: [
{
mouseButton: Enums.MouseBindings.Primary,
},
],
bindings,
});
},
// capture viewport
@@ -977,8 +1074,8 @@ function commandsModule({
if (!cornerstoneViewportService.getCornerstoneViewport(activeViewportId)) {
// Cannot download a non-cornerstone viewport (image).
uiNotificationService.show({
title: 'Download Image',
message: 'Image cannot be downloaded',
title: i18n.t('Tools:Download Image'),
message: i18n.t('Tools:Image cannot be downloaded'),
type: 'error',
});
return;
@@ -989,7 +1086,7 @@ function commandsModule({
if (uiModalService) {
uiModalService.show({
content: CornerstoneViewportDownloadForm,
title: 'Download High Quality Image',
title: i18n.t('Tools:Download High Quality Image'),
contentProps: {
activeViewportId,
cornerstoneViewportService,
@@ -1440,48 +1537,24 @@ function commandsModule({
* as a segmentation representation to the viewport.
*/
createLabelmapForViewport: async ({ viewportId, options = {} }) => {
const { viewportGridService, displaySetService, segmentationService } =
servicesManager.services;
const { viewports } = viewportGridService.getState();
const targetViewportId = viewportId;
const viewport = viewports.get(targetViewportId);
// Todo: add support for multiple display sets
const displaySetInstanceUID =
options.displaySetInstanceUID || viewport.displaySetInstanceUIDs[0];
const segs = segmentationService.getSegmentations();
const label = options.label || `Segmentation ${segs.length + 1}`;
const segmentationId = options.segmentationId || `${csUtils.uuidv4()}`;
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
// This will create the segmentation and register it as a display set
const generatedSegmentationId = await segmentationService.createLabelmapForDisplaySet(
displaySet,
{
label,
segmentationId,
segments: options.createInitialSegment
? {
1: {
label: `${i18n.t('Segment')} 1`,
active: true,
},
}
: {},
}
);
// Also add the segmentation representation to the viewport
await segmentationService.addSegmentationRepresentation(viewportId, {
segmentationId,
type: Enums.SegmentationRepresentations.Labelmap,
return createSegmentationForViewport(servicesManager, {
viewportId,
options,
segmentationType: SegmentationRepresentations.Labelmap,
});
},
/**
* Creates a contour for the active viewport
*
* The created contour will be registered as a display set and also added
* as a segmentation representation to the viewport.
*/
createContourForViewport: async ({ viewportId, options = {} }) => {
return createSegmentationForViewport(servicesManager, {
viewportId,
options,
segmentationType: SegmentationRepresentations.Contour,
});
return generatedSegmentationId;
},
/**
@@ -1500,9 +1573,9 @@ function commandsModule({
* Adds a new segment to a segmentation
* @param props.segmentationId - The ID of the segmentation to add the segment to
*/
addSegmentCommand: ({ segmentationId }) => {
addSegmentCommand: ({ segmentationId, config }) => {
const { segmentationService } = servicesManager.services;
segmentationService.addSegment(segmentationId);
segmentationService.addSegment(segmentationId, config);
},
/**
@@ -1518,7 +1591,33 @@ function commandsModule({
segmentationId
);
segmentationService.setActiveSegment(segmentationId, segmentIndex);
segmentationService.jumpToSegmentCenter(segmentationId, segmentIndex);
const { highlightAlpha, highlightSegment, animationLength, animationFunctionType } =
(customizationService.getCustomization(
'panelSegmentation.jumpToSegmentHighlightAnimationConfig'
) as Object as {
highlightAlpha?: number;
highlightSegment?: boolean;
animationLength?: number;
animationFunctionType?: EasingFunctionEnum;
}) ?? {};
const validAnimationFunctionType = Object.values(EasingFunctionEnum).includes(
animationFunctionType
)
? animationFunctionType
: undefined;
segmentationService.jumpToSegmentNext(
segmentationId,
segmentIndex,
undefined,
highlightAlpha,
highlightSegment,
animationLength,
undefined,
validAnimationFunctionType
);
},
/**
@@ -1573,15 +1672,13 @@ function commandsModule({
* Stores a segmentation and shows it in the viewport
* @param props.segmentationId - The ID of the segmentation to store
*/
storeSegmentationCommand: async ({ segmentationId }) => {
storeSegmentationCommand: async args => {
const { segmentationId } = args;
const { segmentationService, viewportGridService } = servicesManager.services;
const displaySetInstanceUIDs = await createReportAsync({
servicesManager,
getReport: () =>
commandsManager.runCommand('storeSegmentation', {
segmentationId,
}),
getReport: () => commandsManager.runCommand('storeSegmentation', args),
reportType: 'Segmentation',
});
@@ -1656,56 +1753,6 @@ function commandsModule({
segmentationService.setRenderInactiveSegmentations(viewportId, !renderInactive);
},
/**
* Sets the fill alpha value for a segmentation type
* @param props.type - The type of segmentation
* @param props.value - The alpha value to set
*/
setFillAlphaCommand: ({ type, value }) => {
const { segmentationService } = servicesManager.services;
segmentationService.setStyle({ type }, { fillAlpha: value });
},
/**
* Sets the outline width for a segmentation type
* @param props.type - The type of segmentation
* @param props.value - The width value to set
*/
setOutlineWidthCommand: ({ type, value }) => {
const { segmentationService } = servicesManager.services;
segmentationService.setStyle({ type }, { outlineWidth: value });
},
/**
* Sets whether to render fill for a segmentation type
* @param props.type - The type of segmentation
* @param props.value - Whether to render fill
*/
setRenderFillCommand: ({ type, value }) => {
const { segmentationService } = servicesManager.services;
segmentationService.setStyle({ type }, { renderFill: value });
},
/**
* Sets whether to render outline for a segmentation type
* @param props.type - The type of segmentation
* @param props.value - Whether to render outline
*/
setRenderOutlineCommand: ({ type, value }) => {
const { segmentationService } = servicesManager.services;
segmentationService.setStyle({ type }, { renderOutline: value });
},
/**
* Sets the fill alpha for inactive segmentations
* @param props.type - The type of segmentation
* @param props.value - The alpha value to set
*/
setFillAlphaInactiveCommand: ({ type, value }) => {
const { segmentationService } = servicesManager.services;
segmentationService.setStyle({ type }, { fillAlphaInactive: value });
},
editSegmentLabel: async ({ segmentationId, segmentIndex }) => {
const { segmentationService, uiDialogService } = servicesManager.services;
const segmentation = segmentationService.getSegmentation(segmentationId);
@@ -1718,8 +1765,8 @@ function commandsModule({
callInputDialog({
uiDialogService,
title: 'Edit Segment Label',
placeholder: 'Enter new label',
title: i18n.t('Tools:Edit Segment Label'),
placeholder: i18n.t('Tools:Enter new label'),
defaultValue: segment.label,
}).then(label => {
segmentationService.setSegmentLabel(segmentationId, segmentIndex, label);
@@ -1738,8 +1785,8 @@ function commandsModule({
callInputDialog({
uiDialogService,
title: 'Edit Segmentation Label',
placeholder: 'Enter new label',
title: i18n.t('Tools:Edit Segmentation Label'),
placeholder: i18n.t('Tools:Enter new label'),
defaultValue: label,
}).then(label => {
segmentationService.addOrUpdateSegmentation({ segmentationId, label });
@@ -1761,7 +1808,7 @@ function commandsModule({
uiDialogService.show({
content: colorPickerDialog,
title: 'Segment Color',
title: i18n.t('Tools:Segment Color'),
contentProps: {
value: rgbaColor,
onSave: newRgbaColor => {
@@ -1831,22 +1878,24 @@ function commandsModule({
}
});
},
toggleSegmentLabel: () => {
toggleSegmentLabel: ({ enabled }: { enabled?: boolean }) => {
const toolName = cornerstoneTools.SegmentLabelTool.toolName;
const toolGroupIds = toolGroupService.getToolGroupIds();
const isOn = toolGroupIds.some(toolGroupId => {
const isToolOn = toolGroupIds.some(toolGroupId => {
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId);
const mode = toolGroup.getToolInstance(toolName)?.mode;
return mode === 'Active';
});
const enableTool = enabled !== undefined ? enabled : !isToolOn;
toolGroupIds.forEach(toolGroupId => {
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId);
if (isOn) {
toolGroup.setToolDisabled(toolName);
} else {
if (enableTool) {
toolGroup.setToolActive(toolName);
} else {
toolGroup.setToolDisabled(toolName);
}
});
},
@@ -1969,17 +2018,11 @@ function commandsModule({
segmentAI.initViewport(viewport);
}
},
setBrushSize: ({ value, toolNames }) => {
setBrushSize: ({ value }) => {
const brushSize = Number(value);
toolGroupService.getToolGroupIds()?.forEach(toolGroupId => {
if (toolNames?.length === 0) {
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize);
} else {
toolNames?.forEach(toolName => {
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize, toolName);
});
}
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize);
});
},
setThresholdRange: ({
@@ -2008,26 +2051,10 @@ function commandsModule({
}
},
increaseBrushSize: () => {
const toolGroupIds = toolGroupService.getToolGroupIds();
if (!toolGroupIds?.length) {
return;
}
for (const toolGroupId of toolGroupIds) {
const brushSize = segmentationUtils.getBrushSizeForToolGroup(toolGroupId);
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize + 3);
}
_handleBrushSizeAction('increase');
},
decreaseBrushSize: () => {
const toolGroupIds = toolGroupService.getToolGroupIds();
if (!toolGroupIds?.length) {
return;
}
for (const toolGroupId of toolGroupIds) {
const brushSize = segmentationUtils.getBrushSizeForToolGroup(toolGroupId);
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize - 3);
}
_handleBrushSizeAction('decrease');
},
addNewSegment: () => {
const { segmentationService } = servicesManager.services;
@@ -2203,6 +2230,205 @@ function commandsModule({
deleting,
});
},
activateSelectedSegmentationOfType: ({ segmentationRepresentationType }) => {
const { segmentationService, viewportGridService } = servicesManager.services;
const activeViewportId = viewportGridService.getActiveViewportId();
const { selectedSegmentationsForViewport } =
useSelectedSegmentationsForViewportStore.getState();
const segmentationId = selectedSegmentationsForViewport[activeViewportId]?.get(
segmentationRepresentationType
);
if (!segmentationId) {
return;
}
segmentationService.setActiveSegmentation(activeViewportId, segmentationId);
},
setDynamicCursorSizeForSculptorTool: ({ value: isDynamicCursorSize }) => {
const viewportId = viewportGridService.getActiveViewportId();
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
const sculptorToolInstance = toolGroup.getToolInstance(toolNames.SculptorTool);
const oldConfiguration = sculptorToolInstance.configuration;
sculptorToolInstance.configuration = {
...oldConfiguration,
updateCursorSize: isDynamicCursorSize ? 'dynamic' : '',
};
},
setInterpolationToolConfiguration: ({ value: interpolateContours, toolNames }) => {
const viewportId = viewportGridService.getActiveViewportId();
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
// Set the interpolation configuration for the active tool.
const activeTool = toolGroupService.getActiveToolForViewport(viewportId);
const interpolationConfig = {
interpolation: {
enabled: interpolateContours,
},
};
toolGroup.setToolConfiguration(activeTool, interpolationConfig);
// Now set the interpolation configuration for the other tools specified.
if (toolNames) {
Object.values(toolGroup.getToolInstances()).forEach(toolInstance => {
if (toolNames?.includes(toolInstance.toolName)) {
toolGroup.setToolConfiguration(toolInstance.toolName, interpolationConfig);
}
});
}
},
setSimplifiedSplineForSplineContourSegmentationTool: ({ value: simplifiedSpline }) => {
const viewportId = viewportGridService.getActiveViewportId();
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
Object.values(toolGroup.getToolInstances()).forEach(toolInstance => {
if (toolInstance instanceof SplineContourSegmentationTool) {
const oldConfiguration = toolInstance.configuration;
toolInstance.configuration = {
...oldConfiguration,
simplifiedSpline,
};
}
});
},
removeSmallContours: ({ areaThreshold: threshold }) => {
const viewportId = viewportGridService.getActiveViewportId();
const activeSegmentation = segmentationService.getActiveSegmentation(viewportId);
const activeSegment = segmentationService.getActiveSegment(viewportId);
if (!activeSegmentation || !activeSegment) {
return;
}
const { removeContourIslands } = segmentationUtilities;
removeContourIslands(activeSegmentation.segmentationId, activeSegment.segmentIndex, {
threshold,
});
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
renderingEngine.render();
},
applyLogicalContourOperation: ({
segmentAInfo,
segmentBInfo,
resultSegmentInfo,
logicalOperation,
}: {
segmentAInfo: SegmentInfo;
segmentBInfo: SegmentInfo;
resultSegmentInfo: OperatorOptions;
logicalOperation: LogicalOperation;
}) => {
switch (logicalOperation) {
case LogicalOperation.Union:
add(segmentAInfo, segmentBInfo, resultSegmentInfo);
break;
case LogicalOperation.Intersect:
intersect(segmentAInfo, segmentBInfo, resultSegmentInfo);
break;
case LogicalOperation.Subtract:
subtract(segmentAInfo, segmentBInfo, resultSegmentInfo);
break;
default:
throw new Error('Unsupported logical operation');
}
},
copyContourSegment: ({
sourceSegmentInfo,
targetSegmentInfo,
}: {
sourceSegmentInfo: SegmentInfo;
targetSegmentInfo?: SegmentInfo;
}) => {
if (!targetSegmentInfo) {
targetSegmentInfo = {
segmentationId: sourceSegmentInfo.segmentationId,
segmentIndex: segmentationService.getNextAvailableSegmentIndex(
sourceSegmentInfo.segmentationId
),
};
segmentationService.addSegment(targetSegmentInfo.segmentationId, {
segmentIndex: targetSegmentInfo.segmentIndex,
});
}
copy(sourceSegmentInfo, targetSegmentInfo);
},
smoothContours: () => {
const viewportId = viewportGridService.getActiveViewportId();
const activeSegmentation = segmentationService.getActiveSegmentation(viewportId);
const activeSegment = segmentationService.getActiveSegment(viewportId);
if (!activeSegmentation || !activeSegment) {
return;
}
const { smoothContours } = segmentationUtilities;
smoothContours(activeSegmentation.segmentationId, activeSegment.segmentIndex);
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
renderingEngine.render();
},
removeContourHoles: () => {
const viewportId = viewportGridService.getActiveViewportId();
const activeSegmentation = segmentationService.getActiveSegmentation(viewportId);
const activeSegment = segmentationService.getActiveSegment(viewportId);
if (!activeSegmentation || !activeSegment) {
return;
}
const { removeContourHoles } = segmentationUtilities;
removeContourHoles(activeSegmentation.segmentationId, activeSegment.segmentIndex);
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
renderingEngine.render();
},
decimateContours: () => {
const viewportId = viewportGridService.getActiveViewportId();
const activeSegmentation = segmentationService.getActiveSegmentation(viewportId);
const activeSegment = segmentationService.getActiveSegment(viewportId);
if (!activeSegmentation || !activeSegment) {
return;
}
const { decimateContours } = segmentationUtilities;
decimateContours(activeSegmentation.segmentationId, activeSegment.segmentIndex);
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
renderingEngine.render();
},
convertContourHoles: () => {
const viewportId = viewportGridService.getActiveViewportId();
const activeSegmentation = segmentationService.getActiveSegmentation(viewportId);
const activeSegment = segmentationService.getActiveSegment(viewportId);
if (!activeSegmentation || !activeSegment) {
return;
}
const targetSegmentInfo = {
segmentationId: activeSegmentation.segmentationId,
segmentIndex: segmentationService.getNextAvailableSegmentIndex(
activeSegmentation.segmentationId
),
};
segmentationService.addSegment(targetSegmentInfo.segmentationId, {
segmentIndex: targetSegmentInfo.segmentIndex,
});
const { convertContourHoles } = segmentationUtilities;
convertContourHoles(
activeSegmentation.segmentationId,
activeSegment.segmentIndex,
targetSegmentInfo.segmentationId,
targetSegmentInfo.segmentIndex
);
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
renderingEngine.render();
},
};
const definitions = {
@@ -2401,6 +2627,9 @@ function commandsModule({
createLabelmapForViewport: {
commandFn: actions.createLabelmapForViewport,
},
createContourForViewport: {
commandFn: actions.createContourForViewport,
},
setActiveSegmentation: {
commandFn: actions.setActiveSegmentation,
},
@@ -2444,19 +2673,25 @@ function commandsModule({
commandFn: actions.toggleRenderInactiveSegmentationsCommand,
},
setFillAlpha: {
commandFn: actions.setFillAlphaCommand,
commandFn: createSetStyleCommand('fillAlpha'),
},
setOutlineWidth: {
commandFn: actions.setOutlineWidthCommand,
commandFn: createSetStyleCommand('outlineWidth'),
},
setRenderFill: {
commandFn: actions.setRenderFillCommand,
commandFn: createSetStyleCommand('renderFill'),
},
setRenderFillInactive: {
commandFn: createSetStyleCommand('renderFillInactive'),
},
setRenderOutline: {
commandFn: actions.setRenderOutlineCommand,
commandFn: createSetStyleCommand('renderOutline'),
},
setRenderOutlineInactive: {
commandFn: createSetStyleCommand('renderOutlineInactive'),
},
setFillAlphaInactive: {
commandFn: actions.setFillAlphaInactiveCommand,
commandFn: createSetStyleCommand('fillAlphaInactive'),
},
editSegmentLabel: {
commandFn: actions.editSegmentLabel,
@@ -2502,6 +2737,18 @@ function commandsModule({
toggleSegmentLabel: actions.toggleSegmentLabel,
jumpToMeasurementViewport: actions.jumpToMeasurementViewport,
initializeSegmentLabelTool: actions.initializeSegmentLabelTool,
activateSelectedSegmentationOfType: actions.activateSelectedSegmentationOfType,
setDynamicCursorSizeForSculptorTool: actions.setDynamicCursorSizeForSculptorTool,
setSimplifiedSplineForSplineContourSegmentationTool:
actions.setSimplifiedSplineForSplineContourSegmentationTool,
removeSmallContours: actions.removeSmallContours,
applyLogicalContourOperation: actions.applyLogicalContourOperation,
copyContourSegment: actions.copyContourSegment,
smoothContours: actions.smoothContours,
removeContourHoles: actions.removeContourHoles,
decimateContours: actions.decimateContours,
convertContourHoles: actions.convertContourHoles,
setInterpolationToolConfiguration: actions.setInterpolationToolConfiguration,
};
return {
@@ -55,7 +55,7 @@ const DEFAULT_TYPES = [GroupAccordion, Content, Trigger];
* measurements panel for a practical, working example.
*/
export function AccordionGroup(props) {
const { grouping, items, children, sourceChildren, type } = props;
const { grouping, items, children, sourceChildren } = props;
const childProps = useSystem();
let defaultValue = props.defaultValue;
const groups = grouping.groupingFunction(items, grouping, childProps);
@@ -1,23 +0,0 @@
.dicom-upload-drop-area-border-dash {
background-image: repeating-linear-gradient(
to right,
#7bb2ce 0%,
#7bb2ce 50%,
transparent 50%,
transparent 100%
),
repeating-linear-gradient(to right, #7bb2ce 0%, #7bb2ce 50%, transparent 50%, transparent 100%),
repeating-linear-gradient(to bottom, #7bb2ce 0%, #7bb2ce 50%, transparent 50%, transparent 100%),
repeating-linear-gradient(to bottom, #7bb2ce 0%, #7bb2ce 50%, transparent 50%, transparent 100%);
background-position:
left top,
left bottom,
left top,
right top;
background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
background-size:
20px 3px,
20px 3px,
3px 20px,
3px 20px;
}
@@ -5,8 +5,8 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import DicomFileUploader from '../../utils/DicomFileUploader';
import DicomUploadProgress from './DicomUploadProgress';
import { Button, ButtonEnums } from '@ohif/ui';
import './DicomUpload.css';
import { Button } from '@ohif/ui-next';
// Removed dashed border CSS; using simple 1px solid border with muted foreground color
type DicomUploadProps = {
dataSource;
@@ -15,7 +15,8 @@ type DicomUploadProps = {
};
function DicomUpload({ dataSource, onComplete, onStarted }: DicomUploadProps): ReactElement {
const baseClassNames = 'min-h-[480px] flex flex-col bg-black select-none';
const baseClassNames =
'min-h-[375px] flex flex-col bg-black select-none rounded-lg overflow-hidden';
const [dicomFileUploaderArr, setDicomFileUploaderArr] = useState([]);
const onDrop = useCallback(async acceptedFiles => {
@@ -34,9 +35,10 @@ function DicomUpload({ dataSource, onComplete, onStarted }: DicomUploadProps): R
{({ getRootProps }) => (
<div
{...getRootProps()}
className="dicom-upload-drop-area-border-dash m-5 flex h-full flex-col items-center justify-center"
className="m-5 flex h-full flex-col items-center justify-center rounded-2xl border"
style={{ borderColor: 'hsl(var(--muted-foreground) / 0.25)' }}
>
<div className="flex gap-3">
<div className="flex gap-2">
<Dropzone
onDrop={onDrop}
noDrag
@@ -44,11 +46,16 @@ function DicomUpload({ dataSource, onComplete, onStarted }: DicomUploadProps): R
{({ getRootProps, getInputProps }) => (
<div {...getRootProps()}>
<Button
variant="default"
size="lg"
disabled={false}
onClick={() => {}}
>
{'Add files'}
<input {...getInputProps()} />
<input
{...getInputProps()}
style={{ display: 'none' }}
/>
</Button>
</div>
)}
@@ -60,7 +67,8 @@ function DicomUpload({ dataSource, onComplete, onStarted }: DicomUploadProps): R
{({ getRootProps, getInputProps }) => (
<div {...getRootProps()}>
<Button
type={ButtonEnums.type.secondary}
variant="secondary"
size="lg"
disabled={false}
onClick={() => {}}
>
@@ -69,14 +77,15 @@ function DicomUpload({ dataSource, onComplete, onStarted }: DicomUploadProps): R
{...getInputProps()}
webkitdirectory="true"
mozdirectory="true"
style={{ display: 'none' }}
/>
</Button>
</div>
)}
</Dropzone>
</div>
<div className="pt-5">or drag images or folders here</div>
<div className="text-aqua-pale pt-3 text-lg">(DICOM files supported)</div>
<div className="text-foreground pt-6 text-base">or drag images or folders here</div>
<div className="text-muted-foreground pt-1 text-base">(DICOM files supported)</div>
</div>
)}
</Dropzone>
@@ -1,7 +1,7 @@
import React, { useCallback, useEffect, useRef, useState, ReactElement } from 'react';
import PropTypes from 'prop-types';
import { useSystem } from '@ohif/core';
import { Button } from '@ohif/ui';
import { Button } from '@ohif/ui-next';
import { Icons } from '@ohif/ui-next';
import DicomFileUploader, {
EVENTS,
@@ -276,7 +276,7 @@ function DicomUploadProgress({
const getNumCompletedAndTimeRemainingComponent = (): ReactElement => {
return (
<div className="bg-primary-dark flex h-14 items-center px-1 pb-4 text-lg text-white">
<div className="bg-muted flex h-14 items-center px-1 pb-4 text-lg text-white">
{numFilesCompleted === dicomFileUploaderArr.length ? (
<>
<span className={NO_WRAP_ELLIPSIS_CLASS_NAMES}>{`${dicomFileUploaderArr.length} ${
@@ -308,7 +308,7 @@ function DicomUploadProgress({
<span
className={
'text-primary hover:text-primary-lightactive:text-aqua-pale ml-auto cursor-pointer whitespace-nowrap'
'text-primary hover:text-highlight active:text-muted-foreground ml-auto cursor-pointer whitespace-nowrap'
}
onClick={cancelAllUploads}
>
@@ -337,11 +337,11 @@ function DicomUploadProgress({
const getPercentCompleteComponent = (): ReactElement => {
return (
<div className="ohif-scrollbar border-secondary-light overflow-y-scroll border-b px-2">
<div className="ohif-scrollbar border-input overflow-y-scroll border-b px-2">
<div className="min-h-14 flex w-full items-center p-2.5">
{numFilesCompleted === dicomFileUploaderArr.length ? (
<>
<div className="text-primary-light text-xl">
<div className="text-highlight text-xl">
{numFails > 0
? `Completed with ${numFails} ${numFails > 1 ? 'errors' : 'error'}!`
: 'Completed!'}
@@ -359,7 +359,7 @@ function DicomUploadProgress({
></ProgressLoadingBar>
</div>
<div className="ml-1 flex w-24 items-center">
<div className="w-10 text-right text-foreground">{`${getPercentCompleteRounded()}%`}</div>
<div className="text-foreground w-10 text-right">{`${getPercentCompleteRounded()}%`}</div>
{getShowFailedOnlyIconComponent()}
</div>
</>
@@ -56,7 +56,7 @@ const DicomUploadProgressItem = memo(
return (
<Icons.ByName
name="status-tracked"
className="text-primary-light"
className="text-highlight"
/>
);
case UploadStatus.InProgress:
@@ -71,7 +71,7 @@ const DicomUploadProgressItem = memo(
};
return (
<div className="min-h-14 border-secondary-light flex w-full items-center overflow-hidden border-b p-2.5 text-lg">
<div className="min-h-14 border-input flex w-full items-center overflow-hidden border-b p-2.5 text-lg">
<div className="self-top flex w-0 shrink grow flex-col gap-1">
<div className="flex gap-4">
<div className="flex w-6 shrink-0 items-center justify-center">{getStatusIcon()}</div>
@@ -0,0 +1,108 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuPortal,
DropdownMenuSubContent,
DropdownMenuLabel,
DropdownMenuItem,
DropdownMenuSeparator,
Icons,
} from '@ohif/ui-next';
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
interface ExportSegmentationSubMenuItemProps {
segmentationId: string;
segmentationRepresentationType: string;
allowExport: boolean;
actions: {
storeSegmentation: (segmentationId: string, modality?: string) => Promise<unknown>;
onSegmentationDownloadRTSS: (segmentationId: string) => void;
onSegmentationDownload: (segmentationId: string) => void;
downloadCSVSegmentationReport: (segmentationId: string) => void;
};
}
export const ExportSegmentationSubMenuItem: React.FC<ExportSegmentationSubMenuItemProps> = ({
segmentationId,
segmentationRepresentationType,
allowExport,
actions,
}) => {
const { t } = useTranslation('SegmentationPanel');
return (
<DropdownMenuSub>
<DropdownMenuSubTrigger className="pl-1">
<Icons.Export className="text-foreground" />
<span className="pl-2">{t('Download & Export')}</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
<DropdownMenuLabel className="flex items-center pl-0">
<Icons.Download className="h-5 w-5" />
<span className="pl-1">{t('Download')}</span>
</DropdownMenuLabel>
{segmentationRepresentationType === SegmentationRepresentations.Labelmap && (
<DropdownMenuItem
onClick={e => {
e.preventDefault();
actions.downloadCSVSegmentationReport(segmentationId);
}}
disabled={!allowExport}
>
{t('CSV Report')}
</DropdownMenuItem>
)}
{segmentationRepresentationType === SegmentationRepresentations.Labelmap && (
<DropdownMenuItem
onClick={e => {
e.preventDefault();
actions.onSegmentationDownload(segmentationId);
}}
disabled={!allowExport}
>
{t('DICOM SEG')}
</DropdownMenuItem>
)}
<DropdownMenuItem
onClick={e => {
e.preventDefault();
actions.onSegmentationDownloadRTSS(segmentationId);
}}
disabled={!allowExport}
>
{t('DICOM RTSS')}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuLabel className="flex items-center pl-0">
<Icons.Export className="h-5 w-5" />
<span className="pl-1 pt-1">{t('Export')}</span>
</DropdownMenuLabel>
{segmentationRepresentationType === SegmentationRepresentations.Labelmap && (
<DropdownMenuItem
onClick={e => {
e.preventDefault();
actions.storeSegmentation(segmentationId, 'SEG');
}}
disabled={!allowExport}
>
{t('DICOM SEG')}
</DropdownMenuItem>
)}
<DropdownMenuItem
onClick={e => {
e.preventDefault();
actions.storeSegmentation(segmentationId, 'RTSTRUCT');
}}
disabled={!allowExport}
>
{t('DICOM RTSS')}
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
);
};
@@ -8,9 +8,11 @@ import {
DropdownMenuItem,
Icons,
} from '@ohif/ui-next';
import { useTranslation } from 'react-i18next';
export function MeasumentsMenu(props) {
const { group, classNames } = props;
const { t } = useTranslation('MeasurementTable');
if (!group.items?.length) {
console.log('No items to iterate', group.items);
return null;
@@ -44,7 +46,7 @@ export function MeasumentsMenu(props) {
className={`h-6 w-6 transition-opacity ${
isSelected || !isVisible ? 'opacity-100' : 'opacity-50 group-hover:opacity-100'
}`}
aria-label={isVisible ? 'Hide' : 'Show'}
aria-label={isVisible ? t('Hide') : t('Show')}
onClick={e => {
e.stopPropagation();
onAction(e, ['jumpToMeasurement', 'toggleVisibilityMeasurement']);
@@ -70,7 +72,7 @@ export function MeasumentsMenu(props) {
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={e => onAction(e, 'removeMeasurement')}>
<Icons.Delete className="text-foreground" />
<span className="pl-2">Delete</span>
<span className="pl-2">{t('Delete')}</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -5,6 +5,7 @@ import { utils } from '../..';
import { useViewportSegmentations } from '../../hooks';
import { useMeasurementTracking } from '../../hooks/useMeasurementTracking';
import { useViewportDisplaySets } from '../../hooks/useViewportDisplaySets';
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
/**
* NavigationComponent provides navigation controls for viewports containing
@@ -27,7 +28,11 @@ function NavigationComponent({ viewportId }: { viewportId: string }) {
viewportId,
});
const hasSegmentations = segmentationsWithRepresentations.length > 0;
const hasSegmentations =
segmentationsWithRepresentations.length > 0 &&
segmentationsWithRepresentations.some(
segmentation => segmentation?.representation?.type !== SegmentationRepresentations.Surface
);
// prefer segment navigation if available
const navigationMode = hasSegmentations
@@ -87,7 +92,10 @@ function NavigationComponent({ viewportId }: { viewportId: string }) {
return;
}
const segmentationId = segmentationsWithRepresentations[0].segmentation.segmentationId;
const activeSegmentationWithRepresentation = segmentationsWithRepresentations.find(
segmentation => segmentation?.representation?.active
);
const segmentationId = activeSegmentationWithRepresentation.segmentation.segmentationId;
utils.handleSegmentChange({
direction,
@@ -0,0 +1,71 @@
import React, { useState } from 'react';
import { Switch } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
import { useTranslation } from 'react-i18next';
export default function SegmentationToolConfig() {
const { commandsManager } = useSystem();
const { t } = useTranslation('SegmentationPanel');
// Get initial states based on current configuration
const [previewEdits, setPreviewEdits] = useState(false);
const [segmentLabelEnabled, setSegmentLabelEnabled] = useState(false);
const [toggleSegmentEnabled, setToggleSegmentEnabled] = useState(false);
const [useCenterAsSegmentIndex, setUseCenterAsSegmentIndex] = useState(false);
const handlePreviewEditsChange = checked => {
setPreviewEdits(checked);
commandsManager.run('toggleSegmentPreviewEdit', { toggle: checked });
};
const handleToggleSegmentEnabledChange = checked => {
setToggleSegmentEnabled(checked);
commandsManager.run('toggleSegmentSelect', { toggle: checked });
};
const handleUseCenterAsSegmentIndexChange = checked => {
setUseCenterAsSegmentIndex(checked);
commandsManager.run('toggleUseCenterSegmentIndex', { toggle: checked });
};
const handleSegmentLabelEnabledChange = checked => {
setSegmentLabelEnabled(checked);
commandsManager.run('toggleSegmentLabel', { enabled: checked });
};
return (
<div className="bg-muted flex flex-col gap-2 border-b border-b-[2px] border-black px-2 py-3">
<div className="flex items-center gap-2">
<Switch
checked={previewEdits}
onCheckedChange={handlePreviewEditsChange}
/>
<span className="text-foreground text-base">{t('Preview edits before creating')}</span>
</div>
<div className="flex items-center gap-2">
<Switch
checked={useCenterAsSegmentIndex}
onCheckedChange={handleUseCenterAsSegmentIndexChange}
/>
<span className="text-foreground text-base">{t('Use center as segment index')}</span>
</div>
<div className="flex items-center gap-2">
<Switch
checked={toggleSegmentEnabled}
onCheckedChange={handleToggleSegmentEnabledChange}
/>
<span className="text-foreground text-base">{t('Hover on segment border to activate')}</span>
</div>
<div className="flex items-center gap-2">
<Switch
checked={segmentLabelEnabled}
onCheckedChange={handleSegmentLabelEnabledChange}
/>
<span className="text-foreground text-base">{t('Show segment name on hover')}</span>
</div>
</div>
);
}
@@ -0,0 +1,64 @@
import React, { useCallback } from 'react';
import { cn, ToolButton } from '@ohif/ui-next';
import { useUIStateStore } from '@ohif/extension-default';
interface SegmentationUtilityButtonProps {
className?: string;
isActive?: boolean;
id: string;
}
/**
* A button that represents a segmentation utility.
* It is implicitly the PopoverTrigger for the options Popover panel that is
* opened in the PanelSegmentation component. It in essence is the PopoverTrigger
* because typically this button has the associated options to display in the Popover.
* Furthermore it also typically has a command for toggling the activeSegmentationUtility
* value in the UIStateStore that triggers the fetching of its options.
* @param props - The props for the button.
* @param props.className - The class name for the button.
* @param props.isActive - Whether the button is active
* @param props.id - The id of the button.
*/
function SegmentationUtilityButton(props: SegmentationUtilityButtonProps) {
const { className, isActive, id } = props;
const activeSegmentationUtility = useUIStateStore(
store => store.uiState.activeSegmentationUtility
);
const toolButtonClassName = cn(
'w-7 h-7 text-primary hover:text-primary hover:!bg-primary/30',
className,
isActive && 'bg-primary/30'
);
const handleMouseDownCapture = useCallback(
event => {
if (activeSegmentationUtility === id) {
// If this active button is clicked, prevent the default Popover
// behaviour of closing the Popover on a pointer/mouse down.
// Not doing this will cause the Popover to close and then reopen again.
// Why? Because propagating this event will cause PanelSegmentation to
// close the Popover by clearing the activeSegmentationUtility. Then
// this button will set the activeSegmentationUtility again and the
// Popover will reopen.
event.preventDefault();
event.stopPropagation();
}
},
[activeSegmentationUtility, id]
);
return (
<div onPointerDownCapture={handleMouseDownCapture}>
<ToolButton
{...props}
className={toolButtonClassName}
/>
</div>
);
}
export default SegmentationUtilityButton;
@@ -1,6 +1,5 @@
import React from 'react';
import { useActiveViewportDisplaySets, useSystem, utils } from '@ohif/core';
// import { AccordionContent, AccordionItem, AccordionTrigger } from '@ohif/ui-next';
import { useActiveViewportDisplaySets, utils } from '@ohif/core';
import { AccordionGroup } from './AccordionGroup';
import MeasurementsOrAdditionalFindings from './MeasurementsOrAdditionalFindings';
@@ -63,8 +62,7 @@ export const groupByStudy = (items, grouping, childProps) => {
export function StudyMeasurements(props): React.ReactNode {
const { items, grouping = {}, children } = props;
const system = useSystem();
const activeDisplaySets = useActiveViewportDisplaySets(system);
const activeDisplaySets = useActiveViewportDisplaySets();
const activeStudyUID = activeDisplaySets?.[0]?.StudyInstanceUID;
return (
@@ -1,9 +1,11 @@
import React from 'react';
import { Button, Icons } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
import { useTranslation } from 'react-i18next';
export function StudyMeasurementsActions({ items, StudyInstanceUID, measurementFilter, actions }) {
const { commandsManager } = useSystem();
const { t } = useTranslation('MeasurementTable');
const disabled = !items?.length;
if (disabled) {
@@ -45,7 +47,7 @@ export function StudyMeasurementsActions({ items, StudyInstanceUID, measurementF
}}
>
<Icons.Add />
Create SR
{t('Create SR')}
</Button>
<Button
size="sm"
@@ -63,7 +65,7 @@ export function StudyMeasurementsActions({ items, StudyInstanceUID, measurementF
}}
>
<Icons.Delete />
Delete
{t('Delete')}
</Button>
</div>
</div>
@@ -14,6 +14,7 @@ import {
Switch,
} from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
import { useTranslation } from 'react-i18next';
import { useViewportDisplaySets } from '../../hooks/useViewportDisplaySets';
import SelectItemWithModality from '../SelectItemWithModality';
@@ -21,6 +22,7 @@ import { useViewportRendering } from '../../hooks';
function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: string }>) {
const { commandsManager, servicesManager } = useSystem();
const { t } = useTranslation();
const [pendingForegrounds, setPendingForegrounds] = useState<string[]>([]);
const [pendingSegmentations, setPendingSegmentations] = useState<string[]>([]);
const { toggleColorbar } = useViewportRendering(viewportId);
@@ -230,7 +232,7 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
disabled={potentialForegroundDisplaySets.length === 0}
>
<Icons.Plus className="h-4 w-4" />
Foreground
{t('Common:Foreground')}
</Button>
<Button
variant="ghost"
@@ -242,7 +244,7 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
dataCY={`AddSegmentationDataOverlay-${viewportId}`}
>
<Icons.Plus className="h-4 w-4" />
Segmentation
{t('Tools:Segmentation')}
</Button>
</div>
@@ -320,7 +322,7 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
onValueChange={value => handlePendingSegmentationSelection(pendingId, value)}
>
<SelectTrigger className="flex-1">
<SelectValue placeholder="SELECT A SEGMENTATION" />
<SelectValue placeholder={t('Common:SELECT A SEGMENTATION')} />
</SelectTrigger>
<SelectContent>
{potentialOverlayDisplaySets.map(item => (
@@ -428,7 +430,7 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri
onValueChange={value => handlePendingForegroundSelection(pendingId, value)}
>
<SelectTrigger className="flex-1">
<SelectValue placeholder="SELECT A FOREGROUND" />
<SelectValue placeholder={t('Common:SELECT A FOREGROUND')} />
</SelectTrigger>
<SelectContent>
{potentialForegroundDisplaySets.map(item => (
@@ -17,9 +17,10 @@ const workerFn = () => {
});
};
const getViewportVolumeHistogram = async (viewport, volume, options?) => {
workerManager.registerWorker('histogram-worker', workerFn, WorkerOptions);
// Register worker once at module load time
workerManager.registerWorker('histogram-worker', workerFn, WorkerOptions);
const getViewportVolumeHistogram = async (viewport, volume, options?) => {
const volumeImageData = viewport.getImageData(volume.volumeId);
if (!volumeImageData) {
@@ -29,8 +30,7 @@ const getViewportVolumeHistogram = async (viewport, volume, options?) => {
let scalarData = volume.scalarData;
if (volume.numTimePoints > 1) {
const targetTimePoint = volume.numTimePoints - 1; // or any other time point you need
scalarData = volume.voxelManager.getTimePointScalarData(targetTimePoint);
scalarData = volume.voxelManager.getDimensionGroupScalarData(volume.numTimePoints);
} else {
scalarData = volume.voxelManager.getCompleteScalarDataArray();
}
@@ -7,6 +7,10 @@ import { getViewportVolumeHistogram } from './getViewportVolumeHistogram';
* Gets node opacity from volume actor
*/
export const getNodeOpacity = (volumeActor, nodeIndex) => {
if (!volumeActor) {
return undefined;
}
const volumeOpacity = volumeActor.getProperty().getScalarOpacity(0);
const nodeValue = [];
@@ -19,6 +23,10 @@ export const getNodeOpacity = (volumeActor, nodeIndex) => {
* Checks if the opacity applied to the PET volume follows a specific pattern
*/
export const isPetVolumeWithDefaultOpacity = (volumeId: string, volumeActor) => {
if (!volumeActor) {
return false;
}
const volume = cs3DCache.getVolume(volumeId);
if (!volume || volume.metadata.Modality !== 'PT') {
@@ -59,6 +67,10 @@ export const isPetVolumeWithDefaultOpacity = (volumeId: string, volumeActor) =>
* Checks if volume has constant opacity
*/
export const isVolumeWithConstantOpacity = volumeActor => {
if (!volumeActor) {
return false;
}
const volumeOpacity = volumeActor.getProperty().getScalarOpacity(0);
const opacitySize = volumeOpacity.getSize();
const firstNodeValue = [];
@@ -91,7 +103,7 @@ export const getWindowLevelsData = async (
const volumeIds = (viewport as Types.IBaseVolumeViewport).getAllVolumeIds();
const viewportProperties = viewport.getProperties();
const { voiRange } = viewportProperties;
const { voiRange } = viewportProperties || {};
const viewportVoi = voiRange
? {
windowWidth: voiRange.upper - voiRange.lower,
@@ -1,9 +1,11 @@
import React, { ReactElement, useCallback } from 'react';
import { Switch } from '@ohif/ui-next';
import { useViewportRendering } from '../../hooks/useViewportRendering';
import { useTranslation } from 'react-i18next';
export function Colorbar({ viewportId }: { viewportId?: string } = {}): ReactElement {
const { hasColorbar, toggleColorbar } = useViewportRendering(viewportId);
const { t } = useTranslation('WindowLevelActionMenu');
const handleToggle = useCallback(() => {
toggleColorbar();
@@ -16,7 +18,7 @@ export function Colorbar({ viewportId }: { viewportId?: string } = {}): ReactEle
className="flex-grow"
onClick={handleToggle}
>
Display Color bar
{t('Display Color bar')}
</span>
<Switch
className="ml-2 flex-shrink-0"
@@ -1,9 +1,11 @@
import React, { ReactElement, useEffect, useRef, useState } from 'react';
import { AllInOneMenu, ScrollArea, Switch, Tabs, TabsList, TabsTrigger } from '@ohif/ui-next';
import { useViewportRendering } from '../../hooks/useViewportRendering';
import { useTranslation } from 'react-i18next';
export function Colormap({ viewportId }: { viewportId?: string } = {}): ReactElement {
const { viewportDisplaySets } = useViewportRendering(viewportId);
const { t } = useTranslation('WindowLevelActionMenu');
const [activeDisplaySetUID, setActiveDisplaySetUID] = useState<string | undefined>(
viewportDisplaySets?.[0]?.displaySetInstanceUID
@@ -74,7 +76,7 @@ export function Colormap({ viewportId }: { viewportId?: string } = {}): ReactEle
className="hover:bg-accent flex h-8 w-full flex-shrink-0 cursor-pointer items-center px-2 text-base hover:rounded"
onClick={() => setShowPreview(!showPreview)}
>
<span className="flex-shrink-0">Preview in viewport</span>
<span className="flex-shrink-0">{t('Preview in viewport')}</span>
<Switch
className="ml-auto flex-shrink-0"
checked={showPreview}
@@ -96,7 +98,7 @@ export function Colormap({ viewportId }: { viewportId?: string } = {}): ReactEle
{colormaps.map((colormap, index) => (
<AllInOneMenu.Item
key={index}
label={colormap.description}
label={t(`Colormaps:${colormap.Name}`, { defaultValue: colormap.Name })}
useIconSpace={false}
onClick={() => {
setCurrentColormap(colormap);
@@ -2,6 +2,7 @@ import React, { ReactElement, useState, useEffect, useCallback } from 'react';
import { VolumeLightingProps } from '../../types/ViewportPresets';
import { Numeric } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
import { useTranslation } from 'react-i18next';
export function VolumeLighting({ viewportId, hasShade }: VolumeLightingProps): ReactElement {
const { servicesManager, commandsManager } = useSystem();
@@ -11,6 +12,7 @@ export function VolumeLighting({ viewportId, hasShade }: VolumeLightingProps): R
diffuse: null,
specular: null,
});
const { t } = useTranslation('WindowLevelActionMenu');
// Single callback to handle all lighting property changes
const onLightingChange = useCallback(
@@ -45,9 +47,9 @@ export function VolumeLighting({ viewportId, hasShade }: VolumeLightingProps): R
// Configuration for our lighting properties
const lightingProperties = [
{ key: 'ambient', label: 'Ambient' },
{ key: 'diffuse', label: 'Diffuse' },
{ key: 'specular', label: 'Specular' },
{ key: 'ambient', label: t('Ambient') },
{ key: 'diffuse', label: t('Diffuse') },
{ key: 'specular', label: t('Specular') },
];
return (
@@ -5,10 +5,13 @@ import { VolumeShift } from './VolumeShift';
import { VolumeLighting } from './VolumeLighting';
import { VolumeShade } from './VolumeShade';
import { useViewportRendering } from '../../hooks/useViewportRendering';
import { useTranslation } from 'react-i18next';
export function VolumeRenderingOptions({ viewportId }: { viewportId?: string } = {}): ReactElement {
const { volumeRenderingQualityRange } = useViewportRendering(viewportId);
const [hasShade, setShade] = useState(false);
const { t } = useTranslation('WindowLevelActionMenu');
return (
<AllInOneMenu.ItemPanel>
<VolumeRenderingQuality
@@ -17,7 +20,7 @@ export function VolumeRenderingOptions({ viewportId }: { viewportId?: string } =
/>
<VolumeShift viewportId={viewportId} />
<div className="mt-2 flex h-8 !h-[20px] w-full flex-shrink-0 items-center justify-start px-2 text-base">
<div className="text-muted-foreground text-sm">Lighting</div>
<div className="text-muted-foreground text-sm">{t('Lighting')}</div>
</div>
<div className="bg-background mt-1 mb-1 h-px w-full"></div>
<div className="hover:bg-accent flex h-8 w-full flex-shrink-0 items-center px-2 text-base hover:rounded">
@@ -4,17 +4,19 @@ import React, { ReactElement } from 'react';
import { VolumeRenderingPresetsContent } from './VolumeRenderingPresetsContent';
import { useSystem } from '@ohif/core';
import { useViewportRendering } from '../../hooks/useViewportRendering';
import { useTranslation } from 'react-i18next';
export function VolumeRenderingPresets({ viewportId }: { viewportId?: string } = {}): ReactElement {
const { volumeRenderingPresets } = useViewportRendering(viewportId);
const { servicesManager } = useSystem();
const { uiDialogService } = servicesManager.services;
const { t } = useTranslation('WindowLevelActionMenu');
const onClickPresets = () => {
uiDialogService.show({
id: 'volume-rendering-presets',
content: VolumeRenderingPresetsContent,
title: 'Rendering Presets',
title: t('Rendering Presets'),
isDraggable: true,
contentProps: {
presets: volumeRenderingPresets,
@@ -25,7 +27,7 @@ export function VolumeRenderingPresets({ viewportId }: { viewportId?: string } =
return (
<AllInOneMenu.Item
label="Rendering Presets"
label={t('Rendering Presets')}
icon={<Icons.VolumeRendering />}
rightIcon={<Icons.ByName name="action-new-dialog" />}
onClick={onClickPresets}
@@ -3,6 +3,7 @@ import React, { ReactElement, useState, useCallback } from 'react';
import { PresetDialog } from '@ohif/ui-next';
import { ViewportPreset, VolumeRenderingPresetsContentProps } from '../../types/ViewportPresets';
import { useSystem } from '@ohif/core';
import { useTranslation } from 'react-i18next';
interface Props extends VolumeRenderingPresetsContentProps {
hide: () => void;
@@ -12,6 +13,7 @@ export function VolumeRenderingPresetsContent({ presets, viewportId, hide }: Pro
const { commandsManager } = useSystem();
const [searchValue, setSearchValue] = useState('');
const [selectedPreset, setSelectedPreset] = useState<ViewportPreset | null>(null);
const { t } = useTranslation('WindowLevelActionMenu');
const handleSearchChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
setSearchValue(event.target.value);
@@ -41,7 +43,7 @@ export function VolumeRenderingPresetsContent({ presets, viewportId, hide }: Pro
<PresetDialog.PresetSearch
value={searchValue}
onChange={handleSearchChange}
placeholder="Search all"
placeholder={t('Search all', 'Search all')}
/>
</PresetDialog.PresetFilter>
<PresetDialog.PresetGrid>
@@ -71,7 +73,7 @@ export function VolumeRenderingPresetsContent({ presets, viewportId, hide }: Pro
</PresetDialog.PresetBody>
<FooterAction className="mt-4 flex-shrink-0">
<FooterAction.Right>
<FooterAction.Secondary onClick={hide}>Cancel</FooterAction.Secondary>
<FooterAction.Secondary onClick={hide}>{t('Common:Cancel')}</FooterAction.Secondary>
</FooterAction.Right>
</FooterAction>
</PresetDialog>
@@ -2,6 +2,7 @@ import React, { ReactElement, useCallback, useState, useEffect } from 'react';
import { VolumeRenderingQualityProps } from '../../types/ViewportPresets';
import { Numeric } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
import { useTranslation } from 'react-i18next';
export function VolumeRenderingQuality({
volumeRenderingQualityRange,
@@ -11,6 +12,7 @@ export function VolumeRenderingQuality({
const { cornerstoneViewportService } = servicesManager.services;
const { min, max, step } = volumeRenderingQualityRange;
const [quality, setQuality] = useState(null);
const { t } = useTranslation('WindowLevelActionMenu');
const onChange = useCallback(
(value: number) => {
@@ -51,7 +53,7 @@ export function VolumeRenderingQuality({
onChange={onChange}
>
<div className="flex flex-row items-center">
<Numeric.Label className="w-16">Quality</Numeric.Label>
<Numeric.Label className="w-16">{t('Quality')}</Numeric.Label>
<Numeric.SingleRange sliderClassName="mx-2 flex-grow" />
</div>
</Numeric.Container>
@@ -2,11 +2,13 @@ import React, { ReactElement, useCallback, useEffect, useState } from 'react';
import { Switch } from '@ohif/ui-next';
import { VolumeShadeProps } from '../../types/ViewportPresets';
import { useSystem } from '@ohif/core';
import { useTranslation } from 'react-i18next';
export function VolumeShade({
viewportId,
onClickShade = bool => {},
}: VolumeShadeProps): ReactElement {
const { t } = useTranslation('WindowLevelActionMenu');
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService } = servicesManager.services;
const [shade, setShade] = useState(true);
@@ -29,7 +31,7 @@ export function VolumeShade({
return (
<>
<span className="flex-grow">Shade</span>
<span className="flex-grow">{t('Shade')}</span>
<Switch
className="ml-2 flex-shrink-0"
key={key}
@@ -2,6 +2,7 @@ import React, { ReactElement, useCallback, useEffect, useState, useRef } from 'r
import { VolumeShiftProps } from '../../types/ViewportPresets';
import { Numeric } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
import { useTranslation } from 'react-i18next';
export function VolumeShift({ viewportId }: VolumeShiftProps): ReactElement {
const { servicesManager, commandsManager } = useSystem();
@@ -19,6 +20,7 @@ export function VolumeShift({ viewportId }: VolumeShiftProps): ReactElement {
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
const { actor } = viewport.getActors()[0];
const ofun = actor.getProperty().getScalarOpacity(0);
const { t } = useTranslation('WindowLevelActionMenu');
useEffect(() => {
if (isBlocking) {
@@ -65,7 +67,7 @@ export function VolumeShift({ viewportId }: VolumeShiftProps): ReactElement {
onMouseUp={() => setIsBlocking(false)}
>
<div className="flex flex-row items-center">
<Numeric.Label className="w-16">Shift</Numeric.Label>
<Numeric.Label className="w-16">{t('Shift')}</Numeric.Label>
<Numeric.SingleRange sliderClassName="mx-2 flex-grow" />
</div>
</Numeric.Container>
@@ -2,8 +2,10 @@ import React, { ReactElement, useEffect, useRef, useState } from 'react';
import { AllInOneMenu, ScrollArea, Switch, Tabs, TabsList, TabsTrigger } from '@ohif/ui-next';
import { useViewportRendering } from '../../hooks/useViewportRendering';
import { WindowLevelPreset } from '../../types/WindowLevel';
import { useTranslation } from 'react-i18next';
export function WindowLevel({ viewportId }: { viewportId?: string } = {}): ReactElement {
const { t } = useTranslation('WindowLevelActionMenu');
const { viewportDisplaySets } = useViewportRendering(viewportId);
const [activeDisplaySetUID, setActiveDisplaySetUID] = useState<string | undefined>(
viewportDisplaySets?.[0]?.displaySetInstanceUID
@@ -68,7 +70,7 @@ export function WindowLevel({ viewportId }: { viewportId?: string } = {}): React
className="hover:bg-accent flex h-8 w-full flex-shrink-0 cursor-pointer items-center px-2 text-base hover:rounded"
onClick={() => setShowPreview(!showPreview)}
>
<span className="flex-shrink-0">Preview in viewport</span>
<span className="flex-shrink-0">{t('Preview in viewport')}</span>
<Switch
className="ml-auto flex-shrink-0"
checked={showPreview}
@@ -91,7 +93,7 @@ export function WindowLevel({ viewportId }: { viewportId?: string } = {}): React
{windowLevelPresets.map((preset, index) => (
<AllInOneMenu.Item
key={index}
label={preset.description}
label={t(preset.description)}
secondaryLabel={`${preset.window} / ${preset.level}`}
useIconSpace={false}
onClick={() => {
@@ -7,25 +7,27 @@ import { WindowLevel } from './WindowLevel';
import { VolumeRenderingPresets } from './VolumeRenderingPresets';
import { VolumeRenderingOptions } from './VolumeRenderingOptions';
import { useViewportRendering } from '../../hooks/useViewportRendering';
import i18n from 'i18next';
export type WindowLevelActionMenuProps = {
viewportId: string;
element?: HTMLElement;
align?: 'start' | 'end' | 'center';
side?: 'top' | 'bottom' | 'left' | 'right';
onVisibilityChange?: (isVisible: boolean) => void;
};
export function WindowLevelActionMenu({
viewportId,
element,
align,
side,
onVisibilityChange,
}: WindowLevelActionMenuProps): ReactElement {
return (
<WindowLevelActionMenuContent
viewportId={viewportId}
align={align}
side={side}
onVisibilityChange={onVisibilityChange}
/>
);
}
@@ -34,10 +36,12 @@ export function WindowLevelActionMenuContent({
viewportId,
align,
side,
onVisibilityChange,
}: {
viewportId: string;
align?: string;
side?: string;
onVisibilityChange?: (isVisible: boolean) => void;
}): ReactElement {
const { t } = useTranslation('WindowLevelActionMenu');
// Use a stable key for the menu to avoid infinite re-renders
@@ -58,6 +62,8 @@ export function WindowLevelActionMenuContent({
isVisible={true}
align={align}
side={side}
backLabel={i18n.t('WindowLevelActionMenu:Back to Display Options')}
onVisibilityChange={onVisibilityChange}
>
<AllInOneMenu.ItemPanel>
{!is3DVolume && <Colorbar viewportId={viewportId} />}
@@ -65,7 +71,7 @@ export function WindowLevelActionMenuContent({
{colorbarProperties?.colormaps && !is3DVolume && (
<AllInOneMenu.SubMenu
key="colorLUTPresets"
itemLabel="Color LUT"
itemLabel={t('Color LUT')}
itemIcon="icon-color-lut"
className="flex h-[calc(100%-32px)] flex-col"
>
@@ -86,7 +92,7 @@ export function WindowLevelActionMenuContent({
{volumeRenderingPresets && is3DVolume && <VolumeRenderingPresets viewportId={viewportId} />}
{volumeRenderingQualityRange && is3DVolume && (
<AllInOneMenu.SubMenu itemLabel="Rendering Options">
<AllInOneMenu.SubMenu itemLabel={t('Rendering Options')}>
<VolumeRenderingOptions viewportId={viewportId} />
</AllInOneMenu.SubMenu>
)}
@@ -16,7 +16,6 @@ import { useViewportRendering } from '../../hooks';
export function WindowLevelActionMenuWrapper(
props: withAppTypes<{
viewportId: string;
element?: HTMLElement;
location?: number;
isOpen?: boolean;
onOpen?: () => void;
@@ -28,7 +27,6 @@ export function WindowLevelActionMenuWrapper(
): ReactNode {
const {
viewportId,
element,
location,
isOpen = false,
onOpen,
@@ -123,9 +121,9 @@ export function WindowLevelActionMenuWrapper(
>
<WindowLevelActionMenu
viewportId={viewportIdToUse}
element={element}
align={align}
side={side}
onVisibilityChange={handleOpenChange}
/>
</PopoverContent>
</Popover>
@@ -3,24 +3,21 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuSubContent,
Icons,
useSegmentationTableContext,
useSegmentationExpanded,
} from '@ohif/ui-next';
import { useTranslation } from 'react-i18next';
import { useSystem } from '@ohif/core/src';
import { ExportSegmentationSubMenuItem } from '../components/ExportSegmentationSubMenuItem';
/**
* Custom dropdown menu component for segmentation panel that uses context for data
*/
export const CustomDropdownMenuContent = () => {
const { commandsManager } = useSystem();
const { t } = useTranslation('SegmentationTable');
const { t } = useTranslation('SegmentationPanel');
const {
onSegmentationAdd,
onSegmentationRemoveFromViewport,
@@ -29,6 +26,8 @@ export const CustomDropdownMenuContent = () => {
exportOptions,
activeSegmentation,
activeSegmentationId,
segmentationRepresentationTypes,
disableEditing,
} = useSegmentationTableContext('CustomDropdownMenu');
// Try to get segmentation data from expanded context first, fall back to table context
@@ -47,21 +46,21 @@ export const CustomDropdownMenuContent = () => {
segmentationId = activeSegmentationId;
}
// Determine if export is allowed for this segmentation
if (exportOptions && segmentationId) {
const exportOption = exportOptions.find(opt => opt.segmentationId === segmentationId);
allowExport = exportOption?.isExportable || false;
}
if (!segmentation || !segmentationId) {
return null;
}
// Determine if export is allowed for this segmentation
if (exportOptions) {
const exportOption = exportOptions.find(opt => opt.segmentationId === segmentationId);
allowExport = exportOption?.isExportable || false;
}
const actions = {
storeSegmentation: async segmentationId => {
storeSegmentation: async (segmentationId, modality = 'SEG') => {
commandsManager.run({
commandName: 'storeSegmentation',
commandOptions: { segmentationId },
commandOptions: { segmentationId, modality },
context: 'CORNERSTONE',
});
},
@@ -78,10 +77,19 @@ export const CustomDropdownMenuContent = () => {
return (
<DropdownMenuContent align="start">
<DropdownMenuItem onClick={() => onSegmentationAdd(segmentationId)}>
<Icons.Add className="text-foreground" />
<span className="pl-2">{t('Create New Segmentation')}</span>
</DropdownMenuItem>
{!disableEditing && (
<DropdownMenuItem
onClick={() =>
onSegmentationAdd({
segmentationId,
segmentationRepresentationType: segmentationRepresentationTypes?.[0],
})
}
>
<Icons.Add className="text-foreground" />
<span className="pl-2">{t('Create New Segmentation')}</span>
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
<DropdownMenuLabel>{t('Manage Current Segmentation')}</DropdownMenuLabel>
<DropdownMenuItem onClick={() => onSegmentationRemoveFromViewport(segmentationId)}>
@@ -92,61 +100,12 @@ export const CustomDropdownMenuContent = () => {
<Icons.Rename className="text-foreground" />
<span className="pl-2">{t('Rename')}</span>
</DropdownMenuItem>
<DropdownMenuSub>
<DropdownMenuSubTrigger className="pl-1">
<Icons.Export className="text-foreground" />
<span className="pl-2">{t('Download & Export')}</span>
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent>
<DropdownMenuLabel className="flex items-center pl-0">
<Icons.Download className="h-5 w-5" />
<span className="pl-1">{t('Download')}</span>
</DropdownMenuLabel>
<DropdownMenuItem
onClick={e => {
e.preventDefault();
actions.downloadCSVSegmentationReport(segmentationId);
}}
disabled={!allowExport}
>
{t('CSV Report')}
</DropdownMenuItem>
<DropdownMenuItem
onClick={e => {
e.preventDefault();
actions.onSegmentationDownload(segmentationId);
}}
disabled={!allowExport}
>
{t('DICOM SEG')}
</DropdownMenuItem>
<DropdownMenuItem
onClick={e => {
e.preventDefault();
actions.onSegmentationDownloadRTSS(segmentationId);
}}
disabled={!allowExport}
>
{t('DICOM RTSS')}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuLabel className="flex items-center pl-0">
<Icons.Export className="h-5 w-5" />
<span className="pl-1 pt-1">{t('Export')}</span>
</DropdownMenuLabel>
<DropdownMenuItem
onClick={e => {
e.preventDefault();
actions.storeSegmentation(segmentationId);
}}
disabled={!allowExport}
>
{t('DICOM SEG')}
</DropdownMenuItem>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
<ExportSegmentationSubMenuItem
segmentationId={segmentationId}
segmentationRepresentationType={segmentationRepresentationTypes?.[0]}
allowExport={allowExport}
actions={actions}
/>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => onSegmentationDelete(segmentationId)}>
<Icons.Delete className="text-red-600" />
@@ -19,7 +19,7 @@ export const CustomSegmentStatisticsHeader = ({
}: CustomSegmentStatisticsHeaderProps) => {
const { servicesManager, commandsManager } = useSystem();
const { segmentationService } = servicesManager.services;
const { t } = useTranslation('SegmentationTable');
const { t } = useTranslation('SegmentationPanel');
const segmentation = segmentationService.getSegmentation(segmentationId);
const segment = segmentation.segments[segmentIndex];
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import { ImageModal, FooterAction } from '@ohif/ui-next';
import { useTranslation } from 'react-i18next';
const MAX_TEXTURE_SIZE = 10000;
const DEFAULT_FILENAME = 'image';
@@ -37,6 +37,7 @@ function ViewportDownloadFormNew({
const [showWarningMessage, setShowWarningMessage] = useState(true);
const [filename, setFilename] = useState(DEFAULT_FILENAME);
const [fileType, setFileType] = useState('jpg');
const { t } = useTranslation('CaptureViewportModal');
useEffect(() => {
if (!viewportElement) {
@@ -82,7 +83,7 @@ function ViewportDownloadFormNew({
value={filename}
onChange={e => setFilename(e.target.value)}
>
File name
{t('File name')}
</ImageModal.Filename>
<ImageModal.Filetype
selected={fileType}
@@ -94,6 +95,10 @@ function ViewportDownloadFormNew({
<ImageModal.ImageSize
width={dimensions.width.toString()}
height={dimensions.height.toString()}
widthLabel={t('Width')}
heightLabel={t('Height')}
widthPlaceholder={t('Width')}
heightPlaceholder={t('Height')}
onWidthChange={e => {
onDimensionsChange({
...dimensions,
@@ -109,7 +114,7 @@ function ViewportDownloadFormNew({
maxWidth={MAX_TEXTURE_SIZE.toString()}
maxHeight={MAX_TEXTURE_SIZE.toString()}
>
Image size <span className="text-muted-foreground">px</span>
{t('Image size in pixels')}
</ImageModal.ImageSize>
<ImageModal.SwitchOption
@@ -117,7 +122,7 @@ function ViewportDownloadFormNew({
checked={showAnnotations}
onCheckedChange={onAnnotationsChange}
>
Include annotations
{t('Include annotations')}
</ImageModal.SwitchOption>
{warningState.enabled && (
<ImageModal.SwitchOption
@@ -125,19 +130,21 @@ function ViewportDownloadFormNew({
checked={showWarningMessage}
onCheckedChange={setShowWarningMessage}
>
Include warning message
{t('Include warning message')}
</ImageModal.SwitchOption>
)}
<FooterAction className="mt-2">
<FooterAction.Right>
<FooterAction.Secondary onClick={onClose}>Cancel</FooterAction.Secondary>
<FooterAction.Secondary onClick={onClose}>
{t('Common:Cancel')}
</FooterAction.Secondary>
<FooterAction.Primary
onClick={() => {
onDownload(filename || DEFAULT_FILENAME, fileType);
onClose();
}}
>
Save
{t('Common:Save')}
</FooterAction.Primary>
</FooterAction.Right>
</FooterAction>
@@ -1,6 +1,12 @@
import { CinePlayer } from '@ohif/ui-next';
import DicomUpload from '../components/DicomUpload/DicomUpload';
// Provide a wider default container for the DICOM Upload modal, without
// affecting other dialogs. We attach a static property that WorkList reads.
const DicomUploadWithSize = Object.assign(DicomUpload, {
containerClassName: 'max-w-3xl',
});
export default {
cinePlayer: CinePlayer,
autoCineModalities: ['OT', 'US'],
@@ -11,6 +17,6 @@ export default {
onBeforeDicomStore: ({ dicomDict, measurementData, naturalizedReport }) => {
return dicomDict;
},
dicomUploadComponent: DicomUpload,
dicomUploadComponent: DicomUploadWithSize,
codingValues: {},
};
@@ -1,18 +1,94 @@
import { CustomDropdownMenuContent } from './CustomDropdownMenuContent';
import { CustomSegmentStatisticsHeader } from './CustomSegmentStatisticsHeader';
import React, { useState } from 'react';
import { Switch } from '@ohif/ui-next';
import SegmentationToolConfig from '../components/SegmentationToolConfig';
import React from 'react';
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
export default function getSegmentationPanelCustomization({ commandsManager, servicesManager }) {
const { segmentationService } = servicesManager.services;
let contourRenderFillChangedGlobally = false;
// Listen to when the global CONTOUR type renderFill style property is changed.
const { unsubscribe } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_STYLE_MODIFIED,
({ specifier, style }) => {
if (
specifier.type === SegmentationRepresentations.Contour &&
specifier.segmentationId == null &&
specifier.viewportId == null &&
style.renderFill != null
) {
unsubscribe();
contourRenderFillChangedGlobally = true;
}
}
);
return {
'panelSegmentation.customDropdownMenuContent': CustomDropdownMenuContent,
'panelSegmentation.customSegmentStatisticsHeader': CustomSegmentStatisticsHeader,
'panelSegmentation.disableEditing': false,
'panelSegmentation.showAddSegment': true,
'panelSegmentation.onSegmentationAdd': () => {
'panelSegmentation.onSegmentationAdd': async ({
segmentationRepresentationType = SegmentationRepresentations.Labelmap,
}) => {
const { viewportGridService } = servicesManager.services;
const viewportId = viewportGridService.getState().activeViewportId;
commandsManager.run('createLabelmapForViewport', { viewportId });
if (segmentationRepresentationType === SegmentationRepresentations.Labelmap) {
commandsManager.run('createLabelmapForViewport', { viewportId });
} else if (segmentationRepresentationType === SegmentationRepresentations.Contour) {
const segmentationId = await commandsManager.run('createContourForViewport', {
viewportId,
});
// Override the default (i.e. hydrated RTSTRUCT) style for contours if the global CONTOUR type
// renderFill style property has not been changed.
if (!contourRenderFillChangedGlobally) {
segmentationService.setStyle(
{ segmentationId, type: SegmentationRepresentations.Contour },
{
renderFill: true,
renderFillInactive: true,
},
// Do not merge so that these created contours inherit other type-specific style properties like the fill alpha.
// Merging would otherwise permanently inherit the fill alpha and any inheritance from the type level would be lost.
false
);
}
// If the global CONTOUR type renderFill style property is already set, do not subscribe to the SEGMENTATION_STYLE_MODIFIED event.
if (contourRenderFillChangedGlobally) {
return;
}
// Subscribe to the SEGMENTATION_STYLE_MODIFIED event to listen for changes to the CONTOUR type renderFill style property.
const { unsubscribe } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_STYLE_MODIFIED,
({ specifier, style }) => {
if (
specifier.type === SegmentationRepresentations.Contour &&
specifier.segmentationId == null &&
specifier.viewportId == null &&
style.renderFill != null
) {
// We are here because the renderFill style property is globally being changed for ALL contours.
// When this occurs, the desire is for ALL contours to inherit the property. To make this happen,
// we have to clear the style property that was set for this specific segmentation
// when it was created above.
// We can now also unsubscribe because this change only needs to be made when the global CONTOUR type
// renderFill style property is first changed.
contourRenderFillChangedGlobally = true;
unsubscribe();
segmentationService.setStyle(
{ segmentationId, type: SegmentationRepresentations.Contour },
{},
false
);
}
}
);
}
},
'panelSegmentation.tableMode': 'collapsed',
'panelSegmentation.readableText': {
@@ -33,53 +109,11 @@ export default function getSegmentationPanelCustomization({ commandsManager, ser
lesionGlycolysis: 'Lesion Glycolysis',
center: 'Center',
},
'segmentationToolbox.config': () => {
// Get initial states based on current configuration
const [previewEdits, setPreviewEdits] = useState(false);
const [toggleSegmentEnabled, setToggleSegmentEnabled] = useState(false);
const [useCenterAsSegmentIndex, setUseCenterAsSegmentIndex] = useState(false);
const handlePreviewEditsChange = checked => {
setPreviewEdits(checked);
commandsManager.run('toggleSegmentPreviewEdit', { toggle: checked });
};
const handleToggleSegmentEnabledChange = checked => {
setToggleSegmentEnabled(checked);
commandsManager.run('toggleSegmentSelect', { toggle: checked });
};
const handleUseCenterAsSegmentIndexChange = checked => {
setUseCenterAsSegmentIndex(checked);
commandsManager.run('toggleUseCenterSegmentIndex', { toggle: checked });
};
return (
<div className="bg-muted flex flex-col gap-4 border-b border-b-[2px] border-black px-2 py-3">
<div className="flex items-center gap-2">
<Switch
checked={previewEdits}
onCheckedChange={handlePreviewEditsChange}
/>
<span className="text-base text-white">Preview edits before creating</span>
</div>
<div className="flex items-center gap-2">
<Switch
checked={useCenterAsSegmentIndex}
onCheckedChange={handleUseCenterAsSegmentIndexChange}
/>
<span className="text-base text-white">Use center as segment index</span>
</div>
<div className="flex items-center gap-2">
<Switch
checked={toggleSegmentEnabled}
onCheckedChange={handleToggleSegmentEnabledChange}
/>
<span className="text-base text-white">Hover on segment border to activate</span>
</div>
</div>
);
'labelMapSegmentationToolbox.config': () => {
return <SegmentationToolConfig />;
},
'contourSegmentationToolbox.config': () => {
return <SegmentationToolConfig />;
},
};
}
+45 -13
View File
@@ -1,53 +1,67 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Toolbox } from '@ohif/extension-default';
import PanelSegmentation from './panels/PanelSegmentation';
import ActiveViewportWindowLevel from './components/ActiveViewportWindowLevel';
import PanelMeasurement from './panels/PanelMeasurement';
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
import i18n from '@ohif/i18n';
const getPanelModule = ({ commandsManager, servicesManager, extensionManager }: withAppTypes) => {
const wrappedPanelSegmentation = ({ configuration }) => {
const { toolbarService } = servicesManager.services;
const toolSectionMap = {
[SegmentationRepresentations.Labelmap]: toolbarService.sections.labelMapSegmentationToolbox,
[SegmentationRepresentations.Contour]: toolbarService.sections.contourSegmentationToolbox,
};
const wrappedPanelSegmentation = props => {
return (
<PanelSegmentation
commandsManager={commandsManager}
servicesManager={servicesManager}
extensionManager={extensionManager}
configuration={{
...configuration,
...props?.configuration,
}}
segmentationRepresentationTypes={props?.segmentationRepresentationTypes}
/>
);
};
const wrappedPanelSegmentationNoHeader = ({ configuration }) => {
const wrappedPanelSegmentationNoHeader = props => {
return (
<PanelSegmentation
commandsManager={commandsManager}
servicesManager={servicesManager}
extensionManager={extensionManager}
configuration={{
...configuration,
...props?.configuration,
}}
segmentationRepresentationTypes={props?.segmentationRepresentationTypes}
/>
);
};
const wrappedPanelSegmentationWithTools = ({ configuration }) => {
const { toolbarService } = servicesManager.services;
const wrappedPanelSegmentationWithTools = props => {
const { t } = useTranslation('SegmentationPanel');
const tKey = `${props.segmentationRepresentationTypes?.[0] ?? 'Segmentation'} tools`;
const tValue = t(tKey);
return (
<>
<Toolbox
buttonSectionId={toolbarService.sections.segmentationToolbox}
title="Segmentation Tools"
buttonSectionId={toolSectionMap[props.segmentationRepresentationTypes?.[0]]}
title={tValue}
/>
<PanelSegmentation
commandsManager={commandsManager}
servicesManager={servicesManager}
extensionManager={extensionManager}
configuration={{
...configuration,
...props?.configuration,
}}
segmentationRepresentationTypes={props?.segmentationRepresentationTypes}
/>
</>
);
@@ -82,11 +96,29 @@ const getPanelModule = ({ commandsManager, servicesManager, extensionManager }:
component: wrappedPanelSegmentationNoHeader,
},
{
name: 'panelSegmentationWithTools',
name: 'panelSegmentationWithToolsLabelMap',
iconName: 'tab-segmentation',
iconLabel: 'Segmentation',
label: 'Segmentation',
component: wrappedPanelSegmentationWithTools,
label: i18n.t('SegmentationPanel:Labelmap'),
component: props =>
wrappedPanelSegmentationWithTools({
...props,
segmentationRepresentationTypes: [
SegmentationRepresentations.Labelmap,
SegmentationRepresentations.Surface,
],
}),
},
{
name: 'panelSegmentationWithToolsContour',
iconName: 'tab-contours',
iconLabel: 'Segmentation',
label: i18n.t('SegmentationPanel:Contour'),
component: props =>
wrappedPanelSegmentationWithTools({
...props,
segmentationRepresentationTypes: [SegmentationRepresentations.Contour],
}),
},
];
};
@@ -1,4 +1,5 @@
import { Enums } from '@cornerstonejs/tools';
import i18n from '@ohif/i18n';
import { utils } from '@ohif/ui-next';
import { ViewportDataOverlayMenuWrapper } from './components/ViewportDataOverlaySettingMenu/ViewportDataOverlayMenuWrapper';
import { ViewportOrientationMenuWrapper } from './components/ViewportOrientationMenu/ViewportOrientationMenuWrapper';
@@ -14,7 +15,7 @@ import AdvancedRenderingControls from './components/AdvancedRenderingControls';
const getDisabledState = (disabledText?: string) => ({
disabled: true,
disabledText: disabledText ?? 'Not available on the current viewport',
disabledText: disabledText ?? i18n.t('Buttons:Not available on the current viewport'),
});
export default function getToolbarModule({ servicesManager, extensionManager }: withAppTypes) {
@@ -396,7 +397,9 @@ export default function getToolbarModule({ servicesManager, extensionManager }:
);
if (!hasAnySupportedModality) {
return getDisabledState(disabledText || 'Tool not available for this modality');
return getDisabledState(
disabledText || i18n.t('Buttons:Tool not available for this modality')
);
}
}
},
@@ -1,6 +1,8 @@
import { useState, useEffect } from 'react';
import debounce from 'lodash.debounce';
import { useSystem } from '@ohif/core';
import i18n from '@ohif/i18n';
function mapMeasurementToDisplay(measurement, displaySetService) {
const { referenceSeriesUID } = measurement;
@@ -13,7 +15,8 @@ function mapMeasurementToDisplay(measurement, displaySetService) {
const { findingSites, finding, label: baseLabel, displayText: baseDisplayText } = measurement;
const firstSite = findingSites?.[0];
const label = baseLabel || finding?.text || firstSite?.text || '(empty)';
const label =
baseLabel || finding?.text || firstSite?.text || i18n.t('MeasurementTable:empty');
// Initialize displayText with the structure used in Length.ts and CobbAngle.ts
const displayText = {
@@ -682,7 +682,7 @@ export function useViewportRendering(
const actorEntries = viewport.getActors();
const actorEntry = actorEntries?.find(entry =>
entry.referencedId.includes(activeDisplaySetInstanceUID)
entry.referencedId?.includes(activeDisplaySetInstanceUID)
);
if (!actorEntry) {
@@ -152,15 +152,27 @@ export function useViewportSegmentations({
const representations = segmentationService.getSegmentationRepresentations(viewportId);
const newSegmentationsWithRepresentations = representations.map(representation => {
const segmentation = segmentationService.getSegmentation(representation.segmentationId);
const mappedSegmentation = mapSegmentationToDisplay(segmentation, customizationService);
return {
representation,
segmentation: mappedSegmentation,
};
// Deduplicate representations by segmentationId to prevent showing
// the same segmentation multiple times in the panel when it has
// multiple representation types (e.g., labelmap and surface)
const uniqueSegmentationMap = new Map();
representations.forEach(representation => {
if (!uniqueSegmentationMap.has(representation.segmentationId)) {
uniqueSegmentationMap.set(representation.segmentationId, representation);
}
});
const newSegmentationsWithRepresentations = Array.from(uniqueSegmentationMap.values()).map(
representation => {
const segmentation = segmentationService.getSegmentation(representation.segmentationId);
const mappedSegmentation = mapSegmentationToDisplay(segmentation, customizationService);
return {
representation,
segmentation: mappedSegmentation,
};
}
);
setSegmentationsWithRepresentations({
segmentationsWithRepresentations: newSegmentationsWithRepresentations,
disabled: false,
+2 -2
View File
@@ -1,9 +1,9 @@
import { HYDRATE_SEG_SYNC_GROUP, VOI_SYNC_GROUP } from './mpr';
import i18n from 'i18next';
export const fourUp = {
id: 'fourUp',
locked: true,
name: '3D four up',
name: i18n.t('Hps:3D four up'),
icon: 'layout-advanced-3d-four-up',
isPreset: true,
createdDate: '2023-03-15T10:29:44.894Z',
+3 -3
View File
@@ -1,9 +1,9 @@
import { Types } from '@ohif/core';
import i18n from 'i18next';
const frameView: Types.HangingProtocol.Protocol = {
id: '@ohif/frameView',
description: 'Frame view for the active series',
name: 'Frame View',
description: i18n.t('Hps:Frame view for the active series'),
name: i18n.t('Hps:Frame View'),
icon: 'tool-stack-scroll',
isPreset: true,
toolGroupIds: ['default'],
+2 -2
View File
@@ -1,9 +1,9 @@
import { HYDRATE_SEG_SYNC_GROUP, VOI_SYNC_GROUP } from './mpr';
import i18n from 'i18next';
export const main3D = {
id: 'main3D',
locked: true,
name: '3D main',
name: i18n.t('Hps:3D main'),
icon: 'layout-advanced-3d-main',
isPreset: true,
createdDate: '2023-03-15T10:29:44.894Z',
+2 -2
View File
@@ -1,5 +1,5 @@
import { Types } from '@ohif/core';
import i18n from 'i18next';
export const VOI_SYNC_GROUP = {
type: 'voi',
id: 'mpr',
@@ -22,7 +22,7 @@ export const HYDRATE_SEG_SYNC_GROUP = {
export const mpr: Types.HangingProtocol.Protocol = {
id: 'mpr',
name: 'MPR',
name: i18n.t('Hps:MPR'),
locked: true,
icon: 'layout-advanced-mpr',
isPreset: true,
@@ -1,9 +1,9 @@
import { HYDRATE_SEG_SYNC_GROUP, VOI_SYNC_GROUP } from './mpr';
import i18n from 'i18next';
export const mprAnd3DVolumeViewport = {
id: 'mprAnd3DVolumeViewport',
locked: true,
name: 'mpr',
name: i18n.t('Hps:mpr'),
createdDate: '2023-03-15T10:29:44.894Z',
modifiedDate: '2023-03-15T10:29:44.894Z',
availableTo: {},
+2 -1
View File
@@ -1,9 +1,10 @@
import { HYDRATE_SEG_SYNC_GROUP, VOI_SYNC_GROUP } from './mpr';
import i18n from 'i18next';
export const only3D = {
id: 'only3D',
locked: true,
name: '3D only',
name: i18n.t('Hps:3D only'),
icon: 'layout-advanced-3d-only',
isPreset: true,
createdDate: '2023-03-15T10:29:44.894Z',
Loaded 100 of 1128 files, more files were not shown because too many files have changed in this diff. Show more