diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 36a22e9794c..08be2afbc7d 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -51,28 +51,29 @@ body: validations: required: true - - type: input - id: os - 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 + - type: textarea + id: system_info 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 diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 83713b9419a..2e40f9ba83e 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -23,6 +23,8 @@ jobs: - 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 diff --git a/.netlify/build-deploy-preview.sh b/.netlify/build-deploy-preview.sh index 4aafd667fd2..84ab04ebc66 100755 --- a/.netlify/build-deploy-preview.sh +++ b/.netlify/build-deploy-preview.sh @@ -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 diff --git a/.webpack/webpack.base.js b/.webpack/webpack.base.js index ff278678395..28ab4f1e72f 100644 --- a/.webpack/webpack.base.js +++ b/.webpack/webpack.base.js @@ -201,8 +201,6 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => { '@hooks': path.resolve(__dirname, '../platform/app/src/hooks'), '@routes': path.resolve(__dirname, '../platform/app/src/routes'), '@state': path.resolve(__dirname, '../platform/app/src/state'), - 'dicom-microscopy-viewer': - 'dicom-microscopy-viewer/dist/dynamic-import/dicomMicroscopyViewer.min.js', }, // Which directories to search when resolving modules modules: [ diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f387e5742a..2dd07ad4228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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-monorepo-root @@ -11,7 +11,1385 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [3.12.0-beta.135](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.134...v3.12.0-beta.135) (2026-02-05) + + +### Bug Fixes + +* **jump-to-contour-segment:** For contours like those from RSTRUCTs, use generic jumpToSegmentCenter if a segment center is available. ([#5785](https://github.com/OHIF/Viewers/issues/5785)) ([45739b3](https://github.com/OHIF/Viewers/commit/45739b31a2c72cb646c733929f733a8e180a616c)) + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [3.12.0-beta.133](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.132...v3.12.0-beta.133) (2026-02-04) + + +### Bug Fixes + +* **jump-to-label-map:** Use `undefined` for viewportId for arrow navigation. ([#5774](https://github.com/OHIF/Viewers/issues/5774)) ([512aa3b](https://github.com/OHIF/Viewers/commit/512aa3b07f4c783040acd67e3fd2103559a06522)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.131](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.130...v3.12.0-beta.131) (2026-01-30) + + +### Bug Fixes + +* **segmentation:** Use jumpToSegmentNext instead of jumpToSegmentCenter for arrow navigation. ([#5759](https://github.com/OHIF/Viewers/issues/5759)) ([02423c5](https://github.com/OHIF/Viewers/commit/02423c5bf3131006df92c8054994887d1811d821)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.129](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.128...v3.12.0-beta.129) (2026-01-27) + + +### Bug Fixes + +* Handle optional ContentSequence for NUM content items ([#5703](https://github.com/OHIF/Viewers/issues/5703)) ([6dedf55](https://github.com/OHIF/Viewers/commit/6dedf5573e78a5751bed0b79646700acedfc8a38)) + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [3.12.0-beta.127](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.126...v3.12.0-beta.127) (2026-01-22) + + +### Bug Fixes + +* Navigate to contour should work ([#5718](https://github.com/OHIF/Viewers/issues/5718)) ([0fd517d](https://github.com/OHIF/Viewers/commit/0fd517d6ab583652a3667f9af49413648f2755db)) + + + + + +# [3.12.0-beta.126](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.125...v3.12.0-beta.126) (2026-01-22) + + +### Bug Fixes + +* the null value check issue in image spacing calculation ([#5726](https://github.com/OHIF/Viewers/issues/5726)) ([afe184e](https://github.com/OHIF/Viewers/commit/afe184ec78dcc8f780e64687e06670365beeb77e)) + + + + + +# [3.12.0-beta.125](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.124...v3.12.0-beta.125) (2026-01-21) + + +### Bug Fixes + +* **security:** Ignore CVE-2026-23745, CVE-2026-23950 and CVE-2024-28863 because they are limited to build/dev environments. ([#5733](https://github.com/OHIF/Viewers/issues/5733)) ([320f0a7](https://github.com/OHIF/Viewers/commit/320f0a7463ad95251842ece6cd329bba7b6b2a97)) + + + + + +# [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) + + +### Bug Fixes + +* **window-level-action-menu:** Window level menu no longer needs two clicks to open after it is used ([#5711](https://github.com/OHIF/Viewers/issues/5711)) ([eb6b814](https://github.com/OHIF/Viewers/commit/eb6b814fc9c35feb6aa5f4378632b53202b0d8a3)) + + + + + +# [3.12.0-beta.122](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.121...v3.12.0-beta.122) (2026-01-13) + + +### Bug Fixes + +* **segmentation:** List surface representations in the segmentation table for 3D views. ([#5700](https://github.com/OHIF/Viewers/issues/5700)) ([82e6a18](https://github.com/OHIF/Viewers/commit/82e6a18735b2e2278d6b95037d82bd3cb529104d)) + + + + + +# [3.12.0-beta.121](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.120...v3.12.0-beta.121) (2026-01-12) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix a segmentation exception after capture. ([#5701](https://github.com/OHIF/Viewers/issues/5701)) ([aba4855](https://github.com/OHIF/Viewers/commit/aba48552765d03cf82e6a92cdd443b246560c4fd)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.119](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.118...v3.12.0-beta.119) (2026-01-08) + + +### Bug Fixes + +* **DicomTagBrowser:** Check for null metadata. ([#5696](https://github.com/OHIF/Viewers/issues/5696)) ([6429a55](https://github.com/OHIF/Viewers/commit/6429a5518a56f7fbd9d948510c52e0d467c9ca57)) + + + + + +# [3.12.0-beta.118](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.117...v3.12.0-beta.118) (2026-01-07) + + +### Bug Fixes + +* **segmentation:** Use active segmentation for navigation. ([#5692](https://github.com/OHIF/Viewers/issues/5692)) ([ecc74a5](https://github.com/OHIF/Viewers/commit/ecc74a53e097b13851ea8f7be0cabab6b0d82b7f)) + + + + + +# [3.12.0-beta.117](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.116...v3.12.0-beta.117) (2026-01-06) + + +### Bug Fixes + +* **capture:** Ensure download filename has the correct extension. ([#5691](https://github.com/OHIF/Viewers/issues/5691)) ([38a8406](https://github.com/OHIF/Viewers/commit/38a8406a66a5d37c403d1dbd09d7e1a62affff15)) + + + + + +# [3.12.0-beta.116](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.115...v3.12.0-beta.116) (2026-01-06) + + +### Bug Fixes + +* NM multiframe with TimeSlotVector ([#5666](https://github.com/OHIF/Viewers/issues/5666)) ([a74648f](https://github.com/OHIF/Viewers/commit/a74648f8ec23c01b91588ff7c6ee1620ca456ed7)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.114](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.113...v3.12.0-beta.114) (2026-01-05) + + +### Bug Fixes + +* **security:** update qs package to fix vulnerability CVE-2025-15284 ([#5686](https://github.com/OHIF/Viewers/issues/5686)) ([ca364a3](https://github.com/OHIF/Viewers/commit/ca364a3af7a29dc98d5c5b8e4c73abebc107c7ce)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.112](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.111...v3.12.0-beta.112) (2025-12-19) + + +### Bug Fixes + +* pass metadata to viewport orientation matching ([#5667](https://github.com/OHIF/Viewers/issues/5667)) ([cd253f2](https://github.com/OHIF/Viewers/commit/cd253f20bde68f33a01ac1fe5e1cf6b987d9d5e2)) + + + + + +# [3.12.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.110...v3.12.0-beta.111) (2025-12-18) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix segment visibility issue. ([#5664](https://github.com/OHIF/Viewers/issues/5664)) ([3440f66](https://github.com/OHIF/Viewers/commit/3440f66ebd0900c0f5b1a5edbdf6f3b5fc72bbcf)) + + + + + +# [3.12.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.109...v3.12.0-beta.110) (2025-12-18) + + +### Bug Fixes + +* prevent annotation from appearing in active viewport when switching series with different Frame of Reference UID ([#5630](https://github.com/OHIF/Viewers/issues/5630)) ([658994c](https://github.com/OHIF/Viewers/commit/658994c854a03203054168dcfd383c0a6e320df0)) + + + + + +# [3.12.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.108...v3.12.0-beta.109) (2025-12-18) + + +### Bug Fixes + +* **segmentation:** When setting the segmentation style and the representation type is not provided, apply the style to all representation types. ([#5662](https://github.com/OHIF/Viewers/issues/5662)) ([f3beb8e](https://github.com/OHIF/Viewers/commit/f3beb8e7115429bdb0e37f4c61491d0b28a2f6b6)) + + + + + +# [3.12.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.107...v3.12.0-beta.108) (2025-12-17) + + +### Bug Fixes + +* Jump to measurement from measurement panel after rotating viewport ([#5090](https://github.com/OHIF/Viewers/issues/5090)) ([ef98516](https://github.com/OHIF/Viewers/commit/ef98516b4fcd76f796bfd5560f98c968e48df645)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16) + + +### Bug Fixes + +* preclinical mode not working ([#5631](https://github.com/OHIF/Viewers/issues/5631)) ([2de81ef](https://github.com/OHIF/Viewers/commit/2de81efa7e6be534972cd763271c038b293d86f0)) + + + + + +# [3.12.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.104...v3.12.0-beta.105) (2025-12-15) + + +### Bug Fixes + +* seg sync for data without FOR ([#5565](https://github.com/OHIF/Viewers/issues/5565)) ([6c628e0](https://github.com/OHIF/Viewers/commit/6c628e09376a0371a84ea55bcecfd25368ee43e8)) + + + + + +# [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-monorepo-root + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.101...v3.12.0-beta.102) (2025-12-10) + + +### Bug Fixes + +* **DataSource:** migrate configuration dialog to ui-next components ([#5626](https://github.com/OHIF/Viewers/issues/5626)) ([c0f39ae](https://github.com/OHIF/Viewers/commit/c0f39aeb51dc5abad6381706eaa0ac69a54979f2)) +* **routes:** display 404 feedback page for unmatched URLs ([#5627](https://github.com/OHIF/Viewers/issues/5627)) ([eef755e](https://github.com/OHIF/Viewers/commit/eef755ef05e38d5271b1c79e4dd191d723895470)) + + + + + +# [3.12.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.100...v3.12.0-beta.101) (2025-12-09) + + +### Bug Fixes + +* **seg-viewport:** add guard for missing reference display set handler to prevent viewport crash ([#5618](https://github.com/OHIF/Viewers/issues/5618)) ([8dde223](https://github.com/OHIF/Viewers/commit/8dde2237a2c1123fc448ae8b68e2f59af376b294)) + + + + + +# [3.12.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.99...v3.12.0-beta.100) (2025-12-05) + + +### Features + +* **notification:** add custom notification component support ([#5605](https://github.com/OHIF/Viewers/issues/5605)) ([c4e5a46](https://github.com/OHIF/Viewers/commit/c4e5a4616db87b39df5e16b75f1ba3c18407468f)) + + + + + +# [3.12.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.98...v3.12.0-beta.99) (2025-12-04) + + +### Bug Fixes + +* Initial sort not consistent ([#5224](https://github.com/OHIF/Viewers/issues/5224)) ([77f9f8e](https://github.com/OHIF/Viewers/commit/77f9f8e1c4af6b5d22ecf9332b5edf503cd5b848)) + + + + + +# [3.12.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.97...v3.12.0-beta.98) (2025-12-03) + + +### Bug Fixes + +* **clipboard:** Adds tooltip clipboard icon feedback when copy fails ([#5609](https://github.com/OHIF/Viewers/issues/5609)) ([6566354](https://github.com/OHIF/Viewers/commit/6566354986583e5d7db01c8734e282faf7a072b9)) + + + + + +# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03) + + +### Bug Fixes + +* skip VOI sync for empty viewports ([#5599](https://github.com/OHIF/Viewers/issues/5599)) ([1aa4431](https://github.com/OHIF/Viewers/commit/1aa443112186851855a638ab974a3060c3e28ea0)) + + + + + +# [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-monorepo-root + + + + + +# [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 ohif-monorepo-root + + + + + +# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25) + + +### Features + +* tooltips for tool settings ([#5586](https://github.com/OHIF/Viewers/issues/5586)) ([411553e](https://github.com/OHIF/Viewers/commit/411553ee916b8a63b2a934a7e1274bc449041016)) + + + + + +# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24) + + +### Bug Fixes + +* **docs:** Empty commit as an attempt to fix the docs build. ([#5582](https://github.com/OHIF/Viewers/issues/5582)) ([94b0358](https://github.com/OHIF/Viewers/commit/94b0358f18f81fa2822a208d68decfce076ef577)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21) + + +### Bug Fixes + +* **DicomUpload:** Switch to use ui-next components and colors ([#5576](https://github.com/OHIF/Viewers/issues/5576)) ([62d6bf4](https://github.com/OHIF/Viewers/commit/62d6bf437b167ac3e60a0f134869c9f96d199d0f)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20) + + +### Bug Fixes + +* Remove dead code and fix rectangle roi rehydration ([#5490](https://github.com/OHIF/Viewers/issues/5490)) ([9ca02b4](https://github.com/OHIF/Viewers/commit/9ca02b4071f77600f7dcd87930fecdcf3d1c249f)) + + + + + +# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19) + + +### Bug Fixes + +* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f)) + + + + + +# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13) + + +### Bug Fixes + +* **segmentation:** Allow for manually added label map and contour segmentations to be used as viewport data overlays. ([#5562](https://github.com/OHIF/Viewers/issues/5562)) ([02f6744](https://github.com/OHIF/Viewers/commit/02f6744dab993f16cf2516fd5f657e0743e3fa42)) + + + + + +# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11) + + +### Bug Fixes + +* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f)) + + + + + +# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10) + + +### Bug Fixes + +* **dev:** update webpack proxy config syntax for Orthanc dev mode ([#5531](https://github.com/OHIF/Viewers/issues/5531)) ([58aa0d1](https://github.com/OHIF/Viewers/commit/58aa0d1eaae2e1a8103e6096864a4e5d7baf090e)) + + + + + +# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06) + + +### Features + +* **metadata:** metadata access improvement ([#5292](https://github.com/OHIF/Viewers/issues/5292)) ([16233d9](https://github.com/OHIF/Viewers/commit/16233d980f17abfc30461b46c6b1888be10c7840)) + + + + + +# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05) + + +### Bug Fixes + +* **segmentation:** When synchronizing a segmentation representation, use the type of the source viewport segmentation to determine the type of the target viewport segmentation ([#5546](https://github.com/OHIF/Viewers/issues/5546)) ([a0556b0](https://github.com/OHIF/Viewers/commit/a0556b08be98e0bba950ad7b9b575d7b971104ba)) + + + + + +# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05) + + +### Bug Fixes + +* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30) + + +### Bug Fixes + +* **security:** Exact versioning and docs dependabot alerts ([#5536](https://github.com/OHIF/Viewers/issues/5536)) ([000e42e](https://github.com/OHIF/Viewers/commit/000e42e9a6eaa4f0878cbf877f03b09a459ad18e)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28) + + +### Bug Fixes + +* **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](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190)) + + + + + +# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27) + + +### Bug Fixes + +* **security:** Addressed dependabot alert CVE-2025-59288 concerning playwright ([#5527](https://github.com/OHIF/Viewers/issues/5527)) ([1f11c83](https://github.com/OHIF/Viewers/commit/1f11c83006bff98cb8434f0662b4de55d5b9bf78)) + + + + + +# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24) + + +### Bug Fixes + +* **security:** For bun, enforce frozen lockfile. For yarn, strongly suggest using frozen lockfile. ([#5508](https://github.com/OHIF/Viewers/issues/5508)) ([1009c60](https://github.com/OHIF/Viewers/commit/1009c6091107d2db0768622120f916208a391343)) + + + + + +# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23) + + +### Features + +* **jpeg2000:** Add 16-bit RGB support to JPEG2000 decoder ([#5519](https://github.com/OHIF/Viewers/issues/5519)) ([a154443](https://github.com/OHIF/Viewers/commit/a1544432db42bf77864bb7df2f757cae819a4b4d)) + + + + + +# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22) + + +### Bug Fixes + +* Fixed the tooltip text of the window button, it was mistakenly written in Chinese in the en-us json ([#5513](https://github.com/OHIF/Viewers/issues/5513)) ([8b9445c](https://github.com/OHIF/Viewers/commit/8b9445c442d0a40b7e4393614313192bd006461b)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21) + + +### Bug Fixes + +* **segmentation:** Fixed the manual scrolling of the segments list and implemented automatic scrolling to the active segment. ([#5510](https://github.com/OHIF/Viewers/issues/5510)) ([1df4f84](https://github.com/OHIF/Viewers/commit/1df4f843fe3c377643938563ff9e2e4f930d8537)) + + + + + +# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20) + + +### Bug Fixes + +* **rendering:** Fix palette color LUT conversion causing black images ([#5509](https://github.com/OHIF/Viewers/issues/5509)) ([ffd9ec3](https://github.com/OHIF/Viewers/commit/ffd9ec32730580e266ffae365a10ac3e524dd6cf)) + + + + + +# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20) + + +### Bug Fixes + +* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404) + + + + + +# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18) + + +### Bug Fixes + +* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462)) + + + + + +# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17) + + +### Bug Fixes + +* **ErrorBoundary:** Allow for details to be shown in production. ([#5504](https://github.com/OHIF/Viewers/issues/5504)) ([4620cc3](https://github.com/OHIF/Viewers/commit/4620cc3acf89f218c3feaaf4c153a3dc8b023b23)) + + + + + +# [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-monorepo-root + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15) + + +### Bug Fixes + +* **docker:** Use bun version 1.2.23 for docker build. ([#5501](https://github.com/OHIF/Viewers/issues/5501)) ([86e924f](https://github.com/OHIF/Viewers/commit/86e924fb88cc146f91c7d826ee071e451de47942)) + + + + + +# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15) + + +### Bug Fixes + +* Segmentation not displayed and unable to draw segments in volume viewport after converting Labelmap to Surface ([#5488](https://github.com/OHIF/Viewers/issues/5488)) ([faea09e](https://github.com/OHIF/Viewers/commit/faea09e7ef2667aad5cef1efc2a6516677ca2915)) + + + + + +# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15) + + +### Bug Fixes + +* **segmentationService:** prevent no representation crash ([#5495](https://github.com/OHIF/Viewers/issues/5495)) ([3137aed](https://github.com/OHIF/Viewers/commit/3137aed0170196a61e16c6dac0c15bcc41691919)) + + + + + +# [3.12.0-beta.61](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.60...v3.12.0-beta.61) (2025-10-15) + + +### Bug Fixes + +* **docs:** Set exact version for glob. ([#5497](https://github.com/OHIF/Viewers/issues/5497)) ([0d2f4e7](https://github.com/OHIF/Viewers/commit/0d2f4e715a941fa458ebf8e64972088580a04952)) + + + + + +# [3.12.0-beta.60](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.59...v3.12.0-beta.60) (2025-10-15) + + +### Bug Fixes + +* **security:** Run bun audit before bun install. ([#5496](https://github.com/OHIF/Viewers/issues/5496)) ([fbd9371](https://github.com/OHIF/Viewers/commit/fbd9371884d0e87376d67eb98c17a4d62e4e6dd1)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.57](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.56...v3.12.0-beta.57) (2025-10-14) + + +### Bug Fixes + +* **bun:** Use fixed bun version ([#5493](https://github.com/OHIF/Viewers/issues/5493)) ([3fe67d5](https://github.com/OHIF/Viewers/commit/3fe67d5c98a22edc32cb0d3de5edc05b722dd68e)) + + + + + +# [3.12.0-beta.56](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.55...v3.12.0-beta.56) (2025-10-10) + + +### Bug Fixes + +* Update Dockerfile ([#5484](https://github.com/OHIF/Viewers/issues/5484)) ([41c0287](https://github.com/OHIF/Viewers/commit/41c028781b80f0ca80f4d6cde3ca8e7958f1baed)) + + + + + +# [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-monorepo-root + + + + + +# [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-monorepo-root + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.52](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.51...v3.12.0-beta.52) (2025-10-07) + + +### Bug Fixes + +* **segmentation:** Segmentation not displayed and unable to draw segments when switching back to volume/stack viewport from 3D viewport ([#5430](https://github.com/OHIF/Viewers/issues/5430)) ([78df8ff](https://github.com/OHIF/Viewers/commit/78df8ff03a371904a390c63a95628d5511dad136)) + + + + + +# [3.12.0-beta.51](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.50...v3.12.0-beta.51) (2025-10-07) + + +### Bug Fixes + +* validation for Percentage of Max SUV input field in TMTV module ([#5417](https://github.com/OHIF/Viewers/issues/5417)) ([5040c94](https://github.com/OHIF/Viewers/commit/5040c947d2f47e5ec66285fa353e3e0ffd127ba7)) + + + + + +# [3.12.0-beta.50](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.49...v3.12.0-beta.50) (2025-10-07) + + +### Bug Fixes + +* **Segmentation:** [Bug [#5420](https://github.com/OHIF/Viewers/issues/5420)] Segmentation color resets after using toggleOneUp ([#5465](https://github.com/OHIF/Viewers/issues/5465)) ([2c6ce21](https://github.com/OHIF/Viewers/commit/2c6ce21c8f79f8b3e6dc47b5aa6f717b6b5cbd2a)) + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.48](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.47...v3.12.0-beta.48) (2025-10-07) + + +### Bug Fixes + +* **measurementTrackingMachine:** remove unreachable states ([#5460](https://github.com/OHIF/Viewers/issues/5460)) ([7f12f39](https://github.com/OHIF/Viewers/commit/7f12f398fb145a31e5557dbee51c0a9b0f40a35b)) + + + + + +# [3.12.0-beta.47](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.46...v3.12.0-beta.47) (2025-10-07) + + +### Bug Fixes + +* **toolbar:** prevent duplicate command execution on repeated interactions ([#5456](https://github.com/OHIF/Viewers/issues/5456)) ([4d04dd5](https://github.com/OHIF/Viewers/commit/4d04dd5651f4cf8dcaf4732baee5e218e92bd3c1)) + + + + + +# [3.12.0-beta.46](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.45...v3.12.0-beta.46) (2025-10-06) + + +### Bug Fixes + +* viewport ready event firing logic ([#5462](https://github.com/OHIF/Viewers/issues/5462)) ([d2ae13b](https://github.com/OHIF/Viewers/commit/d2ae13b03f21e3ab9545d50d7403ec52004bfbfc)) + + + + + +# [3.12.0-beta.45](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.44...v3.12.0-beta.45) (2025-10-06) + + +### Bug Fixes + +* **router:** opt in to React Router v7 future flags ([#5461](https://github.com/OHIF/Viewers/issues/5461)) ([e2964bf](https://github.com/OHIF/Viewers/commit/e2964bf66947d18af06f292d9395b465545fdf9d)) + + + + + +# [3.12.0-beta.44](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.43...v3.12.0-beta.44) (2025-10-06) + + +### Bug Fixes + +* Safely handle missing active viewport ([#5448](https://github.com/OHIF/Viewers/issues/5448)) ([792db26](https://github.com/OHIF/Viewers/commit/792db26ff1591f9e054486f1bee01c422e774101)) + + + + + +# [3.12.0-beta.43](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.42...v3.12.0-beta.43) (2025-10-06) + + +### Features + +* **MeasurementService:** add rendering of unmapped measurements ([#5416](https://github.com/OHIF/Viewers/issues/5416)) ([851e74d](https://github.com/OHIF/Viewers/commit/851e74d7b867a806befb5d85fd71ff9a75e9f2d2)) + + + + + +# [3.12.0-beta.42](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.41...v3.12.0-beta.42) (2025-10-03) + + +### Bug Fixes + +* duplicate segmentation entries on layout switch ([#5458](https://github.com/OHIF/Viewers/issues/5458)) ([9e951e8](https://github.com/OHIF/Viewers/commit/9e951e88005af505ae0c07fbcd3366de12b773dd)) + + + + + +# [3.12.0-beta.41](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.40...v3.12.0-beta.41) (2025-10-03) + + +### Bug Fixes + +* study browser mode for multiple studies ([#5457](https://github.com/OHIF/Viewers/issues/5457)) ([6a1df9d](https://github.com/OHIF/Viewers/commit/6a1df9de4ad3c10a4653f18e03aff6bda4962fd1)) + + + + + +# [3.12.0-beta.40](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.39...v3.12.0-beta.40) (2025-10-02) + + +### Bug Fixes + +* **studyBrowserCustomization:** fix thumbnail menu item actions in study browser ([#5283](https://github.com/OHIF/Viewers/issues/5283)) ([2f28a0d](https://github.com/OHIF/Viewers/commit/2f28a0deab5e43f432bbd9b5989928c9b0450278)) + + + + + +# [3.12.0-beta.39](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.38...v3.12.0-beta.39) (2025-10-01) + + +### Features + +* Add inherit from basic/longitudinal modes ([#5435](https://github.com/OHIF/Viewers/issues/5435)) ([9ad0d7f](https://github.com/OHIF/Viewers/commit/9ad0d7fc8cf34867fa2cfc2bf2b2f63451ec03e5)) + + + + + +# [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-monorepo-root + + + + + +# [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-monorepo-root + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.35](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.34...v3.12.0-beta.35) (2025-09-29) + + +### Bug Fixes + +* **segmentation:** Restored segment label tool in longitudinal mode. ([#5443](https://github.com/OHIF/Viewers/issues/5443)) ([dd86a8e](https://github.com/OHIF/Viewers/commit/dd86a8eabd0712d25e7652a242377fe09597e8d5)) + + + + + +# [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-monorepo-root + + + + + +# [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 ohif-monorepo-root + + + + + +# [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) + + +### Bug Fixes + +* **i18n:** Add and update i18n Translation for Toolbar Tool Names ([#5392](https://github.com/OHIF/Viewers/issues/5392)) ([7783d0f](https://github.com/OHIF/Viewers/commit/7783d0f558bbb7016c29513b6fe76ed069dbd75c)) + + + + + +# [3.12.0-beta.29](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.28...v3.12.0-beta.29) (2025-09-22) + + +### Features + +* **Segmentation:** Segmentation highlight animation function selection ([#5401](https://github.com/OHIF/Viewers/issues/5401)) ([69dbe27](https://github.com/OHIF/Viewers/commit/69dbe2778493cdfc7a8da5e757c95217bb31edc2)) + + + + + +# [3.12.0-beta.28](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.27...v3.12.0-beta.28) (2025-09-19) + + +### Bug Fixes + +* **SegmentationService:** _getSegmentCenter wrongly handling seg center availability ([#5396](https://github.com/OHIF/Viewers/issues/5396)) ([74b82bd](https://github.com/OHIF/Viewers/commit/74b82bd93442411ad68f4872992126c40321eb9e)) + + + + + +# [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-monorepo-root + + + + + +# [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-monorepo-root + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.23...v3.12.0-beta.24) (2025-09-10) + + +### Bug Fixes + +* **ErrorBoundary:** Fixing console errors and Improving error logging for better developer experience ([#5378](https://github.com/OHIF/Viewers/issues/5378)) ([56c5828](https://github.com/OHIF/Viewers/commit/56c5828b02d53bfd17182bb8d0c126b7cd4f400e)) + + + + + +# [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) + + +### Bug Fixes + +* **oidc:** resolve navigation throttling and blank screen during sign-in/sign-out with Keycloak + dcm4chee + Traefik ([#5373](https://github.com/OHIF/Viewers/issues/5373)) ([79473c2](https://github.com/OHIF/Viewers/commit/79473c2ea4196b758614480860a8c596730b4147)) + + + + + +# [3.12.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.20...v3.12.0-beta.21) (2025-08-29) + + +### Bug Fixes + +* routerBasename declaration location ([#5347](https://github.com/OHIF/Viewers/issues/5347)) ([0f6f2bb](https://github.com/OHIF/Viewers/commit/0f6f2bb432d7021d3a92b38b58de51a380d9b0c9)) + + + + + +# [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-monorepo-root + + + + + +# [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-monorepo-root + + + + + +# [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-monorepo-root + + + + + +# [3.12.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.16...v3.12.0-beta.17) (2025-08-28) + + +### Bug Fixes + +* seg preview ([#5338](https://github.com/OHIF/Viewers/issues/5338)) ([d91cc23](https://github.com/OHIF/Viewers/commit/d91cc230e5231ca32ee28736f661a1497f7834c5)) + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + + +### Bug Fixes + +* docker deploy ([#5359](https://github.com/OHIF/Viewers/issues/5359)) ([368a176](https://github.com/OHIF/Viewers/commit/368a176c093aa2ccf4197c46543209d5ff69939a)) + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + + +### Bug Fixes + +* Docker build failure due to missing lerna command ([#5357](https://github.com/OHIF/Viewers/issues/5357)) ([bedb185](https://github.com/OHIF/Viewers/commit/bedb1854f96928f6db5707dce74ea75b1445e9bd)) + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + + +### Bug Fixes + +* local ModalitiesInStudy check ([#5285](https://github.com/OHIF/Viewers/issues/5285)) ([d269404](https://github.com/OHIF/Viewers/commit/d2694048a47faf9028184d0272df12e2602ae674)) + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + + +### Bug Fixes + +* add back clear segmentation representations ([#5336](https://github.com/OHIF/Viewers/issues/5336)) ([21d6ba2](https://github.com/OHIF/Viewers/commit/21d6ba2bb30df5b99bf5728163caff514aaa5dcd)) + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + + +### Bug Fixes + +* Segmentation stats customization module ([#5324](https://github.com/OHIF/Viewers/issues/5324)) ([b9d6f55](https://github.com/OHIF/Viewers/commit/b9d6f558cbe5957f83e6d92305a78662a0b186ed)) + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + + +### Bug Fixes + +* Add seriesInstanceUID fallback for callByRetrieveAETitle ([#5332](https://github.com/OHIF/Viewers/issues/5332)) ([840b5d3](https://github.com/OHIF/Viewers/commit/840b5d37d15537abc38316d636da7c144e6d9223)) + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + + +### Features + +* Jump to measurement moves the camera if it is not in the viewport ([#5023](https://github.com/OHIF/Viewers/issues/5023)) ([6fe9df6](https://github.com/OHIF/Viewers/commit/6fe9df6ec93c0f37e3bb9f8bf6425bae025e75c8)) + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + + +### Bug Fixes + +* header icon logic ([#5339](https://github.com/OHIF/Viewers/issues/5339)) ([a2133ef](https://github.com/OHIF/Viewers/commit/a2133ef6cb6e47fad22908fb1338ee57c67eb8f8)) + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + + +### Features + +* Add WSI bulk data annotations support (ANN) ([#4972](https://github.com/OHIF/Viewers/issues/4972)) ([2ed6818](https://github.com/OHIF/Viewers/commit/2ed681817142b13971bb17b3058be26bbab6b90b)) + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + + +### Bug Fixes + +* load 3dsr ([#5335](https://github.com/OHIF/Viewers/issues/5335)) ([becba78](https://github.com/OHIF/Viewers/commit/becba7861f99ae4e190caddea8b663e60a9883f5)) + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + + +### Features + +* **orientationMarkers:** improved the orientation markers with a simplified version ([#5328](https://github.com/OHIF/Viewers/issues/5328)) ([266b1f5](https://github.com/OHIF/Viewers/commit/266b1f58f01ac6742a9bb40dda28459e7cddc683)) + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + + +### Bug Fixes + +* Update Dockerfile ([#5319](https://github.com/OHIF/Viewers/issues/5319)) ([0e83f45](https://github.com/OHIF/Viewers/commit/0e83f4515c902ffa3dbab2d68ec81b93881e1768)) + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package ohif-monorepo-root + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package ohif-monorepo-root + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + + +### Bug Fixes + +* Guard series.instances in DicomJSONDataSource to prevent TypeError (OHI-2114) ([#5312](https://github.com/OHIF/Viewers/issues/5312)) ([f1d158b](https://github.com/OHIF/Viewers/commit/f1d158bbe81fd9f6052ce23c8588c9c93d76360c)) + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package ohif-monorepo-root + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package ohif-monorepo-root diff --git a/README.md b/README.md index 3d56f4fb101..552bb2c06c7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,313 @@ Due to the peculiarities of the IDC deployment procedure, this repository follow Whenever this repository is updated, the following procedure should be followed: -1. Update `master`. -2. Wait for the dev deploy to be completed (managed via CircleCI in https://app.circleci.com/projects/github/ImagingDataCommons/ViewersV3). -3. Test in IDC dev tier. -4. If test is satisfactory, update `main` to match `master`. + + + + + + + +| | | | +| :-: | :--- | :--- | +| Measurement tracking | Measurement Tracking | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5) | +| Segmentations | Labelmap Segmentations | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.12.2.1107.5.2.32.35162.30000015050317233592200000046) | +| Hanging Protocols | Fusion and Custom Hanging protocols | [Demo](https://viewer.ohif.org/tmtv?StudyInstanceUIDs=1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463) | +| Volume Rendering | Volume Rendering | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&hangingprotocolId=mprAnd3DVolumeViewport) | +| PDF | PDF | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=2.25.317377619501274872606137091638706705333) | +| RTSTRUCT | RT STRUCT | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.5962.99.1.2968617883.1314880426.1493322302363.3.0) | +| 4D | 4D | [Demo](https://viewer.ohif.org/dynamic-volume?StudyInstanceUIDs=2.25.232704420736447710317909004159492840763) | +| VIDEO | Video | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=2.25.96975534054447904995905761963464388233) | +| microscopy | Slide Microscopy | [Demo](https://viewer.ohif.org/microscopy?StudyInstanceUIDs=2.25.141277760791347900862109212450152067508) | + +## About + +The OHIF Viewer can retrieve +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. + +Almost everything offers some degree of customization and configuration. If it +doesn't support something you need, we accept pull requests and have an ever +improving Extension System. + +## Why Choose Us + +### Community & Experience + +The OHIF Viewer is a collaborative effort that has served as the basis for many +active, production, and FDA Cleared medical imaging viewers. It benefits from +our extensive community's collective experience, and from the sponsored +contributions of individuals, research groups, and commercial organizations. + +### Built to Adapt + +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 its own extension system. The same extensibility +that allows us to offer: + +- 2D and 3D medical image viewing +- Multiplanar Reconstruction (MPR) +- Maximum Intensity Project (MIP) +- Whole slide microscopy viewing +- PDF and Dicom Structured Report rendering +- Segmentation rendering as labelmaps and contours +- User Access Control (UAC) +- Context specific toolbar and side panel content +- and many others + +Can be leveraged by you to customize the viewer for your workflow, and to add +any new functionality you may need (and wish to maintain privately without +forking). + +### Support + +- [Report a Bug 🐛](https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Community%3A+Report+%3Abug%3A%2CAwaiting+Reproduction&projects=&template=bug-report.yml&title=%5BBug%5D+) +- [Request a Feature 🚀](https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Community%3A+Request+%3Ahand%3A&projects=&template=feature-request.yml&title=%5BFeature+Request%5D+) +- [Ask a Question 🤗](community.ohif.org) +- [Slack Channel](https://join.slack.com/t/cornerstonejs/shared_invite/zt-1r8xb2zau-dOxlD6jit3TN0Uwf928w9Q) + +For commercial support, academic collaborations, and answers to common +questions; please use [Get Support](https://ohif.org/get-support/) to contact +us. + + +## Developing + +### Branches + +#### `master` branch - The latest dev (beta) release + +- `master` - The latest dev release + +This is typically where the latest development happens. Code that is in the master branch has passed code reviews and automated tests, but it may not be deemed ready for production. This branch usually contains the most recent changes and features being worked on by the development team. It's often the starting point for creating feature branches (where new features are developed) and hotfix branches (for urgent fixes). + +Each package is tagged with beta version numbers, and published to npm such as `@ohif/ui@3.6.0-beta.1` + +### `release/*` branches - The latest stable releases +Once the `master` branch code reaches a stable, release-ready state, we conduct a comprehensive code review and QA testing. Upon approval, we create a new release branch from `master`. These branches represent the latest stable version considered ready for production. + +For example, `release/3.5` is the branch for version 3.5.0, and `release/3.6` is for version 3.6.0. After each release, we wait a few days to ensure no critical bugs. If any are found, we fix them in the release branch and create a new release with a minor version bump, e.g., 3.5.1 in the `release/3.5` branch. + +Each package is tagged with version numbers and published to npm, such as `@ohif/ui@3.5.0`. Note that `master` is always ahead of the `release` branch. We publish docker builds for both beta and stable releases. + +Here is a schematic representation of our development workflow: + +![alt text](platform/docs/docs/assets/img/github-readme-branches-Jun2024.png) + + + + + +### Requirements + +- [Yarn 1.20.0+](https://yarnpkg.com/en/docs/install) +- [Node 18+](https://nodejs.org/en/) +- Yarn Workspaces should be enabled on your machine: + - `yarn config set workspaces-experimental true` + +### Getting Started + +1. [Fork this repository][how-to-fork] +2. [Clone your forked repository][how-to-clone] + - `git clone https://github.com/YOUR-USERNAME/Viewers.git` +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 --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 + +_From this repository's root directory:_ + +```bash +# Enable Yarn Workspaces +yarn config set workspaces-experimental true + +# Restore dependencies +yarn install --frozen-lockfile +``` + +## Commands + +These commands are available from the root directory. Each project directory +also supports a number of commands that can be found in their respective +`README.md` and `package.json` files. + +| Yarn Commands | Description | +| ---------------------------- | ------------------------------------------------------------- | +| **Develop** | | +| `dev` | Default development experience for Viewer | +| `dev:fast` | Our experimental fast dev mode that uses rsbuild instead of webpack | +| `test:unit` | Jest multi-project test runner; overall coverage | +| **Deploy** | | +| `build`\* | Builds production output for our PWA Viewer | | + +\* - For more information on different builds, check out our [Deploy +Docs][deployment-docs] + +## Project + +The OHIF Medical Image Viewing Platform is maintained as a +[`monorepo`][monorepo]. This means that this repository, instead of containing a +single project, contains many projects. If you explore our project structure, +you'll see the following: + +```bash +. +├── extensions # +│ ├── _example # Skeleton of example extension +│ ├── default # basic set of useful functionalities (datasources, panels, etc) +│ ├── cornerstone # image rendering and tools w/ Cornerstone3D +│ ├── cornerstone-dicom-sr # DICOM Structured Report rendering and export +│ ├── cornerstone-dicom-sr # DICOM Structured Report rendering and export +│ ├── cornerstone-dicom-seg # DICOM Segmentation rendering and export +│ ├── cornerstone-dicom-rt # DICOM RTSTRUCT rendering +│ ├── cornerstone-microscopy # Whole Slide Microscopy rendering +│ ├── dicom-pdf # PDF rendering +│ ├── dicom-video # DICOM RESTful Services +│ ├── measurement-tracking # Longitudinal measurement tracking +│ ├── tmtv # Total Metabolic Tumor Volume (TMTV) calculation +| + +│ +├── modes # +│ ├── _example # Skeleton of example mode +│ ├── basic-dev-mode # Basic development mode +│ ├── longitudinal # Longitudinal mode (measurement tracking) +│ ├── tmtv # Total Metabolic Tumor Volume (TMTV) calculation mode +│ └── microscopy # Whole Slide Microscopy mode +│ +├── platform # +│ ├── core # Business Logic +│ ├── i18n # Internationalization Support +│ ├── ui # React component library +│ ├── docs # Documentation +│ └── viewer # Connects platform and extension projects +│ +├── ... # misc. shared configuration +├── lerna.json # MonoRepo (Lerna) settings +├── package.json # Shared devDependencies and commands +└── README.md # This file +``` + +## Acknowledgments + +To acknowledge the OHIF Viewer in an academic publication, please cite + +> _Open Health Imaging Foundation Viewer: An Extensible Open-Source Framework +> for Building Web-Based Imaging Applications to Support Cancer Research_ +> +> Erik Ziegler, Trinity Urban, Danny Brown, James Petts, Steve D. Pieper, Rob +> Lewis, Chris Hafey, and Gordon J. Harris +> +> _JCO Clinical Cancer Informatics_, no. 4 (2020), 336-345, DOI: +> [10.1200/CCI.19.00131](https://www.doi.org/10.1200/CCI.19.00131) +> +> Open-Access on Pubmed Central: +> https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7259879/ + +or, for v1, please cite: + +> _LesionTracker: Extensible Open-Source Zero-Footprint Web Viewer for Cancer +> Imaging Research and Clinical Trials_ +> +> Trinity Urban, Erik Ziegler, Rob Lewis, Chris Hafey, Cheryl Sadow, Annick D. +> Van den Abbeele and Gordon J. Harris +> +> _Cancer Research_, November 1 2017 (77) (21) e119-e122 DOI: +> [10.1158/0008-5472.CAN-17-0334](https://www.doi.org/10.1158/0008-5472.CAN-17-0334) + +**Note:** If you use or find this repository helpful, please take the time to +star this repository on GitHub. This is an easy way for us to assess adoption +and it can help us obtain future funding for the project. + +This work is supported primarily by the National Institutes of Health, National +Cancer Institute, Informatics Technology for Cancer Research (ITCR) program, +under a +[grant to Dr. Gordon Harris at Massachusetts General Hospital (U24 CA199460)](https://projectreporter.nih.gov/project_info_description.cfm?aid=8971104). + +[NCI Imaging Data Commons (IDC) project](https://imaging.datacommons.cancer.gov/) supported the development of new features and bug fixes marked with ["IDC:priority"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Apriority), +["IDC:candidate"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Acandidate) or ["IDC:collaboration"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Acollaboration). NCI Imaging Data Commons is supported by contract number 19X037Q from +Leidos Biomedical Research under Task Order HHSN26100071 from NCI. [IDC Viewer](https://learn.canceridc.dev/portal/visualization) is a customized version of the OHIF Viewer. + +This project is tested with BrowserStack. Thank you for supporting open-source! + +## License + +MIT © [OHIF](https://github.com/OHIF) + + + + + +[lerna-image]: https://img.shields.io/badge/maintained%20with-lerna-cc00ff.svg +[lerna-url]: https://lerna.js.org/ +[netlify-image]: https://api.netlify.com/api/v1/badges/32708787-c9b0-4634-b50f-7ca41952da77/deploy-status +[netlify-url]: https://app.netlify.com/sites/ohif-dev/deploys +[all-contributors-image]: https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square +[circleci-image]: https://circleci.com/gh/OHIF/Viewers.svg?style=svg +[circleci-url]: https://circleci.com/gh/OHIF/Viewers +[codecov-image]: https://codecov.io/gh/OHIF/Viewers/branch/master/graph/badge.svg +[codecov-url]: https://codecov.io/gh/OHIF/Viewers/branch/master +[prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square +[prettier-url]: https://github.com/prettier/prettier +[semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg +[semantic-url]: https://github.com/semantic-release/semantic-release + +[npm-url]: https://npmjs.org/package/@ohif/app +[npm-downloads-image]: https://img.shields.io/npm/dm/@ohif/app.svg?style=flat-square +[npm-version-image]: https://img.shields.io/npm/v/@ohif/app.svg?style=flat-square +[docker-pulls-img]: https://img.shields.io/docker/pulls/ohif/viewer.svg?style=flat-square +[docker-image-url]: https://hub.docker.com/r/ohif/app +[license-image]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square +[license-url]: LICENSE +[percy-image]: https://percy.io/static/images/percy-badge.svg +[percy-url]: https://percy.io/Open-Health-Imaging-Foundation/OHIF-Viewer + +[monorepo]: https://en.wikipedia.org/wiki/Monorepo +[how-to-fork]: https://help.github.com/en/articles/fork-a-repo +[how-to-clone]: https://help.github.com/en/articles/fork-a-repo#step-2-create-a-local-clone-of-your-fork +[ohif-architecture]: https://docs.ohif.org/architecture/index.html +[ohif-extensions]: https://docs.ohif.org/architecture/index.html +[deployment-docs]: https://docs.ohif.org/deployment/ +[react-url]: https://reactjs.org/ +[pwa-url]: https://developers.google.com/web/progressive-web-apps/ +[ohif-viewer-url]: https://www.npmjs.com/package/@ohif/app +[configuration-url]: https://docs.ohif.org/configuring/ +[extensions-url]: https://docs.ohif.org/extensions/ + +[platform-core]: platform/core/README.md +[core-npm]: https://www.npmjs.com/package/@ohif/core +[platform-i18n]: platform/i18n/README.md +[i18n-npm]: https://www.npmjs.com/package/@ohif/i18n +[platform-ui]: platform/ui/README.md +[ui-npm]: https://www.npmjs.com/package/@ohif/ui +[platform-viewer]: platform/app/README.md +[viewer-npm]: https://www.npmjs.com/package/@ohif/app + +[extension-cornerstone]: extensions/cornerstone/README.md +[cornerstone-npm]: https://www.npmjs.com/package/@ohif/extension-cornerstone +[extension-dicom-html]: extensions/dicom-html/README.md +[html-npm]: https://www.npmjs.com/package/@ohif/extension-dicom-html +[extension-dicom-microscopy]: extensions/dicom-microscopy/README.md +[microscopy-npm]: https://www.npmjs.com/package/@ohif/extension-dicom-microscopy +[extension-dicom-pdf]: extensions/dicom-pdf/README.md +[pdf-npm]: https://www.npmjs.com/package/@ohif/extension-dicom-pdf +[extension-vtk]: extensions/vtk/README.md +[vtk-npm]: https://www.npmjs.com/package/@ohif/extension-vtk + + +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FOHIF%2FViewers.svg?type=large&issueType=license)](https://app.fossa.com/projects/git%2Bgithub.com%2FOHIF%2FViewers?ref=badge_large&issueType=license) diff --git a/addOns/externals/devDependencies/CHANGELOG.md b/addOns/externals/devDependencies/CHANGELOG.md index 64bec2f2ffc..f9e00bebae5 100644 --- a/addOns/externals/devDependencies/CHANGELOG.md +++ b/addOns/externals/devDependencies/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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 @@ -11,7 +11,1096 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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 + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @externals/devDependencies + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @externals/devDependencies diff --git a/addOns/externals/devDependencies/package.json b/addOns/externals/devDependencies/package.json index 67b5841abe0..da75d2de0a7 100644 --- a/addOns/externals/devDependencies/package.json +++ b/addOns/externals/devDependencies/package.json @@ -1,7 +1,7 @@ { "name": "@externals/devDependencies", "description": "External dev dependencies - put dev build dependencies here", - "version": "3.11.1", + "version": "3.12.0", "license": "MIT", "private": true, "engines": { @@ -10,7 +10,7 @@ }, "dependencies": { "@babel/runtime": "7.28.2", - "@kitware/vtk.js": "32.12.0", + "@kitware/vtk.js": "34.15.1", "clsx": "2.1.1", "core-js": "3.45.1", "moment": "2.30.1" diff --git a/addOns/externals/dicom-microscopy-viewer/CHANGELOG.md b/addOns/externals/dicom-microscopy-viewer/CHANGELOG.md index 1e8e2a116c5..11a053d3fd4 100644 --- a/addOns/externals/dicom-microscopy-viewer/CHANGELOG.md +++ b/addOns/externals/dicom-microscopy-viewer/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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 @@ -11,7 +11,1096 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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 + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + + +### Features + +* Add WSI bulk data annotations support (ANN) ([#4972](https://github.com/OHIF/Viewers/issues/4972)) ([2ed6818](https://github.com/OHIF/Viewers/commit/2ed681817142b13971bb17b3058be26bbab6b90b)) + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @externals/dicom-microscopy-viewer + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @externals/dicom-microscopy-viewer diff --git a/addOns/externals/dicom-microscopy-viewer/package.json b/addOns/externals/dicom-microscopy-viewer/package.json index 5cb54670c63..150d849ad31 100644 --- a/addOns/externals/dicom-microscopy-viewer/package.json +++ b/addOns/externals/dicom-microscopy-viewer/package.json @@ -1,9 +1,9 @@ { "name": "@externals/dicom-microscopy-viewer", "description": "External reference to dicom-microscopy-viewer", - "version": "3.11.1", + "version": "3.12.0", "license": "MIT", "dependencies": { - "dicom-microscopy-viewer": "0.46.1" + "dicom-microscopy-viewer": "0.48.17" } } diff --git a/addOns/package.json b/addOns/package.json index 16931f93956..931e844aef3 100644 --- a/addOns/package.json +++ b/addOns/package.json @@ -38,8 +38,8 @@ "resolutions": { "**/@babel/runtime": "7.28.2", "commander": "8.3.0", - "dcmjs": "0.43.1", - "dicomweb-client": ">=0.10.4", + "dcmjs": "0.49.4", + "dicomweb-client": "0.10.4", "nth-check": "2.1.1", "trim-newlines": "5.0.0", "glob-parent": "6.0.2", diff --git a/babel.config.js b/babel.config.js index b7871a95aa8..bcc3570792e 100644 --- a/babel.config.js +++ b/babel.config.js @@ -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: { diff --git a/bun.lock b/bun.lock index 52ad7b53c06..9e3a5185d7c 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,6 @@ { "lockfileVersion": 1, - "configVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "ohif-monorepo-root", @@ -53,10 +53,10 @@ }, "addOns/externals/devDependencies": { "name": "@externals/devDependencies", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", - "@kitware/vtk.js": "32.12.0", + "@kitware/vtk.js": "34.15.1", "clsx": "2.1.1", "core-js": "3.45.1", "moment": "2.30.1", @@ -138,24 +138,25 @@ }, "addOns/externals/dicom-microscopy-viewer": { "name": "@externals/dicom-microscopy-viewer", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { - "dicom-microscopy-viewer": "0.46.1", + "dicom-microscopy-viewer": "0.48.17", }, }, "extensions/cornerstone": { "name": "@ohif/extension-cornerstone", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", - "@cornerstonejs/adapters": "4.5.13", - "@cornerstonejs/ai": "4.5.13", - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/labelmap-interpolation": "4.5.13", - "@cornerstonejs/polymorphic-segmentation": "4.5.13", - "@cornerstonejs/tools": "4.5.13", + "@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", + "@kitware/vtk.js": "34.15.1", "html2canvas": "1.4.1", "lodash.compact": "3.0.1", "lodash.debounce": "4.0.8", @@ -168,12 +169,12 @@ "peerDependencies": { "@cornerstonejs/codec-charls": "1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2", - "@cornerstonejs/codec-openjpeg": "1.2.4", + "@cornerstonejs/codec-openjpeg": "1.3.0", "@cornerstonejs/codec-openjph": "2.4.7", - "@cornerstonejs/dicom-image-loader": "4.5.13", - "@ohif/core": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@cornerstonejs/dicom-image-loader": "4.15.29", + "@ohif/core": "3.12.0-beta.133", + "@ohif/ui": "3.12.0-beta.133", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", @@ -184,86 +185,86 @@ }, "extensions/cornerstone-dicom-pmap": { "name": "@ohif/extension-cornerstone-dicom-pmap", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", - "@cornerstonejs/adapters": "4.5.13", - "@cornerstonejs/core": "4.5.13", - "@kitware/vtk.js": "32.12.0", + "@cornerstonejs/adapters": "4.15.29", + "@cornerstonejs/core": "4.15.29", + "@kitware/vtk.js": "34.15.1", "react-color": "2.19.3", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/i18n": "3.11.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/i18n": "3.12.0-beta.133", "prop-types": "15.8.1", "react": "18.3.1", "react-dom": "18.3.1", "react-i18next": "12.3.1", - "react-router": "6.30.1", - "react-router-dom": "6.30.1", + "react-router": "6.30.3", + "react-router-dom": "6.30.3", }, }, "extensions/cornerstone-dicom-rt": { "name": "@ohif/extension-cornerstone-dicom-rt", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "react-color": "2.19.3", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/i18n": "3.11.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/i18n": "3.12.0-beta.133", "prop-types": "15.8.1", "react": "18.3.1", "react-dom": "18.3.1", "react-i18next": "12.3.1", - "react-router": "6.30.1", - "react-router-dom": "6.30.1", + "react-router": "6.30.3", + "react-router-dom": "6.30.3", }, }, "extensions/cornerstone-dicom-seg": { "name": "@ohif/extension-cornerstone-dicom-seg", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", - "@cornerstonejs/adapters": "4.5.13", - "@cornerstonejs/core": "4.5.13", - "@kitware/vtk.js": "32.12.0", + "@cornerstonejs/adapters": "4.15.29", + "@cornerstonejs/core": "4.15.29", + "@kitware/vtk.js": "34.15.1", "react-color": "2.19.3", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/i18n": "3.11.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/i18n": "3.12.0-beta.133", "prop-types": "15.8.1", "react": "18.3.1", "react-dom": "18.3.1", "react-i18next": "12.3.1", - "react-router": "6.30.1", - "react-router-dom": "6.30.1", + "react-router": "6.30.3", + "react-router-dom": "6.30.3", }, }, "extensions/cornerstone-dicom-sr": { "name": "@ohif/extension-cornerstone-dicom-sr", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", - "@cornerstonejs/adapters": "4.5.13", - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/tools": "4.5.13", + "@cornerstonejs/adapters": "4.15.29", + "@cornerstonejs/core": "4.15.29", + "@cornerstonejs/tools": "4.15.29", "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-measurement-tracking": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-measurement-tracking": "3.12.0-beta.133", + "@ohif/ui": "3.12.0-beta.133", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", @@ -272,20 +273,20 @@ }, "extensions/cornerstone-dynamic-volume": { "name": "@ohif/extension-cornerstone-dynamic-volume", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/tools": "4.5.13", + "@cornerstonejs/core": "4.15.29", + "@cornerstonejs/tools": "4.15.29", "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/i18n": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/i18n": "3.12.0-beta.133", + "@ohif/ui": "3.12.0-beta.133", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", @@ -294,7 +295,7 @@ }, "extensions/default": { "name": "@ohif/extension-default", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/calculate-suv": "1.1.0", @@ -302,9 +303,9 @@ "lodash.uniqby": "4.7.0", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/i18n": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/i18n": "3.12.0-beta.133", + "dcmjs": "0.49.4", "dicomweb-client": "0.10.4", "prop-types": "15.8.1", "react": "18.3.1", @@ -317,40 +318,40 @@ }, "extensions/dicom-microscopy": { "name": "@ohif/extension-dicom-microscopy", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/codec-charls": "1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2", - "@cornerstonejs/codec-openjpeg": "1.2.4", + "@cornerstonejs/codec-openjpeg": "1.3.0", "colormap": "2.3.2", "lodash.debounce": "4.0.8", "mathjs": "12.4.3", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/i18n": "3.11.1", - "@ohif/ui": "3.11.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/i18n": "3.12.0-beta.133", + "@ohif/ui": "3.12.0-beta.133", "prop-types": "15.8.1", "react": "18.3.1", "react-dom": "18.3.1", "react-i18next": "12.3.1", - "react-router": "6.30.1", - "react-router-dom": "6.30.1", + "react-router": "6.30.3", + "react-router-dom": "6.30.3", }, }, "extensions/dicom-pdf": { "name": "@ohif/extension-dicom-pdf", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/ui": "3.12.0-beta.133", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", @@ -359,61 +360,39 @@ }, "extensions/dicom-video": { "name": "@ohif/extension-dicom-video", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/ui": "3.12.0-beta.133", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", "react": "18.3.1", }, }, - "extensions/idc": { - "name": "@ohif/extension-idc", - "version": "0.0.1", - "dependencies": { - "@babel/runtime": "^7.20.13", - }, - "peerDependencies": { - "@ohif/core": "3.10.4", - "@ohif/extension-cornerstone": "3.10.4", - "@ohif/extension-default": "3.10.4", - "@ohif/i18n": "3.10.4", - "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", - "react-window": "^1.8.9", - "webpack": "5.89.0", - "webpack-merge": "^5.7.3", - }, - }, "extensions/measurement-tracking": { "name": "@ohif/extension-measurement-tracking", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", - "@ohif/ui": "3.11.1", + "@ohif/ui": "3.12.0-beta.133", "@xstate/react": "3.2.2", "xstate": "4.38.3", }, "peerDependencies": { - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/tools": "4.5.13", - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/ui": "3.11.1", + "@cornerstonejs/core": "4.15.29", + "@cornerstonejs/tools": "4.15.29", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/ui": "3.12.0-beta.133", "classnames": "2.5.1", - "dcmjs": "0.43.1", + "dcmjs": "0.49.4", "lodash.debounce": "4.0.8", "prop-types": "15.8.1", "react": "18.3.1", @@ -424,15 +403,15 @@ }, "extensions/test-extension": { "name": "@ohif/extension-test", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/ui": "3.12.0-beta.133", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", @@ -441,15 +420,15 @@ }, "extensions/tmtv": { "name": "@ohif/extension-tmtv", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "classnames": "2.5.1", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/ui": "3.12.0-beta.133", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", @@ -458,16 +437,16 @@ }, "extensions/usAnnotation": { "name": "@ohif/extension-ultrasound-pleura-bline", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/tools": "4.5.13", - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/i18n": "3.11.1", - "@ohif/ui-next": "3.11.1", + "@cornerstonejs/core": "4.15.29", + "@cornerstonejs/tools": "4.15.29", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/i18n": "3.12.0-beta.133", + "@ohif/ui-next": "3.12.0-beta.133", }, "devDependencies": { "@babel/core": "7.28.0", @@ -502,15 +481,36 @@ "react": "18.3.1", "react-dom": "18.3.1", "react-i18next": "12.3.1", - "react-router": "6.30.1", - "react-router-dom": "6.30.1", + "react-router": "6.30.3", + "react-router-dom": "6.30.3", + "webpack": "5.95.0", + "webpack-merge": "5.10.0", + }, + }, + "modes/basic": { + "name": "@ohif/mode-basic", + "version": "3.12.0-beta.133", + "dependencies": { + "@babel/runtime": "7.28.2", + }, + "devDependencies": { "webpack": "5.95.0", "webpack-merge": "5.10.0", }, + "peerDependencies": { + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/extension-dicom-pdf": "3.12.0-beta.133", + "@ohif/extension-dicom-video": "3.12.0-beta.133", + }, }, "modes/basic-dev-mode": { "name": "@ohif/mode-basic-dev-mode", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -520,17 +520,17 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-dicom-pdf": "3.11.1", - "@ohif/extension-dicom-video": "3.11.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/extension-dicom-pdf": "3.12.0-beta.133", + "@ohif/extension-dicom-video": "3.12.0-beta.133", }, }, "modes/basic-test-mode": { "name": "@ohif/mode-test", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -540,19 +540,19 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-dicom-pdf": "3.11.1", - "@ohif/extension-dicom-video": "3.11.1", - "@ohif/extension-measurement-tracking": "3.11.1", - "@ohif/extension-test": "3.11.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/extension-dicom-pdf": "3.12.0-beta.133", + "@ohif/extension-dicom-video": "3.12.0-beta.133", + "@ohif/extension-measurement-tracking": "3.12.0-beta.133", + "@ohif/extension-test": "3.12.0-beta.133", }, }, "modes/longitudinal": { "name": "@ohif/mode-longitudinal", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -562,32 +562,33 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-rt": "3.11.1", - "@ohif/extension-cornerstone-dicom-seg": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-dicom-pdf": "3.11.1", - "@ohif/extension-dicom-video": "3.11.1", - "@ohif/extension-measurement-tracking": "3.11.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/extension-dicom-pdf": "3.12.0-beta.133", + "@ohif/extension-dicom-video": "3.12.0-beta.133", + "@ohif/extension-measurement-tracking": "3.12.0-beta.133", + "@ohif/mode-basic": "3.12.0-beta.133", }, }, "modes/microscopy": { "name": "@ohif/mode-microscopy", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-dicom-microscopy": "3.11.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-dicom-microscopy": "3.12.0-beta.133", }, }, "modes/preclinical-4d": { "name": "@ohif/mode-preclinical-4d", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", }, @@ -596,17 +597,17 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-seg": "3.11.1", - "@ohif/extension-cornerstone-dynamic-volume": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-tmtv": "3.11.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dynamic-volume": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/extension-tmtv": "3.12.0-beta.133", }, }, "modes/segmentation": { "name": "@ohif/mode-segmentation", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -638,19 +639,20 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-rt": "3.11.1", - "@ohif/extension-cornerstone-dicom-seg": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-dicom-pdf": "3.11.1", - "@ohif/extension-dicom-video": "3.11.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/extension-dicom-pdf": "3.12.0-beta.133", + "@ohif/extension-dicom-video": "3.12.0-beta.133", + "@ohif/mode-basic": "3.12.0-beta.133", }, }, "modes/tmtv": { "name": "@ohif/mode-tmtv", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "i18next": "17.3.1", @@ -660,25 +662,25 @@ "webpack-merge": "5.10.0", }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-dicom-pdf": "3.11.1", - "@ohif/extension-dicom-video": "3.11.1", - "@ohif/extension-measurement-tracking": "3.11.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/extension-dicom-pdf": "3.12.0-beta.133", + "@ohif/extension-dicom-video": "3.12.0-beta.133", + "@ohif/extension-measurement-tracking": "3.12.0-beta.133", }, }, "modes/usAnnotation": { "name": "@ohif/mode-ultrasound-pleura-bline", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/tools": "4.5.13", - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-ultrasound-pleura-bline": "3.11.1", + "@cornerstonejs/core": "4.15.29", + "@cornerstonejs/tools": "4.15.29", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.133", + "@ohif/extension-ultrasound-pleura-bline": "3.12.0-beta.133", "i18next": "17.3.1", }, "devDependencies": { @@ -710,40 +712,40 @@ }, "platform/app": { "name": "@ohif/app", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/codec-charls": "1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2", - "@cornerstonejs/codec-openjpeg": "1.2.4", + "@cornerstonejs/codec-openjpeg": "1.3.0", "@cornerstonejs/codec-openjph": "2.4.7", - "@cornerstonejs/dicom-image-loader": "4.5.13", + "@cornerstonejs/dicom-image-loader": "4.15.29", "@emotion/serialize": "1.3.3", - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-rt": "3.11.1", - "@ohif/extension-cornerstone-dicom-seg": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-dicom-microscopy": "3.11.1", - "@ohif/extension-dicom-pdf": "3.11.1", - "@ohif/extension-dicom-video": "3.11.1", - "@ohif/extension-test": "3.11.1", - "@ohif/extension-ultrasound-pleura-bline": "3.11.1", - "@ohif/i18n": "3.11.1", - "@ohif/mode-basic-dev-mode": "3.11.1", - "@ohif/mode-longitudinal": "3.11.1", - "@ohif/mode-microscopy": "3.11.1", - "@ohif/mode-test": "3.11.1", - "@ohif/mode-ultrasound-pleura-bline": "3.11.1", - "@ohif/ui": "3.11.1", - "@ohif/ui-next": "3.11.1", + "@ohif/core": "3.12.0-beta.133", + "@ohif/extension-cornerstone": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.133", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.133", + "@ohif/extension-default": "3.12.0-beta.133", + "@ohif/extension-dicom-microscopy": "3.12.0-beta.133", + "@ohif/extension-dicom-pdf": "3.12.0-beta.133", + "@ohif/extension-dicom-video": "3.12.0-beta.133", + "@ohif/extension-test": "3.12.0-beta.133", + "@ohif/extension-ultrasound-pleura-bline": "3.12.0-beta.133", + "@ohif/i18n": "3.12.0-beta.133", + "@ohif/mode-basic-dev-mode": "3.12.0-beta.133", + "@ohif/mode-longitudinal": "3.12.0-beta.133", + "@ohif/mode-microscopy": "3.12.0-beta.133", + "@ohif/mode-test": "3.12.0-beta.133", + "@ohif/mode-ultrasound-pleura-bline": "3.12.0-beta.133", + "@ohif/ui": "3.12.0-beta.133", + "@ohif/ui-next": "3.12.0-beta.133", "@svgr/webpack": "8.1.0", "@types/react": "18.3.23", "classnames": "2.5.1", "core-js": "3.45.1", "cornerstone-math": "0.1.10", - "dcmjs": "0.43.1", + "dcmjs": "0.49.4", "detect-gpu": "4.0.50", "dicom-parser": "1.8.21", "dotenv-webpack": "1.8.0", @@ -762,8 +764,8 @@ "react-dropzone": "10.2.2", "react-i18next": "12.3.1", "react-resize-detector": "10.0.1", - "react-router": "6.30.1", - "react-router-dom": "6.30.1", + "react-router": "6.30.3", + "react-router-dom": "6.30.3", "react-shepherd": "6.1.1", "shepherd.js": "13.0.3", "url-loader": "4.1.1", @@ -789,7 +791,7 @@ }, "platform/cli": { "name": "@ohif/cli", - "version": "3.11.1", + "version": "3.12.0-beta.133", "bin": { "ohif-cli": "src/index.js", }, @@ -813,10 +815,10 @@ }, "platform/core": { "name": "@ohif/core", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", - "dcmjs": "0.43.1", + "dcmjs": "0.49.4", "dicomweb-client": "0.10.4", "gl-matrix": "3.4.3", "immutability-helper": "3.1.1", @@ -836,18 +838,18 @@ "peerDependencies": { "@cornerstonejs/codec-charls": "1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2", - "@cornerstonejs/codec-openjpeg": "1.2.4", + "@cornerstonejs/codec-openjpeg": "1.3.0", "@cornerstonejs/codec-openjph": "2.4.7", - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/dicom-image-loader": "4.5.13", - "@ohif/ui": "3.11.1", + "@cornerstonejs/core": "4.15.29", + "@cornerstonejs/dicom-image-loader": "4.15.29", + "@ohif/ui": "3.12.0-beta.133", "cornerstone-math": "0.1.9", "dicom-parser": "1.8.21", }, }, "platform/i18n": { "name": "@ohif/i18n", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@babel/runtime": "7.28.2", "i18next-locize-backend": "2.2.2", @@ -872,7 +874,7 @@ }, "platform/ui": { "name": "@ohif/ui", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@testing-library/react": "13.4.0", "browser-detect": "0.2.28", @@ -923,7 +925,7 @@ }, "platform/ui-next": { "name": "@ohif/ui-next", - "version": "3.11.1", + "version": "3.12.0-beta.133", "dependencies": { "@radix-ui/react-accordion": "1.2.11", "@radix-ui/react-checkbox": "1.3.2", @@ -968,18 +970,20 @@ }, "overrides": { "@babel/runtime-corejs2": "7.26.10", + "@cornerstonejs/codec-openjpeg": "1.3.0", "axios": "1.12.0", "body-parser": "1.20.3", "commander": "8.3.0", "core-js": "3.45.1", "cross-env": "7.0.3", "cross-spawn": "7.0.6", - "dcmjs": "0.43.1", - "form-data": ">=4.0.4", + "dcmjs": "0.49.4", "glob-parent": "6.0.2", + "node-forge": "1.3.2", "nth-check": "2.1.1", "package-json": "8.1.1", "path-to-regexp": "0.1.12", + "qs": "6.14.1", "rollup": "2.79.2", "tapable": "2.2.2", "trim": "1.0.1", @@ -1226,13 +1230,13 @@ "@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="], - "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], + "@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="], "@bcoe/v8-coverage": ["@bcoe/v8-coverage@0.2.3", "", {}, "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="], - "@cornerstonejs/adapters": ["@cornerstonejs/adapters@4.5.13", "", { "dependencies": { "@babel/runtime-corejs2": "7.26.10", "buffer": "6.0.3", "dcmjs": "0.43.1", "gl-matrix": "3.4.3", "ndarray": "1.0.19" }, "peerDependencies": { "@cornerstonejs/core": "4.5.13", "@cornerstonejs/tools": "4.5.13" } }, "sha512-qWB32zsY4Ets2/si2fUBqHckBZYrdEYSPK/mya3S4+qLdB5KImZZJQYq4aqVg+gRvZc9aHIKVI1jjZCkxdvV0A=="], + "@cornerstonejs/adapters": ["@cornerstonejs/adapters@4.15.29", "", { "dependencies": { "@babel/runtime-corejs2": "7.26.10", "buffer": "6.0.3", "dcmjs": "0.48.0", "gl-matrix": "3.4.3", "ndarray": "1.0.19" }, "peerDependencies": { "@cornerstonejs/core": "4.15.29", "@cornerstonejs/tools": "4.15.29" } }, "sha512-OcG3YtCQ+C7X/f7buK3dsMOc8NyqsgH/LZRT/JbvppUnqhhfyKIpBTtJG+2+roMyLsi3QrsCeI9Eu6KaQgFFvw=="], - "@cornerstonejs/ai": ["@cornerstonejs/ai@4.5.13", "", { "dependencies": { "@babel/runtime-corejs2": "7.26.10", "buffer": "6.0.3", "dcmjs": "0.43.1", "gl-matrix": "3.4.3", "lodash.clonedeep": "4.5.0", "ndarray": "1.0.19", "onnxruntime-common": "1.17.1", "onnxruntime-web": "1.17.1" }, "peerDependencies": { "@cornerstonejs/core": "4.5.13", "@cornerstonejs/tools": "4.5.13" } }, "sha512-5eVrkiUuSWcqc8KOWwXzN/1KwbgVzS/9AZjwZ7GJdFYBmRTOkFVhgKlx86VsXecgzuJ6tUSP72ChyPEDgYuh+g=="], + "@cornerstonejs/ai": ["@cornerstonejs/ai@4.15.29", "", { "dependencies": { "@babel/runtime-corejs2": "7.26.10", "buffer": "6.0.3", "dcmjs": "0.48.0", "gl-matrix": "3.4.3", "lodash.clonedeep": "4.5.0", "ndarray": "1.0.19", "onnxruntime-common": "1.17.1", "onnxruntime-web": "1.17.1" }, "peerDependencies": { "@cornerstonejs/core": "4.15.29", "@cornerstonejs/tools": "4.15.29" } }, "sha512-fTbfAE7HpXES+vPYzhP0eGljmwykGkbY38LymbMsrn7p6DmvKuJIh4sfUzUtIPW+5kMnCiXAEUhi3+oebQZs0g=="], "@cornerstonejs/calculate-suv": ["@cornerstonejs/calculate-suv@1.1.0", "", {}, "sha512-Q9XraiDJif9aMFArD2iEuDO/HXbcRVCqB7KfaHgDrdTTjgDFovS91Psbdim7crypRSvE6dh/+HKeFNHdvNkA6w=="], @@ -1240,19 +1244,19 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": ["@cornerstonejs/codec-libjpeg-turbo-8bit@1.2.2", "", {}, "sha512-aAUMK2958YNpOb/7G6e2/aG7hExTiFTASlMt/v90XA0pRHdWiNg5ny4S5SAju0FbIw4zcMnR0qfY+yW3VG2ivg=="], - "@cornerstonejs/codec-openjpeg": ["@cornerstonejs/codec-openjpeg@1.2.4", "", {}, "sha512-UT2su6xZZnCPSuWf2ldzKa/2+guQ7BGgfBSKqxanggwJHh48gZqIAzekmsLyJHMMK5YDK+ti+fzvVJhBS3Xi/g=="], + "@cornerstonejs/codec-openjpeg": ["@cornerstonejs/codec-openjpeg@1.3.0", "", {}, "sha512-hP8WAZ63AcaDYmHbBVTY04x424AglXsRHrI6VBdW4eTiJ76f0heWrEodT9Sb3sNwazzYuyMOIZNndJPeVSeHcw=="], "@cornerstonejs/codec-openjph": ["@cornerstonejs/codec-openjph@2.4.7", "", {}, "sha512-qvP4q4JDib7mi9r7LqKOwqz7YZ8gjtDX4ZCezeYf8+eb7MBXCz5uXAMeVF3yz9Axw4XiIMdB/pqXkm8tqCl13w=="], - "@cornerstonejs/core": ["@cornerstonejs/core@4.5.13", "", { "dependencies": { "@kitware/vtk.js": "32.12.1", "comlink": "4.4.2", "gl-matrix": "3.4.3", "loglevel": "1.9.2" } }, "sha512-a/cQ16KNxVW32WdIihAjt/ry5SP+O6ffQ8ecf0iNa/cbqyOuvw+R6xSue2ZcTLHm/vC3R7PaocDdAwuvIEVwKw=="], + "@cornerstonejs/core": ["@cornerstonejs/core@4.15.29", "", { "dependencies": { "@kitware/vtk.js": "34.15.1", "comlink": "4.4.2", "gl-matrix": "3.4.3", "loglevel": "1.9.2" } }, "sha512-o+5gXbxtZde1GIULmw+hWwf6KI3eiXCre0U1C372jbovxA66JH2t+2VW1nj0F2JRRnO0FjgT/+DWYCWt0ILGoA=="], - "@cornerstonejs/dicom-image-loader": ["@cornerstonejs/dicom-image-loader@4.5.13", "", { "dependencies": { "@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.7", "comlink": "4.4.2", "dicom-parser": "1.8.21", "jpeg-lossless-decoder-js": "2.1.2", "pako": "2.1.0", "uuid": "9.0.1" }, "peerDependencies": { "@cornerstonejs/core": "4.5.13" } }, "sha512-2u5yiS3gObKMWAeABhBl9OpWK5fSclMIWjYVgF9bAu9lNIksRkbRj6U+gpcftAKKXF0gW+uecRIiBqFgEcr2eQ=="], + "@cornerstonejs/dicom-image-loader": ["@cornerstonejs/dicom-image-loader@4.15.29", "", { "dependencies": { "@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", "comlink": "4.4.2", "dicom-parser": "1.8.21", "jpeg-lossless-decoder-js": "2.1.2", "pako": "2.1.0", "uuid": "9.0.1" }, "peerDependencies": { "@cornerstonejs/core": "4.15.29" } }, "sha512-+R7NoFyVTz2OSKclVQ21dqwgYmFM5q7SZc59YPcxfop7rk2+9H5rRxa04ytKHPjz+N5AlqbLKpJvy/RE10Xcew=="], - "@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@4.5.13", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "4.5.13", "@cornerstonejs/tools": "4.5.13", "@kitware/vtk.js": "32.12.1" } }, "sha512-DbmdJ4jyaJbXaNL3/jVSzA90JHQpV3Q2qsaTTRJoUfVhklgZs64ehyhv5UxR9C5wJoAyiUUGx7zDITX0R0on2w=="], + "@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@4.15.29", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "4.15.29", "@cornerstonejs/tools": "4.15.29", "@kitware/vtk.js": "34.15.1" } }, "sha512-4pZTcZWv88l59HDSd/arlJ/WNcdxf9WZbm4GiwsrOZxKx8vkLivnKA+pjPUu1LugCJjlLMgugEVnBhZCk+hW7w=="], - "@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@4.5.13", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "4.5.13", "@cornerstonejs/tools": "4.5.13", "@kitware/vtk.js": "32.12.1" } }, "sha512-aQTHy2Sz8svoEVJUiK+bbJ9CTPDAly4qScMuzO5hd72MxEzhqcTWACsAIqGP6nlzBSnytQwapB7Xpbmb+N6rwA=="], + "@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@4.15.29", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "4.15.29", "@cornerstonejs/tools": "4.15.29", "@kitware/vtk.js": "34.15.1" } }, "sha512-EMCw2Y+bjtI3qfCAv1VbAumWH8XNokEMA1roI03jLzaCvr0+8Jts9Z3P4kyQmOm60veaad9214yM9IZ41J4zGQ=="], - "@cornerstonejs/tools": ["@cornerstonejs/tools@4.5.13", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "4.4.2", "lodash.get": "4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "4.5.13", "@kitware/vtk.js": "32.12.1", "@types/d3-array": "3.2.1", "@types/d3-interpolate": "3.0.4", "d3-array": "3.2.4", "d3-interpolate": "3.0.1", "gl-matrix": "3.4.3" } }, "sha512-aAzXwhLc/qNMPQFryu+xJaur9IHKdhxZBDcINLwl0nerMgLpPF2vIbFHvGJQNTl2njzo9Z64MJFDMVnB/rdWtQ=="], + "@cornerstonejs/tools": ["@cornerstonejs/tools@4.15.29", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "4.4.2", "lodash.get": "4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "4.15.29", "@kitware/vtk.js": "34.15.1", "@types/d3-array": "3.2.1", "@types/d3-interpolate": "3.0.4", "d3-array": "3.2.4", "d3-interpolate": "3.0.1", "gl-matrix": "3.4.3" } }, "sha512-MG+RPJ8EUyT3tWcHimSoDHOTeodNQAz7vPG+H03BjkbRu2RPsRVQRqSqoWHkyTOgyUv1XTk0JXJTXZQ/I7xcdA=="], "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], @@ -1322,9 +1326,9 @@ "@emotion/weak-memoize": ["@emotion/weak-memoize@0.4.0", "", {}, "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="], - "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="], - "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], "@eslint/eslintrc": ["@eslint/eslintrc@2.1.4", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ=="], @@ -1334,9 +1338,7 @@ "@externals/dicom-microscopy-viewer": ["@externals/dicom-microscopy-viewer@workspace:addOns/externals/dicom-microscopy-viewer"], - "@floating-ui/core": ["@floating-ui/core@1.7.3", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w=="], - - "@floating-ui/dom": ["@floating-ui/dom@1.7.4", "", { "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" } }, "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA=="], + "@floating-ui/core": ["@floating-ui/core@1.7.2", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw=="], "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.6", "", { "dependencies": { "@floating-ui/dom": "^1.7.4" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw=="], @@ -1422,13 +1424,7 @@ "@jsonjoy.com/codegen": ["@jsonjoy.com/codegen@1.0.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g=="], - "@jsonjoy.com/json-pack": ["@jsonjoy.com/json-pack@1.21.0", "", { "dependencies": { "@jsonjoy.com/base64": "^1.1.2", "@jsonjoy.com/buffers": "^1.2.0", "@jsonjoy.com/codegen": "^1.0.0", "@jsonjoy.com/json-pointer": "^1.0.2", "@jsonjoy.com/util": "^1.9.0", "hyperdyperid": "^1.2.0", "thingies": "^2.5.0", "tree-dump": "^1.1.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg=="], - - "@jsonjoy.com/json-pointer": ["@jsonjoy.com/json-pointer@1.0.2", "", { "dependencies": { "@jsonjoy.com/codegen": "^1.0.0", "@jsonjoy.com/util": "^1.9.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg=="], - - "@jsonjoy.com/util": ["@jsonjoy.com/util@1.9.0", "", { "dependencies": { "@jsonjoy.com/buffers": "^1.0.0", "@jsonjoy.com/codegen": "^1.0.0" }, "peerDependencies": { "tslib": "2" } }, "sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ=="], - - "@kitware/vtk.js": ["@kitware/vtk.js@32.12.0", "", { "dependencies": { "@babel/runtime": "7.22.11", "@types/webxr": "^0.5.5", "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "^3.1.3", "fflate": "0.7.3", "gl-matrix": "3.4.3", "globalthis": "1.0.3", "seedrandom": "3.0.5", "shader-loader": "1.3.1", "shelljs": "0.8.5", "spark-md5": "3.0.2", "stream-browserify": "3.0.0", "utif": "3.1.0", "webworker-promise": "0.5.0", "worker-loader": "3.0.8", "xmlbuilder2": "3.0.2" }, "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", "wslink": ">=1.1.0 || ^2.0.0" }, "bin": { "xml2json": "Utilities/XMLConverter/xml2json-cli.js", "vtkDataConverter": "Utilities/DataGenerator/convert-cli.js" } }, "sha512-QKO2BCAtNsl0p62RoA7vJbQ2NAJPyf0upPddOoNC+Qpfa1gPTGDatdyPm2/hLF9mYov6Lom7dEpfSVv8/bekzA=="], + "@kitware/vtk.js": ["@kitware/vtk.js@34.15.1", "", { "dependencies": { "@babel/runtime": "^7.28.2", "@types/webxr": "^0.5.5", "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "^3.1.3", "fflate": "0.7.3", "gl-matrix": "3.4.3", "globalthis": "1.0.3", "seedrandom": "3.0.5", "shader-loader": "1.3.1", "shelljs": "0.8.5", "spark-md5": "3.0.2", "stream-browserify": "3.0.0", "utif": "3.1.0", "webworker-promise": "0.5.0", "worker-loader": "3.0.8", "xmlbuilder2": "3.0.2" }, "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", "wslink": ">=1.1.0 || ^2.0.0" }, "bin": { "vtkDataConverter": "Utilities/DataGenerator/convert-cli.js", "xml2json": "Utilities/XMLConverter/xml2json-cli.js" } }, "sha512-9BH/a++drhclknBKNVzVpcOzDK95nc/OflKpQE8nY+IlaXTB1+Z6WXS6IwkEEUkRRUqSVskcW4ksgWdxihB49w=="], "@leichtgewicht/ip-codec": ["@leichtgewicht/ip-codec@2.0.5", "", {}, "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw=="], @@ -1436,14 +1432,6 @@ "@lerna/create": ["@lerna/create@7.4.2", "", { "dependencies": { "@lerna/child-process": "7.4.2", "@npmcli/run-script": "6.0.2", "@nx/devkit": ">=16.5.1 < 17", "@octokit/plugin-enterprise-rest": "6.0.1", "@octokit/rest": "19.0.11", "byte-size": "8.1.1", "chalk": "4.1.0", "clone-deep": "4.0.1", "cmd-shim": "6.0.1", "columnify": "1.6.0", "conventional-changelog-core": "5.0.1", "conventional-recommended-bump": "7.0.1", "cosmiconfig": "^8.2.0", "dedent": "0.7.0", "execa": "5.0.0", "fs-extra": "^11.1.1", "get-stream": "6.0.0", "git-url-parse": "13.1.0", "glob-parent": "5.1.2", "globby": "11.1.0", "graceful-fs": "4.2.11", "has-unicode": "2.0.1", "ini": "^1.3.8", "init-package-json": "5.0.0", "inquirer": "^8.2.4", "is-ci": "3.0.1", "is-stream": "2.0.0", "js-yaml": "4.1.0", "libnpmpublish": "7.3.0", "load-json-file": "6.2.0", "lodash": "^4.17.21", "make-dir": "4.0.0", "minimatch": "3.0.5", "multimatch": "5.0.0", "node-fetch": "2.6.7", "npm-package-arg": "8.1.1", "npm-packlist": "5.1.1", "npm-registry-fetch": "^14.0.5", "npmlog": "^6.0.2", "nx": ">=16.5.1 < 17", "p-map": "4.0.0", "p-map-series": "2.1.0", "p-queue": "6.6.2", "p-reduce": "^2.1.0", "pacote": "^15.2.0", "pify": "5.0.0", "read-cmd-shim": "4.0.0", "read-package-json": "6.0.4", "resolve-from": "5.0.0", "rimraf": "^4.4.1", "semver": "^7.3.4", "signal-exit": "3.0.7", "slash": "^3.0.0", "ssri": "^9.0.1", "strong-log-transformer": "2.1.0", "tar": "6.1.11", "temp-dir": "1.0.0", "upath": "2.0.1", "uuid": "^9.0.0", "validate-npm-package-license": "^3.0.4", "validate-npm-package-name": "5.0.0", "write-file-atomic": "5.0.1", "write-pkg": "4.0.0", "yargs": "16.2.0", "yargs-parser": "20.2.4" } }, "sha512-1wplFbQ52K8E/unnqB0Tq39Z4e+NEoNrpovEnl6GpsTUrC6WDp8+w0Le2uCBV0hXyemxChduCkLz4/y1H1wTeg=="], - "@mapbox/jsonlint-lines-primitives": ["@mapbox/jsonlint-lines-primitives@2.0.2", "", {}, "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ=="], - - "@mapbox/mapbox-gl-style-spec": ["@mapbox/mapbox-gl-style-spec@13.28.0", "", { "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/point-geometry": "^0.1.0", "@mapbox/unitbezier": "^0.0.0", "csscolorparser": "~1.0.2", "json-stringify-pretty-compact": "^2.0.0", "minimist": "^1.2.6", "rw": "^1.3.3", "sort-object": "^0.3.2" }, "bin": { "gl-style-format": "bin/gl-style-format.js", "gl-style-migrate": "bin/gl-style-migrate.js", "gl-style-validate": "bin/gl-style-validate.js", "gl-style-composite": "bin/gl-style-composite.js" } }, "sha512-B8xM7Fp1nh5kejfIl4SWeY0gtIeewbuRencqO3cJDrCHZpaPg7uY+V8abuR+esMeuOjRl5cLhVTP40v+1ywxbg=="], - - "@mapbox/point-geometry": ["@mapbox/point-geometry@0.1.0", "", {}, "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="], - - "@mapbox/unitbezier": ["@mapbox/unitbezier@0.0.0", "", {}, "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA=="], - "@microsoft/tsdoc": ["@microsoft/tsdoc@0.14.2", "", {}, "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug=="], "@microsoft/tsdoc-config": ["@microsoft/tsdoc-config@0.16.2", "", { "dependencies": { "@microsoft/tsdoc": "0.14.2", "ajv": "~6.12.6", "jju": "~1.4.0", "resolve": "~1.19.0" } }, "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw=="], @@ -1468,7 +1456,7 @@ "@multiformats/sha3": ["@multiformats/sha3@3.0.2", "", { "dependencies": { "js-sha3": "^0.9.1", "multiformats": "^13.0.0" } }, "sha512-fBxODTXa1sOWYB9q6GSFe2HYSVwMEdnPa7c7FgNhr/rMFQ2HGtwmRppTm317HSpGSTUkoTvyKQDNcteJEGU+bg=="], - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" } }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.1", "", { "dependencies": { "@emnapi/core": "^1.4.5", "@emnapi/runtime": "^1.4.5", "@tybys/wasm-util": "^0.10.0" } }, "sha512-KVlQ/jgywZpixGCKMNwxStmmbYEMyokZpCf2YuIChhfJA2uqfAKNEM8INz7zzTo55iEXfBhIIs3VqYyqzDLj8g=="], "@nicolo-ribaudo/eslint-scope-5-internals": ["@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1", "", { "dependencies": { "eslint-scope": "5.1.1" } }, "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg=="], @@ -1584,6 +1572,8 @@ "@ohif/i18n": ["@ohif/i18n@workspace:platform/i18n"], + "@ohif/mode-basic": ["@ohif/mode-basic@workspace:modes/basic"], + "@ohif/mode-basic-dev-mode": ["@ohif/mode-basic-dev-mode@workspace:modes/basic-dev-mode"], "@ohif/mode-longitudinal": ["@ohif/mode-longitudinal@workspace:modes/longitudinal"], @@ -1752,7 +1742,7 @@ "@react-dnd/shallowequal": ["@react-dnd/shallowequal@2.0.0", "", {}, "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg=="], - "@remix-run/router": ["@remix-run/router@1.23.0", "", {}, "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA=="], + "@remix-run/router": ["@remix-run/router@1.23.2", "", {}, "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w=="], "@rollup/plugin-babel": ["@rollup/plugin-babel@5.3.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.10.4", "@rollup/pluginutils": "^3.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0", "@types/babel__core": "^7.1.9", "rollup": "^1.20.0||^2.0.0" }, "optionalPeers": ["@types/babel__core"] }, "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q=="], @@ -1796,7 +1786,7 @@ "@rspack/lite-tapable": ["@rspack/lite-tapable@1.0.1", "", {}, "sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w=="], - "@rspack/plugin-react-refresh": ["@rspack/plugin-react-refresh@1.5.3", "", { "dependencies": { "error-stack-parser": "^2.1.4", "html-entities": "^2.6.0" }, "peerDependencies": { "react-refresh": ">=0.10.0 <1.0.0", "webpack-hot-middleware": "2.x" }, "optionalPeers": ["webpack-hot-middleware"] }, "sha512-VOnQMf3YOHkTqJ0+BJbrYga4tQAWNwoAnkgwRauXB4HOyCc5wLfBs9DcOFla/2usnRT3Sq6CMVhXmdPobwAoTA=="], + "@rspack/plugin-react-refresh": ["@rspack/plugin-react-refresh@1.5.0", "", { "dependencies": { "error-stack-parser": "^2.1.4", "html-entities": "^2.6.0" }, "peerDependencies": { "react-refresh": ">=0.10.0 <1.0.0", "webpack-hot-middleware": "2.x" }, "optionalPeers": ["webpack-hot-middleware"] }, "sha512-pYOmc1mrK8Ui/7VWUgjKt9YqrxFn4woURTgGpFYWwsFvJxmWm05zog4fUbChvErbaBHkx1aA+KHxIvM/6tFODg=="], "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], @@ -1902,7 +1892,7 @@ "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], - "@types/emscripten": ["@types/emscripten@1.41.5", "", {}, "sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q=="], + "@types/emscripten": ["@types/emscripten@1.40.1", "", {}, "sha512-sr53lnYkQNhjHNN0oJDdUm5564biioI5DuOpycufDVK7D3y+GR3oUswe2rlwY1nPNyusHbrJ9WoTyIHl4/Bpwg=="], "@types/eslint": ["@types/eslint@7.29.0", "", { "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng=="], @@ -1912,7 +1902,7 @@ "@types/express": ["@types/express@4.17.25", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "^1" } }, "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw=="], - "@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.7", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg=="], + "@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.6", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A=="], "@types/glob": ["@types/glob@7.2.0", "", { "dependencies": { "@types/minimatch": "*", "@types/node": "*" } }, "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA=="], @@ -1950,7 +1940,7 @@ "@types/node": ["@types/node@20.19.9", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw=="], - "@types/node-forge": ["@types/node-forge@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw=="], + "@types/node-forge": ["@types/node-forge@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-zePQJSW5QkwSHKRApqWCVKeKoSOt4xvEnLENZPjyvm9Ezdf/EyDeJM7jqLzOwjVICQQzvLZ63T55MKdJB5H6ww=="], "@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="], @@ -1964,6 +1954,8 @@ "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], + "@types/rbush": ["@types/rbush@4.0.0", "", {}, "sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ=="], + "@types/react": ["@types/react@18.3.23", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w=="], "@types/react-dom": ["@types/react-dom@18.3.7", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ=="], @@ -2258,7 +2250,7 @@ "babel-plugin-transform-import-meta": ["babel-plugin-transform-import-meta@2.3.3", "", { "dependencies": { "@babel/template": "^7.25.9", "tslib": "^2.8.1" }, "peerDependencies": { "@babel/core": "^7.10.0" } }, "sha512-bbh30qz1m6ZU1ybJoNOhA2zaDvmeXMnGNBMVMDOJ1Fni4+wMBoy/j7MTRVmqAUCIcy54/rEnr9VEBsfcgbpm3Q=="], - "babel-preset-current-node-syntax": ["babel-preset-current-node-syntax@1.2.0", "", { "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0 || ^8.0.0-0" } }, "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg=="], + "babel-preset-current-node-syntax": ["babel-preset-current-node-syntax@1.1.1", "", { "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0 || ^8.0.0-0" } }, "sha512-23fWKohMTvS5s0wwJKycOe0dBdCwQ6+iiLaNR9zy8P13mtFRFM9qLLX6HJX5DL2pi/FNDf3fCQHM4FIMoHH/7w=="], "babel-preset-jest": ["babel-preset-jest@29.6.3", "", { "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA=="], @@ -2380,7 +2372,7 @@ "caniuse-api": ["caniuse-api@3.0.0", "", { "dependencies": { "browserslist": "^4.0.0", "caniuse-lite": "^1.0.0", "lodash.memoize": "^4.1.2", "lodash.uniq": "^4.5.0" } }, "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw=="], - "caniuse-lite": ["caniuse-lite@1.0.30001755", "", {}, "sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA=="], + "caniuse-lite": ["caniuse-lite@1.0.30001727", "", {}, "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q=="], "caseless": ["caseless@0.12.0", "", {}, "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="], @@ -2410,7 +2402,7 @@ "ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], - "cipher-base": ["cipher-base@1.0.7", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.2" } }, "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA=="], + "cipher-base": ["cipher-base@1.0.6", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1" } }, "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw=="], "cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], @@ -2582,8 +2574,6 @@ "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], - "csscolorparser": ["csscolorparser@1.0.3", "", {}, "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w=="], - "cssdb": ["cssdb@7.11.2", "", {}, "sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A=="], "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], @@ -2666,7 +2656,7 @@ "dayjs": ["dayjs@1.11.19", "", {}, "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="], - "dcmjs": ["dcmjs@0.43.1", "", { "dependencies": { "@babel/runtime-corejs3": "^7.22.5", "adm-zip": "^0.5.10", "gl-matrix": "^3.1.0", "lodash.clonedeep": "^4.5.0", "loglevel": "^1.8.1", "ndarray": "^1.0.19", "pako": "^2.0.4" } }, "sha512-7IAB2cs2F8QYINbfhA7PV+IDraqBuHn8N31xJc6HsyNxZXyGUPQRujcq732ACqjLhl7oJi1z8PJJgLdblQtD3Q=="], + "dcmjs": ["dcmjs@0.49.4", "", { "dependencies": { "@babel/runtime-corejs3": "^7.22.5", "adm-zip": "^0.5.10", "gl-matrix": "^3.1.0", "lodash.clonedeep": "^4.5.0", "loglevel": "^1.8.1", "ndarray": "^1.0.19", "pako": "^2.0.4" } }, "sha512-w77Gde5JvLjg37FyGIAsTB+oWZIqkO/5NvBeheEVKy6k9XjBgeGsoAbVAByjuGAFLpGqRbf9iWI0edBq87yu/g=="], "debounce": ["debounce@1.2.1", "", {}, "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="], @@ -2752,7 +2742,7 @@ "detective": ["detective@5.2.1", "", { "dependencies": { "acorn-node": "^1.8.2", "defined": "^1.0.0", "minimist": "^1.2.6" }, "bin": { "detective": "bin/detective.js" } }, "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw=="], - "dicom-microscopy-viewer": ["dicom-microscopy-viewer@0.46.1", "", { "dependencies": { "@cornerstonejs/codec-charls": "^1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.5", "colormap": "^2.3", "dcmjs": "^0.29.8", "dicomicc": "^0.1", "dicomweb-client": "^0.8.4", "image-type": "^4.1", "mathjs": "^11.2", "ol": "^7.1", "uuid": "^9.0" } }, "sha512-QLozX/iM6ZA0TxheHQnTNiLg+RbSVlxYKMNG9qdqV5oNbEDOf+z4/8mDqnAQ8wjlQXE2MbUDX8JRa0LmO9mDTg=="], + "dicom-microscopy-viewer": ["dicom-microscopy-viewer@0.48.17", "", { "dependencies": { "@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", "colormap": "^2.3", "dcmjs": "^0.41.0", "dicomicc": "^0.1", "dicomweb-client": "^0.10.3", "image-type": "^4.1", "mathjs": "^11.2", "ol": "^10.6.0", "uuid": "^9.0" } }, "sha512-lzl9wXSg5skCp93kUj+VFFydt1wSgVFIrNw/BeUsP+Im+csUukPE0l1KJPC3xbJNAfyqTCU1PZ3aYSEdVD7aCg=="], "dicom-parser": ["dicom-parser@1.8.21", "", {}, "sha512-lYCweHQDsC8UFpXErPlg86Px2A8bay0HiUY+wzoG3xv5GzgqVHU3lziwSc/Gzn7VV7y2KeP072SzCviuOoU02w=="], @@ -2818,7 +2808,7 @@ "duplexer": ["duplexer@0.1.2", "", {}, "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="], - "earcut": ["earcut@2.2.4", "", {}, "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ=="], + "earcut": ["earcut@3.0.2", "", {}, "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ=="], "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], @@ -2846,7 +2836,7 @@ "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], - "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], + "enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="], "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], @@ -3050,7 +3040,7 @@ "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], - "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + "follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="], "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], @@ -3058,7 +3048,7 @@ "forever-agent": ["forever-agent@0.6.1", "", {}, "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw=="], - "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], + "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], @@ -3326,7 +3316,7 @@ "ipfs-unixfs": ["ipfs-unixfs@11.2.5", "", { "dependencies": { "protons-runtime": "^5.5.0", "uint8arraylist": "^2.4.8" } }, "sha512-uasYJ0GLPbViaTFsOLnL9YPjX5VmhnqtWRriogAHOe4ApmIi9VAOFBzgDHsUW2ub4pEa/EysbtWk126g2vkU/g=="], - "ipfs-unixfs-exporter": ["ipfs-unixfs-exporter@13.7.3", "", { "dependencies": { "@ipld/dag-cbor": "^9.2.4", "@ipld/dag-json": "^10.2.5", "@ipld/dag-pb": "^4.1.5", "@multiformats/murmur3": "^2.1.8", "hamt-sharding": "^3.0.6", "interface-blockstore": "^5.3.2", "ipfs-unixfs": "^11.0.0", "it-filter": "^3.1.4", "it-last": "^3.0.9", "it-map": "^3.1.4", "it-parallel": "^3.0.13", "it-pipe": "^3.0.1", "it-pushable": "^3.2.3", "multiformats": "^13.3.7", "p-queue": "^8.1.0", "progress-events": "^1.0.1" } }, "sha512-sTFjAEnsPu5irh9rvT1j5mNf7nXnW78x5SJrCIrNZb1UqkXQtNX81RjAnTBShUtZ5ujSOc/yrC9Az8il8NVkKQ=="], + "ipfs-unixfs-exporter": ["ipfs-unixfs-exporter@13.6.6", "", { "dependencies": { "@ipld/dag-cbor": "^9.2.4", "@ipld/dag-json": "^10.2.5", "@ipld/dag-pb": "^4.1.5", "@multiformats/murmur3": "^2.1.8", "hamt-sharding": "^3.0.6", "interface-blockstore": "^5.3.2", "ipfs-unixfs": "^11.0.0", "it-filter": "^3.1.4", "it-last": "^3.0.9", "it-map": "^3.1.4", "it-parallel": "^3.0.13", "it-pipe": "^3.0.1", "it-pushable": "^3.2.3", "multiformats": "^13.3.7", "p-queue": "^8.1.0", "progress-events": "^1.0.1" } }, "sha512-i/U1gU8AIFuuJH4y06SjtpFpFAhBixrMkvZJsurrGregSkoSroBxsxJZDMb64p2TenN+m5397oznINEbOzPQZA=="], "is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="], @@ -3596,8 +3586,6 @@ "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], - "json-stringify-pretty-compact": ["json-stringify-pretty-compact@2.0.0", "", {}, "sha512-WRitRfs6BGq4q8gTgOy4ek7iPFXjbra0H3PmDLKm2xnZ+Gh1HUhiKGgCZkSPNULlP7mvfu6FV/mOLhCarspADQ=="], - "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], @@ -3748,8 +3736,6 @@ "map-obj": ["map-obj@4.3.0", "", {}, "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ=="], - "mapbox-to-css-font": ["mapbox-to-css-font@2.4.5", "", {}, "sha512-VJ6nB8emkO9VODI0Fk+TQ/0zKBTqmf/Pkt8Xv0kHstoc0iXRajA00DAid4Kc3K5xeFIOoiZrVxijEzj0GLVO2w=="], - "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], "material-colors": ["material-colors@1.2.6", "", {}, "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="], @@ -3948,7 +3934,7 @@ "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], - "node-forge": ["node-forge@1.3.1", "", {}, "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="], + "node-forge": ["node-forge@1.3.2", "", {}, "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw=="], "node-gyp": ["node-gyp@9.4.1", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "glob": "^7.1.4", "graceful-fs": "^4.2.6", "make-fetch-happen": "^10.0.3", "nopt": "^6.0.0", "npmlog": "^6.0.0", "rimraf": "^3.0.2", "semver": "^7.3.5", "tar": "^6.1.2", "which": "^2.0.2" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ=="], @@ -4028,9 +4014,7 @@ "oidc-client-ts": ["oidc-client-ts@3.3.0", "", { "dependencies": { "jwt-decode": "^4.0.0" } }, "sha512-t13S540ZwFOEZKLYHJwSfITugupW4uYLwuQSSXyKH/wHwZ+7FvgHE7gnNJh1YQIZ1Yd1hKSRjqeXGSUtS0r9JA=="], - "ol": ["ol@7.5.2", "", { "dependencies": { "earcut": "^2.2.3", "geotiff": "^2.0.7", "ol-mapbox-style": "^10.1.0", "pbf": "3.2.1", "rbush": "^3.0.1" } }, "sha512-HJbb3CxXrksM6ct367LsP3N+uh+iBBMdP3DeGGipdV9YAYTP0vTJzqGnoqQ6C2IW4qf8krw9yuyQbc9fjOIaOQ=="], - - "ol-mapbox-style": ["ol-mapbox-style@10.7.0", "", { "dependencies": { "@mapbox/mapbox-gl-style-spec": "^13.23.1", "mapbox-to-css-font": "^2.4.1", "ol": "^7.3.0" } }, "sha512-S/UdYBuOjrotcR95Iq9AejGYbifKeZE85D9VtH11ryJLQPTZXZSW1J5bIXcr4AlAH6tyjPPHTK34AdkwB32Myw=="], + "ol": ["ol@10.6.1", "", { "dependencies": { "@types/rbush": "4.0.0", "earcut": "^3.0.0", "geotiff": "^2.1.3", "pbf": "4.0.1", "rbush": "^4.0.0" } }, "sha512-xp174YOwPeLj7c7/8TCIEHQ4d41tgTDDhdv6SqNdySsql5/MaFJEJkjlsYcvOPt7xA6vrum/QG4UdJ0iCGT1cg=="], "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], @@ -4138,9 +4122,9 @@ "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], - "pbf": ["pbf@3.2.1", "", { "dependencies": { "ieee754": "^1.1.12", "resolve-protobuf-schema": "^2.1.0" }, "bin": { "pbf": "bin/pbf" } }, "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ=="], + "pbf": ["pbf@4.0.1", "", { "dependencies": { "resolve-protobuf-schema": "^2.1.0" }, "bin": { "pbf": "bin/pbf" } }, "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA=="], - "pbkdf2": ["pbkdf2@3.1.5", "", { "dependencies": { "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "ripemd160": "^2.0.3", "safe-buffer": "^5.2.1", "sha.js": "^2.4.12", "to-buffer": "^1.2.1" } }, "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ=="], + "pbkdf2": ["pbkdf2@3.1.3", "", { "dependencies": { "create-hash": "~1.1.3", "create-hmac": "^1.1.7", "ripemd160": "=2.0.1", "safe-buffer": "^5.2.1", "sha.js": "^2.4.11", "to-buffer": "^1.2.0" } }, "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA=="], "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], @@ -4378,7 +4362,7 @@ "pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], - "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + "qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="], "query-string": ["query-string@6.14.1", "", { "dependencies": { "decode-uri-component": "^0.2.0", "filter-obj": "^1.1.0", "split-on-first": "^1.0.0", "strict-uri-encode": "^2.0.0" } }, "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw=="], @@ -4390,7 +4374,7 @@ "quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="], - "quickselect": ["quickselect@2.0.0", "", {}, "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="], + "quickselect": ["quickselect@3.0.0", "", {}, "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g=="], "rabin-rs": ["rabin-rs@2.1.0", "", {}, "sha512-5y72gAXPzIBsAMHcpxZP8eMDuDT98qMP1BqSDHRbHkJJXEgWIN1lA47LxUqzsK6jknOJtgfkQr9v+7qMlFDm6g=="], @@ -4406,7 +4390,7 @@ "raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="], - "rbush": ["rbush@3.0.1", "", { "dependencies": { "quickselect": "^2.0.0" } }, "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w=="], + "rbush": ["rbush@4.0.1", "", { "dependencies": { "quickselect": "^3.0.0" } }, "sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ=="], "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], @@ -4454,9 +4438,9 @@ "react-resize-detector": ["react-resize-detector@10.0.1", "", { "dependencies": { "lodash": "^4.17.21" }, "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" } }, "sha512-CR2EdP83ycGlWkhhrd6+hhZVhPJO4xnzClFCTBXlODVTHOgiDJQu77sBt67J7P3gfU4ec/kOuf2c5EcyTUNLXQ=="], - "react-router": ["react-router@6.30.1", "", { "dependencies": { "@remix-run/router": "1.23.0" }, "peerDependencies": { "react": ">=16.8" } }, "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ=="], + "react-router": ["react-router@6.30.3", "", { "dependencies": { "@remix-run/router": "1.23.2" }, "peerDependencies": { "react": ">=16.8" } }, "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw=="], - "react-router-dom": ["react-router-dom@6.30.1", "", { "dependencies": { "@remix-run/router": "1.23.0", "react-router": "6.30.1" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw=="], + "react-router-dom": ["react-router-dom@6.30.3", "", { "dependencies": { "@remix-run/router": "1.23.2", "react-router": "6.30.3" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag=="], "react-select": ["react-select@5.7.4", "", { "dependencies": { "@babel/runtime": "^7.12.0", "@emotion/cache": "^11.4.0", "@emotion/react": "^11.8.1", "@floating-ui/dom": "^1.0.1", "@types/react-transition-group": "^4.4.0", "memoize-one": "^6.0.0", "prop-types": "^15.6.0", "react-transition-group": "^4.3.0", "use-isomorphic-layout-effect": "^1.1.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-NhuE56X+p9QDFh4BgeygHFIvJJszO1i1KSkg/JPcIJrbovyRtI+GuOEa4XzFCEpZRAEoEI8u/cAHK+jG/PgUzQ=="], @@ -4580,8 +4564,6 @@ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], - "rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="], - "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], @@ -4688,14 +4670,8 @@ "sonner": ["sonner@1.7.4", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw=="], - "sort-asc": ["sort-asc@0.1.0", "", {}, "sha512-jBgdDd+rQ+HkZF2/OHCmace5dvpos/aWQpcxuyRs9QUbPRnkEJmYVo81PIGpjIdpOcsnJ4rGjStfDHsbn+UVyw=="], - - "sort-desc": ["sort-desc@0.1.1", "", {}, "sha512-jfZacW5SKOP97BF5rX5kQfJmRVZP5/adDUTY8fCSPvNcXDVpUEe2pr/iKGlcyZzchRJZrswnp68fgk3qBXgkJw=="], - "sort-keys": ["sort-keys@1.1.2", "", { "dependencies": { "is-plain-obj": "^1.0.0" } }, "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg=="], - "sort-object": ["sort-object@0.3.2", "", { "dependencies": { "sort-asc": "^0.1.0", "sort-desc": "^0.1.1" } }, "sha512-aAQiEdqFTTdsvUFxXm3umdo04J7MRljoVGbBlkH7BgNsMvVNAJyGj7C/wV1A8wHWAJj/YikeZbfuCKqhggNWGA=="], - "source-list-map": ["source-list-map@2.0.1", "", {}, "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw=="], "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], @@ -5240,14 +5216,6 @@ "@cornerstonejs/ai/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], - "@cornerstonejs/core/@kitware/vtk.js": ["@kitware/vtk.js@32.12.1", "", { "dependencies": { "@babel/runtime": "7.22.11", "@types/webxr": "^0.5.5", "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "^3.1.3", "fflate": "0.7.3", "gl-matrix": "3.4.3", "globalthis": "1.0.3", "seedrandom": "3.0.5", "shader-loader": "1.3.1", "shelljs": "0.8.5", "spark-md5": "3.0.2", "stream-browserify": "3.0.0", "utif": "3.1.0", "webworker-promise": "0.5.0", "worker-loader": "3.0.8", "xmlbuilder2": "3.0.2" }, "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", "wslink": ">=1.1.0 || ^2.0.0" }, "bin": { "xml2json": "Utilities/XMLConverter/xml2json-cli.js", "vtkDataConverter": "Utilities/DataGenerator/convert-cli.js" } }, "sha512-IF8WK7WDu84035ws3HZXz8wys8O81mmeFayYFbdBEfKjgZ4k0EGCK25YCGc6Q47Zxq1iutjs3Zs2xAgGjTgGQg=="], - - "@cornerstonejs/labelmap-interpolation/@kitware/vtk.js": ["@kitware/vtk.js@32.12.1", "", { "dependencies": { "@babel/runtime": "7.22.11", "@types/webxr": "^0.5.5", "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "^3.1.3", "fflate": "0.7.3", "gl-matrix": "3.4.3", "globalthis": "1.0.3", "seedrandom": "3.0.5", "shader-loader": "1.3.1", "shelljs": "0.8.5", "spark-md5": "3.0.2", "stream-browserify": "3.0.0", "utif": "3.1.0", "webworker-promise": "0.5.0", "worker-loader": "3.0.8", "xmlbuilder2": "3.0.2" }, "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", "wslink": ">=1.1.0 || ^2.0.0" }, "bin": { "xml2json": "Utilities/XMLConverter/xml2json-cli.js", "vtkDataConverter": "Utilities/DataGenerator/convert-cli.js" } }, "sha512-IF8WK7WDu84035ws3HZXz8wys8O81mmeFayYFbdBEfKjgZ4k0EGCK25YCGc6Q47Zxq1iutjs3Zs2xAgGjTgGQg=="], - - "@cornerstonejs/polymorphic-segmentation/@kitware/vtk.js": ["@kitware/vtk.js@32.12.1", "", { "dependencies": { "@babel/runtime": "7.22.11", "@types/webxr": "^0.5.5", "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "^3.1.3", "fflate": "0.7.3", "gl-matrix": "3.4.3", "globalthis": "1.0.3", "seedrandom": "3.0.5", "shader-loader": "1.3.1", "shelljs": "0.8.5", "spark-md5": "3.0.2", "stream-browserify": "3.0.0", "utif": "3.1.0", "webworker-promise": "0.5.0", "worker-loader": "3.0.8", "xmlbuilder2": "3.0.2" }, "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", "wslink": ">=1.1.0 || ^2.0.0" }, "bin": { "xml2json": "Utilities/XMLConverter/xml2json-cli.js", "vtkDataConverter": "Utilities/DataGenerator/convert-cli.js" } }, "sha512-IF8WK7WDu84035ws3HZXz8wys8O81mmeFayYFbdBEfKjgZ4k0EGCK25YCGc6Q47Zxq1iutjs3Zs2xAgGjTgGQg=="], - - "@cornerstonejs/tools/@kitware/vtk.js": ["@kitware/vtk.js@32.12.1", "", { "dependencies": { "@babel/runtime": "7.22.11", "@types/webxr": "^0.5.5", "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "^3.1.3", "fflate": "0.7.3", "gl-matrix": "3.4.3", "globalthis": "1.0.3", "seedrandom": "3.0.5", "shader-loader": "1.3.1", "shelljs": "0.8.5", "spark-md5": "3.0.2", "stream-browserify": "3.0.0", "utif": "3.1.0", "webworker-promise": "0.5.0", "worker-loader": "3.0.8", "xmlbuilder2": "3.0.2" }, "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", "wslink": ">=1.1.0 || ^2.0.0" }, "bin": { "xml2json": "Utilities/XMLConverter/xml2json-cli.js", "vtkDataConverter": "Utilities/DataGenerator/convert-cli.js" } }, "sha512-IF8WK7WDu84035ws3HZXz8wys8O81mmeFayYFbdBEfKjgZ4k0EGCK25YCGc6Q47Zxq1iutjs3Zs2xAgGjTgGQg=="], - "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], "@cypress/request/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], @@ -5310,8 +5278,6 @@ "@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "@kitware/vtk.js/@babel/runtime": ["@babel/runtime@7.22.11", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA=="], - "@lerna/child-process/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@lerna/child-process/execa": ["execa@5.0.0", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ=="], @@ -5378,9 +5344,13 @@ "@ohif/app/copy-webpack-plugin": ["copy-webpack-plugin@10.2.4", "", { "dependencies": { "fast-glob": "^3.2.7", "glob-parent": "^6.0.1", "globby": "^12.0.2", "normalize-path": "^3.0.0", "schema-utils": "^4.0.0", "serialize-javascript": "^6.0.0" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg=="], + "@ohif/extension-default/webpack": ["webpack@5.101.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.2", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-B4t+nJqytPeuZlHuIKTbalhljIFXeNRqrUGAQgTGlfOl2lXXKXw+yZu6bicycP+PUlM44CxBjCFD6aciKFT3LQ=="], + + "@ohif/extension-measurement-tracking/webpack": ["webpack@5.101.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.2", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-B4t+nJqytPeuZlHuIKTbalhljIFXeNRqrUGAQgTGlfOl2lXXKXw+yZu6bicycP+PUlM44CxBjCFD6aciKFT3LQ=="], + "@rollup/plugin-node-resolve/deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], - "@rollup/plugin-node-resolve/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], + "@rollup/plugin-node-resolve/resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], "@rollup/pluginutils/@types/estree": ["@types/estree@0.0.39", "", {}, "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="], @@ -5440,7 +5410,7 @@ "babel-loader/schema-utils": ["schema-utils@2.7.1", "", { "dependencies": { "@types/json-schema": "^7.0.5", "ajv": "^6.12.4", "ajv-keywords": "^3.5.2" } }, "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg=="], - "babel-plugin-macros/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], + "babel-plugin-macros/resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], "babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="], @@ -5456,8 +5426,6 @@ "body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], - "body-parser/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="], - "boxen/camelcase": ["camelcase@7.0.1", "", {}, "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw=="], "boxen/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], @@ -5466,12 +5434,6 @@ "boxen/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], - "browserify-aes/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - - "browserify-des/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - - "browserify-rsa/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - "browserify-sign/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], "browserify-sign/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], @@ -5496,8 +5458,6 @@ "chalk-template/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "cipher-base/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - "clean-css/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "clipboardy/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], @@ -5520,8 +5480,6 @@ "create-hmac/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - "create-jest/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "cssnano/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], @@ -5572,8 +5530,6 @@ "del/rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="], - "dicom-microscopy-viewer/dicomweb-client": ["dicomweb-client@0.8.4", "", {}, "sha512-/6oY3/Fg9JyAlbTWuJOYbVqici3+nlZt43+Z/Y47RNiqLc028JcxNlY28u4VQqksxfB59f1hhNbsqsHyDT4vhw=="], - "dicom-microscopy-viewer/mathjs": ["mathjs@11.12.0", "", { "dependencies": { "@babel/runtime": "^7.23.2", "complex.js": "^2.1.1", "decimal.js": "^10.4.3", "escape-latex": "^1.2.0", "fraction.js": "4.3.4", "javascript-natural-sort": "^0.7.1", "seedrandom": "^3.0.5", "tiny-emitter": "^2.1.0", "typed-function": "^4.1.1" }, "bin": { "mathjs": "bin/cli.js" } }, "sha512-UGhVw8rS1AyedyI55DGz9q1qZ0p98kyKPyc9vherBkoueLntPfKtPBh14x+V4cdUWK0NZV2TBwqRFlvadscSuw=="], "diffie-hellman/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="], @@ -5582,6 +5538,8 @@ "dotenv-defaults/dotenv": ["dotenv@6.2.0", "", {}, "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w=="], + "dotenv-webpack/webpack": ["webpack@5.101.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.2", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-B4t+nJqytPeuZlHuIKTbalhljIFXeNRqrUGAQgTGlfOl2lXXKXw+yZu6bicycP+PUlM44CxBjCFD6aciKFT3LQ=="], + "elliptic/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="], "es-abstract/globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], @@ -5612,6 +5570,8 @@ "eslint-loader/rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="], + "eslint-loader/webpack": ["webpack@5.101.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.2", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-B4t+nJqytPeuZlHuIKTbalhljIFXeNRqrUGAQgTGlfOl2lXXKXw+yZu6bicycP+PUlM44CxBjCFD6aciKFT3LQ=="], + "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], @@ -5650,8 +5610,6 @@ "express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], - "express/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="], - "express/range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], "express/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], @@ -5746,7 +5704,7 @@ "istanbul-lib-source-maps/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], - "itk-wasm/fs-extra": ["fs-extra@11.3.2", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A=="], + "itk-wasm/fs-extra": ["fs-extra@11.3.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew=="], "itk-wasm/glob": ["glob@8.1.0", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^5.0.1", "once": "^1.3.0" } }, "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ=="], @@ -6068,6 +6026,8 @@ "oidc-client/serialize-javascript": ["serialize-javascript@4.0.0", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw=="], + "optimize-css-assets-webpack-plugin/webpack": ["webpack@5.101.0", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.2", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-B4t+nJqytPeuZlHuIKTbalhljIFXeNRqrUGAQgTGlfOl2lXXKXw+yZu6bicycP+PUlM44CxBjCFD6aciKFT3LQ=="], + "ora/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], @@ -6080,8 +6040,6 @@ "pacote/ssri": ["ssri@10.0.6", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ=="], - "parse-asn1/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - "parse-json/lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], @@ -6130,12 +6088,6 @@ "public-encrypt/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="], - "public-encrypt/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - - "randombytes/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - - "randomfill/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - "raw-body/bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], "raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], @@ -6172,10 +6124,12 @@ "readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], - "rechoir/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], + "rechoir/resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], "redent/indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + "regjsparser/jsesc": ["jsesc@3.0.2", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g=="], + "renderkid/css-select": ["css-select@4.3.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" } }, "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ=="], "restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], @@ -6316,8 +6270,6 @@ "webpack-sources/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], - "websocket-driver/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - "widest-line/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "workbox-build/ajv": ["ajv@8.12.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA=="], @@ -6354,14 +6306,6 @@ "@apideck/better-ajv-errors/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - "@cornerstonejs/core/@kitware/vtk.js/@babel/runtime": ["@babel/runtime@7.22.11", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA=="], - - "@cornerstonejs/labelmap-interpolation/@kitware/vtk.js/@babel/runtime": ["@babel/runtime@7.22.11", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA=="], - - "@cornerstonejs/polymorphic-segmentation/@kitware/vtk.js/@babel/runtime": ["@babel/runtime@7.22.11", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA=="], - - "@cornerstonejs/tools/@kitware/vtk.js/@babel/runtime": ["@babel/runtime@7.22.11", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA=="], - "@eslint/eslintrc/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], @@ -6414,8 +6358,6 @@ "@lerna/create/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "@lerna/create/cosmiconfig/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], - "@lerna/create/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], "@lerna/create/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], @@ -6442,9 +6384,17 @@ "@ohif/app/copy-webpack-plugin/globby": ["globby@12.2.0", "", { "dependencies": { "array-union": "^3.0.1", "dir-glob": "^3.0.1", "fast-glob": "^3.2.7", "ignore": "^5.1.9", "merge2": "^1.4.1", "slash": "^4.0.0" } }, "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA=="], - "@svgr/core/cosmiconfig/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "@ohif/extension-default/webpack/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], + + "@ohif/extension-default/webpack/webpack-sources": ["webpack-sources@3.3.3", "", {}, "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg=="], - "@svgr/plugin-svgo/cosmiconfig/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "@ohif/extension-measurement-tracking/webpack/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], + + "@ohif/extension-measurement-tracking/webpack/webpack-sources": ["webpack-sources@3.3.3", "", {}, "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg=="], + + "@svgr/core/cosmiconfig/js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + + "@svgr/plugin-svgo/cosmiconfig/js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], "@testing-library/dom/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -6540,6 +6490,10 @@ "dicom-microscopy-viewer/mathjs/fraction.js": ["fraction.js@4.3.4", "", {}, "sha512-pwiTgt0Q7t+GHZA4yaLjObx4vXmmdcS0iSJ19o8d/goUGgItX9UZWKWNnLHehxviD8wU2IWRsnR8cD5+yOJP2Q=="], + "dotenv-webpack/webpack/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], + + "dotenv-webpack/webpack/webpack-sources": ["webpack-sources@3.3.3", "", {}, "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg=="], + "escodegen/optionator/levn": ["levn@0.3.0", "", { "dependencies": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" } }, "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA=="], "escodegen/optionator/prelude-ls": ["prelude-ls@1.1.2", "", {}, "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w=="], @@ -6550,6 +6504,10 @@ "eslint-loader/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "eslint-loader/webpack/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], + + "eslint-loader/webpack/webpack-sources": ["webpack-sources@3.3.3", "", {}, "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg=="], + "eslint-webpack-plugin/schema-utils/ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="], "eslint/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -6730,8 +6688,6 @@ "lerna/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "lerna/cosmiconfig/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], - "lerna/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], "lerna/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], @@ -6822,8 +6778,6 @@ "make-fetch-happen/ssri/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], - "md5.js/hash-base/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - "meow/normalize-package-data/hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="], "meow/read-pkg-up/type-fest": ["type-fest@0.8.1", "", {}, "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="], @@ -6860,6 +6814,10 @@ "nx/yargs/y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + "optimize-css-assets-webpack-plugin/webpack/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], + + "optimize-css-assets-webpack-plugin/webpack/webpack-sources": ["webpack-sources@3.3.3", "", {}, "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg=="], + "ora/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "pacote/npm-package-arg/hosted-git-info": ["hosted-git-info@6.1.3", "", { "dependencies": { "lru-cache": "^7.5.1" } }, "sha512-HVJyzUrLIL1c0QmviVh5E8VGyUS7xCFPS6yydaVd1UegW+ibV/CohqTH9MkOLDp5o+rb82DMo77PTuc9F/8GKw=="], @@ -6872,6 +6830,10 @@ "patch-package/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "pbkdf2/create-hash/ripemd160": ["ripemd160@2.0.2", "", { "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" } }, "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA=="], + + "pbkdf2/ripemd160/hash-base": ["hash-base@2.0.2", "", { "dependencies": { "inherits": "^2.0.1" } }, "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw=="], + "pkg-install/execa/get-stream": ["get-stream@4.1.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w=="], "pkg-install/execa/is-stream": ["is-stream@1.1.0", "", {}, "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ=="], @@ -6954,7 +6916,7 @@ "webpack/schema-utils/ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="], - "widest-line/string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + "widest-line/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], "workbox-build/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], @@ -6978,8 +6940,6 @@ "@lerna/child-process/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - "@lerna/create/cosmiconfig/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "@lerna/create/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], "@lerna/create/node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], @@ -6998,6 +6958,10 @@ "@ohif/app/copy-webpack-plugin/globby/slash": ["slash@4.0.0", "", {}, "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew=="], + "@ohif/extension-default/webpack/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], + + "@ohif/extension-measurement-tracking/webpack/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], + "@svgr/core/cosmiconfig/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "@svgr/plugin-svgo/cosmiconfig/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -7012,6 +6976,8 @@ "cacache/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "cacache/tar/fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "clipboardy/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], "conventional-changelog-core/normalize-package-data/hosted-git-info/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], @@ -7036,6 +7002,10 @@ "decompress-tar/tar-stream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + "dotenv-webpack/webpack/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], + + "eslint-loader/webpack/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], + "eslint/find-up/locate-path/p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], "expect/jest-matcher-utils/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -7076,8 +7046,6 @@ "jest-validate/pretty-format/ansi-styles/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], - "lerna/cosmiconfig/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "lerna/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], "lerna/node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], @@ -7146,6 +7114,8 @@ "npm-registry-fetch/npm-package-arg/hosted-git-info/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], + "optimize-css-assets-webpack-plugin/webpack/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], + "pacote/npm-package-arg/hosted-git-info/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], "pacote/npm-packlist/ignore-walk/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 00000000000..3bfa8645410 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[install] +frozenLockfile = true diff --git a/bunfig.update-lockfile.toml b/bunfig.update-lockfile.toml new file mode 100644 index 00000000000..c4961dc06b3 --- /dev/null +++ b/bunfig.update-lockfile.toml @@ -0,0 +1,2 @@ +[install] +frozenLockfile = false diff --git a/commit.txt b/commit.txt index 8b0a9b0ca2c..87278693ff2 100644 --- a/commit.txt +++ b/commit.txt @@ -1 +1 @@ -93dabb380574e96fc9d2495978f1ee75acaa6f02 +27b1afe040061c6afc15bf131e28cdff3a5f0ec7 diff --git a/diff.txt b/diff.txt deleted file mode 100644 index 88c9a649343..00000000000 --- a/diff.txt +++ /dev/null @@ -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({ - > - - - - - {/* Divider */} --
-+
- - - -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) => ( - - (); 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(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; diff --git a/extensions/cornerstone-dicom-rt/src/loadRTStruct.js b/extensions/cornerstone-dicom-rt/src/loadRTStruct.js index 0e9ab38417e..ea02187bccc 100644 --- a/extensions/cornerstone-dicom-rt/src/loadRTStruct.js +++ b/extensions/cornerstone-dicom-rt/src/loadRTStruct.js @@ -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} Promise that resolves to the processed RT Structure instance with loaded contour data + * @throws {Promise} 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} 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} 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]; } diff --git a/extensions/cornerstone-dicom-rt/src/viewports/OHIFCornerstoneRTViewport.tsx b/extensions/cornerstone-dicom-rt/src/viewports/OHIFCornerstoneRTViewport.tsx index 8d70b270184..f225ab30301 100644 --- a/extensions/cornerstone-dicom-rt/src/viewports/OHIFCornerstoneRTViewport.tsx +++ b/extensions/cornerstone-dicom-rt/src/viewports/OHIFCornerstoneRTViewport.tsx @@ -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); } diff --git a/extensions/cornerstone-dicom-seg/CHANGELOG.md b/extensions/cornerstone-dicom-seg/CHANGELOG.md index 9726576a3b4..d25b3a94073 100644 --- a/extensions/cornerstone-dicom-seg/CHANGELOG.md +++ b/extensions/cornerstone-dicom-seg/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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-seg @@ -11,7 +11,1141 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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-seg + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [3.12.0-beta.133](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.132...v3.12.0-beta.133) (2026-02-04) + + +### Bug Fixes + +* **jump-to-label-map:** Use `undefined` for viewportId for arrow navigation. ([#5774](https://github.com/OHIF/Viewers/issues/5774)) ([512aa3b](https://github.com/OHIF/Viewers/commit/512aa3b07f4c783040acd67e3fd2103559a06522)) + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [3.12.0-beta.122](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.121...v3.12.0-beta.122) (2026-01-13) + + +### Bug Fixes + +* **segmentation:** List surface representations in the segmentation table for 3D views. ([#5700](https://github.com/OHIF/Viewers/issues/5700)) ([82e6a18](https://github.com/OHIF/Viewers/commit/82e6a18735b2e2278d6b95037d82bd3cb529104d)) + + + + + +# [3.12.0-beta.121](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.120...v3.12.0-beta.121) (2026-01-12) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix a segmentation exception after capture. ([#5701](https://github.com/OHIF/Viewers/issues/5701)) ([aba4855](https://github.com/OHIF/Viewers/commit/aba48552765d03cf82e6a92cdd443b246560c4fd)) + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [3.12.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.110...v3.12.0-beta.111) (2025-12-18) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix segment visibility issue. ([#5664](https://github.com/OHIF/Viewers/issues/5664)) ([3440f66](https://github.com/OHIF/Viewers/commit/3440f66ebd0900c0f5b1a5edbdf6f3b5fc72bbcf)) + + + + + +# [3.12.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.109...v3.12.0-beta.110) (2025-12-18) + + +### Bug Fixes + +* prevent annotation from appearing in active viewport when switching series with different Frame of Reference UID ([#5630](https://github.com/OHIF/Viewers/issues/5630)) ([658994c](https://github.com/OHIF/Viewers/commit/658994c854a03203054168dcfd383c0a6e320df0)) + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16) + + +### Bug Fixes + +* preclinical mode not working ([#5631](https://github.com/OHIF/Viewers/issues/5631)) ([2de81ef](https://github.com/OHIF/Viewers/commit/2de81efa7e6be534972cd763271c038b293d86f0)) + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [3.12.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.100...v3.12.0-beta.101) (2025-12-09) + + +### Bug Fixes + +* **seg-viewport:** add guard for missing reference display set handler to prevent viewport crash ([#5618](https://github.com/OHIF/Viewers/issues/5618)) ([8dde223](https://github.com/OHIF/Viewers/commit/8dde2237a2c1123fc448ae8b68e2f59af376b294)) + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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 @ohif/extension-cornerstone-dicom-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19) + + +### Bug Fixes + +* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f)) + + + + + +# [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-seg + + + + + +# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11) + + +### Bug Fixes + +* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f)) + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05) + + +### Bug Fixes + +* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece)) + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28) + + +### Bug Fixes + +* **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](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190)) + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20) + + +### Bug Fixes + +* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404) + + + + + +# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18) + + +### Bug Fixes + +* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462)) + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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 @ohif/extension-cornerstone-dicom-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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 @ohif/extension-cornerstone-dicom-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [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-seg + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + + +### Features + +* Add WSI bulk data annotations support (ANN) ([#4972](https://github.com/OHIF/Viewers/issues/4972)) ([2ed6818](https://github.com/OHIF/Viewers/commit/2ed681817142b13971bb17b3058be26bbab6b90b)) + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg diff --git a/extensions/cornerstone-dicom-seg/package.json b/extensions/cornerstone-dicom-seg/package.json index 684df2997a7..01dc828e740 100644 --- a/extensions/cornerstone-dicom-seg/package.json +++ b/extensions/cornerstone-dicom-seg/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-cornerstone-dicom-seg", - "version": "3.11.1", + "version": "3.12.0", "description": "DICOM SEG read workflow", "author": "OHIF", "license": "MIT", @@ -33,22 +33,22 @@ "start": "yarn run dev" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/i18n": "3.11.1", + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone": "3.12.0", + "@ohif/extension-default": "3.12.0", + "@ohif/i18n": "3.12.0", "prop-types": "15.8.1", "react": "18.3.1", "react-dom": "18.3.1", "react-i18next": "12.3.1", - "react-router": "6.30.1", - "react-router-dom": "6.30.1" + "react-router": "6.30.3", + "react-router-dom": "6.30.3" }, "dependencies": { "@babel/runtime": "7.28.2", - "@cornerstonejs/adapters": "4.5.13", - "@cornerstonejs/core": "4.5.13", - "@kitware/vtk.js": "32.12.0", + "@cornerstonejs/adapters": "4.15.29", + "@cornerstonejs/core": "4.15.29", + "@kitware/vtk.js": "34.15.1", "react-color": "2.19.3" } } diff --git a/extensions/cornerstone-dicom-seg/src/commandsModule.ts b/extensions/cornerstone-dicom-seg/src/commandsModule.ts index cde160b1947..6f300e10b71 100644 --- a/extensions/cornerstone-dicom-seg/src/commandsModule.ts +++ b/extensions/cornerstone-dicom-seg/src/commandsModule.ts @@ -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); } }, - }; - const definitions = { - loadSegmentationsForViewport: { - commandFn: actions.loadSegmentationsForViewport, + 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); + } }, + }; - generateSegmentation: { - commandFn: actions.generateSegmentation, - }, - downloadSegmentation: { - commandFn: actions.downloadSegmentation, - }, - storeSegmentation: { - commandFn: actions.storeSegmentation, - }, - downloadRTSS: { - commandFn: actions.downloadRTSS, - }, + const definitions = { + loadSegmentationsForViewport: actions.loadSegmentationsForViewport, + generateSegmentation: actions.generateSegmentation, + downloadSegmentation: actions.downloadSegmentation, + storeSegmentation: actions.storeSegmentation, + downloadRTSS: actions.downloadRTSS, + toggleActiveSegmentationUtility: actions.toggleActiveSegmentationUtility, }; return { diff --git a/extensions/cornerstone-dicom-seg/src/components/LogicalContourOperationsOptions.tsx b/extensions/cornerstone-dicom-seg/src/components/LogicalContourOperationsOptions.tsx new file mode 100644 index 00000000000..8f3e625bb7a --- /dev/null +++ b/extensions/cornerstone-dicom-seg/src/components/LogicalContourOperationsOptions.tsx @@ -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 ( +
+
{label}
+ +
+ ); +} + +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(activeSegmentIndex?.toString() || ''); + const [segmentB, setSegmentB] = useState(''); + const [createNewSegment, setCreateNewSegment] = useState(false); + const [newSegmentName, setNewSegmentName] = useState(''); + + 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 ( +
+
+
+ + + {options.map(option => { + const { value, icon } = option; + return ( + setOperation(option)} + > + + + ); + })} + + +
{t(operation.label)}
+
+
+ +
+
+ + +
+ +
+ +
+
+ + +
+
+ setNewSegmentName(e.target.value)} + /> +
+
+
+ ); +} + +export default LogicalContourOperationOptions; diff --git a/extensions/cornerstone-dicom-seg/src/components/SimplifyContourOptions.tsx b/extensions/cornerstone-dicom-seg/src/components/SimplifyContourOptions.tsx new file mode 100644 index 00000000000..27ecf70be9c --- /dev/null +++ b/extensions/cornerstone-dicom-seg/src/components/SimplifyContourOptions.tsx @@ -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 ( +
+
+
{t('Fill contour holes')}
+ + +
+
+
{t('Remove Small Contours')}
+
+ + setAreaThreshold(Number(e.target.value))} + /> +
+ + +
+
+
{t('Create New Segment from Holes')}
+ +
+
+ ); +} + +export default SimplifyContourOptions; diff --git a/extensions/cornerstone-dicom-seg/src/components/SmoothContoursOptions.tsx b/extensions/cornerstone-dicom-seg/src/components/SmoothContoursOptions.tsx new file mode 100644 index 00000000000..677c56c33cd --- /dev/null +++ b/extensions/cornerstone-dicom-seg/src/components/SmoothContoursOptions.tsx @@ -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 ( +
+
+
{t('Smooth all edges')}
+ + +
+
+
{t('Remove extra points')}
+ +
+
+ ); +} + +export default SmoothContoursOptions; diff --git a/extensions/cornerstone-dicom-seg/src/getSopClassHandlerModule.ts b/extensions/cornerstone-dicom-seg/src/getSopClassHandlerModule.ts index 705a46f1186..903d0216a1a 100644 --- a/extensions/cornerstone-dicom-seg/src/getSopClassHandlerModule.ts +++ b/extensions/cornerstone-dicom-seg/src/getSopClassHandlerModule.ts @@ -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) { diff --git a/extensions/cornerstone-dicom-seg/src/getToolbarModule.ts b/extensions/cornerstone-dicom-seg/src/getToolbarModule.ts index 8c023b49ff6..dcbeab7bf73 100644 --- a/extensions/cornerstone-dicom-seg/src/getToolbarModule.ts +++ b/extensions/cornerstone-dicom-seg/src/getToolbarModule.ts @@ -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; + } + } + }, + }, ]; } diff --git a/extensions/cornerstone-dicom-seg/src/viewports/OHIFCornerstoneSEGViewport.tsx b/extensions/cornerstone-dicom-seg/src/viewports/OHIFCornerstoneSEGViewport.tsx index 81e42d2d427..4cc6edcc38c 100644 --- a/extensions/cornerstone-dicom-seg/src/viewports/OHIFCornerstoneSEGViewport.tsx +++ b/extensions/cornerstone-dicom-seg/src/viewports/OHIFCornerstoneSEGViewport.tsx @@ -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); } diff --git a/extensions/cornerstone-dicom-sr/CHANGELOG.md b/extensions/cornerstone-dicom-sr/CHANGELOG.md index f0542d3693b..fd84f81c297 100644 --- a/extensions/cornerstone-dicom-sr/CHANGELOG.md +++ b/extensions/cornerstone-dicom-sr/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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-sr @@ -11,7 +11,1150 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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-sr + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [3.12.0-beta.133](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.132...v3.12.0-beta.133) (2026-02-04) + + +### Bug Fixes + +* **jump-to-label-map:** Use `undefined` for viewportId for arrow navigation. ([#5774](https://github.com/OHIF/Viewers/issues/5774)) ([512aa3b](https://github.com/OHIF/Viewers/commit/512aa3b07f4c783040acd67e3fd2103559a06522)) + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [3.12.0-beta.129](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.128...v3.12.0-beta.129) (2026-01-27) + + +### Bug Fixes + +* Handle optional ContentSequence for NUM content items ([#5703](https://github.com/OHIF/Viewers/issues/5703)) ([6dedf55](https://github.com/OHIF/Viewers/commit/6dedf5573e78a5751bed0b79646700acedfc8a38)) + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [3.12.0-beta.122](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.121...v3.12.0-beta.122) (2026-01-13) + + +### Bug Fixes + +* **segmentation:** List surface representations in the segmentation table for 3D views. ([#5700](https://github.com/OHIF/Viewers/issues/5700)) ([82e6a18](https://github.com/OHIF/Viewers/commit/82e6a18735b2e2278d6b95037d82bd3cb529104d)) + + + + + +# [3.12.0-beta.121](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.120...v3.12.0-beta.121) (2026-01-12) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix a segmentation exception after capture. ([#5701](https://github.com/OHIF/Viewers/issues/5701)) ([aba4855](https://github.com/OHIF/Viewers/commit/aba48552765d03cf82e6a92cdd443b246560c4fd)) + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [3.12.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.110...v3.12.0-beta.111) (2025-12-18) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix segment visibility issue. ([#5664](https://github.com/OHIF/Viewers/issues/5664)) ([3440f66](https://github.com/OHIF/Viewers/commit/3440f66ebd0900c0f5b1a5edbdf6f3b5fc72bbcf)) + + + + + +# [3.12.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.109...v3.12.0-beta.110) (2025-12-18) + + +### Bug Fixes + +* prevent annotation from appearing in active viewport when switching series with different Frame of Reference UID ([#5630](https://github.com/OHIF/Viewers/issues/5630)) ([658994c](https://github.com/OHIF/Viewers/commit/658994c854a03203054168dcfd383c0a6e320df0)) + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16) + + +### Bug Fixes + +* preclinical mode not working ([#5631](https://github.com/OHIF/Viewers/issues/5631)) ([2de81ef](https://github.com/OHIF/Viewers/commit/2de81efa7e6be534972cd763271c038b293d86f0)) + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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 @ohif/extension-cornerstone-dicom-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20) + + +### Bug Fixes + +* Remove dead code and fix rectangle roi rehydration ([#5490](https://github.com/OHIF/Viewers/issues/5490)) ([9ca02b4](https://github.com/OHIF/Viewers/commit/9ca02b4071f77600f7dcd87930fecdcf3d1c249f)) + + + + + +# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19) + + +### Bug Fixes + +* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f)) + + + + + +# [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-sr + + + + + +# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11) + + +### Bug Fixes + +* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f)) + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05) + + +### Bug Fixes + +* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece)) + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28) + + +### Bug Fixes + +* **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](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190)) + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20) + + +### Bug Fixes + +* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404) + + + + + +# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18) + + +### Bug Fixes + +* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462)) + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [3.12.0-beta.43](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.42...v3.12.0-beta.43) (2025-10-06) + + +### Features + +* **MeasurementService:** add rendering of unmapped measurements ([#5416](https://github.com/OHIF/Viewers/issues/5416)) ([851e74d](https://github.com/OHIF/Viewers/commit/851e74d7b867a806befb5d85fd71ff9a75e9f2d2)) + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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 @ohif/extension-cornerstone-dicom-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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 @ohif/extension-cornerstone-dicom-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [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-sr + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + + +### Features + +* Add WSI bulk data annotations support (ANN) ([#4972](https://github.com/OHIF/Viewers/issues/4972)) ([2ed6818](https://github.com/OHIF/Viewers/commit/2ed681817142b13971bb17b3058be26bbab6b90b)) + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + + +### Bug Fixes + +* load 3dsr ([#5335](https://github.com/OHIF/Viewers/issues/5335)) ([becba78](https://github.com/OHIF/Viewers/commit/becba7861f99ae4e190caddea8b663e60a9883f5)) + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr diff --git a/extensions/cornerstone-dicom-sr/package.json b/extensions/cornerstone-dicom-sr/package.json index b929c2b7a78..bd26b86a43c 100644 --- a/extensions/cornerstone-dicom-sr/package.json +++ b/extensions/cornerstone-dicom-sr/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-cornerstone-dicom-sr", - "version": "3.11.1", + "version": "3.12.0", "description": "OHIF extension for an SR Cornerstone Viewport", "author": "OHIF", "license": "MIT", @@ -34,11 +34,11 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-measurement-tracking": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone": "3.12.0", + "@ohif/extension-measurement-tracking": "3.12.0", + "@ohif/ui": "3.12.0", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", @@ -46,9 +46,9 @@ }, "dependencies": { "@babel/runtime": "7.28.2", - "@cornerstonejs/adapters": "4.5.13", - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/tools": "4.5.13", + "@cornerstonejs/adapters": "4.15.29", + "@cornerstonejs/core": "4.15.29", + "@cornerstonejs/tools": "4.15.29", "classnames": "2.5.1" } } diff --git a/extensions/cornerstone-dicom-sr/src/commandsModule.ts b/extensions/cornerstone-dicom-sr/src/commandsModule.ts index d00969c3bf7..8ba532414ba 100644 --- a/extensions/cornerstone-dicom-sr/src/commandsModule.ts +++ b/extensions/cornerstone-dicom-sr/src/commandsModule.ts @@ -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' }); }, /** diff --git a/extensions/cornerstone-dicom-sr/src/components/OHIFCornerstoneSRMeasurementViewport.tsx b/extensions/cornerstone-dicom-sr/src/components/OHIFCornerstoneSRMeasurementViewport.tsx index 740ae90da12..9d6b8f213b6 100644 --- a/extensions/cornerstone-dicom-sr/src/components/OHIFCornerstoneSRMeasurementViewport.tsx +++ b/extensions/cornerstone-dicom-sr/src/components/OHIFCornerstoneSRMeasurementViewport.tsx @@ -86,7 +86,7 @@ function OHIFCornerstoneSRMeasurementViewport(props) { const { presentationIds } = viewportOptions; const measurement = srDisplaySet.measurements[newMeasurementSelected]; setPositionPresentation(presentationIds.positionPresentationId, { - viewReference: { + viewReference: measurement.viewReference || { referencedImageId: measurement.imageId, }, }); diff --git a/extensions/cornerstone-dicom-sr/src/getSopClassHandlerModule.ts b/extensions/cornerstone-dicom-sr/src/getSopClassHandlerModule.ts index a039a7090ff..b50553b53e5 100644 --- a/extensions/cornerstone-dicom-sr/src/getSopClassHandlerModule.ts +++ b/extensions/cornerstone-dicom-sr/src/getSopClassHandlerModule.ts @@ -22,7 +22,7 @@ const { CodeScheme: Cornerstone3DCodeScheme, } = adaptersSR.Cornerstone3D; -type InstanceMetadata = Types.InstanceMetadata; +type InstanceMetadata = OhifTypes.InstanceMetadata; /** * TODO @@ -35,6 +35,7 @@ const sopClassUids = [ sopClassDictionary.BasicTextSR, sopClassDictionary.EnhancedSR, sopClassDictionary.ComprehensiveSR, + sopClassDictionary.Comprehensive3DSR, ]; const validateSameStudyUID = (uid: string, instances): void => { @@ -53,7 +54,7 @@ const validateSameStudyUID = (uid: string, instances): void => { * @param instances is a list of instances from THIS series that are not * in this DICOM SR Display Set already. */ -function addInstances(instances: InstanceMetadata[], displaySetService: DisplaySetService) { +function addInstances(instances: InstanceMetadata[], _displaySetService: DisplaySetService) { this.instances.push(...instances); utils.sortStudyInstances(this.instances); // The last instance is the newest one, so is the one most interesting. @@ -68,9 +69,10 @@ function addInstances(instances: InstanceMetadata[], displaySetService: DisplayS * 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, @@ -98,6 +100,7 @@ function _getDisplaySetsFromSeries( SeriesTime, ConceptNameCodeSequence, SOPClassUID, + imageId: predecessorImageId, } = instance; validateSameStudyUID(instance.StudyInstanceUID, instances); @@ -126,6 +129,7 @@ function _getDisplaySetsFromSeries( isImagingMeasurementReport, sopClassUids, instance, + predecessorImageId, addInstances, label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('SR')}`, }; @@ -142,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 ) { @@ -182,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, @@ -226,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 ) { @@ -288,7 +288,7 @@ function _checkIfCanAddMeasurementsToDisplaySet( is3DMeasurement && _measurementBelongsToDisplaySet({ measurement, displaySet: newDisplaySet }) ) { - addSRAnnotation(measurement, null, null); + addSRAnnotation({ measurement, displaySet: newDisplaySet }); measurement.loaded = true; measurement.displaySetInstanceUID = newDisplaySet.displaySetInstanceUID; unloadedMeasurements.splice(j, 1); @@ -309,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); } } @@ -325,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; @@ -360,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; @@ -387,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( @@ -426,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 = {}; @@ -474,8 +469,8 @@ function _getMergedContentSequencesByTrackingUniqueIdentifiers(MeasurementGroups * it calls the _processTID1410Measurement function. * Otherwise, it calls the _processNonGeometricallyDefinedMeasurement function. * - * @param {Array} 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))) { @@ -490,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, @@ -564,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'); @@ -642,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; + + if (ValueType !== 'SCOORD' && ValueType !== 'SCOORD3D') { + console.warn(`Graphic ${ValueType} not currently supported, skipping annotation.`); + return; + } + + const coords = _getCoordsFromSCOORDOrSCOORD3D(ContentSequence); - const coords = _getCoordsFromSCOORDOrSCOORD3D(ContentSequence); - if (coords) { - measurement.coords.push(coords); + if (coords) { + measurement.coords.push(coords); + } } if (MeasuredValueSequence) { @@ -665,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; @@ -680,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' } @@ -701,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( @@ -753,7 +753,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) { diff --git a/extensions/cornerstone-dicom-sr/src/init.ts b/extensions/cornerstone-dicom-sr/src/init.ts index 7bf80bd2bab..cf0ec0911b1 100644 --- a/extensions/cornerstone-dicom-sr/src/init.ts +++ b/extensions/cornerstone-dicom-sr/src/init.ts @@ -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', diff --git a/extensions/cornerstone-dicom-sr/src/tools/SCOORD3DPointTool.ts b/extensions/cornerstone-dicom-sr/src/tools/SCOORD3DPointTool.ts deleted file mode 100644 index d05a23cfdc3..00000000000 --- a/extensions/cornerstone-dicom-sr/src/tools/SCOORD3DPointTool.ts +++ /dev/null @@ -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 { - // 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), - }; - } -} diff --git a/extensions/cornerstone-dicom-sr/src/tools/toolNames.ts b/extensions/cornerstone-dicom-sr/src/tools/toolNames.ts index 535c11f28e9..fcf629c8c09 100644 --- a/extensions/cornerstone-dicom-sr/src/tools/toolNames.ts +++ b/extensions/cornerstone-dicom-sr/src/tools/toolNames.ts @@ -9,7 +9,6 @@ const toolNames = { SRCobbAngle: 'SRCobbAngle', SRRectangleROI: 'SRRectangleROI', SRPlanarFreehandROI: 'SRPlanarFreehandROI', - SRSCOORD3DPoint: 'SRSCOORD3DPoint', }; export default toolNames; diff --git a/extensions/cornerstone-dicom-sr/src/utils/SRSCOOR3DProbeMapper.ts b/extensions/cornerstone-dicom-sr/src/utils/SRSCOOR3DProbeMapper.ts deleted file mode 100644 index 50f377c15b7..00000000000 --- a/extensions/cornerstone-dicom-sr/src/utils/SRSCOOR3DProbeMapper.ts +++ /dev/null @@ -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; diff --git a/extensions/cornerstone-dicom-sr/src/utils/addSRAnnotation.ts b/extensions/cornerstone-dicom-sr/src/utils/addSRAnnotation.ts index 03b53b226a7..c24105192d8 100644 --- a/extensions/cornerstone-dicom-sr/src/utils/addSRAnnotation.ts +++ b/extensions/cornerstone-dicom-sr/src/utils/addSRAnnotation.ts @@ -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,37 +64,58 @@ 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 + */ + 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, + planeRestriction, valueType, graphicType, FrameOfReferenceUID: frameOfReferenceUID, referencedImageId: imageId, + displaySetInstanceUID: displaySet.displaySetInstanceUID, }, data: { label: measurement.labels?.[0]?.value || undefined, @@ -67,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); } diff --git a/extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.ts b/extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.ts index adb21318d25..c6c67477745 100644 --- a/extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.ts +++ b/extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.ts @@ -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, diff --git a/extensions/cornerstone-dynamic-volume/CHANGELOG.md b/extensions/cornerstone-dynamic-volume/CHANGELOG.md index f9b92b8c45f..e5a0a295ccb 100644 --- a/extensions/cornerstone-dynamic-volume/CHANGELOG.md +++ b/extensions/cornerstone-dynamic-volume/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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-dynamic-volume @@ -11,7 +11,1138 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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-dynamic-volume + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [3.12.0-beta.133](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.132...v3.12.0-beta.133) (2026-02-04) + + +### Bug Fixes + +* **jump-to-label-map:** Use `undefined` for viewportId for arrow navigation. ([#5774](https://github.com/OHIF/Viewers/issues/5774)) ([512aa3b](https://github.com/OHIF/Viewers/commit/512aa3b07f4c783040acd67e3fd2103559a06522)) + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [3.12.0-beta.122](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.121...v3.12.0-beta.122) (2026-01-13) + + +### Bug Fixes + +* **segmentation:** List surface representations in the segmentation table for 3D views. ([#5700](https://github.com/OHIF/Viewers/issues/5700)) ([82e6a18](https://github.com/OHIF/Viewers/commit/82e6a18735b2e2278d6b95037d82bd3cb529104d)) + + + + + +# [3.12.0-beta.121](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.120...v3.12.0-beta.121) (2026-01-12) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix a segmentation exception after capture. ([#5701](https://github.com/OHIF/Viewers/issues/5701)) ([aba4855](https://github.com/OHIF/Viewers/commit/aba48552765d03cf82e6a92cdd443b246560c4fd)) + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [3.12.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.110...v3.12.0-beta.111) (2025-12-18) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix segment visibility issue. ([#5664](https://github.com/OHIF/Viewers/issues/5664)) ([3440f66](https://github.com/OHIF/Viewers/commit/3440f66ebd0900c0f5b1a5edbdf6f3b5fc72bbcf)) + + + + + +# [3.12.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.109...v3.12.0-beta.110) (2025-12-18) + + +### Bug Fixes + +* prevent annotation from appearing in active viewport when switching series with different Frame of Reference UID ([#5630](https://github.com/OHIF/Viewers/issues/5630)) ([658994c](https://github.com/OHIF/Viewers/commit/658994c854a03203054168dcfd383c0a6e320df0)) + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16) + + +### Bug Fixes + +* preclinical mode not working ([#5631](https://github.com/OHIF/Viewers/issues/5631)) ([2de81ef](https://github.com/OHIF/Viewers/commit/2de81efa7e6be534972cd763271c038b293d86f0)) + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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 @ohif/extension-cornerstone-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19) + + +### Bug Fixes + +* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f)) + + + + + +# [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-dynamic-volume + + + + + +# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11) + + +### Bug Fixes + +* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f)) + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05) + + +### Bug Fixes + +* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece)) + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28) + + +### Bug Fixes + +* **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](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190)) + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20) + + +### Bug Fixes + +* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404) + + + + + +# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18) + + +### Bug Fixes + +* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462)) + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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 @ohif/extension-cornerstone-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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 @ohif/extension-cornerstone-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [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-dynamic-volume + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + + +### Features + +* Add WSI bulk data annotations support (ANN) ([#4972](https://github.com/OHIF/Viewers/issues/4972)) ([2ed6818](https://github.com/OHIF/Viewers/commit/2ed681817142b13971bb17b3058be26bbab6b90b)) + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume diff --git a/extensions/cornerstone-dynamic-volume/package.json b/extensions/cornerstone-dynamic-volume/package.json index 17ce9207707..8f3bc29af66 100644 --- a/extensions/cornerstone-dynamic-volume/package.json +++ b/extensions/cornerstone-dynamic-volume/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-cornerstone-dynamic-volume", - "version": "3.11.1", + "version": "3.12.0", "description": "OHIF extension for 4D volumes data", "author": "OHIF", "license": "MIT", @@ -29,12 +29,12 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/i18n": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone": "3.12.0", + "@ohif/extension-default": "3.12.0", + "@ohif/i18n": "3.12.0", + "@ohif/ui": "3.12.0", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", @@ -42,8 +42,8 @@ }, "dependencies": { "@babel/runtime": "7.28.2", - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/tools": "4.5.13", + "@cornerstonejs/core": "4.15.29", + "@cornerstonejs/tools": "4.15.29", "classnames": "2.5.1" } } diff --git a/extensions/cornerstone-dynamic-volume/src/commandsModule.ts b/extensions/cornerstone-dynamic-volume/src/commandsModule.ts index b50589056ae..6a264f2a68e 100644 --- a/extensions/cornerstone-dynamic-volume/src/commandsModule.ts +++ b/extensions/cornerstone-dynamic-volume/src/commandsModule.ts @@ -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'; + +import * as importedActions from './actions'; -const LABELMAP = Enums.SegmentationRepresentations.Labelmap; +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; diff --git a/extensions/cornerstone-dynamic-volume/src/getPanelModule.tsx b/extensions/cornerstone-dynamic-volume/src/getPanelModule.tsx index ee55b0d7704..3badd838f8c 100644 --- a/extensions/cornerstone-dynamic-volume/src/getPanelModule.tsx +++ b/extensions/cornerstone-dynamic-volume/src/getPanelModule.tsx @@ -22,7 +22,7 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager, co <> /../../platform/$1/src', + '^@cornerstonejs/([^/]+)/(.*)$': '/../../node_modules/@cornerstonejs/$1/dist/esm/$2', + '^@cornerstonejs/([^/]+)$': '/../../node_modules/@cornerstonejs/$1/dist/esm', + }, // rootDir: "../.." // testMatch: [ // //`/platform/${pack.name}/**/*.spec.js` diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json index 3be0b2f2b81..599a5049f05 100644 --- a/extensions/cornerstone/package.json +++ b/extensions/cornerstone/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-cornerstone", - "version": "3.11.1", + "version": "3.12.0", "description": "OHIF extension for Cornerstone", "author": "OHIF", "license": "MIT", @@ -38,12 +38,12 @@ "peerDependencies": { "@cornerstonejs/codec-charls": "1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2", - "@cornerstonejs/codec-openjpeg": "1.2.4", + "@cornerstonejs/codec-openjpeg": "1.3.0", "@cornerstonejs/codec-openjph": "2.4.7", - "@cornerstonejs/dicom-image-loader": "4.5.13", - "@ohif/core": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@cornerstonejs/dicom-image-loader": "4.15.29", + "@ohif/core": "3.12.0", + "@ohif/ui": "3.12.0", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", @@ -53,14 +53,15 @@ }, "dependencies": { "@babel/runtime": "7.28.2", - "@cornerstonejs/adapters": "4.5.13", - "@cornerstonejs/ai": "4.5.13", - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/labelmap-interpolation": "4.5.13", - "@cornerstonejs/polymorphic-segmentation": "4.5.13", - "@cornerstonejs/tools": "4.5.13", + "@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", + "@kitware/vtk.js": "34.15.1", "html2canvas": "1.4.1", "lodash.compact": "3.0.1", "lodash.debounce": "4.0.8", diff --git a/extensions/cornerstone/src/Viewport/Overlays/CustomizableViewportOverlay.tsx b/extensions/cornerstone/src/Viewport/Overlays/CustomizableViewportOverlay.tsx index 6fc5e5aebc1..5e76c2046d5 100644 --- a/extensions/cornerstone/src/Viewport/Overlays/CustomizableViewportOverlay.tsx +++ b/extensions/cornerstone/src/Viewport/Overlays/CustomizableViewportOverlay.tsx @@ -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'; diff --git a/extensions/cornerstone/src/Viewport/Overlays/ViewportOrientationMarkers.tsx b/extensions/cornerstone/src/Viewport/Overlays/ViewportOrientationMarkers.tsx index 3bf776ed734..c390308b8ab 100644 --- a/extensions/cornerstone/src/Viewport/Overlays/ViewportOrientationMarkers.tsx +++ b/extensions/cornerstone/src/Viewport/Overlays/ViewportOrientationMarkers.tsx @@ -1,12 +1,6 @@ -import React, { useEffect, useState, useMemo, useRef } from 'react'; +import React, { useEffect, useState, useMemo } from 'react'; import classNames from 'classnames'; -import { - metaData, - Enums, - Types, - getEnabledElement, - utilities as coreUtilities, -} from '@cornerstonejs/core'; +import { metaData, Enums, getEnabledElement } from '@cornerstonejs/core'; import { utilities } from '@cornerstonejs/tools'; import { vec3 } from 'gl-matrix'; @@ -22,75 +16,36 @@ function ViewportOrientationMarkers({ servicesManager, orientationMarkers = ['top', 'left'], }: withAppTypes) { - // Rotation is in degrees - const [rotation, setRotation] = useState(0); - const [flipHorizontal, setFlipHorizontal] = useState(false); - const [flipVertical, setFlipVertical] = useState(false); + const [cameraModifiedTime, setCameraModifiedTime] = useState(0); const { isViewportBackgroundLight: isLight } = useViewportRendering(viewportId); const { cornerstoneViewportService } = servicesManager.services; - // Store initial viewUp and viewRight for volume viewports - const initialVolumeOrientationRef = useRef<{ - initialViewUp: number[] | null; - initialViewRight: number[] | null; - }>({ - initialViewUp: null, - initialViewRight: null, - }); - - useEffect(() => { - initialVolumeOrientationRef.current.initialViewUp = null; - initialVolumeOrientationRef.current.initialViewRight = null; - - if (viewportData?.viewportType !== 'stack' && element && getEnabledElement(element)) { - const { viewport } = getEnabledElement(element); - const { viewUp, viewPlaneNormal } = viewport.getCamera(); - - const viewRight = vec3.create(); - vec3.cross(viewRight, viewUp, viewPlaneNormal); - - initialVolumeOrientationRef.current.initialViewUp = [...viewUp]; - initialVolumeOrientationRef.current.initialViewRight = [...viewRight]; - } - }, [element, viewportData]); - useEffect(() => { - const cameraModifiedListener = (evt: Types.EventTypes.CameraModifiedEvent) => { - const { previousCamera, camera } = evt.detail; - - const { rotation } = camera; - if (rotation !== undefined) { - setRotation(rotation); - } - - if ( - camera.flipHorizontal !== undefined && - previousCamera.flipHorizontal !== camera.flipHorizontal - ) { - setFlipHorizontal(camera.flipHorizontal); - } - - if ( - camera.flipVertical !== undefined && - previousCamera.flipVertical !== camera.flipVertical - ) { - setFlipVertical(camera.flipVertical); - } - }; - + const cameraModifiedListener = () => setCameraModifiedTime(Date.now()); element.addEventListener(Enums.Events.CAMERA_MODIFIED, cameraModifiedListener); return () => { element.removeEventListener(Enums.Events.CAMERA_MODIFIED, cameraModifiedListener); }; - }, []); + }, [element]); const markers = useMemo(() => { - if (!viewportData) { + if (!viewportData || cameraModifiedTime === 0) { + return ''; + } + + if (!element || !getEnabledElement(element)) { + console.log(`ViewportOrientationMarkers :: Viewport element not enabled (${viewportId})`); + return ''; + } + + const ohifViewport = cornerstoneViewportService.getViewportInfo(viewportId); + + if (!ohifViewport) { + console.log(`ViewportOrientationMarkers :: No viewport (${viewportId})`); return ''; } - let rowCosines, columnCosines, isDefaultValueSetForRowCosine, isDefaultValueSetForColumnCosine; if (viewportData.viewportType === 'stack') { const imageIndex = imageSliceData.imageIndex; const imageId = viewportData.data[0].imageIds?.[imageIndex]; @@ -100,58 +55,25 @@ function ViewportOrientationMarkers({ return false; } - ({ - rowCosines, - columnCosines, - isDefaultValueSetForColumnCosine, - isDefaultValueSetForColumnCosine, - } = metaData.get('imagePlaneModule', imageId) || {}); - } else { - if (!element || !getEnabledElement(element)) { - return ''; - } + const { isDefaultValueSetForRowCosine, isDefaultValueSetForColumnCosine } = + metaData.get('imagePlaneModule', imageId) || {}; - if ( - initialVolumeOrientationRef.current.initialViewUp && - initialVolumeOrientationRef.current.initialViewRight - ) { - // Use initial orientation values for consistency, even as the camera changes - columnCosines = [ - -initialVolumeOrientationRef.current.initialViewUp[0], - -initialVolumeOrientationRef.current.initialViewUp[1], - -initialVolumeOrientationRef.current.initialViewUp[2], - ]; - rowCosines = initialVolumeOrientationRef.current.initialViewRight; - } else { - console.warn('ViewportOrientationMarkers::No initial orientation values'); + if (isDefaultValueSetForColumnCosine || isDefaultValueSetForRowCosine) { return ''; } } - if ( - !rowCosines || - !columnCosines || - rotation === undefined || - isDefaultValueSetForRowCosine || - isDefaultValueSetForColumnCosine - ) { - return ''; - } - - const markers = _getOrientationMarkers( - rowCosines, - columnCosines, - rotation, - flipVertical, - flipHorizontal - ); + const { viewport } = getEnabledElement(element); + const p00 = viewport.canvasToWorld([0, 0]); + const p10 = viewport.canvasToWorld([1, 0]); + const p01 = viewport.canvasToWorld([0, 1]); + const rowCosines = vec3.sub(vec3.create(), p10, p00); + const columnCosines = vec3.sub(vec3.create(), p01, p00); - const ohifViewport = cornerstoneViewportService.getViewportInfo(viewportId); + vec3.normalize(rowCosines, rowCosines); + vec3.normalize(columnCosines, columnCosines); - if (!ohifViewport) { - console.log('ViewportOrientationMarkers::No viewport'); - return null; - } + const markers = _getOrientationMarkers(rowCosines, columnCosines); return orientationMarkers.map((m, index) => (
{markers[m]}
)); - }, [ - viewportData, - imageSliceData, - rotation, - flipVertical, - flipHorizontal, - orientationMarkers, - element, - isLight, - ]); + }, [viewportData, imageSliceData, cameraModifiedTime, orientationMarkers, element, isLight]); return
{markers}
; } @@ -189,18 +102,13 @@ function ViewportOrientationMarkers({ * * @param {*} rowCosines * @param {*} columnCosines - * @param {*} rotation in degrees - * @returns */ -function _getOrientationMarkers(rowCosines, columnCosines, rotation, flipVertical, flipHorizontal) { +function _getOrientationMarkers(rowCosines, columnCosines) { const rowString = getOrientationStringLPS(rowCosines); const columnString = getOrientationStringLPS(columnCosines); const oppositeRowString = invertOrientationStringLPS(rowString); const oppositeColumnString = invertOrientationStringLPS(columnString); - // Round to 4 decimal places (after rotate right 3 times and flip horizontally -> rotation is 90.00000000000001) - rotation = Math.round(rotation * 10000) / 10000; - const markers = { top: oppositeColumnString, left: oppositeRowString, @@ -208,43 +116,6 @@ function _getOrientationMarkers(rowCosines, columnCosines, rotation, flipVertica bottom: columnString, }; - // If any vertical or horizontal flips are applied, change the orientation strings ahead of - // the rotation applications - if (flipVertical) { - markers.top = invertOrientationStringLPS(markers.top); - markers.bottom = invertOrientationStringLPS(markers.bottom); - } - - if (flipHorizontal) { - markers.left = invertOrientationStringLPS(markers.left); - markers.right = invertOrientationStringLPS(markers.right); - } - - // Swap the labels accordingly if the viewport has been rotated - // This could be done in a more complex way for intermediate rotation values (e.g. 45 degrees) - if (rotation === 90 || rotation === -270) { - return { - top: markers.left, - left: invertOrientationStringLPS(markers.top), - right: invertOrientationStringLPS(markers.bottom), - bottom: markers.right, // left - }; - } else if (rotation === -90 || rotation === 270) { - return { - top: invertOrientationStringLPS(markers.left), - left: markers.top, - bottom: markers.left, - right: markers.bottom, - }; - } else if (rotation === 180 || rotation === -180) { - return { - top: invertOrientationStringLPS(markers.top), - left: invertOrientationStringLPS(markers.left), - bottom: invertOrientationStringLPS(markers.bottom), - right: invertOrientationStringLPS(markers.right), - }; - } - return markers; } diff --git a/extensions/cornerstone/src/Viewport/Overlays/utils.ts b/extensions/cornerstone/src/Viewport/Overlays/utils.ts index cd90a095eb8..aa50e38ca87 100644 --- a/extensions/cornerstone/src/Viewport/Overlays/utils.ts +++ b/extensions/cornerstone/src/Viewport/Overlays/utils.ts @@ -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 }; diff --git a/extensions/cornerstone/src/commandsModule.ts b/extensions/cornerstone/src/commandsModule.ts index 0c5c3e523dc..fe47568067f 100644 --- a/extensions/cornerstone/src/commandsModule.ts +++ b/extensions/cornerstone/src/commandsModule.ts @@ -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'; @@ -41,6 +50,14 @@ import { generateSegmentationCSVReport } from './utils/generateSegmentationCSVRe import { getUpdatedViewportsForSegmentation } from './utils/hydrationUtils'; import { SegmentationRepresentations } from '@cornerstonejs/tools/enums'; import { DisplaySet } from 'platform/core/src/types'; +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 = { @@ -59,10 +76,7 @@ const getLabelmapTools = ({ toolGroupService }) => { // tools is an object with toolName as the key and tool as the value Object.keys(tools).forEach(toolName => { const tool = tools[toolName]; - if ( - tool instanceof cornerstoneTools.LabelmapBaseTool && - tool.shouldResolvePreviewRequests() - ) { + if (tool instanceof cornerstoneTools.LabelmapBaseTool) { labelmapTools.push(tool); } }); @@ -107,6 +121,7 @@ function commandsModule({ }: OhifTypes.Extensions.ExtensionParams): OhifTypes.Extensions.CommandsModule { const { viewportGridService, + toolbarService, toolGroupService, cineService, uiDialogService, @@ -215,6 +230,50 @@ function commandsModule({ await Promise.all(loadPromises); return true; + } + + 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 = { @@ -258,6 +317,27 @@ function commandsModule({ const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId); viewport.setViewReference(metadata); viewport.render(); + + /** + * If the measurement is not visible inside the current viewport, + * we need to move the camera to the measurement. + */ + if (!isMeasurementWithinViewport(viewport, measurement)) { + const camera = viewport.getCamera(); + const { focalPoint: cameraFocalPoint, position: cameraPosition } = camera; + const { center, extent } = getCenterExtent(measurement); + const position = vec3.sub(vec3.create(), cameraPosition, cameraFocalPoint); + vec3.add(position, position, center); + viewport.setCamera({ focalPoint: center, position: position as any }); + /** Zoom out if the measurement is too large */ + const measurementSize = vec3.dist(extent.min, extent.max); + if (measurementSize > camera.parallelScale) { + const scaleFactor = measurementSize / camera.parallelScale; + viewport.setZoom(viewport.getZoom() / scaleFactor); + } + viewport.render(); + } + return; } @@ -312,16 +392,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, }); } @@ -619,8 +711,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, }); @@ -834,8 +926,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 || '', }); @@ -1027,17 +1119,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) { @@ -1065,11 +1161,7 @@ function commandsModule({ // Set the new toolName to be active toolGroup.setToolActive(toolName, { - bindings: [ - { - mouseButton: Enums.MouseBindings.Primary, - }, - ], + bindings, }); }, // capture viewport @@ -1079,8 +1171,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; @@ -1091,7 +1183,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, @@ -1588,48 +1680,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; }, /** @@ -1648,9 +1716,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); }, /** @@ -1666,7 +1734,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 + ); }, /** @@ -1721,15 +1815,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', }); @@ -1804,56 +1896,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); @@ -1866,8 +1908,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); @@ -1886,8 +1928,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 }); @@ -1909,7 +1951,7 @@ function commandsModule({ uiDialogService.show({ content: colorPickerDialog, - title: 'Segment Color', + title: i18n.t('Tools:Segment Color'), contentProps: { value: rgbaColor, onSave: newRgbaColor => { @@ -1979,22 +2021,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); } }); }, @@ -2117,17 +2161,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: ({ @@ -2156,26 +2194,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; @@ -2351,6 +2373,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 = { @@ -2561,6 +2782,9 @@ function commandsModule({ createLabelmapForViewport: { commandFn: actions.createLabelmapForViewport, }, + createContourForViewport: { + commandFn: actions.createContourForViewport, + }, setActiveSegmentation: { commandFn: actions.setActiveSegmentation, }, @@ -2604,19 +2828,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, @@ -2662,6 +2892,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 { diff --git a/extensions/cornerstone/src/components/AccordionGroup/AccordionGroup.tsx b/extensions/cornerstone/src/components/AccordionGroup/AccordionGroup.tsx index 337614a29e4..d91ba8834ee 100644 --- a/extensions/cornerstone/src/components/AccordionGroup/AccordionGroup.tsx +++ b/extensions/cornerstone/src/components/AccordionGroup/AccordionGroup.tsx @@ -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); diff --git a/extensions/cornerstone/src/components/DicomUpload/DicomUpload.css b/extensions/cornerstone/src/components/DicomUpload/DicomUpload.css deleted file mode 100644 index d37d2a6a649..00000000000 --- a/extensions/cornerstone/src/components/DicomUpload/DicomUpload.css +++ /dev/null @@ -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; -} diff --git a/extensions/cornerstone/src/components/DicomUpload/DicomUpload.tsx b/extensions/cornerstone/src/components/DicomUpload/DicomUpload.tsx index 8284ee07055..dd91df875a8 100644 --- a/extensions/cornerstone/src/components/DicomUpload/DicomUpload.tsx +++ b/extensions/cornerstone/src/components/DicomUpload/DicomUpload.tsx @@ -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 }) => (
-
+
(
)} @@ -60,7 +67,8 @@ function DicomUpload({ dataSource, onComplete, onStarted }: DicomUploadProps): R {({ getRootProps, getInputProps }) => (
)}
-
or drag images or folders here
-
(DICOM files supported)
+
or drag images or folders here
+
(DICOM files supported)
)} diff --git a/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgress.tsx b/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgress.tsx index e1de213a655..dd3f4d441b0 100644 --- a/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgress.tsx +++ b/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgress.tsx @@ -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 ( -
+
{numFilesCompleted === dicomFileUploaderArr.length ? ( <> {`${dicomFileUploaderArr.length} ${ @@ -308,7 +308,7 @@ function DicomUploadProgress({ @@ -337,11 +337,11 @@ function DicomUploadProgress({ const getPercentCompleteComponent = (): ReactElement => { return ( -
+
{numFilesCompleted === dicomFileUploaderArr.length ? ( <> -
+
{numFails > 0 ? `Completed with ${numFails} ${numFails > 1 ? 'errors' : 'error'}!` : 'Completed!'} @@ -359,7 +359,7 @@ function DicomUploadProgress({ >
-
{`${getPercentCompleteRounded()}%`}
+
{`${getPercentCompleteRounded()}%`}
{getShowFailedOnlyIconComponent()}
diff --git a/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgressItem.tsx b/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgressItem.tsx index 37277d75214..4ff3d00784f 100644 --- a/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgressItem.tsx +++ b/extensions/cornerstone/src/components/DicomUpload/DicomUploadProgressItem.tsx @@ -56,7 +56,7 @@ const DicomUploadProgressItem = memo( return ( ); case UploadStatus.InProgress: @@ -71,7 +71,7 @@ const DicomUploadProgressItem = memo( }; return ( -
+
{getStatusIcon()}
diff --git a/extensions/cornerstone/src/components/ExportSegmentationSubMenuItem.tsx b/extensions/cornerstone/src/components/ExportSegmentationSubMenuItem.tsx new file mode 100644 index 00000000000..10eec5e8e63 --- /dev/null +++ b/extensions/cornerstone/src/components/ExportSegmentationSubMenuItem.tsx @@ -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; + onSegmentationDownloadRTSS: (segmentationId: string) => void; + onSegmentationDownload: (segmentationId: string) => void; + downloadCSVSegmentationReport: (segmentationId: string) => void; + }; +} + +export const ExportSegmentationSubMenuItem: React.FC = ({ + segmentationId, + segmentationRepresentationType, + allowExport, + actions, +}) => { + const { t } = useTranslation('SegmentationPanel'); + + return ( + + + + {t('Download & Export')} + + + + + + {t('Download')} + + {segmentationRepresentationType === SegmentationRepresentations.Labelmap && ( + { + e.preventDefault(); + actions.downloadCSVSegmentationReport(segmentationId); + }} + disabled={!allowExport} + > + {t('CSV Report')} + + )} + {segmentationRepresentationType === SegmentationRepresentations.Labelmap && ( + { + e.preventDefault(); + actions.onSegmentationDownload(segmentationId); + }} + disabled={!allowExport} + > + {t('DICOM SEG')} + + )} + { + e.preventDefault(); + actions.onSegmentationDownloadRTSS(segmentationId); + }} + disabled={!allowExport} + > + {t('DICOM RTSS')} + + + + + {t('Export')} + + {segmentationRepresentationType === SegmentationRepresentations.Labelmap && ( + { + e.preventDefault(); + actions.storeSegmentation(segmentationId, 'SEG'); + }} + disabled={!allowExport} + > + {t('DICOM SEG')} + + )} + { + e.preventDefault(); + actions.storeSegmentation(segmentationId, 'RTSTRUCT'); + }} + disabled={!allowExport} + > + {t('DICOM RTSS')} + + + + + ); +}; diff --git a/extensions/cornerstone/src/components/MeasurementsMenu.tsx b/extensions/cornerstone/src/components/MeasurementsMenu.tsx index 6266e4721c2..34cdbcb339e 100644 --- a/extensions/cornerstone/src/components/MeasurementsMenu.tsx +++ b/extensions/cornerstone/src/components/MeasurementsMenu.tsx @@ -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) { onAction(e, 'removeMeasurement')}> - Delete + {t('Delete')} diff --git a/extensions/cornerstone/src/components/NavigationComponent/NavigationComponent.tsx b/extensions/cornerstone/src/components/NavigationComponent/NavigationComponent.tsx index 6fb85cf2041..5deb9985346 100644 --- a/extensions/cornerstone/src/components/NavigationComponent/NavigationComponent.tsx +++ b/extensions/cornerstone/src/components/NavigationComponent/NavigationComponent.tsx @@ -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, diff --git a/extensions/cornerstone/src/components/SegmentationToolConfig.tsx b/extensions/cornerstone/src/components/SegmentationToolConfig.tsx new file mode 100644 index 00000000000..7b49b988502 --- /dev/null +++ b/extensions/cornerstone/src/components/SegmentationToolConfig.tsx @@ -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 ( +
+
+ + {t('Preview edits before creating')} +
+ +
+ + {t('Use center as segment index')} +
+ +
+ + {t('Hover on segment border to activate')} +
+ +
+ + {t('Show segment name on hover')} +
+
+ ); +} diff --git a/extensions/cornerstone/src/components/SegmentationUtilityButton.tsx b/extensions/cornerstone/src/components/SegmentationUtilityButton.tsx new file mode 100644 index 00000000000..1dd29020c14 --- /dev/null +++ b/extensions/cornerstone/src/components/SegmentationUtilityButton.tsx @@ -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 ( +
+ +
+ ); +} + +export default SegmentationUtilityButton; diff --git a/extensions/cornerstone/src/components/StudyMeasurements.tsx b/extensions/cornerstone/src/components/StudyMeasurements.tsx index dd1f864a4d4..c13e4f72205 100644 --- a/extensions/cornerstone/src/components/StudyMeasurements.tsx +++ b/extensions/cornerstone/src/components/StudyMeasurements.tsx @@ -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 ( diff --git a/extensions/cornerstone/src/components/StudyMeasurementsActions.tsx b/extensions/cornerstone/src/components/StudyMeasurementsActions.tsx index c3cdcbddab9..ca6a5ec1ec1 100644 --- a/extensions/cornerstone/src/components/StudyMeasurementsActions.tsx +++ b/extensions/cornerstone/src/components/StudyMeasurementsActions.tsx @@ -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 }} > - Create SR + {t('Create SR')}
diff --git a/extensions/cornerstone/src/components/ViewportDataOverlaySettingMenu/ViewportDataOverlayMenu.tsx b/extensions/cornerstone/src/components/ViewportDataOverlaySettingMenu/ViewportDataOverlayMenu.tsx index b843bbc7abb..0d58e705c1a 100644 --- a/extensions/cornerstone/src/components/ViewportDataOverlaySettingMenu/ViewportDataOverlayMenu.tsx +++ b/extensions/cornerstone/src/components/ViewportDataOverlaySettingMenu/ViewportDataOverlayMenu.tsx @@ -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([]); const [pendingSegmentations, setPendingSegmentations] = useState([]); const { toggleColorbar } = useViewportRendering(viewportId); @@ -230,7 +232,7 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri disabled={potentialForegroundDisplaySets.length === 0} > - Foreground + {t('Common:Foreground')}
@@ -320,7 +322,7 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri onValueChange={value => handlePendingSegmentationSelection(pendingId, value)} > - + {potentialOverlayDisplaySets.map(item => ( @@ -428,7 +430,7 @@ function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: stri onValueChange={value => handlePendingForegroundSelection(pendingId, value)} > - + {potentialForegroundDisplaySets.map(item => ( diff --git a/extensions/cornerstone/src/components/ViewportWindowLevel/getViewportVolumeHistogram.ts b/extensions/cornerstone/src/components/ViewportWindowLevel/getViewportVolumeHistogram.ts index f3911e42b04..413200ec813 100644 --- a/extensions/cornerstone/src/components/ViewportWindowLevel/getViewportVolumeHistogram.ts +++ b/extensions/cornerstone/src/components/ViewportWindowLevel/getViewportVolumeHistogram.ts @@ -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(); } diff --git a/extensions/cornerstone/src/components/ViewportWindowLevel/utils.ts b/extensions/cornerstone/src/components/ViewportWindowLevel/utils.ts index 8dcbf509c1a..a68728aab2b 100644 --- a/extensions/cornerstone/src/components/ViewportWindowLevel/utils.ts +++ b/extensions/cornerstone/src/components/ViewportWindowLevel/utils.ts @@ -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, diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/Colorbar.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/Colorbar.tsx index e6deb33be64..bbe393b14df 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/Colorbar.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/Colorbar.tsx @@ -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')} ( 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)} > - Preview in viewport + {t('Preview in viewport')} ( { setCurrentColormap(colormap); diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeLighting.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeLighting.tsx index 5883a5f98b9..ff1f1e92707 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeLighting.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeLighting.tsx @@ -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 ( diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingOptions.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingOptions.tsx index 34e877ca909..9fd35ee69bb 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingOptions.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingOptions.tsx @@ -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 (
-
Lighting
+
{t('Lighting')}
diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingPresets.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingPresets.tsx index 52eeacc6c4e..4276e8fc6cb 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingPresets.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingPresets.tsx @@ -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 ( } rightIcon={} onClick={onClickPresets} diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingPresetsContent.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingPresetsContent.tsx index 1d048bba4d2..6a7ac0f86c0 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingPresetsContent.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingPresetsContent.tsx @@ -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(null); + const { t } = useTranslation('WindowLevelActionMenu'); const handleSearchChange = useCallback((event: React.ChangeEvent) => { setSearchValue(event.target.value); @@ -41,7 +43,7 @@ export function VolumeRenderingPresetsContent({ presets, viewportId, hide }: Pro @@ -71,7 +73,7 @@ export function VolumeRenderingPresetsContent({ presets, viewportId, hide }: Pro - Cancel + {t('Common:Cancel')} diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingQuality.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingQuality.tsx index 1536eafed09..fe00a35c1de 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingQuality.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeRenderingQuality.tsx @@ -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} >
- Quality + {t('Quality')}
diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeShade.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeShade.tsx index c374956b78f..e9e5d5f6937 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeShade.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/VolumeShade.tsx @@ -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 ( <> - Shade + {t('Shade')} { if (isBlocking) { @@ -65,7 +67,7 @@ export function VolumeShift({ viewportId }: VolumeShiftProps): ReactElement { onMouseUp={() => setIsBlocking(false)} >
- Shift + {t('Shift')}
diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevel.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevel.tsx index d9ded5ca48e..623cdaedc11 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevel.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevel.tsx @@ -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( 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)} > - Preview in viewport + {t('Preview in viewport')} ( { diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx index 4bd9bdca805..6946127f6ea 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx @@ -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 ( ); } @@ -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} > {!is3DVolume && } @@ -65,7 +71,7 @@ export function WindowLevelActionMenuContent({ {colorbarProperties?.colormaps && !is3DVolume && ( @@ -86,7 +92,7 @@ export function WindowLevelActionMenuContent({ {volumeRenderingPresets && is3DVolume && } {volumeRenderingQualityRange && is3DVolume && ( - + )} diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenuWrapper.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenuWrapper.tsx index b1ad429de23..669876a5abc 100644 --- a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenuWrapper.tsx +++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenuWrapper.tsx @@ -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( > diff --git a/extensions/cornerstone/src/customizations/CustomDropdownMenuContent.tsx b/extensions/cornerstone/src/customizations/CustomDropdownMenuContent.tsx index aed2eb8cc5e..072d25da791 100644 --- a/extensions/cornerstone/src/customizations/CustomDropdownMenuContent.tsx +++ b/extensions/cornerstone/src/customizations/CustomDropdownMenuContent.tsx @@ -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; } + if (!segmentation || !segmentationId) { + return null; + } + // Determine if export is allowed for this segmentation - if (exportOptions && segmentationId) { + if (exportOptions) { const exportOption = exportOptions.find(opt => opt.segmentationId === segmentationId); allowExport = exportOption?.isExportable || false; } - if (!segmentation || !segmentationId) { - return null; - } - 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 ( - onSegmentationAdd(segmentationId)}> - - {t('Create New Segmentation')} - + {!disableEditing && ( + + onSegmentationAdd({ + segmentationId, + segmentationRepresentationType: segmentationRepresentationTypes?.[0], + }) + } + > + + {t('Create New Segmentation')} + + )} {t('Manage Current Segmentation')} onSegmentationRemoveFromViewport(segmentationId)}> @@ -92,61 +100,12 @@ export const CustomDropdownMenuContent = () => { {t('Rename')} - - - - {t('Download & Export')} - - - - - - {t('Download')} - - { - e.preventDefault(); - actions.downloadCSVSegmentationReport(segmentationId); - }} - disabled={!allowExport} - > - {t('CSV Report')} - - { - e.preventDefault(); - actions.onSegmentationDownload(segmentationId); - }} - disabled={!allowExport} - > - {t('DICOM SEG')} - - { - e.preventDefault(); - actions.onSegmentationDownloadRTSS(segmentationId); - }} - disabled={!allowExport} - > - {t('DICOM RTSS')} - - - - - {t('Export')} - - { - e.preventDefault(); - actions.storeSegmentation(segmentationId); - }} - disabled={!allowExport} - > - {t('DICOM SEG')} - - - - + onSegmentationDelete(segmentationId)}> diff --git a/extensions/cornerstone/src/customizations/CustomSegmentStatisticsHeader.tsx b/extensions/cornerstone/src/customizations/CustomSegmentStatisticsHeader.tsx index 9c089e26d21..91599b11525 100644 --- a/extensions/cornerstone/src/customizations/CustomSegmentStatisticsHeader.tsx +++ b/extensions/cornerstone/src/customizations/CustomSegmentStatisticsHeader.tsx @@ -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]; diff --git a/extensions/cornerstone/src/customizations/captureViewportModalCustomization.tsx b/extensions/cornerstone/src/customizations/captureViewportModalCustomization.tsx index f35443f6452..c9e08a4bb37 100644 --- a/extensions/cornerstone/src/customizations/captureViewportModalCustomization.tsx +++ b/extensions/cornerstone/src/customizations/captureViewportModalCustomization.tsx @@ -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')} { onDimensionsChange({ ...dimensions, @@ -109,7 +114,7 @@ function ViewportDownloadFormNew({ maxWidth={MAX_TEXTURE_SIZE.toString()} maxHeight={MAX_TEXTURE_SIZE.toString()} > - Image size px + {t('Image size in pixels')} - Include annotations + {t('Include annotations')} {warningState.enabled && ( - Include warning message + {t('Include warning message')} )} - Cancel + + {t('Common:Cancel')} + { onDownload(filename || DEFAULT_FILENAME, fileType); onClose(); }} > - Save + {t('Common:Save')} diff --git a/extensions/cornerstone/src/customizations/miscCustomization.ts b/extensions/cornerstone/src/customizations/miscCustomization.ts index 073e389218c..a19f3c08470 100644 --- a/extensions/cornerstone/src/customizations/miscCustomization.ts +++ b/extensions/cornerstone/src/customizations/miscCustomization.ts @@ -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: {}, }; diff --git a/extensions/cornerstone/src/customizations/segmentationPanelCustomization.tsx b/extensions/cornerstone/src/customizations/segmentationPanelCustomization.tsx index 82280b270f1..124b507fa40 100644 --- a/extensions/cornerstone/src/customizations/segmentationPanelCustomization.tsx +++ b/extensions/cornerstone/src/customizations/segmentationPanelCustomization.tsx @@ -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 ( -
-
- - Preview edits before creating -
- -
- - Use center as segment index -
- -
- - Hover on segment border to activate -
-
- ); + 'labelMapSegmentationToolbox.config': () => { + return ; + }, + 'contourSegmentationToolbox.config': () => { + return ; }, }; } diff --git a/extensions/cornerstone/src/getPanelModule.tsx b/extensions/cornerstone/src/getPanelModule.tsx index 19ec9d76edd..384d1162eab 100644 --- a/extensions/cornerstone/src/getPanelModule.tsx +++ b/extensions/cornerstone/src/getPanelModule.tsx @@ -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 ( ); }; - const wrappedPanelSegmentationNoHeader = ({ configuration }) => { + const wrappedPanelSegmentationNoHeader = props => { return ( ); }; - 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 ( <> ); @@ -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], + }), }, ]; }; diff --git a/extensions/cornerstone/src/getToolbarModule.tsx b/extensions/cornerstone/src/getToolbarModule.tsx index d435e45f7cf..229bb9ce223 100644 --- a/extensions/cornerstone/src/getToolbarModule.tsx +++ b/extensions/cornerstone/src/getToolbarModule.tsx @@ -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') + ); } } }, diff --git a/extensions/cornerstone/src/hooks/useMeasurements.ts b/extensions/cornerstone/src/hooks/useMeasurements.ts index a8bd3504a80..2d5d23c3169 100644 --- a/extensions/cornerstone/src/hooks/useMeasurements.ts +++ b/extensions/cornerstone/src/hooks/useMeasurements.ts @@ -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 = { diff --git a/extensions/cornerstone/src/hooks/useViewportRendering.tsx b/extensions/cornerstone/src/hooks/useViewportRendering.tsx index 49af2299e08..08538623e7d 100644 --- a/extensions/cornerstone/src/hooks/useViewportRendering.tsx +++ b/extensions/cornerstone/src/hooks/useViewportRendering.tsx @@ -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) { diff --git a/extensions/cornerstone/src/hooks/useViewportSegmentations.ts b/extensions/cornerstone/src/hooks/useViewportSegmentations.ts index 03b5619af6f..25ecb6c6d93 100644 --- a/extensions/cornerstone/src/hooks/useViewportSegmentations.ts +++ b/extensions/cornerstone/src/hooks/useViewportSegmentations.ts @@ -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, diff --git a/extensions/cornerstone/src/hps/fourUp.ts b/extensions/cornerstone/src/hps/fourUp.ts index 11facdf9aaf..620dbb9b14c 100644 --- a/extensions/cornerstone/src/hps/fourUp.ts +++ b/extensions/cornerstone/src/hps/fourUp.ts @@ -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', diff --git a/extensions/cornerstone/src/hps/frameView.ts b/extensions/cornerstone/src/hps/frameView.ts index 72f3a9a9055..7c9f9f6b197 100644 --- a/extensions/cornerstone/src/hps/frameView.ts +++ b/extensions/cornerstone/src/hps/frameView.ts @@ -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'], diff --git a/extensions/cornerstone/src/hps/main3D.ts b/extensions/cornerstone/src/hps/main3D.ts index 1842d5bc089..580e2b5c4e1 100644 --- a/extensions/cornerstone/src/hps/main3D.ts +++ b/extensions/cornerstone/src/hps/main3D.ts @@ -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', diff --git a/extensions/cornerstone/src/hps/mpr.ts b/extensions/cornerstone/src/hps/mpr.ts index 331855abed3..c28c8a53703 100644 --- a/extensions/cornerstone/src/hps/mpr.ts +++ b/extensions/cornerstone/src/hps/mpr.ts @@ -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, diff --git a/extensions/cornerstone/src/hps/mprAnd3DVolumeViewport.ts b/extensions/cornerstone/src/hps/mprAnd3DVolumeViewport.ts index c26a6d5eef4..627aa782df0 100644 --- a/extensions/cornerstone/src/hps/mprAnd3DVolumeViewport.ts +++ b/extensions/cornerstone/src/hps/mprAnd3DVolumeViewport.ts @@ -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: {}, diff --git a/extensions/cornerstone/src/hps/only3D.ts b/extensions/cornerstone/src/hps/only3D.ts index 36f3534efba..08f610bc32f 100644 --- a/extensions/cornerstone/src/hps/only3D.ts +++ b/extensions/cornerstone/src/hps/only3D.ts @@ -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', diff --git a/extensions/cornerstone/src/hps/primary3D.ts b/extensions/cornerstone/src/hps/primary3D.ts index 70dfecce7b1..068248fb0d1 100644 --- a/extensions/cornerstone/src/hps/primary3D.ts +++ b/extensions/cornerstone/src/hps/primary3D.ts @@ -1,9 +1,9 @@ import { HYDRATE_SEG_SYNC_GROUP, VOI_SYNC_GROUP } from './mpr'; - +import i18n from 'i18next'; export const primary3D = { id: 'primary3D', locked: true, - name: '3D primary', + name: i18n.t('Hps:3D primary'), icon: 'layout-advanced-3d-primary', isPreset: true, createdDate: '2023-03-15T10:29:44.894Z', diff --git a/extensions/cornerstone/src/hps/primaryAxial.ts b/extensions/cornerstone/src/hps/primaryAxial.ts index 2d240b5f32f..97f9cd04f4e 100644 --- a/extensions/cornerstone/src/hps/primaryAxial.ts +++ b/extensions/cornerstone/src/hps/primaryAxial.ts @@ -1,9 +1,9 @@ import { HYDRATE_SEG_SYNC_GROUP, VOI_SYNC_GROUP } from './mpr'; - +import i18n from 'i18next'; export const primaryAxial = { id: 'primaryAxial', locked: true, - name: 'Axial Primary', + name: i18n.t('Hps:Axial Primary'), icon: 'layout-advanced-axial-primary', isPreset: true, createdDate: '2023-03-15T10:29:44.894Z', diff --git a/extensions/cornerstone/src/index.tsx b/extensions/cornerstone/src/index.tsx index ff292aa8b5c..5111dbaf5b5 100644 --- a/extensions/cornerstone/src/index.tsx +++ b/extensions/cornerstone/src/index.tsx @@ -44,6 +44,7 @@ import { usePositionPresentationStore, useSegmentationPresentationStore, useSynchronizersStore, + useSelectedSegmentationsForViewportStore, } from './stores'; import { useToggleOneUpViewportGridStore } from '@ohif/extension-default'; import { useActiveViewportSegmentationRepresentations } from './hooks/useActiveViewportSegmentationRepresentations'; @@ -57,6 +58,7 @@ import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownload import utils from './utils'; import { useMeasurementTracking } from './hooks/useMeasurementTracking'; import { setUpSegmentationEventHandlers } from './utils/setUpSegmentationEventHandlers'; +import { setUpAnnotationEventHandlers } from './utils/setUpAnnotationEventHandlers'; export * from './components'; const { imageRetrieveMetadataProvider } = cornerstone.utilities; @@ -102,6 +104,9 @@ const cornerstoneExtension: Types.Extensions.Extension = { }); unsubscriptions.push(...segmentationUnsubscriptions); + const annotationUnsubscriptions = setUpAnnotationEventHandlers(); + unsubscriptions.push(...annotationUnsubscriptions); + toolbarService.registerEventForToolbarUpdate(cornerstoneViewportService, [ cornerstoneViewportService.EVENTS.VIEWPORT_DATA_CHANGED, ]); @@ -109,6 +114,7 @@ const cornerstoneExtension: Types.Extensions.Extension = { toolbarService.registerEventForToolbarUpdate(segmentationService, [ segmentationService.EVENTS.SEGMENTATION_REMOVED, segmentationService.EVENTS.SEGMENTATION_MODIFIED, + segmentationService.EVENTS.SEGMENTATION_ANNOTATION_CUT_MERGE_PROCESS_COMPLETED, ]); toolbarService.registerEventForToolbarUpdate(cornerstone.eventTarget, [ @@ -153,6 +159,9 @@ const cornerstoneExtension: Types.Extensions.Extension = { useSynchronizersStore.getState().clearSynchronizersStore(); useToggleOneUpViewportGridStore.getState().clearToggleOneUpViewportGridStore(); useSegmentationPresentationStore.getState().clearSegmentationPresentationStore(); + useSelectedSegmentationsForViewportStore + .getState() + .clearSelectedSegmentationsForViewportStore(); segmentationService.removeAllSegmentations(); }, @@ -256,6 +265,7 @@ export { usePositionPresentationStore, useSegmentationPresentationStore, useSynchronizersStore, + useSelectedSegmentationsForViewportStore, Enums, useMeasurements, useActiveViewportSegmentationRepresentations, diff --git a/extensions/cornerstone/src/init.tsx b/extensions/cornerstone/src/init.tsx index f5db9060a6e..c8fc0ffade4 100644 --- a/extensions/cornerstone/src/init.tsx +++ b/extensions/cornerstone/src/init.tsx @@ -55,6 +55,10 @@ export default async function init({ extensionManager, appConfig, }: withAppTypes): Promise { + // Use a public library path of PUBLIC_URL plus the component name + // This safely separates components that are loaded as-is. + window.PUBLIC_LIB_URL ||= './${component}/'; + // Note: this should run first before initializing the cornerstone // DO NOT CHANGE THE ORDER @@ -102,6 +106,12 @@ export default async function init({ colorbarService.EVENTS.STATE_CHANGED, ]); + toolbarService.registerEventForToolbarUpdate(segmentationService, [ + segmentationService.EVENTS.SEGMENTATION_MODIFIED, + segmentationService.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED, + segmentationService.EVENTS.SEGMENTATION_ANNOTATION_CUT_MERGE_PROCESS_COMPLETED, + ]); + window.services = servicesManager.services; window.extensionManager = extensionManager; window.commandsManager = commandsManager; @@ -127,10 +137,18 @@ export default async function init({ getSegmentationPresentationId ); - cornerstoneTools.segmentation.config.style.setStyle( + segmentationService.setStyle( { type: SegmentationRepresentations.Contour }, { + // Declare these alpha values at the Contour type level so that they can be set/changed/inherited for all contour segmentations. + fillAlpha: 0.5, + fillAlphaInactive: 0.4, + + // In general do not fill contours so that hydrated RTSTRUCTs are not filled in when active or inactive by default. + // However, hydrated RTSTRUCTs are filled in when active or inactive if the user chooses to fill ALL contours. + // Those Contours created in OHIF (i.e. using the Segmentation Panel) will override both fill properties upon creation. renderFill: false, + renderFillInactive: false, } ); @@ -195,7 +213,6 @@ export default async function init({ commandsManager.runCommand('jumpToMeasurementViewport', { measurement, annotationUID, evt }); }); - // When a custom image load is performed, update the relevant viewports hangingProtocolService.subscribe( hangingProtocolService.EVENTS.CUSTOM_IMAGE_LOAD_PERFORMED, diff --git a/extensions/cornerstone/src/initCornerstoneTools.js b/extensions/cornerstone/src/initCornerstoneTools.js index 73eacffd9f9..27bbca70c95 100644 --- a/extensions/cornerstone/src/initCornerstoneTools.js +++ b/extensions/cornerstone/src/initCornerstoneTools.js @@ -41,6 +41,10 @@ import { SegmentSelectTool, RegionSegmentPlusTool, SegmentLabelTool, + LivewireContourSegmentationTool, + SculptorTool, + SplineContourSegmentationTool, + LabelMapEditWithContourTool, } from '@cornerstonejs/tools'; import { LabelmapSlicePropagationTool, MarkerLabelmapTool } from '@cornerstonejs/ai'; import * as polySeg from '@cornerstonejs/polymorphic-segmentation'; @@ -109,6 +113,10 @@ export default function initCornerstoneTools(configuration = {}) { addTool(LabelmapSlicePropagationTool); addTool(MarkerLabelmapTool); addTool(RegionSegmentPlusTool); + addTool(LivewireContourSegmentationTool); + addTool(SculptorTool); + addTool(SplineContourSegmentationTool); + addTool(LabelMapEditWithContourTool); // Modify annotation tools to use dashed lines on SR const annotationStyle = { textBoxFontSize: '15px', @@ -168,6 +176,10 @@ const toolNames = { LabelmapSlicePropagation: LabelmapSlicePropagationTool.toolName, MarkerLabelmap: MarkerLabelmapTool.toolName, RegionSegmentPlus: RegionSegmentPlusTool.toolName, + LivewireContourSegmentation: LivewireContourSegmentationTool.toolName, + SculptorTool: SculptorTool.toolName, + SplineContourSegmentation: SplineContourSegmentationTool.toolName, + LabelMapEditWithContourTool: LabelMapEditWithContourTool.toolName, }; export { toolNames }; diff --git a/extensions/cornerstone/src/initMeasurementService.ts b/extensions/cornerstone/src/initMeasurementService.ts index ab150a4e0a0..9decf4cd433 100644 --- a/extensions/cornerstone/src/initMeasurementService.ts +++ b/extensions/cornerstone/src/initMeasurementService.ts @@ -450,7 +450,7 @@ const connectMeasurementServiceToTools = ({ if (measurement?.metadata?.referencedImageId) { imageId = measurement.metadata.referencedImageId; frameNumber = getSOPInstanceAttributes(measurement.metadata.referencedImageId).frameNumber; - } else { + } else if (instance) { imageId = dataSource.getImageIdsForInstance({ instance }); } @@ -461,6 +461,9 @@ const connectMeasurementServiceToTools = ({ const annotationManager = annotation.state.getAnnotationManager(); const newAnnotation = { annotationUID: measurement.uid, + // Not used in CS3D but stored in the CS3D state so that saving + // can apply the predecessor consistently. + predecessorImageId: measurement?.predecessorImageId, highlighted: false, isLocked: false, // This is used to force a re-render of the annotation to @@ -503,11 +506,15 @@ const connectMeasurementServiceToTools = ({ } const removedAnnotation = annotation.state.getAnnotation(removedMeasurementId); removeAnnotation(removedMeasurementId); - commandsManager.run('triggerCreateAnnotationMemo', { - annotation: removedAnnotation, - FrameOfReferenceUID: removedAnnotation.metadata.FrameOfReferenceUID, - options: { deleting: true }, - }); + // Ensure `removedAnnotation` is available before triggering the memo, + // as it can be undefined during an undo operation + if (removedAnnotation) { + commandsManager.run('triggerCreateAnnotationMemo', { + annotation: removedAnnotation, + FrameOfReferenceUID: removedAnnotation.metadata.FrameOfReferenceUID, + options: { deleting: true }, + }); + } const renderingEngine = cornerstoneViewportService.getRenderingEngine(); // Note: We could do a better job by triggering the render on the // viewport itself, but the removeAnnotation does not include that info... diff --git a/extensions/cornerstone/src/panels/PanelMeasurement.tsx b/extensions/cornerstone/src/panels/PanelMeasurement.tsx index 0cd861bd8bb..224a299f2a9 100644 --- a/extensions/cornerstone/src/panels/PanelMeasurement.tsx +++ b/extensions/cornerstone/src/panels/PanelMeasurement.tsx @@ -78,6 +78,7 @@ export default function PanelMeasurement(props): React.ReactNode { ); return cloned; } + // Need to merge defaults on the content props to ensure they get passed to children return ; } diff --git a/extensions/cornerstone/src/panels/PanelSegmentation.tsx b/extensions/cornerstone/src/panels/PanelSegmentation.tsx index 2baf9d14594..c43a08cb0a2 100644 --- a/extensions/cornerstone/src/panels/PanelSegmentation.tsx +++ b/extensions/cornerstone/src/panels/PanelSegmentation.tsx @@ -1,16 +1,102 @@ -import React from 'react'; -import { SegmentationTable } from '@ohif/ui-next'; +import React, { useCallback, useEffect } from 'react'; +import { + IconPresentationProvider, + Popover, + PopoverAnchor, + PopoverContent, + SegmentationTable, + ToolSettings, +} from '@ohif/ui-next'; import { useActiveViewportSegmentationRepresentations } from '../hooks/useActiveViewportSegmentationRepresentations'; -import { metaData, cache } from '@cornerstonejs/core'; -import { useSystem } from '@ohif/core/src'; +import { useActiveToolOptions, useSystem } from '@ohif/core/src'; +import { SegmentationRepresentations } from '@cornerstonejs/tools/enums'; +import { Toolbar, useUIStateStore } from '@ohif/extension-default'; +import SegmentationUtilityButton from '../components/SegmentationUtilityButton'; +import { useSelectedSegmentationsForViewportStore } from '../stores'; +import { + hasExportableLabelMapData, + hasExportableContourData, +} from '../utils/segmentationExportUtils'; -export default function PanelSegmentation({ children }: withAppTypes) { +type PanelSegmentationProps = { + children?: React.ReactNode; + + // The representation types for this segmentation panel. Undefined means all types. + // The first element is the primary type. Additional elements are secondary types. + segmentationRepresentationTypes?: SegmentationRepresentations[]; +} & withAppTypes; + +export default function PanelSegmentation({ + children, + segmentationRepresentationTypes, +}: PanelSegmentationProps) { const { commandsManager, servicesManager } = useSystem(); - const { customizationService, displaySetService } = servicesManager.services; + const { + customizationService, + displaySetService, + viewportGridService, + toolbarService, + segmentationService, + } = servicesManager.services; + const { activeViewportId } = viewportGridService.getState(); + + const utilitiesSectionMap = { + [SegmentationRepresentations.Labelmap]: toolbarService.sections.labelMapSegmentationUtilities, + [SegmentationRepresentations.Contour]: toolbarService.sections.contourSegmentationUtilities, + }; + + const selectedSegmentationsForViewportMap = useSelectedSegmentationsForViewportStore( + store => store.selectedSegmentationsForViewport[activeViewportId] + ); + + const selectedSegmentationIdForType = segmentationRepresentationTypes + ? segmentationRepresentationTypes.reduce( + (selectedSegmentation, type) => + selectedSegmentation || + (selectedSegmentationsForViewportMap?.has(type) + ? selectedSegmentationsForViewportMap?.get(type) + : undefined), + undefined + ) + : segmentationService?.getActiveSegmentation(activeViewportId)?.segmentationId; + + const buttonSection = segmentationRepresentationTypes?.[0] + ? utilitiesSectionMap[segmentationRepresentationTypes[0]] + : undefined; + + const { activeToolOptions: activeUtilityOptions } = useActiveToolOptions({ + buttonSectionId: buttonSection, + }); const { segmentationsWithRepresentations, disabled } = useActiveViewportSegmentationRepresentations(); + const setUIState = useUIStateStore(store => store.setUIState); + + // useEffect for handling clicks on any of the non-active viewports. + // The ViewportGrid stops the propagation of pointer/mouse events + // for non-active viewports so the Popover below + // is not closed when clicking on any of the non-active viewports. + useEffect(() => { + setUIState('activeSegmentationUtility', null); + toolbarService.refreshToolbarState({ viewportId: activeViewportId }); + }, [activeViewportId, setUIState, toolbarService]); + + // The callback for handling clicks outside of the Popover and, the SegmentationUtilityButton + // that triggered it to open. Clicks outside those components must close the Popover. + // The Popover is made visible whenever the options associated with the + // activeSegmentationUtility exist. Thus clearing the activeSegmentationUtility + // clears the associated options and will keep the Popover closed. + const handlePopoverOpenChange = useCallback( + (open: boolean) => { + if (!open) { + setUIState('activeSegmentationUtility', null); + toolbarService.refreshToolbarState({ viewportId: activeViewportId }); + } + }, + [activeViewportId, setUIState, toolbarService] + ); + // Extract customization options const segmentationTableMode = customizationService.getCustomization( 'panelSegmentation.tableMode' @@ -35,6 +121,7 @@ export default function PanelSegmentation({ children }: withAppTypes) { }, onSegmentAdd: segmentationId => { commandsManager.run('addSegment', { segmentationId }); + commandsManager.run('setActiveSegmentation', { segmentationId }); }, onSegmentClick: (segmentationId, segmentIndex) => { commandsManager.run('setActiveSegmentAndCenter', { segmentationId, segmentIndex }); @@ -51,6 +138,14 @@ export default function PanelSegmentation({ children }: withAppTypes) { onSegmentDelete: (segmentationId, segmentIndex) => { commandsManager.run('deleteSegment', { segmentationId, segmentIndex }); }, + onSegmentCopy: + segmentationRepresentationTypes?.[0] === SegmentationRepresentations.Contour + ? (segmentationId, segmentIndex) => { + commandsManager.run('copyContourSegment', { + sourceSegmentInfo: { segmentationId, segmentIndex }, + }); + } + : undefined, onToggleSegmentVisibility: (segmentationId, segmentIndex, type) => { commandsManager.run('toggleSegmentVisibility', { segmentationId, segmentIndex, type }); }, @@ -76,18 +171,36 @@ export default function PanelSegmentation({ children }: withAppTypes) { commandsManager.run('deleteSegmentation', { segmentationId }); }, setFillAlpha: ({ type }, value) => { + commandsManager.run('activateSelectedSegmentationOfType', { + segmentationRepresentationType: type, + }); commandsManager.run('setFillAlpha', { type, value }); }, setOutlineWidth: ({ type }, value) => { + commandsManager.run('activateSelectedSegmentationOfType', { + segmentationRepresentationType: type, + }); commandsManager.run('setOutlineWidth', { type, value }); }, setRenderFill: ({ type }, value) => { + commandsManager.run('activateSelectedSegmentationOfType', { + segmentationRepresentationType: type, + }); commandsManager.run('setRenderFill', { type, value }); }, + setRenderFillInactive: ({ type }, value) => { + commandsManager.run('setRenderFillInactive', { type, value }); + }, setRenderOutline: ({ type }, value) => { + commandsManager.run('activateSelectedSegmentationOfType', { + segmentationRepresentationType: type, + }); commandsManager.run('setRenderOutline', { type, value }); }, - setFillAlphaInactive: ({ type }, value) => { + setRenderOutlineInactive: ({ type }, value) => { + commandsManager.run('setRenderOutlineInactive', { type, value }); + }, + setFillAlphaInactive: ({ type }: { type?: string }, value) => { commandsManager.run('setFillAlphaInactive', { type, value }); }, getRenderInactiveSegmentations: () => { @@ -96,52 +209,26 @@ export default function PanelSegmentation({ children }: withAppTypes) { }; // Generate export options + // Map each segmentation to an export option for it. + // A segmentation is exportable if it has any labelmap or contour data. const exportOptions = segmentationsWithRepresentations.map(({ segmentation }) => { const { representationData, segmentationId } = segmentation; - const { Labelmap } = representationData; + const { Labelmap, Contour } = representationData; - if (!Labelmap) { + if (!Labelmap && !Contour) { return { segmentationId, isExportable: true }; } - // Check if any segments have anything drawn in any of the viewports - const hasAnySegmentData = (() => { - const imageIds = Labelmap.imageIds; - if (!imageIds?.length) return false; - - for (const imageId of imageIds) { - const pixelData = cache.getImage(imageId)?.getPixelData(); - if (!pixelData) continue; - - for (let i = 0; i < pixelData.length; i++) { - if (pixelData[i] !== 0) return true; - } - } - return false; - })(); - - if (!hasAnySegmentData) { - return { segmentationId, isExportable: false }; - } - - const referencedImageIds = Labelmap.referencedImageIds; - const firstImageId = referencedImageIds[0]; - const instance = metaData.get('instance', firstImageId); - - if (!instance) { + if ( + !hasExportableLabelMapData(Labelmap, displaySetService) && + !hasExportableContourData(Contour) + ) { return { segmentationId, isExportable: false }; } - const SOPInstanceUID = instance.SOPInstanceUID || instance.SopInstanceUID; - const SeriesInstanceUID = instance.SeriesInstanceUID; - const displaySet = displaySetService.getDisplaySetForSOPInstanceUID( - SOPInstanceUID, - SeriesInstanceUID - ); - return { segmentationId, - isExportable: displaySet?.isReconstructable, + isExportable: true, }; }); @@ -150,15 +237,34 @@ export default function PanelSegmentation({ children }: withAppTypes) { disabled, data: segmentationsWithRepresentations, mode: segmentationTableMode, - title: 'Segmentations', + title: `${segmentationRepresentationTypes?.[0] ? `${segmentationRepresentationTypes[0]} ` : ''}Segmentations`, exportOptions, disableEditing, onSegmentationAdd, showAddSegment, renderInactiveSegmentations: handlers.getRenderInactiveSegmentations(), + segmentationRepresentationTypes, + selectedSegmentationIdForType, ...handlers, }; + const renderUtilitiesToolbar = () => { + if (!buttonSection) { + return null; + } + + return ( + +
+ +
+
+ ); + }; + const renderSegments = () => { return ( @@ -175,6 +281,7 @@ export default function PanelSegmentation({ children }: withAppTypes) { if (tableProps.mode === 'collapsed') { return ( + {renderUtilitiesToolbar()} @@ -193,6 +300,7 @@ export default function PanelSegmentation({ children }: withAppTypes) { return ( <> + {renderUtilitiesToolbar()} @@ -211,11 +319,27 @@ export default function PanelSegmentation({ children }: withAppTypes) { }; return ( - - {children} - - - {renderModeContent()} - + + + + {children} + + + {renderModeContent()} + + + {activeUtilityOptions && ( + + + + )} + ); } diff --git a/extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts b/extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts index fe14ade92c4..8940a17cd82 100644 --- a/extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts +++ b/extensions/cornerstone/src/services/CornerstoneCacheService/CornerstoneCacheService.ts @@ -233,7 +233,7 @@ class CornerstoneCacheService { for (const displaySet of displaySets) { const { Modality } = displaySet; const isParametricMap = Modality === 'PMAP'; - const isSeg = Modality === 'SEG'; + const isSegOrRtstruct = Modality === 'SEG' || Modality === 'RTSTRUCT'; // Don't create volumes for the displaySets that have custom load // function (e.g., SEG, RT, since they rely on the reference volumes @@ -276,7 +276,7 @@ class CornerstoneCacheService { // Parametric maps do not have image ids but they already have volume data // therefore a new volume should not be created. - if (!isParametricMap && !isSeg && (!volumeImageIds || !volume)) { + if (!isParametricMap && !isSegOrRtstruct && (!volumeImageIds || !volume)) { volumeImageIds = this._getCornerstoneVolumeImageIds(displaySet, dataSource); volume = await volumeLoader.createAndCacheVolume(volumeId, { diff --git a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.test.ts b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.test.ts new file mode 100644 index 00000000000..6e8c28a8d86 --- /dev/null +++ b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.test.ts @@ -0,0 +1,3084 @@ +import { + cache, + Enums as csEnums, + eventTarget, + geometryLoader, + getEnabledElementByViewportId, + imageLoader, + Types as csTypes, + metaData, +} from '@cornerstonejs/core'; +import { ViewportType } from '@cornerstonejs/core/enums'; + +import { + Enums as csToolsEnums, + segmentation as cstSegmentation, + Types as cstTypes, +} from '@cornerstonejs/tools'; + +import { EasingFunctionEnum, EasingFunctionMap } from '../../utils/transitions'; +import * as MapROIContoursToRTStructData from './RTSTRUCT/mapROIContoursToRTStructData'; +import SegmentationServiceClass, { SegmentationRepresentation } from './SegmentationService'; + +jest.mock('@cornerstonejs/core', () => ({ + ...jest.requireActual('@cornerstonejs/core'), + getEnabledElementByViewportId: jest.fn(), + eventTarget: { + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + }, +})); + +jest.mock('@cornerstonejs/tools', () => ({ + ...jest.requireActual('@cornerstonejs/tools'), + segmentation: { + ...jest.requireActual('@cornerstonejs/tools').segmentation, + activeSegmentation: { + getActiveSegmentation: jest.fn(), + setActiveSegmentation: jest.fn(), + }, + addSegmentations: jest.fn(), + config: { + color: { + getSegmentIndexColor: jest.fn(), + setSegmentIndexColor: jest.fn(), + }, + visibility: { + getHiddenSegmentIndices: jest.fn(), + getSegmentIndexVisibility: jest.fn(), + setSegmentIndexVisibility: jest.fn(), + setSegmentationRepresentationVisibility: jest.fn(), + }, + style: { + hasCustomStyle: jest.fn(), + getRenderInactiveSegmentations: jest.fn(), + getStyle: jest.fn(), + resetToGlobalStyle: jest.fn(), + setRenderInactiveSegmentations: jest.fn(), + setStyle: jest.fn(), + }, + }, + getLabelmapImageIds: jest.fn(), + helpers: { convertStackToVolumeLabelmap: jest.fn() }, + removeSegment: jest.fn(), + removeSegmentationRepresentations: jest.fn(), + segmentIndex: { + setActiveSegmentIndex: jest.fn(), + }, + segmentLocking: { + isSegmentIndexLocked: jest.fn(), + setSegmentIndexLocked: jest.fn(), + }, + state: { + addColorLUT: jest.fn(), + getSegmentation: jest.fn(), + getSegmentations: jest.fn(), + getSegmentationRepresentationsBySegmentationId: jest.fn(), + getSegmentationRepresentations: jest.fn(), + getViewportIdsWithSegmentation: jest.fn(), + removeAllSegmentations: jest.fn(), + removeSegmentation: jest.fn(), + updateLabelmapSegmentationImageReferences: jest.fn(), + }, + triggerSegmentationEvents: { triggerSegmentationRepresentationModified: jest.fn() }, + }, +})); + +const serviceManagerMock = { + services: { + cornerstoneViewportService: { + getCornerstoneViewport: jest.fn(), + }, + displaySetService: { + getDisplaySetByUID: jest.fn(), + }, + viewportGridService: { + EVENTS: { + GRID_STATE_CHANGED: 'event::gridStateChanged', + }, + getState: jest.fn(), + setDisplaySetsForViewport: jest.fn(), + subscribe: jest.fn(), + }, + }, +}; + +describe('SegmentationService', () => { + let service: SegmentationServiceClass; + const viewportId = 'viewportId'; + const mockCornerstoneRepresentations = [ + { + segmentationId: 'd7682642-c41d-abe5-3c78-716191336ab1', + type: 'Labelmap', + active: true, + visible: true, + colorLUTIndex: 0, + segments: { + '1': { visible: true }, + }, + config: { + cfun: { nodes: [] }, + ofun: { nodes: [] }, + colorLUTIndex: 0, + colorLUTOrIndex: 0, + }, + }, + { + segmentationId: 'd7682642-c41d-abe5-3c78-716191336ab2', + type: 'Labelmap', + active: true, + visible: true, + colorLUTIndex: 0, + segments: { + '1': { visible: true }, + }, + config: { + cfun: { nodes: [] }, + ofun: { nodes: [] }, + colorLUTIndex: 0, + colorLUTOrIndex: 0, + }, + }, + ]; + const mockCornerstoneSegmentation = { + segmentationId: '1d6ce0c8-aeae-6890-1cc6-b39560866811', + label: 'Segmentation', + cachedStats: {}, + segments: { + '1': { + active: true, + cachedStats: {}, + label: 'Segment 1', + locked: false, + segmentIndex: 1, + }, + }, + representationData: { Labelmap: {} }, + }; + const mockVolumeCornerstoneSegmentation = { + ...mockCornerstoneSegmentation, + representationData: { Labelmap: { volumeId: 'volumeId' } }, + }; + const mockCornerstoneStackViewport = { + element: { + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + }, + id: viewportId, + type: ViewportType.STACK, + getFrameOfReferenceUID: jest.fn(), + getViewPresentation: jest.fn(), + getViewReference: jest.fn(), + }; + const mockCornerstoneVolumeViewport = { + id: viewportId, + type: ViewportType.VOLUME_3D, + getFrameOfReferenceUID: jest.fn(), + setViewPresentation: jest.fn(), + setViewReference: jest.fn(), + render: jest.fn(), + }; + const representations = [ + { + ...mockCornerstoneRepresentations[0], + viewportId: 'viewportId', + id: 'test-id', + label: 'Test Segmentation', + styles: {}, + segments: { + 1: { + color: [255, 0, 0, 1], + opacity: 1, + segmentIndex: 1, + visible: true, + }, + }, + }, + ] as SegmentationRepresentation[]; + + beforeEach(() => { + service = new SegmentationServiceClass({ servicesManager: serviceManagerMock }); + + jest.clearAllMocks(); + }); + + it('should instantiate the service properly', () => { + expect(service).toBeDefined(); + expect(service.servicesManager).toBe(serviceManagerMock); + expect(service.EVENTS).toBeDefined(); + }); + + it('should instantiate service through registration', () => { + // @ts-expect-error - mock only has a subset of the properties + const service = SegmentationServiceClass.REGISTRATION.create({ + servicesManager: serviceManagerMock, + }); + + expect(service).toBeDefined(); + }); + + describe('onModeEnter', () => { + it('should add event listeners', () => { + service.onModeEnter(); + + expect(eventTarget.addEventListener).toHaveBeenCalledTimes(8); + + expect(eventTarget.addEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_MODIFIED, + expect.any(Function) + ); + expect(eventTarget.addEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_REMOVED, + expect.any(Function) + ); + expect(eventTarget.addEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_DATA_MODIFIED, + expect.any(Function) + ); + expect(eventTarget.addEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_REPRESENTATION_MODIFIED, + expect.any(Function) + ); + expect(eventTarget.addEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_REPRESENTATION_ADDED, + expect.any(Function) + ); + expect(eventTarget.addEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_REPRESENTATION_REMOVED, + expect.any(Function) + ); + expect(eventTarget.addEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_ADDED, + expect.any(Function) + ); + expect(eventTarget.addEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.ANNOTATION_CUT_MERGE_PROCESS_COMPLETED, + expect.any(Function) + ); + }); + }); + + describe('onModeExit', () => { + it('should remove event listeners', () => { + jest.spyOn(service, 'reset'); + + service.onModeExit(); + + expect(eventTarget.removeEventListener).toHaveBeenCalledTimes(7); + + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_MODIFIED, + expect.any(Function) + ); + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_REMOVED, + expect.any(Function) + ); + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_DATA_MODIFIED, + expect.any(Function) + ); + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_REPRESENTATION_MODIFIED, + expect.any(Function) + ); + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_REPRESENTATION_ADDED, + expect.any(Function) + ); + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_REPRESENTATION_REMOVED, + expect.any(Function) + ); + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_ADDED, + expect.any(Function) + ); + + expect(service.reset).toHaveBeenCalled(); + }); + }); + + describe('getSegmentation', () => { + it('should call cornerstone to get specific segmentation', () => { + service.getSegmentation('segmentationId'); + + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledWith('segmentationId'); + }); + }); + + describe('getSegmentations', () => { + it('should call cornerstone to get all segmentations', () => { + service.getSegmentations(); + + expect(cstSegmentation.state.getSegmentations).toHaveBeenCalled(); + }); + }); + + describe('getPresentation', () => { + it('should properly retrieve the segmentation presentations', () => { + jest + .spyOn(cstSegmentation.state, 'getSegmentationRepresentations') + .mockReturnValueOnce( + mockCornerstoneRepresentations as cstTypes.SegmentationRepresentation[] + ); + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(cstSegmentation.config.color, 'getSegmentIndexColor') + .mockReturnValue([0, 0, 0, 1]); + jest + .spyOn(cstSegmentation.config.visibility, 'getSegmentIndexVisibility') + .mockReturnValue(true); + jest.spyOn(cstSegmentation.config.style, 'getStyle').mockReturnValue({}); + + const returnedPresentations = service.getPresentation(viewportId); + + // config is empty due to _toOHIFSegmentationRepresentation returning empty config + expect(returnedPresentations).toEqual([ + { + config: {}, + hydrated: true, + segmentationId: 'd7682642-c41d-abe5-3c78-716191336ab1', + type: 'Labelmap', + }, + { + config: {}, + hydrated: true, + segmentationId: 'd7682642-c41d-abe5-3c78-716191336ab2', + type: 'Labelmap', + }, + ]); + }); + + it('should ignore when representation is undefined', () => { + jest.spyOn(service, 'getSegmentationRepresentations').mockReturnValueOnce([undefined]); + + const returnedPresentations = service.getPresentation(viewportId); + + expect(returnedPresentations).toEqual([]); + }); + }); + + describe('getRepresentationsForSegmentation', () => { + it('should call cornerstone to get representations for a segmentation', () => { + service.getRepresentationsForSegmentation('segmentationId'); + + expect( + cstSegmentation.state.getSegmentationRepresentationsBySegmentationId + ).toHaveBeenCalledWith('segmentationId'); + }); + }); + + describe('getSegmentationRepresentations', () => { + it('should properly map cornerstone representations to OHIF representations', () => { + jest + .spyOn(cstSegmentation.state, 'getSegmentationRepresentations') + .mockReturnValueOnce( + mockCornerstoneRepresentations as cstTypes.SegmentationRepresentation[] + ); + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(cstSegmentation.config.color, 'getSegmentIndexColor') + .mockReturnValue([0, 0, 0, 1]); + jest + .spyOn(cstSegmentation.config.visibility, 'getSegmentIndexVisibility') + .mockReturnValue(true); + jest.spyOn(cstSegmentation.config.style, 'getStyle').mockReturnValue({}); + + const returnedPresentations = service.getSegmentationRepresentations(viewportId); + + expect(returnedPresentations).toEqual([ + { + active: true, + colorLUTIndex: 0, + config: {}, + id: 'd7682642-c41d-abe5-3c78-716191336ab1-Labelmap-viewportId', + label: 'Segmentation', + segmentationId: 'd7682642-c41d-abe5-3c78-716191336ab1', + segments: { + '1': { + color: [0, 0, 0, 1], + opacity: 1, + segmentIndex: 1, + visible: true, + }, + }, + styles: {}, + type: 'Labelmap', + viewportId: 'viewportId', + visible: true, + }, + { + active: true, + colorLUTIndex: 0, + config: {}, + id: 'd7682642-c41d-abe5-3c78-716191336ab2-Labelmap-viewportId', + label: 'Segmentation', + segmentationId: 'd7682642-c41d-abe5-3c78-716191336ab2', + segments: { + '1': { + color: [0, 0, 0, 1], + opacity: 1, + segmentIndex: 1, + visible: true, + }, + }, + styles: {}, + type: 'Labelmap', + viewportId: 'viewportId', + visible: true, + }, + ]); + + expect(cstSegmentation.state.getSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.getSegmentationRepresentations).toHaveBeenCalledWith( + viewportId, + {} + ); + + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledTimes(2); + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledWith( + mockCornerstoneRepresentations[0].segmentationId + ); + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledWith( + mockCornerstoneRepresentations[1].segmentationId + ); + + expect(cstSegmentation.config.color.getSegmentIndexColor).toHaveBeenCalledTimes(2); + expect(cstSegmentation.config.color.getSegmentIndexColor).toHaveBeenCalledWith( + viewportId, + mockCornerstoneRepresentations[0].segmentationId, + mockCornerstoneSegmentation.segments['1'].segmentIndex + ); + expect(cstSegmentation.config.color.getSegmentIndexColor).toHaveBeenCalledWith( + viewportId, + mockCornerstoneRepresentations[1].segmentationId, + mockCornerstoneSegmentation.segments['1'].segmentIndex + ); + + expect(cstSegmentation.config.visibility.getSegmentIndexVisibility).toHaveBeenCalledTimes(2); + expect(cstSegmentation.config.visibility.getSegmentIndexVisibility).toHaveBeenCalledWith( + viewportId, + { + segmentationId: mockCornerstoneRepresentations[0].segmentationId, + type: mockCornerstoneRepresentations[0].type, + }, + mockCornerstoneSegmentation.segments['1'].segmentIndex + ); + expect(cstSegmentation.config.visibility.getSegmentIndexVisibility).toHaveBeenCalledWith( + viewportId, + { + segmentationId: mockCornerstoneRepresentations[1].segmentationId, + type: mockCornerstoneRepresentations[1].type, + }, + mockCornerstoneSegmentation.segments['1'].segmentIndex + ); + + expect(cstSegmentation.config.style.getStyle).toHaveBeenCalledTimes(2); + expect(cstSegmentation.config.style.getStyle).toHaveBeenCalledWith({ + viewportId, + segmentationId: mockCornerstoneRepresentations[0].segmentationId, + type: 'Labelmap', + }); + expect(cstSegmentation.config.style.getStyle).toHaveBeenCalledWith({ + viewportId, + segmentationId: mockCornerstoneRepresentations[1].segmentationId, + type: 'Labelmap', + }); + }); + + it('should throw an error if the segmentation is not found', () => { + jest + .spyOn(cstSegmentation.state, 'getSegmentationRepresentations') + .mockReturnValueOnce( + mockCornerstoneRepresentations as cstTypes.SegmentationRepresentation[] + ); + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(null as cstTypes.Segmentation); + + expect(() => service.getSegmentationRepresentations(viewportId)).toThrow( + `Segmentation with ID ${mockCornerstoneRepresentations[0].segmentationId} not found.` + ); + }); + + it('should forward the specifier to the cornerstone getSegmentationRepresentations', () => { + jest + .spyOn(cstSegmentation.state, 'getSegmentationRepresentations') + .mockReturnValueOnce( + mockCornerstoneRepresentations as cstTypes.SegmentationRepresentation[] + ); + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(cstSegmentation.config.color, 'getSegmentIndexColor') + .mockReturnValue([0, 0, 0, 1]); + jest + .spyOn(cstSegmentation.config.visibility, 'getSegmentIndexVisibility') + .mockReturnValue(true); + jest.spyOn(cstSegmentation.config.style, 'getStyle').mockReturnValue({}); + + service.getSegmentationRepresentations(viewportId, { + segmentationId: mockCornerstoneRepresentations[0].segmentationId, + type: mockCornerstoneRepresentations[0].type as csToolsEnums.SegmentationRepresentations, + }); + + expect(cstSegmentation.state.getSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.getSegmentationRepresentations).toHaveBeenCalledWith( + viewportId, + { + segmentationId: mockCornerstoneRepresentations[0].segmentationId, + type: mockCornerstoneRepresentations[0].type, + } + ); + }); + }); + + describe('destroy', () => { + it('should remove event listeners and reset the service', () => { + jest.spyOn(service, 'reset'); + + service.destroy(); + + expect(eventTarget.removeEventListener).toHaveBeenCalledTimes(7); + + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_MODIFIED, + expect.any(Function) + ); + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_REMOVED, + expect.any(Function) + ); + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_DATA_MODIFIED, + expect.any(Function) + ); + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_REPRESENTATION_MODIFIED, + expect.any(Function) + ); + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_REPRESENTATION_ADDED, + expect.any(Function) + ); + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_REPRESENTATION_REMOVED, + expect.any(Function) + ); + expect(eventTarget.removeEventListener).toHaveBeenCalledWith( + csToolsEnums.Events.SEGMENTATION_ADDED, + expect.any(Function) + ); + + expect(service.reset).toHaveBeenCalled(); + }); + }); + + describe('addSegmentationRepresentation', () => { + describe('stack viewport', () => { + it('should add a non volume segmentation representation to stack viewport through Cornerstone updateLabelmapSegmentationImageReferences', async () => { + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(serviceManagerMock.services.cornerstoneViewportService, 'getCornerstoneViewport') + // only needed interfaces for the addSegmentationRepresentation call + .mockReturnValue(mockCornerstoneStackViewport as unknown as csTypes.IStackViewport); + jest + .spyOn(cstSegmentation.state, 'updateLabelmapSegmentationImageReferences') + .mockReturnValue('labelmapImageId'); + jest + .spyOn(cstSegmentation, 'addSegmentationRepresentations') + .mockReturnValueOnce(undefined); + + const callback = jest.fn(); + service.subscribe(service.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED, callback); + + await service.addSegmentationRepresentation(viewportId, { + segmentationId: mockCornerstoneSegmentation.segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + }); + + expect( + cstSegmentation.state.updateLabelmapSegmentationImageReferences + ).toHaveBeenCalledTimes(1); + expect( + cstSegmentation.state.updateLabelmapSegmentationImageReferences + ).toHaveBeenCalledWith(viewportId, mockCornerstoneSegmentation.segmentationId); + + // this will be called directly because there's no OHIF conversion needed + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledWith(viewportId, [ + { + type: csToolsEnums.SegmentationRepresentations.Labelmap, + segmentationId: mockCornerstoneSegmentation.segmentationId, + config: { colorLUTOrIndex: undefined }, // expected since _segmentationIdToColorLUTIndexMap wasn't previous set + }, + ]); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith({ + segmentationId: mockCornerstoneSegmentation.segmentationId, + }); + }); + + it('should add a non volume segmentation representation to stack viewport through SegmentationService conversion attempt', async () => { + const frameOfReferenceUID = 'frameOfReferenceUID'; + const imageId = 'imageId'; + const viewportGridServiceUnsubscribe = jest.fn(); + + const prevViewPresentation = {}; + const prevViewReference = {}; + + mockCornerstoneStackViewport.getFrameOfReferenceUID.mockReturnValueOnce( + frameOfReferenceUID + ); + mockCornerstoneStackViewport.getViewPresentation.mockReturnValueOnce(prevViewPresentation); + mockCornerstoneStackViewport.getViewReference.mockReturnValueOnce(prevViewReference); + + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(serviceManagerMock.services.cornerstoneViewportService, 'getCornerstoneViewport') + // only needed interfaces for the addSegmentationRepresentation call + .mockReturnValue(mockCornerstoneStackViewport as unknown as csTypes.IStackViewport); + jest + .spyOn(cstSegmentation.state, 'updateLabelmapSegmentationImageReferences') + .mockReturnValue(undefined); + jest.spyOn(cstSegmentation, 'getLabelmapImageIds').mockReturnValue([imageId]); + jest + .spyOn(cstSegmentation, 'addSegmentationRepresentations') + .mockReturnValueOnce(undefined); + jest + .spyOn(cache, 'getImage') + .mockReturnValueOnce({ FrameOfReferenceUID: frameOfReferenceUID } as csTypes.IImage); + jest.spyOn(serviceManagerMock.services.viewportGridService, 'getState').mockReturnValue({ + viewports: new Map([ + [ + viewportId, + { displaySetInstanceUIDs: ['displaySetInstanceUID'], viewportOptions: {} }, + ], + ]), + }); + jest + .spyOn(cstSegmentation, 'addSegmentationRepresentations') + .mockReturnValueOnce(undefined); + jest.spyOn(serviceManagerMock.services.viewportGridService, 'subscribe').mockReturnValue({ + unsubscribe: viewportGridServiceUnsubscribe, + }); + + // awaiting here will wait for publishing of viewportGridService.EVENTS.GRID_STATE_CHANGED (deadlock avoidance) + const serviceAddSegmentationRepresentationPromise = service.addSegmentationRepresentation( + viewportId, + { + segmentationId: mockCornerstoneSegmentation.segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + } + ); + + let _addSegmentationRepresentationCallback; + const waitForSubscription = async () => { + let remainingAttempts = 100; + + while (_addSegmentationRepresentationCallback === undefined && remainingAttempts > 0) { + _addSegmentationRepresentationCallback = jest.mocked( + serviceManagerMock.services.viewportGridService.subscribe + ).mock.calls[0]?.[1]; + + await new Promise(resolve => resolve(void 0)); + remainingAttempts--; + } + }; + + await waitForSubscription(); + + // trigger callback being awaited + _addSegmentationRepresentationCallback(); + await serviceAddSegmentationRepresentationPromise; + + expect(cstSegmentation.getLabelmapImageIds).toHaveBeenCalledTimes(1); + expect(cstSegmentation.getLabelmapImageIds).toHaveBeenCalledWith( + mockCornerstoneSegmentation.segmentationId + ); + + expect(mockCornerstoneStackViewport.getFrameOfReferenceUID).toHaveBeenCalledTimes(1); + expect(mockCornerstoneStackViewport.getFrameOfReferenceUID).toHaveBeenCalledWith(); + + expect(cache.getImage).toHaveBeenCalledTimes(1); + expect(cache.getImage).toHaveBeenCalledWith(imageId); + + expect(serviceManagerMock.services.viewportGridService.getState).toHaveBeenCalledTimes(1); + expect(serviceManagerMock.services.viewportGridService.getState).toHaveBeenCalledWith(); + + expect(mockCornerstoneStackViewport.getViewPresentation).toHaveBeenCalledTimes(1); + expect(mockCornerstoneStackViewport.getViewPresentation).toHaveBeenCalledWith(); + + expect(mockCornerstoneStackViewport.getViewReference).toHaveBeenCalledTimes(1); + expect(mockCornerstoneStackViewport.getViewReference).toHaveBeenCalledWith(); + + expect( + serviceManagerMock.services.cornerstoneViewportService.getCornerstoneViewport + ).toHaveBeenCalledTimes(2); + expect( + serviceManagerMock.services.cornerstoneViewportService.getCornerstoneViewport + ).toHaveBeenCalledWith(viewportId); + + expect(mockCornerstoneStackViewport.element.addEventListener).toHaveBeenCalledTimes(1); + expect(mockCornerstoneStackViewport.element.addEventListener).toHaveBeenCalledWith( + csEnums.Events.VOLUME_VIEWPORT_NEW_VOLUME, + expect.any(Function) + ); + + const volumeViewportNewVolumeHandlerCallback = jest.mocked( + mockCornerstoneStackViewport.element.addEventListener + ).mock.calls[0][1]; + + expect( + serviceManagerMock.services.viewportGridService.setDisplaySetsForViewport + ).toHaveBeenCalledTimes(1); + expect( + serviceManagerMock.services.viewportGridService.setDisplaySetsForViewport + ).toHaveBeenCalledWith({ + viewportId, + displaySetInstanceUIDs: ['displaySetInstanceUID'], + viewportOptions: { viewportType: ViewportType.ORTHOGRAPHIC }, + }); + + expect( + cstSegmentation.triggerSegmentationEvents.triggerSegmentationRepresentationModified + ).toHaveBeenCalledTimes(1); + expect( + cstSegmentation.triggerSegmentationEvents.triggerSegmentationRepresentationModified + ).toHaveBeenCalledWith( + viewportId, + mockCornerstoneSegmentation.segmentationId, + csToolsEnums.SegmentationRepresentations.Labelmap + ); + + jest + .spyOn(serviceManagerMock.services.cornerstoneViewportService, 'getCornerstoneViewport') + // only needed interfaces for the addSegmentationRepresentation call + .mockReturnValue(mockCornerstoneVolumeViewport as unknown as csTypes.IVolumeViewport); + + volumeViewportNewVolumeHandlerCallback(); + + expect( + serviceManagerMock.services.cornerstoneViewportService.getCornerstoneViewport + ).toHaveBeenCalledTimes(3); + expect( + serviceManagerMock.services.cornerstoneViewportService.getCornerstoneViewport + ).toHaveBeenCalledWith(viewportId); + + expect(mockCornerstoneVolumeViewport.setViewPresentation).toHaveBeenCalledTimes(1); + expect(mockCornerstoneVolumeViewport.setViewPresentation).toHaveBeenCalledWith( + prevViewPresentation + ); + + expect(mockCornerstoneVolumeViewport.setViewReference).toHaveBeenCalledTimes(1); + expect(mockCornerstoneVolumeViewport.setViewReference).toHaveBeenCalledWith( + prevViewReference + ); + + expect(mockCornerstoneVolumeViewport.render).toHaveBeenCalledTimes(1); + expect(mockCornerstoneVolumeViewport.render).toHaveBeenCalledWith(); + + expect(mockCornerstoneStackViewport.element.removeEventListener).toHaveBeenCalledTimes(1); + expect(mockCornerstoneStackViewport.element.removeEventListener).toHaveBeenCalledWith( + csEnums.Events.VOLUME_VIEWPORT_NEW_VOLUME, + expect.any(Function) + ); + + expect(serviceManagerMock.services.viewportGridService.subscribe).toHaveBeenCalledTimes(1); + expect(serviceManagerMock.services.viewportGridService.subscribe).toHaveBeenCalledWith( + serviceManagerMock.services.viewportGridService.EVENTS.GRID_STATE_CHANGED, + expect.any(Function) + ); + + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledWith(viewportId, [ + { + type: csToolsEnums.SegmentationRepresentations.Labelmap, + segmentationId: mockCornerstoneSegmentation.segmentationId, + config: { colorLUTOrIndex: undefined }, + }, + ]); + + expect(viewportGridServiceUnsubscribe).toHaveBeenCalledTimes(1); + expect(viewportGridServiceUnsubscribe).toHaveBeenCalledWith(); + }); + + it('should add a volume segmentation representation to stack viewport through SegmentationService conversion', async () => { + mockCornerstoneStackViewport.getViewPresentation.mockReturnValueOnce({}); + mockCornerstoneStackViewport.getViewReference.mockReturnValueOnce({}); + + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockVolumeCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(serviceManagerMock.services.cornerstoneViewportService, 'getCornerstoneViewport') + // only needed interfaces for the addSegmentationRepresentation call + .mockReturnValue(mockCornerstoneStackViewport as unknown as csTypes.IStackViewport); + jest.spyOn(serviceManagerMock.services.viewportGridService, 'getState').mockReturnValue({ + viewports: new Map([ + [ + viewportId, + { displaySetInstanceUIDs: ['displaySetInstanceUID'], viewportOptions: {} }, + ], + ]), + }); + + // awaiting here will wait for publishing of viewportGridService.EVENTS.GRID_STATE_CHANGED (deadlock avoidance) + const serviceAddSegmentationRepresentationPromise = service.addSegmentationRepresentation( + viewportId, + { + segmentationId: mockCornerstoneSegmentation.segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + } + ); + + let _addSegmentationRepresentationCallback; + const waitForSubscription = async () => { + let remainingAttempts = 100; + + while (_addSegmentationRepresentationCallback === undefined && remainingAttempts > 0) { + _addSegmentationRepresentationCallback = jest.mocked( + serviceManagerMock.services.viewportGridService.subscribe + ).mock.calls[0]?.[1]; + + await new Promise(resolve => resolve(void 0)); + remainingAttempts--; + } + }; + + await waitForSubscription(); + + // trigger callback being awaited + _addSegmentationRepresentationCallback(); + await serviceAddSegmentationRepresentationPromise; + + // guarantee the early exit at handleStackViewportCase (isVolumeSegmentation) + expect( + cstSegmentation.state.updateLabelmapSegmentationImageReferences + ).not.toHaveBeenCalled(); + + expect( + serviceManagerMock.services.viewportGridService.setDisplaySetsForViewport + ).toHaveBeenCalledTimes(1); + expect( + serviceManagerMock.services.viewportGridService.setDisplaySetsForViewport + ).toHaveBeenCalledWith({ + viewportId, + displaySetInstanceUIDs: ['displaySetInstanceUID'], + viewportOptions: { viewportType: ViewportType.ORTHOGRAPHIC }, + }); + + const volumeViewportNewVolumeHandlerCallback = jest.mocked( + mockCornerstoneStackViewport.element.addEventListener + ).mock.calls[0][1]; + + jest + .spyOn(serviceManagerMock.services.cornerstoneViewportService, 'getCornerstoneViewport') + // only needed interfaces for the addSegmentationRepresentation call + .mockReturnValue(mockCornerstoneVolumeViewport as unknown as csTypes.IVolumeViewport); + + volumeViewportNewVolumeHandlerCallback(); + + expect( + serviceManagerMock.services.cornerstoneViewportService.getCornerstoneViewport + ).toHaveBeenCalledTimes(3); + expect( + serviceManagerMock.services.cornerstoneViewportService.getCornerstoneViewport + ).toHaveBeenCalledWith(viewportId); + + expect(mockCornerstoneStackViewport.element.removeEventListener).toHaveBeenCalledTimes(1); + expect(mockCornerstoneStackViewport.element.removeEventListener).toHaveBeenCalledWith( + csEnums.Events.VOLUME_VIEWPORT_NEW_VOLUME, + expect.any(Function) + ); + + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledWith(viewportId, [ + { + type: csToolsEnums.SegmentationRepresentations.Labelmap, + segmentationId: mockCornerstoneSegmentation.segmentationId, + config: { colorLUTOrIndex: undefined }, + }, + ]); + }); + }); + + describe('volume viewport', () => { + it('should add a segmentation representation to volume viewport without need for handling', async () => { + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(serviceManagerMock.services.cornerstoneViewportService, 'getCornerstoneViewport') + // only needed interfaces for the addSegmentationRepresentation call + .mockReturnValue(mockCornerstoneVolumeViewport as unknown as csTypes.IVolumeViewport); + jest + .spyOn(cstSegmentation, 'addSegmentationRepresentations') + .mockReturnValueOnce(undefined); + + const callback = jest.fn(); + service.subscribe(service.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED, callback); + + await service.addSegmentationRepresentation(viewportId, { + segmentationId: mockCornerstoneSegmentation.segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + }); + + expect(serviceManagerMock.services.viewportGridService.getState).not.toHaveBeenCalled(); + + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledWith(viewportId, [ + { + type: csToolsEnums.SegmentationRepresentations.Surface, + segmentationId: mockCornerstoneSegmentation.segmentationId, + config: { colorLUTOrIndex: undefined }, + }, + ]); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith({ + segmentationId: mockCornerstoneSegmentation.segmentationId, + }); + }); + + it('should add a surface segmentation representation to volume viewport without need for handling', async () => { + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(serviceManagerMock.services.cornerstoneViewportService, 'getCornerstoneViewport') + // only needed interfaces for the addSegmentationRepresentation call + .mockReturnValue({ + ...mockCornerstoneVolumeViewport, + type: ViewportType.VOLUME_3D, + } as unknown as csTypes.IVolumeViewport); + jest + .spyOn(cstSegmentation, 'addSegmentationRepresentations') + .mockReturnValueOnce(undefined); + + await service.addSegmentationRepresentation(viewportId, { + segmentationId: mockCornerstoneSegmentation.segmentationId, + }); + + expect(serviceManagerMock.services.viewportGridService.getState).not.toHaveBeenCalled(); + + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledWith(viewportId, [ + { + type: csToolsEnums.SegmentationRepresentations.Surface, + segmentationId: mockCornerstoneSegmentation.segmentationId, + config: { colorLUTOrIndex: undefined }, + }, + ]); + }); + + it('should add a volume segmentation representation to volume viewport through SegmentationService handling', async () => { + mockCornerstoneVolumeViewport.type = ViewportType.ORTHOGRAPHIC; + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockVolumeCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(serviceManagerMock.services.cornerstoneViewportService, 'getCornerstoneViewport') + .mockReturnValue(mockCornerstoneVolumeViewport as unknown as csTypes.IVolumeViewport); + jest + .spyOn(cstSegmentation, 'addSegmentationRepresentations') + .mockReturnValueOnce(undefined); + + const callback = jest.fn(); + service.subscribe(service.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED, callback); + + await service.addSegmentationRepresentation(viewportId, { + segmentationId: mockCornerstoneSegmentation.segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + }); + + expect(serviceManagerMock.services.viewportGridService.getState).not.toHaveBeenCalled(); + + expect(mockCornerstoneVolumeViewport.getFrameOfReferenceUID).not.toHaveBeenCalled(); + + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledWith(viewportId, [ + { + type: csToolsEnums.SegmentationRepresentations.Labelmap, + segmentationId: mockCornerstoneSegmentation.segmentationId, + config: { colorLUTOrIndex: undefined }, + }, + ]); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith({ + segmentationId: mockCornerstoneSegmentation.segmentationId, + }); + mockCornerstoneVolumeViewport.type = ViewportType.VOLUME_3D; + }); + + it('should add a volume segmentation representation to volume viewport through SegmentationService handling', async () => { + const frameOfReferenceUID = 'frameOfReferenceUID'; + const imageId = 'imageId'; + + mockCornerstoneVolumeViewport.type = ViewportType.ORTHOGRAPHIC; + mockCornerstoneVolumeViewport.getFrameOfReferenceUID.mockReturnValueOnce( + frameOfReferenceUID + ); + + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(serviceManagerMock.services.cornerstoneViewportService, 'getCornerstoneViewport') + .mockReturnValue(mockCornerstoneVolumeViewport as unknown as csTypes.IVolumeViewport); + jest + .spyOn(cstSegmentation, 'addSegmentationRepresentations') + .mockReturnValueOnce(undefined); + jest.spyOn(cstSegmentation, 'getLabelmapImageIds').mockReturnValue([imageId]); + jest + .spyOn(cache, 'getImage') + .mockReturnValueOnce({ FrameOfReferenceUID: frameOfReferenceUID } as csTypes.IImage); + jest + .spyOn(cstSegmentation.helpers, 'convertStackToVolumeLabelmap') + .mockReturnValueOnce(undefined); + + const callback = jest.fn(); + service.subscribe(service.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED, callback); + + await service.addSegmentationRepresentation(viewportId, { + segmentationId: mockCornerstoneSegmentation.segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + }); + + expect(mockCornerstoneVolumeViewport.getFrameOfReferenceUID).toHaveBeenCalledTimes(1); + expect(mockCornerstoneVolumeViewport.getFrameOfReferenceUID).toHaveBeenCalledWith(); + + expect(cstSegmentation.getLabelmapImageIds).toHaveBeenCalledTimes(1); + expect(cstSegmentation.getLabelmapImageIds).toHaveBeenCalledWith( + mockCornerstoneSegmentation.segmentationId + ); + + expect(cache.getImage).toHaveBeenCalledTimes(1); + expect(cache.getImage).toHaveBeenCalledWith(imageId); + + expect(cstSegmentation.helpers.convertStackToVolumeLabelmap).toHaveBeenCalledTimes(1); + expect(cstSegmentation.helpers.convertStackToVolumeLabelmap).toHaveBeenCalledWith( + mockCornerstoneSegmentation + ); + + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledWith(viewportId, [ + { + type: csToolsEnums.SegmentationRepresentations.Labelmap, + segmentationId: mockCornerstoneSegmentation.segmentationId, + config: { colorLUTOrIndex: undefined }, + }, + ]); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith({ + segmentationId: mockCornerstoneSegmentation.segmentationId, + }); + mockCornerstoneVolumeViewport.type = ViewportType.VOLUME_3D; + }); + }); + + it('should early return if the viewport is not found', () => { + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(serviceManagerMock.services.cornerstoneViewportService, 'getCornerstoneViewport') + .mockReturnValue(null); + jest.spyOn(console, 'warn').mockReturnValue(undefined); + + const callback = jest.fn(); + service.subscribe(service.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED, callback); + + service.addSegmentationRepresentation(viewportId, { + segmentationId: mockCornerstoneSegmentation.segmentationId, + }); + + expect(console.warn).toHaveBeenCalledTimes(1); + expect(console.warn).toHaveBeenCalledWith(`Viewport with id ${viewportId} not found.`); + + expect(callback).not.toHaveBeenCalled(); + }); + }); + + describe('createLabelmapForDisplaySet', () => { + it('should create a labelmap for a non dynamic volume display set', async () => { + const displaySet = { + imageIds: ['imageId'], + isDynamicVolume: false, + SeriesNumber: 1, + SeriesDescription: 'Series Description', + } as unknown as AppTypes.DisplaySet; + + jest + .spyOn(imageLoader, 'createAndCacheDerivedLabelmapImages') + .mockReturnValue([{ imageId: 'imageId' }] as csTypes.IImage[]); + jest + .spyOn(cstSegmentation.state, 'getSegmentations') + .mockReturnValue([{ segmentationId: 'segmentationId' }] as cstTypes.Segmentation[]); + jest.spyOn(service, 'addOrUpdateSegmentation').mockReturnValue(undefined); + + const retrievedSegmentationId = await service.createLabelmapForDisplaySet(displaySet); + + expect(imageLoader.createAndCacheDerivedLabelmapImages).toHaveBeenCalledTimes(1); + expect(imageLoader.createAndCacheDerivedLabelmapImages).toHaveBeenCalledWith(['imageId']); + + expect(cstSegmentation.state.getSegmentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.getSegmentations).toHaveBeenCalledWith(); + + expect(service.addOrUpdateSegmentation).toHaveBeenCalledTimes(1); + expect(service.addOrUpdateSegmentation).toHaveBeenCalledWith({ + config: { + cachedStats: { + info: 'S1: Series Description', + }, + label: 'Segmentation 2', + segments: { + '1': { + active: true, + label: 'Segment 1', + }, + }, + }, + representation: { + data: { + imageIds: ['imageId'], + referencedImageIds: ['imageId'], + }, + type: 'Labelmap', + }, + segmentationId: expect.any(String), + }); + + expect(retrievedSegmentationId).toEqual(expect.any(String)); + }); + + it('should create a labelmap for a dynamic volume display set', async () => { + const segmentationId = 'segmentationId'; + const displaySet = { + imageIds: ['imageId'], + isDynamicVolume: true, + SeriesNumber: 1, + SeriesDescription: 'Series Description', + dynamicVolumeInfo: { + timePoints: ['timePoint1', 'timePoint2', 'timePoint3'], + }, + } as unknown as AppTypes.DisplaySet; + + jest + .spyOn(imageLoader, 'createAndCacheDerivedLabelmapImages') + .mockReturnValue([{ imageId: 'imageId' }] as csTypes.IImage[]); + jest + .spyOn(cstSegmentation.state, 'getSegmentations') + .mockReturnValue([{ segmentationId }] as cstTypes.Segmentation[]); + jest.spyOn(service, 'addOrUpdateSegmentation').mockReturnValue(undefined); + + const options = { + segmentationId, + segments: { + 1: { + label: 'Custom Segment 1', + active: true, + }, + }, + FrameOfReferenceUID: 'frameOfReferenceUID', + label: 'Segmentation 2', + }; + + const retrievedSegmentationId = await service.createLabelmapForDisplaySet( + displaySet, + options + ); + + expect(imageLoader.createAndCacheDerivedLabelmapImages).toHaveBeenCalledTimes(1); + expect(imageLoader.createAndCacheDerivedLabelmapImages).toHaveBeenCalledWith('timePoint2'); + + expect(service.addOrUpdateSegmentation).toHaveBeenCalledTimes(1); + expect(service.addOrUpdateSegmentation).toHaveBeenCalledWith({ + config: { + cachedStats: { + info: 'S1: Series Description', + }, + label: 'Segmentation 2', + segments: { + '1': { + active: true, + label: 'Custom Segment 1', + }, + }, + }, + representation: { + data: { + imageIds: ['imageId'], + referencedImageIds: 'timePoint2', + }, + type: 'Labelmap', + }, + segmentationId: segmentationId, + }); + + expect(retrievedSegmentationId).toEqual(segmentationId); + }); + }); + + describe('createSegmentationForSEGDisplaySet', () => { + it('should throw an error if the type is not labelmap', async () => { + await expect( + service.createSegmentationForSEGDisplaySet( + {}, + { + type: csToolsEnums.SegmentationRepresentations.Contour, + } + ) + ).rejects.toThrow('Only labelmap type is supported for SEG display sets right now'); + }); + + it('should throw and error if the labelmap images are not found', async () => { + await expect( + service.createSegmentationForSEGDisplaySet({ + labelMapImages: [], + }) + ).rejects.toThrow('SEG reading failed'); + }); + + it('should throw an error if the referenced display set is not found', async () => { + jest + .spyOn(serviceManagerMock.services.displaySetService, 'getDisplaySetByUID') + .mockReturnValue({ + instances: [], + }); + + await expect( + service.createSegmentationForSEGDisplaySet({ + displaySetInstanceUID: 'display-set-uid', + referencedDisplaySetInstanceUID: 'non-existent-display-set-uid', + labelMapImages: [{}, {}], + }) + ).rejects.toThrow('No instances were provided for the referenced display set of the SEG'); + }); + + it('it should create a segmentation for a SEG display set', async () => { + const segmentationId = 'segmentationId'; + + const voxelManager = { + getScalarData: jest.fn().mockReturnValue([1, 0, 0]), + setScalarData: jest.fn(), + }; + + const segDisplaySet = { + centroids: new Map([ + [0, { image: { x: 0, y: 0, z: 0 }, world: { x: 0, y: 0, z: 0 } }], + [2, { image: { x: 200, y: 200, z: 200 }, world: { x: 200, y: 200, z: 200 } }], + ]), + displaySetInstanceUID: 'display-set-uid', + referencedDisplaySetInstanceUID: 'existent-display-set-uid', + labelMapImages: [ + { imageId: 'imageId1', referencedImageId: 'referencedImageId1', voxelManager }, + { imageId: 'imageId2', referencedImageId: 'referencedImageId2', voxelManager }, + ], + segMetadata: { + data: [ + {}, + { + SegmentedPropertyCategoryCodeSequence: { + CodeMeaning: 'Segmented Property Category Code Sequence', + }, + SegmentNumber: '1', + SegmentLabel: 'Segment 1', + SegmentAlgorithmType: 'MANUAL', + SegmentAlgorithmName: 'OHIF Brush', + SegmentedPropertyTypeCodeSequence: { + CodeMeaning: 'Segmented Property Category Code Sequence', + }, + rgba: [255, 0, 0, 255], + }, + { + SegmentedPropertyCategoryCodeSequence: { + CodeMeaning: 'Segmented Property Category Code Sequence', + }, + SegmentNumber: '2', + SegmentAlgorithmType: 'MANUAL', + SegmentAlgorithmName: 'OHIF Brush', + SegmentedPropertyTypeCodeSequence: { + CodeMeaning: 'Segmented Property Type Code Sequence', + }, + rgba: [0, 255, 0, 255], + }, + ], + }, + SeriesDate: '2025-01-01', + SeriesDescription: 'Series Description', + }; + + const referencedDisplaySet = { + instances: [{ imageId: 'referencedImageId1' }, { imageId: 'referencedImageId2' }], + }; + + jest + .spyOn(serviceManagerMock.services.displaySetService, 'getDisplaySetByUID') + .mockReturnValue(referencedDisplaySet); + // @ts-expect-error - jest can't handle Array.prototype.flat typing + jest.spyOn(Array.prototype, 'flat'); + jest.spyOn(metaData, 'get').mockReturnValue({}); + jest.spyOn(service, 'addOrUpdateSegmentation').mockReturnValue(undefined); + + const callback = jest.fn(); + service.subscribe(service.EVENTS.SEGMENTATION_LOADING_COMPLETE, callback); + + const retrievedSegmentationId = await service.createSegmentationForSEGDisplaySet( + segDisplaySet, + { + type: csToolsEnums.SegmentationRepresentations.Labelmap, + segmentationId, + } + ); + + expect( + serviceManagerMock.services.displaySetService.getDisplaySetByUID + ).toHaveBeenCalledTimes(1); + expect(serviceManagerMock.services.displaySetService.getDisplaySetByUID).toHaveBeenCalledWith( + 'existent-display-set-uid' + ); + + expect(Array.prototype.flat).toHaveBeenCalledTimes(1); + + expect(metaData.get).toHaveBeenCalledTimes(2); + expect(metaData.get).toHaveBeenCalledWith('instance', 'referencedImageId1'); + expect(metaData.get).toHaveBeenCalledWith('instance', 'referencedImageId2'); + + expect(voxelManager.getScalarData).toHaveBeenCalledTimes(2); + expect(voxelManager.getScalarData).toHaveBeenCalledWith(); + expect(voxelManager.setScalarData).toHaveBeenCalledTimes(2); + expect(voxelManager.setScalarData).toHaveBeenCalledWith([1, 0, 0]); + + expect(cstSegmentation.state.addColorLUT).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.addColorLUT).toHaveBeenCalledWith([ + [0, 0, 0, 0], + [255, 0, 0, 255], + [0, 255, 0, 255], + ]); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith({ + segmentationId, + segDisplaySet, + }); + + const expectedSegmentation = { + config: { + label: segDisplaySet.SeriesDescription, + segments: { + '1': { + active: false, + cachedStats: { + algorithmName: 'OHIF Brush', + algorithmType: 'MANUAL', + category: 'Segmented Property Category Code Sequence', + center: { + image: [0, 0, 0], + world: [0, 0, 0], + }, + modifiedTime: '2025-01-01', + type: 'Segmented Property Category Code Sequence', + }, + label: 'Segment 1', + locked: false, + segmentIndex: 1, + }, + '2': { + active: false, + cachedStats: { + algorithmName: 'OHIF Brush', + algorithmType: 'MANUAL', + category: 'Segmented Property Category Code Sequence', + center: { + image: [200, 200, 200], + world: [200, 200, 200], + }, + modifiedTime: '2025-01-01', + type: 'Segmented Property Type Code Sequence', + }, + label: 'Segment 2', + locked: false, + segmentIndex: 2, + }, + }, + }, + representation: { + data: { + imageIds: ['imageId1', 'imageId2'], + referencedImageIds: ['referencedImageId1', 'referencedImageId2'], + }, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + }, + segmentationId, + }; + + expect(service.addOrUpdateSegmentation).toHaveBeenCalledTimes(1); + expect(service.addOrUpdateSegmentation).toHaveBeenCalledWith(expectedSegmentation); + + const expectedSegDisplaySet = { + ...segDisplaySet, + firstSegmentedSliceImageId: 'referencedImageId1', + imageIds: ['imageId1', 'imageId2'], + images: [ + { + imageId: 'imageId1', + referencedImageId: 'referencedImageId1', + voxelManager, + }, + { + imageId: 'imageId2', + referencedImageId: 'referencedImageId2', + voxelManager, + }, + ], + isLoaded: true, + labelMapImages: [ + { + imageId: 'imageId1', + referencedImageId: 'referencedImageId1', + voxelManager, + }, + { + imageId: 'imageId2', + referencedImageId: 'referencedImageId2', + voxelManager, + }, + ], + }; + + expect(segDisplaySet).toEqual(expectedSegDisplaySet); + + expect(retrievedSegmentationId).toEqual(segmentationId); + }); + }); + + describe('createSegmentationForRTDisplaySet', () => { + it('should throw an error if the type is not contour', async () => { + await expect( + service.createSegmentationForRTDisplaySet( + { + modality: 'RTSTRUCT', + }, + { + type: csToolsEnums.SegmentationRepresentations.Labelmap, + } + ) + ).rejects.toThrow('Only contour type is supported for RT display sets right now'); + }); + + it('should throw an error if the structureSet is not loaded', async () => { + await expect( + service.createSegmentationForRTDisplaySet( + { + modality: 'RTSTRUCT', + displaySetInstanceUID: 'display-set-uid', + }, + { + type: csToolsEnums.SegmentationRepresentations.Contour, + } + ) + ).rejects.toThrow( + 'To create the contours from RT displaySet, the displaySet should be loaded first. You can perform rtDisplaySet.load() before calling this method.' + ); + }); + + it('should throw and error if structureSet does not contain any ROIContours', async () => { + const rtStructDisplaySet = { + modality: 'RTSTRUCT', + displaySetInstanceUID: 'display-set-uid', + referencedDisplaySetInstanceUID: 'existent-display-set-uid', + structureSet: { + ReferencedSOPInstanceUIDsSet: new Set(['referencedImageId1', 'referencedImageId2']), + ROIContours: [], + }, + }; + + const referencedDisplaySet = { + instances: [{ imageId: 'referencedImageId1' }, { imageId: 'referencedImageId2' }], + }; + + jest + .spyOn(serviceManagerMock.services.displaySetService, 'getDisplaySetByUID') + .mockReturnValue(referencedDisplaySet); + + await expect( + service.createSegmentationForRTDisplaySet(rtStructDisplaySet, { + type: csToolsEnums.SegmentationRepresentations.Contour, + }) + ).rejects.toThrow( + 'The structureSet does not contain any ROIContours. Please ensure the structureSet is loaded first.' + ); + + expect( + serviceManagerMock.services.displaySetService.getDisplaySetByUID + ).toHaveBeenCalledTimes(1); + expect(serviceManagerMock.services.displaySetService.getDisplaySetByUID).toHaveBeenCalledWith( + 'existent-display-set-uid' + ); + }); + + it('should create a segmentation for a RTSTRUCT display set', async () => { + const segmentationId = 'segmentationId'; + const rtStructDisplaySet = { + modality: 'RTSTRUCT', + displaySetInstanceUID: 'display-set-uid', + referencedDisplaySetInstanceUID: 'existent-display-set-uid', + SeriesDate: '2025-01-01', + SeriesDescription: 'Series Description', + structureSet: { + ReferencedSOPInstanceUIDsSet: new Set(['referencedIsmageId1', 'referencedImageId2']), + ROIContours: [{}, {}, {}], + frameOfReferenceUID: 'frameOfReferenceUID', + }, + }; + + const referencedDisplaySet = { + instances: [{ imageId: 'referencedImageId1' }, { imageId: 'referencedImageId2' }], + imageIds: ['referencedImageId1', 'referencedImageId2'], + }; + + const allRTStructData = [ + { + data: {}, + id: 'id3', + color: [255, 0, 0, 255], + group: 'group3', + segmentIndex: 3, + geometryId: 'geometryId3', + }, + { + data: {}, + id: 'id2', + color: [0, 255, 0, 255], + group: 'group2', + segmentIndex: 2, + geometryId: 'geometryId2', + }, + { + data: {}, + id: 'id1', + color: [0, 0, 255, 255], + group: 'group1', + segmentIndex: 1, + geometryId: 'geometryId1', + }, + ]; + + jest + .spyOn(serviceManagerMock.services.displaySetService, 'getDisplaySetByUID') + .mockReturnValue(referencedDisplaySet); + jest + .spyOn(MapROIContoursToRTStructData, 'mapROIContoursToRTStructData') + .mockReturnValue(allRTStructData); + jest.spyOn(service, 'addOrUpdateSegmentation').mockReturnValue(undefined); + jest.spyOn(geometryLoader, 'createAndCacheGeometry').mockReturnValue({ + // @ts-expect-error - only mocking needed properties + data: { centroid: [0, 0, 0] }, + }); + + const segmentLoadingCompleteCallback = jest.fn(); + service.subscribe(service.EVENTS.SEGMENT_LOADING_COMPLETE, segmentLoadingCompleteCallback); + + const segmentationLoadingCompleteCallback = jest.fn(); + service.subscribe( + service.EVENTS.SEGMENTATION_LOADING_COMPLETE, + segmentationLoadingCompleteCallback + ); + + const retrievedSegmentationId = await service.createSegmentationForRTDisplaySet( + rtStructDisplaySet, + { + type: csToolsEnums.SegmentationRepresentations.Contour, + segmentationId, + } + ); + + expect(MapROIContoursToRTStructData.mapROIContoursToRTStructData).toHaveBeenCalledTimes(1); + expect(MapROIContoursToRTStructData.mapROIContoursToRTStructData).toHaveBeenCalledWith( + rtStructDisplaySet.structureSet, + rtStructDisplaySet.displaySetInstanceUID + ); + + expect(geometryLoader.createAndCacheGeometry).toHaveBeenCalledTimes(3); + allRTStructData.forEach(data => { + expect(geometryLoader.createAndCacheGeometry).toHaveBeenCalledWith(data.geometryId, { + geometryData: { + data: data.data, + id: data.id, + color: data.color, + frameOfReferenceUID: rtStructDisplaySet.structureSet.frameOfReferenceUID, + segmentIndex: data.segmentIndex, + }, + type: csEnums.GeometryType.CONTOUR, + }); + }); + + expect(segmentLoadingCompleteCallback).toHaveBeenCalledTimes(3); + expect(segmentLoadingCompleteCallback).toHaveBeenCalledWith({ + percentComplete: expect.any(Number), + numSegments: 3, + }); + + expect(cstSegmentation.state.addColorLUT).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.addColorLUT).toHaveBeenCalledWith([ + [0, 0, 0, 0], + [0, 0, 255, 255], + [0, 255, 0, 255], + [255, 0, 0, 255], + ]); + + expect(segmentationLoadingCompleteCallback).toHaveBeenCalledTimes(1); + expect(segmentationLoadingCompleteCallback).toHaveBeenCalledWith({ + segmentationId, + rtDisplaySet: rtStructDisplaySet, + }); + + const expectedSegmentation = { + config: { + label: rtStructDisplaySet.SeriesDescription, + segments: { + '1': { + active: false, + cachedStats: { + center: { + world: [0, 0, 0], + }, + modifiedTime: rtStructDisplaySet.SeriesDate, + }, + group: 'group1', + label: 'id1', + locked: false, + segmentIndex: 1, + }, + '2': { + active: false, + cachedStats: { + center: { + world: [0, 0, 0], + }, + modifiedTime: rtStructDisplaySet.SeriesDate, + }, + group: 'group2', + label: 'id2', + locked: false, + segmentIndex: 2, + }, + '3': { + active: false, + cachedStats: { + center: { + world: [0, 0, 0], + }, + modifiedTime: rtStructDisplaySet.SeriesDate, + }, + group: 'group3', + label: 'id3', + locked: false, + segmentIndex: 3, + }, + }, + }, + representation: { + data: { + geometryIds: ['geometryId1', 'geometryId2', 'geometryId3'], + }, + type: csToolsEnums.SegmentationRepresentations.Contour, + }, + segmentationId, + }; + expect(service.addOrUpdateSegmentation).toHaveBeenCalledTimes(1); + expect(service.addOrUpdateSegmentation).toHaveBeenCalledWith(expectedSegmentation); + + expect(retrievedSegmentationId).toEqual(segmentationId); + + const expectedRtStructDisplaySet = { + ...rtStructDisplaySet, + isLoaded: true, + }; + + expect(rtStructDisplaySet).toEqual(expectedRtStructDisplaySet); + }); + + it('should ignore when a segment fails to initialize', async () => { + const segmentationId = 'segmentationId'; + const rtStructDisplaySet = { + modality: 'RTSTRUCT', + displaySetInstanceUID: 'display-set-uid', + referencedDisplaySetInstanceUID: 'existent-display-set-uid', + SeriesDate: '2025-01-01', + SeriesDescription: 'Series Description', + structureSet: { + ReferencedSOPInstanceUIDsSet: new Set(['referencedIsmageId1', 'referencedImageId2']), + ROIContours: [{}, {}, {}], + frameOfReferenceUID: 'frameOfReferenceUID', + }, + }; + + const referencedDisplaySet = { + instances: [{ imageId: 'referencedImageId1' }, { imageId: 'referencedImageId2' }], + }; + + const allRTStructData = [ + { + data: {}, + id: 'id3', + color: [255, 0, 0, 255], + group: 'group3', + segmentIndex: 3, + geometryId: 'geometryId3', + }, + ]; + + jest + .spyOn(serviceManagerMock.services.displaySetService, 'getDisplaySetByUID') + .mockReturnValue(referencedDisplaySet); + jest + .spyOn(MapROIContoursToRTStructData, 'mapROIContoursToRTStructData') + .mockReturnValue(allRTStructData); + jest + .spyOn(geometryLoader, 'createAndCacheGeometry') + .mockRejectedValue(new Error('Segment Initialization Error') as never); + jest.spyOn(console, 'warn').mockImplementation(() => {}); + jest.spyOn(service, 'addOrUpdateSegmentation').mockReturnValue(undefined); + + const segmentLoadingCompleteCallback = jest.fn(); + service.subscribe(service.EVENTS.SEGMENT_LOADING_COMPLETE, segmentLoadingCompleteCallback); + + const segmentationLoadingCompleteCallback = jest.fn(); + service.subscribe( + service.EVENTS.SEGMENTATION_LOADING_COMPLETE, + segmentationLoadingCompleteCallback + ); + + await expect( + service.createSegmentationForRTDisplaySet(rtStructDisplaySet, { + type: csToolsEnums.SegmentationRepresentations.Contour, + segmentationId, + }) + ).resolves.not.toThrow(); + + expect(segmentLoadingCompleteCallback).not.toHaveBeenCalled(); + + expect(console.warn).toHaveBeenCalledTimes(1); + expect(console.warn).toHaveBeenCalledWith( + `Error initializing contour for segment ${allRTStructData[0].segmentIndex}:`, + expect.any(Error) + ); + + expect(segmentationLoadingCompleteCallback).toHaveBeenCalledTimes(1); + + expect(service.addOrUpdateSegmentation).toHaveBeenCalledTimes(1); + }); + }); + + describe('addOrUpdateSegmentation', () => { + it('should add new segmentation if it does not exist', () => { + const segmentationId = 'segmentationId'; + const segmentationData = { + segmentationId, + config: { + label: 'Segmentation 1', + }, + }; + + jest.spyOn(cstSegmentation.state, 'getSegmentation').mockReturnValue(undefined); + jest.spyOn(cstSegmentation, 'addSegmentations').mockReturnValue(undefined); + + service.addOrUpdateSegmentation(segmentationData); + + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledWith(segmentationId); + + expect(cstSegmentation.addSegmentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.addSegmentations).toHaveBeenCalledWith([segmentationData]); + }); + + it('should update existing segmentation if it exists', () => { + const segmentationId = 'segmentationId'; + const segmentationData = { + segmentationId, + config: { + label: 'Segmentation 1', + }, + }; + + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation); + jest.spyOn(cstSegmentation, 'updateSegmentations').mockReturnValue(undefined); + + service.addOrUpdateSegmentation(segmentationData); + + expect(cstSegmentation.updateSegmentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.updateSegmentations).toHaveBeenCalledWith([ + { segmentationId, payload: segmentationData }, + ]); + }); + }); + + describe('setActiveSegmentation', () => { + it('should set the active segmentation for a viewport', () => { + const viewportId = 'viewportId'; + const segmentationId = 'segmentationId'; + + service.setActiveSegmentation(viewportId, segmentationId); + + expect(cstSegmentation.activeSegmentation.setActiveSegmentation).toHaveBeenCalledTimes(1); + expect(cstSegmentation.activeSegmentation.setActiveSegmentation).toHaveBeenCalledWith( + viewportId, + segmentationId + ); + }); + }); + + describe('getActiveSegmentation', () => { + it('should get the active segmentation for a viewport', () => { + const viewportId = 'viewportId'; + + service.getActiveSegmentation(viewportId); + + expect(cstSegmentation.activeSegmentation.getActiveSegmentation).toHaveBeenCalledTimes(1); + expect(cstSegmentation.activeSegmentation.getActiveSegmentation).toHaveBeenCalledWith( + viewportId + ); + }); + }); + + describe('getActiveSegment', () => { + it('should return undefined if no active segmentation', () => { + const viewportId = 'viewportId'; + jest.spyOn(cstSegmentation.activeSegmentation, 'getActiveSegmentation').mockReturnValue(null); + + const activeSegment = service.getActiveSegment(viewportId); + + expect(cstSegmentation.activeSegmentation.getActiveSegmentation).toHaveBeenCalledTimes(1); + expect(cstSegmentation.activeSegmentation.getActiveSegmentation).toHaveBeenCalledWith( + viewportId + ); + + expect(activeSegment).toBeUndefined(); + }); + + it('should find and return the active segment', () => { + const viewportId = 'viewportId'; + jest + .spyOn(cstSegmentation.activeSegmentation, 'getActiveSegmentation') + .mockReturnValue(mockCornerstoneSegmentation); + + const activeSegment = service.getActiveSegment(viewportId); + + expect(activeSegment).toEqual(mockCornerstoneSegmentation.segments['1']); + }); + }); + + describe('hasCustomStyles', () => { + it('should return true if the segmentation has custom styles', () => { + const viewportId = 'viewportId'; + const segmentationId = 'segmentationId'; + const type = csToolsEnums.SegmentationRepresentations.Contour; + + jest.spyOn(cstSegmentation.config.style, 'hasCustomStyle').mockReturnValue(true); + + const hasCustomStyles = service.hasCustomStyles({ viewportId, segmentationId, type }); + + expect(cstSegmentation.config.style.hasCustomStyle).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.style.hasCustomStyle).toHaveBeenCalledWith({ + viewportId, + segmentationId, + type, + }); + + expect(hasCustomStyles).toBe(true); + }); + }); + + describe('getStyle', () => { + it('should return the style for the segmentation', () => { + const viewportId = 'viewportId'; + const segmentationId = 'segmentationId'; + const type = csToolsEnums.SegmentationRepresentations.Contour; + const segmentIndex = 1; + + jest.spyOn(cstSegmentation.config.style, 'getStyle').mockReturnValue({}); + + const style = service.getStyle({ viewportId, segmentationId, type, segmentIndex }); + + expect(cstSegmentation.config.style.getStyle).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.style.getStyle).toHaveBeenCalledWith({ + viewportId, + segmentationId, + type, + segmentIndex, + }); + + expect(style).toEqual({}); + }); + }); + + describe('setStyle', () => { + it('should set the style for the segmentation', () => { + const viewportId = 'viewportId'; + const segmentationId = 'segmentationId'; + const type = csToolsEnums.SegmentationRepresentations.Contour; + const segmentIndex = 1; + const style = { + fillAlpha: 0.5, + outlineWidth: 2, + renderOutline: true, + renderFill: true, + }; + + jest.spyOn(cstSegmentation.config.style, 'setStyle').mockReturnValue(undefined); + + service.setStyle({ viewportId, segmentationId, type, segmentIndex }, style); + + expect(cstSegmentation.config.style.setStyle).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.style.setStyle).toHaveBeenCalledWith( + { + viewportId, + segmentationId, + type, + segmentIndex, + }, + style, + true + ); + }); + }); + + describe('resetToGlobalStyle', () => { + it('should reset the style for the segmentation', () => { + jest.spyOn(cstSegmentation.config.style, 'resetToGlobalStyle').mockReturnValue(undefined); + + service.resetToGlobalStyle(); + + expect(cstSegmentation.config.style.resetToGlobalStyle).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.style.resetToGlobalStyle).toHaveBeenCalledWith(); + }); + }); + + describe('addSegment', () => { + it('should throw an error if the segment index is 0', () => { + const segmentationId = 'segmentationId'; + const config = { + segmentIndex: 0, + }; + + expect(() => service.addSegment(segmentationId, config)).toThrow( + 'Segment index 0 is reserved for "no label"' + ); + }); + + it('should add a new segment with next available index if not provided', () => { + const segmentationId = 'segmentationId'; + const config = { + label: 'New Segment 2', + visibility: true, + }; + + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation); + jest.spyOn(cstSegmentation, 'updateSegmentations').mockReturnValue(undefined); + jest.spyOn(cstSegmentation.segmentIndex, 'setActiveSegmentIndex').mockReturnValue(undefined); + jest + .spyOn(cstSegmentation.state, 'getViewportIdsWithSegmentation') + .mockReturnValue(['viewportId']); + jest + .spyOn(cstSegmentation.config.visibility, 'setSegmentIndexVisibility') + .mockReturnValue(undefined); + + service.addSegment(segmentationId, config); + + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledTimes(2); + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledWith(segmentationId); + + expect(cstSegmentation.updateSegmentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.updateSegmentations).toHaveBeenCalledWith([ + { + segmentationId, + payload: { + segments: { + ...mockCornerstoneSegmentation.segments, + '2': { + label: 'New Segment 2', + segmentIndex: 2, + cachedStats: {}, + locked: false, + ...config, + }, + }, + }, + }, + ]); + + expect(cstSegmentation.segmentIndex.setActiveSegmentIndex).toHaveBeenCalledTimes(1); + expect(cstSegmentation.segmentIndex.setActiveSegmentIndex).toHaveBeenCalledWith( + segmentationId, + 2 + ); + + expect(cstSegmentation.state.getViewportIdsWithSegmentation).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.getViewportIdsWithSegmentation).toHaveBeenCalledWith( + segmentationId + ); + + expect(cstSegmentation.config.visibility.setSegmentIndexVisibility).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.visibility.setSegmentIndexVisibility).toHaveBeenCalledWith( + 'viewportId', + { segmentationId, type: undefined }, + 2, + config.visibility + ); + }); + + it('should set properties if segment index already exists', () => { + const segmentationId = 'segmentationId'; + const config = { + segmentIndex: 1, + isLocked: false, + active: true, + color: [255, 0, 0, 255] as csTypes.Color, + }; + + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation); + jest.spyOn(cstSegmentation, 'updateSegmentations').mockReturnValue(undefined); + jest.spyOn(cstSegmentation.segmentIndex, 'setActiveSegmentIndex').mockReturnValue(undefined); + jest.spyOn(service, 'getViewportIdsWithSegmentation').mockReturnValue(['viewportId']); + jest + .spyOn(cstSegmentation.segmentLocking, 'setSegmentIndexLocked') + .mockReturnValue(undefined); + jest.spyOn(cstSegmentation.config.color, 'setSegmentIndexColor').mockReturnValue(undefined); + jest + .spyOn(cstSegmentation.state, 'getSegmentationRepresentations') + .mockReturnValue([{ colorLUTIndex: 1 }] as cstTypes.SegmentationRepresentation[]); + + service.addSegment(segmentationId, config); + + expect(cstSegmentation.updateSegmentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.updateSegmentations).toHaveBeenCalledWith([ + { + segmentationId, + payload: { + segments: { + ...mockCornerstoneSegmentation.segments, + '1': { + label: 'Segment 1', + segmentIndex: 1, + cachedStats: {}, + locked: false, + active: true, + ...config, + }, + }, + }, + }, + ]); + + expect(cstSegmentation.segmentLocking.setSegmentIndexLocked).toHaveBeenCalledTimes(1); + expect(cstSegmentation.segmentLocking.setSegmentIndexLocked).toHaveBeenCalledWith( + segmentationId, + 1, + config.isLocked + ); + + expect(cstSegmentation.config.color.setSegmentIndexColor).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.color.setSegmentIndexColor).toHaveBeenCalledWith( + 'viewportId', + segmentationId, + 1, + config.color + ); + }); + }); + + describe('removeSegment', () => { + it('should remove the segment', () => { + const segmentationId = 'segmentationId'; + const segmentIndex = 1; + jest.spyOn(cstSegmentation, 'removeSegment').mockReturnValue(undefined); + + service.removeSegment(segmentationId, segmentIndex); + + expect(cstSegmentation.removeSegment).toHaveBeenCalledTimes(1); + expect(cstSegmentation.removeSegment).toHaveBeenCalledWith(segmentationId, segmentIndex, { + recordHistory: true, + }); + }); + }); + + describe('setSegmentVisibility', () => { + it('should set the visibility of the segment', () => { + const viewportId = 'viewportId'; + const segmentationId = 'segmentationId'; + const segmentIndex = 1; + const isVisible = true; + const type = csToolsEnums.SegmentationRepresentations.Contour; + + jest + .spyOn(cstSegmentation.config.visibility, 'setSegmentIndexVisibility') + .mockReturnValue(undefined); + + service.setSegmentVisibility(viewportId, segmentationId, segmentIndex, isVisible, type); + + expect(cstSegmentation.config.visibility.setSegmentIndexVisibility).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.visibility.setSegmentIndexVisibility).toHaveBeenCalledWith( + viewportId, + { segmentationId, type }, + segmentIndex, + isVisible + ); + }); + }); + + describe('setSegmentLocked', () => { + it('should set the locked status of the segment', () => { + const segmentationId = 'segmentationId'; + const segmentIndex = 1; + const isLocked = true; + + jest + .spyOn(cstSegmentation.segmentLocking, 'setSegmentIndexLocked') + .mockReturnValue(undefined); + + service.setSegmentLocked(segmentationId, segmentIndex, isLocked); + + expect(cstSegmentation.segmentLocking.setSegmentIndexLocked).toHaveBeenCalledTimes(1); + expect(cstSegmentation.segmentLocking.setSegmentIndexLocked).toHaveBeenCalledWith( + segmentationId, + segmentIndex, + isLocked + ); + }); + }); + + describe('toggleSegmentLocked', () => { + it('should toggle the locked status of the segment', () => { + const segmentationId = 'segmentationId'; + const segmentIndex = 1; + const isLocked = true; + + jest.spyOn(cstSegmentation.segmentLocking, 'isSegmentIndexLocked').mockReturnValue(isLocked); + jest + .spyOn(cstSegmentation.segmentLocking, 'setSegmentIndexLocked') + .mockReturnValue(undefined); + + service.toggleSegmentLocked(segmentationId, segmentIndex); + + expect(cstSegmentation.segmentLocking.isSegmentIndexLocked).toHaveBeenCalledTimes(1); + expect(cstSegmentation.segmentLocking.isSegmentIndexLocked).toHaveBeenCalledWith( + segmentationId, + segmentIndex + ); + + expect(cstSegmentation.segmentLocking.setSegmentIndexLocked).toHaveBeenCalledTimes(1); + expect(cstSegmentation.segmentLocking.setSegmentIndexLocked).toHaveBeenCalledWith( + segmentationId, + segmentIndex, + !isLocked + ); + }); + }); + + describe('toggleSegmentVisibility', () => { + it('should toggle the visibility of the segment', () => { + const viewportId = 'viewportId'; + const segmentationId = 'segmentationId'; + const segmentIndex = 1; + const isVisible = true; + const type = csToolsEnums.SegmentationRepresentations.Contour; + + jest + .spyOn(cstSegmentation.config.visibility, 'getSegmentIndexVisibility') + .mockReturnValue(isVisible); + jest + .spyOn(cstSegmentation.config.visibility, 'setSegmentIndexVisibility') + .mockReturnValue(undefined); + + service.toggleSegmentVisibility(viewportId, segmentationId, segmentIndex, type); + + expect(cstSegmentation.config.visibility.getSegmentIndexVisibility).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.visibility.getSegmentIndexVisibility).toHaveBeenCalledWith( + viewportId, + { segmentationId, type }, + segmentIndex + ); + + expect(cstSegmentation.config.visibility.setSegmentIndexVisibility).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.visibility.setSegmentIndexVisibility).toHaveBeenCalledWith( + viewportId, + { segmentationId, type }, + segmentIndex, + !isVisible + ); + }); + }); + + describe('setSegmentColor', () => { + const viewportId = 'viewportId'; + const segmentationId = 'segmentationId'; + const segmentIndex = 1; + const color = [255, 0, 0, 255] as csTypes.Color; + + it('should set the color of the segment', () => { + jest + .spyOn(cstSegmentation.state, 'getSegmentationRepresentations') + .mockReturnValue([{ colorLUTIndex: 1 }] as cstTypes.SegmentationRepresentation[]); + jest.spyOn(cstSegmentation.config.color, 'setSegmentIndexColor').mockReturnValue(undefined); + + service.setSegmentColor(viewportId, segmentationId, segmentIndex, color); + + expect(cstSegmentation.state.getSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.getSegmentationRepresentations).toHaveBeenCalledWith( + viewportId, + { segmentationId } + ); + + expect(cstSegmentation.config.color.setSegmentIndexColor).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.color.setSegmentIndexColor).toHaveBeenCalledWith( + viewportId, + segmentationId, + segmentIndex, + color + ); + }); + + it('should set the color of the segment with the colorLUTIndex', async () => { + jest + .spyOn(cstSegmentation.state, 'getSegmentationRepresentations') + .mockReturnValue([{ colorLUTIndex: 1 }] as cstTypes.SegmentationRepresentation[]); + jest.spyOn(cstSegmentation.config.color, 'setSegmentIndexColor').mockReturnValue(undefined); + + service.setSegmentColor(viewportId, segmentationId, segmentIndex, color); + + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation as cstTypes.Segmentation); + jest + .spyOn(serviceManagerMock.services.cornerstoneViewportService, 'getCornerstoneViewport') + // only needed interfaces for the addSegmentationRepresentation call + .mockReturnValue(mockCornerstoneStackViewport as unknown as csTypes.IStackViewport); + jest + .spyOn(cstSegmentation.state, 'updateLabelmapSegmentationImageReferences') + .mockReturnValue('labelmapImageId'); + jest.spyOn(cstSegmentation, 'addSegmentationRepresentations').mockReturnValueOnce(undefined); + + await service.addSegmentationRepresentation(viewportId, { + segmentationId: segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + config: { blendMode: csEnums.BlendModes.COMPOSITE }, + suppressEvents: true, + }); + + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.addSegmentationRepresentations).toHaveBeenCalledWith(viewportId, [ + { + type: csToolsEnums.SegmentationRepresentations.Labelmap, + segmentationId: segmentationId, + config: { colorLUTOrIndex: 1, blendMode: csEnums.BlendModes.COMPOSITE }, + }, + ]); + }); + }); + + describe('getSegmentColor', () => { + it('should get the color of the segment', () => { + const viewportId = 'viewportId'; + const segmentationId = 'segmentationId'; + const segmentIndex = 1; + const color = [255, 0, 0, 255] as csTypes.Color; + + jest.spyOn(cstSegmentation.config.color, 'getSegmentIndexColor').mockReturnValue(color); + + const returnedColor = service.getSegmentColor(viewportId, segmentationId, segmentIndex); + + expect(cstSegmentation.config.color.getSegmentIndexColor).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.color.getSegmentIndexColor).toHaveBeenCalledWith( + viewportId, + segmentationId, + segmentIndex + ); + + expect(returnedColor).toEqual(color); + }); + }); + + describe('getLabelmapVolume', () => { + it('should get the labelmap volume for the segmentation', () => { + const segmentationId = 'segmentationId'; + const labelmapVolume = { id: 'volumeId' }; + mockCornerstoneSegmentation.representationData.Labelmap = { volumeId: 'volumeId' }; + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation); + // @ts-expect-error - no need to mock every property for this test + jest.spyOn(cache, 'getVolume').mockReturnValue(labelmapVolume); + + const returnedLabelmapVolume = service.getLabelmapVolume(segmentationId); + + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledWith(segmentationId); + + expect(cache.getVolume).toHaveBeenCalledTimes(1); + expect(cache.getVolume).toHaveBeenCalledWith(labelmapVolume.id); + + expect(returnedLabelmapVolume).toEqual(labelmapVolume); + + mockCornerstoneSegmentation.representationData.Labelmap = {}; + }); + + it('should return null if the segmentation does not have a labelmap volume', () => { + const segmentationId = 'segmentationId'; + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation); + + const returnedLabelmapVolume = service.getLabelmapVolume(segmentationId); + + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledTimes(1); + + expect(returnedLabelmapVolume).toEqual(null); + }); + }); + + describe('setSegmentLabel', () => { + it('should set the label of the segment', () => { + const segmentationId = 'segmentationId'; + const segmentIndex = 1; + const label = 'New Segment 1'; + + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation); + jest.spyOn(cstSegmentation, 'updateSegmentations').mockReturnValue(undefined); + + service.setSegmentLabel(segmentationId, segmentIndex, label); + + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledWith(segmentationId); + + expect(cstSegmentation.updateSegmentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.updateSegmentations).toHaveBeenCalledWith([ + { + segmentationId, + payload: { + segments: { + ...mockCornerstoneSegmentation.segments, + [segmentIndex]: { ...mockCornerstoneSegmentation.segments[segmentIndex], label }, + }, + }, + }, + ]); + }); + }); + + describe('setActiveSegment', () => { + it('should set the active segment for the segmentation', () => { + const segmentationId = 'segmentationId'; + const segmentIndex = 1; + + jest.spyOn(cstSegmentation.segmentIndex, 'setActiveSegmentIndex').mockReturnValue(undefined); + + service.setActiveSegment(segmentationId, segmentIndex); + + expect(cstSegmentation.segmentIndex.setActiveSegmentIndex).toHaveBeenCalledTimes(1); + expect(cstSegmentation.segmentIndex.setActiveSegmentIndex).toHaveBeenCalledWith( + segmentationId, + segmentIndex + ); + }); + }); + + describe('setRenderInactiveSegmentations', () => { + it('should set the render inactive segmentations for the viewport', () => { + const viewportId = 'viewportId'; + const renderInactive = true; + + jest + .spyOn(cstSegmentation.config.style, 'setRenderInactiveSegmentations') + .mockReturnValue(undefined); + + service.setRenderInactiveSegmentations(viewportId, renderInactive); + + expect(cstSegmentation.config.style.setRenderInactiveSegmentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.style.setRenderInactiveSegmentations).toHaveBeenCalledWith( + viewportId, + renderInactive + ); + }); + }); + + describe('getRenderInactiveSegmentations', () => { + it('should get the render inactive segmentations for the viewport', () => { + const viewportId = 'viewportId'; + const renderInactive = true; + + jest + .spyOn(cstSegmentation.config.style, 'getRenderInactiveSegmentations') + .mockReturnValue(renderInactive); + + const returnedRenderInactive = service.getRenderInactiveSegmentations(viewportId); + + expect(cstSegmentation.config.style.getRenderInactiveSegmentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.style.getRenderInactiveSegmentations).toHaveBeenCalledWith( + viewportId + ); + + expect(returnedRenderInactive).toEqual(renderInactive); + }); + }); + + describe('setSegmentationGroupStats', () => { + it('should set the segmentation group stats', () => { + const segmentationIds = ['segmentationId1', 'segmentationId2']; + const stats = { key: 'value' }; + + service.setSegmentationGroupStats(segmentationIds, stats); + + const returnedStats = service.getSegmentationGroupStats(segmentationIds); + + expect(returnedStats).toEqual(stats); + }); + }); + + describe('getSegmentationGroupStats', () => { + it('should get the segmentation group stats', () => { + const segmentationIds = ['segmentationId1', 'segmentationId2']; + const stats = { key: 'value' }; + + let returnedStats = service.getSegmentationGroupStats(segmentationIds); + + expect(returnedStats).toEqual(undefined); + + service.setSegmentationGroupStats(segmentationIds, stats); + + returnedStats = service.getSegmentationGroupStats(segmentationIds); + + expect(returnedStats).toEqual(stats); + }); + }); + + describe('toggleSegmentationRepresentationVisibility', () => { + it('should toggle the visibility of the segmentation representation', () => { + jest.spyOn(service, 'getSegmentationRepresentations').mockReturnValue(representations); + jest + .spyOn(cstSegmentation.config.visibility, 'getHiddenSegmentIndices') + .mockReturnValue(new Set()); + jest + .spyOn(cstSegmentation.config.visibility, 'setSegmentationRepresentationVisibility') + .mockReturnValue(undefined); + + service.toggleSegmentationRepresentationVisibility(viewportId, { + segmentationId: representations[0].segmentationId, + type: representations[0].type, + }); + + expect(service.getSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(service.getSegmentationRepresentations).toHaveBeenCalledWith(viewportId, { + segmentationId: representations[0].segmentationId, + type: representations[0].type, + }); + + expect(cstSegmentation.config.visibility.getHiddenSegmentIndices).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.visibility.getHiddenSegmentIndices).toHaveBeenCalledWith( + viewportId, + { + segmentationId: representations[0].segmentationId, + type: representations[0].type, + } + ); + + expect( + cstSegmentation.config.visibility.setSegmentationRepresentationVisibility + ).toHaveBeenCalledTimes(1); + expect( + cstSegmentation.config.visibility.setSegmentationRepresentationVisibility + ).toHaveBeenCalledWith( + viewportId, + { + segmentationId: representations[0].segmentationId, + type: representations[0].type, + }, + false + ); + }); + }); + + describe('setSegmentationRepresentationVisibility', () => { + it('should early return if the representation is not found', () => { + jest.spyOn(service, 'getSegmentationRepresentations').mockReturnValueOnce([]); + jest.spyOn(console, 'debug').mockReturnValue(undefined); + jest.spyOn(cstSegmentation.config.visibility, 'setSegmentationRepresentationVisibility'); + + service['_setSegmentationRepresentationVisibility']( + viewportId, + representations[0].segmentationId, + representations[0].type, + true + ); + + expect(console.debug).toHaveBeenCalledTimes(1); + expect(console.debug).toHaveBeenCalledWith( + 'No segmentation representation found for the given viewportId and segmentationId' + ); + + expect( + cstSegmentation.config.visibility.setSegmentationRepresentationVisibility + ).not.toHaveBeenCalled(); + }); + }); + + describe('getViewportIdsWithSegmentation', () => { + it('should get the viewport ids with the segmentation', () => { + const segmentationId = 'segmentationId'; + const viewportIds = ['viewportId1', 'viewportId2']; + + jest + .spyOn(cstSegmentation.state, 'getViewportIdsWithSegmentation') + .mockReturnValue(viewportIds); + + const returnedViewportIds = service.getViewportIdsWithSegmentation(segmentationId); + + expect(cstSegmentation.state.getViewportIdsWithSegmentation).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.getViewportIdsWithSegmentation).toHaveBeenCalledWith( + segmentationId + ); + + expect(returnedViewportIds).toEqual(viewportIds); + }); + }); + + describe('clearSegmentationRepresentations', () => { + it('should clear the segmentation representations', () => { + const viewportId = 'viewportId'; + jest.spyOn(service, 'removeSegmentationRepresentations').mockReturnValue(undefined); + + service.clearSegmentationRepresentations(viewportId); + + expect(service.removeSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(service.removeSegmentationRepresentations).toHaveBeenCalledWith(viewportId); + }); + }); + + describe('remove', () => { + it('should remove the segmentation', () => { + const segmentationId = 'segmentationId'; + + jest.spyOn(cstSegmentation.state, 'removeSegmentation').mockReturnValue(undefined); + + service.remove(segmentationId); + + expect(cstSegmentation.state.removeSegmentation).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.removeSegmentation).toHaveBeenCalledWith(segmentationId); + }); + }); + + describe('removeAllSegmentations', () => { + it('should remove all segmentations', () => { + jest.spyOn(cstSegmentation.state, 'removeAllSegmentations').mockReturnValue(undefined); + + service.removeAllSegmentations(); + + expect(cstSegmentation.state.removeAllSegmentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.removeAllSegmentations).toHaveBeenCalledWith(); + }); + }); + + describe('removeSegmentationRepresentations', () => { + it('should remove the segmentation representations', () => { + const viewportId = 'viewportId'; + const specifier = { + segmentationId: 'segmentationId', + type: csToolsEnums.SegmentationRepresentations.Labelmap, + }; + jest.spyOn(cstSegmentation, 'removeSegmentationRepresentations').mockReturnValue(undefined); + + service.removeSegmentationRepresentations(viewportId, specifier); + + expect(cstSegmentation.removeSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(cstSegmentation.removeSegmentationRepresentations).toHaveBeenCalledWith( + viewportId, + specifier + ); + }); + }); + + describe('jumpToSegmentCenter', () => { + it('should early return if the center is not found', () => { + const segmentationId = 'segmentationId'; + const segmentIndex = 1; + + jest.spyOn(console, 'warn').mockReturnValue(undefined); + jest.spyOn(cstSegmentation.state, 'getSegmentation').mockReturnValue(null); + jest.spyOn(service, 'getViewportIdsWithSegmentation'); + + service.jumpToSegmentCenter(segmentationId, segmentIndex); + + expect(console.warn).toHaveBeenCalledTimes(1); + expect(console.warn).toHaveBeenCalledWith( + 'No center found for segmentation', + segmentationId, + segmentIndex + ); + + expect(service.getViewportIdsWithSegmentation).not.toHaveBeenCalled(); + }); + + it('should correctly handle scenario where viewportId is provided', () => { + const segmentationId = 'segmentationId'; + const segmentIndex = 1; + const viewportId = 'viewportId'; + const viewport = { jumpToWorld: jest.fn() }; + + const segmentationWithCenter = { + ...mockCornerstoneSegmentation, + segments: { + ...mockCornerstoneSegmentation.segments, + [segmentIndex]: { + ...mockCornerstoneSegmentation.segments[segmentIndex], + cachedStats: { center: { image: [1, 1, 1], world: [10, 10, 10] } }, + }, + }, + }; + + jest.spyOn(cstSegmentation.state, 'getSegmentation').mockReturnValue(segmentationWithCenter); + jest.spyOn(service, 'getViewportIdsWithSegmentation'); + // @ts-expect-error - mock only needed properties + getEnabledElementByViewportId.mockReturnValue({ viewport }); + jest.spyOn(service, 'highlightSegment').mockReturnValue(undefined); + + service.jumpToSegmentCenter(segmentationId, segmentIndex, viewportId); + + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledWith(segmentationId); + + expect(service.getViewportIdsWithSegmentation).not.toHaveBeenCalled(); + + expect(viewport.jumpToWorld).toHaveBeenCalledTimes(1); + expect(viewport.jumpToWorld).toHaveBeenCalledWith([10, 10, 10]); + + expect(service.highlightSegment).toHaveBeenCalledTimes(1); + expect(service.highlightSegment).toHaveBeenCalledWith( + segmentationId, + segmentIndex, + viewportId, + 0.9, + 750, + false, + EasingFunctionEnum.EASE_IN_OUT + ); + }); + + it('should correctly handle custom animation parameters', () => { + const segmentationId = 'segmentationId'; + const segmentIndex = 1; + const viewportId = 'viewportId'; + const viewport = { jumpToWorld: jest.fn() }; + + const segmentationWithNamedStatsCenter = { + ...mockCornerstoneSegmentation, + segments: { + ...mockCornerstoneSegmentation.segments, + [segmentIndex]: { + ...mockCornerstoneSegmentation.segments[segmentIndex], + cachedStats: { + namedStats: { center: { value: [1, 1, 1] } }, + }, + }, + }, + }; + + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(segmentationWithNamedStatsCenter); + jest.spyOn(service, 'getViewportIdsWithSegmentation').mockReturnValue([viewportId]); + // @ts-expect-error - mock only needed properties + getEnabledElementByViewportId.mockReturnValue({ viewport }); + jest.spyOn(service, 'highlightSegment').mockReturnValue(undefined); + + service.jumpToSegmentCenter( + segmentationId, + segmentIndex, + undefined, + 0.8, + true, + 800, + false, + EasingFunctionEnum.LINEAR + ); + + expect(service.getViewportIdsWithSegmentation).toHaveBeenCalledTimes(1); + expect(service.getViewportIdsWithSegmentation).toHaveBeenCalledWith(segmentationId); + + expect(viewport.jumpToWorld).toHaveBeenCalledTimes(1); + expect(viewport.jumpToWorld).toHaveBeenCalledWith([1, 1, 1]); + + expect(service.highlightSegment).toHaveBeenCalledTimes(1); + expect(service.highlightSegment).toHaveBeenCalledWith( + segmentationId, + segmentIndex, + viewportId, + 0.8, + 800, + false, + EasingFunctionEnum.LINEAR + ); + }); + }); + + describe('highlightSegment', () => { + describe('LABELMAP Segmentation', () => { + it('should correctly handle scenario where viewportId is provided', () => { + const segmentIndex = 1; + const viewportId = 'viewportId'; + const initialFillAlpha = 0.3; + const animationDuration = 750; + const animationFunctionConstant = 0.85; + const easingFunction = jest.fn(() => animationFunctionConstant); + + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation); + jest.spyOn(service, 'getViewportIdsWithSegmentation'); + jest.spyOn(service, 'getSegmentationRepresentations').mockReturnValue(representations); + jest.spyOn(cstSegmentation.config.style, 'getStyle').mockReturnValue({ + fillAlpha: initialFillAlpha, + }); + jest.spyOn(window, 'requestAnimationFrame').mockReturnValue(undefined); + jest.spyOn(EasingFunctionMap, 'get').mockReturnValue(easingFunction); + + service.highlightSegment( + representations[0].segmentationId, + segmentIndex, + viewportId, + 0.9, + animationDuration, + false, + EasingFunctionEnum.EASE_IN_OUT + ); + + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledTimes(1); + expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledWith( + representations[0].segmentationId + ); + + expect(service.getViewportIdsWithSegmentation).not.toHaveBeenCalled(); + + expect(service.getSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(service.getSegmentationRepresentations).toHaveBeenCalledWith(viewportId, { + segmentationId: representations[0].segmentationId, + }); + + expect(cstSegmentation.config.style.getStyle).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.style.getStyle).toHaveBeenCalledWith({ + viewportId, + segmentationId: representations[0].segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + segmentIndex, + }); + + expect(window.requestAnimationFrame).toHaveBeenCalledTimes(1); + expect(window.requestAnimationFrame).toHaveBeenCalledWith(expect.any(Function)); + + const animationCallback = jest.mocked(window.requestAnimationFrame).mock.calls[0][0]; + + // during animation call + animationCallback(0); + + expect(EasingFunctionMap.get).toHaveBeenCalledTimes(1); + expect(EasingFunctionMap.get).toHaveBeenCalledWith(EasingFunctionEnum.EASE_IN_OUT); + + expect(cstSegmentation.config.style.setStyle).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.style.setStyle).toHaveBeenCalledWith( + { + segmentationId: representations[0].segmentationId, + segmentIndex, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + }, + { + fillAlpha: animationFunctionConstant, + } + ); + + expect(easingFunction).toHaveBeenCalledTimes(1); + expect(easingFunction).toHaveBeenCalledWith(0, initialFillAlpha); + + expect(window.requestAnimationFrame).toHaveBeenCalledTimes(2); + expect(window.requestAnimationFrame).toHaveBeenCalledWith(expect.any(Function)); + + // end of animation call + animationCallback(animationDuration); + + expect(cstSegmentation.config.style.setStyle).toHaveBeenCalledTimes(3); + expect(cstSegmentation.config.style.setStyle).toHaveBeenCalledWith( + { + segmentationId: representations[0].segmentationId, + segmentIndex, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + }, + {}, + false + ); + + expect(window.requestAnimationFrame).not.toHaveBeenCalledTimes(3); + }); + + it('should throw if hideOthers is true', () => { + const segmentIndex = 1; + + jest.spyOn(window, 'clearInterval').mockReturnValue(undefined); + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation); + jest.spyOn(service, 'getViewportIdsWithSegmentation').mockReturnValue([viewportId]); + jest.spyOn(service, 'getSegmentationRepresentations').mockReturnValue(representations); + + expect(() => + service.highlightSegment(representations[0].segmentationId, segmentIndex) + ).toThrow('hideOthers is not working right now'); + + expect(window.clearInterval).not.toHaveBeenCalled(); + + expect(service.getViewportIdsWithSegmentation).toHaveBeenCalledTimes(1); + expect(service.getViewportIdsWithSegmentation).toHaveBeenCalledWith( + representations[0].segmentationId + ); + + expect(service.getSegmentationRepresentations).toHaveBeenCalledTimes(1); + expect(service.getSegmentationRepresentations).toHaveBeenCalledWith(viewportId, { + segmentationId: representations[0].segmentationId, + }); + }); + }); + + describe('CONTOUR Segmentation', () => { + it('should correctly handle scenario where viewportId is provided', () => { + const segmentIndex = 1; + const viewportId = 'viewportId'; + const initialOutlineWidth = 3; + const animationDuration = 750; + const animationFunctionConstant = 5; + const easingFunction = jest.fn(() => animationFunctionConstant); + + const contourRepresentations = [ + { + ...representations[0], + type: csToolsEnums.SegmentationRepresentations.Contour, + }, + ]; + + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation); + jest.spyOn(service, 'getViewportIdsWithSegmentation'); + jest + .spyOn(service, 'getSegmentationRepresentations') + .mockReturnValue(contourRepresentations); + jest.spyOn(cstSegmentation.config.style, 'getStyle').mockReturnValue({ + outlineWidth: initialOutlineWidth, + }); + jest.spyOn(window, 'requestAnimationFrame').mockReturnValue(undefined); + jest.spyOn(EasingFunctionMap, 'get').mockReturnValue(easingFunction); + jest.spyOn(cstSegmentation.config.style, 'resetToGlobalStyle').mockReturnValue(undefined); + + service.highlightSegment( + contourRepresentations[0].segmentationId, + segmentIndex, + viewportId, + 0.9, + animationDuration, + false, + EasingFunctionEnum.LINEAR + ); + + expect(service.getViewportIdsWithSegmentation).not.toHaveBeenCalled(); + + expect(cstSegmentation.config.style.getStyle).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.style.getStyle).toHaveBeenCalledWith({ + type: csToolsEnums.SegmentationRepresentations.Contour, + }); + + expect(window.requestAnimationFrame).toHaveBeenCalledTimes(1); + expect(window.requestAnimationFrame).toHaveBeenCalledWith(expect.any(Function)); + + const animationCallback = jest.mocked(window.requestAnimationFrame).mock.calls[0][0]; + const approximateStartTime = performance.now(); + + // during animation call + animationCallback(approximateStartTime + 200); + + expect(EasingFunctionMap.get).toHaveBeenCalledTimes(1); + expect(EasingFunctionMap.get).toHaveBeenCalledWith(EasingFunctionEnum.LINEAR); + + expect(cstSegmentation.config.style.setStyle).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.style.setStyle).toHaveBeenCalledWith( + { + segmentationId: contourRepresentations[0].segmentationId, + segmentIndex, + type: csToolsEnums.SegmentationRepresentations.Contour, + }, + { + outlineWidth: animationFunctionConstant, + } + ); + + expect(easingFunction).toHaveBeenCalledTimes(1); + expect(easingFunction).toHaveBeenCalledWith(expect.any(Number), initialOutlineWidth, 5); + + expect(window.requestAnimationFrame).toHaveBeenCalledTimes(2); + expect(window.requestAnimationFrame).toHaveBeenCalledWith(expect.any(Function)); + + // end of animation call + animationCallback(approximateStartTime + animationDuration); + + expect(cstSegmentation.config.style.setStyle).toHaveBeenCalledTimes(1); + + expect(cstSegmentation.config.style.resetToGlobalStyle).toHaveBeenCalledTimes(1); + expect(cstSegmentation.config.style.resetToGlobalStyle).toHaveBeenCalledWith(); + + expect(window.requestAnimationFrame).not.toHaveBeenCalledTimes(3); + }); + }); + + it('should clear interval if it exists', () => { + expect(service.highlightIntervalId).toBe(null); + + service.highlightIntervalId = 'intervalId'; + + jest + .spyOn(cstSegmentation.state, 'getSegmentation') + .mockReturnValue(mockCornerstoneSegmentation); + jest.spyOn(service, 'getViewportIdsWithSegmentation'); + jest.spyOn(service, 'getSegmentationRepresentations').mockReturnValue(representations); + jest.spyOn(cstSegmentation.config.style, 'getStyle').mockReturnValue({ + fillAlpha: 0.3, + }); + jest.spyOn(window, 'requestAnimationFrame').mockReturnValue(undefined); + jest.spyOn(window, 'clearInterval').mockReturnValue(undefined); + + service.highlightSegment(representations[0].segmentationId, 1, viewportId, 0.9, 750, false); + + expect(window.clearInterval).toHaveBeenCalledTimes(1); + expect(window.clearInterval).toHaveBeenCalledWith('intervalId'); + }); + }); + + describe('_onSegmentationDataModifiedFromSource', () => { + it('should broadcast the event', () => { + jest.spyOn(eventTarget, 'addEventListener').mockReturnValue(undefined); + + const callback = jest.fn(); + service.subscribe(service.EVENTS.SEGMENTATION_DATA_MODIFIED, callback); + + service.onModeEnter(); + + const _onSegmentationDataModifiedFromSource = jest.mocked(eventTarget.addEventListener).mock + .calls[2][1]; + + _onSegmentationDataModifiedFromSource({ + detail: { + segmentationId: 'segmentationId', + }, + }); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith({ + segmentationId: 'segmentationId', + }); + }); + }); + + describe('_onSegmentationRepresentationModifiedFromSource', () => { + it('should broadcast the event', () => { + jest.spyOn(eventTarget, 'addEventListener').mockReturnValue(undefined); + + const callback = jest.fn(); + service.subscribe(service.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED, callback); + + service.onModeEnter(); + + const _onSegmentationRepresentationModifiedFromSource = jest.mocked( + eventTarget.addEventListener + ).mock.calls[3][1]; + + _onSegmentationRepresentationModifiedFromSource({ + detail: { + segmentationId: 'segmentationId', + viewportId: 'viewportId', + }, + }); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith({ + segmentationId: 'segmentationId', + viewportId: 'viewportId', + }); + }); + }); + + describe('_onSegmentationModifiedFromSource', () => { + it('should broadcast the event', () => { + jest.spyOn(eventTarget, 'addEventListener').mockReturnValue(undefined); + + const callback = jest.fn(); + service.subscribe(service.EVENTS.SEGMENTATION_MODIFIED, callback); + + service.onModeEnter(); + + const _onSegmentationModifiedFromSource = jest.mocked(eventTarget.addEventListener).mock + .calls[0][1]; + + _onSegmentationModifiedFromSource({ + detail: { + segmentationId: 'segmentationId', + }, + }); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith({ + segmentationId: 'segmentationId', + }); + }); + }); + + describe('_onSegmentationAddedFromSource', () => { + it('should broadcast the event', () => { + jest.spyOn(eventTarget, 'addEventListener').mockReturnValue(undefined); + + const callback = jest.fn(); + service.subscribe(service.EVENTS.SEGMENTATION_ADDED, callback); + + service.onModeEnter(); + + const _onSegmentationAddedFromSource = jest.mocked(eventTarget.addEventListener).mock + .calls[6][1]; + + _onSegmentationAddedFromSource({ + detail: { + segmentationId: 'segmentationId', + }, + }); + + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith({ + segmentationId: 'segmentationId', + }); + }); + }); +}); diff --git a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts index b4fb27fa12e..1fc7065376b 100644 --- a/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts +++ b/extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts @@ -9,30 +9,38 @@ import { utilities as csUtils, metaData, } from '@cornerstonejs/core'; +import { ViewportType } from '@cornerstonejs/core/enums'; + import { Enums as csToolsEnums, segmentation as cstSegmentation, Types as cstTypes, + annotation as cstAnnotation, } from '@cornerstonejs/tools'; + import { PubSubService, Types as OHIFTypes } from '@ohif/core'; import i18n from '@ohif/i18n'; -import { easeInOutBell, easeInOutBellRelative } from '../../utils/transitions'; + +import { VOLUME_LOADER_SCHEME } from '../../constants'; import { mapROIContoursToRTStructData } from './RTSTRUCT/mapROIContoursToRTStructData'; -import { SegmentationRepresentations } from '@cornerstonejs/tools/enums'; -import { addColorLUT } from '@cornerstonejs/tools/segmentation/addColorLUT'; -import { getNextColorLUTIndex } from '@cornerstonejs/tools/segmentation/getNextColorLUTIndex'; -import { Segment } from '@cornerstonejs/tools/types/SegmentationStateTypes'; -import { ContourStyle, LabelmapStyle, SurfaceStyle } from '@cornerstonejs/tools/types'; -import { ViewportType } from '@cornerstonejs/core/enums'; import { SegmentationPresentation, SegmentationPresentationItem } from '../../types/Presentation'; -import { updateLabelmapSegmentationImageReferences } from '@cornerstonejs/tools/segmentation/updateLabelmapSegmentationImageReferences'; -import { triggerSegmentationRepresentationModified } from '@cornerstonejs/tools/segmentation/triggerSegmentationEvents'; -import { convertStackToVolumeLabelmap } from '@cornerstonejs/tools/segmentation/helpers/convertStackToVolumeLabelmap'; -import { getLabelmapImageIds } from '@cornerstonejs/tools/segmentation'; -import { VOLUME_LOADER_SCHEME } from '../../constants'; +import { EasingFunctionEnum, EasingFunctionMap } from '../../utils/transitions'; +import { ViewReference } from '@cornerstonejs/core/types'; -const LABELMAP = csToolsEnums.SegmentationRepresentations.Labelmap; -const CONTOUR = csToolsEnums.SegmentationRepresentations.Contour; +const { DefaultHistoryMemo } = csUtils.HistoryMemo; + +const { + Labelmap: LABELMAP, + Contour: CONTOUR, + Surface: SURFACE, +} = csToolsEnums.SegmentationRepresentations; + +const { + getLabelmapImageIds, + helpers: { convertStackToVolumeLabelmap }, + state: { addColorLUT, updateLabelmapSegmentationImageReferences }, + triggerSegmentationEvents: { triggerSegmentationRepresentationModified }, +} = cstSegmentation; export type SegmentRepresentation = { segmentIndex: number; @@ -77,6 +85,10 @@ const EVENTS = { SEGMENT_LOADING_COMPLETE: 'event::segment_loading_complete', // loading completed for all segments SEGMENTATION_LOADING_COMPLETE: 'event::segmentation_loading_complete', + // fired when a contour annotation cut merge process is completed + SEGMENTATION_ANNOTATION_CUT_MERGE_PROCESS_COMPLETED: + 'event::annotation_cut_merge_process_completed', + SEGMENTATION_STYLE_MODIFIED: 'event::segmentation_style_modified', }; const VALUE_TYPES = {}; @@ -149,13 +161,11 @@ class SegmentationService extends PubSubService { const representations = this.getSegmentationRepresentations(viewportId); for (const representation of representations) { - const { segmentationId } = representation; - if (!representation) { continue; } - const { type } = representation; + const { segmentationId, type } = representation; segmentationsMap.set(segmentationId, { segmentationId, @@ -212,7 +222,7 @@ class SegmentationService extends PubSubService { viewportId: string, specifier: { segmentationId?: string; - type?: SegmentationRepresentations; + type?: csToolsEnums.SegmentationRepresentations; } = {} ): SegmentationRepresentation[] { // Get all representations for the viewportId @@ -245,9 +255,19 @@ class SegmentationService extends PubSubService { this._onSegmentationDataModifiedFromSource ); + eventTarget.removeEventListener( + csToolsEnums.Events.SEGMENTATION_REPRESENTATION_MODIFIED, + this._onSegmentationRepresentationModifiedFromSource + ); + eventTarget.removeEventListener( csToolsEnums.Events.SEGMENTATION_REPRESENTATION_ADDED, - this._onSegmentationModifiedFromSource + this._onSegmentationRepresentationModifiedFromSource + ); + + eventTarget.removeEventListener( + csToolsEnums.Events.SEGMENTATION_REPRESENTATION_REMOVED, + this._onSegmentationRepresentationModifiedFromSource ); eventTarget.removeEventListener( @@ -262,11 +282,13 @@ class SegmentationService extends PubSubService { viewportId: string, { segmentationId, + predecessorImageId, type, config, suppressEvents = false, }: { segmentationId: string; + predecessorImageId?: string; type?: csToolsEnums.SegmentationRepresentations; config?: { blendMode?: csEnums.BlendModes; @@ -275,6 +297,9 @@ class SegmentationService extends PubSubService { } ): Promise { const segmentation = this.getSegmentation(segmentationId); + if (segmentation && !segmentation.predecessorImageId && predecessorImageId) { + segmentation.predecessorImageId = predecessorImageId; + } const csViewport = this.getAndValidateViewport(viewportId); if (!csViewport) { @@ -283,11 +308,13 @@ class SegmentationService extends PubSubService { const colorLUTIndex = this._segmentationIdToColorLUTIndexMap.get(segmentationId); - const defaultRepresentationType = csToolsEnums.SegmentationRepresentations.Labelmap; - let representationTypeToUse = type || defaultRepresentationType; let isConverted = false; - if (type === csToolsEnums.SegmentationRepresentations.Labelmap) { + const defaultRepresentationType: csToolsEnums.SegmentationRepresentations = + csViewport.type === ViewportType.VOLUME_3D ? SURFACE : LABELMAP; + let representationTypeToUse = type || defaultRepresentationType; + + if (representationTypeToUse === LABELMAP) { const { isVolumeViewport, isVolumeSegmentation } = this.determineViewportAndSegmentationType( csViewport, segmentation @@ -336,6 +363,39 @@ class SegmentationService extends PubSubService { FrameOfReferenceUID?: string; label?: string; } + ): Promise { + return this._createSegmentationForDisplaySet(displaySet, LABELMAP, options); + } + + public async createContourForDisplaySet( + displaySet: AppTypes.DisplaySet, + options?: { + segmentationId?: string; + segments?: { [segmentIndex: number]: Partial }; + FrameOfReferenceUID?: string; + label?: string; + } + ): Promise { + return this._createSegmentationForDisplaySet(displaySet, CONTOUR, options); + } + + /** + * Private method to create segmentation for a display set with the specified type + * + * @param displaySet - The display set to create the segmentation for + * @param segmentationType - The type of segmentation (SegmentationRepresentations enum) + * @param options - Optional parameters for creating the segmentation + * @returns A promise that resolves to the created segmentation ID + */ + private async _createSegmentationForDisplaySet( + displaySet: AppTypes.DisplaySet, + segmentationType: SegmentationRepresentations, + options?: { + segmentationId?: string; + segments?: { [segmentIndex: number]: Partial }; + FrameOfReferenceUID?: string; + label?: string; + } ): Promise { // Todo: random does not makes sense, make this better, like // labelmap 1, 2, 3 etc @@ -354,14 +414,14 @@ class SegmentationService extends PubSubService { const derivedImages = await imageLoader.createAndCacheDerivedLabelmapImages(referenceImageIds); const segs = this.getSegmentations(); - const label = options.label || `Segmentation ${segs.length + 1}`; + const label = options?.label || `Segmentation ${segs.length + 1}`; const segImageIds = derivedImages.map(image => image.imageId); const segmentationPublicInput: cstTypes.SegmentationPublicInput = { segmentationId, representation: { - type: LABELMAP, + type: segmentationType, data: { imageIds: segImageIds, // referencedVolumeId: this._getVolumeIdForDisplaySet(displaySet), @@ -371,7 +431,7 @@ class SegmentationService extends PubSubService { config: { label, segments: - options.segments && Object.keys(options.segments).length > 0 + options?.segments && Object.keys(options.segments).length > 0 ? options.segments : { 1: { @@ -393,7 +453,7 @@ class SegmentationService extends PubSubService { segDisplaySet, options: { segmentationId?: string; - type: SegmentationRepresentations; + type: csToolsEnums.SegmentationRepresentations; } = { type: LABELMAP, } @@ -503,9 +563,7 @@ class SegmentationService extends PubSubService { }; }); - // get next color lut index - const colorLUTIndex = getNextColorLUTIndex(); - addColorLUT(colorLUT, colorLUTIndex); + const colorLUTIndex = addColorLUT(colorLUT); this._segmentationIdToColorLUTIndexMap.set(segmentationId, colorLUTIndex); this._broadcastEvent(EVENTS.SEGMENTATION_LOADING_COMPLETE, { @@ -540,7 +598,7 @@ class SegmentationService extends PubSubService { rtDisplaySet, options: { segmentationId?: string; - type: SegmentationRepresentations; + type: csToolsEnums.SegmentationRepresentations; } = { type: CONTOUR, } @@ -574,10 +632,19 @@ class SegmentationService extends PubSubService { // find the first image id that contains a referenced SOP instance UID const firstSegmentedSliceImageId = referencedImageIds?.find(imageId => - referencedImageIdsWithGeometry.some(referencedId => imageId.includes(referencedId)) + referencedImageIdsWithGeometry.some(referencedId => + imageId.includes(referencedId as string) + ) ) || null; rtDisplaySet.firstSegmentedSliceImageId = firstSegmentedSliceImageId; + + if (!structureSet.ROIContours?.length) { + throw new Error( + 'The structureSet does not contain any ROIContours. Please ensure the structureSet is loaded first.' + ); + } + // Map ROI contours to RT Struct Data const allRTStructData = mapROIContoursToRTStructData(structureSet, rtDisplaySetUID); @@ -600,12 +667,6 @@ class SegmentationService extends PubSubService { }, }; - if (!structureSet.ROIContours?.length) { - throw new Error( - 'The structureSet does not contain any ROIContours. Please ensure the structureSet is loaded first.' - ); - } - const segments: { [segmentIndex: string]: cstTypes.Segment } = {}; let segmentsCachedStats = {}; @@ -613,7 +674,9 @@ class SegmentationService extends PubSubService { const colorLUT = [[0, 0, 0, 0]]; // First entry is transparent for index 0 // Process each segment similarly to the SEG function - for (const rtStructData of allRTStructData) { + for (let i = 0; i < allRTStructData.length; i++) { + const rtStructData = allRTStructData[i]; + const { data, id, color, segmentIndex, geometryId, group } = rtStructData; // Add the color to the colorLUT array @@ -662,8 +725,7 @@ class SegmentationService extends PubSubService { } // Create and register the colorLUT - const colorLUTIndex = getNextColorLUTIndex(); - addColorLUT(colorLUT, colorLUTIndex); + const colorLUTIndex = addColorLUT(colorLUT); this._segmentationIdToColorLUTIndexMap.set(segmentationId, colorLUTIndex); // Assign processed segments to segmentation config @@ -760,7 +822,7 @@ class SegmentationService extends PubSubService { public hasCustomStyles(specifier: { viewportId: string; segmentationId: string; - type: SegmentationRepresentations; + type: csToolsEnums.SegmentationRepresentations; }): boolean { return cstSegmentation.config.style.hasCustomStyle(specifier); } @@ -768,7 +830,7 @@ class SegmentationService extends PubSubService { public getStyle = (specifier: { viewportId: string; segmentationId: string; - type: SegmentationRepresentations; + type: csToolsEnums.SegmentationRepresentations; segmentIndex?: number; }) => { const style = cstSegmentation.config.style.getStyle(specifier); @@ -778,20 +840,35 @@ class SegmentationService extends PubSubService { public setStyle = ( specifier: { - type: SegmentationRepresentations; + type: csToolsEnums.SegmentationRepresentations; viewportId?: string; segmentationId?: string; segmentIndex?: number; }, - style: LabelmapStyle | ContourStyle | SurfaceStyle + style: cstTypes.LabelmapStyle | cstTypes.ContourStyle | cstTypes.SurfaceStyle, + merge: boolean = true ) => { - cstSegmentation.config.style.setStyle(specifier, style); + cstSegmentation.config.style.setStyle(specifier, style, merge); + this._broadcastEvent(EVENTS.SEGMENTATION_STYLE_MODIFIED, { + specifier, + style, + merge, + }); }; public resetToGlobalStyle = () => { cstSegmentation.config.style.resetToGlobalStyle(); }; + public getNextAvailableSegmentIndex(segmentationId: string): number { + const csSegmentation = this.getCornerstoneSegmentation(segmentationId); + // grab the next available segment index based on the object keys, + // so basically get the highest segment index value + 1 + + const segmentKeys = Object.keys(csSegmentation.segments); + return segmentKeys.length === 0 ? 1 : Math.max(...segmentKeys.map(Number)) + 1; + } + /** * Adds a new segment to the specified segmentation. * @param segmentationId - The ID of the segmentation to add the segment to. @@ -825,10 +902,7 @@ class SegmentationService extends PubSubService { let segmentIndex = config.segmentIndex; if (!segmentIndex) { - // grab the next available segment index based on the object keys, - // so basically get the highest segment index value + 1 - const segmentKeys = Object.keys(csSegmentation.segments); - segmentIndex = segmentKeys.length === 0 ? 1 : Math.max(...segmentKeys.map(Number)) + 1; + segmentIndex = this.getNextAvailableSegmentIndex(segmentationId); } // update the segmentation @@ -879,11 +953,75 @@ class SegmentationService extends PubSubService { }); } + /** + * Creates a memo that records the current state of a segment (segmentationId/segmentIndex) + * so that undo can restore it via addSegment and redo can call removeSegment again + * without recording history. + * + * @param segmentationId - The ID of the segmentation. + * @param segmentIndex - The index of the segment (must still exist when called). + * @param _options - Reserved (e.g. deleting) for future use. + * @returns A Memo with restoreMemo(undo): undo => addSegment, redo => removeSegment (skipRecordingHistory). + */ + public createSegmentIndexMemo( + segmentationId: string, + segmentIndex: number, + _options?: { deleting?: boolean } + ): csTypes.Memo | null { + const csSegmentation = this.getCornerstoneSegmentation(segmentationId); + const segment = csSegmentation?.segments?.[segmentIndex]; + if (!segment) { + return null; + } + + let color: csTypes.Color | undefined; + let visibility: boolean | undefined; + const viewportIds = this.getViewportIdsWithSegmentation(segmentationId); + if (viewportIds.length > 0) { + const firstViewportId = viewportIds[0]; + const representations = this.getSegmentationRepresentations(firstViewportId, { + segmentationId, + }); + const repType = representations[0]?.type ?? LABELMAP; + color = this.getSegmentColor(firstViewportId, segmentationId, segmentIndex); + visibility = cstSegmentation.config.visibility.getSegmentIndexVisibility( + firstViewportId, + { segmentationId, type: repType }, + segmentIndex + ); + } + + const segmentState = { + segmentIndex, + label: segment.label, + isLocked: segment.locked, + active: segment.active, + color, + visibility, + }; + + const service = this; + const memo: csTypes.Memo = { + id: csUtils.uuidv4(), + operationType: 'segmentIndex', + restoreMemo(undo?: boolean) { + if (undo === true) { + service.addSegment(segmentationId, segmentState); + } else { + // Redo: remove the segment via cornerstone without recording history + cstSegmentation.removeSegment(segmentationId, segmentIndex, { recordHistory: false }); + } + }, + }; + return memo; + } + /** * Removes a segment from a segmentation and updates the active segment index if necessary. * * @param segmentationId - The ID of the segmentation containing the segment to remove. * @param segmentIndex - The index of the segment to remove. + * @param options - Optional. skipRecordingHistory: if true, do not push undo memo (used when redoing). * * @remarks * This method performs the following actions: @@ -892,8 +1030,21 @@ class SegmentationService extends PubSubService { * 3. If the removed segment was the active segment, it updates the active segment index. * */ - public removeSegment(segmentationId: string, segmentIndex: number): void { - cstSegmentation.removeSegment(segmentationId, segmentIndex); + public removeSegment( + segmentationId: string, + segmentIndex: number, + options?: { skipRecordingHistory?: boolean } + ): void { + let memo; + if (!options?.skipRecordingHistory) { + memo = this.createSegmentIndexMemo(segmentationId, segmentIndex, { deleting: true }); + DefaultHistoryMemo.startGroupRecording(); + cstSegmentation.removeSegment(segmentationId, segmentIndex, { recordHistory: true }); + DefaultHistoryMemo.push(memo); + DefaultHistoryMemo.endGroupRecording(); + } else { + cstSegmentation.removeSegment(segmentationId, segmentIndex, { recordHistory: false }); + } } public setSegmentVisibility( @@ -901,7 +1052,7 @@ class SegmentationService extends PubSubService { segmentationId: string, segmentIndex: number, isVisible: boolean, - type?: SegmentationRepresentations + type?: csToolsEnums.SegmentationRepresentations ): void { this._setSegmentVisibility(viewportId, segmentationId, segmentIndex, isVisible, type); } @@ -938,7 +1089,7 @@ class SegmentationService extends PubSubService { viewportId: string, segmentationId: string, segmentIndex: number, - type: SegmentationRepresentations + type: csToolsEnums.SegmentationRepresentations ): void { const isVisible = cstSegmentation.config.visibility.getSegmentIndexVisibility( viewportId, @@ -969,6 +1120,12 @@ class SegmentationService extends PubSubService { segmentIndex: number, color: csTypes.Color ): void { + const segmentationRepresentations = this.getSegmentationRepresentations(viewportId, { + segmentationId, + }); + const { colorLUTIndex } = segmentationRepresentations[0]; + this._segmentationIdToColorLUTIndexMap.set(segmentationId, colorLUTIndex); + cstSegmentation.config.color.setSegmentIndexColor( viewportId, segmentationId, @@ -1010,7 +1167,7 @@ class SegmentationService extends PubSubService { public getLabelmapVolume(segmentationId: string) { const csSegmentation = cstSegmentation.state.getSegmentation(segmentationId); const labelmapData = csSegmentation.representationData[ - SegmentationRepresentations.Labelmap + LABELMAP ] as cstTypes.LabelmapToolOperationDataVolume; if (!labelmapData || !labelmapData.volumeId) { @@ -1112,7 +1269,10 @@ class SegmentationService extends PubSubService { */ public toggleSegmentationRepresentationVisibility = ( viewportId: string, - { segmentationId, type }: { segmentationId: string; type: SegmentationRepresentations } + { + segmentationId, + type, + }: { segmentationId: string; type: csToolsEnums.SegmentationRepresentations } ): void => { this._toggleSegmentationRepresentationVisibility(viewportId, segmentationId, type); }; @@ -1161,12 +1321,133 @@ class SegmentationService extends PubSubService { viewportId: string, specifier: { segmentationId?: string; - type?: SegmentationRepresentations; + type?: csToolsEnums.SegmentationRepresentations; } = {} ): void { cstSegmentation.removeSegmentationRepresentations(viewportId, specifier); } + /** + * Jumps to the next slice that contains the specified segment in the viewport. + * For labelmaps, it jumps to the segment center. For contours, it cycles through + * all slices that contain contour data for the segment. + * + * @param segmentationId - The ID of the segmentation + * @param segmentIndex - The index of the segment to jump to + * @param viewportId - Optional viewport ID. If not provided, applies to all viewports with this segmentation + * @param highlightAlpha - Alpha value for highlighting (0-1) + * @param highlightSegment - Whether to highlight the segment after jumping + * @param animationLength - Length of highlight animation in milliseconds + * @param highlightHideOthers - Whether to hide other segments during highlight + * @param animationFunctionType - The easing function to use for animation + */ + public jumpToSegmentNext( + segmentationId: string, + segmentIndex: number, + forViewportId?: string, + direction = 1, + highlightAlpha = 0.9, + highlightSegment = true, + animationLength = 750, + highlightHideOthers = false, + animationFunctionType: EasingFunctionEnum = EasingFunctionEnum.EASE_IN_OUT + ): void { + const viewportIds = forViewportId + ? [forViewportId] + : this.getViewportIdsWithSegmentation(segmentationId); + + viewportIds.forEach(viewportId => { + const representations = this.getSegmentationRepresentations(viewportId, { + segmentationId, + }); + + if (!representations || representations.length === 0) { + return; + } + + const representation = representations[0]; + const { type } = representation; + + // For contours, check if we have a segment center. + const center = + type === CONTOUR ? this._getSegmentCenter(segmentationId, segmentIndex) : undefined; + const canUseSegmentCenter = type !== CONTOUR || !!center; + if (canUseSegmentCenter) { + this.jumpToSegmentCenter( + segmentationId, + segmentIndex, + viewportId, + highlightAlpha, + highlightSegment, + animationLength, + highlightHideOthers, + animationFunctionType, + center + ); + return; + } + + const { viewport } = getEnabledElementByViewportId(viewportId); + if (!viewport) { + return; + } + + const viewRefs = this._getContourViewReferences(segmentationId, viewport, segmentIndex); + if (!viewRefs) { + return; + } + + // Get the current slice index + const currentSliceIndex = viewport.getCurrentImageIdIndex(); + let nearestSliceIndex = null; + let loopSliceIndex = null; + + for (const [sliceIndex] of viewRefs.entries()) { + // Track loop index for wraparound (smallest for forward, largest for backward) + if (direction > 0) { + if (loopSliceIndex === null || sliceIndex < loopSliceIndex) { + loopSliceIndex = sliceIndex; + } + } else { + if (loopSliceIndex === null || sliceIndex > loopSliceIndex) { + loopSliceIndex = sliceIndex; + } + } + + if (direction > 0) { + // Forward direction: find nearest slice after current + if (sliceIndex <= currentSliceIndex) { + continue; + } + if (nearestSliceIndex === null || sliceIndex < nearestSliceIndex) { + nearestSliceIndex = sliceIndex; + } + } else { + // Backward direction: find nearest slice before current + if (sliceIndex >= currentSliceIndex) { + continue; + } + if (nearestSliceIndex === null || sliceIndex > nearestSliceIndex) { + nearestSliceIndex = sliceIndex; + } + } + } + + // Wraparound: if no slice found in direction, use loop index + nearestSliceIndex = nearestSliceIndex ?? loopSliceIndex; + if (nearestSliceIndex === null) { + return; + } + const viewRef = viewRefs.get(nearestSliceIndex); + viewport.setViewReference(viewRef); + viewport.render(); + }); + } + + /** + * Jumps the viewport to the center of hte given segment. + * Only works for labelmaps, and may result in not showing any contours. + */ public jumpToSegmentCenter( segmentationId: string, segmentIndex: number, @@ -1175,15 +1456,16 @@ class SegmentationService extends PubSubService { highlightSegment = true, animationLength = 750, highlightHideOthers = false, - highlightFunctionType = 'ease-in-out' // todo: make animation functions configurable from outside + animationFunctionType: EasingFunctionEnum = EasingFunctionEnum.EASE_IN_OUT, + center?: { image?: csTypes.Point3; world: csTypes.Point3 } ): void { - const center = this._getSegmentCenter(segmentationId, segmentIndex); - if (!center) { + const resolvedCenter = center ?? this._getSegmentCenter(segmentationId, segmentIndex); + if (!resolvedCenter) { console.warn('No center found for segmentation', segmentationId, segmentIndex); return; } - const { world } = center as { world: csTypes.Point3 }; + const { world } = resolvedCenter as { world: csTypes.Point3 }; // need to find which viewports are displaying the segmentation const viewportIds = viewportId @@ -1192,6 +1474,10 @@ class SegmentationService extends PubSubService { viewportIds.forEach(viewportId => { const { viewport } = getEnabledElementByViewportId(viewportId); + if (!viewport?.jumpToWorld) { + return; + } + viewport.jumpToWorld(world); highlightSegment && @@ -1201,7 +1487,8 @@ class SegmentationService extends PubSubService { viewportId, highlightAlpha, animationLength, - highlightHideOthers + highlightHideOthers, + animationFunctionType ); }); } @@ -1213,7 +1500,7 @@ class SegmentationService extends PubSubService { alpha = 0.9, animationLength = 750, hideOthers = true, - highlightFunctionType = 'ease-in-out' + animationFunctionType: EasingFunctionEnum = EasingFunctionEnum.EASE_IN_OUT ): void { if (this.highlightIntervalId) { clearInterval(this.highlightIntervalId); @@ -1246,7 +1533,8 @@ class SegmentationService extends PubSubService { segments, viewportId, animationLength, - representation + representation, + animationFunctionType ); }); } @@ -1271,7 +1559,7 @@ class SegmentationService extends PubSubService { private _setSegmentationRepresentationVisibility( viewportId: string, segmentationId: string, - type: SegmentationRepresentations, + type: csToolsEnums.SegmentationRepresentations, isVisible: boolean ): void { const representations = this.getSegmentationRepresentations(viewportId, { @@ -1331,14 +1619,17 @@ class SegmentationService extends PubSubService { private async handleVolumeViewportCase(csViewport, segmentation, isVolumeSegmentation) { if (csViewport.type === ViewportType.VOLUME_3D) { - return { representationTypeToUse: SegmentationRepresentations.Surface, isConverted: false }; + return { + representationTypeToUse: SURFACE, + isConverted: false, + }; } else { await this.handleVolumeViewport( csViewport as csTypes.IVolumeViewport, segmentation, isVolumeSegmentation ); - return { representationTypeToUse: SegmentationRepresentations.Labelmap, isConverted: false }; + return { representationTypeToUse: LABELMAP, isConverted: false }; } } @@ -1348,14 +1639,17 @@ class SegmentationService extends PubSubService { isVolumeSegmentation: boolean, viewportId: string, segmentationId: string - ): Promise<{ representationTypeToUse: SegmentationRepresentations; isConverted: boolean }> { + ): Promise<{ + representationTypeToUse: csToolsEnums.SegmentationRepresentations; + isConverted: boolean; + }> { if (isVolumeSegmentation) { const isConverted = await this.convertStackToVolumeViewport(csViewport); - return { representationTypeToUse: SegmentationRepresentations.Labelmap, isConverted }; + return { representationTypeToUse: LABELMAP, isConverted }; } if (updateLabelmapSegmentationImageReferences(viewportId, segmentationId)) { - return { representationTypeToUse: SegmentationRepresentations.Labelmap, isConverted: false }; + return { representationTypeToUse: LABELMAP, isConverted: false }; } const isConverted = await this.attemptStackToVolumeConversion( @@ -1365,7 +1659,7 @@ class SegmentationService extends PubSubService { segmentationId ); - return { representationTypeToUse: SegmentationRepresentations.Labelmap, isConverted }; + return { representationTypeToUse: LABELMAP, isConverted }; } private async _addSegmentationRepresentation( @@ -1476,11 +1770,7 @@ class SegmentationService extends PubSubService { segImage.FrameOfReferenceUID === frameOfReferenceUID ) { const isConverted = await this.convertStackToVolumeViewport(viewport); - triggerSegmentationRepresentationModified( - viewportId, - segmentationId, - SegmentationRepresentations.Labelmap - ); + triggerSegmentationRepresentationModified(viewportId, segmentationId, LABELMAP); return isConverted; } @@ -1598,6 +1888,11 @@ class SegmentationService extends PubSubService { csToolsEnums.Events.SEGMENTATION_ADDED, this._onSegmentationAddedFromSource ); + + eventTarget.addEventListener( + csToolsEnums.Events.ANNOTATION_CUT_MERGE_PROCESS_COMPLETED, + this._onAnnotationCutMergeProcessCompletedFromSource + ); } private getCornerstoneSegmentation(segmentationId: string) { @@ -1608,10 +1903,11 @@ class SegmentationService extends PubSubService { segmentIndex: number, alpha: number, hideOthers: boolean, - segments: Segment[], + segments: cstTypes.Segment[], viewportId: string, animationLength: number, - representation: cstTypes.SegmentationRepresentation + representation: cstTypes.SegmentationRepresentation, + animationFunctionType: EasingFunctionEnum ) { const { segmentationId } = representation; const newSegmentSpecificConfig = { @@ -1620,13 +1916,6 @@ class SegmentationService extends PubSubService { if (hideOthers) { throw new Error('hideOthers is not working right now'); - for (let i = 0; i < segments.length; i++) { - if (i !== segmentIndex) { - newSegmentSpecificConfig[i] = { - fillAlpha: 0, - }; - } - } } const { fillAlpha } = this.getStyle({ @@ -1645,6 +1934,8 @@ class SegmentationService extends PubSubService { const elapsed = timestamp - startTime; const progress = Math.min(elapsed / animationLength, 1); + const easingFunction = EasingFunctionMap.get(animationFunctionType); + cstSegmentation.config.style.setStyle( { segmentationId, @@ -1652,7 +1943,7 @@ class SegmentationService extends PubSubService { type: LABELMAP, }, { - fillAlpha: easeInOutBell(progress, fillAlpha), + fillAlpha: easingFunction(progress, fillAlpha), } ); @@ -1665,7 +1956,8 @@ class SegmentationService extends PubSubService { segmentIndex, type: LABELMAP, }, - {} + {}, + false ); } }; @@ -1677,21 +1969,20 @@ class SegmentationService extends PubSubService { segmentIndex: number, alpha: number, hideOthers: boolean, - segments: Segment[], + segments: cstTypes.Segment[], viewportId: string, animationLength: number, - representation: cstTypes.SegmentationRepresentation + representation: cstTypes.SegmentationRepresentation, + animationFunctionType: EasingFunctionEnum ) { const { segmentationId } = representation; const startTime = performance.now(); const prevStyle = cstSegmentation.config.style.getStyle({ type: CONTOUR, - }) as ContourStyle; + }) as cstTypes.ContourStyle; const prevOutlineWidth = prevStyle.outlineWidth; - // make this configurable - const baseline = Math.max(prevOutlineWidth * 3.5, 5); const animate = (currentTime: number) => { const progress = (currentTime - startTime) / animationLength; @@ -1700,7 +1991,8 @@ class SegmentationService extends PubSubService { return; } - const reversedProgress = easeInOutBellRelative(progress, baseline, prevOutlineWidth); + const OUTLINE_ANIMATION_SCALE_FACTOR = 5; + const easingFunction = EasingFunctionMap.get(animationFunctionType); cstSegmentation.config.style.setStyle( { @@ -1709,7 +2001,7 @@ class SegmentationService extends PubSubService { type: CONTOUR, }, { - outlineWidth: reversedProgress, + outlineWidth: easingFunction(progress, prevOutlineWidth, OUTLINE_ANIMATION_SCALE_FACTOR), } ); @@ -1722,24 +2014,18 @@ class SegmentationService extends PubSubService { private _toggleSegmentationRepresentationVisibility = ( viewportId: string, segmentationId: string, - type: SegmentationRepresentations + type: csToolsEnums.SegmentationRepresentations ): void => { - const representations = this.getSegmentationRepresentations(viewportId, { - segmentationId, - type, - }); - const representation = representations[0]; - const segmentsHidden = cstSegmentation.config.visibility.getHiddenSegmentIndices(viewportId, { segmentationId, - type: representation.type, + type, }); const currentVisibility = segmentsHidden.size === 0; this._setSegmentationRepresentationVisibility( viewportId, segmentationId, - representation.type, + type, !currentVisibility ); }; @@ -1790,7 +2076,7 @@ class SegmentationService extends PubSubService { segmentationId: string, segmentIndex: number, isVisible: boolean, - type?: SegmentationRepresentations + type?: csToolsEnums.SegmentationRepresentations ) { cstSegmentation.config.visibility.setSegmentIndexVisibility( viewportId, @@ -1853,6 +2139,69 @@ class SegmentationService extends PubSubService { segmentationId, }); }; + + private _onAnnotationCutMergeProcessCompletedFromSource = evt => { + const { segmentationId } = evt.detail; + this._broadcastEvent(this.EVENTS.SEGMENTATION_ANNOTATION_CUT_MERGE_PROCESS_COMPLETED, { + segmentationId, + }); + }; + + /** + * Gets slice indices for contour segmentations by checking view references from annotation metadata + * @private + */ + protected _getContourViewReferences( + segmentationId: string, + viewport: csTypes.IViewport, + segmentIndex?: number + ): Map | undefined { + const segmentation = cstSegmentation.state.getSegmentation(segmentationId); + const contourData = segmentation.representationData[CONTOUR]; + + if (!contourData || !contourData.annotationUIDsMap) { + return; + } + + const viewReferences = new Map(); + // Iterate through the annotationUIDsMap + contourData.annotationUIDsMap.forEach((annotationUIDs, currentSegmentIndex) => { + // Filter by segment index if specified + if (segmentIndex !== undefined && currentSegmentIndex !== segmentIndex) { + return; + } + // Process each annotation UID + annotationUIDs.forEach(annotationUID => { + const annotation = cstAnnotation.state.getAnnotation(annotationUID); + + if (!annotation?.metadata) { + return; + } + const { metadata } = annotation; + // Check if the viewport can view this annotation's view reference with navigation + const isViewable = viewport.isReferenceViewable(metadata, { + withNavigation: true, + }); + + if (!isViewable) { + return; + } + + const { sliceIndex } = metadata; + if (sliceIndex === undefined) { + console.warn("Can't find slice index:", metadata); + return; + } + + viewReferences.set(sliceIndex, metadata); + }); + }); + + if (viewReferences.size === 0) { + return; + } + return viewReferences; + } } export default SegmentationService; diff --git a/extensions/cornerstone/src/services/SyncGroupService/createHydrateSegmentationSynchronizer.ts b/extensions/cornerstone/src/services/SyncGroupService/createHydrateSegmentationSynchronizer.ts index f72c7f89fbe..f29e086d3de 100644 --- a/extensions/cornerstone/src/services/SyncGroupService/createHydrateSegmentationSynchronizer.ts +++ b/extensions/cornerstone/src/services/SyncGroupService/createHydrateSegmentationSynchronizer.ts @@ -6,6 +6,8 @@ import { Types as ToolsTypes, } from '@cornerstonejs/tools'; +import { isAnyDisplaySetCommon } from '../../utils/isAnyDisplaySetCommon'; + const { createSynchronizer } = SynchronizerManager; const { SEGMENTATION_REPRESENTATION_MODIFIED } = Enums.Events; const { BlendModes } = CoreEnums; @@ -34,6 +36,12 @@ export default function createHydrateSegmentationSynchronizer( return stackImageSynchronizer; } +/** + * This method will add the segmentation representation to any target viewports having: + * + * 1. the same FrameOfReferenceUID (FOR) as the segmentation representation, or + * 2. a shared DisplaySet with the source viewport when no FOR is present. + */ const segmentationRepresentationModifiedCallback = async ( synchronizerInstance: Synchronizer, sourceViewport: Types.IViewportId, @@ -43,16 +51,22 @@ const segmentationRepresentationModifiedCallback = async ( ) => { const event = sourceEvent as ToolsTypes.EventTypes.SegmentationRepresentationModifiedEventType; - const { segmentationId } = event.detail; - const { segmentationService } = servicesManager.services; + const { segmentationId, type: segmentationRepresentationType } = event.detail; + const { segmentationService, cornerstoneViewportService } = servicesManager.services; const targetViewportId = targetViewport.viewportId; + const sourceViewportId = sourceViewport.viewportId; const { viewport } = getEnabledElementByViewportId(targetViewportId); + const sourceViewportInfo = cornerstoneViewportService.getViewportInfo(sourceViewportId); + const targetViewportInfo = cornerstoneViewportService.getViewportInfo(targetViewportId); + + const sourceDisplaySetUIDs = extractDisplaySetUIDs(sourceViewportInfo); + const targetDisplaySetUIDs = extractDisplaySetUIDs(targetViewportInfo); - const targetFrameOfReferenceUID = viewport.getFrameOfReferenceUID(); + const sharedDisplaySetExists = isAnyDisplaySetCommon(sourceDisplaySetUIDs, targetDisplaySetUIDs); - if (!targetFrameOfReferenceUID) { + if (!sharedDisplaySetExists && !viewport.getFrameOfReferenceUID()) { return; } @@ -65,20 +79,26 @@ const segmentationRepresentationModifiedCallback = async ( return; } - // whatever type the source viewport has, we need to add that to the target viewport - const sourceViewportRepresentation = segmentationService.getSegmentationRepresentations( - sourceViewport.viewportId, - { segmentationId } - ); - - const type = sourceViewportRepresentation[0].type; + // Ensure the segmentation representation aligns with the target viewport type. + const type: Enums.SegmentationRepresentations = + viewport.type === CoreEnums.ViewportType.VOLUME_3D + ? Enums.SegmentationRepresentations.Surface + : ((segmentationRepresentationType as Enums.SegmentationRepresentations) ?? + Enums.SegmentationRepresentations.Labelmap); await segmentationService.addSegmentationRepresentation(targetViewportId, { segmentationId, type, config: { blendMode: - viewport.getBlendMode() === 1 ? BlendModes.LABELMAP_EDGE_PROJECTION_BLEND : undefined, + viewport?.getBlendMode?.() === 1 ? BlendModes.LABELMAP_EDGE_PROJECTION_BLEND : undefined, }, }); }; + +/** + * Extracts the displaySetInstanceUIDs from a viewportInfo. + */ +function extractDisplaySetUIDs(viewportInfo) { + return viewportInfo.getViewportData().data.map(ds => ds.displaySetInstanceUID); +} diff --git a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts index 201970f290d..698c8e4a080 100644 --- a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts +++ b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts @@ -1,3 +1,4 @@ +import { vec3 } from 'gl-matrix'; import { PubSubService } from '@ohif/core'; import { Types as OhifTypes } from '@ohif/core'; import { @@ -36,6 +37,8 @@ import { useLutPresentationStore } from '../../stores/useLutPresentationStore'; import { usePositionPresentationStore } from '../../stores/usePositionPresentationStore'; import { useSynchronizersStore } from '../../stores/useSynchronizersStore'; import { useSegmentationPresentationStore } from '../../stores/useSegmentationPresentationStore'; +import getClosestOrientationFromIOP from '../../utils/isReferenceViewable'; +import { BlendModes } from '@cornerstonejs/core/enums'; const EVENTS = { VIEWPORT_DATA_CHANGED: 'event::cornerstoneViewportService:viewportDataChanged', @@ -417,6 +420,11 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi // and we would lose the presentation. this.storePresentation({ viewportId: viewportInfo.getViewportId() }); + // Todo: i don't like this here, move it + this.servicesManager.services.segmentationService.clearSegmentationRepresentations( + viewportInfo.getViewportId() + ); + if (!viewportInfo) { throw new Error('element is not enabled for the given viewportId'); } @@ -533,8 +541,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi * viewport to display the image in where it matches, in order: * * Active viewport that can be navigated to the given image without orientation change * * Other viewport that can be navigated to the given image without orientation change - * * Active viewport that can change orientation to display the image - * * Other viewport that can change orientation to display the image + * * Best-aligned viewport that can display the image with an orientation change * * It returns `null` otherwise, indicating that a viewport needs display set/type * changes in order to display the image. @@ -557,7 +564,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi if (!activeViewport) { console.warn('No active viewport found for', activeViewportId); } - if (activeViewport?.isReferenceViewable(metadata, { withNavigation: true })) { + if (activeViewport?.isReferenceViewable(metadata, WITH_NAVIGATION)) { return activeViewportId; } @@ -565,25 +572,19 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi // without considering orientation changes. for (const id of this.viewportsById.keys()) { const viewport = this.getCornerstoneViewport(id); - if (viewport?.isReferenceViewable(metadata, { withNavigation: true })) { + if (viewport?.isReferenceViewable(metadata, WITH_NAVIGATION)) { return id; } } - // No viewport is in the right display set/orientation to show this, so see if - // the active viewport could change orientations to show this - if ( - activeViewport?.isReferenceViewable(metadata, { withNavigation: true, withOrientation: true }) - ) { - return activeViewportId; - } + // Compute view-plane alignment scores for all viewports to prefer the one + // requiring the least orientation change when navigation-only is not possible. + const viewportAlignmentData = this.getViewportAlignmentData(metadata); // See if any viewport could show this with an orientation change - for (const id of this.viewportsById.keys()) { + for (const { viewportId: id } of viewportAlignmentData) { const viewport = this.getCornerstoneViewport(id); - if ( - viewport?.isReferenceViewable(metadata, { withNavigation: true, withOrientation: true }) - ) { + if (viewport?.isReferenceViewable(metadata, WITH_ORIENTATION)) { return id; } } @@ -593,68 +594,161 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi } /** - * Figures out which viewport to update when the viewport type needs to change. - * This may not be the active viewport if there is already a viewport showing - * the display set, but in the wrong orientation. - * - * The viewport will need to update the viewport type and/or display set to - * display the resulting data. + * Given a metadata instance containing a planeRestriction, returns the + * ordered list of best orientation match viewport ids. * - * The first choice will be a viewport already showing the correct display set, - * but showing it as a stack. - * - * Second choice is to see if there is a viewport already showing the right - * orientation for the image, but the wrong display set. This fixes the - * case where the user is in MPR and a viewport other than active should be - * the one to change to display the iamge. - * - * Final choice is to use the provide activeViewportId. This will cover - * changes to/from video and wsi viewports and other cases where no - * viewport is really even close to being able to display the measurement. + * This uses the planeRestriction preferentially as that one is more reliably + * filled than the viewport normal since it is created from data points on + * rehydration. + */ + public getViewportAlignmentData(metadata) { + const viewportAlignmentData = []; + const { viewPlaneNormal: refViewPlaneNormal, planeRestriction } = metadata; + const inPlaneVector1 = planeRestriction?.inPlaneVector1; + const inPlaneVector2 = planeRestriction?.inPlaneVector2; + + for (const id of this.viewportsById.keys()) { + const viewport = this.getCornerstoneViewport(id); + const { viewPlaneNormal } = viewport.getCamera(); + + if (!viewPlaneNormal) { + continue; + } + let alignmentScore = 0; + if (inPlaneVector1 || inPlaneVector2) { + const inPlane1Score = inPlaneVector1 + ? -Math.abs(vec3.dot(viewPlaneNormal, inPlaneVector1)) + : 0; + const inPlane2Score = inPlaneVector2 + ? -Math.abs(vec3.dot(viewPlaneNormal, inPlaneVector2)) + : 0; + alignmentScore = inPlane1Score + inPlane2Score; + } else if (refViewPlaneNormal) { + alignmentScore = Math.abs(vec3.dot(viewPlaneNormal, refViewPlaneNormal)); + } + viewportAlignmentData.push({ viewportId: id, alignmentScore }); + } + + // Try best-aligned viewports first + viewportAlignmentData.sort((a, b) => b.alignmentScore - a.alignmentScore); + return viewportAlignmentData; + } + + /** + * Figures out which viewport to update when the viewport type needs to change. + * Orchestrates the search strategies in order of preference. */ public findUpdateableViewportConfiguration(activeViewportId: string, measurement) { const { metadata, displaySetInstanceUID } = measurement; - const { volumeId, referencedImageId } = metadata; - const { displaySetService, viewportGridService } = this.servicesManager.services; + const { displaySetService } = this.servicesManager.services; const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID); + // 1. Determine the target Viewport Type (Stack vs Volume) + const viewportType = this.determineTargetViewportType(displaySet, metadata); + + // 2. Strategy: Find viewport already showing this volume + const volumeMatch = this.findViewportShowingVolume( + metadata, + displaySetInstanceUID, + viewportType + ); + if (volumeMatch) { + return volumeMatch; + } + + // 3. Strategy: Find viewport with compatible orientation (even if different display set) + const compatibleMatch = this.findViewportConvertibleToVolume( + metadata, + displaySetInstanceUID, + viewportType + ); + if (compatibleMatch) { + return compatibleMatch; + } + + // 4. Strategy: Find viewport with matching orientation via IOP + const orientationMatch = this.findViewportWithMatchingOrientation( + metadata, + displaySetInstanceUID, + viewportType + ); + if (orientationMatch) { + return orientationMatch; + } + + // 5. Fallback: Use the active viewport + return { + viewportId: activeViewportId, + displaySetInstanceUID, + viewportOptions: { viewportType }, + }; + } + + /** + * Determines if the viewport should be what is specified in + * the viewportType of the display set, or stack if the display + * set isn't reconstructable and there is a referenced image id, otherwise + * volume. + * + * Expect there to be more rules in the future for different types of annotations/settings + * such as 3d annotations. + */ + public determineTargetViewportType(displaySet, metadata): string { let { viewportType } = displaySet; + if (!viewportType) { - if (referencedImageId && !displaySet.isReconstructable) { + if (metadata.referencedImageId && !displaySet.isReconstructable) { viewportType = csEnums.ViewportType.STACK; - } else if (volumeId) { + } else if (metadata.volumeId) { viewportType = 'volume'; } } + return viewportType; + } - // Find viewports that could be updated to be volumes to show this view - // That prefers a viewport already showing the right display set. - if (volumeId) { - for (const id of this.viewportsById.keys()) { - const viewport = this.getCornerstoneViewport(id); - if (viewport?.isReferenceViewable(metadata, { asVolume: true, withNavigation: true })) { - return { - viewportId: id, - displaySetInstanceUID, - viewportOptions: { viewportType }, - }; - } + /** + * Find viewports that could be updated to be volumes to show this view. + * Prefers a viewport already showing the right display set. + */ + public findViewportShowingVolume(metadata, displaySetInstanceUID, viewportType) { + if (!metadata.volumeId) { + return null; + } + + for (const id of this.viewportsById.keys()) { + const viewport = this.getCornerstoneViewport(id); + if (viewport?.isReferenceViewable(metadata, { asVolume: true, withNavigation: true })) { + return { + viewportId: id, + displaySetInstanceUID, + viewportOptions: { viewportType }, + }; } } + return null; + } - // Find a viewport in the correct orientation showing a different display set - // which could be used to display the annotation. + /** + * Find a viewport that could be converted to a volume to show this annotation, + * already showing the right display set. + */ + public findViewportConvertibleToVolume(metadata, displaySetInstanceUID, viewportType) { + const { viewportGridService } = this.servicesManager.services; const altMetadata = { ...metadata, volumeId: null, referencedImageId: null }; + for (const id of this.viewportsById.keys()) { const viewport = this.getCornerstoneViewport(id); const viewportDisplaySetUID = viewportGridService.getDisplaySetsUIDsForViewport(id)?.[0]; + if (!viewportDisplaySetUID || !viewport) { continue; } - if (volumeId) { + + if (metadata.volumeId) { altMetadata.volumeId = viewportDisplaySetUID; } altMetadata.FrameOfReferenceUID = this._getFrameOfReferenceUID(viewportDisplaySetUID); + if (viewport.isReferenceViewable(altMetadata, { asVolume: true, withNavigation: true })) { return { viewportId: id, @@ -663,13 +757,22 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi }; } } + return null; + } - // Just display in the active viewport - return { - viewportId: activeViewportId, - displaySetInstanceUID, - viewportOptions: { viewportType }, - }; + /** + * Find a viewport with the closest orientation but on a different display set. + */ + public findViewportWithMatchingOrientation(metadata, displaySetInstanceUID, viewportType) { + const viewportAlignmentData = this.getViewportAlignmentData(metadata); + if (viewportAlignmentData?.length) { + return { + ...viewportAlignmentData[0], + displaySetInstanceUID, + viewportOptions: { viewportType }, + }; + } + return null; } /** @@ -709,7 +812,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi // is being used to navigate to the initial view position for measurement // navigation and other navigation forcing specific views. let initialImageIndexToUse = - presentations?.positionPresentation?.initialImageIndex ?? initialImageIndex; + presentations?.positionPresentation?.initialImageIndex ?? (initialImageIndex as number); const { rotation, flipHorizontal, displayArea } = viewportInfo.getViewportOptions(); @@ -928,6 +1031,10 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi const displaySet = displaySetService.getDisplaySetByUID(displaySetUIDs[0]); const displaySetModality = displaySet?.Modality; + // seems like a hack but we need the actor to be ready first before + // we set the properties + const timeoutViewportCallback = (callback: () => void) => setTimeout(callback, 0); + // filter overlay display sets (e.g. segmentation) since they will get handled below via the segmentation service const filteredVolumeInputArray = volumeInputArray .map((volumeInput, index) => { @@ -979,12 +1086,21 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi const backgroundDisplaySet = displaySetService.getDisplaySetsBy( displaySet => !displaySet.isOverlayDisplaySet && - displaySet.images.some(image => image.imageId === sampleImageId) + displaySet.images?.some(image => image.imageId === sampleImageId) ); if (backgroundDisplaySet.length !== 1) { throw new Error('Background display set not found'); } + + if (viewport.type === csEnums.ViewportType.VOLUME_3D) { + timeoutViewportCallback(() => { + viewportGridService.setDisplaySetsForViewport({ + viewportId: viewport.id, + displaySetInstanceUIDs: [backgroundDisplaySet[0].displaySetInstanceUID], + }); + }); + } } }); } @@ -1001,15 +1117,13 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi viewport.render(); volumesProperties.forEach(({ properties, volumeId }) => { - setTimeout(() => { - // seems like a hack but we need the actor to be ready first before - // we set the properties + timeoutViewportCallback(() => { viewport.setProperties(properties, volumeId); viewport.render(); - }, 0); + }); }); - this.setPresentations(viewport.id, presentations, viewportInfo); + this.setPresentations(viewport.id, presentations); if (!presentations.positionPresentation) { const imageIndex = this._getInitialImageIndexForViewport(viewportInfo); @@ -1074,9 +1188,15 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi ? csToolsEnums.SegmentationRepresentations.Labelmap : csToolsEnums.SegmentationRepresentations.Contour; + const { predecessorImageId } = displaySet; segmentationService.addSegmentationRepresentation(viewport.id, { segmentationId, + predecessorImageId, type: representationType, + config: { + blendMode: + viewport?.getBlendMode?.() === 1 ? BlendModes.LABELMAP_EDGE_PROJECTION_BLEND : undefined, + }, }); // store the segmentation presentation id in the viewport info @@ -1330,10 +1450,24 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi segmentationPresentation.forEach((presentationItem: SegmentationPresentationItem) => { const { segmentationId, type, hydrated } = presentationItem; + const { Labelmap, Surface } = csToolsEnums.SegmentationRepresentations; + const isVolume3D = viewport.type === csEnums.ViewportType.VOLUME_3D; + + // Determine the appropriate segmentation representation for the viewport. + // If the current type is Surface but the viewport is not 3D, fallback to Labelmap. + // Otherwise, use the existing type. + const representationType = type === Surface && !isVolume3D ? Labelmap : type; + if (hydrated) { segmentationService.addSegmentationRepresentation(viewport.id, { segmentationId, - type, + type: representationType, + config: { + blendMode: + viewport?.getBlendMode?.() === 1 + ? BlendModes.LABELMAP_EDGE_PROJECTION_BLEND + : undefined, + }, }); } }); diff --git a/extensions/cornerstone/src/stores/index.ts b/extensions/cornerstone/src/stores/index.ts index f834c825ebc..2a1a519f23b 100644 --- a/extensions/cornerstone/src/stores/index.ts +++ b/extensions/cornerstone/src/stores/index.ts @@ -2,3 +2,4 @@ export { useLutPresentationStore } from './useLutPresentationStore'; export { usePositionPresentationStore } from './usePositionPresentationStore'; export { useSegmentationPresentationStore } from './useSegmentationPresentationStore'; export { useSynchronizersStore } from './useSynchronizersStore'; +export { useSelectedSegmentationsForViewportStore } from './useSelectedSegmentationsForViewportStore'; diff --git a/extensions/cornerstone/src/stores/useSelectedSegmentationsForViewportStore.ts b/extensions/cornerstone/src/stores/useSelectedSegmentationsForViewportStore.ts new file mode 100644 index 00000000000..efd30a7de1d --- /dev/null +++ b/extensions/cornerstone/src/stores/useSelectedSegmentationsForViewportStore.ts @@ -0,0 +1,85 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { SelectedSegmentationByTypeMap } from '../types/SelectedSegmentationByTypeMap'; + +const PRESENTATION_TYPE_ID = 'selectedSegmentationsForViewportId'; +const DEBUG_STORE = false; + +/** + * Represents the state and actions for managing selected segmentations for a viewport by representation type. + */ +type SelectedSegmentationsForViewportState = { + /** + * Stores a mapping from viewport id to a map of representation type to segmentation id. + */ + selectedSegmentationsForViewport: Record; + + /** + * Sets the selected segmentations for a given viewport id. + * + * @param key - The viewport id. + * @param value - The `SelectedSegmentationByTypeMap` to associate with the viewport id. + */ + setSelectedSegmentationsForViewport: (key: string, value: SelectedSegmentationByTypeMap) => void; + + /** + * Clears all selected segmentations for all viewports. + */ + clearSelectedSegmentationsForViewportStore: () => void; + + /** + * Type identifier for the store. + */ + type: string; +}; + +/** + * Creates the Selected Segmentations For Viewport store. + * + * @param set - The zustand set function. + * @returns The selected segmentations for viewport store state and actions. + */ +const createSelectedSegmentationsForViewportStore = ( + set +): SelectedSegmentationsForViewportState => ({ + selectedSegmentationsForViewport: {}, + type: PRESENTATION_TYPE_ID, + + /** + * Sets the selected segmentations for a given viewport id. + */ + setSelectedSegmentationsForViewport: (key, value) => + set( + state => ({ + selectedSegmentationsForViewport: { + ...state.selectedSegmentationsForViewport, + [key]: value, + }, + }), + false, + 'setSelectedSegmentationsForViewport' + ), + + /** + * Clears all selected segmentations for all viewports. + */ + clearSelectedSegmentationsForViewportStore: () => + set( + { selectedSegmentationsForViewport: {} }, + false, + 'clearSelectedSegmentationsForViewportStore' + ), +}); + +/** + * Zustand store for managing selected segmentations for a viewport by representation type. + * Applies devtools middleware when DEBUG_STORE is enabled. + */ +export const useSelectedSegmentationsForViewportStore = + create()( + DEBUG_STORE + ? devtools(createSelectedSegmentationsForViewportStore, { + name: 'SelectedSegmentationsForViewportStore', + }) + : createSelectedSegmentationsForViewportStore + ); diff --git a/extensions/cornerstone/src/types/SelectedSegmentationByTypeMap.ts b/extensions/cornerstone/src/types/SelectedSegmentationByTypeMap.ts new file mode 100644 index 00000000000..05092d817b1 --- /dev/null +++ b/extensions/cornerstone/src/types/SelectedSegmentationByTypeMap.ts @@ -0,0 +1,3 @@ +import { SegmentationRepresentations } from '@cornerstonejs/tools/enums'; + +export type SelectedSegmentationByTypeMap = Map; diff --git a/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx b/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx index 9e08f4d3f04..81e70c19f77 100644 --- a/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx +++ b/extensions/cornerstone/src/utils/CornerstoneViewportDownloadForm.tsx @@ -1,3 +1,4 @@ +import { utils } from '@ohif/core'; import React, { useEffect, useState } from 'react'; import html2canvas from 'html2canvas'; import { getEnabledElement, StackViewport, BaseVolumeViewport } from '@cornerstonejs/core'; @@ -5,6 +6,8 @@ import { ToolGroupManager, segmentation, Enums } from '@cornerstonejs/tools'; import { getEnabledElement as OHIFgetEnabledElement } from '../state'; import { useSystem } from '@ohif/core/src'; +const { downloadUrl } = utils; + const DEFAULT_SIZE = 512; const MAX_TEXTURE_SIZE = 10000; const VIEWPORT_ID = 'cornerstone-viewport-download-form'; @@ -64,7 +67,12 @@ const CornerstoneViewportDownloadForm = ({ return () => { Object.keys(toolModeAndBindings).forEach(toolName => { const { mode, bindings } = toolModeAndBindings[toolName]; - toolGroup.setToolMode(toolName, mode, { bindings }); + try { + toolGroup.setToolMode(toolName, mode, { bindings }); + } catch (error) { + // Handle errors when restoring tool mode during cleanup (e.g., when tool state is undefined) + console.debug('Error restoring tool mode during cleanup:', toolName, error); + } }); }; }, []); @@ -121,7 +129,7 @@ const CornerstoneViewportDownloadForm = ({ downloadViewport.setVolumes([{ volumeId: volumeIds[0] }]); } - if (segmentationRepresentations.length > 0) { + if (segmentationRepresentations?.length) { segmentationRepresentations.forEach(segRepresentation => { const { segmentationId, colorLUTIndex, type } = segRepresentation; if (type === Enums.SegmentationRepresentations.Labelmap) { @@ -207,7 +215,7 @@ const CornerstoneViewportDownloadForm = ({ } }, [viewportDimensions, showAnnotations]); - const handleDownload = async (filename: string, fileType: string) => { + const handleDownload = async (baseFilename: string, fileType: string) => { const divForDownloadViewport = document.querySelector( `div[data-viewport-uid="${VIEWPORT_ID}"]` ); @@ -217,11 +225,9 @@ const CornerstoneViewportDownloadForm = ({ return; } + const filename = `${baseFilename}.${fileType}`; const canvas = await html2canvas(divForDownloadViewport as HTMLElement); - const link = document.createElement('a'); - link.download = `${filename}.${fileType}`; - link.href = canvas.toDataURL(`image/${fileType}`, 1.0); - link.click(); + downloadUrl(canvas.toDataURL(`image/${fileType}`, 1.0), { filename }); }; const ViewportDownloadFormNew = customizationService.getCustomization( diff --git a/extensions/cornerstone/src/utils/createSegmentationForViewport.ts b/extensions/cornerstone/src/utils/createSegmentationForViewport.ts new file mode 100644 index 00000000000..497050623a8 --- /dev/null +++ b/extensions/cornerstone/src/utils/createSegmentationForViewport.ts @@ -0,0 +1,74 @@ +import { utilities as csUtils } from '@cornerstonejs/core'; +import { SegmentationRepresentations } from '@cornerstonejs/tools/enums'; +import i18n from '@ohif/i18n'; +import { ServicesManager } from '@ohif/core'; + +function _createDefaultSegments(createInitialSegment?: boolean) { + return createInitialSegment + ? { + 1: { + label: `${i18n.t('Tools:Segment')} 1`, + active: true, + }, + } + : {}; +} + +type CreateSegmentationForViewportOptions = { + displaySetInstanceUID?: string; + label?: string; + segmentationId?: string; + createInitialSegment?: boolean; +}; + +type CreateSegmentationForViewportParams = { + viewportId: string; + options?: CreateSegmentationForViewportOptions; + segmentationType: SegmentationRepresentations; +}; + +/** + * Creates a segmentation for the active viewport + * + * The created segmentation will be registered as a display set and also added + * as a segmentation representation to the viewport. + */ +export async function createSegmentationForViewport( + servicesManager: ServicesManager, + { viewportId, options = {}, segmentationType }: CreateSegmentationForViewportParams +): Promise { + 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 || `${i18n.t('Tools:Segmentation')} ${segs.length + 1}`; + const segmentationId = options.segmentationId || `${csUtils.uuidv4()}`; + + const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID); + + const segmentationCreationOptions = { + label, + segmentationId, + segments: _createDefaultSegments(options.createInitialSegment), + }; + + // This will create the segmentation and register it as a display set + const generatedSegmentationId = await (segmentationType === SegmentationRepresentations.Labelmap + ? segmentationService.createLabelmapForDisplaySet(displaySet, segmentationCreationOptions) + : segmentationService.createContourForDisplaySet(displaySet, segmentationCreationOptions)); + + // Also add the segmentation representation to the viewport + await segmentationService.addSegmentationRepresentation(viewportId, { + segmentationId, + type: segmentationType, + }); + + return generatedSegmentationId; +} diff --git a/extensions/cornerstone/src/utils/findNearbyToolData.test.ts b/extensions/cornerstone/src/utils/findNearbyToolData.test.ts new file mode 100644 index 00000000000..fc25f737c04 --- /dev/null +++ b/extensions/cornerstone/src/utils/findNearbyToolData.test.ts @@ -0,0 +1,125 @@ +import { findNearbyToolData } from './findNearbyToolData'; + +describe('findNearbyToolData', () => { + const mockCommandsManager = { + runCommand: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return undefined when event is null', () => { + const result = findNearbyToolData(mockCommandsManager, null); + + expect(result).toBeUndefined(); + expect(mockCommandsManager.runCommand).not.toHaveBeenCalled(); + }); + + it('should return undefined when event is undefined', () => { + const result = findNearbyToolData(mockCommandsManager, undefined); + + expect(result).toBeUndefined(); + expect(mockCommandsManager.runCommand).not.toHaveBeenCalled(); + }); + + it('should return undefined when event has no detail', () => { + const event = {}; + const result = findNearbyToolData(mockCommandsManager, event); + + expect(result).toBeUndefined(); + expect(mockCommandsManager.runCommand).not.toHaveBeenCalled(); + }); + + it('should return undefined when event detail is null', () => { + const event = { detail: null }; + const result = findNearbyToolData(mockCommandsManager, event); + + expect(result).toBeUndefined(); + expect(mockCommandsManager.runCommand).not.toHaveBeenCalled(); + }); + + it('should call runCommand with correct parameters when event has valid detail', () => { + const mockElement = document.createElement('div'); + const mockCurrentPoints = { canvas: { x: 100, y: 200 } }; + const mockToolData = { id: 'annotation-1' }; + const event = { + detail: { + element: mockElement, + currentPoints: mockCurrentPoints, + }, + }; + + mockCommandsManager.runCommand.mockReturnValue(mockToolData); + + const result = findNearbyToolData(mockCommandsManager, event); + + expect(mockCommandsManager.runCommand).toHaveBeenCalledWith( + 'getNearbyAnnotation', + { + element: mockElement, + canvasCoordinates: mockCurrentPoints.canvas, + }, + 'CORNERSTONE' + ); + expect(result).toBe(mockToolData); + }); + + it('should handle event with element but no currentPoints', () => { + const mockElement = document.createElement('div'); + const event = { + detail: { + element: mockElement, + }, + }; + + findNearbyToolData(mockCommandsManager, event); + + expect(mockCommandsManager.runCommand).toHaveBeenCalledWith( + 'getNearbyAnnotation', + { + element: mockElement, + canvasCoordinates: undefined, + }, + 'CORNERSTONE' + ); + }); + + it('should handle event with currentPoints but no canvas coordinates', () => { + const mockElement = document.createElement('div'); + const mockCurrentPoints = {}; + const event = { + detail: { + element: mockElement, + currentPoints: mockCurrentPoints, + }, + }; + + findNearbyToolData(mockCommandsManager, event); + + expect(mockCommandsManager.runCommand).toHaveBeenCalledWith( + 'getNearbyAnnotation', + { + element: mockElement, + canvasCoordinates: undefined, + }, + 'CORNERSTONE' + ); + }); + + it('should return result from runCommand', () => { + const mockToolData = { id: 'test-annotation', data: 'test-data' }; + const event = { + detail: { + element: document.createElement('div'), + currentPoints: { canvas: { x: 50, y: 75 } }, + }, + }; + + mockCommandsManager.runCommand.mockReturnValue(mockToolData); + + const result = findNearbyToolData(mockCommandsManager, event); + + expect(result).toBe(mockToolData); + }); +}); diff --git a/extensions/cornerstone/src/utils/generateSegmentationCSVReport.test.ts b/extensions/cornerstone/src/utils/generateSegmentationCSVReport.test.ts new file mode 100644 index 00000000000..170d262653a --- /dev/null +++ b/extensions/cornerstone/src/utils/generateSegmentationCSVReport.test.ts @@ -0,0 +1,320 @@ +import { generateSegmentationCSVReport } from './generateSegmentationCSVReport'; + +// Mock DOM APIs +Object.defineProperty(global, 'Blob', { + writable: true, + value: jest.fn().mockImplementation((content, options) => ({ + content, + options, + })), +}); + +Object.defineProperty(global, 'URL', { + writable: true, + value: { + createObjectURL: jest.fn().mockReturnValue('mock-url'), + revokeObjectURL: () => undefined, + }, +}); + +const mockDocument = { + addEventListener: jest.fn(), + createElement: jest.fn().mockReturnValue({ + setAttribute: jest.fn(), + click: jest.fn(), + style: {}, + }), + body: { + appendChild: jest.fn(), + removeChild: jest.fn(), + }, +}; + +Object.defineProperty(global, 'document', { + writable: true, + value: mockDocument, +}); + +describe('generateSegmentationCSVReport', () => { + const mockInfo = { + reference: { + SeriesNumber: '1', + SeriesInstanceUID: 'series-uid-123', + StudyInstanceUID: 'study-uid-456', + SeriesDate: '20231201', + SeriesTime: '120000', + SeriesDescription: 'Test Series', + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should generate CSV with basic segmentation data', () => { + const segmentationData = { + segmentationId: 'seg-123', + label: 'Test Segmentation', + }; + + generateSegmentationCSVReport(segmentationData, mockInfo); + + expect(global.Blob).toHaveBeenCalledWith([expect.stringContaining('Segmentation ID,seg-123')], { + type: 'text/csv;charset=utf-8;', + }); + }); + + it('should handle segmentation data without id and label', () => { + const segmentationData = {}; + + generateSegmentationCSVReport(segmentationData, mockInfo); + + expect(global.Blob).toHaveBeenCalledWith( + [expect.stringContaining('Segmentation ID,\nSegmentation Label,')], + { type: 'text/csv;charset=utf-8;' } + ); + }); + + it('should include reference information when provided', () => { + const segmentationData = { + segmentationId: 'seg-123', + label: 'Test Segmentation', + }; + + generateSegmentationCSVReport(segmentationData, mockInfo); + + expect(global.Blob).toHaveBeenCalledWith( + [expect.stringContaining('reference Series Number,1')], + { type: 'text/csv;charset=utf-8;' } + ); + }); + + it('should skip empty reference values', () => { + const segmentationData = { + segmentationId: 'seg-123', + label: 'Test Segmentation', + }; + const infoWithEmptyValues = { + reference: { + SeriesNumber: '1', + SeriesInstanceUID: '', + StudyInstanceUID: null, + SeriesDate: undefined, + SeriesTime: '120000', + SeriesDescription: 'Test Series', + }, + }; + + generateSegmentationCSVReport(segmentationData, infoWithEmptyValues); + + const csvContent = (global.Blob as jest.Mock).mock.calls[0][0][0]; + expect(csvContent).toContain('reference Series Number,1'); + expect(csvContent).toContain('reference Series Time,120000'); + expect(csvContent).not.toContain('reference Series Instance UID'); + expect(csvContent).not.toContain('reference Study Instance UID'); + expect(csvContent).not.toContain('reference Series Date'); + }); + + it('should handle segments with basic properties', () => { + const segmentationData = { + segmentationId: 'seg-123', + label: 'Test Segmentation', + segments: { + '1': { + segmentIndex: 1, + label: 'Segment 1', + locked: true, + active: false, + }, + '2': { + segmentIndex: 2, + label: 'Segment 2', + locked: false, + active: true, + }, + }, + }; + + generateSegmentationCSVReport(segmentationData, mockInfo); + + const csvContent = (global.Blob as jest.Mock).mock.calls[0][0][0]; + expect(csvContent).toContain('Label,Segment 1,Segment 2'); + expect(csvContent).toContain('Segment Index,1,2'); + expect(csvContent).toContain('Locked,Yes,No'); + expect(csvContent).toContain('Active,No,Yes'); + }); + + it('should handle segments with statistics', () => { + const segmentationData = { + segmentationId: 'seg-123', + label: 'Test Segmentation', + segments: { + '1': { + segmentIndex: 1, + label: 'Segment 1', + locked: false, + active: true, + cachedStats: { + namedStats: { + mean: { + name: 'mean', + label: 'Mean', + value: 100.5, + unit: 'HU', + }, + volume: { + name: 'volume', + label: 'Volume', + value: 250.75, + unit: 'mm³', + }, + }, + }, + }, + }, + }; + + generateSegmentationCSVReport(segmentationData, mockInfo); + + const csvContent = (global.Blob as jest.Mock).mock.calls[0][0][0]; + expect(csvContent).toContain('Mean (HU),100.5'); + expect(csvContent).toContain('Volume (mm³),250.75'); + }); + + it('should handle segments with statistics without units', () => { + const segmentationData = { + segmentationId: 'seg-123', + segments: { + '1': { + segmentIndex: 1, + label: 'Segment 1', + cachedStats: { + namedStats: { + count: { + name: 'count', + label: 'Count', + value: 42, + }, + }, + }, + }, + }, + }; + + generateSegmentationCSVReport(segmentationData, mockInfo); + + const csvContent = (global.Blob as jest.Mock).mock.calls[0][0][0]; + expect(csvContent).toContain('Count,42'); + }); + + it('should handle segments without cachedStats', () => { + const segmentationData = { + segmentationId: 'seg-123', + segments: { + '1': { + segmentIndex: 1, + label: 'Segment 1', + locked: false, + active: true, + }, + }, + }; + + generateSegmentationCSVReport(segmentationData, mockInfo); + + expect(global.Blob).toHaveBeenCalled(); + }); + + it('should handle segments with empty namedStats', () => { + const segmentationData = { + segmentationId: 'seg-123', + segments: { + '1': { + segmentIndex: 1, + label: 'Segment 1', + cachedStats: { + namedStats: {}, + }, + }, + }, + }; + + generateSegmentationCSVReport(segmentationData, mockInfo); + + expect(global.Blob).toHaveBeenCalled(); + }); + + it('should escape CSV special characters', () => { + const segmentationData = { + segmentationId: 'seg,with,commas', + label: 'Test "quoted" label', + segments: { + '1': { + segmentIndex: 1, + label: 'Segment\nwith\nnewlines', + cachedStats: { + namedStats: { + test: { + name: 'test', + label: 'Test,Value', + value: 'value"with"quotes', + }, + }, + }, + }, + }, + }; + + generateSegmentationCSVReport(segmentationData, mockInfo); + + const csvContent = (global.Blob as jest.Mock).mock.calls[0][0][0]; + expect(csvContent).toContain('"seg,with,commas"'); + expect(csvContent).toContain('"Test ""quoted"" label"'); + expect(csvContent).toContain('"Segment\nwith\nnewlines"'); + }); + + it('should create download link with correct attributes', () => { + const mockLink = { + setAttribute: jest.fn(), + click: jest.fn(), + style: {}, + }; + mockDocument.createElement.mockReturnValue(mockLink); + + const segmentationData = { + segmentationId: 'seg-123', + label: 'Test Segmentation', + }; + + generateSegmentationCSVReport(segmentationData, mockInfo); + + expect(mockLink.setAttribute).toHaveBeenCalledWith('href', 'mock-url'); + expect(mockLink.setAttribute).toHaveBeenCalledWith( + 'download', + expect.stringMatching(/Test Segmentation_Report_\d{4}-\d{2}-\d{2}\.csv/) + ); + expect(mockLink.click).toHaveBeenCalled(); + expect(mockDocument.body.appendChild).toHaveBeenCalledWith(mockLink); + expect(mockDocument.body.removeChild).toHaveBeenCalledWith(mockLink); + }); + + it('should use default filename when label is missing', () => { + const mockLink = { + setAttribute: jest.fn(), + click: jest.fn(), + style: {}, + }; + mockDocument.createElement.mockReturnValue(mockLink); + + const segmentationData = { + segmentationId: 'seg-123', + }; + + generateSegmentationCSVReport(segmentationData, mockInfo); + + expect(mockLink.setAttribute).toHaveBeenCalledWith( + 'download', + expect.stringMatching(/Segmentation_Report_\d{4}-\d{2}-\d{2}\.csv/) + ); + }); +}); diff --git a/extensions/cornerstone/src/utils/generateSegmentationCSVReport.ts b/extensions/cornerstone/src/utils/generateSegmentationCSVReport.ts index 023f46df6e9..d4252a6ab4a 100644 --- a/extensions/cornerstone/src/utils/generateSegmentationCSVReport.ts +++ b/extensions/cornerstone/src/utils/generateSegmentationCSVReport.ts @@ -1,3 +1,7 @@ +import { utils } from '@ohif/core'; + +const { downloadCsv } = utils; + export function generateSegmentationCSVReport( segmentationData, info: { @@ -121,19 +125,7 @@ export function generateSegmentationCSVReport( csvString += formattedRow.join(',') + '\n'; } - // Create a download link and trigger the download - const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;' }); - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - - link.setAttribute('href', url); - link.setAttribute( - 'download', - `${segmentationData.label || 'Segmentation'}_Report_${new Date().toISOString().split('T')[0]}.csv` - ); - link.style.visibility = 'hidden'; - - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); + downloadCsv(csvString, { + filename: `${segmentationData.label || 'Segmentation'}_Report_${new Date().toISOString().split('T')[0]}.csv`, + }); } diff --git a/extensions/cornerstone/src/utils/getActiveViewportEnabledElement.test.ts b/extensions/cornerstone/src/utils/getActiveViewportEnabledElement.test.ts new file mode 100644 index 00000000000..fd3da66bcdf --- /dev/null +++ b/extensions/cornerstone/src/utils/getActiveViewportEnabledElement.test.ts @@ -0,0 +1,70 @@ +import { getViewportEnabledElement } from './getViewportEnabledElement'; +import getActiveViewportEnabledElement from './getActiveViewportEnabledElement'; + +jest.mock('./getViewportEnabledElement', () => ({ + getViewportEnabledElement: jest.fn(), +})); + +describe('getActiveViewportEnabledElement', () => { + const mockViewportGridService = { + getState: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return enabled element for active viewport', () => { + const mockEnabledElement = { viewport: 'test-viewport' }; + mockViewportGridService.getState.mockReturnValue({ activeViewportId: 'viewport-1' }); + (getViewportEnabledElement as jest.Mock).mockReturnValue(mockEnabledElement); + + const result = getActiveViewportEnabledElement(mockViewportGridService); + + expect(mockViewportGridService.getState).toHaveBeenCalledTimes(1); + expect(getViewportEnabledElement).toHaveBeenCalledWith('viewport-1'); + expect(result).toBe(mockEnabledElement); + }); + + it('should handle null activeViewportId', () => { + const mockEnabledElement = null; + mockViewportGridService.getState.mockReturnValue({ activeViewportId: null }); + (getViewportEnabledElement as jest.Mock).mockReturnValue(mockEnabledElement); + + const result = getActiveViewportEnabledElement(mockViewportGridService); + + expect(getViewportEnabledElement).toHaveBeenCalledWith(null); + expect(result).toBe(null); + }); + + it('should handle undefined activeViewportId', () => { + const mockEnabledElement = undefined; + mockViewportGridService.getState.mockReturnValue({ activeViewportId: undefined }); + (getViewportEnabledElement as jest.Mock).mockReturnValue(mockEnabledElement); + + const result = getActiveViewportEnabledElement(mockViewportGridService); + + expect(getViewportEnabledElement).toHaveBeenCalledWith(undefined); + expect(result).toBe(undefined); + }); + + it('should handle empty state object', () => { + const mockEnabledElement = undefined; + mockViewportGridService.getState.mockReturnValue({}); + (getViewportEnabledElement as jest.Mock).mockReturnValue(mockEnabledElement); + + const result = getActiveViewportEnabledElement(mockViewportGridService); + + expect(getViewportEnabledElement).toHaveBeenCalledWith(undefined); + expect(result).toBe(undefined); + }); + + it('should handle getViewportEnabledElement returning null', () => { + mockViewportGridService.getState.mockReturnValue({ activeViewportId: 'viewport-1' }); + (getViewportEnabledElement as jest.Mock).mockReturnValue(null); + + const result = getActiveViewportEnabledElement(mockViewportGridService); + + expect(result).toBe(null); + }); +}); diff --git a/extensions/cornerstone/src/utils/getCenterExtent.test.ts b/extensions/cornerstone/src/utils/getCenterExtent.test.ts new file mode 100644 index 00000000000..72c06a6db96 --- /dev/null +++ b/extensions/cornerstone/src/utils/getCenterExtent.test.ts @@ -0,0 +1,313 @@ +import { getCenterExtent } from './getCenterExtent'; + +describe('getCenterExtent', () => { + it('should return default values when points is undefined', () => { + const measurement = {}; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [0, 0, 0], + extent: { + min: [0, 0, 0], + max: [0, 0, 0], + }, + }); + }); + + it('should return default values when points is null', () => { + const measurement = { points: null }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [0, 0, 0], + extent: { + min: [0, 0, 0], + max: [0, 0, 0], + }, + }); + }); + + it('should return default values when points is not an array', () => { + const measurement = { points: 'invalid' }; + + // @ts-expect-error - purposely passing an invalid type + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [0, 0, 0], + extent: { + min: [0, 0, 0], + max: [0, 0, 0], + }, + }); + }); + + it('should return default values when points array is empty', () => { + const measurement = { points: [] }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [0, 0, 0], + extent: { + min: [0, 0, 0], + max: [0, 0, 0], + }, + }); + }); + + it('should handle single point correctly', () => { + const measurement = { + points: [[5, 10, 15]], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [5, 10, 15], + extent: { + min: [5, 10, 15], + max: [5, 10, 15], + }, + }); + }); + + it('should calculate center and extent for two points', () => { + const measurement = { + points: [ + [0, 0, 0], + [10, 20, 30], + ], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [5, 10, 15], + extent: { + min: [0, 0, 0], + max: [10, 20, 30], + }, + }); + }); + + it('should calculate center and extent for multiple points', () => { + const measurement = { + points: [ + [0, 0, 0], + [10, 20, 30], + [5, 5, 5], + [-5, 15, 25], + ], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [2.5, 10, 15], + extent: { + min: [-5, 0, 0], + max: [10, 20, 30], + }, + }); + }); + + it('should handle negative coordinates', () => { + const measurement = { + points: [ + [-10, -20, -30], + [-5, -15, -25], + ], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [-7.5, -17.5, -27.5], + extent: { + min: [-10, -20, -30], + max: [-5, -15, -25], + }, + }); + }); + + it('should handle mixed positive and negative coordinates', () => { + const measurement = { + points: [ + [-10, -5, 0], + [10, 5, 20], + [0, 0, -10], + ], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [0, 0, 5], + extent: { + min: [-10, -5, -10], + max: [10, 5, 20], + }, + }); + }); + + it('should handle floating point coordinates', () => { + const measurement = { + points: [ + [1.5, 2.3, 3.1], + [4.2, 8.9, 6.3], + ], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [2.85, 5.6, 4.7], + extent: { + min: [1.5, 2.3, 3.1], + max: [4.2, 8.9, 6.3], + }, + }); + }); + + it('should handle points with same coordinates', () => { + const measurement = { + points: [ + [5, 5, 5], + [5, 5, 5], + [5, 5, 5], + ], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [5, 5, 5], + extent: { + min: [5, 5, 5], + max: [5, 5, 5], + }, + }); + }); + + it('should handle zero coordinates', () => { + const measurement = { + points: [ + [0, 0, 0], + [0, 0, 0], + ], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [0, 0, 0], + extent: { + min: [0, 0, 0], + max: [0, 0, 0], + }, + }); + }); + + it('should handle large coordinate values', () => { + const measurement = { + points: [ + [1000000, 2000000, 3000000], + [1000001, 2000001, 3000001], + ], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [1000000.5, 2000000.5, 3000000.5], + extent: { + min: [1000000, 2000000, 3000000], + max: [1000001, 2000001, 3000001], + }, + }); + }); + + it('should handle points in different order', () => { + const measurement = { + points: [ + [10, 20, 30], + [0, 0, 0], + [5, 10, 15], + ], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [5, 10, 15], + extent: { + min: [0, 0, 0], + max: [10, 20, 30], + }, + }); + }); + + it('should handle points where min and max are not the first point', () => { + const measurement = { + points: [ + [5, 5, 5], + [0, 0, 0], + [10, 10, 10], + [3, 3, 3], + ], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [5, 5, 5], + extent: { + min: [0, 0, 0], + max: [10, 10, 10], + }, + }); + }); + + it('should handle asymmetric extent distribution', () => { + const measurement = { + points: [ + [0, 0, 0], + [100, 10, 1], + [50, 50, 50], + ], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [50, 25, 25], + extent: { + min: [0, 0, 0], + max: [100, 50, 50], + }, + }); + }); + + it('should handle points with different ranges per dimension', () => { + const measurement = { + points: [ + [0, 100, 1000], + [1, 101, 1001], + [0.5, 100.5, 1000.5], + ], + }; + + const result = getCenterExtent(measurement); + + expect(result).toEqual({ + center: [0.5, 100.5, 1000.5], + extent: { + min: [0, 100, 1000], + max: [1, 101, 1001], + }, + }); + }); +}); diff --git a/extensions/cornerstone/src/utils/getCenterExtent.ts b/extensions/cornerstone/src/utils/getCenterExtent.ts new file mode 100644 index 00000000000..8873ec1ab39 --- /dev/null +++ b/extensions/cornerstone/src/utils/getCenterExtent.ts @@ -0,0 +1,46 @@ +/** + * Calculates the center point and bounding box extent of a measurement based on its points. + * @param {Object} measurement - The measurement object containing points + * @param {Array>} measurement.points - Array of 3D points [x, y, z] + * @returns {Object} Object containing center and extent + * @returns {Array} returns.center - The center point [x, y, z] + * @returns {Object} returns.extent - The bounding box extent with min and max points + */ +export const getCenterExtent = (measurement: { points?: number[][] }) => { + const { points } = measurement; + + if (!points || !Array.isArray(points) || points.length === 0) { + // Return default values if no points are available + const defaultCenter: [number, number, number] = [0, 0, 0]; + const defaultExtent = { + min: [0, 0, 0] as [number, number, number], + max: [0, 0, 0] as [number, number, number], + }; + return { center: defaultCenter, extent: defaultExtent }; + } + + // Initialize min and max with the first point + const min: [number, number, number] = [...points[0]] as [number, number, number]; + const max: [number, number, number] = [...points[0]] as [number, number, number]; + + // Find the bounding box by iterating through all points + for (let i = 1; i < points.length; i++) { + const point = points[i]; + for (let j = 0; j < 3; j++) { + min[j] = Math.min(min[j], point[j]); + max[j] = Math.max(max[j], point[j]); + } + } + + // Calculate the center point + const center: [number, number, number] = [ + (min[0] + max[0]) / 2, + (min[1] + max[1]) / 2, + (min[2] + max[2]) / 2, + ]; + + return { + center, + extent: { min, max }, + }; +}; diff --git a/extensions/cornerstone/src/utils/getCornerstoneBlendMode.test.ts b/extensions/cornerstone/src/utils/getCornerstoneBlendMode.test.ts new file mode 100644 index 00000000000..dc92713f376 --- /dev/null +++ b/extensions/cornerstone/src/utils/getCornerstoneBlendMode.test.ts @@ -0,0 +1,59 @@ +import { Enums } from '@cornerstonejs/core'; +import getCornerstoneBlendMode from './getCornerstoneBlendMode'; + +jest.mock('@cornerstonejs/core', () => ({ + Enums: { + BlendModes: { + COMPOSITE: 'composite', + MAXIMUM_INTENSITY_BLEND: 'mip', + MINIMUM_INTENSITY_BLEND: 'minip', + AVERAGE_INTENSITY_BLEND: 'avg', + }, + }, +})); + +describe('getCornerstoneBlendMode', () => { + it('should return COMPOSITE when blendMode is null', () => { + const result = getCornerstoneBlendMode(null); + expect(result).toBe(Enums.BlendModes.COMPOSITE); + }); + + it('should return COMPOSITE when blendMode is undefined', () => { + const result = getCornerstoneBlendMode(undefined); + expect(result).toBe(Enums.BlendModes.COMPOSITE); + }); + + it('should return COMPOSITE when blendMode is empty string', () => { + const result = getCornerstoneBlendMode(''); + expect(result).toBe(Enums.BlendModes.COMPOSITE); + }); + + it('should return MAXIMUM_INTENSITY_BLEND when blendMode is mip', () => { + const result = getCornerstoneBlendMode('mip'); + expect(result).toBe(Enums.BlendModes.MAXIMUM_INTENSITY_BLEND); + }); + + it('should return MAXIMUM_INTENSITY_BLEND when blendMode is MIP (uppercase)', () => { + const result = getCornerstoneBlendMode('MIP'); + expect(result).toBe(Enums.BlendModes.MAXIMUM_INTENSITY_BLEND); + }); + + it('should return MINIMUM_INTENSITY_BLEND when blendMode is MINIP (uppercase)', () => { + const result = getCornerstoneBlendMode('MINIP'); + expect(result).toBe(Enums.BlendModes.MINIMUM_INTENSITY_BLEND); + }); + + it('should return AVERAGE_INTENSITY_BLEND when blendMode is avg', () => { + const result = getCornerstoneBlendMode('avg'); + expect(result).toBe(Enums.BlendModes.AVERAGE_INTENSITY_BLEND); + }); + + it('should throw error for unsupported blend mode', () => { + expect(() => getCornerstoneBlendMode('invalid')).toThrow('Unsupported blend mode: invalid'); + }); + + it('should handle mixed case blend mode', () => { + const result = getCornerstoneBlendMode('MiP'); + expect(result).toBe(Enums.BlendModes.MAXIMUM_INTENSITY_BLEND); + }); +}); diff --git a/extensions/cornerstone/src/utils/getCornerstoneOrientation.test.ts b/extensions/cornerstone/src/utils/getCornerstoneOrientation.test.ts new file mode 100644 index 00000000000..0e8e18d5bbd --- /dev/null +++ b/extensions/cornerstone/src/utils/getCornerstoneOrientation.test.ts @@ -0,0 +1,60 @@ +import { Enums } from '@cornerstonejs/core'; +import getCornerstoneOrientation from './getCornerstoneOrientation'; + +jest.mock('@cornerstonejs/core', () => ({ + Enums: { + OrientationAxis: { + AXIAL: 'axial', + SAGITTAL: 'sagittal', + CORONAL: 'coronal', + ACQUISITION: 'acquisition', + }, + }, +})); + +describe('getCornerstoneOrientation', () => { + it('should return AXIAL when orientation is axial', () => { + const result = getCornerstoneOrientation('axial'); + expect(result).toBe(Enums.OrientationAxis.AXIAL); + }); + + it('should return AXIAL when orientation is AXIAL (uppercase)', () => { + const result = getCornerstoneOrientation('AXIAL'); + expect(result).toBe(Enums.OrientationAxis.AXIAL); + }); + + it('should return SAGITTAL when orientation is sagittal', () => { + const result = getCornerstoneOrientation('sagittal'); + expect(result).toBe(Enums.OrientationAxis.SAGITTAL); + }); + + it('should return CORONAL when orientation is coronal', () => { + const result = getCornerstoneOrientation('coronal'); + expect(result).toBe(Enums.OrientationAxis.CORONAL); + }); + + it('should return ACQUISITION for unknown orientation', () => { + const result = getCornerstoneOrientation('unknown'); + expect(result).toBe(Enums.OrientationAxis.ACQUISITION); + }); + + it('should return ACQUISITION when orientation is null', () => { + const result = getCornerstoneOrientation(null); + expect(result).toBe(Enums.OrientationAxis.ACQUISITION); + }); + + it('should return ACQUISITION when orientation is undefined', () => { + const result = getCornerstoneOrientation(undefined); + expect(result).toBe(Enums.OrientationAxis.ACQUISITION); + }); + + it('should return ACQUISITION when orientation is empty string', () => { + const result = getCornerstoneOrientation(''); + expect(result).toBe(Enums.OrientationAxis.ACQUISITION); + }); + + it('should handle mixed case orientation', () => { + const result = getCornerstoneOrientation('CoRoNaL'); + expect(result).toBe(Enums.OrientationAxis.CORONAL); + }); +}); diff --git a/extensions/cornerstone/src/utils/getCornerstoneViewportType.test.ts b/extensions/cornerstone/src/utils/getCornerstoneViewportType.test.ts new file mode 100644 index 00000000000..7afd41f68b7 --- /dev/null +++ b/extensions/cornerstone/src/utils/getCornerstoneViewportType.test.ts @@ -0,0 +1,91 @@ +import type { Types } from '@ohif/core'; +import { Enums } from '@cornerstonejs/core'; +import getCornerstoneViewportType from './getCornerstoneViewportType'; + +jest.mock('@cornerstonejs/core', () => ({ + Enums: { + ViewportType: { + STACK: 'stack', + VIDEO: 'video', + WHOLE_SLIDE: 'wholeslide', + ORTHOGRAPHIC: 'orthographic', + VOLUME_3D: 'volume3d', + }, + }, +})); + +describe('getCornerstoneViewportType', () => { + it('should return STACK when viewportType is stack', () => { + const result = getCornerstoneViewportType('stack'); + expect(result).toBe(Enums.ViewportType.STACK); + }); + + it('should return STACK when viewportType is STACK (uppercase)', () => { + const result = getCornerstoneViewportType('STACK'); + expect(result).toBe(Enums.ViewportType.STACK); + }); + + it('should return VIDEO when viewportType is video', () => { + const result = getCornerstoneViewportType('video'); + expect(result).toBe(Enums.ViewportType.VIDEO); + }); + + it('should return WHOLE_SLIDE when viewportType is wholeslide', () => { + const result = getCornerstoneViewportType('wholeslide'); + expect(result).toBe(Enums.ViewportType.WHOLE_SLIDE); + }); + + it('should return ORTHOGRAPHIC when viewportType is volume', () => { + const result = getCornerstoneViewportType('volume'); + expect(result).toBe(Enums.ViewportType.ORTHOGRAPHIC); + }); + + it('should return ORTHOGRAPHIC when viewportType is orthographic', () => { + const result = getCornerstoneViewportType('orthographic'); + expect(result).toBe(Enums.ViewportType.ORTHOGRAPHIC); + }); + + it('should return VOLUME_3D when viewportType is volume3d', () => { + const result = getCornerstoneViewportType('volume3d'); + expect(result).toBe(Enums.ViewportType.VOLUME_3D); + }); + + it('should throw error for invalid viewport type', () => { + expect(() => getCornerstoneViewportType('invalid')).toThrow( + 'Invalid viewport type: invalid. Valid types are: stack, volume, video, wholeslide' + ); + }); + + it('should use displaySet viewportType when provided', () => { + const displaySets = [{ viewportType: 'stack' }] as Types.DisplaySet[]; + const result = getCornerstoneViewportType('volume', displaySets); + expect(result).toBe(Enums.ViewportType.STACK); + }); + + it('should use displaySet viewportType with case insensitive matching', () => { + const displaySets = [{ viewportType: 'VIDEO' }] as Types.DisplaySet[]; + const result = getCornerstoneViewportType('stack', displaySets); + expect(result).toBe(Enums.ViewportType.VIDEO); + }); + + it('should fallback to viewportType when displaySet has no viewportType', () => { + const displaySets = [{}] as Types.DisplaySet[]; + const result = getCornerstoneViewportType('volume', displaySets); + expect(result).toBe(Enums.ViewportType.ORTHOGRAPHIC); + }); + + it('should handle empty displaySets array', () => { + const result = getCornerstoneViewportType('stack', []); + expect(result).toBe(Enums.ViewportType.STACK); + }); + + it('should handle null displaySets', () => { + const result = getCornerstoneViewportType('video', null); + expect(result).toBe(Enums.ViewportType.VIDEO); + }); + + it('should handle undefined displaySets', () => { + const result = getCornerstoneViewportType('wholeslide', undefined); + expect(result).toBe(Enums.ViewportType.WHOLE_SLIDE); + }); +}); diff --git a/extensions/cornerstone/src/utils/getInterleavedFrames.test.ts b/extensions/cornerstone/src/utils/getInterleavedFrames.test.ts new file mode 100644 index 00000000000..62899043a66 --- /dev/null +++ b/extensions/cornerstone/src/utils/getInterleavedFrames.test.ts @@ -0,0 +1,85 @@ +import getInterleavedFrames from './getInterleavedFrames'; + +describe('getInterleavedFrames', () => { + it('should return single element when input has one element', () => { + const imageIds = ['image-1']; + const result = getInterleavedFrames(imageIds); + + expect(result).toEqual([{ imageId: 'image-1', imageIdIndex: 0 }]); + }); + + it('should return correct order for three elements', () => { + const imageIds = ['image-1', 'image-2', 'image-3']; + const result = getInterleavedFrames(imageIds); + + expect(result).toEqual([ + { imageId: 'image-2', imageIdIndex: 1 }, + { imageId: 'image-1', imageIdIndex: 0 }, + { imageId: 'image-3', imageIdIndex: 2 }, + ]); + }); + + it('should start with middle element', () => { + const imageIds = ['image-1', 'image-2', 'image-3', 'image-4', 'image-5']; + const result = getInterleavedFrames(imageIds); + + expect(result[0]).toEqual({ imageId: 'image-3', imageIdIndex: 2 }); + }); + + it('should interleave elements correctly for even length array', () => { + const imageIds = ['image-1', 'image-2', 'image-3', 'image-4', 'image-5', 'image-6']; + const result = getInterleavedFrames(imageIds); + + expect(result).toEqual([ + { imageId: 'image-4', imageIdIndex: 3 }, + { imageId: 'image-3', imageIdIndex: 2 }, + { imageId: 'image-5', imageIdIndex: 4 }, + { imageId: 'image-2', imageIdIndex: 1 }, + { imageId: 'image-6', imageIdIndex: 5 }, + { imageId: 'image-1', imageIdIndex: 0 }, + ]); + }); + + it('should interleave elements correctly for odd length array', () => { + const imageIds = ['image-1', 'image-2', 'image-3', 'image-4', 'image-5', 'image-6', 'image-7']; + const result = getInterleavedFrames(imageIds); + + expect(result).toEqual([ + { imageId: 'image-4', imageIdIndex: 3 }, + { imageId: 'image-3', imageIdIndex: 2 }, + { imageId: 'image-5', imageIdIndex: 4 }, + { imageId: 'image-2', imageIdIndex: 1 }, + { imageId: 'image-6', imageIdIndex: 5 }, + { imageId: 'image-1', imageIdIndex: 0 }, + { imageId: 'image-7', imageIdIndex: 6 }, + ]); + }); + + it('should handle large array correctly', () => { + const imageIds = Array.from({ length: 10 }, (_, i) => `image-${i + 1}`); + const result = getInterleavedFrames(imageIds); + + expect(result).toHaveLength(10); + expect(result[0]).toEqual({ imageId: 'image-6', imageIdIndex: 5 }); + expect(result[8]).toEqual({ imageId: 'image-10', imageIdIndex: 9 }); + expect(result[9]).toEqual({ imageId: 'image-1', imageIdIndex: 0 }); + }); + + it('should handle empty array', () => { + const imageIds = []; + const result = getInterleavedFrames(imageIds); + + expect(result).toEqual([]); + }); + + it('should handle duplicate imageIds with different indices', () => { + const imageIds = ['duplicate', 'unique', 'duplicate']; + const result = getInterleavedFrames(imageIds); + + expect(result).toEqual([ + { imageId: 'unique', imageIdIndex: 1 }, + { imageId: 'duplicate', imageIdIndex: 0 }, + { imageId: 'duplicate', imageIdIndex: 2 }, + ]); + }); +}); diff --git a/extensions/cornerstone/src/utils/getInterleavedFrames.js b/extensions/cornerstone/src/utils/getInterleavedFrames.ts similarity index 82% rename from extensions/cornerstone/src/utils/getInterleavedFrames.js rename to extensions/cornerstone/src/utils/getInterleavedFrames.ts index b380a9350b3..f37a6bbe7b5 100644 --- a/extensions/cornerstone/src/utils/getInterleavedFrames.js +++ b/extensions/cornerstone/src/utils/getInterleavedFrames.ts @@ -1,4 +1,17 @@ -export default function getInterleavedFrames(imageIds) { +interface ImageIdToPrefetch { + imageId: string; + imageIdIndex: number; +} + +export default function getInterleavedFrames(imageIds: string[]): ImageIdToPrefetch[] { + if (imageIds.length === 0) { + return []; + } + + if (imageIds.length === 1) { + return [{ imageId: imageIds[0], imageIdIndex: 0 }]; + } + const minImageIdIndex = 0; const maxImageIdIndex = imageIds.length - 1; @@ -8,7 +21,7 @@ export default function getInterleavedFrames(imageIds) { let upperImageIdIndex = middleImageIdIndex; // Build up an array of images to prefetch, starting with the current image. - const imageIdsToPrefetch = [ + const imageIdsToPrefetch: ImageIdToPrefetch[] = [ { imageId: imageIds[middleImageIdIndex], imageIdIndex: middleImageIdIndex }, ]; diff --git a/extensions/cornerstone/src/utils/getNthFrames.test.ts b/extensions/cornerstone/src/utils/getNthFrames.test.ts new file mode 100644 index 00000000000..414ac81f43b --- /dev/null +++ b/extensions/cornerstone/src/utils/getNthFrames.test.ts @@ -0,0 +1,145 @@ +import getNthFrames from './getNthFrames'; + +describe('getNthFrames', () => { + const createMockImageLoadRequest = (imageId: string, imageIdIndex: number) => ({ + imageId, + callLoadImage: jest.fn(), + additionalDetails: 'test-details', + imageIdIndex, + options: { test: 'option' }, + }); + + it('should return empty array when input is empty', () => { + const result = getNthFrames([]); + expect(result).toEqual([]); + }); + + it('should return same array when input has one element', () => { + const imageIds = [createMockImageLoadRequest('image-1', 0)]; + const result = getNthFrames(imageIds); + expect(result).toEqual(imageIds); + }); + + it('should return same array when input has two elements', () => { + const imageIds = [ + createMockImageLoadRequest('image-1', 0), + createMockImageLoadRequest('image-2', 1), + ]; + const result = getNthFrames(imageIds); + expect(result).toEqual(imageIds); + }); + + it('should prioritize first two elements', () => { + const imageIds = Array.from({ length: 10 }, (_, i) => + createMockImageLoadRequest(`image-${i}`, i) + ); + const result = getNthFrames(imageIds); + + expect(result[0]).toBe(imageIds[0]); + expect(result[1]).toBe(imageIds[1]); + }); + + it('should prioritize last three elements', () => { + const imageIds = Array.from({ length: 10 }, (_, i) => + createMockImageLoadRequest(`image-${i}`, i) + ); + const result = getNthFrames(imageIds); + + expect(result).toContain(imageIds[7]); + expect(result).toContain(imageIds[8]); + expect(result).toContain(imageIds[9]); + }); + + it('should include center elements', () => { + const imageIds = Array.from({ length: 20 }, (_, i) => + createMockImageLoadRequest(`image-${i}`, i) + ); + const result = getNthFrames(imageIds); + const centerStart = imageIds.length / 2 - 3; + const centerEnd = centerStart + 6; + + for (let i = Math.ceil(centerStart); i < centerEnd; i++) { + if (i >= 0 && i < imageIds.length) { + expect(result).toContain(imageIds[i]); + } + } + }); + + it('should include nth elements where i % 7 === 2', () => { + const imageIds = Array.from({ length: 30 }, (_, i) => + createMockImageLoadRequest(`image-${i}`, i) + ); + const result = getNthFrames(imageIds); + + for (let i = 0; i < imageIds.length; i++) { + if (i % 7 === 2 && i >= 2 && i <= imageIds.length - 4) { + const centerStart = imageIds.length / 2 - 3; + const centerEnd = centerStart + 6; + if (!(i > centerStart && i < centerEnd)) { + expect(result).toContain(imageIds[i]); + } + } + } + }); + + it('should include nth elements where i % 7 === 5', () => { + const imageIds = Array.from({ length: 30 }, (_, i) => + createMockImageLoadRequest(`image-${i}`, i) + ); + const result = getNthFrames(imageIds); + + for (let i = 0; i < imageIds.length; i++) { + if (i % 7 === 5 && i >= 2 && i <= imageIds.length - 4) { + const centerStart = imageIds.length / 2 - 3; + const centerEnd = centerStart + 6; + if (!(i > centerStart && i < centerEnd)) { + expect(result).toContain(imageIds[i]); + } + } + } + }); + + it('should handle large arrays correctly', () => { + const imageIds = Array.from({ length: 100 }, (_, i) => + createMockImageLoadRequest(`image-${i}`, i) + ); + const result = getNthFrames(imageIds); + + expect(result.length).toBe(100); + expect(result[0]).toBe(imageIds[0]); + expect(result[1]).toBe(imageIds[1]); + expect(result).toContain(imageIds[97]); + expect(result).toContain(imageIds[98]); + expect(result).toContain(imageIds[99]); + }); + + it('should handle arrays where centerStart is negative', () => { + const imageIds = Array.from({ length: 3 }, (_, i) => + createMockImageLoadRequest(`image-${i}`, i) + ); + const result = getNthFrames(imageIds); + + expect(result).toEqual(imageIds); + }); + + it('should handle arrays where centerEnd exceeds length', () => { + const imageIds = Array.from({ length: 4 }, (_, i) => + createMockImageLoadRequest(`image-${i}`, i) + ); + const result = getNthFrames(imageIds); + + expect(result).toEqual(expect.arrayContaining(imageIds)); + expect(result.length).toBe(4); + }); + + it('should preserve object references', () => { + const imageIds = Array.from({ length: 10 }, (_, i) => + createMockImageLoadRequest(`image-${i}`, i) + ); + const result = getNthFrames(imageIds); + + result.forEach(item => { + expect(imageIds).toContain(item); + }); + }); +}); diff --git a/extensions/cornerstone/src/utils/getNthFrames.js b/extensions/cornerstone/src/utils/getNthFrames.ts similarity index 85% rename from extensions/cornerstone/src/utils/getNthFrames.js rename to extensions/cornerstone/src/utils/getNthFrames.ts index 38df66d9587..24e4987776f 100644 --- a/extensions/cornerstone/src/utils/getNthFrames.js +++ b/extensions/cornerstone/src/utils/getNthFrames.ts @@ -9,12 +9,9 @@ * What this does is return the first/center/start objects, as those * are often used first, then a selection of objects scattered over the * instances in order to allow making requests over a set of image instances. - * - * @param {[]} imageIds - * @returns [] reordered to be an nth selection */ -export default function getNthFrames(imageIds) { - const frames = [[], [], [], [], []]; +export default function getNthFrames(imageIds: T[]): T[] { + const frames: T[][] = [[], [], [], [], []]; const centerStart = imageIds.length / 2 - 3; const centerEnd = centerStart + 6; diff --git a/extensions/cornerstone/src/utils/getViewportEnabledElement.test.ts b/extensions/cornerstone/src/utils/getViewportEnabledElement.test.ts new file mode 100644 index 00000000000..e1496ffab6e --- /dev/null +++ b/extensions/cornerstone/src/utils/getViewportEnabledElement.test.ts @@ -0,0 +1,83 @@ +import { getEnabledElement } from '@cornerstonejs/core'; +import { getEnabledElement as OHIFgetEnabledElement } from '../state'; +import { getViewportEnabledElement } from './getViewportEnabledElement'; + +jest.mock('@cornerstonejs/core', () => ({ + getEnabledElement: jest.fn(), +})); + +jest.mock('../state', () => ({ + getEnabledElement: jest.fn(), +})); + +describe('getViewportEnabledElement', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return enabled element when OHIF getEnabledElement returns element', () => { + const mockElement = document.createElement('div'); + const mockEnabledElement = { viewport: 'test-viewport' }; + + (OHIFgetEnabledElement as jest.Mock).mockReturnValue({ element: mockElement }); + (getEnabledElement as jest.Mock).mockReturnValue(mockEnabledElement); + + const result = getViewportEnabledElement('test-viewport-id'); + + expect(OHIFgetEnabledElement).toHaveBeenCalledWith('test-viewport-id'); + expect(getEnabledElement).toHaveBeenCalledWith(mockElement); + expect(result).toBe(mockEnabledElement); + }); + + it('should return enabled element when OHIF getEnabledElement returns null', () => { + const mockEnabledElement = { viewport: 'test-viewport' }; + + (OHIFgetEnabledElement as jest.Mock).mockReturnValue(null); + (getEnabledElement as jest.Mock).mockReturnValue(mockEnabledElement); + + const result = getViewportEnabledElement('test-viewport-id'); + + expect(OHIFgetEnabledElement).toHaveBeenCalledWith('test-viewport-id'); + expect(getEnabledElement).toHaveBeenCalledWith(undefined); + expect(result).toBe(mockEnabledElement); + }); + + it('should return enabled element when OHIF getEnabledElement returns object without element', () => { + const mockEnabledElement = { viewport: 'test-viewport' }; + + (OHIFgetEnabledElement as jest.Mock).mockReturnValue({}); + (getEnabledElement as jest.Mock).mockReturnValue(mockEnabledElement); + + const result = getViewportEnabledElement('test-viewport-id'); + + expect(OHIFgetEnabledElement).toHaveBeenCalledWith('test-viewport-id'); + expect(getEnabledElement).toHaveBeenCalledWith(undefined); + expect(result).toBe(mockEnabledElement); + }); + + it('should return null when cornerstone getEnabledElement returns null', () => { + const mockElement = document.createElement('div'); + + (OHIFgetEnabledElement as jest.Mock).mockReturnValue({ element: mockElement }); + (getEnabledElement as jest.Mock).mockReturnValue(null); + + const result = getViewportEnabledElement('test-viewport-id'); + + expect(OHIFgetEnabledElement).toHaveBeenCalledWith('test-viewport-id'); + expect(getEnabledElement).toHaveBeenCalledWith(mockElement); + expect(result).toBe(null); + }); + + it('should return undefined when cornerstone getEnabledElement returns undefined', () => { + const mockElement = document.createElement('div'); + + (OHIFgetEnabledElement as jest.Mock).mockReturnValue({ element: mockElement }); + (getEnabledElement as jest.Mock).mockReturnValue(undefined); + + const result = getViewportEnabledElement('test-viewport-id'); + + expect(OHIFgetEnabledElement).toHaveBeenCalledWith('test-viewport-id'); + expect(getEnabledElement).toHaveBeenCalledWith(mockElement); + expect(result).toBe(undefined); + }); +}); diff --git a/extensions/cornerstone/src/utils/getViewportOrientationFromImageOrientationPatient.test.ts b/extensions/cornerstone/src/utils/getViewportOrientationFromImageOrientationPatient.test.ts new file mode 100644 index 00000000000..153baabcc12 --- /dev/null +++ b/extensions/cornerstone/src/utils/getViewportOrientationFromImageOrientationPatient.test.ts @@ -0,0 +1,50 @@ +import { getViewportOrientationFromImageOrientationPatient } from './getViewportOrientationFromImageOrientationPatient'; + +describe('getViewportOrientationFromImageOrientationPatient', () => { + it('should return undefined when imageOrientationPatient is null', () => { + const result = getViewportOrientationFromImageOrientationPatient(null); + expect(result).toBeUndefined(); + }); + + it('should return undefined when imageOrientationPatient is undefined', () => { + const result = getViewportOrientationFromImageOrientationPatient(undefined); + expect(result).toBeUndefined(); + }); + + it('should return undefined when imageOrientationPatient has length less than 6', () => { + const result = getViewportOrientationFromImageOrientationPatient([1, 0, 0, 0, 1]); + expect(result).toBeUndefined(); + }); + + it('should return undefined when imageOrientationPatient has length greater than 6', () => { + const result = getViewportOrientationFromImageOrientationPatient([1, 0, 0, 0, 1, 0, 0]); + expect(result).toBeUndefined(); + }); + + it('should return undefined when imageOrientationPatient is empty array', () => { + const result = getViewportOrientationFromImageOrientationPatient([]); + expect(result).toBeUndefined(); + }); + + it('should return "axial" when imageOrientationPatient matches axial orientation', () => { + const result = getViewportOrientationFromImageOrientationPatient([1, 0, 0, 0, 1, 0]); + expect(result).toBe('axial'); + }); + + it('should return "sagittal" when imageOrientationPatient matches sagittal orientation', () => { + const result = getViewportOrientationFromImageOrientationPatient([0, 1, 0, 0, 0, -1]); + expect(result).toBe('sagittal'); + }); + + it('should return "coronal" when imageOrientationPatient matches coronal orientation', () => { + const result = getViewportOrientationFromImageOrientationPatient([1, 0, 0, 0, 0, -1]); + expect(result).toBe('coronal'); + }); + + it('should return undefined when no orientation matches', () => { + const result = getViewportOrientationFromImageOrientationPatient([ + 0.5, 0.5, 0.7, 0.3, 0.8, 0.5, + ]); + expect(result).toBeUndefined(); + }); +}); diff --git a/extensions/cornerstone/src/utils/hydrationUtils.test.ts b/extensions/cornerstone/src/utils/hydrationUtils.test.ts new file mode 100644 index 00000000000..abf52ac19bd --- /dev/null +++ b/extensions/cornerstone/src/utils/hydrationUtils.test.ts @@ -0,0 +1,307 @@ +import { getUpdatedViewportsForSegmentation } from './hydrationUtils'; + +describe('getUpdatedViewportsForSegmentation', () => { + const mockHangingProtocolService = { + getViewportsRequireUpdate: jest.fn(), + }; + + const mockViewportGridService = { + getState: jest.fn(), + }; + + const mockServicesManager = { + services: { + hangingProtocolService: mockHangingProtocolService, + viewportGridService: mockViewportGridService, + }, + }; + + const mockViewport = { + viewportOptions: { + viewportId: 'target-viewport-id', + }, + }; + + const mockViewports = new Map([ + ['viewport-1', mockViewport], + ['active-viewport-id', mockViewport], + ]); + + const defaultParameters = { + viewportId: 'viewport-1', + servicesManager: mockServicesManager as unknown as AppTypes.ServicesManager, + displaySetInstanceUIDs: ['display-set-1'], + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockViewportGridService.getState.mockReturnValue({ + isHangingProtocolLayout: true, + viewports: mockViewports, + activeViewportId: 'active-viewport-id', + }); + mockHangingProtocolService.getViewportsRequireUpdate.mockReturnValue([]); + }); + + it('should get updated viewports for segmentation', () => { + const mockUpdatedViewports = [ + { + viewportOptions: { + viewportType: 'stack', + }, + }, + { + viewportOptions: { + viewportType: 'volume', + }, + }, + ]; + + mockHangingProtocolService.getViewportsRequireUpdate.mockReturnValue(mockUpdatedViewports); + + const result = getUpdatedViewportsForSegmentation(defaultParameters); + + expect(mockViewportGridService.getState).toHaveBeenCalled(); + expect(mockHangingProtocolService.getViewportsRequireUpdate).toHaveBeenCalledWith( + mockViewport.viewportOptions.viewportId, + defaultParameters.displaySetInstanceUIDs[0], + true + ); + expect(result).toEqual(mockUpdatedViewports); + }); + + it('should handle viewports without viewportOptions', () => { + const mockUpdatedViewports = [ + { + viewportOptions: { + viewportType: 'stack', + }, + }, + { + someOtherProperty: 'value', + }, + { + viewportOptions: null, + }, + ]; + + mockHangingProtocolService.getViewportsRequireUpdate.mockReturnValue(mockUpdatedViewports); + + const result = getUpdatedViewportsForSegmentation(defaultParameters); + + expect(result).toEqual([ + { + viewportOptions: { + viewportType: 'stack', + }, + }, + { + someOtherProperty: 'value', + }, + { + viewportOptions: null, + }, + ]); + }); + + it('should use activeViewportId when viewportId is not provided', () => { + const result = getUpdatedViewportsForSegmentation({ + ...defaultParameters, + viewportId: null, + }); + + expect(mockHangingProtocolService.getViewportsRequireUpdate).toHaveBeenCalledWith( + mockViewport.viewportOptions.viewportId, + defaultParameters.displaySetInstanceUIDs[0], + true + ); + expect(result).toEqual([]); + }); + + it('should use activeViewportId when viewportId is undefined', () => { + const result = getUpdatedViewportsForSegmentation({ + ...defaultParameters, + viewportId: undefined, + }); + + expect(mockHangingProtocolService.getViewportsRequireUpdate).toHaveBeenCalledWith( + mockViewport.viewportOptions.viewportId, + defaultParameters.displaySetInstanceUIDs[0], + true + ); + expect(result).toEqual([]); + }); + + it('should handle isHangingProtocolLayout false', () => { + mockViewportGridService.getState.mockReturnValue({ + isHangingProtocolLayout: false, + viewports: mockViewports, + activeViewportId: 'active-viewport-id', + }); + + const result = getUpdatedViewportsForSegmentation(defaultParameters); + + expect(mockHangingProtocolService.getViewportsRequireUpdate).toHaveBeenCalledWith( + mockViewport.viewportOptions.viewportId, + defaultParameters.displaySetInstanceUIDs[0], + false + ); + expect(result).toEqual([]); + }); + + it('should handle multiple displaySetInstanceUIDs by using first one', () => { + const result = getUpdatedViewportsForSegmentation({ + ...defaultParameters, + displaySetInstanceUIDs: ['display-set-1', 'display-set-2', 'display-set-3'], + }); + + expect(mockHangingProtocolService.getViewportsRequireUpdate).toHaveBeenCalledWith( + mockViewport.viewportOptions.viewportId, + 'display-set-1', + true + ); + expect(result).toEqual([]); + }); + + it('should handle empty displaySetInstanceUIDs array', () => { + const result = getUpdatedViewportsForSegmentation({ + ...defaultParameters, + displaySetInstanceUIDs: [], + }); + + expect(mockHangingProtocolService.getViewportsRequireUpdate).toHaveBeenCalledWith( + mockViewport.viewportOptions.viewportId, + undefined, + true + ); + expect(result).toEqual([]); + }); + + it('should handle viewport not found in viewports map', () => { + mockViewportGridService.getState.mockReturnValue({ + isHangingProtocolLayout: true, + viewports: new Map(), + activeViewportId: 'non-existent-viewport', + }); + + expect(() => getUpdatedViewportsForSegmentation(defaultParameters)).toThrow(); + }); + + it('should handle viewport with missing viewportOptions', () => { + const viewportWithoutOptions = {}; + const viewportsMap = new Map([['viewport-1', viewportWithoutOptions]]); + + mockViewportGridService.getState.mockReturnValue({ + isHangingProtocolLayout: true, + viewports: viewportsMap, + activeViewportId: 'active-viewport-id', + }); + + expect(() => getUpdatedViewportsForSegmentation(defaultParameters)).toThrow(); + }); + + it('should handle viewport with null viewportOptions', () => { + const viewportWithNullOptions = { + viewportOptions: null, + }; + const viewportsMap = new Map([['viewport-1', viewportWithNullOptions]]); + + mockViewportGridService.getState.mockReturnValue({ + isHangingProtocolLayout: true, + viewports: viewportsMap, + activeViewportId: 'active-viewport-id', + }); + + expect(() => getUpdatedViewportsForSegmentation(defaultParameters)).toThrow(); + }); + + it('should handle getViewportsRequireUpdate returning null', () => { + mockHangingProtocolService.getViewportsRequireUpdate.mockReturnValue(null); + + expect(getUpdatedViewportsForSegmentation(defaultParameters)).toEqual(null); + }); + + it('should handle mixed viewport types including volume3d', () => { + const mockUpdatedViewports = [ + { viewportOptions: { viewportType: 'stack' } }, + { viewportOptions: { viewportType: 'volume3d' } }, + { viewportOptions: { viewportType: 'volume3d' } }, + { viewportOptions: { viewportType: 'orthogonal' } }, + ]; + + mockHangingProtocolService.getViewportsRequireUpdate.mockReturnValue(mockUpdatedViewports); + + const result = getUpdatedViewportsForSegmentation(defaultParameters); + + expect(result).toEqual(mockUpdatedViewports); + }); + + it('should handle viewports with undefined viewportType', () => { + const mockUpdatedViewports = [ + { + viewportOptions: { + viewportType: undefined, + }, + }, + { + viewportOptions: { + viewportType: 'volume3d', + }, + }, + { + viewportOptions: { + someOtherProperty: 'value', + }, + }, + ]; + + mockHangingProtocolService.getViewportsRequireUpdate.mockReturnValue(mockUpdatedViewports); + + const result = getUpdatedViewportsForSegmentation(defaultParameters); + + expect(result).toEqual(mockUpdatedViewports); + }); + + it('should handle complex viewport structure', () => { + const complexViewport = { + viewportOptions: { + viewportId: 'complex-viewport-id', + viewportType: 'stack', + orientation: 'axial', + initialImageOptions: { + index: 0, + }, + }, + displaySetOptions: { + displaySetInstanceUID: 'display-set-1', + }, + }; + + const viewportsMap = new Map([['viewport-1', complexViewport]]); + + mockViewportGridService.getState.mockReturnValue({ + isHangingProtocolLayout: true, + viewports: viewportsMap, + activeViewportId: 'active-viewport-id', + }); + + const mockUpdatedViewports = [ + { + viewportOptions: { + viewportType: 'stack', + }, + }, + ]; + + mockHangingProtocolService.getViewportsRequireUpdate.mockReturnValue(mockUpdatedViewports); + + const result = getUpdatedViewportsForSegmentation(defaultParameters); + + expect(mockHangingProtocolService.getViewportsRequireUpdate).toHaveBeenCalledWith( + 'complex-viewport-id', + defaultParameters.displaySetInstanceUIDs[0], + true + ); + expect(result).toEqual(mockUpdatedViewports); + }); +}); diff --git a/extensions/cornerstone/src/utils/hydrationUtils.ts b/extensions/cornerstone/src/utils/hydrationUtils.ts index fd5f6120b1a..c1a9715b2bf 100644 --- a/extensions/cornerstone/src/utils/hydrationUtils.ts +++ b/extensions/cornerstone/src/utils/hydrationUtils.ts @@ -16,7 +16,7 @@ function getUpdatedViewportsForSegmentation({ isHangingProtocolLayout ); - return updatedViewports.filter(v => v.viewportOptions?.viewportType !== 'volume3d'); + return updatedViewports; } const getTargetViewport = ({ viewportId, viewportGridService }) => { diff --git a/extensions/cornerstone/src/utils/index.ts b/extensions/cornerstone/src/utils/index.ts index a05c5509c0e..467b6cb683c 100644 --- a/extensions/cornerstone/src/utils/index.ts +++ b/extensions/cornerstone/src/utils/index.ts @@ -9,6 +9,8 @@ import promptHydrationDialog, { HydrationCallback, HydrationSRResult, } from './promptHydrationDialog'; +import { getCenterExtent } from './getCenterExtent'; +import { createSegmentationForViewport } from './createSegmentationForViewport'; const utils = { handleSegmentChange, @@ -16,6 +18,8 @@ const utils = { setupSegmentationDataModifiedHandler, setupSegmentationModifiedHandler, promptHydrationDialog, + getCenterExtent, + createSegmentationForViewport, }; export type { HydrationDialogProps, HydrationCallback, HydrationSRResult }; diff --git a/extensions/cornerstone/src/utils/initViewTiming.test.ts b/extensions/cornerstone/src/utils/initViewTiming.test.ts new file mode 100644 index 00000000000..e6adabbe2ee --- /dev/null +++ b/extensions/cornerstone/src/utils/initViewTiming.test.ts @@ -0,0 +1,342 @@ +import { log, Enums } from '@ohif/core'; +import { EVENTS } from '@cornerstonejs/core'; +import initViewTiming from './initViewTiming'; + +jest.mock('@ohif/core', () => ({ + log: { + timingKeys: {}, + timeEnd: jest.fn(), + }, + Enums: { + TimingEnum: { + DISPLAY_SETS_TO_ALL_IMAGES: 'DISPLAY_SETS_TO_ALL_IMAGES', + DISPLAY_SETS_TO_FIRST_IMAGE: 'DISPLAY_SETS_TO_FIRST_IMAGE', + STUDY_TO_FIRST_IMAGE: 'STUDY_TO_FIRST_IMAGE', + SCRIPT_TO_VIEW: 'SCRIPT_TO_VIEW', + }, + }, +})); + +jest.mock('@cornerstonejs/core', () => ({ + EVENTS: { + IMAGE_RENDERED: 'IMAGE_RENDERED', + }, +})); + +describe('initViewTiming', () => { + const mockElement = { + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + }; + + const defaultParameters = { + element: mockElement, + }; + + const clearUnevenlyHandledViewportState = () => { + const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1]; + imageRenderedListener({ + detail: { + viewportStatus: 'rendered', + element: mockElement, + }, + }); + }; + + beforeEach(() => { + jest.clearAllMocks(); + // @ts-expect-error - restarting the object that will have values set by each test + log.timingKeys = {}; + }); + + it('should return early when no timing keys are set', () => { + // @ts-expect-error - the idea is to test an invalid state + log.timingKeys = {}; + + initViewTiming(defaultParameters); + + expect(mockElement.addEventListener).not.toHaveBeenCalled(); + }); + + it('should add event listener when timing keys are present', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + initViewTiming(defaultParameters); + + expect(mockElement.addEventListener).toHaveBeenCalledWith( + EVENTS.IMAGE_RENDERED, + expect.any(Function) + ); + + clearUnevenlyHandledViewportState(); + }); + + it('should handle multiple timing keys', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES] = true; + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + initViewTiming(defaultParameters); + + expect(mockElement.addEventListener).toHaveBeenCalledWith( + EVENTS.IMAGE_RENDERED, + expect.any(Function) + ); + + clearUnevenlyHandledViewportState(); + }); + + it('should return early in imageRenderedListener when viewportStatus is preRender', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + initViewTiming(defaultParameters); + + const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1]; + const evt = { + detail: { + viewportStatus: 'preRender', + element: mockElement, + }, + }; + + imageRenderedListener(evt); + + expect(log.timeEnd).not.toHaveBeenCalled(); + expect(mockElement.removeEventListener).not.toHaveBeenCalled(); + + clearUnevenlyHandledViewportState(); + }); + + it('should call timeEnd for timing keys when image is rendered', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + initViewTiming(defaultParameters); + + const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1]; + const evt = { + detail: { + viewportStatus: 'rendered', + element: mockElement, + }, + }; + + imageRenderedListener(evt); + + expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE); + expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.STUDY_TO_FIRST_IMAGE); + expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.SCRIPT_TO_VIEW); + }); + + it('should remove event listener after image is rendered', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + initViewTiming(defaultParameters); + + const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1]; + const evt = { + detail: { + viewportStatus: 'rendered', + element: mockElement, + }, + }; + + imageRenderedListener(evt); + + expect(mockElement.removeEventListener).toHaveBeenCalledWith( + EVENTS.IMAGE_RENDERED, + imageRenderedListener + ); + }); + + it('should not call timeEnd for ALL_IMAGES when viewports are still waiting', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + initViewTiming(defaultParameters); + initViewTiming(defaultParameters); + + const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1]; + const evt = { + detail: { + viewportStatus: 'rendered', + element: mockElement, + }, + }; + + imageRenderedListener(evt); + + expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE); + expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.STUDY_TO_FIRST_IMAGE); + expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.SCRIPT_TO_VIEW); + expect(log.timeEnd).not.toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES); + + clearUnevenlyHandledViewportState(); + }); + + it('should handle multiple viewports finishing in sequence', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + const mockElement2 = { + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + }; + + initViewTiming(defaultParameters); + initViewTiming({ element: mockElement2 }); + + const imageRenderedListener1 = mockElement.addEventListener.mock.calls[0][1]; + const imageRenderedListener2 = mockElement2.addEventListener.mock.calls[0][1]; + + const evt1 = { + detail: { + viewportStatus: 'rendered', + element: mockElement, + }, + }; + + const evt2 = { + detail: { + viewportStatus: 'rendered', + element: mockElement2, + }, + }; + + imageRenderedListener1(evt1); + + expect(log.timeEnd).not.toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES); + + imageRenderedListener2(evt2); + + expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES); + }); + + it('should call timeEnd for ALL_IMAGES when last viewport finishes', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + initViewTiming(defaultParameters); + + const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1]; + const evt = { + detail: { + viewportStatus: 'rendered', + element: mockElement, + }, + }; + + imageRenderedListener(evt); + + expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES); + }); + + it('should handle different viewportStatus values', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + initViewTiming(defaultParameters); + + const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1]; + const evt = { + detail: { + viewportStatus: 'loading', + element: mockElement, + }, + }; + + imageRenderedListener(evt); + + expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE); + expect(mockElement.removeEventListener).toHaveBeenCalled(); + }); + + it('should handle undefined viewportStatus', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + initViewTiming(defaultParameters); + + const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1]; + const evt = { + detail: { + element: mockElement, + }, + }; + + imageRenderedListener(evt); + + expect(log.timeEnd).toHaveBeenCalledWith(Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE); + expect(mockElement.removeEventListener).toHaveBeenCalled(); + }); + + it('should handle missing detail object', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + initViewTiming(defaultParameters); + + const imageRenderedListener = mockElement.addEventListener.mock.calls[0][1]; + const evt = {}; + + expect(() => imageRenderedListener(evt)).toThrow(); + }); + + it('should handle timing keys with falsy values', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = false; + log.timingKeys[Enums.TimingEnum.STUDY_TO_FIRST_IMAGE] = null; + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES] = undefined; + + initViewTiming(defaultParameters); + + expect(mockElement.addEventListener).not.toHaveBeenCalled(); + }); + + it('should handle timing keys with truthy values', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = 'some value'; + log.timingKeys[Enums.TimingEnum.STUDY_TO_FIRST_IMAGE] = 1; + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES] = {}; + + initViewTiming(defaultParameters); + + expect(mockElement.addEventListener).toHaveBeenCalledWith( + EVENTS.IMAGE_RENDERED, + expect.any(Function) + ); + }); + + it('should initialize IMAGE_TIMING_KEYS on first call', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + initViewTiming(defaultParameters); + + expect(mockElement.addEventListener).toHaveBeenCalled(); + }); + + it('should handle empty element object', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + const emptyElement = {}; + + expect(() => initViewTiming({ element: emptyElement })).toThrow(); + }); + + it('should handle null element', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + expect(() => initViewTiming({ element: null })).toThrow(); + }); + + it('should handle multiple calls with same element', () => { + log.timingKeys[Enums.TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE] = true; + + initViewTiming(defaultParameters); + initViewTiming(defaultParameters); + initViewTiming(defaultParameters); + + expect(mockElement.addEventListener).toHaveBeenCalledTimes(3); + }); + + it('should handle case where no timing keys match', () => { + // @ts-expect-error - the idea is to test an invalid state + log.timingKeys = { + someOtherKey: true, + anotherKey: 'value', + }; + + initViewTiming(defaultParameters); + + expect(mockElement.addEventListener).not.toHaveBeenCalled(); + }); +}); diff --git a/extensions/cornerstone/src/utils/initWebWorkerProgressHandler.test.ts b/extensions/cornerstone/src/utils/initWebWorkerProgressHandler.test.ts new file mode 100644 index 00000000000..400d5a632b0 --- /dev/null +++ b/extensions/cornerstone/src/utils/initWebWorkerProgressHandler.test.ts @@ -0,0 +1,381 @@ +import { eventTarget, EVENTS } from '@cornerstonejs/core'; +import * as cornerstoneTools from '@cornerstonejs/tools'; +import { initializeWebWorkerProgressHandler } from './initWebWorkerProgressHandler'; + +jest.mock('@cornerstonejs/core', () => ({ + eventTarget: { + addEventListener: jest.fn(), + }, + EVENTS: { + WEB_WORKER_PROGRESS: 'WEB_WORKER_PROGRESS', + }, +})); + +jest.mock('@cornerstonejs/tools', () => ({ + Enums: { + WorkerTypes: { + COMPUTE_STATISTICS: 'COMPUTE_STATISTICS', + }, + }, +})); + +describe('initializeWebWorkerProgressHandler', () => { + const mockUINotificationService = { + show: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + jest.spyOn(console, 'error').mockImplementation(); + jest.spyOn(console, 'debug').mockImplementation(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should register event listener for WEB_WORKER_PROGRESS', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + expect(eventTarget.addEventListener).toHaveBeenCalledWith( + EVENTS.WEB_WORKER_PROGRESS, + expect.any(Function) + ); + }); + + it('should skip notifications for COMPUTE_STATISTICS worker type', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + const detail = { + progress: 0, + type: cornerstoneTools.Enums.WorkerTypes.COMPUTE_STATISTICS, + id: 'test-id', + }; + + eventHandler({ detail }); + + expect(mockUINotificationService.show).not.toHaveBeenCalled(); + }); + + it('should show notification when progress is 0 for new task', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + const detail = { + progress: 0, + type: 'TEST_WORKER', + id: 'test-id', + }; + + eventHandler({ detail }); + + expect(mockUINotificationService.show).toHaveBeenCalledWith({ + id: 'worker-task-test_worker', + title: 'TEST_WORKER', + message: 'Computing...', + autoClose: false, + allowDuplicates: false, + deduplicationInterval: 60000, + promise: expect.any(Promise), + promiseMessages: { + loading: 'Computing...', + success: 'Completed successfully', + error: 'Web Worker failed', + }, + }); + }); + + it('should not show duplicate notification for same task type', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + const detail = { + progress: 0, + type: 'TEST_WORKER', + id: 'test-id-1', + }; + + eventHandler({ detail }); + eventHandler({ detail: { ...detail, id: 'test-id-2' } }); + + expect(mockUINotificationService.show).toHaveBeenCalledTimes(1); + expect(console.debug).toHaveBeenCalledWith( + 'Already tracking a "TEST_WORKER" task, skipping duplicate notification' + ); + }); + + it('should resolve promise when progress is 100', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + + const startDetail = { + progress: 0, + type: 'TEST_WORKER', + id: 'test-id', + }; + + eventHandler({ detail: startDetail }); + + const completeDetail = { + progress: 100, + type: 'TEST_WORKER', + id: 'test-id', + }; + + eventHandler({ detail: completeDetail }); + + expect(console.debug).toHaveBeenCalledWith('Worker task "TEST_WORKER" completed successfully'); + }); + + it('should handle completion for non-tracked task gracefully', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + const detail = { + progress: 100, + type: 'UNKNOWN_WORKER', + id: 'test-id', + }; + + eventHandler({ detail }); + + expect(console.debug).not.toHaveBeenCalledWith( + 'Worker task "UNKNOWN_WORKER" completed successfully' + ); + }); + + it('should normalize task keys correctly', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + const detail = { + progress: 0, + type: 'Test Worker Type', + id: 'test-id', + }; + + eventHandler({ detail }); + + expect(mockUINotificationService.show).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'worker-task-test-worker-type', + }) + ); + }); + + it('should handle error when setting active worker task', () => { + jest.spyOn(global, 'Promise').mockImplementationOnce(() => { + throw new Error('Promise creation failed'); + }); + + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + const detail = { + progress: 0, + type: 'TEST_WORKER', + id: 'test-id', + }; + + expect(() => eventHandler({ detail })).not.toThrow(); + expect(console.error).toHaveBeenCalledWith( + 'Error in web worker progress handler for type "TEST_WORKER":', + expect.any(Error) + ); + + jest.restoreAllMocks(); + }); + + it('should handle error when showing notification', () => { + mockUINotificationService.show.mockImplementationOnce(() => { + throw new Error('Notification service failed'); + }); + + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + const detail = { + progress: 0, + type: 'TEST_WORKER', + id: 'test-id', + }; + + expect(() => eventHandler({ detail })).not.toThrow(); + expect(console.error).toHaveBeenCalledWith( + 'Error showing web worker notification for type "TEST_WORKER":', + expect.any(Error) + ); + }); + + it('should handle missing detail object', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + + expect(() => eventHandler({})).not.toThrow(); + expect(console.error).toHaveBeenCalledWith( + 'Error in web worker progress handler for type "undefined":', + expect.any(Error) + ); + }); + + it('should handle undefined detail', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + + expect(() => eventHandler({ detail: undefined })).not.toThrow(); + expect(console.error).toHaveBeenCalledWith( + 'Error in web worker progress handler for type "undefined":', + expect.any(Error) + ); + }); + + it('should handle cleanup error gracefully', () => { + const mockMap = new Map(); + mockMap.delete = jest.fn(() => { + throw new Error('Delete failed'); + }); + + jest.spyOn(global, 'Map').mockImplementationOnce(() => mockMap); + + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + const detail = { + progress: 0, + type: 'TEST_WORKER', + id: 'test-id', + }; + + mockUINotificationService.show.mockImplementationOnce(() => { + throw new Error('Notification failed'); + }); + + expect(() => eventHandler({ detail })).not.toThrow(); + expect(console.error).toHaveBeenCalledWith( + 'Error cleaning up active worker task for type "TEST_WORKER":', + expect.any(Error) + ); + + jest.restoreAllMocks(); + }); + + it('should handle intermediate progress values', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + + const startDetail = { + progress: 0, + type: 'TEST_WORKER', + id: 'test-id', + }; + + eventHandler({ detail: startDetail }); + + const intermediateDetail = { + progress: 50, + type: 'TEST_WORKER', + id: 'test-id', + }; + + eventHandler({ detail: intermediateDetail }); + + expect(mockUINotificationService.show).toHaveBeenCalledTimes(1); + }); + + it('should handle multiple different worker types simultaneously', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + + const detail1 = { + progress: 0, + type: 'WORKER_TYPE_1', + id: 'test-id-1', + }; + + const detail2 = { + progress: 0, + type: 'WORKER_TYPE_2', + id: 'test-id-2', + }; + + eventHandler({ detail: detail1 }); + eventHandler({ detail: detail2 }); + + expect(mockUINotificationService.show).toHaveBeenCalledTimes(2); + expect(mockUINotificationService.show).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + id: 'worker-task-worker_type_1', + title: 'WORKER_TYPE_1', + }) + ); + expect(mockUINotificationService.show).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + id: 'worker-task-worker_type_2', + title: 'WORKER_TYPE_2', + }) + ); + }); + + it('should complete multiple worker types independently', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + + eventHandler({ detail: { progress: 0, type: 'WORKER_1', id: 'id-1' } }); + eventHandler({ detail: { progress: 0, type: 'WORKER_2', id: 'id-2' } }); + eventHandler({ detail: { progress: 100, type: 'WORKER_1', id: 'id-1' } }); + + expect(console.debug).toHaveBeenCalledWith('Worker task "WORKER_1" completed successfully'); + + eventHandler({ detail: { progress: 100, type: 'WORKER_2', id: 'id-2' } }); + + expect(console.debug).toHaveBeenCalledWith('Worker task "WORKER_2" completed successfully'); + }); + + it('should handle special characters in worker type names', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + const detail = { + progress: 0, + type: 'Test-Worker_Type@123', + id: 'test-id', + }; + + eventHandler({ detail }); + + expect(mockUINotificationService.show).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'worker-task-test-worker_type@123', + }) + ); + }); + + it('should handle empty worker type', () => { + initializeWebWorkerProgressHandler(mockUINotificationService); + + const eventHandler = (eventTarget.addEventListener as jest.Mock).mock.calls[0][1]; + const detail = { + progress: 0, + type: '', + id: 'test-id', + }; + + eventHandler({ detail }); + + expect(mockUINotificationService.show).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'worker-task-', + title: '', + }) + ); + }); +}); diff --git a/extensions/cornerstone/src/utils/interleave.test.ts b/extensions/cornerstone/src/utils/interleave.test.ts new file mode 100644 index 00000000000..59db7c06f03 --- /dev/null +++ b/extensions/cornerstone/src/utils/interleave.test.ts @@ -0,0 +1,304 @@ +import interleave from './interleave'; + +describe('interleave', () => { + beforeEach(() => { + jest.spyOn(console, 'time').mockImplementation(); + jest.spyOn(console, 'timeEnd').mockImplementation(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should return empty array when input is null', () => { + const result = interleave(null); + + expect(result).toEqual([]); + }); + + it('should return empty array when input is undefined', () => { + const result = interleave(undefined); + + expect(result).toEqual([]); + }); + + it('should return empty array when input is empty array', () => { + const result = interleave([]); + + expect(result).toEqual([]); + }); + + it('should return same array when only one list is provided', () => { + const input = [[1, 2, 3, 4]]; + + const result = interleave(input); + + expect(result).toEqual([1, 2, 3, 4]); + expect(result).toBe(input[0]); + }); + + it('should interleave two arrays of equal length', () => { + const input = [ + [1, 3, 5], + [2, 4, 6], + ]; + + const result = interleave(input); + + expect(result).toEqual([1, 2, 3, 4, 5, 6]); + }); + + it('should interleave three arrays of equal length', () => { + const input = [ + [1, 4, 7], + [2, 5, 8], + [3, 6, 9], + ]; + + const result = interleave(input); + + expect(result).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]); + }); + + it('should handle arrays of different lengths', () => { + const input = [ + [1, 4], + [2, 5, 7, 8], + [3, 6], + ]; + + const result = interleave(input); + + expect(result).toEqual([1, 2, 3, 4, 5, 6, 7, 8]); + }); + + it('should handle first array being longer', () => { + const input = [ + [1, 3, 5, 7, 9, 11], + [2, 4, 6], + ]; + + const result = interleave(input); + + expect(result).toEqual([1, 2, 3, 4, 5, 6, 7, 9, 11]); + }); + + it('should handle second array being longer', () => { + const input = [ + [1, 3, 5], + [2, 4, 6, 8, 10, 12], + ]; + + const result = interleave(input); + + expect(result).toEqual([1, 2, 3, 4, 5, 6, 8, 10, 12]); + }); + + it('should handle empty arrays in the input', () => { + const input = [[1, 3, 5], [], [2, 4, 6]]; + + const result = interleave(input); + + expect(result).toEqual([1, 2, 3, 4, 5, 6]); + }); + + it('should handle all empty arrays', () => { + const input = [[], [], []]; + + const result = interleave(input); + + expect(result).toEqual([]); + }); + + it('should handle single element arrays', () => { + const input = [[1], [2], [3], [4]]; + + const result = interleave(input); + + expect(result).toEqual([1, 2, 3, 4]); + }); + + it('should handle mixed single and multi-element arrays', () => { + const input = [[1], [2, 5, 8], [3], [4, 6, 7, 9]]; + + const result = interleave(input); + + expect(result).toEqual([1, 2, 3, 4, 5, 6, 8, 7, 9]); + }); + + it('should handle string arrays', () => { + const input = [ + ['a', 'c', 'e'], + ['b', 'd', 'f'], + ]; + + const result = interleave(input); + + expect(result).toEqual(['a', 'b', 'c', 'd', 'e', 'f']); + }); + + it('should handle object arrays', () => { + const obj1 = { id: 1 }; + const obj2 = { id: 2 }; + const obj3 = { id: 3 }; + const obj4 = { id: 4 }; + + const input = [ + [obj1, obj3], + [obj2, obj4], + ]; + + const result = interleave(input); + + expect(result).toEqual([obj1, obj2, obj3, obj4]); + }); + + it('should handle large arrays efficiently', () => { + const array1 = Array.from({ length: 1000 }, (_, i) => i * 2); + const array2 = Array.from({ length: 1000 }, (_, i) => i * 2 + 1); + const input = [array1, array2]; + + const startTime = performance.now(); + const result = interleave(input); + const endTime = performance.now(); + + expect(result).toHaveLength(2000); + expect(result[0]).toBe(0); + expect(result[1]).toBe(1); + expect(result[2]).toBe(2); + expect(result[3]).toBe(3); + expect(endTime - startTime).toBeLessThan(100); + }); + + it('should handle very large number of arrays', () => { + const input = Array.from({ length: 100 }, (_, i) => [i, i + 100, i + 200]); + + const result = interleave(input); + + expect(result).toHaveLength(300); + expect(result.slice(0, 100)).toEqual(Array.from({ length: 100 }, (_, i) => i)); + }); + + it('should handle arrays with different data types', () => { + const input = [ + [1, 'a', true], + [2, 'b', false], + [3, 'c', null], + ]; + + const result = interleave(input); + + expect(result).toEqual([1, 2, 3, 'a', 'b', 'c', true, false, null]); + }); + + it('should not mutate original arrays', () => { + const array1 = [1, 3, 5]; + const array2 = [2, 4, 6]; + const input = [array1, array2]; + const originalArray1 = [...array1]; + const originalArray2 = [...array2]; + + interleave(input); + + expect(array1).toEqual(originalArray1); + expect(array2).toEqual(originalArray2); + }); + + it('should handle nested arrays', () => { + const input = [ + [ + [1, 2], + [5, 6], + ], + [ + [3, 4], + [7, 8], + ], + ]; + + const result = interleave(input); + + expect(result).toEqual([ + [1, 2], + [3, 4], + [5, 6], + [7, 8], + ]); + }); + + it('should handle arrays with undefined and null values', () => { + const input = [ + [1, undefined, 3], + [null, 2, 4], + ]; + + const result = interleave(input); + + expect(result).toEqual([1, null, undefined, 2, 3, 4]); + }); + + it('should handle extremely unbalanced arrays', () => { + const input = [[1], Array.from({ length: 1000 }, (_, i) => i + 2)]; + + const result = interleave(input); + + expect(result).toHaveLength(1001); + expect(result[0]).toBe(1); + expect(result[1]).toBe(2); + expect(result[2]).toBe(3); + }); + + it('should handle performance with many small arrays', () => { + const input = Array.from({ length: 1000 }, (_, i) => [i]); + + const startTime = performance.now(); + const result = interleave(input); + const endTime = performance.now(); + + expect(result).toHaveLength(1000); + expect(result).toEqual(Array.from({ length: 1000 }, (_, i) => i)); + expect(endTime - startTime).toBeLessThan(50); + }); + + it('should handle zero values correctly', () => { + const input = [ + [0, 2, 4], + [1, 3, 5], + ]; + + const result = interleave(input); + + expect(result).toEqual([0, 1, 2, 3, 4, 5]); + }); + + it('should handle boolean arrays', () => { + const input = [ + [true, false, true], + [false, true, false], + ]; + + const result = interleave(input); + + expect(result).toEqual([true, false, false, true, true, false]); + }); + + it('should handle single empty array with non-empty arrays', () => { + const input = [[], [1, 2, 3], [4, 5, 6]]; + + const result = interleave(input); + + expect(result).toEqual([1, 4, 2, 5, 3, 6]); + }); + + it('should maintain order within each array', () => { + const input = [ + [10, 20, 30, 40, 50], + [15, 25], + [12, 22, 32], + ]; + + const result = interleave(input); + + expect(result).toEqual([10, 15, 12, 20, 25, 22, 30, 32, 40, 50]); + }); +}); diff --git a/extensions/cornerstone/src/utils/interleave.js b/extensions/cornerstone/src/utils/interleave.ts similarity index 59% rename from extensions/cornerstone/src/utils/interleave.js rename to extensions/cornerstone/src/utils/interleave.ts index a21d3fb7e0b..a3a10cff06a 100644 --- a/extensions/cornerstone/src/utils/interleave.js +++ b/extensions/cornerstone/src/utils/interleave.ts @@ -3,28 +3,30 @@ * in the returned list, the second items are next etc. * Does this in a O(n) fashion, and return lists[0] if there is only one list. * - * @param {[]} lists - * @returns [] reordered to be breadth first traversal of lists + * @param lists - Array of arrays to interleave + * @returns Array reordered to be breadth first traversal of lists */ -export default function interleave(lists) { +export default function interleave(lists: T[][]): T[] { if (!lists || !lists.length) { return []; } if (lists.length === 1) { return lists[0]; } - console.time('interleave'); + const useLists = [...lists]; const ret = []; for (let i = 0; useLists.length > 0; i++) { - for (const list of useLists) { + for (let j = 0; j < useLists.length; j++) { + const list = useLists[j]; if (i >= list.length) { - useLists.splice(useLists.indexOf(list), 1); + useLists.splice(j, 1); + j--; // Adjust index after removal to avoid iterator skipping continue; } ret.push(list[i]); } } - console.timeEnd('interleave'); + return ret; } diff --git a/extensions/cornerstone/src/utils/interleaveCenterLoader.test.ts b/extensions/cornerstone/src/utils/interleaveCenterLoader.test.ts new file mode 100644 index 00000000000..0d91f04d599 --- /dev/null +++ b/extensions/cornerstone/src/utils/interleaveCenterLoader.test.ts @@ -0,0 +1,547 @@ +import { cache, imageLoadPoolManager, Enums } from '@cornerstonejs/core'; +import getInterleavedFrames from './getInterleavedFrames'; +import zip from 'lodash.zip'; +import compact from 'lodash.compact'; +import flatten from 'lodash.flatten'; +import interleaveCenterLoader from './interleaveCenterLoader'; + +jest.mock('@cornerstonejs/core', () => ({ + cache: { + getVolume: jest.fn(), + }, + imageLoadPoolManager: { + addRequest: jest.fn(), + }, + Enums: { + RequestType: { + Prefetch: 'Prefetch', + }, + }, +})); + +jest.mock('./getInterleavedFrames', () => jest.fn()); +jest.mock('lodash.zip', () => jest.fn()); +jest.mock('lodash.compact', () => jest.fn()); +jest.mock('lodash.flatten', () => jest.fn()); + +describe('interleaveCenterLoader', () => { + const mockVolumeInput = { + volumeId: 'test-volume-id', + }; + + const mockVolume = { + metadata: { + SeriesInstanceUID: 'test-series-uid', + }, + getImageLoadRequests: jest.fn(), + }; + + const mockImageLoadRequest = { + imageId: 'test-image-id', + callLoadImage: jest.fn(), + additionalDetails: 'test-details', + imageIdIndex: 0, + options: { test: 'option' }, + }; + + const mockDisplaySetsInfo = [ + { + displaySetInstanceUID: 'test-volume-id', + displaySetOptions: {}, + }, + ]; + + const mockMatchDetails = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: mockDisplaySetsInfo, + }, + ], + ]); + + const defaultParameters = { + data: { + viewportId: 'test-viewport-id', + volumeInputArray: [mockVolumeInput], + }, + displaySetsMatchDetails: {}, + viewportMatchDetails: mockMatchDetails, + }; + + beforeEach(() => { + jest.clearAllMocks(); + (cache.getVolume as jest.Mock).mockReturnValue(mockVolume); + mockVolume.getImageLoadRequests.mockReturnValue([mockImageLoadRequest]); + (getInterleavedFrames as jest.Mock).mockReturnValue([{ imageId: 'test-image-id' }]); + (zip as jest.Mock).mockImplementation((...args) => args[0]); + (compact as jest.Mock).mockImplementation(input => input?.filter(Boolean) ?? []); + (flatten as jest.Mock).mockImplementation(input => input?.flat() ?? []); + }); + + it('should store viewport and volume input array mapping on first call', () => { + interleaveCenterLoader(defaultParameters); + + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId); + }); + + it('should return early when volume is not found', () => { + (cache.getVolume as jest.Mock).mockReturnValue(null); + + const result = interleaveCenterLoader(defaultParameters); + + expect(result).toBeUndefined(); + expect(mockVolume.getImageLoadRequests).not.toHaveBeenCalled(); + }); + + it('should return early when volume input array size does not match display set UIDs', () => { + const mockMatchDetailsWithDifferentSize = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [ + { displaySetInstanceUID: 'display-set-1' }, + { displaySetInstanceUID: 'display-set-2' }, + ], + }, + ], + ]); + + const result = interleaveCenterLoader({ + ...defaultParameters, + viewportMatchDetails: mockMatchDetailsWithDifferentSize, + }); + + expect(result).toBeUndefined(); + }); + + it('should process volume and create interleaved requests', () => { + const result = interleaveCenterLoader(defaultParameters); + + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId); + expect(mockVolume.getImageLoadRequests).toHaveBeenCalled(); + expect(getInterleavedFrames).toHaveBeenCalledWith(['test-image-id']); + expect(zip).toHaveBeenCalled(); + expect(compact).toHaveBeenCalled(); + expect(flatten).toHaveBeenCalled(); + expect(result).toBeInstanceOf(Map); + }); + + it('should add requests to imageLoadPoolManager with correct parameters', () => { + interleaveCenterLoader(defaultParameters); + + expect(imageLoadPoolManager.addRequest).toHaveBeenCalledWith( + expect.any(Function), + Enums.RequestType.Prefetch, + mockImageLoadRequest.additionalDetails, + 0 + ); + }); + + it('should bind callLoadImage with correct parameters', () => { + interleaveCenterLoader(defaultParameters); + + const addRequestCall = (imageLoadPoolManager.addRequest as jest.Mock).mock.calls[0]; + const boundFunction = addRequestCall[0]; + + boundFunction(); + + expect(mockImageLoadRequest.callLoadImage).toHaveBeenCalledWith( + mockImageLoadRequest.imageId, + mockImageLoadRequest.imageIdIndex, + mockImageLoadRequest.options + ); + }); + + it('should handle volumes without image load requests', () => { + mockVolume.getImageLoadRequests.mockReturnValue([]); + + const result = interleaveCenterLoader(defaultParameters); + + expect(zip).toHaveBeenCalledWith(); + expect(result).toBeInstanceOf(Map); + }); + + it('should handle volumes with empty requests array', () => { + mockVolume.getImageLoadRequests.mockReturnValue([]); + + const result = interleaveCenterLoader(defaultParameters); + + expect(getInterleavedFrames).not.toHaveBeenCalled(); + expect(result).toBeInstanceOf(Map); + }); + + it('should handle volumes with null first request', () => { + mockVolume.getImageLoadRequests.mockReturnValue([null]); + + const result = interleaveCenterLoader(defaultParameters); + + expect(getInterleavedFrames).not.toHaveBeenCalled(); + expect(result).toBeInstanceOf(Map); + }); + + it('should handle volumes with requests without imageId', () => { + const requestWithoutImageId = { + callLoadImage: jest.fn(), + additionalDetails: 'test-details', + }; + + mockVolume.getImageLoadRequests.mockReturnValue([requestWithoutImageId]); + + const result = interleaveCenterLoader(defaultParameters); + + expect(getInterleavedFrames).not.toHaveBeenCalled(); + expect(result).toBeInstanceOf(Map); + }); + + it('should handle multiple volume inputs', () => { + const mockVolumeInput2 = { + volumeId: 'test-volume-id-2', + }; + + const mockVolume2 = { + metadata: { + SeriesInstanceUID: 'test-series-uid-2', + }, + getImageLoadRequests: jest.fn().mockReturnValue([mockImageLoadRequest]), + }; + + (cache.getVolume as jest.Mock) + .mockReturnValueOnce(mockVolume) + .mockReturnValueOnce(mockVolume2) + .mockReturnValueOnce(mockVolume) + .mockReturnValueOnce(mockVolume2); + + const mockMatchDetailsWithMultipleVolumes = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [ + { displaySetInstanceUID: 'test-volume-id' }, + { displaySetInstanceUID: 'test-volume-id-2' }, + ], + }, + ], + ]); + + interleaveCenterLoader({ + ...defaultParameters, + data: { + ...defaultParameters.data, + volumeInputArray: [mockVolumeInput, mockVolumeInput2], + }, + viewportMatchDetails: mockMatchDetailsWithMultipleVolumes, + }); + + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId); + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput2.volumeId); + }); + + it('should handle duplicate volume IDs by not adding them again', () => { + const duplicateVolumeInput = { + volumeId: mockVolumeInput.volumeId, + }; + + interleaveCenterLoader({ + ...defaultParameters, + data: { + ...defaultParameters.data, + volumeInputArray: [mockVolumeInput, duplicateVolumeInput], + }, + }); + + expect(cache.getVolume).toHaveBeenCalledTimes(3); + }); + + it('should clear internal maps after processing', () => { + const firstResult = interleaveCenterLoader(defaultParameters); + const secondResult = interleaveCenterLoader(defaultParameters); + + expect(firstResult).toBeInstanceOf(Map); + expect(secondResult).toBeInstanceOf(Map); + }); + + it('should handle multiple image load requests', () => { + const mockImageLoadRequest2 = { + imageId: 'test-image-id-2', + callLoadImage: jest.fn(), + additionalDetails: 'test-details-2', + imageIdIndex: 1, + options: { test: 'option2' }, + }; + + mockVolume.getImageLoadRequests.mockReturnValue([mockImageLoadRequest, mockImageLoadRequest2]); + + (getInterleavedFrames as jest.Mock).mockReturnValue([ + { imageId: 'test-image-id' }, + { imageId: 'test-image-id-2' }, + ]); + + (compact as jest.Mock).mockReturnValue([mockImageLoadRequest, mockImageLoadRequest2]); + (flatten as jest.Mock).mockReturnValue([mockImageLoadRequest, mockImageLoadRequest2]); + + interleaveCenterLoader(defaultParameters); + + expect(getInterleavedFrames).toHaveBeenCalledWith(['test-image-id', 'test-image-id-2']); + expect(imageLoadPoolManager.addRequest).toHaveBeenCalledTimes(2); + }); + + it('should handle getInterleavedFrames returning empty array', () => { + (getInterleavedFrames as jest.Mock).mockReturnValue([]); + + const result = interleaveCenterLoader(defaultParameters); + + expect(imageLoadPoolManager.addRequest).not.toHaveBeenCalled(); + expect(result).toBeInstanceOf(Map); + }); + + it('should handle zip returning empty result', () => { + (zip as jest.Mock).mockReturnValue([]); + + const result = interleaveCenterLoader(defaultParameters); + + expect(compact).toHaveBeenCalledWith([]); + expect(result).toBeInstanceOf(Map); + }); + + it('should handle compact filtering out null values', () => { + (compact as jest.Mock).mockReturnValue([]); + + const result = interleaveCenterLoader(defaultParameters); + + expect(imageLoadPoolManager.addRequest).not.toHaveBeenCalled(); + expect(result).toBeInstanceOf(Map); + }); + + it('should map imageIds to requests correctly', () => { + const mockImageLoadRequest2 = { + imageId: 'test-image-id-2', + callLoadImage: jest.fn(), + }; + + mockVolume.getImageLoadRequests.mockReturnValue([mockImageLoadRequest, mockImageLoadRequest2]); + + (getInterleavedFrames as jest.Mock).mockReturnValue([ + { imageId: 'test-image-id-2' }, + { imageId: 'test-image-id' }, + ]); + + interleaveCenterLoader(defaultParameters); + + expect(getInterleavedFrames).toHaveBeenCalledWith(['test-image-id', 'test-image-id-2']); + }); + + it('should return copy of viewport volume input array map', () => { + const result = interleaveCenterLoader(defaultParameters); + + expect(result.has(defaultParameters.data.viewportId)).toBe(true); + expect(result.get(defaultParameters.data.viewportId)).toEqual( + defaultParameters.data.volumeInputArray + ); + }); + + it('should handle multiple match details with different displaySets', () => { + const mockMultipleMatchDetails = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [{ displaySetInstanceUID: 'display-set-1' }], + }, + ], + [ + 'viewport-2', + { + displaySetsInfo: [{ displaySetInstanceUID: 'display-set-2' }], + }, + ], + ]); + + const result = interleaveCenterLoader({ + ...defaultParameters, + viewportMatchDetails: mockMultipleMatchDetails, + }); + + expect(result).toBeUndefined(); + }); + + it('should handle empty match details', () => { + const emptyMatchDetails = new Map(); + + const result = interleaveCenterLoader({ + ...defaultParameters, + viewportMatchDetails: emptyMatchDetails, + }); + + expect(result).toBeUndefined(); + }); + + it('should handle empty volumeInputArray', () => { + const result = interleaveCenterLoader({ + ...defaultParameters, + data: { + ...defaultParameters.data, + volumeInputArray: [], + }, + }); + + expect(result).toBeUndefined(); + }); + + it('should handle requests with missing properties gracefully', () => { + const incompleteRequest = { + imageId: 'test-image-id', + callLoadImage: jest.fn(), + }; + + mockVolume.getImageLoadRequests.mockReturnValue([incompleteRequest]); + (getInterleavedFrames as jest.Mock).mockReturnValue([{ imageId: 'test-image-id' }]); + (compact as jest.Mock).mockReturnValue([incompleteRequest]); + (flatten as jest.Mock).mockReturnValue([incompleteRequest]); + + interleaveCenterLoader(defaultParameters); + + expect(imageLoadPoolManager.addRequest).toHaveBeenCalledWith( + expect.any(Function), + Enums.RequestType.Prefetch, + undefined, + 0 + ); + }); + + it('should process different viewport IDs separately', () => { + const firstResult = interleaveCenterLoader({ + ...defaultParameters, + data: { + viewportId: 'viewport-1', + volumeInputArray: [mockVolumeInput], + }, + }); + + const secondResult = interleaveCenterLoader({ + ...defaultParameters, + data: { + viewportId: 'viewport-2', + volumeInputArray: [mockVolumeInput], + }, + }); + + expect(firstResult.has('viewport-1')).toBe(true); + expect(secondResult.has('viewport-2')).toBe(true); + }); + + it('should accumulate state across multiple calls before processing', () => { + const mockVolumeInput2 = { + volumeId: 'test-volume-id-2', + }; + + const mockVolume2 = { + metadata: { + SeriesInstanceUID: 'test-series-uid-2', + }, + getImageLoadRequests: jest.fn().mockReturnValue([mockImageLoadRequest]), + }; + + (cache.getVolume as jest.Mock) + .mockReturnValueOnce(mockVolume) + .mockReturnValueOnce(mockVolume) + .mockReturnValueOnce(mockVolume2); + + const firstCall = interleaveCenterLoader({ + ...defaultParameters, + data: { + viewportId: 'viewport-1', + volumeInputArray: [mockVolumeInput], + }, + viewportMatchDetails: new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [ + { displaySetInstanceUID: 'test-volume-id' }, + { displaySetInstanceUID: 'missing-volume-id' }, + ], + }, + ], + ]), + }); + + expect(firstCall).toBeUndefined(); + + const secondCall = interleaveCenterLoader({ + ...defaultParameters, + data: { + viewportId: 'viewport-2', + volumeInputArray: [mockVolumeInput2], + }, + viewportMatchDetails: new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [{ displaySetInstanceUID: 'test-volume-id' }], + }, + ], + [ + 'viewport-2', + { + displaySetsInfo: [{ displaySetInstanceUID: 'test-volume-id-2' }], + }, + ], + ]), + }); + + expect(secondCall).toBeInstanceOf(Map); + expect(secondCall.has('viewport-1')).toBe(true); + expect(secondCall.has('viewport-2')).toBe(true); + }); + + it('should handle volume with undefined metadata', () => { + const mockVolumeWithoutMetadata = { + getImageLoadRequests: jest.fn().mockReturnValue([]), + }; + + (cache.getVolume as jest.Mock).mockReturnValue(mockVolumeWithoutMetadata); + + expect(() => interleaveCenterLoader(defaultParameters)).toThrow(); + }); + + it('should handle multiple volumes with same image requests', () => { + const mockVolume2 = { + metadata: { SeriesInstanceUID: 'test-series-uid-2' }, + getImageLoadRequests: jest.fn().mockReturnValue([mockImageLoadRequest]), + }; + + const mockVolumeInput2 = { volumeId: 'test-volume-id-2' }; + + (cache.getVolume as jest.Mock) + .mockReturnValueOnce(mockVolume) + .mockReturnValueOnce(mockVolume2) + .mockReturnValueOnce(mockVolume) + .mockReturnValueOnce(mockVolume2); + + const mockMatchDetailsWithBothVolumes = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [ + { displaySetInstanceUID: 'test-volume-id' }, + { displaySetInstanceUID: 'test-volume-id-2' }, + ], + }, + ], + ]); + + (compact as jest.Mock).mockReturnValue([mockImageLoadRequest]); + (flatten as jest.Mock).mockReturnValue([mockImageLoadRequest]); + + interleaveCenterLoader({ + ...defaultParameters, + data: { + ...defaultParameters.data, + volumeInputArray: [mockVolumeInput, mockVolumeInput2], + }, + viewportMatchDetails: mockMatchDetailsWithBothVolumes, + }); + + expect(imageLoadPoolManager.addRequest).toHaveBeenCalledTimes(2); + }); +}); diff --git a/extensions/cornerstone/src/utils/interleaveCenterLoader.ts b/extensions/cornerstone/src/utils/interleaveCenterLoader.ts index 413a6a84360..b31ed5927de 100644 --- a/extensions/cornerstone/src/utils/interleaveCenterLoader.ts +++ b/extensions/cornerstone/src/utils/interleaveCenterLoader.ts @@ -84,7 +84,7 @@ export default function interleaveCenterLoader({ // the imageIds and save them in AllRequests for later use const AllRequests = []; volumes.forEach(volume => { - const requests = volume.getImageLoadRequests(); + const requests = volume.getImageLoadRequests?.() ?? []; if (!requests.length || !requests[0] || !requests[0].imageId) { return; diff --git a/extensions/cornerstone/src/utils/interleaveTopToBottom.test.ts b/extensions/cornerstone/src/utils/interleaveTopToBottom.test.ts new file mode 100644 index 00000000000..71afa71a34f --- /dev/null +++ b/extensions/cornerstone/src/utils/interleaveTopToBottom.test.ts @@ -0,0 +1,487 @@ +import { cache, imageLoadPoolManager, Enums } from '@cornerstonejs/core'; +import zip from 'lodash.zip'; +import compact from 'lodash.compact'; +import flatten from 'lodash.flatten'; +import interleaveTopToBottom from './interleaveTopToBottom'; + +jest.mock('@cornerstonejs/core', () => ({ + cache: { + getVolume: jest.fn(), + }, + imageLoadPoolManager: { + addRequest: jest.fn(), + }, + Enums: { + RequestType: { + Prefetch: 'Prefetch', + }, + }, +})); + +jest.mock('lodash.zip', () => jest.fn()); +jest.mock('lodash.compact', () => jest.fn()); +jest.mock('lodash.flatten', () => jest.fn()); + +describe('interleaveTopToBottom', () => { + const mockVolumeInput = { + volumeId: 'test-volume-id', + }; + + const mockVolume = { + metadata: { + SeriesInstanceUID: 'test-series-uid', + }, + getImageLoadRequests: jest.fn(), + }; + + const mockImageLoadRequest = { + imageId: 'test-image-id', + callLoadImage: jest.fn(), + additionalDetails: 'test-details', + imageIdIndex: 0, + options: { test: 'option' }, + }; + + const mockDisplaySetsInfo = [ + { + displaySetInstanceUID: 'display-set-1', + displaySetOptions: {}, + }, + ]; + + const mockMatchDetails = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: mockDisplaySetsInfo, + }, + ], + ]); + + const defaultParameters = { + data: { + viewportId: 'test-viewport-id', + volumeInputArray: [mockVolumeInput], + }, + displaySetsMatchDetails: {}, + viewportMatchDetails: mockMatchDetails, + }; + + beforeEach(() => { + jest.clearAllMocks(); + (cache.getVolume as jest.Mock).mockReturnValue(mockVolume); + mockVolume.getImageLoadRequests.mockReturnValue([mockImageLoadRequest]); + (zip as jest.Mock).mockImplementation((...args) => args[0]); + (compact as jest.Mock).mockImplementation(input => input?.filter(Boolean) ?? []); + (flatten as jest.Mock).mockImplementation(input => input?.flat() ?? []); + }); + + it('should store viewport and volume input array mapping', () => { + interleaveTopToBottom(defaultParameters); + + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId); + }); + + it('should return early when volume is not found', () => { + (cache.getVolume as jest.Mock).mockReturnValue(null); + + const result = interleaveTopToBottom(defaultParameters); + + expect(result).toBeUndefined(); + expect(mockVolume.getImageLoadRequests).not.toHaveBeenCalled(); + }); + + it('should process volume input array and create image load requests', () => { + const result = interleaveTopToBottom(defaultParameters); + + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId); + expect(mockVolume.getImageLoadRequests).toHaveBeenCalled(); + expect(zip).toHaveBeenCalled(); + expect(compact).toHaveBeenCalled(); + expect(flatten).toHaveBeenCalled(); + expect(result).toBeInstanceOf(Map); + }); + + it('should process displaySets without skipLoading option', () => { + const mockMatchDetailsWithoutSkipLoading = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [ + { + displaySetInstanceUID: 'display-set-1', + displaySetOptions: { options: { skipLoading: false } }, + }, + ], + }, + ], + ]); + + const result = interleaveTopToBottom({ + ...defaultParameters, + viewportMatchDetails: mockMatchDetailsWithoutSkipLoading, + }); + + expect(result).toBeInstanceOf(Map); + }); + + it('should return early when viewport volume display set UIDs size does not match', () => { + const mockMatchDetailsWithDifferentSize = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [ + { + displaySetInstanceUID: 'display-set-1', + }, + { + displaySetInstanceUID: 'display-set-2', + }, + ], + }, + ], + ]); + + const result = interleaveTopToBottom({ + ...defaultParameters, + viewportMatchDetails: mockMatchDetailsWithDifferentSize, + }); + + expect(result).toBeUndefined(); + }); + + it('should handle multiple volume inputs', () => { + const mockVolumeInput2 = { + volumeId: 'test-volume-id-2', + }; + + const mockVolume2 = { + metadata: { + SeriesInstanceUID: 'test-series-uid-2', + }, + getImageLoadRequests: jest.fn().mockReturnValue([mockImageLoadRequest]), + }; + + (cache.getVolume as jest.Mock) + .mockReturnValueOnce(mockVolume) + .mockReturnValueOnce(mockVolume2) + .mockReturnValueOnce(mockVolume) + .mockReturnValueOnce(mockVolume2); + + const mockMatchDetailsWithMultipleVolumes = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [ + { displaySetInstanceUID: 'test-volume-id' }, + { displaySetInstanceUID: 'test-volume-id-2' }, + ], + }, + ], + ]); + + interleaveTopToBottom({ + ...defaultParameters, + data: { + ...defaultParameters.data, + volumeInputArray: [mockVolumeInput, mockVolumeInput2], + }, + viewportMatchDetails: mockMatchDetailsWithMultipleVolumes, + }); + + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId); + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput2.volumeId); + }); + + it('should add requests to imageLoadPoolManager with correct parameters', () => { + interleaveTopToBottom(defaultParameters); + + expect(imageLoadPoolManager.addRequest).toHaveBeenCalledWith( + expect.any(Function), + Enums.RequestType.Prefetch, + mockImageLoadRequest.additionalDetails, + 0 + ); + }); + + it('should bind callLoadImage with correct parameters', () => { + interleaveTopToBottom(defaultParameters); + + const addRequestCall = (imageLoadPoolManager.addRequest as jest.Mock).mock.calls[0]; + const boundFunction = addRequestCall[0]; + + boundFunction(); + + expect(mockImageLoadRequest.callLoadImage).toHaveBeenCalledWith( + mockImageLoadRequest.imageId, + mockImageLoadRequest.imageIdIndex, + mockImageLoadRequest.options + ); + }); + + it('should handle volumes without image load requests', () => { + mockVolume.getImageLoadRequests.mockReturnValue(null); + + const result = interleaveTopToBottom(defaultParameters); + + expect(zip).toHaveBeenCalledWith(); + expect(result).toBeInstanceOf(Map); + }); + + it('should handle volumes with empty image load requests', () => { + mockVolume.getImageLoadRequests.mockReturnValue([]); + + const result = interleaveTopToBottom(defaultParameters); + + expect(zip).toHaveBeenCalledWith(); + expect(result).toBeInstanceOf(Map); + }); + + it('should handle volumes with requests without imageId', () => { + const requestWithoutImageId = { + callLoadImage: jest.fn(), + additionalDetails: 'test-details', + }; + + mockVolume.getImageLoadRequests.mockReturnValue([requestWithoutImageId]); + + const result = interleaveTopToBottom(defaultParameters); + + expect(zip).toHaveBeenCalledWith(); + expect(result).toBeInstanceOf(Map); + }); + + it('should reverse the requests before adding to AllRequests', () => { + const mockRequests = [ + { imageId: 'image-1', callLoadImage: jest.fn() }, + { imageId: 'image-2', callLoadImage: jest.fn() }, + ]; + + mockVolume.getImageLoadRequests.mockReturnValue(mockRequests); + + interleaveTopToBottom(defaultParameters); + + expect(mockVolume.getImageLoadRequests).toHaveBeenCalled(); + }); + + it('should handle duplicate volume IDs by not adding them again', () => { + const duplicateVolumeInput = { + volumeId: mockVolumeInput.volumeId, + }; + + interleaveTopToBottom({ + ...defaultParameters, + data: { + ...defaultParameters.data, + volumeInputArray: [mockVolumeInput, duplicateVolumeInput], + }, + }); + + expect(cache.getVolume).toHaveBeenCalledTimes(3); + }); + + it('should clear internal maps after processing', () => { + const firstResult = interleaveTopToBottom(defaultParameters); + const secondResult = interleaveTopToBottom(defaultParameters); + + expect(firstResult).toBeInstanceOf(Map); + expect(secondResult).toBeInstanceOf(Map); + }); + + it('should handle multiple match details with different displaySets', () => { + const mockMultipleMatchDetails = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [{ displaySetInstanceUID: 'display-set-1' }], + }, + ], + [ + 'viewport-2', + { + displaySetsInfo: [{ displaySetInstanceUID: 'display-set-2' }], + }, + ], + ]); + + const result = interleaveTopToBottom({ + ...defaultParameters, + viewportMatchDetails: mockMultipleMatchDetails, + }); + + expect(result).toBeUndefined(); + }); + + it('should handle undefined displaySetOptions', () => { + const mockMatchDetailsWithUndefinedOptions = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [ + { + displaySetInstanceUID: 'display-set-1', + displaySetOptions: undefined, + }, + ], + }, + ], + ]); + + const result = interleaveTopToBottom({ + ...defaultParameters, + viewportMatchDetails: mockMatchDetailsWithUndefinedOptions, + }); + + expect(result).toBeInstanceOf(Map); + }); + + it('should handle displaySetOptions without options property', () => { + const mockMatchDetailsWithoutOptionsProperty = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [ + { + displaySetInstanceUID: 'display-set-1', + displaySetOptions: { someOtherProperty: 'value' }, + }, + ], + }, + ], + ]); + + const result = interleaveTopToBottom({ + ...defaultParameters, + viewportMatchDetails: mockMatchDetailsWithoutOptionsProperty, + }); + + expect(result).toBeInstanceOf(Map); + }); + + it('should handle empty match details', () => { + const emptyMatchDetails = new Map(); + + const result = interleaveTopToBottom({ + ...defaultParameters, + viewportMatchDetails: emptyMatchDetails, + }); + + expect(result).toBeUndefined(); + }); + + it('should handle empty volumeInputArray', () => { + const result = interleaveTopToBottom({ + ...defaultParameters, + data: { + ...defaultParameters.data, + volumeInputArray: [], + }, + }); + + expect(result).toBeUndefined(); + }); + + it('should handle interleavedRequests processing correctly', () => { + const mockRequest1 = { imageId: 'image-1', callLoadImage: jest.fn() }; + const mockRequest2 = { imageId: 'image-2', callLoadImage: jest.fn() }; + + mockVolume.getImageLoadRequests.mockReturnValue([mockRequest1, mockRequest2]); + (compact as jest.Mock).mockReturnValue([mockRequest1, mockRequest2]); + (flatten as jest.Mock).mockReturnValue([mockRequest1, mockRequest2]); + + interleaveTopToBottom(defaultParameters); + + expect(imageLoadPoolManager.addRequest).toHaveBeenCalledTimes(2); + }); + + it('should return copy of viewport volume input array map', () => { + const result = interleaveTopToBottom(defaultParameters); + + expect(result.has(defaultParameters.data.viewportId)).toBe(true); + expect(result.get(defaultParameters.data.viewportId)).toEqual( + defaultParameters.data.volumeInputArray + ); + }); + + it('should process different viewport IDs separately', () => { + const firstResult = interleaveTopToBottom({ + ...defaultParameters, + data: { + viewportId: 'viewport-1', + volumeInputArray: [mockVolumeInput], + }, + }); + + const secondResult = interleaveTopToBottom({ + ...defaultParameters, + data: { + viewportId: 'viewport-2', + volumeInputArray: [mockVolumeInput], + }, + }); + + expect(firstResult.has('viewport-1')).toBe(true); + expect(secondResult.has('viewport-2')).toBe(true); + }); + + it('should handle requests with missing properties gracefully', () => { + const incompleteRequest = { + imageId: 'test-image-id', + callLoadImage: jest.fn(), + }; + + mockVolume.getImageLoadRequests.mockReturnValue([incompleteRequest]); + (compact as jest.Mock).mockReturnValue([incompleteRequest]); + (flatten as jest.Mock).mockReturnValue([incompleteRequest]); + + interleaveTopToBottom(defaultParameters); + + expect(imageLoadPoolManager.addRequest).toHaveBeenCalledWith( + expect.any(Function), + Enums.RequestType.Prefetch, + undefined, + 0 + ); + }); + + it('should handle multiple volumes with same requests', () => { + const mockVolume2 = { + metadata: { SeriesInstanceUID: 'test-series-uid-2' }, + getImageLoadRequests: jest.fn().mockReturnValue([mockImageLoadRequest]), + }; + + const mockVolumeInput2 = { volumeId: 'test-volume-id-2' }; + + (cache.getVolume as jest.Mock) + .mockReturnValueOnce(mockVolume) + .mockReturnValueOnce(mockVolume2) + .mockReturnValueOnce(mockVolume) + .mockReturnValueOnce(mockVolume2); + + const mockMatchDetailsWithBothVolumes = new Map([ + [ + 'viewport-1', + { + displaySetsInfo: [ + { displaySetInstanceUID: 'test-volume-id' }, + { displaySetInstanceUID: 'test-volume-id-2' }, + ], + }, + ], + ]); + + (compact as jest.Mock).mockReturnValue([mockImageLoadRequest]); + (flatten as jest.Mock).mockReturnValue([mockImageLoadRequest]); + + interleaveTopToBottom({ + ...defaultParameters, + data: { + ...defaultParameters.data, + volumeInputArray: [mockVolumeInput, mockVolumeInput2], + }, + viewportMatchDetails: mockMatchDetailsWithBothVolumes, + }); + + expect(imageLoadPoolManager.addRequest).toHaveBeenCalledTimes(2); + }); +}); diff --git a/extensions/cornerstone/src/utils/interleaveTopToBottom.ts b/extensions/cornerstone/src/utils/interleaveTopToBottom.ts index 8ef729b0016..419f734a135 100644 --- a/extensions/cornerstone/src/utils/interleaveTopToBottom.ts +++ b/extensions/cornerstone/src/utils/interleaveTopToBottom.ts @@ -39,31 +39,6 @@ export default function interleaveTopToBottom({ } } - const filteredMatchDetails = []; - const displaySetsToLoad = new Set(); - - // Check all viewports that have a displaySet to be loaded. In some cases - // (eg: line chart viewports which is not a Cornerstone viewport) the - // displaySet is created on the client and there are no instances to be - // downloaded. For those viewports the displaySet may have the `skipLoading` - // option set to true otherwise it may block the download of all other - // instances resulting in blank viewports. - Array.from(matchDetails.values()).forEach(curMatchDetails => { - const { displaySetsInfo } = curMatchDetails; - let numDisplaySetsToLoad = 0; - - displaySetsInfo.forEach(({ displaySetInstanceUID, displaySetOptions }) => { - if (!displaySetOptions?.options?.skipLoading) { - numDisplaySetsToLoad++; - displaySetsToLoad.add(displaySetInstanceUID); - } - }); - - if (numDisplaySetsToLoad) { - filteredMatchDetails.push(curMatchDetails); - } - }); - /** * The following is checking if all the viewports that were matched in the HP has been * successfully created their cornerstone viewport or not. Todo: This can be @@ -108,7 +83,7 @@ export default function interleaveTopToBottom({ // the imageIds and save them in AllRequests for later use const AllRequests = []; volumes.forEach(volume => { - const requests = volume.getImageLoadRequests(); + const requests = volume.getImageLoadRequests?.() ?? []; if (!requests?.[0]?.imageId) { return; diff --git a/extensions/cornerstone/src/utils/isAnyDisplaySetCommon.ts b/extensions/cornerstone/src/utils/isAnyDisplaySetCommon.ts new file mode 100644 index 00000000000..b2875587914 --- /dev/null +++ b/extensions/cornerstone/src/utils/isAnyDisplaySetCommon.ts @@ -0,0 +1,18 @@ +/** + * Checks whether two viewports share at least one common display set. + * + * This method checks to see if the source and target share a display set. + * It performs an O(n * m) comparison between the display sets of each viewport. + * Since each viewport typically contains only a small number of display sets (≤ 5), + * the computational cost is negligible. + * + * @param sourceDisplaySetUIDs - Array of displaySetInstanceUID from the source viewport. + * @param targetDisplaySetUIDs - Array of displaySetInstanceUID from the target viewport. + * @returns true if at least one display set is common; false otherwise. + */ +export function isAnyDisplaySetCommon( + sourceDisplaySetUIDs: string[], + targetDisplaySetUIDs: string[] +): boolean { + return sourceDisplaySetUIDs.some(uid => targetDisplaySetUIDs.includes(uid)); +} diff --git a/extensions/cornerstone/src/utils/isMeasurementWithinViewport.test.ts b/extensions/cornerstone/src/utils/isMeasurementWithinViewport.test.ts new file mode 100644 index 00000000000..4f998d812f4 --- /dev/null +++ b/extensions/cornerstone/src/utils/isMeasurementWithinViewport.test.ts @@ -0,0 +1,340 @@ +import { isMeasurementWithinViewport } from './isMeasurementWithinViewport'; +import { getCenterExtent } from './getCenterExtent'; + +jest.mock('./getCenterExtent', () => ({ + getCenterExtent: jest.fn(), +})); + +describe('isMeasurementWithinViewport', () => { + const mockViewport = { + getCamera: jest.fn(), + }; + + const mockMeasurement = { + points: [ + [0, 0, 0], + [10, 10, 10], + ], + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return true when measurement is within viewport extent', () => { + const mockCamera = { + focalPoint: [5, 5, 5], + parallelScale: 15, + }; + + const mockExtent = { + extent: { + min: [0, 0, 0], + max: [10, 10, 10], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(mockViewport.getCamera).toHaveBeenCalled(); + expect(getCenterExtent).toHaveBeenCalledWith(mockMeasurement); + expect(result).toBe(true); + }); + + it('should return false when measurement min point is outside viewport extent', () => { + const mockCamera = { + focalPoint: [5, 5, 5], + parallelScale: 4, + }; + + const mockExtent = { + extent: { + min: [0, 0, 0], + max: [10, 10, 10], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(false); + }); + + it('should return false when measurement max point is outside viewport extent', () => { + const mockCamera = { + focalPoint: [5, 5, 5], + parallelScale: 4, + }; + + const mockExtent = { + extent: { + min: [1, 1, 1], + max: [15, 15, 15], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(false); + }); + + it('should return false when min point exceeds parallelScale in first dimension', () => { + const mockCamera = { + focalPoint: [10, 5, 5], + parallelScale: 5, + }; + + const mockExtent = { + extent: { + min: [0, 0, 0], + max: [8, 8, 8], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(false); + }); + + it('should return false when max point exceeds parallelScale in second dimension', () => { + const mockCamera = { + focalPoint: [5, 5, 5], + parallelScale: 5, + }; + + const mockExtent = { + extent: { + min: [3, 3, 3], + max: [7, 12, 7], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(false); + }); + + it('should return false when max point exceeds parallelScale in third dimension', () => { + const mockCamera = { + focalPoint: [5, 5, 5], + parallelScale: 5, + }; + + const mockExtent = { + extent: { + min: [3, 3, 3], + max: [7, 7, 12], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(false); + }); + + it('should return true when measurement extent exactly matches parallelScale', () => { + const mockCamera = { + focalPoint: [5, 5, 5], + parallelScale: 5, + }; + + const mockExtent = { + extent: { + min: [0, 0, 0], + max: [10, 10, 10], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(true); + }); + + it('should handle negative coordinates correctly', () => { + const mockCamera = { + focalPoint: [-5, -5, -5], + parallelScale: 10, + }; + + const mockExtent = { + extent: { + min: [-10, -10, -10], + max: [0, 0, 0], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(true); + }); + + it('should handle zero parallelScale', () => { + const mockCamera = { + focalPoint: [0, 0, 0], + parallelScale: 0, + }; + + const mockExtent = { + extent: { + min: [0, 0, 0], + max: [0, 0, 0], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(true); + }); + + it('should handle zero parallelScale with non-zero extent', () => { + const mockCamera = { + focalPoint: [0, 0, 0], + parallelScale: 0, + }; + + const mockExtent = { + extent: { + min: [0, 0, 0], + max: [1, 1, 1], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(false); + }); + + it('should handle large parallelScale values', () => { + const mockCamera = { + focalPoint: [0, 0, 0], + parallelScale: 1000000, + }; + + const mockExtent = { + extent: { + min: [-500, -500, -500], + max: [500, 500, 500], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(true); + }); + + it('should handle floating point coordinates', () => { + const mockCamera = { + focalPoint: [1.5, 2.7, 3.1], + parallelScale: 5.5, + }; + + const mockExtent = { + extent: { + min: [0.1, 0.2, 0.3], + max: [2.9, 5.2, 5.9], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(true); + }); + + it('should fail when min point distance equals parallelScale plus epsilon', () => { + const mockCamera = { + focalPoint: [5, 5, 5], + parallelScale: 5, + }; + + const mockExtent = { + extent: { + min: [-0.1, 0, 0], + max: [10, 10, 10], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(false); + }); + + it('should pass when extent is within parallelScale boundary', () => { + const mockCamera = { + focalPoint: [5, 5, 5], + parallelScale: 6, + }; + + const mockExtent = { + extent: { + min: [-1, -1, -1], + max: [11, 11, 11], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(true); + }); + + it('should handle asymmetric extent around focal point', () => { + const mockCamera = { + focalPoint: [10, 10, 10], + parallelScale: 8, + }; + + const mockExtent = { + extent: { + min: [5, 5, 5], + max: [18, 18, 18], + }, + }; + + mockViewport.getCamera.mockReturnValue(mockCamera); + (getCenterExtent as jest.Mock).mockReturnValue(mockExtent); + + const result = isMeasurementWithinViewport(mockViewport, mockMeasurement); + + expect(result).toBe(true); + }); +}); diff --git a/extensions/cornerstone/src/utils/isMeasurementWithinViewport.ts b/extensions/cornerstone/src/utils/isMeasurementWithinViewport.ts new file mode 100644 index 00000000000..dc18aec4d5d --- /dev/null +++ b/extensions/cornerstone/src/utils/isMeasurementWithinViewport.ts @@ -0,0 +1,33 @@ +import { getCenterExtent } from './getCenterExtent'; + +/** + * Determines if a measurement is within the current viewport extent. + * Uses the measurement's bounding box extent to efficiently check if the entire + * measurement fits within the viewport's visible area. + * + * @param {Object} viewport - The viewport object containing camera information + * @param {Object} measurement - The measurement object containing points to check + * @returns {boolean} True if the measurement extent is within the viewport, false otherwise + */ +export const isMeasurementWithinViewport = (viewport, measurement) => { + const camera = viewport.getCamera(); + const { focalPoint, parallelScale } = camera; + + // Get the measurement's bounding box extent + const { extent } = getCenterExtent(measurement); + const { min, max } = extent; + + // Check if the entire bounding box fits within the viewport extent + // We need to check both the min and max corners of the bounding box + for (let i = 0; i < 3; i++) { + const minDistance = Math.abs(min[i] - focalPoint[i]); + const maxDistance = Math.abs(max[i] - focalPoint[i]); + + // If either the min or max point is outside the viewport extent, return false + if (minDistance > parallelScale || maxDistance > parallelScale) { + return false; + } + } + + return true; // The entire measurement extent is within the viewport +}; diff --git a/extensions/cornerstone/src/utils/isReferenceViewable.test.ts b/extensions/cornerstone/src/utils/isReferenceViewable.test.ts new file mode 100644 index 00000000000..6e9d0a10149 --- /dev/null +++ b/extensions/cornerstone/src/utils/isReferenceViewable.test.ts @@ -0,0 +1,581 @@ +import { vec3 } from 'gl-matrix'; +import { Enums } from '@cornerstonejs/core'; +import getClosestOrientationFromIOP, { isReferenceViewable } from './isReferenceViewable'; + +jest.mock('gl-matrix', () => ({ + vec3: { + fromValues: jest.fn(), + cross: jest.fn(), + dot: jest.fn(), + create: jest.fn(), + }, +})); + +jest.mock('@cornerstonejs/core', () => ({ + Enums: { + OrientationAxis: { + AXIAL: 'axial', + CORONAL: 'coronal', + SAGITTAL: 'sagittal', + }, + }, +})); + +describe('isReferenceViewable', () => { + const mockCornerstoneViewportService = { + getCornerstoneViewport: jest.fn(), + }; + + const mockDisplaySetService = { + getDisplaySetByUID: jest.fn(), + }; + + const mockServicesManager = { + services: { + cornerstoneViewportService: mockCornerstoneViewportService, + displaySetService: mockDisplaySetService, + }, + }; + + const mockViewport = { + isReferenceViewable: jest.fn(), + }; + + const mockReference = { + displaySetInstanceUID: 'test-display-set-uid', + referencedImageId: 'test-image-id', + }; + + const defaultParameters = { + servicesManager: mockServicesManager, + viewportId: 'test-viewport-id', + reference: mockReference, + viewportOptions: undefined, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockCornerstoneViewportService.getCornerstoneViewport.mockReturnValue(mockViewport); + mockViewport.isReferenceViewable.mockReturnValue(true); + }); + + it('should return viewport isReferenceViewable result when viewportOptions is undefined', () => { + mockViewport.isReferenceViewable.mockReturnValue(true); + + const result = isReferenceViewable( + defaultParameters.servicesManager, + defaultParameters.viewportId, + defaultParameters.reference + ); + + expect(mockCornerstoneViewportService.getCornerstoneViewport).toHaveBeenCalledWith( + defaultParameters.viewportId + ); + expect(mockViewport.isReferenceViewable).toHaveBeenCalledWith(mockReference, { + withNavigation: true, + asVolume: true, + }); + expect(result).toBe(true); + }); + + it('should return false when viewport isReferenceViewable returns false', () => { + mockViewport.isReferenceViewable.mockReturnValue(false); + + const result = isReferenceViewable( + defaultParameters.servicesManager, + defaultParameters.viewportId, + defaultParameters.reference + ); + + expect(result).toBe(false); + }); + + it('should check imageIds inclusion for stack viewport type', () => { + const mockDisplaySet = { + instances: [{ imageId: 'image-1' }, { imageId: 'test-image-id' }, { imageId: 'image-3' }], + }; + + const viewportOptions = { + viewportType: 'stack', + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const result = isReferenceViewable( + defaultParameters.servicesManager, + defaultParameters.viewportId, + defaultParameters.reference, + viewportOptions + ); + + expect(mockDisplaySetService.getDisplaySetByUID).toHaveBeenCalledWith( + mockReference.displaySetInstanceUID + ); + expect(result).toBe(true); + }); + + it('should return false when imageId is not in stack', () => { + const mockDisplaySet = { + instances: [{ imageId: 'image-1' }, { imageId: 'image-2' }, { imageId: 'image-3' }], + }; + + const viewportOptions = { + viewportType: 'stack', + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const result = isReferenceViewable( + defaultParameters.servicesManager, + defaultParameters.viewportId, + defaultParameters.reference, + viewportOptions + ); + + expect(result).toBe(false); + }); + + it('should check orientation for volume viewport type', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: [1, 0, 0, 0, 1, 0], + }, + ], + }; + + const viewportOptions = { + viewportType: 'volume', + orientation: 'axial', + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const mockRowCosineVec = [1, 0, 0]; + const mockColCosineVec = [0, 1, 0]; + const mockScanAxisNormal = [0, 0, 1]; + const mockAxialVector = [0, 0, 1]; + + (vec3.fromValues as jest.Mock) + .mockReturnValueOnce(mockRowCosineVec) + .mockReturnValueOnce(mockColCosineVec) + .mockReturnValueOnce(mockAxialVector); + + (vec3.create as jest.Mock).mockReturnValue([]); + (vec3.cross as jest.Mock).mockReturnValue(mockScanAxisNormal); + (vec3.dot as jest.Mock).mockReturnValue(1); + + const result = isReferenceViewable( + defaultParameters.servicesManager, + defaultParameters.viewportId, + defaultParameters.reference, + viewportOptions + ); + + expect(result).toBe(true); + }); + + it('should return false when orientation does not match', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: [1, 0, 0, 0, 1, 0], + }, + ], + }; + + const viewportOptions = { + viewportType: 'volume', + orientation: 'coronal', + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const mockRowCosineVec = [1, 0, 0]; + const mockColCosineVec = [0, 1, 0]; + const mockScanAxisNormal = [0, 0, 1]; + const mockAxialVector = [0, 0, 1]; + + (vec3.fromValues as jest.Mock) + .mockReturnValueOnce(mockRowCosineVec) + .mockReturnValueOnce(mockColCosineVec) + .mockReturnValueOnce(mockAxialVector); + + (vec3.create as jest.Mock).mockReturnValue([]); + (vec3.cross as jest.Mock).mockReturnValue(mockScanAxisNormal); + (vec3.dot as jest.Mock).mockReturnValue(1); + + const result = isReferenceViewable( + defaultParameters.servicesManager, + defaultParameters.viewportId, + defaultParameters.reference, + viewportOptions + ); + + expect(result).toBe(false); + }); + + it('should handle empty instances array for stack viewport', () => { + const mockDisplaySet = { + instances: [], + }; + + const viewportOptions = { + viewportType: 'stack', + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const result = isReferenceViewable( + defaultParameters.servicesManager, + defaultParameters.viewportId, + defaultParameters.reference, + viewportOptions + ); + + expect(result).toBe(false); + }); +}); + +describe('getClosestOrientationFromIOP', () => { + const mockDisplaySetService = { + getDisplaySetByUID: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return undefined when ImageOrientationPatient is not length 6', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: [1, 0, 0, 0, 1], + }, + ], + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const result = getClosestOrientationFromIOP(mockDisplaySetService, 'test-display-set-uid'); + + expect(result).toBeUndefined(); + }); + + it('should return undefined when ImageOrientationPatient is undefined', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: undefined, + }, + ], + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const result = getClosestOrientationFromIOP(mockDisplaySetService, 'test-display-set-uid'); + + expect(result).toBeUndefined(); + }); + + it('should return undefined when ImageOrientationPatient is null', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: null, + }, + ], + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const result = getClosestOrientationFromIOP(mockDisplaySetService, 'test-display-set-uid'); + + expect(result).toBeUndefined(); + }); + + it('should return axial orientation for axial-aligned IOP', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: [1, 0, 0, 0, 1, 0], + }, + ], + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const mockRowCosineVec = [1, 0, 0]; + const mockColCosineVec = [0, 1, 0]; + const mockScanAxisNormal = [0, 0, 1]; + const mockAxialVector = [0, 0, 1]; + const mockCoronalVector = [0, 1, 0]; + const mockSagittalVector = [1, 0, 0]; + + (vec3.fromValues as jest.Mock) + .mockReturnValueOnce(mockRowCosineVec) + .mockReturnValueOnce(mockColCosineVec) + .mockReturnValueOnce(mockAxialVector) + .mockReturnValueOnce(mockCoronalVector) + .mockReturnValueOnce(mockSagittalVector); + + (vec3.create as jest.Mock).mockReturnValue([]); + (vec3.cross as jest.Mock).mockReturnValue(mockScanAxisNormal); + (vec3.dot as jest.Mock) + .mockReturnValueOnce(1.0) + .mockReturnValueOnce(0.0) + .mockReturnValueOnce(0.0); + + const result = getClosestOrientationFromIOP(mockDisplaySetService, 'test-display-set-uid'); + + expect(result).toBe(Enums.OrientationAxis.AXIAL); + }); + + it('should return coronal orientation for coronal-aligned IOP', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: [1, 0, 0, 0, 0, -1], + }, + ], + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const mockRowCosineVec = [1, 0, 0]; + const mockColCosineVec = [0, 0, -1]; + const mockScanAxisNormal = [0, 1, 0]; + const mockAxialVector = [0, 0, 1]; + const mockCoronalVector = [0, 1, 0]; + const mockSagittalVector = [1, 0, 0]; + + (vec3.fromValues as jest.Mock) + .mockReturnValueOnce(mockRowCosineVec) + .mockReturnValueOnce(mockColCosineVec) + .mockReturnValueOnce(mockAxialVector) + .mockReturnValueOnce(mockCoronalVector) + .mockReturnValueOnce(mockSagittalVector); + + (vec3.create as jest.Mock).mockReturnValue([]); + (vec3.cross as jest.Mock).mockReturnValue(mockScanAxisNormal); + (vec3.dot as jest.Mock) + .mockReturnValueOnce(0.0) + .mockReturnValueOnce(1.0) + .mockReturnValueOnce(0.0); + + const result = getClosestOrientationFromIOP(mockDisplaySetService, 'test-display-set-uid'); + + expect(result).toBe(Enums.OrientationAxis.CORONAL); + }); + + it('should return sagittal orientation for sagittal-aligned IOP', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: [0, 1, 0, 0, 0, -1], + }, + ], + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const mockRowCosineVec = [0, 1, 0]; + const mockColCosineVec = [0, 0, -1]; + const mockScanAxisNormal = [1, 0, 0]; + const mockAxialVector = [0, 0, 1]; + const mockCoronalVector = [0, 1, 0]; + const mockSagittalVector = [1, 0, 0]; + + (vec3.fromValues as jest.Mock) + .mockReturnValueOnce(mockRowCosineVec) + .mockReturnValueOnce(mockColCosineVec) + .mockReturnValueOnce(mockAxialVector) + .mockReturnValueOnce(mockCoronalVector) + .mockReturnValueOnce(mockSagittalVector); + + (vec3.create as jest.Mock).mockReturnValue([]); + (vec3.cross as jest.Mock).mockReturnValue(mockScanAxisNormal); + (vec3.dot as jest.Mock) + .mockReturnValueOnce(0.0) + .mockReturnValueOnce(0.0) + .mockReturnValueOnce(1.0); + + const result = getClosestOrientationFromIOP(mockDisplaySetService, 'test-display-set-uid'); + + expect(result).toBe(Enums.OrientationAxis.SAGITTAL); + }); + + it('should handle negative dot products and return correct orientation', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: [1, 0, 0, 0, -1, 0], + }, + ], + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const mockRowCosineVec = [1, 0, 0]; + const mockColCosineVec = [0, -1, 0]; + const mockScanAxisNormal = [0, 0, -1]; + const mockAxialVector = [0, 0, 1]; + const mockCoronalVector = [0, 1, 0]; + const mockSagittalVector = [1, 0, 0]; + + (vec3.fromValues as jest.Mock) + .mockReturnValueOnce(mockRowCosineVec) + .mockReturnValueOnce(mockColCosineVec) + .mockReturnValueOnce(mockAxialVector) + .mockReturnValueOnce(mockCoronalVector) + .mockReturnValueOnce(mockSagittalVector); + + (vec3.create as jest.Mock).mockReturnValue([]); + (vec3.cross as jest.Mock).mockReturnValue(mockScanAxisNormal); + (vec3.dot as jest.Mock) + .mockReturnValueOnce(-1.0) + .mockReturnValueOnce(0.0) + .mockReturnValueOnce(0.0); + + const result = getClosestOrientationFromIOP(mockDisplaySetService, 'test-display-set-uid'); + + expect(result).toBe(Enums.OrientationAxis.AXIAL); + }); + + it('should handle oblique orientations and return closest match', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: [0.7071, 0.7071, 0, 0, 0, 1], + }, + ], + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const mockRowCosineVec = [0.7071, 0.7071, 0]; + const mockColCosineVec = [0, 0, 1]; + const mockScanAxisNormal = [0.7071, -0.7071, 0]; + const mockAxialVector = [0, 0, 1]; + const mockCoronalVector = [0, 1, 0]; + const mockSagittalVector = [1, 0, 0]; + + (vec3.fromValues as jest.Mock) + .mockReturnValueOnce(mockRowCosineVec) + .mockReturnValueOnce(mockColCosineVec) + .mockReturnValueOnce(mockAxialVector) + .mockReturnValueOnce(mockCoronalVector) + .mockReturnValueOnce(mockSagittalVector); + + (vec3.create as jest.Mock).mockReturnValue([]); + (vec3.cross as jest.Mock).mockReturnValue(mockScanAxisNormal); + (vec3.dot as jest.Mock) + .mockReturnValueOnce(0.0) + .mockReturnValueOnce(0.7071) + .mockReturnValueOnce(0.7071); + + const result = getClosestOrientationFromIOP(mockDisplaySetService, 'test-display-set-uid'); + + expect(result).toBe(Enums.OrientationAxis.CORONAL); + }); + + it('should handle zero dot products', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: [1, 0, 0, 0, 1, 0], + }, + ], + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const mockRowCosineVec = [1, 0, 0]; + const mockColCosineVec = [0, 1, 0]; + const mockScanAxisNormal = [0, 0, 1]; + const mockAxialVector = [0, 0, 1]; + const mockCoronalVector = [0, 1, 0]; + const mockSagittalVector = [1, 0, 0]; + + (vec3.fromValues as jest.Mock) + .mockReturnValueOnce(mockRowCosineVec) + .mockReturnValueOnce(mockColCosineVec) + .mockReturnValueOnce(mockAxialVector) + .mockReturnValueOnce(mockCoronalVector) + .mockReturnValueOnce(mockSagittalVector); + + (vec3.create as jest.Mock).mockReturnValue([]); + (vec3.cross as jest.Mock).mockReturnValue(mockScanAxisNormal); + (vec3.dot as jest.Mock) + .mockReturnValueOnce(0.0) + .mockReturnValueOnce(0.0) + .mockReturnValueOnce(0.0); + + const result = getClosestOrientationFromIOP(mockDisplaySetService, 'test-display-set-uid'); + + expect(result).toBe(''); + }); + + it('should call vec3 functions with correct parameters', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: [1, 0, 0, 0, 1, 0], + }, + ], + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const mockRowCosineVec = [1, 0, 0]; + const mockColCosineVec = [0, 1, 0]; + const mockCreatedVec = []; + + (vec3.fromValues as jest.Mock) + .mockReturnValueOnce(mockRowCosineVec) + .mockReturnValueOnce(mockColCosineVec); + + (vec3.create as jest.Mock).mockReturnValue(mockCreatedVec); + (vec3.cross as jest.Mock).mockReturnValue([0, 0, 1]); + (vec3.dot as jest.Mock).mockReturnValue(1.0); + + getClosestOrientationFromIOP(mockDisplaySetService, 'test-display-set-uid'); + + expect(vec3.fromValues).toHaveBeenNthCalledWith(1, 1, 0, 0); + expect(vec3.fromValues).toHaveBeenNthCalledWith(2, 0, 1, 0); + expect(vec3.cross).toHaveBeenCalledWith(mockCreatedVec, mockRowCosineVec, mockColCosineVec); + }); + + it('should handle floating point IOP values', () => { + const mockDisplaySet = { + instances: [ + { + ImageOrientationPatient: [0.866, 0.5, 0, -0.5, 0.866, 0], + }, + ], + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + const mockRowCosineVec = [0.866, 0.5, 0]; + const mockColCosineVec = [-0.5, 0.866, 0]; + const mockScanAxisNormal = [0, 0, 1]; + + (vec3.fromValues as jest.Mock) + .mockReturnValueOnce(mockRowCosineVec) + .mockReturnValueOnce(mockColCosineVec); + + (vec3.create as jest.Mock).mockReturnValue([]); + (vec3.cross as jest.Mock).mockReturnValue(mockScanAxisNormal); + (vec3.dot as jest.Mock) + .mockReturnValueOnce(1.0) + .mockReturnValueOnce(0.0) + .mockReturnValueOnce(0.0); + + const result = getClosestOrientationFromIOP(mockDisplaySetService, 'test-display-set-uid'); + + expect(vec3.fromValues).toHaveBeenNthCalledWith(1, 0.866, 0.5, 0); + expect(vec3.fromValues).toHaveBeenNthCalledWith(2, -0.5, 0.866, 0); + expect(result).toBe(Enums.OrientationAxis.AXIAL); + }); +}); diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/Probe.ts b/extensions/cornerstone/src/utils/measurementServiceMappings/Probe.ts index 8ad3e70e6d5..f480370cf60 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/Probe.ts +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/Probe.ts @@ -107,8 +107,8 @@ function getMappedAnnotations(annotation, displaySetService) { const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0]; const { SeriesNumber } = displaySet; - const { value } = targetStats; - const unit = 'HU'; + const { value, modalityUnit } = targetStats; + const unit = modalityUnit || ''; annotations.push({ SeriesInstanceUID, diff --git a/extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js b/extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js index 7f96f049029..99713c3a466 100644 --- a/extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js +++ b/extensions/cornerstone/src/utils/measurementServiceMappings/constants/supportedTools.js @@ -13,7 +13,6 @@ const supportedTools = [ 'LivewireContour', 'UltrasoundDirectionalTool', 'UltrasoundPleuraBLineTool', - 'SCOORD3DPoint', 'SegmentBidirectional', ]; diff --git a/extensions/cornerstone/src/utils/nthLoader.test.ts b/extensions/cornerstone/src/utils/nthLoader.test.ts new file mode 100644 index 00000000000..f1d0dcc37ff --- /dev/null +++ b/extensions/cornerstone/src/utils/nthLoader.test.ts @@ -0,0 +1,317 @@ +import { cache, imageLoadPoolManager, Enums as csEnums } from '@cornerstonejs/core'; +import interleaveNthLoader from './nthLoader'; +import getNthFrames from './getNthFrames'; +import interleave from './interleave'; + +jest.mock('@cornerstonejs/core', () => ({ + ...jest.requireActual('@cornerstonejs/core'), + cache: { + getVolume: jest.fn(), + }, + imageLoadPoolManager: { + addRequest: jest.fn(), + }, +})); + +jest.mock('./getNthFrames', () => jest.fn()); +jest.mock('./interleave', () => jest.fn()); + +describe('interleaveNthLoader', () => { + const mockVolumeInput = { + volumeId: 'test-volume-id', + }; + + const mockVolume = { + metadata: { + SeriesInstanceUID: 'test-series-uid', + }, + getImageLoadRequests: jest.fn(), + }; + + const mockImageLoadRequest = { + imageId: 'test-image-id', + callLoadImage: jest.fn(), + additionalDetails: 'test-details', + imageIdIndex: 0, + options: { test: 'option' }, + }; + + const defaultParameters = { + data: { + viewportId: 'test-viewport-id', + volumeInputArray: [mockVolumeInput], + }, + displaySetsMatchDetails: {}, + }; + + beforeEach(() => { + jest.clearAllMocks(); + (cache.getVolume as jest.Mock).mockReturnValue(mockVolume); + mockVolume.getImageLoadRequests.mockReturnValue([mockImageLoadRequest]); + (getNthFrames as jest.Mock).mockImplementation(input => input); + (interleave as jest.Mock).mockImplementation(input => input.flat()); + }); + + it('should store viewport and volume input array mapping', () => { + interleaveNthLoader(defaultParameters); + + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId); + }); + + it('should return early when volume is not found', () => { + (cache.getVolume as jest.Mock).mockReturnValue(null); + const consoleSpy = jest.spyOn(console, 'log').mockImplementation(); + + const result = interleaveNthLoader(defaultParameters); + + expect(consoleSpy).toHaveBeenCalledWith("interleaveNthLoader::No volume, can't load it"); + expect(result).toBeUndefined(); + expect(mockVolume.getImageLoadRequests).not.toHaveBeenCalled(); + + consoleSpy.mockRestore(); + }); + + it('should process volume input array and create image load requests', () => { + const result = interleaveNthLoader(defaultParameters); + + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId); + expect(mockVolume.getImageLoadRequests).toHaveBeenCalled(); + expect(getNthFrames).toHaveBeenCalledWith([mockImageLoadRequest]); + expect(interleave).toHaveBeenCalledWith([[mockImageLoadRequest]]); + expect(result).toBeInstanceOf(Map); + }); + + it('should add requests to imageLoadPoolManager with correct parameters', () => { + interleaveNthLoader(defaultParameters); + + expect(imageLoadPoolManager.addRequest).toHaveBeenCalledWith( + expect.any(Function), + csEnums.RequestType.Prefetch, + mockImageLoadRequest.additionalDetails, + 0 + ); + }); + + it('should handle multiple volume inputs', () => { + const mockVolumeInput2 = { + volumeId: 'test-volume-id-2', + }; + + const mockVolume2 = { + metadata: { + SeriesInstanceUID: 'test-series-uid-2', + }, + getImageLoadRequests: jest.fn().mockReturnValue([mockImageLoadRequest]), + }; + + (cache.getVolume as jest.Mock) + .mockReturnValueOnce(mockVolume) + .mockReturnValueOnce(mockVolume2) + .mockReturnValueOnce(mockVolume) + .mockReturnValueOnce(mockVolume2); + + interleaveNthLoader({ + ...defaultParameters, + data: { + ...defaultParameters.data, + volumeInputArray: [mockVolumeInput, mockVolumeInput2], + }, + }); + + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId); + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput2.volumeId); + expect(mockVolume.getImageLoadRequests).toHaveBeenCalled(); + expect(mockVolume2.getImageLoadRequests).toHaveBeenCalled(); + }); + + it('should handle volumes without getImageLoadRequests method', () => { + const mockVolumeWithoutMethod = { + metadata: { + SeriesInstanceUID: 'test-series-uid', + }, + }; + + (cache.getVolume as jest.Mock) + .mockReturnValueOnce(mockVolumeWithoutMethod) + .mockReturnValueOnce(mockVolumeWithoutMethod); + + interleaveNthLoader(defaultParameters); + + expect(getNthFrames).not.toHaveBeenCalled(); + expect(interleave).toHaveBeenCalledWith([]); + }); + + it('should handle volumes with null getImageLoadRequests return', () => { + mockVolume.getImageLoadRequests.mockReturnValue(null); + + interleaveNthLoader(defaultParameters); + + expect(getNthFrames).not.toHaveBeenCalled(); + expect(interleave).toHaveBeenCalledWith([]); + }); + + it('should filter out requests without imageId in first request', () => { + const requestWithoutImageId = { + callLoadImage: jest.fn(), + additionalDetails: 'test-details', + }; + + mockVolume.getImageLoadRequests + .mockReturnValueOnce([requestWithoutImageId, mockImageLoadRequest]) + .mockReturnValueOnce([mockImageLoadRequest]); + + interleaveNthLoader({ + ...defaultParameters, + data: { + ...defaultParameters.data, + volumeInputArray: [mockVolumeInput, { volumeId: 'test-volume-id-2' }], + }, + }); + + expect(getNthFrames).toHaveBeenCalledWith([mockImageLoadRequest]); + }); + + it('should handle empty image load requests', () => { + mockVolume.getImageLoadRequests.mockReturnValue([]); + + interleaveNthLoader(defaultParameters); + + expect(getNthFrames).not.toHaveBeenCalled(); + expect(interleave).toHaveBeenCalledWith([]); + }); + + it('should bind callLoadImage with correct parameters', () => { + interleaveNthLoader(defaultParameters); + + const addRequestCall = (imageLoadPoolManager.addRequest as jest.Mock).mock.calls[0]; + const boundFunction = addRequestCall[0]; + + boundFunction(); + + expect(mockImageLoadRequest.callLoadImage).toHaveBeenCalledWith( + mockImageLoadRequest.imageId, + mockImageLoadRequest.imageIdIndex, + mockImageLoadRequest.options + ); + }); + + it('should handle duplicate volume IDs by not adding them again', () => { + const duplicateVolumeInput = { + volumeId: mockVolumeInput.volumeId, + }; + + interleaveNthLoader({ + ...defaultParameters, + data: { + ...defaultParameters.data, + volumeInputArray: [mockVolumeInput, duplicateVolumeInput], + }, + }); + + expect(cache.getVolume).toHaveBeenCalledTimes(3); + expect(cache.getVolume).toHaveBeenCalledWith(mockVolumeInput.volumeId); + }); + + it('should clear internal maps after processing', () => { + const firstResult = interleaveNthLoader(defaultParameters); + const secondResult = interleaveNthLoader(defaultParameters); + + expect(firstResult).toBeInstanceOf(Map); + expect(secondResult).toBeInstanceOf(Map); + expect(firstResult.get(defaultParameters.data.viewportId)).toEqual( + defaultParameters.data.volumeInputArray + ); + }); + + it('should handle multiple image load requests', () => { + const mockImageLoadRequest2 = { + imageId: 'test-image-id-2', + callLoadImage: jest.fn(), + additionalDetails: 'test-details-2', + imageIdIndex: 1, + options: { test: 'option2' }, + }; + + mockVolume.getImageLoadRequests.mockReturnValue([mockImageLoadRequest, mockImageLoadRequest2]); + + (interleave as jest.Mock).mockReturnValue([mockImageLoadRequest, mockImageLoadRequest2]); + + interleaveNthLoader(defaultParameters); + + expect(imageLoadPoolManager.addRequest).toHaveBeenCalledTimes(2); + expect(imageLoadPoolManager.addRequest).toHaveBeenNthCalledWith( + 1, + expect.any(Function), + csEnums.RequestType.Prefetch, + mockImageLoadRequest.additionalDetails, + 0 + ); + expect(imageLoadPoolManager.addRequest).toHaveBeenNthCalledWith( + 2, + expect.any(Function), + csEnums.RequestType.Prefetch, + mockImageLoadRequest2.additionalDetails, + 0 + ); + }); + + it('should handle volume with undefined metadata', () => { + const mockVolumeWithoutMetadata = { + getImageLoadRequests: jest.fn().mockReturnValue([]), + }; + + (cache.getVolume as jest.Mock).mockReturnValueOnce(mockVolumeWithoutMetadata); + + expect(() => interleaveNthLoader(defaultParameters)).toThrow(); + }); + + it('should return copy of viewport volume input array map', () => { + const result = interleaveNthLoader(defaultParameters); + + expect(result.has(defaultParameters.data.viewportId)).toBe(true); + expect(result.get(defaultParameters.data.viewportId)).toEqual( + defaultParameters.data.volumeInputArray + ); + }); + + it('should handle requests with missing properties gracefully', () => { + const incompleteRequest = { + imageId: 'test-image-id', + callLoadImage: jest.fn(), + }; + + mockVolume.getImageLoadRequests.mockReturnValue([incompleteRequest]); + (interleave as jest.Mock).mockReturnValue([incompleteRequest]); + + interleaveNthLoader(defaultParameters); + + expect(imageLoadPoolManager.addRequest).toHaveBeenCalledWith( + expect.any(Function), + csEnums.RequestType.Prefetch, + undefined, + 0 + ); + }); + + it('should process different viewport IDs separately', () => { + const firstResult = interleaveNthLoader({ + ...defaultParameters, + data: { + viewportId: 'viewport-1', + volumeInputArray: [mockVolumeInput], + }, + }); + + const secondResult = interleaveNthLoader({ + ...defaultParameters, + data: { + viewportId: 'viewport-2', + volumeInputArray: [mockVolumeInput], + }, + }); + + expect(firstResult.has('viewport-1')).toBe(true); + expect(secondResult.has('viewport-2')).toBe(true); + expect(firstResult.has('viewport-2')).toBe(false); + }); +}); diff --git a/extensions/cornerstone/src/utils/promptHydrationDialog.test.ts b/extensions/cornerstone/src/utils/promptHydrationDialog.test.ts new file mode 100644 index 00000000000..73daa2f8e5e --- /dev/null +++ b/extensions/cornerstone/src/utils/promptHydrationDialog.test.ts @@ -0,0 +1,490 @@ +import promptHydrationDialog, { HydrationType } from './promptHydrationDialog'; + +describe('promptHydrationDialog', () => { + const mockUIViewportDialogService = { + show: jest.fn(), + hide: jest.fn(), + }; + + const mockCustomizationService = { + getCustomization: jest.fn(), + }; + + const mockExtensionManager = { + _appConfig: { + measurementTrackingMode: 'standard', + disableConfirmationPrompts: false, + }, + }; + + const mockServicesManager = { + services: { + uiViewportDialogService: mockUIViewportDialogService, + customizationService: mockCustomizationService, + }, + _extensionManager: mockExtensionManager, + }; + + const mockDisplaySet = { + displaySetInstanceUID: 'test-display-set-uid', + SeriesInstanceUID: 'test-series-uid', + }; + + const mockHydrateCallback = jest.fn(); + const mockPreHydrateCallback = jest.fn(); + + const defaultParameters = { + servicesManager: mockServicesManager as unknown as AppTypes.ServicesManager, + viewportId: 'test-viewport-id', + displaySet: mockDisplaySet as unknown as AppTypes.DisplaySet, + preHydrateCallbacks: [mockPreHydrateCallback], + hydrateCallback: mockHydrateCallback, + type: HydrationType.SEG, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockCustomizationService.getCustomization.mockReturnValue('Test message'); + mockHydrateCallback.mockResolvedValue(true); + jest.spyOn(window, 'setTimeout').mockImplementation((callback: () => void) => { + callback(); + return 0 as unknown as NodeJS.Timeout; + }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should return HYDRATE response when disableConfirmationPrompts is true for non-SR types', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = true; + + const result = await promptHydrationDialog(defaultParameters); + + expect(mockUIViewportDialogService.show).not.toHaveBeenCalled(); + expect(mockPreHydrateCallback).toHaveBeenCalled(); + expect(mockHydrateCallback).toHaveBeenCalledWith({ + segDisplaySet: mockDisplaySet, + viewportId: defaultParameters.viewportId, + }); + expect(result).toBe(true); + }); + + it('should show dialog when disableConfirmationPrompts is false for non-SR types', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = false; + + const promise = promptHydrationDialog(defaultParameters); + + expect(mockUIViewportDialogService.show).toHaveBeenCalledWith({ + id: 'promptHydrateSEG', + viewportId: defaultParameters.viewportId, + type: 'info', + message: 'Test message', + actions: [ + { + id: 'no-hydrate', + type: 'secondary', + text: 'No', + value: 0, + }, + { + id: 'yes-hydrate', + type: 'primary', + text: 'Yes', + value: 5, + }, + ], + onSubmit: expect.any(Function), + onOutsideClick: expect.any(Function), + onKeyPress: expect.any(Function), + }); + + const onSubmit = mockUIViewportDialogService.show.mock.calls[0][0].onSubmit; + onSubmit(5); + + const result = await promise; + expect(result).toBe(true); + }); + + it('should handle CANCEL response', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = false; + + const promise = promptHydrationDialog(defaultParameters); + + const onSubmit = mockUIViewportDialogService.show.mock.calls[0][0].onSubmit; + onSubmit(0); + + const result = await promise; + expect(result).toBe(false); + expect(mockHydrateCallback).not.toHaveBeenCalled(); + }); + + it('should handle onOutsideClick', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = false; + + const promise = promptHydrationDialog(defaultParameters); + + const onOutsideClick = mockUIViewportDialogService.show.mock.calls[0][0].onOutsideClick; + onOutsideClick(); + + const result = await promise; + expect(result).toBe(false); + expect(mockUIViewportDialogService.hide).toHaveBeenCalled(); + }); + + it('should handle onKeyPress Enter', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = false; + + const promise = promptHydrationDialog(defaultParameters); + + const onKeyPress = mockUIViewportDialogService.show.mock.calls[0][0].onKeyPress; + onKeyPress({ key: 'Enter' }); + + const result = await promise; + expect(result).toBe(true); + expect(mockUIViewportDialogService.hide).toHaveBeenCalled(); + }); + + it('should handle onKeyPress non-Enter key', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = false; + + const promise = promptHydrationDialog(defaultParameters); + + const onKeyPress = mockUIViewportDialogService.show.mock.calls[0][0].onKeyPress; + onKeyPress({ key: 'Escape' }); + + expect(mockUIViewportDialogService.hide).not.toHaveBeenCalled(); + + const onOutsideClick = mockUIViewportDialogService.show.mock.calls[0][0].onOutsideClick; + onOutsideClick(); + + const result = await promise; + expect(result).toBe(false); + }); + + it('should handle SEG type hydration with setTimeout', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = true; + + await promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.SEG, + }); + + expect(window.setTimeout).toHaveBeenCalledWith(expect.any(Function), 0); + expect(mockHydrateCallback).toHaveBeenCalledWith({ + segDisplaySet: mockDisplaySet, + viewportId: defaultParameters.viewportId, + }); + }); + + it('should handle RTSTRUCT type hydration', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = true; + + const result = await promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.RTSTRUCT, + }); + + expect(mockHydrateCallback).toHaveBeenCalledWith({ + rtDisplaySet: mockDisplaySet, + viewportId: defaultParameters.viewportId, + servicesManager: mockServicesManager, + }); + expect(result).toBe(true); + }); + + it('should handle SR type hydration when standardMode is true', async () => { + mockExtensionManager._appConfig.measurementTrackingMode = 'standard'; + const mockHydrationResult = { + StudyInstanceUID: 'test-study-uid', + SeriesInstanceUIDs: ['series-1', 'series-2'], + }; + mockHydrateCallback.mockResolvedValue(mockHydrationResult); + + const promise = promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.SR, + }); + + const onSubmit = mockUIViewportDialogService.show.mock.calls[0][0].onSubmit; + onSubmit(5); + + const result = await promise; + + expect(result).toEqual({ + userResponse: 5, + displaySetInstanceUID: mockDisplaySet.displaySetInstanceUID, + srSeriesInstanceUID: mockDisplaySet.SeriesInstanceUID, + viewportId: defaultParameters.viewportId, + StudyInstanceUID: mockHydrationResult.StudyInstanceUID, + SeriesInstanceUIDs: mockHydrationResult.SeriesInstanceUIDs, + }); + }); + + it('should handle SR type hydration when standardMode is false', async () => { + mockExtensionManager._appConfig.measurementTrackingMode = 'other'; + + const result = await promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.SR, + }); + + expect(mockUIViewportDialogService.show).not.toHaveBeenCalled(); + expect(result).toEqual({ + userResponse: 5, + displaySetInstanceUID: mockDisplaySet.displaySetInstanceUID, + srSeriesInstanceUID: mockDisplaySet.SeriesInstanceUID, + viewportId: defaultParameters.viewportId, + StudyInstanceUID: undefined, + SeriesInstanceUIDs: undefined, + }); + }); + + it('should handle SR type with CANCEL response', async () => { + mockExtensionManager._appConfig.measurementTrackingMode = 'standard'; + + const promise = promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.SR, + }); + + const onSubmit = mockUIViewportDialogService.show.mock.calls[0][0].onSubmit; + onSubmit(0); + + const result = await promise; + + expect(result).toEqual({ + userResponse: 0, + displaySetInstanceUID: mockDisplaySet.displaySetInstanceUID, + srSeriesInstanceUID: mockDisplaySet.SeriesInstanceUID, + viewportId: defaultParameters.viewportId, + }); + }); + + it('should handle undefined preHydrateCallbacks', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = true; + + await promptHydrationDialog({ + ...defaultParameters, + preHydrateCallbacks: undefined, + }); + + expect(mockHydrateCallback).toHaveBeenCalled(); + }); + + it('should handle empty preHydrateCallbacks array', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = true; + + await promptHydrationDialog({ + ...defaultParameters, + preHydrateCallbacks: [], + }); + + expect(mockHydrateCallback).toHaveBeenCalled(); + }); + + it('should execute multiple preHydrateCallbacks', async () => { + const mockPreHydrateCallback2 = jest.fn(); + mockExtensionManager._appConfig.disableConfirmationPrompts = true; + + await promptHydrationDialog({ + ...defaultParameters, + preHydrateCallbacks: [mockPreHydrateCallback, mockPreHydrateCallback2], + }); + + expect(mockPreHydrateCallback).toHaveBeenCalled(); + expect(mockPreHydrateCallback2).toHaveBeenCalled(); + }); + + it('should get correct customization message key for SEG type', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = false; + + const promise = promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.SEG, + }); + + expect(mockCustomizationService.getCustomization).toHaveBeenCalledWith( + 'viewportNotification.hydrateSEGMessage' + ); + + const onOutsideClick = mockUIViewportDialogService.show.mock.calls[0][0].onOutsideClick; + onOutsideClick(); + await promise; + }); + + it('should get correct customization message key for RTSTRUCT type', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = false; + + const promise = promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.RTSTRUCT, + }); + + expect(mockCustomizationService.getCustomization).toHaveBeenCalledWith( + 'viewportNotification.hydrateRTMessage' + ); + + const onOutsideClick = mockUIViewportDialogService.show.mock.calls[0][0].onOutsideClick; + onOutsideClick(); + await promise; + }); + + it('should get correct customization message key for SR type', async () => { + mockExtensionManager._appConfig.measurementTrackingMode = 'standard'; + + const promise = promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.SR, + }); + + expect(mockCustomizationService.getCustomization).toHaveBeenCalledWith( + 'viewportNotification.hydrateSRMessage' + ); + + const onOutsideClick = mockUIViewportDialogService.show.mock.calls[0][0].onOutsideClick; + onOutsideClick(); + await promise; + }); + + it('should get default customization message key for unknown type', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = false; + + const promise = promptHydrationDialog({ + ...defaultParameters, + type: 'UNKNOWN_TYPE', + }); + + expect(mockCustomizationService.getCustomization).toHaveBeenCalledWith( + 'viewportNotification.hydrateMessage' + ); + + const onOutsideClick = mockUIViewportDialogService.show.mock.calls[0][0].onOutsideClick; + onOutsideClick(); + await promise; + }); + + it('should get correct dialog id for SEG type', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = false; + + const promise = promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.SEG, + }); + + expect(mockUIViewportDialogService.show).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'promptHydrateSEG', + }) + ); + + const onOutsideClick = mockUIViewportDialogService.show.mock.calls[0][0].onOutsideClick; + onOutsideClick(); + await promise; + }); + + it('should get correct dialog id for SR type', async () => { + mockExtensionManager._appConfig.measurementTrackingMode = 'standard'; + + const promise = promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.SR, + }); + + expect(mockUIViewportDialogService.show).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'promptHydrateSR', + }) + ); + + const onOutsideClick = mockUIViewportDialogService.show.mock.calls[0][0].onOutsideClick; + onOutsideClick(); + await promise; + }); + + it('should get default dialog id for unknown type', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = false; + + const promise = promptHydrationDialog({ + ...defaultParameters, + type: 'UNKNOWN_TYPE', + }); + + expect(mockUIViewportDialogService.show).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'promptHydrate', + }) + ); + + const onOutsideClick = mockUIViewportDialogService.show.mock.calls[0][0].onOutsideClick; + onOutsideClick(); + await promise; + }); + + it('should handle hydrateCallback returning false for SEG', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = true; + mockHydrateCallback.mockResolvedValue(false); + + const result = await promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.SEG, + }); + + expect(result).toBe(false); + }); + + it('should handle hydrateCallback returning false for RTSTRUCT', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = true; + mockHydrateCallback.mockResolvedValue(false); + + const result = await promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.RTSTRUCT, + }); + + expect(result).toBe(false); + }); + + it('should handle SR hydration with null result', async () => { + mockExtensionManager._appConfig.measurementTrackingMode = 'standard'; + mockHydrateCallback.mockResolvedValue(null); + + const promise = promptHydrationDialog({ + ...defaultParameters, + type: HydrationType.SR, + }); + + const onSubmit = mockUIViewportDialogService.show.mock.calls[0][0].onSubmit; + onSubmit(5); + + const result = await promise; + + expect(result).toEqual({ + userResponse: 5, + displaySetInstanceUID: mockDisplaySet.displaySetInstanceUID, + srSeriesInstanceUID: mockDisplaySet.SeriesInstanceUID, + viewportId: defaultParameters.viewportId, + StudyInstanceUID: undefined, + SeriesInstanceUIDs: undefined, + }); + }); + + it('should handle RTSTRUCT type in getDialogId', async () => { + mockExtensionManager._appConfig.disableConfirmationPrompts = false; + + const promise = promptHydrationDialog({ + ...defaultParameters, + type: 'RTSTRUCT', + }); + + expect(mockUIViewportDialogService.show).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'promptHydrateRT', + }) + ); + + const onOutsideClick = mockUIViewportDialogService.show.mock.calls[0][0].onOutsideClick; + onOutsideClick(); + await promise; + }); +}); diff --git a/extensions/cornerstone/src/utils/promptHydrationDialog.ts b/extensions/cornerstone/src/utils/promptHydrationDialog.ts index d743f8005c4..0a287241927 100644 --- a/extensions/cornerstone/src/utils/promptHydrationDialog.ts +++ b/extensions/cornerstone/src/utils/promptHydrationDialog.ts @@ -49,7 +49,7 @@ function getCustomizationMessageKey(type: string): string { function getDialogId(type: string): string { switch (type) { - case HydrationType.RTSS: + case HydrationType.RTSTRUCT: return 'promptHydrateRT'; case HydrationType.SEG: return 'promptHydrateSEG'; diff --git a/extensions/cornerstone/src/utils/removeViewportSegmentationRepresentations.test.ts b/extensions/cornerstone/src/utils/removeViewportSegmentationRepresentations.test.ts new file mode 100644 index 00000000000..757afb66ccf --- /dev/null +++ b/extensions/cornerstone/src/utils/removeViewportSegmentationRepresentations.test.ts @@ -0,0 +1,129 @@ +import { segmentation } from '@cornerstonejs/tools'; +import removeViewportSegmentationRepresentations from './removeViewportSegmentationRepresentations'; + +jest.mock('@cornerstonejs/tools', () => ({ + segmentation: { + state: { + getSegmentationRepresentations: jest.fn(), + removeSegmentationRepresentation: jest.fn(), + }, + }, +})); + +describe('removeViewportSegmentationRepresentations', () => { + const viewportId = 'viewport-1'; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return early when no representations are found', () => { + (segmentation.state.getSegmentationRepresentations as jest.Mock).mockReturnValue(null); + + removeViewportSegmentationRepresentations(viewportId); + + expect(segmentation.state.getSegmentationRepresentations).toHaveBeenCalledWith(viewportId); + expect(segmentation.state.removeSegmentationRepresentation).not.toHaveBeenCalled(); + }); + + it('should return early when representations array is empty', () => { + (segmentation.state.getSegmentationRepresentations as jest.Mock).mockReturnValue([]); + + removeViewportSegmentationRepresentations(viewportId); + + expect(segmentation.state.removeSegmentationRepresentation).not.toHaveBeenCalled(); + }); + + it('should remove single segmentation representation', () => { + const representations = [ + { + segmentationRepresentationUID: 'representation-1', + }, + ]; + + (segmentation.state.getSegmentationRepresentations as jest.Mock).mockReturnValue( + representations + ); + + removeViewportSegmentationRepresentations(viewportId); + + expect(segmentation.state.removeSegmentationRepresentation).toHaveBeenCalledTimes(1); + expect(segmentation.state.removeSegmentationRepresentation).toHaveBeenCalledWith( + 'representation-1' + ); + }); + + it('should remove multiple segmentation representations', () => { + const representations = [ + { + segmentationRepresentationUID: 'representation-1', + }, + { + segmentationRepresentationUID: 'representation-2', + }, + { + segmentationRepresentationUID: 'representation-3', + }, + ]; + + (segmentation.state.getSegmentationRepresentations as jest.Mock).mockReturnValue( + representations + ); + + removeViewportSegmentationRepresentations(viewportId); + + expect(segmentation.state.removeSegmentationRepresentation).toHaveBeenCalledTimes(3); + expect(segmentation.state.removeSegmentationRepresentation).toHaveBeenNthCalledWith( + 1, + 'representation-1' + ); + expect(segmentation.state.removeSegmentationRepresentation).toHaveBeenNthCalledWith( + 2, + 'representation-2' + ); + expect(segmentation.state.removeSegmentationRepresentation).toHaveBeenNthCalledWith( + 3, + 'representation-3' + ); + }); + + it('should handle representations with additional properties', () => { + const representations = [ + { + segmentationRepresentationUID: 'representation-1', + type: 'LABELMAP', + active: true, + }, + { + segmentationRepresentationUID: 'representation-2', + type: 'CONTOUR', + active: false, + visible: true, + }, + ]; + + (segmentation.state.getSegmentationRepresentations as jest.Mock).mockReturnValue( + representations + ); + + removeViewportSegmentationRepresentations(viewportId); + + expect(segmentation.state.removeSegmentationRepresentation).toHaveBeenCalledTimes(2); + expect(segmentation.state.removeSegmentationRepresentation).toHaveBeenNthCalledWith( + 1, + 'representation-1' + ); + expect(segmentation.state.removeSegmentationRepresentation).toHaveBeenNthCalledWith( + 2, + 'representation-2' + ); + }); + + it('should handle undefined representations', () => { + (segmentation.state.getSegmentationRepresentations as jest.Mock).mockReturnValue(undefined); + + removeViewportSegmentationRepresentations(viewportId); + + expect(segmentation.state.removeSegmentationRepresentation).not.toHaveBeenCalled(); + }); +}); diff --git a/extensions/cornerstone/src/utils/segmentUtils.test.ts b/extensions/cornerstone/src/utils/segmentUtils.test.ts new file mode 100644 index 00000000000..65b1e92571e --- /dev/null +++ b/extensions/cornerstone/src/utils/segmentUtils.test.ts @@ -0,0 +1,515 @@ +import { handleSegmentChange } from './segmentUtils'; + +describe('handleSegmentChange', () => { + const mockSegmentationService = { + getSegmentation: jest.fn(), + getActiveSegment: jest.fn(), + setActiveSegment: jest.fn(), + jumpToSegmentNext: jest.fn(), + }; + + const mockSegmentation = { + segments: { + '1': { + segmentIndex: 1, + label: 'Segment 1', + }, + '2': { + segmentIndex: 2, + label: 'Segment 2', + }, + '3': { + segmentIndex: 3, + label: 'Segment 3', + }, + }, + }; + + const defaultParameters = { + direction: 1, + segmentationId: 'test-segmentation-id', + viewportId: 'test-viewport-id', + selectedSegmentObjectIndex: 0, + // @ts-expect-error - only part of the SegmentationService is needed + segmentationService: mockSegmentationService as AppTypes.SegmentationService, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + }); + + it('should move to next segment when direction is positive and activeSegment is null', () => { + const direction = 1; + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 0, + }); + + expect(mockSegmentationService.getSegmentation).toHaveBeenCalledWith( + defaultParameters.segmentationId + ); + expect(mockSegmentationService.getActiveSegment).toHaveBeenCalledWith( + defaultParameters.viewportId + ); + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 2 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 2, + undefined, + direction + ); + }); + + it('should move to previous segment when direction is negative and activeSegment is null', () => { + const direction = -1; + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 1, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1, + undefined, + direction + ); + }); + + it('should update selectedSegmentObjectIndex when activeSegment is found', () => { + const direction = 1; + const mockActiveSegment = { + segmentIndex: 2, + }; + mockSegmentationService.getActiveSegment.mockReturnValue(mockActiveSegment); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 0, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 3 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 3, + undefined, + direction + ); + }); + + it('should loop to first segment when going beyond last segment', () => { + const direction = 1; + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 2, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1, + undefined, + direction + ); + }); + + it('should loop to last segment when going before first segment', () => { + const direction = -1; + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 0, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 3 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 3, + undefined, + direction + ); + }); + + it('should handle direction with value greater than 1', () => { + const direction = 2; + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 0, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 3 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 3, + undefined, + direction + ); + }); + + it('should handle direction with value less than -1', () => { + const direction = -2; + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 1, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 3 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 3, + undefined, + direction + ); + }); + + it('should handle single segment scenario with positive direction', () => { + const direction = 1; + const singleSegmentSegmentation = { + segments: { + '1': { + segmentIndex: 1, + label: 'Only Segment', + }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(singleSegmentSegmentation); + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 0, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1, + undefined, + direction + ); + }); + + it('should handle single segment scenario with negative direction', () => { + const direction = -1; + const singleSegmentSegmentation = { + segments: { + '1': { + segmentIndex: 1, + label: 'Only Segment', + }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(singleSegmentSegmentation); + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 0, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1, + undefined, + direction + ); + }); + + it('should handle activeSegment not found in segments', () => { + const direction = 1; + const mockActiveSegment = { + segmentIndex: 99, + }; + mockSegmentationService.getActiveSegment.mockReturnValue(mockActiveSegment); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 0, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1, + undefined, + direction + ); + }); + + it('should handle zero direction', () => { + const direction = 0; + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 1, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 2 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 2, + undefined, + direction + ); + }); + + it('should handle segments with non-sequential indices', () => { + const direction = 1; + const nonSequentialSegmentation = { + segments: { + '5': { + segmentIndex: 5, + label: 'Segment 5', + }, + '10': { + segmentIndex: 10, + label: 'Segment 10', + }, + '15': { + segmentIndex: 15, + label: 'Segment 15', + }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(nonSequentialSegmentation); + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 0, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 10 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 10, + undefined, + direction + ); + }); + + it('should handle wrap around with non-sequential indices', () => { + const direction = 1; + const nonSequentialSegmentation = { + segments: { + '5': { + segmentIndex: 5, + label: 'Segment 5', + }, + '10': { + segmentIndex: 10, + label: 'Segment 10', + }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(nonSequentialSegmentation); + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 1, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 5 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 5, + undefined, + direction + ); + }); + + it('should handle empty segments object', () => { + const direction = 1; + const emptySegmentation = { + segments: {}, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(emptySegmentation); + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 0, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + undefined + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + undefined, + undefined, + direction + ); + }); + + it('should handle large positive direction that wraps multiple times', () => { + const direction = 5; + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 1, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1, + undefined, + direction + ); + }); + + it('should handle large negative direction that wraps multiple times', () => { + const direction = -5; + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 1, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 3 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 3, + undefined, + direction + ); + }); + + it('should handle activeSegment with segmentIndex 0', () => { + const direction = 1; + const mockActiveSegment = { + segmentIndex: 0, + }; + mockSegmentationService.getActiveSegment.mockReturnValue(mockActiveSegment); + + handleSegmentChange({ + ...defaultParameters, + direction, + selectedSegmentObjectIndex: 0, + }); + + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + defaultParameters.segmentationId, + 1, + undefined, + direction + ); + }); + + it('should use different viewport and segmentation IDs', () => { + const direction = 1; + mockSegmentationService.getActiveSegment.mockReturnValue(null); + + handleSegmentChange({ + ...defaultParameters, + segmentationId: 'different-segmentation-id', + viewportId: 'different-viewport-id', + direction, + selectedSegmentObjectIndex: 0, + }); + + expect(mockSegmentationService.getSegmentation).toHaveBeenCalledWith( + 'different-segmentation-id' + ); + expect(mockSegmentationService.getActiveSegment).toHaveBeenCalledWith('different-viewport-id'); + expect(mockSegmentationService.setActiveSegment).toHaveBeenCalledWith( + 'different-segmentation-id', + 2 + ); + expect(mockSegmentationService.jumpToSegmentNext).toHaveBeenCalledWith( + 'different-segmentation-id', + 2, + undefined, + direction + ); + }); +}); diff --git a/extensions/cornerstone/src/utils/segmentUtils.ts b/extensions/cornerstone/src/utils/segmentUtils.ts index 61fed206929..d214d62bf5a 100644 --- a/extensions/cornerstone/src/utils/segmentUtils.ts +++ b/extensions/cornerstone/src/utils/segmentUtils.ts @@ -41,6 +41,6 @@ export const handleSegmentChange = ({ const segmentIndex = Object.values(segments)[newSelectedSegmentIndex]?.segmentIndex; segmentationService.setActiveSegment(segmentationId, segmentIndex); - segmentationService.jumpToSegmentCenter(segmentationId, segmentIndex, viewportId); + segmentationService.jumpToSegmentNext(segmentationId, segmentIndex, undefined, direction); selectedSegmentObjectIndex = newSelectedSegmentIndex; }; diff --git a/extensions/cornerstone/src/utils/segmentationExportUtils.ts b/extensions/cornerstone/src/utils/segmentationExportUtils.ts new file mode 100644 index 00000000000..dddd4e6a5c8 --- /dev/null +++ b/extensions/cornerstone/src/utils/segmentationExportUtils.ts @@ -0,0 +1,86 @@ +import { cache, metaData } from '@cornerstonejs/core'; +import { Types as cstTypes } from '@cornerstonejs/tools'; + +/** + * Checks if a labelmap segmentation has exportable data + * A label map is exportable if it has any pixel data and + * it is referenced by a display set that is reconstructable. + * + * @param labelmap - The labelmap representation data + * @param displaySetService - The display set service instance + * @returns boolean - Whether the labelmap has exportable data + */ +export const hasExportableLabelMapData = ( + labelmap: cstTypes.LabelmapSegmentationData | undefined, + displaySetService: any +): boolean => { + if (!labelmap) { + return false; + } + + const imageIds = labelmap?.imageIds; + if (!imageIds?.length) { + return false; + } + + const hasPixelData = imageIds.some(imageId => { + const pixelData = cache.getImage(imageId)?.getPixelData(); + if (!pixelData) { + return false; + } + + for (let i = 0; i < pixelData.length; i++) { + if (pixelData[i] !== 0) { + return true; + } + } + }); + + if (!hasPixelData) { + return false; + } + + const referencedImageIds = labelmap?.referencedImageIds; + + if (!referencedImageIds) { + return false; + } + const firstImageId = referencedImageIds[0]; + const instance = metaData.get('instance', firstImageId); + + if (!instance) { + return false; + } + + const SOPInstanceUID = instance.SOPInstanceUID || instance.SopInstanceUID; + const SeriesInstanceUID = instance.SeriesInstanceUID; + const displaySet = displaySetService.getDisplaySetForSOPInstanceUID( + SOPInstanceUID, + SeriesInstanceUID + ); + + return !!displaySet; +}; + +/** + * Checks if a contour segmentation has exportable data + * A contour is exportable if it has any contour annotation/geometry data. + * + * @param contour - The contour representation data + * @returns boolean - Whether the contour has exportable data + */ +export const hasExportableContourData = ( + contour: cstTypes.ContourSegmentationData | undefined +): boolean => { + if (!contour) { + return false; + } + + const contourAnnotationUIDsMap = contour?.annotationUIDsMap; + + if (!contourAnnotationUIDsMap) { + return false; + } + + return contourAnnotationUIDsMap.size > 0; +}; diff --git a/extensions/cornerstone/src/utils/segmentationHandlers.test.ts b/extensions/cornerstone/src/utils/segmentationHandlers.test.ts new file mode 100644 index 00000000000..8624ed9a04a --- /dev/null +++ b/extensions/cornerstone/src/utils/segmentationHandlers.test.ts @@ -0,0 +1,614 @@ +import * as cornerstoneTools from '@cornerstonejs/tools'; +import { + setupSegmentationDataModifiedHandler, + setupSegmentationModifiedHandler, +} from './segmentationHandlers'; +import { updateSegmentationStats } from './updateSegmentationStats'; + +jest.mock('@cornerstonejs/tools', () => ({ + annotation: { + state: { + getAllAnnotations: jest.fn(), + removeAnnotation: jest.fn(), + }, + }, + SegmentBidirectionalTool: { + toolName: 'SegmentBidirectional', + }, +})); + +jest.mock('./updateSegmentationStats', () => ({ + updateSegmentationStats: jest.fn(), +})); + +describe('setupSegmentationDataModifiedHandler', () => { + const mockSegmentationService = { + EVENTS: { + SEGMENTATION_DATA_MODIFIED: 'SEGMENTATION_DATA_MODIFIED', + }, + subscribeDebounced: jest.fn(), + getSegmentation: jest.fn(), + addOrUpdateSegmentation: jest.fn(), + }; + + const mockCustomizationService = { + getCustomization: jest.fn(), + }; + + const mockCommandsManager = { + runCommand: jest.fn(), + }; + + const mockUnsubscribeDebounced = jest.fn(); + + const defaultParameters = { + segmentationService: mockSegmentationService, + customizationService: mockCustomizationService, + commandsManager: mockCommandsManager, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockSegmentationService.subscribeDebounced.mockReturnValue({ + unsubscribe: mockUnsubscribeDebounced, + }); + (updateSegmentationStats as jest.Mock).mockResolvedValue(null); + }); + + it('should subscribe to SEGMENTATION_DATA_MODIFIED event with debounce', () => { + setupSegmentationDataModifiedHandler(defaultParameters); + + expect(mockSegmentationService.subscribeDebounced).toHaveBeenCalledWith( + mockSegmentationService.EVENTS.SEGMENTATION_DATA_MODIFIED, + expect.any(Function), + 1000 + ); + }); + + it('should return unsubscribe function', () => { + const result = setupSegmentationDataModifiedHandler(defaultParameters); + + expect(result).toEqual({ + unsubscribe: expect.any(Function), + }); + + result.unsubscribe(); + + expect(mockUnsubscribeDebounced).toHaveBeenCalled(); + }); + + it('should return early when segmentation is null', async () => { + mockSegmentationService.getSegmentation.mockReturnValue(null); + + setupSegmentationDataModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribeDebounced.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(mockSegmentationService.getSegmentation).toHaveBeenCalledWith('test-id'); + expect(updateSegmentationStats).not.toHaveBeenCalled(); + expect(mockSegmentationService.addOrUpdateSegmentation).not.toHaveBeenCalled(); + }); + + it('should return early when disableUpdateSegmentationStats is true', async () => { + const mockSegmentation = { + segments: { + 1: { segmentIndex: 1 }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + mockCustomizationService.getCustomization.mockReturnValue(true); + + setupSegmentationDataModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribeDebounced.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(mockCustomizationService.getCustomization).toHaveBeenCalledWith( + 'panelSegmentation.disableUpdateSegmentationStats' + ); + expect(updateSegmentationStats).not.toHaveBeenCalled(); + expect(mockSegmentationService.addOrUpdateSegmentation).not.toHaveBeenCalled(); + }); + + it('should update segmentation stats when conditions are met', async () => { + const mockSegmentation = { + segments: { + 1: { segmentIndex: 1 }, + 2: { segmentIndex: 2 }, + }, + }; + + const mockUpdatedSegmentation = { + segments: { + 1: { segmentIndex: 1, stats: 'updated' }, + 2: { segmentIndex: 2, stats: 'updated' }, + }, + }; + + const mockReadableText = { mean: 'Mean' }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + mockCustomizationService.getCustomization + .mockReturnValueOnce(false) + .mockReturnValueOnce(mockReadableText); + (updateSegmentationStats as jest.Mock).mockResolvedValue(mockUpdatedSegmentation); + + setupSegmentationDataModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribeDebounced.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(updateSegmentationStats).toHaveBeenCalledWith({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + expect(mockSegmentationService.addOrUpdateSegmentation).toHaveBeenCalledWith({ + segmentationId: 'test-id', + segments: mockUpdatedSegmentation.segments, + }); + }); + + it('should not update segmentation when unsubscribed', async () => { + const mockSegmentation = { + segments: { + 1: { segmentIndex: 1 }, + }, + }; + + const mockUpdatedSegmentation = { + segments: { + 1: { segmentIndex: 1, stats: 'updated' }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + mockCustomizationService.getCustomization.mockReturnValue(false); + (updateSegmentationStats as jest.Mock).mockResolvedValue(mockUpdatedSegmentation); + + const result = setupSegmentationDataModifiedHandler(defaultParameters); + result.unsubscribe(); + + const callback = mockSegmentationService.subscribeDebounced.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(updateSegmentationStats).toHaveBeenCalled(); + expect(mockSegmentationService.addOrUpdateSegmentation).not.toHaveBeenCalled(); + }); + + it('should not update segmentation when updateSegmentationStats returns null', async () => { + const mockSegmentation = { + segments: { + 1: { segmentIndex: 1 }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + mockCustomizationService.getCustomization.mockReturnValue(false); + (updateSegmentationStats as jest.Mock).mockResolvedValue(null); + + setupSegmentationDataModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribeDebounced.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(updateSegmentationStats).toHaveBeenCalled(); + expect(mockSegmentationService.addOrUpdateSegmentation).not.toHaveBeenCalled(); + }); + + it('should run bidirectional command for segments with bidirectional stats', async () => { + const mockSegmentation = { + segments: { + 0: { segmentIndex: 0 }, + 1: { + segmentIndex: 1, + cachedStats: { + namedStats: { + bidirectional: { data: 'test' }, + }, + }, + }, + 2: { segmentIndex: 2 }, + 3: { + segmentIndex: 3, + cachedStats: { + namedStats: { + bidirectional: { data: 'test2' }, + }, + }, + }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + mockCustomizationService.getCustomization.mockReturnValue(false); + (updateSegmentationStats as jest.Mock).mockResolvedValue(null); + + setupSegmentationDataModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribeDebounced.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(mockCommandsManager.runCommand).toHaveBeenCalledTimes(2); + expect(mockCommandsManager.runCommand).toHaveBeenNthCalledWith(1, 'runSegmentBidirectional', { + segmentationId: 'test-id', + segmentIndex: 1, + }); + expect(mockCommandsManager.runCommand).toHaveBeenNthCalledWith(2, 'runSegmentBidirectional', { + segmentationId: 'test-id', + segmentIndex: 3, + }); + }); + + it('should not run bidirectional command for segment index 0', async () => { + const mockSegmentation = { + segments: { + 0: { + segmentIndex: 0, + cachedStats: { + namedStats: { + bidirectional: { data: 'test' }, + }, + }, + }, + 1: { segmentIndex: 1 }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + mockCustomizationService.getCustomization.mockReturnValue(false); + (updateSegmentationStats as jest.Mock).mockResolvedValue(null); + + setupSegmentationDataModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribeDebounced.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(mockCommandsManager.runCommand).not.toHaveBeenCalled(); + }); + + it('should handle segment without cachedStats', async () => { + const mockSegmentation = { + segments: { + 1: { segmentIndex: 1 }, + 2: { + segmentIndex: 2, + cachedStats: undefined, + }, + 3: { + segmentIndex: 3, + cachedStats: { + namedStats: undefined, + }, + }, + 4: { + segmentIndex: 4, + cachedStats: { + namedStats: { + other: { data: 'test' }, + }, + }, + }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + mockCustomizationService.getCustomization.mockReturnValue(false); + (updateSegmentationStats as jest.Mock).mockResolvedValue(null); + + setupSegmentationDataModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribeDebounced.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(mockCommandsManager.runCommand).not.toHaveBeenCalled(); + }); +}); + +describe('setupSegmentationModifiedHandler', () => { + const mockSegmentationService = { + EVENTS: { + SEGMENTATION_MODIFIED: 'SEGMENTATION_MODIFIED', + }, + subscribe: jest.fn(), + getSegmentation: jest.fn(), + }; + + const mockUnsubscribe = jest.fn(); + + const defaultParameters = { + segmentationService: mockSegmentationService, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockSegmentationService.subscribe.mockReturnValue({ + unsubscribe: mockUnsubscribe, + }); + (cornerstoneTools.annotation.state.getAllAnnotations as jest.Mock).mockReturnValue([]); + }); + + it('should subscribe to SEGMENTATION_MODIFIED event', () => { + setupSegmentationModifiedHandler(defaultParameters); + + expect(mockSegmentationService.subscribe).toHaveBeenCalledWith( + mockSegmentationService.EVENTS.SEGMENTATION_MODIFIED, + expect.any(Function) + ); + }); + + it('should return unsubscribe function', () => { + const result = setupSegmentationModifiedHandler(defaultParameters); + + expect(result).toEqual({ + unsubscribe: mockUnsubscribe, + }); + }); + + it('should return early when segmentation is null', async () => { + mockSegmentationService.getSegmentation.mockReturnValue(null); + + setupSegmentationModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(cornerstoneTools.annotation.state.getAllAnnotations).not.toHaveBeenCalled(); + expect(cornerstoneTools.annotation.state.removeAnnotation).not.toHaveBeenCalled(); + }); + + it('should remove bidirectional annotations for non-existent segments', async () => { + const mockSegmentation = { + segments: { + 1: { segmentIndex: 1 }, + 3: { segmentIndex: 3 }, + }, + }; + + const mockAnnotations = [ + { + annotationUID: 'annotation-1', + metadata: { + toolName: 'SegmentBidirectional', + segmentationId: 'test-id', + segmentIndex: 1, + }, + }, + { + annotationUID: 'annotation-2', + metadata: { + toolName: 'SegmentBidirectional', + segmentationId: 'test-id', + segmentIndex: 2, + }, + }, + { + annotationUID: 'annotation-3', + metadata: { + toolName: 'SegmentBidirectional', + segmentationId: 'other-id', + segmentIndex: 2, + }, + }, + { + annotationUID: 'annotation-4', + metadata: { + toolName: 'OtherTool', + segmentationId: 'test-id', + segmentIndex: 2, + }, + }, + ]; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + (cornerstoneTools.annotation.state.getAllAnnotations as jest.Mock).mockReturnValue( + mockAnnotations + ); + + setupSegmentationModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(cornerstoneTools.annotation.state.removeAnnotation).toHaveBeenCalledTimes(1); + expect(cornerstoneTools.annotation.state.removeAnnotation).toHaveBeenCalledWith('annotation-2'); + }); + + it('should not remove annotations for existing segments', async () => { + const mockSegmentation = { + segments: { + 1: { segmentIndex: 1 }, + 2: { segmentIndex: 2 }, + }, + }; + + const mockAnnotations = [ + { + annotationUID: 'annotation-1', + metadata: { + toolName: 'SegmentBidirectional', + segmentationId: 'test-id', + segmentIndex: 1, + }, + }, + { + annotationUID: 'annotation-2', + metadata: { + toolName: 'SegmentBidirectional', + segmentationId: 'test-id', + segmentIndex: 2, + }, + }, + ]; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + (cornerstoneTools.annotation.state.getAllAnnotations as jest.Mock).mockReturnValue( + mockAnnotations + ); + + setupSegmentationModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(cornerstoneTools.annotation.state.removeAnnotation).not.toHaveBeenCalled(); + }); + + it('should handle empty segments object', async () => { + const mockSegmentation = { + segments: {}, + }; + + const mockAnnotations = [ + { + annotationUID: 'annotation-1', + metadata: { + toolName: 'SegmentBidirectional', + segmentationId: 'test-id', + segmentIndex: 1, + }, + }, + ]; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + (cornerstoneTools.annotation.state.getAllAnnotations as jest.Mock).mockReturnValue( + mockAnnotations + ); + + setupSegmentationModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(cornerstoneTools.annotation.state.removeAnnotation).toHaveBeenCalledWith('annotation-1'); + }); + + it('should filter out segment index 0', async () => { + const mockSegmentation = { + segments: { + 0: { segmentIndex: 0 }, + 1: { segmentIndex: 1 }, + }, + }; + + const mockAnnotations = [ + { + annotationUID: 'annotation-0', + metadata: { + toolName: 'SegmentBidirectional', + segmentationId: 'test-id', + segmentIndex: 0, + }, + }, + { + annotationUID: 'annotation-1', + metadata: { + toolName: 'SegmentBidirectional', + segmentationId: 'test-id', + segmentIndex: 1, + }, + }, + ]; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + (cornerstoneTools.annotation.state.getAllAnnotations as jest.Mock).mockReturnValue( + mockAnnotations + ); + + setupSegmentationModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(cornerstoneTools.annotation.state.removeAnnotation).toHaveBeenCalledWith('annotation-0'); + }); + + it('should handle no bidirectional annotations', async () => { + const mockSegmentation = { + segments: { + 1: { segmentIndex: 1 }, + }, + }; + + const mockAnnotations = [ + { + annotationUID: 'annotation-1', + metadata: { + toolName: 'OtherTool', + segmentationId: 'test-id', + segmentIndex: 1, + }, + }, + ]; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + (cornerstoneTools.annotation.state.getAllAnnotations as jest.Mock).mockReturnValue( + mockAnnotations + ); + + setupSegmentationModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(cornerstoneTools.annotation.state.removeAnnotation).not.toHaveBeenCalled(); + }); + + it('should handle mixed annotation types and segmentation IDs', async () => { + const mockSegmentation = { + segments: { + 1: { segmentIndex: 1 }, + }, + }; + + const mockAnnotations = [ + { + annotationUID: 'keep-1', + metadata: { + toolName: 'SegmentBidirectional', + segmentationId: 'test-id', + segmentIndex: 1, + }, + }, + { + annotationUID: 'remove-1', + metadata: { + toolName: 'SegmentBidirectional', + segmentationId: 'test-id', + segmentIndex: 2, + }, + }, + { + annotationUID: 'keep-2', + metadata: { + toolName: 'SegmentBidirectional', + segmentationId: 'other-id', + segmentIndex: 2, + }, + }, + { + annotationUID: 'keep-3', + metadata: { + toolName: 'OtherTool', + segmentationId: 'test-id', + segmentIndex: 2, + }, + }, + ]; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + (cornerstoneTools.annotation.state.getAllAnnotations as jest.Mock).mockReturnValue( + mockAnnotations + ); + + setupSegmentationModifiedHandler(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + await callback({ segmentationId: 'test-id' }); + + expect(cornerstoneTools.annotation.state.removeAnnotation).toHaveBeenCalledTimes(1); + expect(cornerstoneTools.annotation.state.removeAnnotation).toHaveBeenCalledWith('remove-1'); + }); +}); diff --git a/extensions/cornerstone/src/utils/segmentationHandlers.ts b/extensions/cornerstone/src/utils/segmentationHandlers.ts index b79f30e7ce8..3b7cd497081 100644 --- a/extensions/cornerstone/src/utils/segmentationHandlers.ts +++ b/extensions/cornerstone/src/utils/segmentationHandlers.ts @@ -1,5 +1,7 @@ import * as cornerstoneTools from '@cornerstonejs/tools'; import { updateSegmentationStats } from './updateSegmentationStats'; +import { useSelectedSegmentationsForViewportStore } from '../stores'; +import { SegmentationRepresentations } from '@cornerstonejs/tools/enums'; /** * Sets up the handler for segmentation data modification events @@ -16,9 +18,13 @@ export function setupSegmentationDataModifiedHandler({ const { unsubscribe: debouncedUnsubscribe } = segmentationService.subscribeDebounced( segmentationService.EVENTS.SEGMENTATION_DATA_MODIFIED, async ({ segmentationId }) => { + const disableUpdateSegmentationStats = customizationService.getCustomization( + 'panelSegmentation.disableUpdateSegmentationStats' + ); + const segmentation = segmentationService.getSegmentation(segmentationId); - if (!segmentation) { + if (!segmentation || disableUpdateSegmentationStats) { return; } @@ -82,27 +88,21 @@ export function setupSegmentationModifiedHandler({ segmentationService }) { annotation.metadata.toolName === cornerstoneTools.SegmentBidirectionalTool.toolName ); - let toRemoveUIDs = []; - if (!segmentation) { - toRemoveUIDs = bidirectionalAnnotations.map( - annotation => annotation.metadata.segmentationId === segmentationId - ); - return; - } else { - const segmentIndices = Object.keys(segmentation.segments) - .map(index => parseInt(index)) - .filter(index => index > 0); - - // check if there is a bidirectional data that exists but the segment - // does not exists anymore we need to remove the bidirectional data - const bidirectionalAnnotationsToRemove = bidirectionalAnnotations.filter( - annotation => - annotation.metadata.segmentationId === segmentationId && - !segmentIndices.includes(annotation.metadata.segmentIndex) - ); - - toRemoveUIDs = bidirectionalAnnotationsToRemove.map(annotation => annotation.annotationUID); - } + const segmentIndices = Object.keys(segmentation.segments) + .map(index => parseInt(index)) + .filter(index => index > 0); + + // check if there is a bidirectional data that exists but the segment + // does not exists anymore we need to remove the bidirectional data + const bidirectionalAnnotationsToRemove = bidirectionalAnnotations.filter( + annotation => + annotation.metadata.segmentationId === segmentationId && + !segmentIndices.includes(annotation.metadata.segmentIndex) + ); + + const toRemoveUIDs = bidirectionalAnnotationsToRemove.map( + annotation => annotation.annotationUID + ); toRemoveUIDs.forEach(uid => { cornerstoneTools.annotation.state.removeAnnotation(uid); @@ -112,3 +112,49 @@ export function setupSegmentationModifiedHandler({ segmentationService }) { return { unsubscribe }; } + +/** + * Sets up auto tab switching for when the first segmentation is added into the viewer. + */ +export function setUpSelectedSegmentationsForViewportHandler({ segmentationService }) { + const selectedSegmentationsForViewportEvents = [ + segmentationService.EVENTS.SEGMENTATION_MODIFIED, + segmentationService.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED, + ]; + + const unsubscribeSelectedSegmentationsForViewportEvents = selectedSegmentationsForViewportEvents + .map(eventName => + segmentationService.subscribe(eventName, event => { + const { viewportId } = event; + + if (!viewportId) { + return; + } + + const { selectedSegmentationsForViewport, setSelectedSegmentationsForViewport } = + useSelectedSegmentationsForViewportStore.getState(); + + const representations = segmentationService.getSegmentationRepresentations(viewportId); + + const activeRepresentation = representations.find(representation => representation.active); + + const typeToSegmentationIdMap = + selectedSegmentationsForViewport[viewportId] ?? + new Map(); + + if (activeRepresentation) { + typeToSegmentationIdMap.set( + activeRepresentation.type, + activeRepresentation.segmentationId + ); + } else { + typeToSegmentationIdMap.clear(); + } + + setSelectedSegmentationsForViewport(viewportId, typeToSegmentationIdMap); + }) + ) + .map(subscription => subscription.unsubscribe); + + return { unsubscribeSelectedSegmentationsForViewportEvents }; +} diff --git a/extensions/cornerstone/src/utils/setUpAnnotationEventHandlers.ts b/extensions/cornerstone/src/utils/setUpAnnotationEventHandlers.ts new file mode 100644 index 00000000000..8a90129ac72 --- /dev/null +++ b/extensions/cornerstone/src/utils/setUpAnnotationEventHandlers.ts @@ -0,0 +1,26 @@ +import * as csTools from '@cornerstonejs/tools'; + +import { eventTarget } from '@cornerstonejs/core'; + +const { Enums: csToolsEnums } = csTools; + +const _autoAcceptAnnotationInterpolationHandler = evt => { + const { element } = evt.detail; + csTools.utilities.contours.acceptAutogeneratedInterpolations(element, {}); +}; + +export const setUpAnnotationEventHandlers = (): [() => void] => { + eventTarget.addEventListener( + csToolsEnums.Events.ANNOTATION_INTERPOLATION_PROCESS_COMPLETED, + _autoAcceptAnnotationInterpolationHandler + ); + + const annotationInterpolationProcessCompletedUnsubscribe = () => { + eventTarget.removeEventListener( + csToolsEnums.Events.ANNOTATION_INTERPOLATION_PROCESS_COMPLETED, + _autoAcceptAnnotationInterpolationHandler + ); + }; + + return [annotationInterpolationProcessCompletedUnsubscribe]; +}; diff --git a/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.test.ts b/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.test.ts new file mode 100644 index 00000000000..90e611217a9 --- /dev/null +++ b/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.test.ts @@ -0,0 +1,362 @@ +import { setUpSegmentationEventHandlers } from './setUpSegmentationEventHandlers'; +import { + setupSegmentationDataModifiedHandler, + setupSegmentationModifiedHandler, + setUpSelectedSegmentationsForViewportHandler, +} from './segmentationHandlers'; + +jest.mock('./segmentationHandlers', () => ({ + setupSegmentationDataModifiedHandler: jest.fn(), + setupSegmentationModifiedHandler: jest.fn(), + setUpSelectedSegmentationsForViewportHandler: jest.fn(), +})); + +describe('setUpSegmentationEventHandlers', () => { + const mockSegmentationService = { + EVENTS: { + SEGMENTATION_ADDED: 'SEGMENTATION_ADDED', + }, + subscribe: jest.fn(), + getSegmentation: jest.fn(), + }; + + const mockCustomizationService = {}; + + const mockDisplaySetService = { + getDisplaySetByUID: jest.fn(), + addDisplaySets: jest.fn(), + }; + + const mockCommandsManager = {}; + + const mockServicesManager = { + services: { + segmentationService: mockSegmentationService, + customizationService: mockCustomizationService, + displaySetService: mockDisplaySetService, + }, + }; + + const mockUnsubscribeDataModified = jest.fn(); + const mockUnsubscribeModified = jest.fn(); + const mockUnsubscribeCreated = jest.fn(); + const mockUnsubscribeSelectedSegmentationsForViewportEvents = [jest.fn(), jest.fn()]; + + const defaultParameters = { + servicesManager: mockServicesManager as unknown as AppTypes.ServicesManager, + commandsManager: mockCommandsManager, + }; + + beforeEach(() => { + jest.clearAllMocks(); + (setupSegmentationDataModifiedHandler as jest.Mock).mockReturnValue({ + unsubscribe: mockUnsubscribeDataModified, + }); + (setupSegmentationModifiedHandler as jest.Mock).mockReturnValue({ + unsubscribe: mockUnsubscribeModified, + }); + (setUpSelectedSegmentationsForViewportHandler as jest.Mock).mockReturnValue({ + unsubscribeSelectedSegmentationsForViewportEvents: + mockUnsubscribeSelectedSegmentationsForViewportEvents, + }); + mockSegmentationService.subscribe.mockReturnValue({ + unsubscribe: mockUnsubscribeCreated, + }); + }); + + it('should setup segmentation data modified handler', () => { + setUpSegmentationEventHandlers(defaultParameters); + + expect(setupSegmentationDataModifiedHandler).toHaveBeenCalledWith({ + segmentationService: mockSegmentationService, + customizationService: mockCustomizationService, + commandsManager: mockCommandsManager, + }); + }); + + it('should setup segmentation modified handler', () => { + setUpSegmentationEventHandlers(defaultParameters); + + expect(setupSegmentationModifiedHandler).toHaveBeenCalledWith({ + segmentationService: mockSegmentationService, + }); + }); + + it('should subscribe to SEGMENTATION_ADDED event', () => { + setUpSegmentationEventHandlers(defaultParameters); + + expect(mockSegmentationService.subscribe).toHaveBeenCalledWith( + mockSegmentationService.EVENTS.SEGMENTATION_ADDED, + expect.any(Function) + ); + }); + + it('should return unsubscriptions object', () => { + const result = setUpSegmentationEventHandlers(defaultParameters); + + expect(result).toEqual({ + unsubscriptions: [ + mockUnsubscribeDataModified, + mockUnsubscribeModified, + mockUnsubscribeCreated, + ...mockUnsubscribeSelectedSegmentationsForViewportEvents, + ], + }); + }); + + it('should return early when displaySet already exists for segmentationId', () => { + const mockDisplaySet = { + displaySetInstanceUID: 'test-segmentation-id', + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + setUpSegmentationEventHandlers(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + const evt = { segmentationId: 'test-segmentation-id' }; + + callback(evt); + + expect(mockDisplaySetService.getDisplaySetByUID).toHaveBeenCalledWith('test-segmentation-id'); + expect(mockSegmentationService.getSegmentation).not.toHaveBeenCalled(); + expect(mockDisplaySetService.addDisplaySets).not.toHaveBeenCalled(); + }); + + it('should create and add display set when segmentation is added and no displaySet exists', () => { + const mockSegmentation = { + label: 'Test Segmentation Label', + cachedStats: { + info: 'Test Segmentation Label', + }, + representationData: { + Labelmap: { + imageIds: ['image-1', 'image-2', 'image-3'], + }, + }, + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(null); + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + setUpSegmentationEventHandlers(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + const evt = { segmentationId: 'test-segmentation-id' }; + + callback(evt); + + expect(mockDisplaySetService.getDisplaySetByUID).toHaveBeenCalledWith('test-segmentation-id'); + expect(mockSegmentationService.getSegmentation).toHaveBeenCalledWith('test-segmentation-id'); + expect(mockDisplaySetService.addDisplaySets).toHaveBeenCalledWith({ + displaySetInstanceUID: 'test-segmentation-id', + SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', + SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', + SeriesDescription: mockSegmentation.label, + Modality: 'SEG', + numImageFrames: mockSegmentation.representationData.Labelmap.imageIds.length, + imageIds: mockSegmentation.representationData.Labelmap.imageIds, + isOverlayDisplaySet: true, + label: mockSegmentation.label, + madeInClient: true, + segmentationId: 'test-segmentation-id', + isDerived: true, + }); + }); + + it('should handle displaySet undefined when segmentation is added', () => { + const mockSegmentation = { + label: 'Test Segmentation Label', + cachedStats: { + info: 'Test Segmentation Label', + }, + representationData: { + Labelmap: { + imageIds: ['image-1', 'image-2'], + }, + }, + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(undefined); + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + setUpSegmentationEventHandlers(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + const evt = { segmentationId: 'test-segmentation-id' }; + + callback(evt); + + expect(mockSegmentationService.getSegmentation).toHaveBeenCalledWith('test-segmentation-id'); + expect(mockDisplaySetService.addDisplaySets).toHaveBeenCalledWith({ + displaySetInstanceUID: 'test-segmentation-id', + SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', + SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', + SeriesDescription: mockSegmentation.label, + Modality: 'SEG', + numImageFrames: mockSegmentation.representationData.Labelmap.imageIds.length, + imageIds: mockSegmentation.representationData.Labelmap.imageIds, + isOverlayDisplaySet: true, + label: mockSegmentation.label, + madeInClient: true, + segmentationId: 'test-segmentation-id', + isDerived: true, + }); + }); + + it('should handle empty imageIds array', () => { + const mockSegmentation = { + label: 'Empty Segmentation', + cachedStats: { + info: 'Empty Segmentation', + }, + representationData: { + Labelmap: { + imageIds: [], + }, + }, + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(null); + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + setUpSegmentationEventHandlers(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + const evt = { segmentationId: 'empty-segmentation-id' }; + + callback(evt); + + expect(mockDisplaySetService.addDisplaySets).toHaveBeenCalledWith({ + displaySetInstanceUID: 'empty-segmentation-id', + SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', + SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', + SeriesDescription: mockSegmentation.label, + Modality: 'SEG', + numImageFrames: 0, + imageIds: [], + isOverlayDisplaySet: true, + label: mockSegmentation.label, + madeInClient: true, + segmentationId: 'empty-segmentation-id', + isDerived: true, + }); + }); + + it('should handle different segmentation label values', () => { + const mockSegmentation = { + label: 'Custom Label Text', + cachedStats: { + info: 'Custom Label Text', + }, + representationData: { + Labelmap: { + imageIds: ['custom-image-1'], + }, + }, + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(null); + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + setUpSegmentationEventHandlers(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + const evt = { segmentationId: 'custom-segmentation-id' }; + + callback(evt); + + expect(mockDisplaySetService.addDisplaySets).toHaveBeenCalledWith({ + displaySetInstanceUID: 'custom-segmentation-id', + SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', + SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', + SeriesDescription: 'Custom Label Text', + Modality: 'SEG', + numImageFrames: 1, + imageIds: ['custom-image-1'], + isOverlayDisplaySet: true, + label: 'Custom Label Text', + madeInClient: true, + segmentationId: 'custom-segmentation-id', + isDerived: true, + }); + }); + + it('should handle multiple segmentation events', () => { + const mockSegmentation1 = { + label: 'Segmentation 1', + cachedStats: { + info: 'Segmentation 1', + }, + representationData: { + Labelmap: { + imageIds: ['image-1'], + }, + }, + }; + + const mockSegmentation2 = { + label: 'Segmentation 2', + cachedStats: { + info: 'Segmentation 2', + }, + representationData: { + Labelmap: { + imageIds: ['image-2', 'image-3'], + }, + }, + }; + + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(null); + mockSegmentationService.getSegmentation + .mockReturnValueOnce(mockSegmentation1) + .mockReturnValueOnce(mockSegmentation2); + + setUpSegmentationEventHandlers(defaultParameters); + + const callback = mockSegmentationService.subscribe.mock.calls[0][1]; + + callback({ segmentationId: 'segmentation-1' }); + callback({ segmentationId: 'segmentation-2' }); + + expect(mockDisplaySetService.addDisplaySets).toHaveBeenCalledTimes(2); + expect(mockDisplaySetService.addDisplaySets).toHaveBeenNthCalledWith(1, { + displaySetInstanceUID: 'segmentation-1', + SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', + SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', + SeriesDescription: mockSegmentation1.label, + Modality: 'SEG', + numImageFrames: 1, + imageIds: mockSegmentation1.representationData.Labelmap.imageIds, + isOverlayDisplaySet: true, + label: mockSegmentation1.label, + madeInClient: true, + segmentationId: 'segmentation-1', + isDerived: true, + }); + expect(mockDisplaySetService.addDisplaySets).toHaveBeenNthCalledWith(2, { + displaySetInstanceUID: 'segmentation-2', + SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', + SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', + SeriesDescription: mockSegmentation2.label, + Modality: 'SEG', + numImageFrames: 2, + imageIds: mockSegmentation2.representationData.Labelmap.imageIds, + isOverlayDisplaySet: true, + label: mockSegmentation2.label, + madeInClient: true, + segmentationId: 'segmentation-2', + isDerived: true, + }); + }); + + it('should call all unsubscribe functions in returned unsubscriptions array', () => { + const result = setUpSegmentationEventHandlers(defaultParameters); + + result.unsubscriptions.forEach(unsubscribe => unsubscribe()); + + expect(mockUnsubscribeDataModified).toHaveBeenCalled(); + expect(mockUnsubscribeModified).toHaveBeenCalled(); + expect(mockUnsubscribeCreated).toHaveBeenCalled(); + }); +}); diff --git a/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.ts b/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.ts index 678ce2229f9..c7104081fca 100644 --- a/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.ts +++ b/extensions/cornerstone/src/utils/setUpSegmentationEventHandlers.ts @@ -1,4 +1,7 @@ +import { Enums as csToolsEnums } from '@cornerstonejs/tools'; + import { + setUpSelectedSegmentationsForViewportHandler, setupSegmentationDataModifiedHandler, setupSegmentationModifiedHandler, } from './segmentationHandlers'; @@ -27,8 +30,10 @@ export const setUpSegmentationEventHandlers = ({ servicesManager, commandsManage } const segmentation = segmentationService.getSegmentation(segmentationId); - const label = segmentation.cachedStats.info; - const imageIds = segmentation.representationData.Labelmap.imageIds; + const label = segmentation.label; + const imageIds = + segmentation.representationData?.Labelmap?.imageIds ?? + segmentation.representationData?.Contour?.imageIds; // Create a display set for the segmentation const segmentationDisplaySet = { @@ -36,7 +41,9 @@ export const setUpSegmentationEventHandlers = ({ servicesManager, commandsManage SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4', SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', SeriesDescription: label, - Modality: 'SEG', + Modality: segmentation.representationData[csToolsEnums.SegmentationRepresentations.Contour] + ? 'RTSTRUCT' + : 'SEG', numImageFrames: imageIds.length, imageIds, isOverlayDisplaySet: true, @@ -50,10 +57,16 @@ export const setUpSegmentationEventHandlers = ({ servicesManager, commandsManage } ); + const { unsubscribeSelectedSegmentationsForViewportEvents } = + setUpSelectedSegmentationsForViewportHandler({ + segmentationService, + }); + const unsubscriptions = [ unsubscribeSegmentationDataModifiedHandler, unsubscribeSegmentationModifiedHandler, unsubscribeSegmentationCreated, + ...unsubscribeSelectedSegmentationsForViewportEvents, ]; return { unsubscriptions }; diff --git a/extensions/cornerstone/src/utils/toggleVOISliceSync.test.ts b/extensions/cornerstone/src/utils/toggleVOISliceSync.test.ts new file mode 100644 index 00000000000..888cbc69976 --- /dev/null +++ b/extensions/cornerstone/src/utils/toggleVOISliceSync.test.ts @@ -0,0 +1,606 @@ +import toggleVOISliceSync from './toggleVOISliceSync'; + +describe('toggleVOISliceSync', () => { + const mockSyncGroupService = { + getSynchronizersForViewport: jest.fn(), + addViewportToSyncGroup: jest.fn(), + removeViewportFromSyncGroup: jest.fn(), + }; + + const mockViewportGridService = { + getState: jest.fn(), + }; + + const mockDisplaySetService = { + getDisplaySetByUID: jest.fn(), + }; + + const mockCornerstoneViewportService = { + getCornerstoneViewport: jest.fn(), + }; + + const mockServicesManager = { + services: { + syncGroupService: mockSyncGroupService, + viewportGridService: mockViewportGridService, + displaySetService: mockDisplaySetService, + cornerstoneViewportService: mockCornerstoneViewportService, + }, + }; + + const mockViewport = { + id: 'viewport-1', + getRenderingEngine: jest.fn(() => ({ id: 'rendering-engine-1' })), + }; + + const mockViewport2 = { + id: 'viewport-2', + getRenderingEngine: jest.fn(() => ({ id: 'rendering-engine-2' })), + }; + + const mockGridViewport = { + viewportOptions: { + viewportId: 'viewport-1', + }, + displaySetInstanceUIDs: ['displaySet-1'], + }; + + const mockDisplaySet = { + Modality: 'CT', + }; + + const defaultParameters = { + servicesManager: mockServicesManager as unknown as AppTypes.ServicesManager, + viewports: null, + syncId: null, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mockCornerstoneViewportService.getCornerstoneViewport.mockReturnValue(mockViewport); + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + }); + + it('should enable sync when no sync exists for single modality', () => { + const viewports = { + CT: [mockGridViewport], + }; + + mockSyncGroupService.getSynchronizersForViewport.mockReturnValue([]); + + toggleVOISliceSync({ + ...defaultParameters, + viewports, + }); + + expect(mockSyncGroupService.getSynchronizersForViewport).toHaveBeenCalledWith('viewport-1'); + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenCalledWith( + 'viewport-1', + 'rendering-engine-1', + { + type: 'voi', + id: 'VOI_SYNC_CT', + source: true, + target: true, + } + ); + }); + + it('should disable sync when sync already exists', () => { + const viewports = { + CT: [mockGridViewport], + }; + + const mockSyncState = { + id: 'VOI_SYNC_CT', + }; + + mockSyncGroupService.getSynchronizersForViewport.mockReturnValue([mockSyncState]); + + toggleVOISliceSync({ + ...defaultParameters, + viewports, + }); + + expect(mockSyncGroupService.removeViewportFromSyncGroup).toHaveBeenCalledWith( + 'viewport-1', + 'rendering-engine-1', + 'VOI_SYNC_CT' + ); + expect(mockSyncGroupService.addViewportToSyncGroup).not.toHaveBeenCalled(); + }); + + it('should use custom syncId when provided', () => { + const viewports = { + CT: [mockGridViewport], + }; + + mockSyncGroupService.getSynchronizersForViewport.mockReturnValue([]); + + toggleVOISliceSync({ + ...defaultParameters, + viewports, + syncId: 'CUSTOM_SYNC_ID', + }); + + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenCalledWith( + 'viewport-1', + 'rendering-engine-1', + { + type: 'voi', + id: 'CUSTOM_SYNC_ID', + source: true, + target: true, + } + ); + }); + + it('should handle multiple viewports in same modality', () => { + const mockGridViewport2 = { + viewportOptions: { + viewportId: 'viewport-2', + }, + displaySetInstanceUIDs: ['displaySet-2'], + }; + + const viewports = { + CT: [mockGridViewport, mockGridViewport2], + }; + + mockCornerstoneViewportService.getCornerstoneViewport + .mockReturnValueOnce(mockViewport) + .mockReturnValueOnce(mockViewport2); + + mockSyncGroupService.getSynchronizersForViewport.mockReturnValue([]); + + toggleVOISliceSync({ + ...defaultParameters, + viewports, + }); + + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenCalledTimes(2); + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenNthCalledWith( + 1, + 'viewport-1', + 'rendering-engine-1', + { + type: 'voi', + id: 'VOI_SYNC_CT', + source: true, + target: true, + } + ); + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenNthCalledWith( + 2, + 'viewport-2', + 'rendering-engine-2', + { + type: 'voi', + id: 'VOI_SYNC_CT', + source: true, + target: true, + } + ); + }); + + it('should handle multiple modalities separately', () => { + const mockGridViewportMR = { + viewportOptions: { + viewportId: 'viewport-mr', + }, + displaySetInstanceUIDs: ['displaySet-mr'], + }; + + const mockViewportMR = { + id: 'viewport-mr', + getRenderingEngine: jest.fn(() => ({ id: 'rendering-engine-mr' })), + }; + + const viewports = { + CT: [mockGridViewport], + MR: [mockGridViewportMR], + }; + + mockCornerstoneViewportService.getCornerstoneViewport + .mockReturnValueOnce(mockViewport) + .mockReturnValueOnce(mockViewportMR); + + mockSyncGroupService.getSynchronizersForViewport.mockReturnValue([]); + + toggleVOISliceSync({ + ...defaultParameters, + viewports, + }); + + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenCalledTimes(2); + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenNthCalledWith( + 1, + 'viewport-1', + 'rendering-engine-1', + { + type: 'voi', + id: 'VOI_SYNC_CT', + source: true, + target: true, + } + ); + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenNthCalledWith( + 2, + 'viewport-mr', + 'rendering-engine-mr', + { + type: 'voi', + id: 'VOI_SYNC_MR', + source: true, + target: true, + } + ); + }); + + it('should skip viewport when cornerstone viewport is not found', () => { + const viewports = { + CT: [mockGridViewport], + }; + + mockCornerstoneViewportService.getCornerstoneViewport.mockReturnValue(null); + mockSyncGroupService.getSynchronizersForViewport.mockReturnValue([]); + + toggleVOISliceSync({ + ...defaultParameters, + viewports, + }); + + expect(mockSyncGroupService.addViewportToSyncGroup).not.toHaveBeenCalled(); + }); + + it('should generate viewports from grid service when not provided', () => { + const mockViewportsMap = new Map([ + [ + 'viewport-1', + { + ...mockGridViewport, + }, + ], + ]); + + const mockState = { + viewports: mockViewportsMap, + }; + + mockViewportGridService.getState.mockReturnValue(mockState); + mockSyncGroupService.getSynchronizersForViewport.mockReturnValue([]); + + toggleVOISliceSync({ + ...defaultParameters, + viewports: null, + }); + + expect(mockViewportGridService.getState).toHaveBeenCalled(); + expect(mockDisplaySetService.getDisplaySetByUID).toHaveBeenCalledWith('displaySet-1'); + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenCalledWith( + 'viewport-1', + 'rendering-engine-1', + { + type: 'voi', + id: 'VOI_SYNC_CT', + source: true, + target: true, + } + ); + }); + + it('should handle sync state with different id not matching', () => { + const viewports = { + CT: [mockGridViewport], + }; + + const mockSyncState = { + id: 'DIFFERENT_SYNC_ID', + }; + + mockSyncGroupService.getSynchronizersForViewport.mockReturnValue([mockSyncState]); + + toggleVOISliceSync({ + ...defaultParameters, + viewports, + }); + + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenCalledWith( + 'viewport-1', + 'rendering-engine-1', + { + type: 'voi', + id: 'VOI_SYNC_CT', + source: true, + target: true, + } + ); + expect(mockSyncGroupService.removeViewportFromSyncGroup).not.toHaveBeenCalled(); + }); + + it('should handle mixed sync states where some match and some do not', () => { + const mockGridViewport2 = { + viewportOptions: { + viewportId: 'viewport-2', + }, + displaySetInstanceUIDs: ['displaySet-2'], + }; + + const viewports = { + CT: [mockGridViewport, mockGridViewport2], + }; + + const mockSyncState = { + id: 'VOI_SYNC_CT', + }; + + mockSyncGroupService.getSynchronizersForViewport + .mockReturnValueOnce([mockSyncState]) + .mockReturnValueOnce([]); + + mockCornerstoneViewportService.getCornerstoneViewport + .mockReturnValueOnce(mockViewport) + .mockReturnValueOnce(mockViewport2); + + toggleVOISliceSync({ + ...defaultParameters, + viewports, + }); + + expect(mockSyncGroupService.removeViewportFromSyncGroup).toHaveBeenCalledWith( + 'viewport-1', + 'rendering-engine-1', + 'VOI_SYNC_CT' + ); + expect(mockSyncGroupService.removeViewportFromSyncGroup).toHaveBeenCalledWith( + 'viewport-2', + 'rendering-engine-2', + 'VOI_SYNC_CT' + ); + }); + + it('should disable sync for multiple viewports when sync exists', () => { + const mockGridViewport2 = { + viewportOptions: { + viewportId: 'viewport-2', + }, + displaySetInstanceUIDs: ['displaySet-2'], + }; + + const mockViewport2 = { + id: 'viewport-2', + getRenderingEngine: jest.fn(() => ({ id: 'rendering-engine-2' })), + }; + + const viewports = { + CT: [mockGridViewport, mockGridViewport2], + }; + + const mockSyncState = { + id: 'VOI_SYNC_CT', + }; + + mockCornerstoneViewportService.getCornerstoneViewport + .mockReturnValueOnce(mockViewport) + .mockReturnValueOnce(mockViewport2); + + mockSyncGroupService.getSynchronizersForViewport.mockReturnValue([mockSyncState]); + + toggleVOISliceSync({ + ...defaultParameters, + viewports, + }); + + expect(mockSyncGroupService.removeViewportFromSyncGroup).toHaveBeenCalledTimes(2); + expect(mockSyncGroupService.removeViewportFromSyncGroup).toHaveBeenNthCalledWith( + 1, + 'viewport-1', + 'rendering-engine-1', + 'VOI_SYNC_CT' + ); + expect(mockSyncGroupService.removeViewportFromSyncGroup).toHaveBeenNthCalledWith( + 2, + 'viewport-2', + 'rendering-engine-2', + 'VOI_SYNC_CT' + ); + }); + + it('should skip viewport during disable when cornerstone viewport is not found', () => { + const mockGridViewport2 = { + viewportOptions: { + viewportId: 'viewport-2', + }, + displaySetInstanceUIDs: ['displaySet-2'], + }; + + const viewports = { + CT: [mockGridViewport, mockGridViewport2], + }; + + const mockSyncState = { + id: 'VOI_SYNC_CT', + }; + + mockCornerstoneViewportService.getCornerstoneViewport + .mockReturnValueOnce(mockViewport) + .mockReturnValueOnce(null); + + mockSyncGroupService.getSynchronizersForViewport.mockReturnValue([mockSyncState]); + + toggleVOISliceSync({ + ...defaultParameters, + viewports, + }); + + expect(mockSyncGroupService.removeViewportFromSyncGroup).toHaveBeenCalledTimes(1); + expect(mockSyncGroupService.removeViewportFromSyncGroup).toHaveBeenCalledWith( + 'viewport-1', + 'rendering-engine-1', + 'VOI_SYNC_CT' + ); + }); + + it('should handle empty viewports object', () => { + const viewports = {}; + + toggleVOISliceSync({ + ...defaultParameters, + viewports, + }); + + expect(mockSyncGroupService.getSynchronizersForViewport).not.toHaveBeenCalled(); + expect(mockSyncGroupService.addViewportToSyncGroup).not.toHaveBeenCalled(); + expect(mockSyncGroupService.removeViewportFromSyncGroup).not.toHaveBeenCalled(); + }); + + it('should handle modality with empty viewport array', () => { + const viewports = { + CT: [], + }; + + toggleVOISliceSync({ + ...defaultParameters, + viewports, + }); + + expect(mockSyncGroupService.getSynchronizersForViewport).not.toHaveBeenCalled(); + expect(mockSyncGroupService.addViewportToSyncGroup).not.toHaveBeenCalled(); + expect(mockSyncGroupService.removeViewportFromSyncGroup).not.toHaveBeenCalled(); + }); + + it('should handle multiple displaySetInstanceUIDs by using first one', () => { + const mockGridViewportMultipleDisplaySets = { + viewportOptions: { + viewportId: 'viewport-1', + }, + displaySetInstanceUIDs: ['displaySet-1', 'displaySet-2', 'displaySet-3'], + }; + + mockViewportGridService.getState.mockReturnValue({ + viewports: new Map([['viewport-1', mockGridViewportMultipleDisplaySets]]), + }); + mockSyncGroupService.getSynchronizersForViewport.mockReturnValue([]); + mockDisplaySetService.getDisplaySetByUID.mockReturnValue(mockDisplaySet); + + toggleVOISliceSync({ + ...defaultParameters, + }); + + expect(mockDisplaySetService.getDisplaySetByUID).toHaveBeenCalledWith('displaySet-1'); + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenCalledWith( + 'viewport-1', + 'rendering-engine-1', + { + type: 'voi', + id: 'VOI_SYNC_CT', + source: true, + target: true, + } + ); + }); + + it('should group viewports by modality correctly when generating from grid', () => { + const mockViewportsMap = new Map([ + [ + 'viewport-ct-1', + { + viewportOptions: { + viewportId: 'viewport-ct-1', + }, + displaySetInstanceUIDs: ['displaySet-ct-1'], + }, + ], + [ + 'viewport-ct-2', + { + viewportOptions: { + viewportId: 'viewport-ct-2', + }, + displaySetInstanceUIDs: ['displaySet-ct-2'], + }, + ], + [ + 'viewport-mr-1', + { + viewportOptions: { + viewportId: 'viewport-mr-1', + }, + displaySetInstanceUIDs: ['displaySet-mr-1'], + }, + ], + ]); + + const mockState = { + viewports: mockViewportsMap, + }; + + const mockDisplaySetCT = { Modality: 'CT' }; + const mockDisplaySetMR = { Modality: 'MR' }; + const mockViewportCT1 = { + id: 'viewport-ct-1', + getRenderingEngine: jest.fn(() => ({ id: 'rendering-engine-ct-1' })), + }; + const mockViewportCT2 = { + id: 'viewport-ct-2', + getRenderingEngine: jest.fn(() => ({ id: 'rendering-engine-ct-2' })), + }; + const mockViewportMR1 = { + id: 'viewport-mr-1', + getRenderingEngine: jest.fn(() => ({ id: 'rendering-engine-mr-1' })), + }; + + mockViewportGridService.getState.mockReturnValue(mockState); + mockDisplaySetService.getDisplaySetByUID + .mockReturnValueOnce(mockDisplaySetCT) + .mockReturnValueOnce(mockDisplaySetCT) + .mockReturnValueOnce(mockDisplaySetMR); + + mockCornerstoneViewportService.getCornerstoneViewport + .mockReturnValueOnce(mockViewportCT1) + .mockReturnValueOnce(mockViewportCT2) + .mockReturnValueOnce(mockViewportMR1); + + mockSyncGroupService.getSynchronizersForViewport.mockReturnValue([]); + + toggleVOISliceSync({ + ...defaultParameters, + viewports: null, + }); + + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenCalledTimes(3); + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenNthCalledWith( + 1, + 'viewport-ct-1', + 'rendering-engine-ct-1', + { + type: 'voi', + id: 'VOI_SYNC_CT', + source: true, + target: true, + } + ); + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenNthCalledWith( + 2, + 'viewport-ct-2', + 'rendering-engine-ct-2', + { + type: 'voi', + id: 'VOI_SYNC_CT', + source: true, + target: true, + } + ); + expect(mockSyncGroupService.addViewportToSyncGroup).toHaveBeenNthCalledWith( + 3, + 'viewport-mr-1', + 'rendering-engine-mr-1', + { + type: 'voi', + id: 'VOI_SYNC_MR', + source: true, + target: true, + } + ); + }); +}); diff --git a/extensions/cornerstone/src/utils/toggleVOISliceSync.ts b/extensions/cornerstone/src/utils/toggleVOISliceSync.ts index 57e23d11630..11cc347a59a 100644 --- a/extensions/cornerstone/src/utils/toggleVOISliceSync.ts +++ b/extensions/cornerstone/src/utils/toggleVOISliceSync.ts @@ -46,7 +46,7 @@ export default function toggleVOISliceSync({ } syncGroupService.addViewportToSyncGroup(viewportId, viewport.getRenderingEngine().id, { type: 'voi', - id: syncIdToUse, + id: syncIdToUse as string, source: true, target: true, }); @@ -76,14 +76,18 @@ function groupViewportsByModality( viewportGridService: ViewportGridService, displaySetService: DisplaySetService ) { - let { viewports } = viewportGridService.getState(); + const { viewports } = viewportGridService.getState(); - viewports = [...viewports.values()]; + const viewportsArray = [...viewports.values()]; // group the viewports by modality - return viewports.reduce((acc, viewport) => { + return viewportsArray.reduce((acc, viewport) => { const { displaySetInstanceUIDs } = viewport; // Todo: add proper fusion support + // Fix: Skip processing if the viewport is empty. + if (!displaySetInstanceUIDs?.length) { + return acc; + } const displaySetInstanceUID = displaySetInstanceUIDs[0]; const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID); diff --git a/extensions/cornerstone/src/utils/transitions.test.ts b/extensions/cornerstone/src/utils/transitions.test.ts new file mode 100644 index 00000000000..65e5a1f61ad --- /dev/null +++ b/extensions/cornerstone/src/utils/transitions.test.ts @@ -0,0 +1,351 @@ +import { linear, ease, easeIn, easeOut, easeInOut, cubicBezier } from './transitions'; + +describe('Transitions Module', () => { + const EPSILON = 1e-6; + + /** + * Helper function to check if two numbers are approximately equal + */ + const approxEqual = (actual: number, expected: number, tolerance = EPSILON): boolean => { + return Math.abs(actual - expected) < tolerance; + }; + + describe('Standard CSS Easing (Mode 1: no baseline)', () => { + describe('linear', () => { + it('should return 0 at timeProgress=0', () => { + expect(linear(0)).toBe(0); + }); + + it('should return 1 at timeProgress=1', () => { + expect(linear(1)).toBe(1); + }); + + it('should return timeProgress for linear progression', () => { + expect(linear(0.25)).toBe(0.25); + expect(linear(0.5)).toBe(0.5); + expect(linear(0.75)).toBe(0.75); + }); + }); + + describe('easeInOut', () => { + it('should return 0 at timeProgress=0', () => { + expect(easeInOut(0)).toBe(0); + }); + + it('should return 1 at timeProgress=1', () => { + expect(easeInOut(1)).toBe(1); + }); + + it('should return 0.5 at timeProgress=0.5', () => { + const result = easeInOut(0.5); + expect(approxEqual(result, 0.5)).toBe(true); + }); + + it('should be slower at start than linear', () => { + const easedValue = easeInOut(0.2); + expect(easedValue).toBeLessThan(0.2); + }); + + it('should provide smoothing toward the end', () => { + const baseTime = 0.9; + const controlTimeProgressionDifference = 0.2; + + const result1 = easeInOut(baseTime); + const result2 = easeInOut(baseTime - controlTimeProgressionDifference); + + expect(result1 - result2).toBeLessThan(controlTimeProgressionDifference); + }); + }); + + describe('easeIn', () => { + it('should return 0 at timeProgress=0', () => { + expect(easeIn(0)).toBe(0); + }); + + it('should return 1 at timeProgress=1', () => { + expect(easeIn(1)).toBe(1); + }); + + it('should be slower at start', () => { + const easedValue = easeIn(0.4); + expect(easedValue).toBeLessThan(0.4); + }); + + it('should accelerate toward the end', () => { + const baseTime = 0.9; + const controlTimeProgressionDifference = 0.2; + + const result1 = easeIn(baseTime); + const result2 = easeIn(baseTime - controlTimeProgressionDifference); + + expect(result1 - result2).toBeGreaterThan(controlTimeProgressionDifference); + }); + }); + + describe('easeOut', () => { + it('should return 0 at timeProgress=0', () => { + expect(easeOut(0)).toBe(0); + }); + + it('should return 1 at timeProgress=1', () => { + expect(easeOut(1)).toBe(1); + }); + + it('should be faster at start', () => { + const easedValue = easeOut(0.4); + expect(easedValue).toBeGreaterThan(0.4); + }); + + it('should provide smoothing toward the end', () => { + const baseTime = 0.9; + const controlTimeProgressionDifference = 0.2; + + const result1 = ease(baseTime); + const result2 = ease(baseTime - controlTimeProgressionDifference); + + expect(result1 - result2).toBeLessThan(controlTimeProgressionDifference); + }); + }); + + describe('ease', () => { + it('should return 0 at timeProgress=0', () => { + expect(ease(0)).toBe(0); + }); + + it('should return 1 at timeProgress=1', () => { + expect(ease(1)).toBe(1); + }); + + it('should provide significant growth at first half', () => { + const result = ease(0.5); + expect(result).toBeGreaterThan(0.75); + }); + + it('should provide smoothing toward the end', () => { + const baseTime = 0.9; + const controlTimeProgressionDifference = 0.2; + + const result1 = ease(baseTime); + const result2 = ease(baseTime - controlTimeProgressionDifference); + + expect(result1 - result2).toBeLessThan(controlTimeProgressionDifference); + }); + }); + }); + + describe('Baseline Bell-Curve (Mode 2: baseline only)', () => { + const baseline = 0.2; + + describe('linear with baseline', () => { + it('should start and end at baseline', () => { + expect(linear(0, baseline)).toBe(baseline); + expect(linear(1, baseline)).toBe(baseline); + }); + + it('should peak at 1.0 at timeProgress=0.5', () => { + const result = linear(0.5, baseline); + expect(approxEqual(result, 1.0)).toBe(true); + }); + + it('should create symmetric bell curve', () => { + const quarterValue = linear(0.25, baseline); + const threeQuarterValue = linear(0.75, baseline); + expect(approxEqual(quarterValue, threeQuarterValue)).toBe(true); + }); + + it('should be between baseline and 1.0', () => { + for (let t = 0; t <= 1; t += 0.1) { + const result = linear(t, baseline); + expect(result).toBeGreaterThanOrEqual(baseline); + expect(result).toBeLessThanOrEqual(1.0); + } + }); + }); + + describe('easeInOut with baseline', () => { + it('should start and end at baseline', () => { + expect(easeInOut(0, baseline)).toBe(baseline); + expect(easeInOut(1, baseline)).toBe(baseline); + }); + + it('should peak near 1.0 around timeProgress=0.5', () => { + const result = easeInOut(0.5, baseline); + expect(result).toBeGreaterThan(0.8); + expect(result).toBeLessThanOrEqual(1.0); + }); + + it('should create bell curve with easing', () => { + const quarterValue = easeInOut(0.25, baseline); + const threeQuarterValue = easeInOut(0.75, baseline); + expect(approxEqual(quarterValue, threeQuarterValue, 0.01)).toBe(true); + }); + }); + + describe('all functions with baseline', () => { + const testBaseline = 0.3; + + it('should all start and end at baseline', () => { + const functions = [linear, ease, easeIn, easeOut, easeInOut]; + + functions.forEach(fn => { + expect(fn(0, testBaseline)).toBe(testBaseline); + expect(fn(1, testBaseline)).toBe(testBaseline); + }); + }); + }); + }); + + describe('Scaled Bell-Curve (Mode 3: baseline + scale)', () => { + const baseline = 2.0; + const scale = 3.0; + const expectedPeak = baseline * scale; + + describe('linear with baseline and scale', () => { + it('should start and end at baseline', () => { + expect(linear(0, baseline, scale)).toBe(baseline); + expect(linear(1, baseline, scale)).toBe(baseline); + }); + + it('should peak at baseline*scale at timeProgress=0.5', () => { + const result = linear(0.5, baseline, scale); + expect(approxEqual(result, expectedPeak)).toBe(true); + }); + + it('should create symmetric scaled bell curve', () => { + const quarterValue = linear(0.25, baseline, scale); + const threeQuarterValue = linear(0.75, baseline, scale); + expect(approxEqual(quarterValue, threeQuarterValue)).toBe(true); + }); + }); + + describe('easeInOut with baseline and scale', () => { + it('should start and end at baseline', () => { + expect(easeInOut(0, baseline, scale)).toBe(baseline); + expect(easeInOut(1, baseline, scale)).toBe(baseline); + }); + + it('should peak near baseline*scale around timeProgress=0.5', () => { + const result = easeInOut(0.5, baseline, scale); + expect(result).toBeGreaterThan(baseline + (expectedPeak - baseline) * 0.8); + expect(result).toBeLessThanOrEqual(expectedPeak); + }); + }); + + describe('different scale values', () => { + it('should handle scale < 1 (shrinking)', () => { + const shrinkScale = 0.5; + const result = linear(0.5, 1.0, shrinkScale); + expect(approxEqual(result, 0.5)).toBe(true); + }); + + it('should handle scale > 1 (growing)', () => { + const growScale = 2.5; + const result = linear(0.5, 1.0, growScale); + expect(approxEqual(result, 2.5)).toBe(true); + }); + + it('should handle scale = 1 (baseline to baseline)', () => { + const result = linear(0.5, 0.5, 1); + expect(approxEqual(result, 0.5)).toBe(true); + }); + }); + }); + + describe('Edge Cases', () => { + describe('baseline = 0 behavior', () => { + it('should behave like standard easing when baseline=0', () => { + expect(linear(0.5, 0)).toBe(linear(0.5)); + expect(easeInOut(0.5, 0)).toBe(easeInOut(0.5)); + }); + }); + + describe('extreme values', () => { + it('should handle negative baseline', () => { + const result = linear(0.5, -1.0, 2.0); + expect(approxEqual(result, -2.0)).toBe(true); + }); + + it('should handle large baseline values', () => { + const result = linear(0.5, 100, 1.5); + expect(approxEqual(result, 150)).toBe(true); + }); + }); + + describe('timeProgress edge cases', () => { + it('should handle timeProgress < 0', () => { + expect(linear(-0.1)).toBe(0); + expect(easeInOut(-0.1, 0.2)).toBe(0.2); + }); + + it('should handle timeProgress > 1', () => { + expect(linear(1.1)).toBe(1); + expect(easeInOut(1.1, 0.2)).toBe(0.2); + }); + }); + }); + + describe('Mathematical Properties', () => { + describe('bell curve symmetry for symmetric cubic bezier functions', () => { + it('should be symmetric around timeProgress=0.5', () => { + const baseline = 0.1; + const scale = 2.0; + + [linear, easeInOut].forEach(fn => { + for (let offset = 0.1; offset <= 0.4; offset += 0.1) { + const leftValue = fn(0.5 - offset, baseline, scale); + const rightValue = fn(0.5 + offset, baseline, scale); + expect(approxEqual(leftValue, rightValue, 0.01)).toBe(true); + } + }); + }); + }); + + describe('monotonicity in first half', () => { + it('should be non-decreasing from 0 to 0.5 for functions that grow under linear in first half', () => { + const baseline = 0.2; + + [linear, easeIn, easeInOut].forEach(fn => { + let prevValue = fn(0, baseline); + for (let time = 0.1; time <= 0.5; time += 0.1) { + const currentValue = fn(time, baseline); + expect(currentValue).toBeGreaterThanOrEqual(prevValue - EPSILON); + prevValue = currentValue; + } + }); + }); + }); + }); + + describe('Utility Functions', () => { + describe('cubicBezier', () => { + it('should create functions that return 0 at t=0 and 1 at t=1', () => { + const customEasing = cubicBezier(0.25, 0.1, 0.75, 0.9); + expect(customEasing(0)).toBe(0); + expect(customEasing(1)).toBe(1); + }); + + it('should throw error for invalid x values', () => { + expect(() => cubicBezier(-0.1, 0, 1, 1)).toThrow(); + expect(() => cubicBezier(0, 0, 1.1, 1)).toThrow(); + }); + + it('should fallback to binary search when Newton-Raphson fails due to small derivative', () => { + /** + * Create a cubic-bezier curve that has a very flat section (small derivative) + * This will cause Newton-Raphson to fail and use binary search fallback + * Using control points that create a nearly horizontal tangent + * Obs: Values obtained by imperative testing + */ + const problematicEasing = cubicBezier(0.98888, 0.00001, 0.00001, 0.98888); + + /** Test a value in the problematic region where derivative is very small*/ + const result = problematicEasing(0.5); + + expect(typeof result).toBe('number'); + expect(isNaN(result)).toBe(false); + expect(result).toBeGreaterThanOrEqual(0); + expect(result).toBeLessThanOrEqual(1); + }); + }); + }); +}); diff --git a/extensions/cornerstone/src/utils/transitions.ts b/extensions/cornerstone/src/utils/transitions.ts index 7d63f26f747..41991637d7d 100644 --- a/extensions/cornerstone/src/utils/transitions.ts +++ b/extensions/cornerstone/src/utils/transitions.ts @@ -1,63 +1,300 @@ +/** Cubic Bezier Implementation */ + /** - * It is a bell curved function that uses ease in out quadratic for css - * transition timing function for each side of the curve. - * - * @param {number} x - The current time, in the range [0, 1]. - * @param {number} baseline - The baseline value to start from and return to. - * @returns the value of the transition at time x. - */ -export function easeInOutBell(x: number, baseline: number): number { - const alpha = 1 - baseline; - - // prettier-ignore - if (x < 1 / 4) { - return 4 * Math.pow(2 * x, 3) * alpha + baseline; - } else if (x < 1 / 2) { - return (1 - Math.pow(-4 * x + 2, 3) / 2) * alpha + baseline; - } else if (x < 3 / 4) { - return (1 - Math.pow(4 * x - 2, 3) / 2) * alpha + baseline; - } else { - return (- 4 * Math.pow(2 * x - 2, 3)) * alpha + baseline; + * Finds the parameter t for a given x value on a cubic Bézier curve. + * Uses Newton's method first (fast), then falls back to binary search (reliable). + * Based on WebKit/Chromium's UnitBezier implementation. + * + * @param {number} targetX - The x value to find parameter t for + * @param {number} x1 - x-coordinate of first control point + * @param {number} x2 - x-coordinate of second control point + * @returns {number} The parameter t that gives the specified x value + */ +function solveCubicBezierX(targetX: number, x1: number, x2: number): number { + const epsilon = 1e-6; + + // Pre-compute polynomial coefficients for performance + const cx = 3.0 * x1; + const bx = 3.0 * (x2 - x1) - cx; + const ax = 1.0 - cx - bx; + + function sampleCurveX(t: number): number { + // Evaluate: ax*t³ + bx*t² + cx*t using Horner's rule + return ((ax * t + bx) * t + cx) * t; + } + + function sampleCurveDerivativeX(t: number): number { + return (3.0 * ax * t + 2.0 * bx) * t + cx; + } + + /** + * Newton's method - fast convergence with good initial guess + * Try this first as it's normally very fast + */ + function newtonRaphsonMethod(targetX: number): number | null { + let t = targetX; + + for (let iteration = 0; iteration < 8; iteration++) { + const currentX = sampleCurveX(t); + const error = currentX - targetX; + + if (Math.abs(error) < epsilon) { + return t; + } + + const derivative = sampleCurveDerivativeX(t); + + // Break if derivative is too small (avoid division by zero) + if (Math.abs(derivative) < epsilon) { + break; + } + + // Newton-Raphson step: t_new = t_old - f(t_old) / f'(t_old) + t = t - error / derivative; + } + + return null; + } + + /** + * Binary search fallback - guaranteed to converge + * Use this when Newton's method fails + */ + function binarySearchFallback(targetX: number): number { + let lowerBound = 0.0; + let upperBound = 1.0; + let t = targetX; + + while (lowerBound < upperBound) { + const currentX = sampleCurveX(t); + + if (Math.abs(currentX - targetX) < epsilon) { + return t; + } + + if (targetX > currentX) { + lowerBound = t; + } else { + upperBound = t; + } + + t = (upperBound - lowerBound) * 0.5 + lowerBound; + } + + return t; } + + const newtonResult = newtonRaphsonMethod(targetX); + if (newtonResult !== null) { + return newtonResult; + } + + return binarySearchFallback(targetX); } /** - * A reversed bell curved function that starts from 1 and goes to baseline and - * come back to 1 again. It uses ease in out quadratic for css transition - * timing function for each side of the curve. + * Evaluates the Y coordinate of a cubic Bézier curve at parameter t. + * Optimized for CSS timing functions where P0=(0,0) and P3=(1,1). + * Uses pre-computed polynomial coefficients and Horner's rule for performance. * - * @param {number} x - The current time, in the range [0, 1]. - * @param {number} baseline - The baseline value to start from and return to. - * @returns the value of the transition at time x. + * @param {number} t - Parameter value along the curve, typically in [0, 1] + * @param {number} y1 - y-coordinate of first control point + * @param {number} y2 - y-coordinate of second control point + * @returns {number} The Y value of the curve at parameter t */ -export function reverseEaseInOutBell(x: number, baseline: number): number { - const y = easeInOutBell(x, baseline); - return -y + 1 + baseline; +function sampleCurveY(t: number, y1: number, y2: number): number { + // Pre-compute polynomial coefficients for performance + const cy = 3.0 * y1; + const by = 3.0 * (y2 - y1) - cy; + const ay = 1.0 - cy - by; + + // Evaluate: ay*t³ + by*t² + cy*t using Horner's rule + return ((ay * t + by) * t + cy) * t; } -export function easeInOutBellRelative( - x: number, - baseline: number, - prevOutlineWidth: number -): number { - const range = baseline - prevOutlineWidth; - - if (x < 1 / 4) { - return prevOutlineWidth + 4 * Math.pow(2 * x, 3) * range; - } else if (x < 1 / 2) { - return prevOutlineWidth + (1 - Math.pow(-4 * x + 2, 3) / 2) * range; - } else if (x < 3 / 4) { - return prevOutlineWidth + (1 - Math.pow(4 * x - 2, 3) / 2) * range; - } else { - return prevOutlineWidth + -4 * Math.pow(2 * x - 2, 3) * range; +/** + * Cubic Bézier easing function implementation following CSS specifications. + * + * A cubic Bézier curve is defined by four points: P0, P1, P2, and P3. + * In CSS animations, P0 is fixed at (0, 0) and P3 is fixed at (1, 1). + * This function allows you to specify the intermediate control points P1 and P2. + * + * @param {number} x1 - x-coordinate of the first control point P1 (must be in [0, 1]) + * @param {number} y1 - y-coordinate of the first control point P1 + * @param {number} x2 - x-coordinate of the second control point P2 (must be in [0, 1]) + * @param {number} y2 - y-coordinate of the second control point P2 + * @returns {function} A function that takes time t ∈ [0, 1] and returns the eased value + */ +export function cubicBezier(x1: number, y1: number, x2: number, y2: number) { + if (x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1) { + throw new Error('x1 and x2 must be in the range [0, 1]'); } + + return function (timeProgress: number): number { + if (timeProgress <= 0) return 0; + if (timeProgress >= 1) return 1; + + if (x1 === y1 && x2 === y2) { + return timeProgress; + } + + const curveParameter = solveCubicBezierX(timeProgress, x1, x2); + return sampleCurveY(curveParameter, y1, y2); + }; } -export function reverseEaseInOutBellRelative( - x: number, - baseline: number, - prevOutlineWidth: number -): number { - const y = easeInOutBellRelative(x, baseline, prevOutlineWidth); - return y; +/** Core Easing Functions */ + +/** + * Linear easing function - constant speed throughout the animation. + * Equivalent to CSS: cubic-bezier(0.0, 0.0, 1.0, 1.0) + * + * @param {number} timeProgress - The animation progress, in the range [0, 1]. + * @returns {number} The linear eased value. + */ +const linearCore = cubicBezier(0.0, 0.0, 1.0, 1.0); + +/** + * Ease-in easing function - starts slow and accelerates. + * Equivalent to CSS: cubic-bezier(0.42, 0, 1.0, 1.0) + * + * @param {number} timeProgress - The animation progress, in the range [0, 1]. + * @returns {number} The eased value. + */ +const easeInCore = cubicBezier(0.42, 0, 1.0, 1.0); + +/** + * Ease-out easing function - starts fast and decelerates. + * Equivalent to CSS: cubic-bezier(0, 0, 0.58, 1.0) + * + * @param {number} timeProgress - The animation progress, in the range [0, 1]. + * @returns {number} The eased value. + */ +const easeOutCore = cubicBezier(0, 0, 0.58, 1.0); + +/** + * Ease-in-out easing function - starts slow, accelerates, then decelerates. + * Equivalent to CSS: cubic-bezier(0.42, 0, 0.58, 1.0) + * + * @param {number} timeProgress - The animation progress, in the range [0, 1]. + * @returns {number} The eased value. + */ +const easeInOutCore = cubicBezier(0.42, 0, 0.58, 1.0); + +/** + * Standard ease easing function (CSS default). + * Equivalent to CSS: cubic-bezier(0.25, 0.1, 0.25, 1.0) + * + * @param {number} timeProgress - The animation progress, in the range [0, 1]. + * @returns {number} The eased value. + */ +const easeCore = cubicBezier(0.25, 0.1, 0.25, 1.0); + +/** Flexible Easing Function Factory */ + +/** + * Flexible factory function that creates easing functions with optional baseline and scale support. + * Provides bell-curve behavior: baseline → baseline*scale → baseline + * + * @param {Function} coreEasingFn - The core easing function to wrap + * @returns {Function} A function that accepts (timeProgress, baseline?, scale?) and provides flexible behavior + */ +function flexibleEasingFunctionFactory(coreEasingFn: (timeProgress: number) => number) { + return function (timeProgress: number, baseline: number = 0, scale?: number): number { + if (baseline === 0) { + return coreEasingFn(timeProgress); + } + + const easedProgress = coreEasingFn(timeProgress); + + const targetValue = scale ? baseline * scale : 1; + const range = targetValue - baseline; + + // Create bell-curve: baseline → targetValue → baseline + const bellMultiplier = 1 - Math.abs(2 * easedProgress - 1); + return baseline + bellMultiplier * range; + }; +} + +/** + * Linear easing function with optional baseline and scale support. + * - No params: standard linear progression [0, 1] + * - With baseline only: bell-curve baseline → 1 → baseline + * - With baseline+scale: bell-curve baseline → baseline*scale → baseline + * + * @param {number} timeProgress - The animation progress, in the range [0, 1]. + * @param {number} baseline - Optional baseline value (default: 0). Creates bell-curve effect. + * @param {number} scale - Optional scale multiplier (default: 1). Peak value = baseline * scale. + * @returns {number} The linear eased value. + */ +export const linear = flexibleEasingFunctionFactory(linearCore); + +/** + * Standard ease easing function with optional baseline and scale support. + * - No params: standard ease progression [0, 1] + * - With baseline only: bell-curve baseline → 1 → baseline + * - With baseline+scale: bell-curve baseline → baseline*scale → baseline + * + * @param {number} timeProgress - The animation progress, in the range [0, 1]. + * @param {number} baseline - Optional baseline value (default: 0). Creates bell-curve effect. + * @param {number} scale - Optional scale multiplier (default: 1). Peak value = baseline * scale. + * @returns {number} The eased value. + */ +export const ease = flexibleEasingFunctionFactory(easeCore); + +/** + * Ease-in easing function with optional baseline and scale support. + * - No params: standard ease-in progression [0, 1] + * - With baseline only: bell-curve baseline → 1 → baseline + * - With baseline+scale: bell-curve baseline → baseline*scale → baseline + * + * @param {number} timeProgress - The animation progress, in the range [0, 1]. + * @param {number} baseline - Optional baseline value (default: 0). Creates bell-curve effect. + * @param {number} scale - Optional scale multiplier (default: 1). Peak value = baseline * scale. + * @returns {number} The eased value. + */ +export const easeIn = flexibleEasingFunctionFactory(easeInCore); + +/** + * Ease-out easing function with optional baseline and scale support. + * - No params: standard ease-out progression [0, 1] + * - With baseline only: bell-curve baseline → 1 → baseline + * - With baseline+scale: bell-curve baseline → baseline*scale → baseline + * + * @param {number} timeProgress - The animation progress, in the range [0, 1]. + * @param {number} baseline - Optional baseline value (default: 0). Creates bell-curve effect. + * @param {number} scale - Optional scale multiplier (default: 1). Peak value = baseline * scale. + * @returns {number} The eased value. + */ +export const easeOut = flexibleEasingFunctionFactory(easeOutCore); + +/** + * Ease-in-out easing function with optional baseline and scale support. + * - No params: standard ease-in-out progression [0, 1] + * - With baseline only: bell-curve baseline → 1 → baseline + * - With baseline+scale: bell-curve baseline → baseline*scale → baseline + * + * @param {number} timeProgress - The animation progress, in the range [0, 1]. + * @param {number} baseline - Optional baseline value (default: 0). Creates bell-curve effect. + * @param {number} scale - Optional scale multiplier (default: 1). Peak value = baseline * scale. + * @returns {number} The eased value. + */ +export const easeInOut = flexibleEasingFunctionFactory(easeInOutCore); + +/** Export interfaces */ + +export enum EasingFunctionEnum { + EASE = 'ease', + EASE_IN = 'ease-in', + EASE_OUT = 'ease-out', + EASE_IN_OUT = 'ease-in-out', + LINEAR = 'linear', } + +export const EasingFunctionMap = new Map([ + [EasingFunctionEnum.EASE, ease], + [EasingFunctionEnum.EASE_IN, easeIn], + [EasingFunctionEnum.EASE_OUT, easeOut], + [EasingFunctionEnum.EASE_IN_OUT, easeInOut], + [EasingFunctionEnum.LINEAR, linear], +]); diff --git a/extensions/cornerstone/src/utils/updateSegmentationStats.test.ts b/extensions/cornerstone/src/utils/updateSegmentationStats.test.ts new file mode 100644 index 00000000000..a377ed63de7 --- /dev/null +++ b/extensions/cornerstone/src/utils/updateSegmentationStats.test.ts @@ -0,0 +1,942 @@ +import { Types as cstTypes, utilities as cstUtilities } from '@cornerstonejs/tools'; +import { + updateSegmentationStats, + updateSegmentBidirectionalStats, +} from './updateSegmentationStats'; + +jest.mock('@cornerstonejs/tools', () => ({ + utilities: { + segmentation: { + getStatistics: jest.fn(), + }, + }, +})); + +describe('updateSegmentationStats', () => { + const mockSegmentation = { + segments: { + '1': { + segmentIndex: 1, + label: 'Segment 1', + }, + '2': { + segmentIndex: 2, + label: 'Segment 2', + }, + }, + }; + + const mockReadableText = { + mean: 'Mean', + stdDev: 'Standard Deviation', + volume: 'Volume', + max: 'Maximum', + }; + + const mockStats = { + '1': { + array: [ + { + name: 'mean', + value: 100.5, + unit: 'HU', + }, + ], + } as cstTypes.NamedStatistics, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return null if no segmentation is provided', async () => { + const result = await updateSegmentationStats({ + segmentation: null, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result).toBe(null); + expect(cstUtilities.segmentation.getStatistics).not.toHaveBeenCalled(); + }); + + it('should return null if segmentation is undefined', async () => { + const result = await updateSegmentationStats({ + segmentation: undefined, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result).toBe(null); + }); + + it('should return null if no segments found (empty segments)', async () => { + const segmentationWithEmptySegments = { + segments: {}, + }; + + const result = await updateSegmentationStats({ + segmentation: segmentationWithEmptySegments, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result).toBe(null); + expect(cstUtilities.segmentation.getStatistics).not.toHaveBeenCalled(); + }); + + it('should return null if no segments found (only segment 0)', async () => { + const segmentationWithOnlyBackground = { + segments: { + '0': { + segmentIndex: 0, + label: 'Background', + }, + }, + }; + + const result = await updateSegmentationStats({ + segmentation: segmentationWithOnlyBackground, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result).toBe(null); + expect(cstUtilities.segmentation.getStatistics).not.toHaveBeenCalled(); + }); + + it('should return null if getStatistics returns null', async () => { + jest.spyOn(cstUtilities.segmentation, 'getStatistics').mockResolvedValue(null); + + const result = await updateSegmentationStats({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(cstUtilities.segmentation.getStatistics).toHaveBeenCalledWith({ + segmentationId: 'test-id', + segmentIndices: [1, 2], + mode: 'individual', + }); + expect(result).toBe(null); + }); + + it('should return null if getStatistics returns undefined', async () => { + jest.spyOn(cstUtilities.segmentation, 'getStatistics').mockResolvedValue(undefined); + + const result = await updateSegmentationStats({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result).toBe(null); + }); + + it('should initialize cachedStats if not present', async () => { + jest.spyOn(cstUtilities.segmentation, 'getStatistics').mockResolvedValue(mockStats); + + const segmentationWithoutCachedStats = { + segments: { + '1': { + segmentIndex: 1, + label: 'Segment 1', + }, + }, + }; + + const result = await updateSegmentationStats({ + segmentation: segmentationWithoutCachedStats, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result.segments['1'].cachedStats).toBeDefined(); + expect(result.segments['1'].cachedStats.namedStats).toBeDefined(); + expect(result.segments['1'].cachedStats.namedStats.mean).toEqual({ + name: mockStats['1'].array[0].name, + label: mockReadableText.mean, + value: mockStats['1'].array[0].value, + unit: mockStats['1'].array[0].unit, + order: 0, + }); + }); + + it('should preserve existing cachedStats and add namedStats', async () => { + jest.spyOn(cstUtilities.segmentation, 'getStatistics').mockResolvedValue(mockStats); + + const segmentationWithExistingCachedStats = { + segments: { + '1': { + segmentIndex: 1, + label: 'Segment 1', + cachedStats: { + existingData: 'test', + }, + }, + }, + }; + + const result = await updateSegmentationStats({ + segmentation: segmentationWithExistingCachedStats, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result.segments['1'].cachedStats.existingData).toBe('test'); + expect(result.segments['1'].cachedStats.namedStats.mean).toEqual({ + name: mockStats['1'].array[0].name, + label: mockReadableText.mean, + value: mockStats['1'].array[0].value, + unit: mockStats['1'].array[0].unit, + order: 0, + }); + }); + + it('should preserve existing namedStats and merge with new stats', async () => { + jest.spyOn(cstUtilities.segmentation, 'getStatistics').mockResolvedValue(mockStats); + + const segmentationWithExistingNamedStats = { + segments: { + '1': { + segmentIndex: 1, + label: 'Segment 1', + cachedStats: { + namedStats: { + existingStat: { + name: 'existingStat', + value: 'existing', + }, + }, + }, + }, + }, + }; + + const result = await updateSegmentationStats({ + segmentation: segmentationWithExistingNamedStats, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result.segments['1'].cachedStats.namedStats.existingStat).toEqual({ + name: 'existingStat', + value: 'existing', + }); + expect(result.segments['1'].cachedStats.namedStats.mean).toEqual({ + name: mockStats['1'].array[0].name, + label: mockReadableText.mean, + value: mockStats['1'].array[0].value, + unit: mockStats['1'].array[0].unit, + order: 0, + }); + }); + + it('should skip stats not in readableText', async () => { + const mockStatsWithUnknownStat = { + '1': { + array: [ + ...mockStats['1'].array, + { + name: 'unknownStat', + value: 50, + unit: 'mm', + }, + ], + } as cstTypes.NamedStatistics, + }; + + jest + .spyOn(cstUtilities.segmentation, 'getStatistics') + .mockResolvedValue(mockStatsWithUnknownStat); + + const result = await updateSegmentationStats({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result.segments['1'].cachedStats.namedStats.mean).toBeDefined(); + expect(result.segments['1'].cachedStats.namedStats.unknownStat).toBeUndefined(); + }); + + it('should skip stats with invalid name', async () => { + const mockStatsWithInvalidName = { + '1': { + array: [ + ...mockStats['1'].array, + { + name: null, + value: 50, + unit: 'mm', + }, + { + value: 25, + unit: 'cm', + }, + ], + } as cstTypes.NamedStatistics, + }; + + jest + .spyOn(cstUtilities.segmentation, 'getStatistics') + .mockResolvedValue(mockStatsWithInvalidName); + + const result = await updateSegmentationStats({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result.segments['1'].cachedStats.namedStats.mean).toBeDefined(); + expect(Object.keys(result.segments['1'].cachedStats.namedStats)).toHaveLength(1); + }); + + it('should add volume stat when volume exists in segmentStats and readableText', async () => { + console.log('mockSegmentation', mockSegmentation); + + const mockStatsWithVolume = { + '1': { + array: [...mockStats['1'].array], + volume: { + value: 250.75, + unit: 'mm³', + }, + } as cstTypes.NamedStatistics, + }; + + jest.spyOn(cstUtilities.segmentation, 'getStatistics').mockResolvedValue(mockStatsWithVolume); + + const result = await updateSegmentationStats({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result.segments['1'].cachedStats.namedStats.volume).toEqual({ + name: 'volume', + label: mockReadableText.volume, + value: mockStatsWithVolume['1'].volume.value, + unit: mockStatsWithVolume['1'].volume.unit, + order: 2, + }); + }); + + it('should not add volume stat when volume not in readableText', async () => { + console.log('mockSegmentation', mockSegmentation); + + const mockStatsWithVolume = { + '1': { + array: [...mockStats['1'].array], + volume: { + value: 250.75, + unit: 'mm³', + }, + } as cstTypes.NamedStatistics, + }; + + jest.spyOn(cstUtilities.segmentation, 'getStatistics').mockResolvedValue(mockStatsWithVolume); + + const readableTextWithoutVolume = { + mean: 'Mean', + stdDev: 'Standard Deviation', + }; + + const result = await updateSegmentationStats({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: readableTextWithoutVolume, + }); + + expect(result.segments['1'].cachedStats.namedStats.volume).toBeUndefined(); + }); + + it('should not add volume stat when volume already exists in namedStats', async () => { + const mockStatsWithVolume = { + '1': { + array: [ + { + name: 'volume', + value: 200.5, + unit: 'mm³', + }, + ], + volume: { + value: 250.75, + unit: 'mm³', + }, + } as cstTypes.NamedStatistics, + }; + + jest.spyOn(cstUtilities.segmentation, 'getStatistics').mockResolvedValue(mockStatsWithVolume); + + const result = await updateSegmentationStats({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result.segments['1'].cachedStats.namedStats.volume).toEqual({ + name: 'volume', + label: mockReadableText.volume, + value: mockStatsWithVolume['1'].array[0].value, + unit: mockStatsWithVolume['1'].volume.unit, + order: 2, + }); + }); + + it('should not add volume stat when segmentStats.volume is missing', async () => { + jest.spyOn(cstUtilities.segmentation, 'getStatistics').mockResolvedValue(mockStats); + + const result = await updateSegmentationStats({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result.segments['1'].cachedStats.namedStats.volume).toBeUndefined(); + }); + + it('should handle multiple segments with different stats', async () => { + const mockStatsWithMax = { + '1': { + array: [ + ...mockStats['1'].array, + { + name: 'max', + value: 200, + unit: 'HU', + }, + ], + } as cstTypes.NamedStatistics, + '2': { + array: [ + { + name: 'stdDev', + value: 15.2, + unit: 'HU', + }, + ], + volume: { + value: 150.25, + unit: 'mm³', + }, + } as cstTypes.NamedStatistics, + }; + + jest.spyOn(cstUtilities.segmentation, 'getStatistics').mockResolvedValue(mockStatsWithMax); + + const result = await updateSegmentationStats({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result.segments['1'].cachedStats.namedStats.mean).toEqual({ + name: mockStatsWithMax['1'].array[0].name, + label: mockReadableText.mean, + value: mockStatsWithMax['1'].array[0].value, + unit: mockStatsWithMax['1'].array[0].unit, + order: 0, + }); + expect(result.segments['1'].cachedStats.namedStats.max).toEqual({ + name: mockStatsWithMax['1'].array[1].name, + label: mockReadableText.max, + value: mockStatsWithMax['1'].array[1].value, + unit: mockStatsWithMax['1'].array[1].unit, + order: 3, + }); + expect(result.segments['2'].cachedStats.namedStats.stdDev).toEqual({ + name: mockStatsWithMax['2'].array[0].name, + label: mockReadableText.stdDev, + value: mockStatsWithMax['2'].array[0].value, + unit: mockStatsWithMax['2'].array[0].unit, + order: 1, + }); + expect(result.segments['2'].cachedStats.namedStats.volume).toEqual({ + name: 'volume', + label: mockReadableText.volume, + value: mockStatsWithMax['2'].volume.value, + unit: mockStatsWithMax['2'].volume.unit, + order: 2, + }); + }); + + it('should add empty namedStats when no stats are returned(empty array)', async () => { + const mockStatsWithEmptyArray = { + '1': { + array: [], + } as cstTypes.NamedStatistics, + }; + + jest + .spyOn(cstUtilities.segmentation, 'getStatistics') + .mockResolvedValue(mockStatsWithEmptyArray); + + const result = await updateSegmentationStats({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result.segments['1'].cachedStats.namedStats).toEqual({}); + }); + + it('should only initialize namedStats when no matching stats are found', async () => { + const mockStatsWithUnknownStat = { + '1': { + array: [ + { + name: 'unknownStat', + value: 100, + unit: 'unit', + }, + ], + } as cstTypes.NamedStatistics, + }; + + jest + .spyOn(cstUtilities.segmentation, 'getStatistics') + .mockResolvedValue(mockStatsWithUnknownStat); + + const result = await updateSegmentationStats({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result.segments['1'].cachedStats.namedStats).toEqual({}); + }); + + it('should handle segments without array property by initializing cachedStats', async () => { + const mockStatsWithNoArray = { + '1': {} as cstTypes.NamedStatistics, + }; + + jest.spyOn(cstUtilities.segmentation, 'getStatistics').mockResolvedValue(mockStatsWithNoArray); + + const result = await updateSegmentationStats({ + segmentation: mockSegmentation, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result.segments['1'].cachedStats).toEqual({}); + }); + + it('should not update segment if cachedStats is already initialized and no matching stats are found', async () => { + const mockSegmentationWithCachedStats = { + segments: { + '1': { + segmentIndex: 1, + label: 'Segment 1', + cachedStats: { + namedStats: { + existingStat: { + name: 'existing', + value: 'test', + }, + }, + }, + }, + }, + }; + + const mockStatsWithNoArray = { + '1': {} as cstTypes.NamedStatistics, + }; + + jest.spyOn(cstUtilities.segmentation, 'getStatistics').mockResolvedValue(mockStatsWithNoArray); + + const result = await updateSegmentationStats({ + segmentation: mockSegmentationWithCachedStats, + segmentationId: 'test-id', + readableText: mockReadableText, + }); + + expect(result).toBe(null); + }); +}); + +describe('updateSegmentBidirectionalStats', () => { + const mockSegmentationService = { + getSegmentation: jest.fn(), + }; + + const mockAnnotation = { + annotationUID: 'test-annotation-uid', + }; + + const mockBidirectionalData = { + majorAxis: { length: 20.5 }, + minorAxis: { length: 15.3 }, + maxMajor: 20.5, + maxMinor: 15.3, + }; + + const defaultParameters = { + segmentationId: 'test-id', + segmentIndex: 1, + bidirectionalData: mockBidirectionalData, + // @ts-expect-error - only part of the SegmentationService is needed + segmentationService: mockSegmentationService as AppTypes.SegmentationService, + annotation: mockAnnotation, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return null when segmentationId is missing', () => { + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + segmentationId: '', + }); + + expect(result).toBe(null); + expect(mockSegmentationService.getSegmentation).not.toHaveBeenCalled(); + }); + + it('should return null when segmentationId is null', () => { + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + segmentationId: null, + }); + + expect(result).toBe(null); + }); + + it('should return null when segmentIndex is undefined', () => { + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + segmentIndex: undefined, + }); + + expect(result).toBe(null); + }); + + it('should return null when bidirectionalData is missing', () => { + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + bidirectionalData: null, + }); + + expect(result).toBe(null); + }); + + it('should return null when segmentation is not found', () => { + mockSegmentationService.getSegmentation.mockReturnValue(null); + + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + }); + + expect(mockSegmentationService.getSegmentation).toHaveBeenCalledWith('test-id'); + expect(result).toBe(null); + }); + + it('should return null when segment is not found', () => { + const mockSegmentation = { + segments: {}, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + }); + + expect(result).toBe(null); + }); + + it('should return null when majorAxis is missing', () => { + const mockSegmentation = { + segments: { + 1: { + segmentIndex: 1, + label: 'Segment 1', + }, + }, + }; + + const bidirectionalDataWithoutMajorAxis = { + majorAxis: null, + minorAxis: { length: 15.3 }, + maxMajor: 20.5, + maxMinor: 15.3, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + bidirectionalData: bidirectionalDataWithoutMajorAxis, + }); + + expect(result).toBe(null); + }); + + it('should return null when minorAxis is missing', () => { + const mockSegmentation = { + segments: { + 1: { + segmentIndex: 1, + label: 'Segment 1', + }, + }, + }; + + const bidirectionalDataWithoutMinorAxis = { + majorAxis: { length: 20.5 }, + minorAxis: null, + maxMajor: 20.5, + maxMinor: 15.3, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + bidirectionalData: bidirectionalDataWithoutMinorAxis, + }); + + expect(result).toBe(null); + }); + + it('should return null when maxMajor is zero', () => { + const mockSegmentation = { + segments: { + 1: { + segmentIndex: 1, + label: 'Segment 1', + }, + }, + }; + + const bidirectionalDataWithZeroMajor = { + majorAxis: { length: 20.5 }, + minorAxis: { length: 15.3 }, + maxMajor: 0, + maxMinor: 15.3, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + bidirectionalData: bidirectionalDataWithZeroMajor, + }); + + expect(result).toBe(null); + }); + + it('should return null when maxMinor is zero', () => { + const mockSegmentation = { + segments: { + 1: { + segmentIndex: 1, + label: 'Segment 1', + }, + }, + }; + + const bidirectionalDataWithZeroMinor = { + majorAxis: { length: 20.5 }, + minorAxis: { length: 15.3 }, + maxMajor: 20.5, + maxMinor: 0, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + bidirectionalData: bidirectionalDataWithZeroMinor, + }); + + expect(result).toBe(null); + }); + + it('should return null when maxMajor is negative', () => { + const mockSegmentation = { + segments: { + 1: { + segmentIndex: 1, + label: 'Segment 1', + }, + }, + }; + + const bidirectionalDataWithNegativeMajor = { + majorAxis: { length: 20.5 }, + minorAxis: { length: 15.3 }, + maxMajor: -5, + maxMinor: 15.3, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + bidirectionalData: bidirectionalDataWithNegativeMajor, + }); + + expect(result).toBe(null); + }); + + it('should initialize cachedStats when segment has no cachedStats', () => { + const mockSegmentation = { + segments: { + 1: { + segmentIndex: 1, + label: 'Segment 1', + }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + }); + + expect(result.segments[1].cachedStats).toEqual({ + namedStats: { + bidirectional: { + name: 'bidirectional', + label: 'Bidirectional', + annotationUID: 'test-annotation-uid', + value: { + ...mockBidirectionalData, + }, + unit: 'mm', + }, + }, + }); + }); + + it('should initialize namedStats when segment has cachedStats but no namedStats', () => { + const mockSegmentation = { + segments: { + 1: { + segmentIndex: 1, + label: 'Segment 1', + cachedStats: { + existingData: 'test', + }, + }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + }); + + expect(result.segments[1].cachedStats.existingData).toBe('test'); + expect(result.segments[1].cachedStats.namedStats).toEqual({ + bidirectional: { + name: 'bidirectional', + label: 'Bidirectional', + annotationUID: 'test-annotation-uid', + value: { + ...mockBidirectionalData, + }, + unit: 'mm', + }, + }); + }); + + it('should add bidirectional stats to existing namedStats', () => { + const mockSegmentation = { + segments: { + 1: { + segmentIndex: 1, + label: 'Segment 1', + cachedStats: { + namedStats: { + existingStat: { + name: 'existing', + value: 'test', + }, + }, + }, + }, + }, + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + }); + + expect( + (result.segments[1].cachedStats.namedStats as { existingStat: cstTypes.NamedStatistics }) + .existingStat + ).toEqual({ + name: 'existing', + value: 'test', + }); + expect( + (result.segments[1].cachedStats.namedStats as { bidirectional: cstTypes.NamedStatistics }) + .bidirectional + ).toEqual({ + name: 'bidirectional', + label: 'Bidirectional', + annotationUID: 'test-annotation-uid', + value: { + ...mockBidirectionalData, + }, + unit: 'mm', + }); + }); + + it('should update bidirectional stats with correct data structure', () => { + const mockSegmentation = { + segments: { + 1: { + segmentIndex: 1, + label: 'Segment 1', + cachedStats: { + namedStats: {}, + }, + }, + }, + }; + + const customBidirectionalData = { + majorAxis: { length: 25.7 }, + minorAxis: { length: 18.9 }, + maxMajor: 25.7, + maxMinor: 18.9, + }; + + const customAnnotation = { + annotationUID: 'custom-annotation-uid', + }; + + mockSegmentationService.getSegmentation.mockReturnValue(mockSegmentation); + + const result = updateSegmentBidirectionalStats({ + ...defaultParameters, + bidirectionalData: customBidirectionalData, + annotation: customAnnotation, + }); + + expect( + (result.segments[1].cachedStats.namedStats as { bidirectional: cstTypes.NamedStatistics }) + .bidirectional + ).toEqual({ + name: 'bidirectional', + label: 'Bidirectional', + annotationUID: 'custom-annotation-uid', + value: { + ...customBidirectionalData, + }, + unit: 'mm', + }); + }); +}); diff --git a/extensions/cornerstone/src/utils/updateSegmentationStats.ts b/extensions/cornerstone/src/utils/updateSegmentationStats.ts index 936c822b49c..ffc1dbca1cd 100644 --- a/extensions/cornerstone/src/utils/updateSegmentationStats.ts +++ b/extensions/cornerstone/src/utils/updateSegmentationStats.ts @@ -1,4 +1,5 @@ import * as cornerstoneTools from '@cornerstonejs/tools'; +import cloneDeep from 'lodash.clonedeep'; interface BidirectionalAxis { length: number; @@ -51,13 +52,19 @@ export async function updateSegmentationStats({ return null; } - const updatedSegmentation = { ...segmentation }; + const updatedSegmentation = cloneDeep(segmentation); let hasUpdates = false; // Loop through each segment's stats Object.entries(stats).forEach(([segmentIndex, segmentStats]) => { const index = parseInt(segmentIndex); + if (!updatedSegmentation.segments[index]) { + // This happens when a segment is being restored + console.warn('Segment not found to update cached stats:', index); + return; + } + if (!updatedSegmentation.segments[index].cachedStats) { updatedSegmentation.segments[index].cachedStats = {}; hasUpdates = true; diff --git a/extensions/default/CHANGELOG.md b/extensions/default/CHANGELOG.md index a8933be5cf2..70c35c0cea6 100644 --- a/extensions/default/CHANGELOG.md +++ b/extensions/default/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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-default @@ -11,7 +11,1126 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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-default + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [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-default + + + + + +# [3.12.0-beta.126](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.125...v3.12.0-beta.126) (2026-01-22) + + +### Bug Fixes + +* the null value check issue in image spacing calculation ([#5726](https://github.com/OHIF/Viewers/issues/5726)) ([afe184e](https://github.com/OHIF/Viewers/commit/afe184ec78dcc8f780e64687e06670365beeb77e)) + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [3.12.0-beta.119](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.118...v3.12.0-beta.119) (2026-01-08) + + +### Bug Fixes + +* **DicomTagBrowser:** Check for null metadata. ([#5696](https://github.com/OHIF/Viewers/issues/5696)) ([6429a55](https://github.com/OHIF/Viewers/commit/6429a5518a56f7fbd9d948510c52e0d467c9ca57)) + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [3.12.0-beta.116](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.115...v3.12.0-beta.116) (2026-01-06) + + +### Bug Fixes + +* NM multiframe with TimeSlotVector ([#5666](https://github.com/OHIF/Viewers/issues/5666)) ([a74648f](https://github.com/OHIF/Viewers/commit/a74648f8ec23c01b91588ff7c6ee1620ca456ed7)) + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [3.12.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.101...v3.12.0-beta.102) (2025-12-10) + + +### Bug Fixes + +* **DataSource:** migrate configuration dialog to ui-next components ([#5626](https://github.com/OHIF/Viewers/issues/5626)) ([c0f39ae](https://github.com/OHIF/Viewers/commit/c0f39aeb51dc5abad6381706eaa0ac69a54979f2)) + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [3.12.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.98...v3.12.0-beta.99) (2025-12-04) + + +### Bug Fixes + +* Initial sort not consistent ([#5224](https://github.com/OHIF/Viewers/issues/5224)) ([77f9f8e](https://github.com/OHIF/Viewers/commit/77f9f8e1c4af6b5d22ecf9332b5edf503cd5b848)) + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [3.12.0-beta.41](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.40...v3.12.0-beta.41) (2025-10-03) + + +### Bug Fixes + +* study browser mode for multiple studies ([#5457](https://github.com/OHIF/Viewers/issues/5457)) ([6a1df9d](https://github.com/OHIF/Viewers/commit/6a1df9de4ad3c10a4653f18e03aff6bda4962fd1)) + + + + + +# [3.12.0-beta.40](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.39...v3.12.0-beta.40) (2025-10-02) + + +### Bug Fixes + +* **studyBrowserCustomization:** fix thumbnail menu item actions in study browser ([#5283](https://github.com/OHIF/Viewers/issues/5283)) ([2f28a0d](https://github.com/OHIF/Viewers/commit/2f28a0deab5e43f432bbd9b5989928c9b0450278)) + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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 @ohif/extension-default + + + + + +# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25) + + +### Bug Fixes + +* **i18n:** Add and update i18n Translation for Toolbar Tool Names ([#5392](https://github.com/OHIF/Viewers/issues/5392)) ([7783d0f](https://github.com/OHIF/Viewers/commit/7783d0f558bbb7016c29513b6fe76ed069dbd75c)) + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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 @ohif/extension-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [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-default + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + + +### Bug Fixes + +* Add seriesInstanceUID fallback for callByRetrieveAETitle ([#5332](https://github.com/OHIF/Viewers/issues/5332)) ([840b5d3](https://github.com/OHIF/Viewers/commit/840b5d37d15537abc38316d636da7c144e6d9223)) + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + + +### Bug Fixes + +* Guard series.instances in DicomJSONDataSource to prevent TypeError (OHI-2114) ([#5312](https://github.com/OHIF/Viewers/issues/5312)) ([f1d158b](https://github.com/OHIF/Viewers/commit/f1d158bbe81fd9f6052ce23c8588c9c93d76360c)) + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/extension-default + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/extension-default diff --git a/extensions/default/package.json b/extensions/default/package.json index 40137936285..ace6e503633 100644 --- a/extensions/default/package.json +++ b/extensions/default/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-default", - "version": "3.11.1", + "version": "3.12.0", "description": "Common/default features and functionality for basic image viewing", "author": "OHIF Core Team", "license": "MIT", @@ -34,9 +34,9 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/i18n": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0", + "@ohif/i18n": "3.12.0", + "dcmjs": "0.49.4", "dicomweb-client": "0.10.4", "prop-types": "15.8.1", "react": "18.3.1", diff --git a/extensions/default/src/Components/DataSourceConfigurationComponent.tsx b/extensions/default/src/Components/DataSourceConfigurationComponent.tsx index dac608c786b..b6a4cf556ec 100644 --- a/extensions/default/src/Components/DataSourceConfigurationComponent.tsx +++ b/extensions/default/src/Components/DataSourceConfigurationComponent.tsx @@ -28,9 +28,9 @@ function DataSourceConfigurationComponent({ return; } - const { factory: configurationAPIFactory } = customizationService.getCustomization( - activeDataSourceDef.configuration.configurationAPI - ) ?? { factory: () => null }; + const configurationAPIFactory = + customizationService.getCustomization(activeDataSourceDef.configuration.configurationAPI) ?? + (() => null); if (!configurationAPIFactory) { return; @@ -66,6 +66,7 @@ function DataSourceConfigurationComponent({ show({ content: DataSourceConfigurationModalComponent, title: t('Configure Data Source'), + containerClassName: 'max-w-3xl', contentProps: { configurationAPI, configuredItems, diff --git a/extensions/default/src/Components/ItemListComponent.tsx b/extensions/default/src/Components/ItemListComponent.tsx index 796ad7659e7..12d7011dfbd 100644 --- a/extensions/default/src/Components/ItemListComponent.tsx +++ b/extensions/default/src/Components/ItemListComponent.tsx @@ -2,8 +2,7 @@ import classNames from 'classnames'; import React, { ReactElement, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSystem } from '@ohif/core'; -import { Button, InputFilterText } from '@ohif/ui'; -import { Icons } from '@ohif/ui-next'; +import { Button, Icons, InputFilter } from '@ohif/ui-next'; import { Types } from '@ohif/core'; type ItemListComponentProps = { @@ -32,25 +31,30 @@ function ItemListComponent({ return (
-
{t(`Select ${itemLabel}`)}
- {t(`Select ${itemLabel}`)}
+ + onChange={setFilterValue} + > + + + +
{itemList == null ? ( ) : itemList.length === 0 ? ( -
+
{t(`No ${itemLabel} available`)}
) : ( <> -
{t(itemLabel)}
+
{t(itemLabel)}
{itemList .filter( @@ -58,23 +62,24 @@ function ItemListComponent({ !filterValue || item.name.toLowerCase().includes(filterValue.toLowerCase()) ) .map(item => { - const border = - 'rounded border-transparent border-b-secondary-light border-[1px] hover:border-primary-light'; + const border = 'rounded border-transparent border-b-input border-[1px]'; return (
-
{item.name}
+
{item.name}
); diff --git a/extensions/default/src/Components/SidePanelWithServices.tsx b/extensions/default/src/Components/SidePanelWithServices.tsx index 298f5ae03b9..f200dbd561d 100644 --- a/extensions/default/src/Components/SidePanelWithServices.tsx +++ b/extensions/default/src/Components/SidePanelWithServices.tsx @@ -28,7 +28,7 @@ const SidePanelWithServices = ({ onClose, ...props }: SidePanelWithServicesProps) => { - const panelService = servicesManager?.services?.panelService; + const { panelService, toolbarService, viewportGridService } = servicesManager.services; // Tracks whether this SidePanel has been opened at least once since this SidePanel was inserted into the DOM. // Thus going to the Study List page and back to the viewer resets this flag for a SidePanel. @@ -37,9 +37,15 @@ const SidePanelWithServices = ({ const [closedManually, setClosedManually] = useState(false); const [tabs, setTabs] = useState(tabsProp ?? panelService.getPanels(side)); - const handleActiveTabIndexChange = useCallback(({ activeTabIndex }) => { - setActiveTabIndex(activeTabIndex); - }, []); + const handleActiveTabIndexChange = useCallback( + ({ activeTabIndex }) => { + const { activeViewportId: viewportId } = viewportGridService.getState(); + toolbarService.refreshToolbarState({ viewportId }); + + setActiveTabIndex(activeTabIndex); + }, + [toolbarService, viewportGridService] + ); const handleOpen = useCallback(() => { setSidePanelExpanded(true); diff --git a/extensions/default/src/DicomTagBrowser/DicomTagBrowser.tsx b/extensions/default/src/DicomTagBrowser/DicomTagBrowser.tsx index f1ee6a8891d..e3b1d45806a 100644 --- a/extensions/default/src/DicomTagBrowser/DicomTagBrowser.tsx +++ b/extensions/default/src/DicomTagBrowser/DicomTagBrowser.tsx @@ -309,6 +309,10 @@ function getSortedTags(metadata) { function getRows(metadata, depth = 0) { // Tag, Type, Value, Keyword + if (!metadata) { + return []; + } + const keywords = Object.keys(metadata); const rows = []; diff --git a/extensions/default/src/DicomWebDataSource/index.ts b/extensions/default/src/DicomWebDataSource/index.ts index afc781b21ca..5fb351767d8 100644 --- a/extensions/default/src/DicomWebDataSource/index.ts +++ b/extensions/default/src/DicomWebDataSource/index.ts @@ -16,6 +16,7 @@ import { retrieveStudyMetadata, deleteStudyMetadataPromise } from './retrieveStu import StaticWadoClient from './utils/StaticWadoClient'; import getDirectURL from '../utils/getDirectURL'; import { fixBulkDataURI } from './utils/fixBulkDataURI'; +import {HeadersInterface} from '@ohif/core/src/types/RequestHeaders'; const { DicomMetaDictionary, DicomDict } = dcmjs.data; @@ -97,6 +98,20 @@ export type BulkDataURIConfig = { relativeResolution?: 'studies' | 'series'; }; +/** + * The header options are the options passed into the generateWadoHeader + * command. This takes an extensible set of attributes to allow future enhancements. + */ +export interface HeaderOptions { + includeTransferSyntax?: boolean; +} + +/** + * Metadata and some other requests don't permit the transfer syntax to be included, + * so pass in the excludeTransferSyntax parameter. + */ +export const excludeTransferSyntax: HeaderOptions = { includeTransferSyntax: false }; + /** * Creates a DICOM Web API based on the provided configuration. * @@ -128,7 +143,7 @@ function createDicomWebApi(dicomWebConfig: DicomWebConfig, servicesManager) { dicomWebConfigCopy = JSON.parse(JSON.stringify(dicomWebConfig)); getAuthorizationHeader = () => { - const xhrRequestHeaders = {}; + const xhrRequestHeaders: HeadersInterface = {}; const authHeaders = userAuthenticationService.getAuthorizationHeader(); if (authHeaders && authHeaders.Authorization) { xhrRequestHeaders.Authorization = authHeaders.Authorization; @@ -136,19 +151,33 @@ function createDicomWebApi(dicomWebConfig: DicomWebConfig, servicesManager) { return xhrRequestHeaders; }; - generateWadoHeader = () => { + /** + * Generates the wado header for requesting resources from DICOMweb. + * These are classified into those that are dependent on the transfer syntax + * and those that aren't, as defined by the include transfer syntax attribute. + */ + generateWadoHeader = (options: HeaderOptions): HeadersInterface => { const authorizationHeader = getAuthorizationHeader(); - //Generate accept header depending on config params - const formattedAcceptHeader = utils.generateAcceptHeader( - dicomWebConfig.acceptHeader, - dicomWebConfig.requestTransferSyntaxUID, - dicomWebConfig.omitQuotationForMultipartRequest - ); - - return { - ...authorizationHeader, - Accept: formattedAcceptHeader, - }; + if (options?.includeTransferSyntax!==false) { + //Generate accept header depending on config params + const formattedAcceptHeader = utils.generateAcceptHeader( + dicomWebConfig.acceptHeader, + dicomWebConfig.requestTransferSyntaxUID, + dicomWebConfig.omitQuotationForMultipartRequest + ); + return { + ...authorizationHeader, + Accept: formattedAcceptHeader, + }; + } else { + // The base header will be included in the request. We simply skip customization options around + // transfer syntaxes and whether the request is multipart. In other words, a request in + // which the server expects Accept: application/dicom+json will still include that in the + // header. + return { + ...authorizationHeader + }; + } }; qidoConfig = { @@ -410,7 +439,7 @@ function createDicomWebApi(dicomWebConfig: DicomWebConfig, servicesManager) { madeInClient ) => { const enableStudyLazyLoad = false; - wadoDicomWebClient.headers = generateWadoHeader(); + wadoDicomWebClient.headers = generateWadoHeader(excludeTransferSyntax); // data is all SOPInstanceUIDs const data = await retrieveStudyMetadata( wadoDicomWebClient, @@ -484,7 +513,7 @@ function createDicomWebApi(dicomWebConfig: DicomWebConfig, servicesManager) { returnPromises = false ) => { const enableStudyLazyLoad = true; - wadoDicomWebClient.headers = generateWadoHeader(); + wadoDicomWebClient.headers = generateWadoHeader(excludeTransferSyntax); // Get Series const { preLoadData: seriesSummaryMetadata, promises: seriesPromises } = await retrieveStudyMetadata( diff --git a/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx b/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx index 4069bc68faf..052d0fe2839 100644 --- a/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx +++ b/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowser.tsx @@ -7,6 +7,7 @@ import { PanelStudyBrowserHeader } from './PanelStudyBrowserHeader'; import { defaultActionIcons } from './constants'; import MoreDropdownMenu from '../../Components/MoreDropdownMenu'; import { CallbackCustomization } from 'platform/core/src/types'; +import { type TabsProps } from '@ohif/core/src/utils/createStudyBrowserTabs'; const { sortStudyInstances, formatDate, createStudyBrowserTabs } = utils; @@ -27,7 +28,8 @@ function PanelStudyBrowser({ const { servicesManager, commandsManager, extensionManager } = useSystem(); const { displaySetService, customizationService } = servicesManager.services; const navigate = useNavigate(); - const studyMode = customizationService.getCustomization('studyBrowser.studyMode') || 'all'; + const studyMode = + (customizationService.getCustomization('studyBrowser.studyMode') as string) || 'all'; const internalImageViewer = useImageViewer(); const StudyInstanceUIDs = internalImageViewer.StudyInstanceUIDs; @@ -35,9 +37,11 @@ function PanelStudyBrowser({ const [{ activeViewportId, viewports, isHangingProtocolLayout }] = useViewportGrid(); const [activeTabName, setActiveTabName] = useState(studyMode); - const [expandedStudyInstanceUIDs, setExpandedStudyInstanceUIDs] = useState([ - ...StudyInstanceUIDs, - ]); + const [expandedStudyInstanceUIDs, setExpandedStudyInstanceUIDs] = useState( + studyMode === 'primary' && StudyInstanceUIDs.length > 0 + ? [StudyInstanceUIDs[0]] + : [...StudyInstanceUIDs] + ); const [hasLoadedViewports, setHasLoadedViewports] = useState(false); const [studyDisplayList, setStudyDisplayList] = useState([]); const [displaySets, setDisplaySets] = useState([]); @@ -380,8 +384,12 @@ function PanelStudyBrowser({ } const displaySetInstanceUID = jumpToDisplaySet; - // Set the activeTabName and expand the study - const thumbnailLocation = _findTabAndStudyOfDisplaySet(displaySetInstanceUID, tabs); + // It is possible to navigate to a study not currently in view + const thumbnailLocation = _findTabAndStudyOfDisplaySet( + displaySetInstanceUID, + tabs, + activeTabName + ); if (!thumbnailLocation) { return; } @@ -486,7 +494,7 @@ function _mapDisplaySets(displaySets, displaySetLoadingState, thumbnailImageSrcM seriesNumber: ds.SeriesNumber, modality: ds.Modality, seriesDate: formatDate(ds.SeriesDate), - numInstances: ds.numImageFrames, + numInstances: ds.numImageFrames ?? ds.instances?.length, loadingProgress, countIcon: ds.countIcon, messages: ds.messages, @@ -530,23 +538,23 @@ function getImageIdForThumbnail(displaySet, imageIds) { return imageId; } -function _findTabAndStudyOfDisplaySet(displaySetInstanceUID, tabs) { - for (let t = 0; t < tabs.length; t++) { - const { studies } = tabs[t]; - - for (let s = 0; s < studies.length; s++) { - const { displaySets } = studies[s]; - - for (let d = 0; d < displaySets.length; d++) { - const displaySet = displaySets[d]; - - if (displaySet.displaySetInstanceUID === displaySetInstanceUID) { - return { - tabName: tabs[t].name, - StudyInstanceUID: studies[s].studyInstanceUid, - }; - } - } +function _findTabAndStudyOfDisplaySet( + displaySetInstanceUID: string, + tabs: TabsProps, + currentTabName: string +) { + const current = tabs.find(tab => tab.name === currentTabName) || tabs[0]; + const biasedTabs = [current, ...tabs]; + + for (let t = 0; t < biasedTabs.length; t++) { + const study = biasedTabs[t].studies.find(study => + study.displaySets.find(ds => ds.displaySetInstanceUID === displaySetInstanceUID) + ); + if (study) { + return { + tabName: biasedTabs[t].name, + StudyInstanceUID: study.studyInstanceUid, + }; } } } diff --git a/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowserHeader.tsx b/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowserHeader.tsx index e5349b24516..641daae5f86 100644 --- a/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowserHeader.tsx +++ b/extensions/default/src/Panels/StudyBrowser/PanelStudyBrowserHeader.tsx @@ -14,6 +14,7 @@ function PanelStudyBrowserHeader({ actionIcons: actionIcon[]; updateActionIconValue: (actionIcon: actionIcon) => void; }) { + // Button order: Settings button then List view mode (thumbnails vs. list) return ( <>
diff --git a/extensions/default/src/Panels/createReportDialogPrompt.tsx b/extensions/default/src/Panels/createReportDialogPrompt.tsx index 17a0316baa4..6f39153ca76 100644 --- a/extensions/default/src/Panels/createReportDialogPrompt.tsx +++ b/extensions/default/src/Panels/createReportDialogPrompt.tsx @@ -1,12 +1,39 @@ import PROMPT_RESPONSES from '../utils/_shared/PROMPT_RESPONSES'; +/** + * Creates and shows a report dialog prompt. + * The input for this is: + * - `title` shown in the dialog + * - `modality` being stored, used to query existing series + * - `minSeriesNumber` is the start of new series of this modality type. + * Will get set to 4000 if not determined by the modality + * - predecessorImageId is the image id that this series was currently loaded + * from. That allows defaulting the dialog to show the specified series instead + * of always creating a new series. + * + * The response is: + * - `value`, the default name of the object/series being created + * - `dataSourceName`, where to store the object to + * - `series`, is the series to store do, as referenced by a predecessorImageId value. + * - `priorSeriesNumber` is the previously lowest series number at least minSeriesNumber + * of all the seris of the given modality type. + * + * This should be provided to the DICOM encoder, which will get the predecessor + * sequence from the metaData provider so that the saved series will replace + * the existing instance in the same series. + * This will be falsy for a new series. + */ export default function CreateReportDialogPrompt({ title = 'Create Report', + modality = 'SR', + minSeriesNumber = 0, + predecessorImageId, extensionManager, servicesManager, }): Promise<{ value: string; dataSourceName: string; + priorSeriesNumber?: number; series: string; action: (typeof PROMPT_RESPONSES)[keyof typeof PROMPT_RESPONSES]; }> { @@ -16,18 +43,33 @@ export default function CreateReportDialogPrompt({ const allowMultipleDataSources = window.config.allowMultiSelectExport; - return new Promise(function (resolve, reject) { + minSeriesNumber ||= + (modality === 'SR' && 3000) || + (modality === 'SEG' && 3100) || + (modality === 'RTSTRUCT' && 3200) || + 4000; + + return new Promise(function (resolve) { uiDialogService.show({ id: 'report-dialog', title, content: ReportDialog, contentProps: { dataSources: allowMultipleDataSources ? dataSources : undefined, - onSave: async ({ reportName, dataSource: selectedDataSource, series }) => { + predecessorImageId, + minSeriesNumber, + modality, + onSave: async ({ + reportName, + dataSource: selectedDataSource, + series, + priorSeriesNumber, + }) => { resolve({ value: reportName, dataSourceName: selectedDataSource, series, + priorSeriesNumber, action: PROMPT_RESPONSES.CREATE_REPORT, }); }, diff --git a/extensions/default/src/Toolbar/Toolbar.tsx b/extensions/default/src/Toolbar/Toolbar.tsx index 3cf27b8402a..02971484f89 100644 --- a/extensions/default/src/Toolbar/Toolbar.tsx +++ b/extensions/default/src/Toolbar/Toolbar.tsx @@ -1,9 +1,30 @@ import React from 'react'; import { useToolbar } from '@ohif/core'; +/** + * Props for the Toolbar component that renders a collection of toolbar buttons and/or button sections. + * + * @interface ToolbarProps + */ interface ToolbarProps { + /** + * The section of buttons to display in the toolbar. + * Common values include 'primary', 'secondary', 'tertiary', etc. + * Defaults to 'primary' if not specified. + * + * @default 'primary' + */ buttonSection?: string; + + /** + * The unique identifier of the viewport this toolbar is associated with. + */ viewportId?: string; + + /** + * The numeric position or location of the toolbar. + * Used for ordering and layout purposes in the UI. + */ location?: number; } @@ -60,7 +81,17 @@ export function Toolbar({ buttonSection = 'primary', viewportId, location }: Too /> ); - return
{tool}
; + return ( +
+ {tool} +
+ ); })} ); diff --git a/extensions/default/src/Toolbar/ToolbarLayoutSelector.tsx b/extensions/default/src/Toolbar/ToolbarLayoutSelector.tsx index 4c87dade2d8..abb6cac5d7b 100644 --- a/extensions/default/src/Toolbar/ToolbarLayoutSelector.tsx +++ b/extensions/default/src/Toolbar/ToolbarLayoutSelector.tsx @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import { CommandsManager } from '@ohif/core'; import { LayoutSelector } from '@ohif/ui-next'; +import { useTranslation } from 'react-i18next'; function ToolbarLayoutSelectorWithServices({ commandsManager, @@ -13,6 +14,7 @@ function ToolbarLayoutSelectorWithServices({ ...props }) { const { customizationService } = servicesManager.services; + const { t } = useTranslation('ToolbarLayoutSelector'); // Get the presets from the customization service const commonPresets = customizationService?.getCustomization('layoutSelector.commonPresets') || [ @@ -135,14 +137,14 @@ function ToolbarLayoutSelectorWithServices({ onSelectionChange={handleSelectionChange} {...props} > - + {/* Left side - Presets */} {(commonPresets.length > 0 || advancedPresets.length > 0) && (
{commonPresets.length > 0 && ( <> - + {commonPresets.map((preset, index) => ( 0 && ( - + {advancedPresets.map((preset, index) => ( -
Custom
+
{t('Custom')}
- Hover to select
- rows and columns
Click to apply + {t('Hover to select')}
+ {t('rows and columns')}
+ {t('Click to apply')}
diff --git a/extensions/default/src/commandsModule.ts b/extensions/default/src/commandsModule.ts index 436f53b2145..3784b14a74e 100644 --- a/extensions/default/src/commandsModule.ts +++ b/extensions/default/src/commandsModule.ts @@ -66,8 +66,8 @@ const commandsModule = ({ */ addDisplaySetAsLayer: ({ viewportId, displaySetInstanceUID, removeFirst = false }) => { if (!viewportId) { - const { activeViewportId } = servicesManager.services.viewportGridService.getState(); - viewportId = activeViewportId; + const { activeViewportId } = servicesManager.services.viewportGridService.getState(); + viewportId = activeViewportId; } if (!viewportId || !displaySetInstanceUID) { diff --git a/extensions/default/src/customizations/aboutModalCustomization.tsx b/extensions/default/src/customizations/aboutModalCustomization.tsx index d17fc574ecf..93a00e2fb11 100644 --- a/extensions/default/src/customizations/aboutModalCustomization.tsx +++ b/extensions/default/src/customizations/aboutModalCustomization.tsx @@ -1,8 +1,10 @@ import React from 'react'; import { AboutModal } from '@ohif/ui-next'; import detect from 'browser-detect'; +import { useTranslation } from 'react-i18next'; function AboutModalDefault() { + const { t } = useTranslation('AboutModal'); const { os, version, name } = detect(); const browser = `${name[0].toUpperCase()}${name.substr(1)} ${version}`; const versionNumber = process.env.VERSION_NUMBER; @@ -18,11 +20,11 @@ function AboutModalDefault() { { let attempts = 0; @@ -23,8 +25,8 @@ export default { steps: [ { id: 'scroll', - title: 'Scrolling Through Images', - text: 'You can scroll through the images using the mouse wheel or scrollbar.', + title: i18n.t('Onboarding:Scrolling Through Images'), + text: i18n.t('Onboarding:You can scroll through the images using the mouse wheel or scrollbar.'), attachTo: { element: '.viewport-element', on: 'top', @@ -37,8 +39,8 @@ export default { }, { id: 'zoom', - title: 'Zooming In and Out', - text: 'You can zoom the images using the right click.', + title: i18n.t('Onboarding:Zooming In and Out'), + text: i18n.t('Onboarding:You can zoom the images using the right click.'), attachTo: { element: '.viewport-element', on: 'left', @@ -51,8 +53,8 @@ export default { }, { id: 'pan', - title: 'Panning the Image', - text: 'You can pan the images using the middle click.', + title: i18n.t('Onboarding:Panning the Image'), + text: i18n.t('Onboarding:You can pan the images using the middle click.'), attachTo: { element: '.viewport-element', on: 'top', @@ -65,8 +67,8 @@ export default { }, { id: 'windowing', - title: 'Adjusting Window Level', - text: 'You can modify the window level using the left click.', + title: i18n.t('Onboarding:Adjusting Window Level'), + text: i18n.t('Onboarding:You can modify the window level using the left click.'), attachTo: { element: '.viewport-element', on: 'left', @@ -79,8 +81,8 @@ export default { }, { id: 'length', - title: 'Using the Measurement Tools', - text: 'You can measure the length of a region using the Length tool.', + title: i18n.t('Onboarding:Using the Measurement Tools'), + text: i18n.t('Onboarding:You can measure the length of a region using the Length tool.'), attachTo: { element: '[data-cy="MeasurementTools-split-button-primary"]', on: 'bottom', @@ -90,12 +92,12 @@ export default { event: 'click', }, beforeShowPromise: () => - waitForElement('[data-cy="MeasurementTools-split-button-primary]'), + waitForElement('[data-cy="MeasurementTools-split-button-primary"]'), }, { id: 'drawAnnotation', - title: 'Drawing Length Annotations', - text: 'Use the length tool on the viewport to measure the length of a region.', + title: i18n.t('Onboarding:Drawing Length Annotations'), + text: i18n.t('Onboarding:Use the length tool on the viewport to measure the length of a region.'), attachTo: { element: '.viewport-element', on: 'right', @@ -108,8 +110,8 @@ export default { }, { id: 'trackMeasurement', - title: 'Tracking Measurements in the Panel', - text: 'Click yes to track the measurements in the measurement panel.', + title: i18n.t('Onboarding:Tracking Measurements in the Panel'), + text: i18n.t('Onboarding:Click yes to track the measurements in the measurement panel.'), attachTo: { element: '[data-cy="prompt-begin-tracking-yes-btn"]', on: 'bottom', @@ -122,8 +124,8 @@ export default { }, { id: 'openMeasurementPanel', - title: 'Opening the Measurements Panel', - text: 'Click the measurements button to open the measurements panel.', + title: i18n.t('Onboarding:Opening the Measurements Panel'), + text: i18n.t('Onboarding:Click the measurements button to open the measurements panel.'), attachTo: { element: '#trackedMeasurements-btn', on: 'left-start', @@ -136,8 +138,8 @@ export default { }, { id: 'scrollAwayFromMeasurement', - title: 'Scrolling Away from a Measurement', - text: 'Scroll the images using the mouse wheel away from the measurement.', + title: i18n.t('Onboarding:Scrolling Away from a Measurement'), + text: i18n.t('Onboarding:Scroll the images using the mouse wheel away from the measurement.'), attachTo: { element: '.viewport-element', on: 'left', @@ -150,8 +152,8 @@ export default { }, { id: 'jumpToMeasurement', - title: 'Jumping to Measurements in the Panel', - text: 'Click the measurement in the measurement panel to jump to it.', + title: i18n.t('Onboarding:Jumping to Measurements in the Panel'), + text: i18n.t('Onboarding:Click the measurement in the measurement panel to jump to it.'), attachTo: { element: '[data-cy="data-row"]', on: 'left-start', @@ -164,8 +166,8 @@ export default { }, { id: 'changeLayout', - title: 'Changing Layout', - text: 'You can change the layout of the viewer using the layout button.', + title: i18n.t('Onboarding:Changing Layout'), + text: i18n.t('Onboarding:You can change the layout of the viewer using the layout button.'), attachTo: { element: '[data-cy="Layout"]', on: 'bottom', @@ -178,8 +180,8 @@ export default { }, { id: 'selectLayout', - title: 'Selecting the MPR Layout', - text: 'Select the MPR layout to view the images in MPR mode.', + title: i18n.t('Onboarding:Selecting the MPR Layout'), + text: i18n.t('Onboarding:Select the MPR layout to view the images in MPR mode.'), attachTo: { element: '[data-cy="MPR"]', on: 'left-start', @@ -196,7 +198,7 @@ export default { defaultStepOptions: { buttons: [ { - text: 'Skip all', + text: i18n.t('Onboarding:Skip all'), action() { this.complete(); }, diff --git a/extensions/default/src/customizations/reportDialogCustomization.tsx b/extensions/default/src/customizations/reportDialogCustomization.tsx index 2f740f711af..36c25f78794 100644 --- a/extensions/default/src/customizations/reportDialogCustomization.tsx +++ b/extensions/default/src/customizations/reportDialogCustomization.tsx @@ -11,28 +11,44 @@ type DataSource = { type ReportDialogProps = { dataSources: DataSource[]; + modality?: string; + predecessorImageId?: string; hide: () => void; - onSave: (data: { reportName: string; dataSource: string | null; series: string | null }) => void; + onSave: (data: { + reportName: string; + dataSource: string | null; + series: string | null; + priorSeriesNumber: number; + }) => void; onCancel: () => void; }; -function ReportDialog({ dataSources, hide, onSave, onCancel }: ReportDialogProps) { +function ReportDialog({ + dataSources, + modality = 'SR', + predecessorImageId, + minSeriesNumber = 3000, + hide, + onSave, + onCancel, +}: ReportDialogProps) { const { servicesManager } = useSystem(); const [selectedDataSource, setSelectedDataSource] = useState( dataSources?.[0]?.value ?? null ); - const [selectedSeries, setSelectedSeries] = useState(null); - const [reportName, setReportName] = useState(''); - const { displaySetService } = servicesManager.services; + const [selectedSeries, setSelectedSeries] = useState(predecessorImageId || null); + const [reportName, setReportName] = useState(''); + const seriesOptions = useMemo(() => { const displaySetsMap = displaySetService.getDisplaySetCache(); const displaySets = Array.from(displaySetsMap.values()); const options = displaySets - .filter(ds => ds.Modality === 'SR') + .filter(ds => ds.Modality === modality) .map(ds => ({ - value: ds.SeriesInstanceUID, + value: ds.predecessorImageId || ds.SeriesInstanceUID, + seriesNumber: isFinite(ds.SeriesNumber) ? ds.SeriesNumber : minSeriesNumber, description: ds.SeriesDescription, label: `${ds.SeriesDescription} ${ds.SeriesDate}/${ds.SeriesTime} ${ds.SeriesNumber}`, })); @@ -41,11 +57,12 @@ function ReportDialog({ dataSources, hide, onSave, onCancel }: ReportDialogProps { value: null, description: null, + seriesNumber: minSeriesNumber, label: 'Create new series', }, ...options, ]; - }, [displaySetService]); + }, [displaySetService, modality]); useEffect(() => { const seriesOption = seriesOptions.find(s => s.value === selectedSeries); @@ -58,6 +75,7 @@ function ReportDialog({ dataSources, hide, onSave, onCancel }: ReportDialogProps onSave({ reportName, dataSource: selectedDataSource, + priorSeriesNumber: Math.max(...seriesOptions.map(it => it.seriesNumber)), series: selectedSeries, }); hide(); diff --git a/extensions/default/src/customizations/studyBrowserCustomization.ts b/extensions/default/src/customizations/studyBrowserCustomization.ts index cb19efbee69..286832a8004 100644 --- a/extensions/default/src/customizations/studyBrowserCustomization.ts +++ b/extensions/default/src/customizations/studyBrowserCustomization.ts @@ -1,4 +1,5 @@ import { utils } from '@ohif/core'; +import i18n from '@ohif/i18n'; const { formatDate } = utils; export default { @@ -6,46 +7,26 @@ export default { 'studyBrowser.thumbnailMenuItems': [ { id: 'tagBrowser', - label: 'Tag Browser', + label: i18n.t('StudyBrowser:Tag Browser'), iconName: 'DicomTagBrowser', commands: 'openDICOMTagViewer', }, { id: 'addAsLayer', - label: 'Add as Layer', + label: i18n.t('StudyBrowser:Add as Layer'), iconName: 'ViewportViews', commands: 'addDisplaySetAsLayer', }, - { - id: 'addAsLayer', - label: 'Add as Layer', - iconName: 'ViewportViews', - onClick: ({ commandsManager, displaySetInstanceUID, servicesManager }: withAppTypes) => { - const { viewportGridService } = servicesManager.services; - - // Get the active viewport - const { activeViewportId } = viewportGridService.getState(); - if (!activeViewportId) { - return; - } - - // Use the new command to add the display set as a layer - commandsManager.runCommand('addDisplaySetAsLayer', { - viewportId: activeViewportId, - displaySetInstanceUID, - }); - }, - }, ], 'studyBrowser.sortFunctions': [ { - label: 'Series Number', + label: i18n.t('StudyBrowser:Series Number'), sortFunction: (a, b) => { return a?.SeriesNumber - b?.SeriesNumber; }, }, { - label: 'Series Date', + label: i18n.t('StudyBrowser:Series Date'), sortFunction: (a, b) => { const dateA = new Date(formatDate(a?.SeriesDate)); const dateB = new Date(formatDate(b?.SeriesDate)); @@ -83,8 +64,10 @@ export default { } catch (error) { console.warn(error); uiNotificationService.show({ - title: 'Thumbnail Double Click', - message: 'The selected display sets could not be added to the viewport.', + title: i18n.t('StudyBrowser:Thumbnail Double Click'), + message: i18n.t( + 'StudyBrowser:The selected display sets could not be added to the viewport.' + ), type: 'error', duration: 3000, }); diff --git a/extensions/default/src/customizations/userPreferencesCustomization.tsx b/extensions/default/src/customizations/userPreferencesCustomization.tsx index f6adda45ea1..038a40004f5 100644 --- a/extensions/default/src/customizations/userPreferencesCustomization.tsx +++ b/extensions/default/src/customizations/userPreferencesCustomization.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState, useEffect } from 'react'; import { useSystem, hotkeys as hotkeysModule } from '@ohif/core'; import { UserPreferencesModal, FooterAction } from '@ohif/ui-next'; import { useTranslation } from 'react-i18next'; @@ -19,14 +19,40 @@ interface HotkeyDefinitions { function UserPreferencesModalDefault({ hide }: { hide: () => void }) { const { hotkeysManager } = useSystem(); - const { t } = useTranslation('UserPreferencesModal'); + const { t, i18n: i18nextInstance } = useTranslation('UserPreferencesModal'); const { hotkeyDefinitions = {}, hotkeyDefaults = {} } = hotkeysManager; + const fallbackHotkeyDefinitions = useMemo( + () => + hotkeysManager.getValidHotkeyDefinitions( + hotkeysModule.defaults.hotkeyBindings + ) as HotkeyDefinitions, + [hotkeysManager] + ); + + useEffect(() => { + if (!Object.keys(hotkeyDefaults).length) { + hotkeysManager.setDefaultHotKeys(hotkeysModule.defaults.hotkeyBindings); + } + + if (!Object.keys(hotkeyDefinitions).length) { + hotkeysManager.setHotkeys(fallbackHotkeyDefinitions); + } + }, [hotkeysManager, hotkeyDefaults, hotkeyDefinitions, fallbackHotkeyDefinitions]); + + const resolvedHotkeyDefaults = Object.keys(hotkeyDefaults).length + ? (hotkeyDefaults as HotkeyDefinitions) + : fallbackHotkeyDefinitions; + + const initialHotkeyDefinitions = Object.keys(hotkeyDefinitions).length + ? (hotkeyDefinitions as HotkeyDefinitions) + : resolvedHotkeyDefaults; + const currentLanguage = currentLanguageFn(); const [state, setState] = useState({ - hotkeyDefinitions: hotkeyDefinitions as HotkeyDefinitions, + hotkeyDefinitions: initialHotkeyDefinitions, languageValue: currentLanguage.value, }); @@ -51,12 +77,52 @@ function UserPreferencesModalDefault({ hide }: { hide: () => void }) { setState(state => ({ ...state, languageValue: defaultLanguage.value, - hotkeyDefinitions: hotkeyDefaults as HotkeyDefinitions, + hotkeyDefinitions: resolvedHotkeyDefaults, })); hotkeysManager.restoreDefaultBindings(); }; + const displayNames = React.useMemo(() => { + if (typeof Intl === 'undefined' || typeof Intl.DisplayNames !== 'function') { + return null; + } + + const locales = [state.languageValue, currentLanguage.value, i18nextInstance.language, 'en']; + const uniqueLocales = Array.from(new Set(locales.filter(Boolean))); + + try { + return new Intl.DisplayNames(uniqueLocales, { type: 'language', fallback: 'none' }); + } catch (error) { + console.warn('Intl.DisplayNames not supported for locales', uniqueLocales, error); + } + + return null; + }, [state.languageValue, currentLanguage.value, i18nextInstance.language]); + + const getLanguageLabel = React.useCallback( + (languageValue: string, fallbackLabel: string) => { + const translationKey = `LanguageName.${languageValue}`; + if (i18nextInstance.exists(translationKey, { ns: 'UserPreferencesModal' })) { + return t(translationKey); + } + + if (displayNames) { + try { + const localized = displayNames.of(languageValue); + if (localized && localized.toLowerCase() !== languageValue.toLowerCase()) { + return localized.charAt(0).toUpperCase() + localized.slice(1); + } + } catch (error) { + console.debug(`Unable to resolve display name for ${languageValue}`, error); + } + } + + return fallbackLabel; + }, + [displayNames, i18nextInstance, t] + ); + return ( @@ -79,7 +145,7 @@ function UserPreferencesModalDefault({ hide }: { hide: () => void }) { key={lang.value} value={lang.value} > - {lang.label} + {getLanguageLabel(lang.value, lang.label)} ))} @@ -120,6 +186,9 @@ function UserPreferencesModalDefault({ hide }: { hide: () => void }) { onClick={() => { if (state.languageValue !== currentLanguage.value) { i18n.changeLanguage(state.languageValue); + // Force page reload after language change to ensure all translations are applied + window.location.reload(); + return; // Exit early since we're reloading } hotkeysManager.setHotkeys(state.hotkeyDefinitions); hotkeysModule.stopRecord(); diff --git a/extensions/default/src/getDisplaySetsFromUnsupportedSeries.js b/extensions/default/src/getDisplaySetsFromUnsupportedSeries.js index d5329a427f5..8c27139f99d 100644 --- a/extensions/default/src/getDisplaySetsFromUnsupportedSeries.js +++ b/extensions/default/src/getDisplaySetsFromUnsupportedSeries.js @@ -32,7 +32,8 @@ export default function getDisplaySetsFromUnsupportedSeries(instances) { SOPClassUID: instance.SOPClassUID, SeriesDescription: instance.SeriesDescription || '', Modality: instance.Modality, - numImageFrames: instances.length, + instances, + instance: instances[instance.length - 1], unsupported: true, SOPClassHandlerId: 'unsupported', isReconstructable: false, diff --git a/extensions/default/src/getSopClassHandlerModule.js b/extensions/default/src/getSopClassHandlerModule.js index c0f0323c2b5..1355abd6a03 100644 --- a/extensions/default/src/getSopClassHandlerModule.js +++ b/extensions/default/src/getSopClassHandlerModule.js @@ -4,6 +4,7 @@ import { id } from './id'; import getDisplaySetMessages from './getDisplaySetMessages'; import getDisplaySetsFromUnsupportedSeries from './getDisplaySetsFromUnsupportedSeries'; import { chartHandler } from './SOPClassHandlers/chartSOPClassHandler'; +import { metaData } from '@cornerstonejs/core'; const { isImage, @@ -50,11 +51,16 @@ function getDisplaySetInfo(instances) { const timePoint = timePoints[0]; const instancesMap = new Map(); - // O(n) to convert it into a map and O(1) to find each instance - instances.forEach(instance => instancesMap.set(instance.imageId, instance)); - - const firstTimePointInstances = timePoint.map(imageId => instancesMap.get(imageId)); + let firstTimePointInstances; + if (instances[0].NumberOfFrames > 1 && timePoints.length > 1) { + // handle multiframe dynamic volume + firstTimePointInstances = timePoints[0].map(imageId => metaData.get('instance', imageId)); + } else { + // O(n) to convert it into a map and O(1) to find each instance + instances.forEach(instance => instancesMap.set(instance.imageId, instance)); + firstTimePointInstances = timePoint.map(imageId => instancesMap.get(imageId)); + } displaySetInfo = isDisplaySetReconstructable(firstTimePointInstances, appConfig); } else { displaySetInfo = isDisplaySetReconstructable(instances, appConfig); @@ -67,7 +73,7 @@ function getDisplaySetInfo(instances) { }; } -const makeDisplaySet = instances => { +const makeDisplaySet = (instances, index) => { // Need to sort the instances in order to get a consistent instance/thumbnail sortStudyInstances(instances); const instance = instances[0]; @@ -88,16 +94,6 @@ const makeDisplaySet = instances => { // set appropriate attributes to image set... const messages = getDisplaySetMessages(instances, isReconstructable, isDynamicVolume); - const imageIds = dataSource.getImageIdsForDisplaySet(imageSet); - let imageId = imageIds[Math.floor(imageIds.length / 2)]; - let thumbnailInstance = instances[Math.floor(instances.length / 2)]; - if (isDynamicVolume) { - const timePoints = dynamicVolumeInfo.timePoints; - const middleIndex = Math.floor(timePoints.length / 2); - const middleTimePointImageIds = timePoints[middleIndex]; - imageId = middleTimePointImageIds[Math.floor(middleTimePointImageIds.length / 2)]; - } - imageSet.setAttributes({ volumeLoaderSchema, displaySetInstanceUID: imageSet.uid, // create a local alias for the imageSet UID @@ -119,7 +115,6 @@ const makeDisplaySet = instances => { averageSpacingBetweenFrames: averageSpacingBetweenFrames || null, isDynamicVolume, dynamicVolumeInfo, - getThumbnailSrc: dataSource.retrieve.getGetThumbnailSrc?.(thumbnailInstance, imageId), supportsWindowLevel: true, label: instance.SeriesDescription || @@ -127,6 +122,20 @@ const makeDisplaySet = instances => { FrameOfReferenceUID: instance.FrameOfReferenceUID, }); + const imageIds = dataSource.getImageIdsForDisplaySet(imageSet); + let imageId = imageIds[Math.floor(imageIds.length / 2)]; + let thumbnailInstance = instances[Math.floor(instances.length / 2)]; + if (isDynamicVolume) { + const timePoints = dynamicVolumeInfo.timePoints; + const middleIndex = Math.floor(timePoints.length / 2); + const middleTimePointImageIds = timePoints[middleIndex]; + imageId = middleTimePointImageIds[Math.floor(middleTimePointImageIds.length / 2)]; + } + + imageSet.setAttributes({ + getThumbnailSrc: dataSource.retrieve.getGetThumbnailSrc?.(thumbnailInstance, imageId), + }); + const { servicesManager } = appContext; const { customizationService } = servicesManager.services; @@ -187,7 +196,7 @@ function getDisplaySetsFromSeries(instances) { // into their own specific display sets. Place the rest of each // series into another display set. const stackableInstances = []; - instances.forEach(instance => { + instances.forEach((instance, instanceIndex) => { // All imaging modalities must have a valid value for sopClassUid (x00080016) or rows (x00280010) if (!isImage(instance.SOPClassUID) && !instance.Rows) { return; @@ -195,7 +204,7 @@ function getDisplaySetsFromSeries(instances) { let displaySet; if (isMultiFrame(instance)) { - displaySet = makeDisplaySet([instance]); + displaySet = makeDisplaySet([instance], instanceIndex); displaySet.setAttributes({ sopClassUids, numImageFrames: instance.NumberOfFrames, @@ -204,7 +213,7 @@ function getDisplaySetsFromSeries(instances) { }); displaySets.push(displaySet); } else if (isSingleImageModality(instance.Modality)) { - displaySet = makeDisplaySet([instance]); + displaySet = makeDisplaySet([instance], instanceIndex); displaySet.setAttributes({ sopClassUids, instanceNumber: instance.InstanceNumber, @@ -217,7 +226,7 @@ function getDisplaySetsFromSeries(instances) { }); if (stackableInstances.length) { - const displaySet = makeDisplaySet(stackableInstances); + const displaySet = makeDisplaySet(stackableInstances, displaySets.length); displaySet.setAttribute('studyInstanceUid', instances[0].StudyInstanceUID); displaySet.setAttributes({ sopClassUids, diff --git a/extensions/default/src/getToolbarModule.tsx b/extensions/default/src/getToolbarModule.tsx index 6e9b32d4a41..0c295accf80 100644 --- a/extensions/default/src/getToolbarModule.tsx +++ b/extensions/default/src/getToolbarModule.tsx @@ -10,6 +10,7 @@ import ToolButtonListWrapper from './Toolbar/ToolButtonListWrapper'; import ToolRowWrapper from './Toolbar/ToolRowWrapper'; import { ToolBoxButtonGroupWrapper, ToolBoxButtonWrapper } from './Toolbar/ToolBoxWrapper'; import { ToolButtonWrapper } from './Toolbar/ToolButtonWrapper'; +import { Toolbar } from './Toolbar'; export default function getToolbarModule({ commandsManager, servicesManager }: withAppTypes) { const { cineService } = servicesManager.services; @@ -45,6 +46,10 @@ export default function getToolbarModule({ commandsManager, servicesManager }: w name: 'ohif.progressDropdown', defaultComponent: ProgressDropdownWithService, }, + { + name: 'ohif.Toolbar', + defaultComponent: Toolbar, + }, { name: 'evaluate.cine', evaluate: () => { diff --git a/extensions/default/src/hangingprotocols/hpCompare.ts b/extensions/default/src/hangingprotocols/hpCompare.ts index 14cb51d93fa..55b8be6ebac 100644 --- a/extensions/default/src/hangingprotocols/hpCompare.ts +++ b/extensions/default/src/hangingprotocols/hpCompare.ts @@ -1,4 +1,5 @@ import { Types } from '@ohif/core'; +import i18n from 'i18next'; const defaultDisplaySetSelector = { studyMatchingRules: [ @@ -113,8 +114,8 @@ const priorViewport1 = { */ const hpMNCompare: Types.HangingProtocol.Protocol = { id: '@ohif/hpCompare', - description: 'Compare two studies in various layouts', - name: 'Compare Two Studies', + description: i18n.t('Hps:Compare two studies in various layouts'), + name: i18n.t('Hps:Compare Two Studies'), numberOfPriorsReferenced: 1, protocolMatchingRules: [ { diff --git a/extensions/default/src/hangingprotocols/hpMammo.ts b/extensions/default/src/hangingprotocols/hpMammo.ts index 4a97b04aa87..8be1196ef56 100644 --- a/extensions/default/src/hangingprotocols/hpMammo.ts +++ b/extensions/default/src/hangingprotocols/hpMammo.ts @@ -8,6 +8,7 @@ import { RMLOPrior, LMLOPrior, } from './utils/mammoDisplaySetSelector'; +import i18n from 'i18next'; const rightDisplayArea = { storeAsInitialCamera: true, @@ -30,7 +31,7 @@ const leftDisplayArea = { const hpMammography = { id: '@ohif/hpMammo', hasUpdatedPriorsInformation: false, - name: 'Mammography Breast Screening', + name: i18n.t('Hps:Mammography Breast Screening'), protocolMatchingRules: [ { id: 'Mammography', diff --git a/extensions/default/src/utils/Toolbox.tsx b/extensions/default/src/utils/Toolbox.tsx index 42781fd8e78..90aa0f75696 100644 --- a/extensions/default/src/utils/Toolbox.tsx +++ b/extensions/default/src/utils/Toolbox.tsx @@ -1,12 +1,21 @@ import React, { useState } from 'react'; import { Icons, PanelSection, ToolSettings } from '@ohif/ui-next'; -import { useSystem, useToolbar } from '@ohif/core'; -import classnames from 'classnames'; +import { useSystem, useToolbar, useActiveToolOptions } from '@ohif/core'; import { useTranslation } from 'react-i18next'; -interface ButtonProps { - isActive?: boolean; - options?: unknown; +/** + * Props for the Toolbox component that renders a collection of toolbar button sections. + */ +interface ToolboxProps { + /** + * The unique identifier of the button section this toolbox represents. + */ + buttonSectionId: string; + + /** + * The display title for the toolbox. + */ + title: string; } /** @@ -19,7 +28,7 @@ interface ButtonProps { * role in enhancing the app with a toolbox by providing a way to integrate * and display various tools and their corresponding options */ -export function Toolbox({ buttonSectionId, title }: { buttonSectionId: string; title: string }) { +export function Toolbox({ buttonSectionId, title }: ToolboxProps) { const { servicesManager } = useSystem(); const { t } = useTranslation(); @@ -30,6 +39,8 @@ export function Toolbox({ buttonSectionId, title }: { buttonSectionId: string; t buttonSection: buttonSectionId, }); + const { activeToolOptions } = useActiveToolOptions({ buttonSectionId }); + if (!toolboxSections.length) { return null; } @@ -41,35 +52,6 @@ export function Toolbox({ buttonSectionId, title }: { buttonSectionId: string; t ); } - // Helper to check a list of buttons for an active tool. - const findActiveOptions = (buttons: any[]): unknown => { - for (const tool of buttons) { - if (tool.componentProps.isActive) { - return tool.componentProps.options; - } - if (tool.componentProps.buttonSection) { - const nestedButtons = toolbarService.getButtonPropsInButtonSection( - tool.componentProps.buttonSection - ) as ButtonProps[]; - const activeNested = nestedButtons.find(nested => nested.isActive); - if (activeNested) { - return activeNested.options; - } - } - } - return null; - }; - - // Look for active tool options across all sections. - const activeToolOptions = toolboxSections.reduce((activeOptions, section) => { - if (activeOptions) { - return activeOptions; - } - const sectionId = section.componentProps.buttonSection; - const buttons = toolbarService.getButtonSection(sectionId); - return findActiveOptions(buttons); - }, null); - // Define the interaction handler once. const handleInteraction = ({ itemId }: { itemId: string }) => { onInteraction?.({ itemId }); @@ -103,19 +85,20 @@ export function Toolbox({ buttonSectionId, title }: { buttonSectionId: string; t return (
{buttons.map(tool => { - if (!tool) { + // Skip over tools that are not visible. The visible flag is typically set to + // false as a result of the evaluator function. The evaluator might explicitly + // set visible to false. Alternatively, the ToolbarService will set the visible flag to + // false when the evaluator sets disabled to true and the tool has the hideWhenDisabled flag set to true. + if (!tool || !tool.componentProps.visible) { return null; } const { id, Component, componentProps } = tool; return ( -
+
{ return commandsManager.runCommand( @@ -57,11 +53,8 @@ async function promptSaveReport({ servicesManager, commandsManager, extensionMan additionalFindingTypes: ['ArrowAnnotate'], options: { SeriesDescription, - SeriesNumber, - InstanceNumber, - SeriesInstanceUID: promptResult.series, - SeriesDate, - SeriesTime, + SeriesNumber: 1 + priorSeriesNumber, + predecessorImageId: series, }, }, 'CORNERSTONE_STRUCTURED_REPORT' @@ -90,4 +83,20 @@ async function promptSaveReport({ servicesManager, commandsManager, extensionMan } } +export function findPredecessorImageId(annotations) { + let predecessorImageId; + for (const annotation of annotations) { + if ( + predecessorImageId && + annotation.predecessorImageId && + annotation.predecessorImageId !== predecessorImageId + ) { + console.warn('Found multiple source predecessors, not defaulting to same series'); + return; + } + predecessorImageId ||= annotation.predecessorImageId; + } + return predecessorImageId; +} + export default promptSaveReport; diff --git a/extensions/default/src/utils/validations/areAllImageSpacingEqual.ts b/extensions/default/src/utils/validations/areAllImageSpacingEqual.ts index 49906c3c22d..d06cb1dacef 100644 --- a/extensions/default/src/utils/validations/areAllImageSpacingEqual.ts +++ b/extensions/default/src/utils/validations/areAllImageSpacingEqual.ts @@ -24,7 +24,9 @@ export default function areAllImageSpacingEqual( return; } const lastIpp = toNumber(instances[instances.length - 1].ImagePositionPatient); - + if (!lastIpp) { + return; + } const averageSpacingBetweenFrames = _getPerpendicularDistance(firstImagePositionPatient, lastIpp) / (instances.length - 1); diff --git a/extensions/dicom-microscopy/CHANGELOG.md b/extensions/dicom-microscopy/CHANGELOG.md index 9658025b6fb..7d701d7733e 100644 --- a/extensions/dicom-microscopy/CHANGELOG.md +++ b/extensions/dicom-microscopy/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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-dicom-microscopy @@ -11,7 +11,1096 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23) + + +### Features + +* **jpeg2000:** Add 16-bit RGB support to JPEG2000 decoder ([#5519](https://github.com/OHIF/Viewers/issues/5519)) ([a154443](https://github.com/OHIF/Viewers/commit/a1544432db42bf77864bb7df2f757cae819a4b4d)) + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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 @ohif/extension-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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 @ohif/extension-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [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-dicom-microscopy + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + + +### Features + +* Add WSI bulk data annotations support (ANN) ([#4972](https://github.com/OHIF/Viewers/issues/4972)) ([2ed6818](https://github.com/OHIF/Viewers/commit/2ed681817142b13971bb17b3058be26bbab6b90b)) + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/extension-dicom-microscopy + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/extension-dicom-microscopy diff --git a/extensions/dicom-microscopy/package.json b/extensions/dicom-microscopy/package.json index 9724c962cb0..3d363e1d5e5 100644 --- a/extensions/dicom-microscopy/package.json +++ b/extensions/dicom-microscopy/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-dicom-microscopy", - "version": "3.11.1", + "version": "3.12.0", "description": "OHIF extension for DICOM microscopy", "author": "Bill Wallace, md-prog", "license": "MIT", @@ -30,22 +30,22 @@ "start": "yarn run dev" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/i18n": "3.11.1", - "@ohif/ui": "3.11.1", + "@ohif/core": "3.12.0", + "@ohif/extension-default": "3.12.0", + "@ohif/i18n": "3.12.0", + "@ohif/ui": "3.12.0", "prop-types": "15.8.1", "react": "18.3.1", "react-dom": "18.3.1", "react-i18next": "12.3.1", - "react-router": "6.30.1", - "react-router-dom": "6.30.1" + "react-router": "6.30.3", + "react-router-dom": "6.30.3" }, "dependencies": { "@babel/runtime": "7.28.2", "@cornerstonejs/codec-charls": "1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2", - "@cornerstonejs/codec-openjpeg": "1.2.4", + "@cornerstonejs/codec-openjpeg": "1.3.0", "colormap": "2.3.2", "lodash.debounce": "4.0.8", "mathjs": "12.4.3" diff --git a/extensions/dicom-microscopy/src/DicomMicroscopyANNSopClassHandler.js b/extensions/dicom-microscopy/src/DicomMicroscopyANNSopClassHandler.js new file mode 100644 index 00000000000..84a17749ba6 --- /dev/null +++ b/extensions/dicom-microscopy/src/DicomMicroscopyANNSopClassHandler.js @@ -0,0 +1,113 @@ +import OHIF, { DicomMetadataStore } from '@ohif/core'; +import loadAnnotation from './utils/loadAnnotation'; +import getSourceDisplaySet from './utils/getSourceDisplaySet'; + +const { utils } = OHIF; + +const SOP_CLASS_UIDS = { + MICROSCOPY_BULK_SIMPLE_ANNOTATION: '1.2.840.10008.5.1.4.1.1.91.1', +}; + +const SOPClassHandlerId = + '@ohif/extension-dicom-microscopy.sopClassHandlerModule.DicomMicroscopyANNSopClassHandler'; + +function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager) { + // If the series has no instances, stop here + if (!instances || !instances.length) { + throw new Error('No instances were provided'); + } + + const { displaySetService, microscopyService } = servicesManager.services; + + // Sort instances by date/time in ascending order (oldest first) + const sortedInstances = [...instances].sort((a, b) => { + const dateA = `${a.ContentDate}${a.ContentTime}`; + const dateB = `${b.ContentDate}${b.ContentTime}`; + return dateA.localeCompare(dateB); + }); + + // Get the most recent instance (last in the sorted array) + const instance = sortedInstances[sortedInstances.length - 1]; + + const naturalizedDataset = DicomMetadataStore.getSeries( + instance.StudyInstanceUID, + instance.SeriesInstanceUID + ).instances[0]; + + const { + SeriesDescription, + ContentDate, + ContentTime, + SeriesNumber, + StudyInstanceUID, + SeriesInstanceUID, + SOPInstanceUID, + SOPClassUID, + } = instance; + + const displaySet = { + isOverlayDisplaySet: true, + plugin: 'microscopy', + Modality: 'ANN', + thumbnailSrc: null, + altImageText: 'Microscopy Annotation', + displaySetInstanceUID: utils.uuidv4(), + SOPInstanceUID, + SeriesInstanceUID, + StudyInstanceUID, + SOPClassHandlerId, + SOPClassUID, + SeriesDescription, + // Map the content date/time to the series date/time, these are only used for filtering. + SeriesDate: ContentDate, + SeriesTime: ContentTime, + SeriesNumber, + instance, + metadata: naturalizedDataset, + isDerived: true, + isLoading: false, + isLoaded: false, + loadError: false, + }; + + displaySet.load = function () { + return loadAnnotation({ + microscopyService, + displaySet, + extensionManager, + servicesManager, + }).catch(error => { + displaySet.isLoaded = false; + displaySet.loadError = true; + throw new Error(error); + }); + }; + + displaySet.getSourceDisplaySet = function () { + let allDisplaySets = []; + const studyMetadata = DicomMetadataStore.getStudy(StudyInstanceUID); + studyMetadata.series.forEach(series => { + const displaySets = displaySetService.getDisplaySetsForSeries(series.SeriesInstanceUID); + allDisplaySets = allDisplaySets.concat(displaySets); + }); + const ds = getSourceDisplaySet(allDisplaySets, displaySet); + return ds; + }; + + return [displaySet]; +} + +export default function getDicomMicroscopyANNSopClassHandler({ + servicesManager, + extensionManager, +}) { + const getDisplaySetsFromSeries = instances => { + return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager); + }; + + return { + name: 'DicomMicroscopyANNSopClassHandler', + sopClassUids: [SOP_CLASS_UIDS.MICROSCOPY_BULK_SIMPLE_ANNOTATION], + getDisplaySetsFromSeries, + }; +} diff --git a/extensions/dicom-microscopy/src/DicomMicroscopySRSopClassHandler.js b/extensions/dicom-microscopy/src/DicomMicroscopySRSopClassHandler.js index a195a801e8a..c2459ab2e67 100644 --- a/extensions/dicom-microscopy/src/DicomMicroscopySRSopClassHandler.js +++ b/extensions/dicom-microscopy/src/DicomMicroscopySRSopClassHandler.js @@ -62,6 +62,7 @@ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager) } = instance; const displaySet = { + isOverlayDisplaySet: true, plugin: 'microscopy', Modality: 'SR', altImageText: 'Microscopy SR', diff --git a/extensions/dicom-microscopy/src/DicomMicroscopyViewport.tsx b/extensions/dicom-microscopy/src/DicomMicroscopyViewport.tsx index 8975a912da7..46bf0ff4d40 100644 --- a/extensions/dicom-microscopy/src/DicomMicroscopyViewport.tsx +++ b/extensions/dicom-microscopy/src/DicomMicroscopyViewport.tsx @@ -172,7 +172,7 @@ function DicomMicroscopyViewport({ microscopyService.clearAnnotations(); let smDisplaySet = displaySet; - if (displaySet.Modality === 'SR') { + if (displaySet.isOverlayDisplaySet) { // for SR displaySet, let's load the actual image displaySet smDisplaySet = displaySet.getSourceDisplaySet(); } @@ -180,7 +180,7 @@ function DicomMicroscopyViewport({ await loadViewer(smDisplaySet.others); - if (displaySet.Modality === 'SR') { + if (displaySet.isOverlayDisplaySet && !displaySet.isLoaded && !displaySet.isLoading) { displaySet.load(smDisplaySet); } }, @@ -205,8 +205,8 @@ function DicomMicroscopyViewport({ microscopyService.clearAnnotations(); - // loading SR - if (displaySet.Modality === 'SR') { + // loading SR - only if not already loaded and not currently loading + if (displaySet.isOverlayDisplaySet && !displaySet.isLoaded && !displaySet.isLoading) { const referencedDisplaySet = displaySet.getSourceDisplaySet(); displaySet.load(referencedDisplaySet); } diff --git a/extensions/dicom-microscopy/src/components/MicroscopyPanel/MicroscopyPanel.tsx b/extensions/dicom-microscopy/src/components/MicroscopyPanel/MicroscopyPanel.tsx index ed367456e0b..ecb79493268 100644 --- a/extensions/dicom-microscopy/src/components/MicroscopyPanel/MicroscopyPanel.tsx +++ b/extensions/dicom-microscopy/src/components/MicroscopyPanel/MicroscopyPanel.tsx @@ -1,13 +1,14 @@ import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import { callInputDialog } from '@ohif/extension-default'; -import { ExtensionManager, CommandsManager, DicomMetadataStore } from '@ohif/core'; +import { ExtensionManager, CommandsManager, DicomMetadataStore, utils } from '@ohif/core'; import { DataRow } from '@ohif/ui-next'; import { withTranslation, WithTranslation } from 'react-i18next'; import { EVENTS as MicroscopyEvents } from '../../services/MicroscopyService'; import dcmjs from 'dcmjs'; import constructSR from '../../utils/constructSR'; -import { saveByteArray } from '../../utils/saveByteArray'; + +const { downloadDicom } = utils; let saving = false; const { datasetToBuffer } = dcmjs.data; @@ -197,7 +198,7 @@ function MicroscopyPanel(props: IMicroscopyPanelProps) { if (dataSource.wadoRoot == 'saveDicom') { // download as DICOM file const part10Buffer = datasetToBuffer(dataset); - saveByteArray(part10Buffer, `sr-microscopy.dcm`); + downloadDicom(part10Buffer, { filename: `sr-microscopy.dcm` }); } else { // Save into Web Data source const { StudyInstanceUID } = dataset; @@ -277,11 +278,11 @@ function MicroscopyPanel(props: IMicroscopyPanelProps) { * Handler for "Edit" action of an annotation item * @param param0 */ - const onMeasurementItemEditHandler = ({ uid, isActive }: { uid: string; isActive: boolean }) => { + const onMeasurementItemEditHandler = ({ uid }: { uid: string; isActive: boolean }) => { props.commandsManager.runCommand('setLabel', { uid }, 'MICROSCOPY'); }; - const onMeasurementDeleteHandler = ({ uid, isActive }: { uid: string; isActive: boolean }) => { + const onMeasurementDeleteHandler = ({ uid }: { uid: string; isActive: boolean }) => { const roiAnnotation = microscopyService.getAnnotation(uid); microscopyService.removeAnnotation(roiAnnotation); }; diff --git a/extensions/dicom-microscopy/src/components/ViewportOverlay/index.tsx b/extensions/dicom-microscopy/src/components/ViewportOverlay/index.tsx index 7b8175b09f6..80058c10179 100644 --- a/extensions/dicom-microscopy/src/components/ViewportOverlay/index.tsx +++ b/extensions/dicom-microscopy/src/components/ViewportOverlay/index.tsx @@ -3,7 +3,8 @@ import classnames from 'classnames'; import listComponentGenerator from './listComponentGenerator'; import './ViewportOverlay.css'; -import { formatDICOMDate, formatDICOMTime, formatNumberPrecision } from './utils'; +import { formatDICOMDate } from '@ohif/ui-next'; +import { formatDICOMTime, formatNumberPrecision } from './utils'; import { utils } from '@ohif/core'; const { formatPN } = utils; diff --git a/extensions/dicom-microscopy/src/components/ViewportOverlay/utils.ts b/extensions/dicom-microscopy/src/components/ViewportOverlay/utils.ts index d47dd14c15a..795ff0f64e1 100644 --- a/extensions/dicom-microscopy/src/components/ViewportOverlay/utils.ts +++ b/extensions/dicom-microscopy/src/components/ViewportOverlay/utils.ts @@ -1,5 +1,7 @@ import moment from 'moment'; +import i18n from 'i18next'; import * as cornerstone from '@cornerstonejs/core'; +import { formatDICOMDate } from '@ohif/ui-next'; /** * Checks if value is valid. @@ -24,17 +26,6 @@ export function formatNumberPrecision(number, precision) { } } -/** - * 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 }; diff --git a/extensions/dicom-microscopy/src/helpers/formatDICOMDate.js b/extensions/dicom-microscopy/src/helpers/formatDICOMDate.js index c048df47276..b136ae0d89f 100644 --- a/extensions/dicom-microscopy/src/helpers/formatDICOMDate.js +++ b/extensions/dicom-microscopy/src/helpers/formatDICOMDate.js @@ -1,11 +1,11 @@ -import moment from 'moment'; - /** * Formats DICOM date. + * Re-exported from @ohif/ui-next for backward compatibility. * * @param {string} date * @param {string} strFormat */ -export default function formatDICOMDate(date, strFormat = 'MMM D, YYYY') { - return moment(date, 'YYYYMMDD').format(strFormat); -} +import { formatDICOMDate } from '@ohif/ui-next'; + +export { formatDICOMDate }; +export default formatDICOMDate; diff --git a/extensions/dicom-microscopy/src/helpers/index.js b/extensions/dicom-microscopy/src/helpers/index.js index 59468982c5f..73f15ee8db1 100644 --- a/extensions/dicom-microscopy/src/helpers/index.js +++ b/extensions/dicom-microscopy/src/helpers/index.js @@ -1,5 +1,5 @@ import formatDICOMPatientName from './formatDICOMPatientName'; -import formatDICOMDate from './formatDICOMDate'; +import { formatDICOMDate } from '@ohif/ui-next'; import formatDICOMTime from './formatDICOMTime'; import formatNumberPrecision from './formatNumberPrecision'; import isValidNumber from './isValidNumber'; diff --git a/extensions/dicom-microscopy/src/index.tsx b/extensions/dicom-microscopy/src/index.tsx index 4023222c357..6bea12a21c9 100644 --- a/extensions/dicom-microscopy/src/index.tsx +++ b/extensions/dicom-microscopy/src/index.tsx @@ -7,6 +7,7 @@ import { Types } from '@ohif/core'; import { useViewportGrid } from '@ohif/ui-next'; import getDicomMicroscopySRSopClassHandler from './DicomMicroscopySRSopClassHandler'; +import getDicomMicroscopyANNSopClassHandler from './DicomMicroscopyANNSopClassHandler'; import MicroscopyService from './services/MicroscopyService'; import { useResizeDetector } from 'react-resize-detector'; import debounce from 'lodash.debounce'; @@ -43,7 +44,7 @@ const extension: Types.Extensions.Extension = { * {name, component} object. Example of a viewport module is the CornerstoneViewport * that is provided by the Cornerstone extension in OHIF. */ - getViewportModule({ servicesManager, extensionManager, commandsManager }) { + getViewportModule({ servicesManager }) { /** * * @param props {*} @@ -150,7 +151,10 @@ const extension: Types.Extensions.Extension = { * Examples include the default sop class handler provided by the default extension */ getSopClassHandlerModule(params) { - return [getDicomMicroscopySRSopClassHandler(params)]; + return [ + getDicomMicroscopySRSopClassHandler(params), + getDicomMicroscopyANNSopClassHandler(params), + ]; }, getPanelModule, diff --git a/extensions/dicom-microscopy/src/services/MicroscopyService.ts b/extensions/dicom-microscopy/src/services/MicroscopyService.ts index e59f048af6c..5a25b20fcaf 100644 --- a/extensions/dicom-microscopy/src/services/MicroscopyService.ts +++ b/extensions/dicom-microscopy/src/services/MicroscopyService.ts @@ -22,7 +22,7 @@ export default class MicroscopyService extends PubSubService { return { name: 'microscopyService', altName: 'MicroscopyService', - create: (props) => { + create: props => { return new MicroscopyService(props); }, }; @@ -71,7 +71,7 @@ export default class MicroscopyService extends PubSubService { } public importDicomMicroscopyViewer(): Promise { - return this.peerImport("dicom-microscopy-viewer"); + return this.peerImport('dicom-microscopy-viewer'); } /** @@ -260,6 +260,11 @@ export default class MicroscopyService extends PubSubService { return Array.from(this.managedViewers).filter(filter); } + getManagedViewersForViewport(viewportId) { + const filter = managedViewer => managedViewer.viewportId === viewportId; + return Array.from(this.managedViewers).filter(filter); + } + /** * Restores the created annotations for the viewer being added * @@ -290,6 +295,13 @@ export default class MicroscopyService extends PubSubService { * @returns {ViewerManager} managed viewer */ addViewer(viewer, viewportId, container, studyInstanceUID, seriesInstanceUID) { + // Check if a viewer already exists for this viewportId + const existingViewer = Array.from(this.managedViewers).find(mv => mv.viewportId === viewportId); + if (existingViewer) { + // If a viewer exists, remove it first + this.removeViewer(existingViewer.viewer); + } + const managedViewer = new ViewerManager( viewer, viewportId, @@ -355,6 +367,10 @@ export default class MicroscopyService extends PubSubService { } }); + if (recentDisplaySet.isLoading) { + return; + } + recentDisplaySet.isLoading = true; recentDisplaySet.load(smDisplaySet); diff --git a/extensions/dicom-microscopy/src/utils/RoiAnnotation.js b/extensions/dicom-microscopy/src/utils/RoiAnnotation.js index 550a791008b..2c8f8e93000 100644 --- a/extensions/dicom-microscopy/src/utils/RoiAnnotation.js +++ b/extensions/dicom-microscopy/src/utils/RoiAnnotation.js @@ -1,6 +1,7 @@ import areaOfPolygon from './areaOfPolygon'; import { PubSubService } from '@ohif/core'; +import i18n from '@ohif/i18n'; const EVENTS = { LABEL_UPDATED: 'labelUpdated', @@ -168,7 +169,8 @@ class RoiAnnotation extends PubSubService { * @returns {String} Text with geometry type and label */ getDetailedLabel() { - const label = this.label ? `${this.label}` : '(empty)'; + const translatedEmpty = i18n?.t('MeasurementTable:empty') || '(empty)'; + const label = this.label ? `${this.label}` : translatedEmpty; return label; } diff --git a/extensions/dicom-microscopy/src/utils/dcmCodeValues.js b/extensions/dicom-microscopy/src/utils/dcmCodeValues.js index 82d489919be..2d194ed4e53 100644 --- a/extensions/dicom-microscopy/src/utils/dcmCodeValues.js +++ b/extensions/dicom-microscopy/src/utils/dcmCodeValues.js @@ -9,6 +9,9 @@ const DCM_CODE_VALUES = { SHORT_AXIS: 'G-A186', LONG_AXIS: 'G-A185', ELLIPSE_AREA: 'G-D7FE', // TODO: Remove this + ANNOTATION: '121071', + ANNOTATION_GROUP: '121072', + ANNOTATION_LABEL: '121073', }; export default DCM_CODE_VALUES; diff --git a/extensions/dicom-microscopy/src/utils/getSourceDisplaySet.js b/extensions/dicom-microscopy/src/utils/getSourceDisplaySet.js index a2c7439e0bf..5a42a34f344 100644 --- a/extensions/dicom-microscopy/src/utils/getSourceDisplaySet.js +++ b/extensions/dicom-microscopy/src/utils/getSourceDisplaySet.js @@ -6,7 +6,15 @@ * @returns */ export default function getSourceDisplaySet(allDisplaySets, microscopySRDisplaySet) { - const { ReferencedFrameOfReferenceUID } = microscopySRDisplaySet; + const { ReferencedFrameOfReferenceUID, metadata } = microscopySRDisplaySet; + + if (metadata.ReferencedSeriesSequence) { + const { ReferencedSeriesSequence } = metadata; + const referencedSeries = ReferencedSeriesSequence[0]; + const { SeriesInstanceUID } = referencedSeries; + const displaySets = allDisplaySets.filter(ds => ds.SeriesInstanceUID === SeriesInstanceUID); + return displaySets[0]; + } const otherDisplaySets = allDisplaySets.filter( ds => ds.displaySetInstanceUID !== microscopySRDisplaySet.displaySetInstanceUID diff --git a/extensions/dicom-microscopy/src/utils/loadAnnotation.js b/extensions/dicom-microscopy/src/utils/loadAnnotation.js new file mode 100644 index 00000000000..dcbc7a79954 --- /dev/null +++ b/extensions/dicom-microscopy/src/utils/loadAnnotation.js @@ -0,0 +1,95 @@ +import getDicomWebClient from './dicomWebClient'; + +/** + * Loads and displays DICOM Microscopy Bulk Simple Annotations. + * + * This utility function: + * 1. Retrieves series metadata from a DICOMweb server using study and series instance UIDs + * 2. Converts metadata into MicroscopyBulkSimpleAnnotations objects + * 3. Adds annotations to the viewer in groups (identified by AnnotationGroupUID) + * 4. Applies a consistent yellow color ([255, 234, 0]) to all annotation groups + * 5. Makes the annotation groups visible in the viewer + * + * @param {Object} params - The parameters object + * @param {Object} params.microscopyService - Service for handling microscopy operations + * @param {Object} params.displaySet - The display set containing metadata + * @param {Object} params.extensionManager - Manager for extensions + * @param {Object} params.servicesManager - Manager for services + * @returns {Promise} A promise that resolves with the loaded display set + */ +export default function loadAnnotation({ + microscopyService, + displaySet, + extensionManager, + servicesManager, +}) { + const { uiNotificationService } = servicesManager.services; + return new Promise(async (resolve, reject) => { + try { + displaySet.isLoading = true; + const { metadata } = displaySet; + + const dicomMicroscopyModule = await microscopyService.importDicomMicroscopyViewer(); + + const client = getDicomWebClient({ + extensionManager, + servicesManager, + }); + + const viewportId = servicesManager.services.viewportGridService.getActiveViewportId(); + const managedViewers = microscopyService.getManagedViewersForViewport(viewportId); + const managedViewer = managedViewers[0]; + + client + .retrieveSeriesMetadata({ + studyInstanceUID: metadata.StudyInstanceUID, + seriesInstanceUID: metadata.SeriesInstanceUID, + }) + .then(async retrievedMetadata => { + const annotations = retrievedMetadata.map( + metadata => + new dicomMicroscopyModule.metadata.MicroscopyBulkSimpleAnnotations({ metadata }) + ); + + uiNotificationService.show({ + message: 'Loading annotations...', + type: 'info', + }); + + await Promise.all( + annotations.map(async ann => { + try { + await managedViewer.viewer.addAnnotationGroups(ann); + + ann.AnnotationGroupSequence.forEach(item => { + const annotationGroupUID = item.AnnotationGroupUID; + managedViewer.viewer.setAnnotationGroupStyle(annotationGroupUID, { + color: [255, 234, 0], + }); + }); + + ann.AnnotationGroupSequence.forEach(item => { + const annotationGroupUID = item.AnnotationGroupUID; + managedViewer.viewer.showAnnotationGroup(annotationGroupUID); + }); + } catch (error) { + console.error('failed to add annotation groups:', error); + uiNotificationService.show({ + title: 'Error loading annotations', + message: error.message, + type: 'error', + }); + } + }) + ); + + displaySet.isLoaded = true; + displaySet.isLoading = false; + resolve(displaySet); + }); + } catch (error) { + console.error('Error loading annotation:', error); + reject(error); + } + }); +} diff --git a/extensions/dicom-microscopy/src/utils/saveByteArray.ts b/extensions/dicom-microscopy/src/utils/saveByteArray.ts deleted file mode 100644 index 123f1c17f42..00000000000 --- a/extensions/dicom-microscopy/src/utils/saveByteArray.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Trigger file download from an array buffer - * @param buffer - * @param filename - */ -export function saveByteArray(buffer: ArrayBuffer, filename: string) { - const blob = new Blob([buffer], { type: 'application/dicom' }); - const link = document.createElement('a'); - link.href = window.URL.createObjectURL(blob); - link.download = filename; - link.click(); -} diff --git a/extensions/dicom-pdf/CHANGELOG.md b/extensions/dicom-pdf/CHANGELOG.md index 4752378bdfc..7c6d244bd67 100644 --- a/extensions/dicom-pdf/CHANGELOG.md +++ b/extensions/dicom-pdf/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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-dicom-pdf @@ -11,7 +11,1096 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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-dicom-pdf + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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 @ohif/extension-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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 @ohif/extension-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [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-dicom-pdf + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/extension-dicom-pdf + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/extension-dicom-pdf diff --git a/extensions/dicom-pdf/package.json b/extensions/dicom-pdf/package.json index 972d58bccfb..dd24d2a78ce 100644 --- a/extensions/dicom-pdf/package.json +++ b/extensions/dicom-pdf/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-dicom-pdf", - "version": "3.11.1", + "version": "3.12.0", "description": "OHIF extension for PDF display", "author": "OHIF", "license": "MIT", @@ -30,9 +30,9 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0", + "@ohif/ui": "3.12.0", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", diff --git a/extensions/dicom-video/CHANGELOG.md b/extensions/dicom-video/CHANGELOG.md index d6e66a217a2..c082ed3ddb8 100644 --- a/extensions/dicom-video/CHANGELOG.md +++ b/extensions/dicom-video/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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-dicom-video @@ -11,7 +11,1096 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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-dicom-video + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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 @ohif/extension-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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 @ohif/extension-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [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-dicom-video + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/extension-dicom-video + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/extension-dicom-video diff --git a/extensions/dicom-video/package.json b/extensions/dicom-video/package.json index 48efff6ee3d..8fd996e5fd8 100644 --- a/extensions/dicom-video/package.json +++ b/extensions/dicom-video/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-dicom-video", - "version": "3.11.1", + "version": "3.12.0", "description": "OHIF extension for video display", "author": "OHIF", "license": "MIT", @@ -30,9 +30,9 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0", + "@ohif/ui": "3.12.0", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", diff --git a/extensions/measurement-tracking/CHANGELOG.md b/extensions/measurement-tracking/CHANGELOG.md index 3a0fb7e833c..381db5f6012 100644 --- a/extensions/measurement-tracking/CHANGELOG.md +++ b/extensions/measurement-tracking/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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-measurement-tracking @@ -11,7 +11,1144 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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-measurement-tracking + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [3.12.0-beta.133](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.132...v3.12.0-beta.133) (2026-02-04) + + +### Bug Fixes + +* **jump-to-label-map:** Use `undefined` for viewportId for arrow navigation. ([#5774](https://github.com/OHIF/Viewers/issues/5774)) ([512aa3b](https://github.com/OHIF/Viewers/commit/512aa3b07f4c783040acd67e3fd2103559a06522)) + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [3.12.0-beta.122](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.121...v3.12.0-beta.122) (2026-01-13) + + +### Bug Fixes + +* **segmentation:** List surface representations in the segmentation table for 3D views. ([#5700](https://github.com/OHIF/Viewers/issues/5700)) ([82e6a18](https://github.com/OHIF/Viewers/commit/82e6a18735b2e2278d6b95037d82bd3cb529104d)) + + + + + +# [3.12.0-beta.121](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.120...v3.12.0-beta.121) (2026-01-12) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix a segmentation exception after capture. ([#5701](https://github.com/OHIF/Viewers/issues/5701)) ([aba4855](https://github.com/OHIF/Viewers/commit/aba48552765d03cf82e6a92cdd443b246560c4fd)) + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [3.12.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.110...v3.12.0-beta.111) (2025-12-18) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix segment visibility issue. ([#5664](https://github.com/OHIF/Viewers/issues/5664)) ([3440f66](https://github.com/OHIF/Viewers/commit/3440f66ebd0900c0f5b1a5edbdf6f3b5fc72bbcf)) + + + + + +# [3.12.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.109...v3.12.0-beta.110) (2025-12-18) + + +### Bug Fixes + +* prevent annotation from appearing in active viewport when switching series with different Frame of Reference UID ([#5630](https://github.com/OHIF/Viewers/issues/5630)) ([658994c](https://github.com/OHIF/Viewers/commit/658994c854a03203054168dcfd383c0a6e320df0)) + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16) + + +### Bug Fixes + +* preclinical mode not working ([#5631](https://github.com/OHIF/Viewers/issues/5631)) ([2de81ef](https://github.com/OHIF/Viewers/commit/2de81efa7e6be534972cd763271c038b293d86f0)) + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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 @ohif/extension-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19) + + +### Bug Fixes + +* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f)) + + + + + +# [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-measurement-tracking + + + + + +# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11) + + +### Bug Fixes + +* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f)) + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05) + + +### Bug Fixes + +* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece)) + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28) + + +### Bug Fixes + +* **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](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190)) + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20) + + +### Bug Fixes + +* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404) + + + + + +# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18) + + +### Bug Fixes + +* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462)) + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [3.12.0-beta.48](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.47...v3.12.0-beta.48) (2025-10-07) + + +### Bug Fixes + +* **measurementTrackingMachine:** remove unreachable states ([#5460](https://github.com/OHIF/Viewers/issues/5460)) ([7f12f39](https://github.com/OHIF/Viewers/commit/7f12f398fb145a31e5557dbee51c0a9b0f40a35b)) + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [3.12.0-beta.43](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.42...v3.12.0-beta.43) (2025-10-06) + + +### Features + +* **MeasurementService:** add rendering of unmapped measurements ([#5416](https://github.com/OHIF/Viewers/issues/5416)) ([851e74d](https://github.com/OHIF/Viewers/commit/851e74d7b867a806befb5d85fd71ff9a75e9f2d2)) + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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 @ohif/extension-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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 @ohif/extension-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [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-measurement-tracking + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + + +### Features + +* Add WSI bulk data annotations support (ANN) ([#4972](https://github.com/OHIF/Viewers/issues/4972)) ([2ed6818](https://github.com/OHIF/Viewers/commit/2ed681817142b13971bb17b3058be26bbab6b90b)) + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/extension-measurement-tracking + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/extension-measurement-tracking diff --git a/extensions/measurement-tracking/package.json b/extensions/measurement-tracking/package.json index f667538c718..bc4a881bb25 100644 --- a/extensions/measurement-tracking/package.json +++ b/extensions/measurement-tracking/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-measurement-tracking", - "version": "3.11.1", + "version": "3.12.0", "description": "Tracking features and functionality for basic image viewing", "author": "OHIF Core Team", "license": "MIT", @@ -32,14 +32,14 @@ "start": "yarn run dev" }, "peerDependencies": { - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/tools": "4.5.13", - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/ui": "3.11.1", + "@cornerstonejs/core": "4.15.29", + "@cornerstonejs/tools": "4.15.29", + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0", + "@ohif/extension-default": "3.12.0", + "@ohif/ui": "3.12.0", "classnames": "2.5.1", - "dcmjs": "0.43.1", + "dcmjs": "0.49.4", "lodash.debounce": "4.0.8", "prop-types": "15.8.1", "react": "18.3.1", @@ -49,7 +49,7 @@ }, "dependencies": { "@babel/runtime": "7.28.2", - "@ohif/ui": "3.11.1", + "@ohif/ui": "3.12.0", "@xstate/react": "3.2.2", "xstate": "4.38.3" } diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js index b84040c733d..d14523613ef 100644 --- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js +++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js @@ -37,9 +37,6 @@ const machineConfiguration = { target: 'promptLabelAnnotation', actions: ['setPreviousState'], }, - { - target: 'off', - }, ], }, }, @@ -139,9 +136,6 @@ const machineConfiguration = { 'clearAllMeasurements', ], }, - { - target: 'idle', - }, ], SET_TRACKED_SERIES: [ { diff --git a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking.tsx b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking.tsx index 6735066da69..c8523dd2e45 100644 --- a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking.tsx +++ b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking.tsx @@ -13,7 +13,8 @@ import { import { useTrackedMeasurements } from '../getContextModule'; import { UntrackSeriesModal } from './PanelStudyBrowserTracking/untrackSeriesModal'; -const { filterMeasurementsBySeriesUID, filterAny } = utils.MeasurementFilters; +const { filterMeasurementsBySeriesUID, filterAny } = + utils.MeasurementFilters; function PanelMeasurementTableTracking(props) { const [viewportGrid] = useViewportGrid(); @@ -22,7 +23,9 @@ function PanelMeasurementTableTracking(props) { const [trackedMeasurements, sendTrackedMeasurementsEvent] = useTrackedMeasurements(); const { trackedStudy, trackedSeries } = trackedMeasurements.context; - const measurementFilter = trackedStudy ? filterMeasurementsBySeriesUID(trackedSeries) : filterAny; + const measurementFilter = trackedStudy + ? filterMeasurementsBySeriesUID(trackedSeries) + : filterAny; const onUntrackConfirm = () => { sendTrackedMeasurementsEvent('UNTRACK_ALL', {}); diff --git a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx index 411a56ec7c3..575ed7c16a2 100644 --- a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx +++ b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx @@ -24,7 +24,7 @@ export default function PanelStudyBrowserTracking({ const checkDirtyMeasurements = displaySetInstanceUID => { const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID); - if (displaySet.Modality === 'SR') { + if (displaySet.Modality === 'SR' || displaySet.Modality === 'ANN') { const activeViewportId = viewportGridService.getActiveViewportId(); sendTrackedMeasurementsEvent('CHECK_DIRTY', { viewportId: activeViewportId, @@ -95,7 +95,7 @@ export default function PanelStudyBrowserTracking({ seriesNumber: ds.SeriesNumber, modality: ds.Modality, seriesDate: ds.SeriesDate ? new Date(ds.SeriesDate).toLocaleDateString() : '', - numInstances: ds.numImageFrames, + numInstances: ds.numImageFrames ?? ds.instances?.length, loadingProgress, countIcon: ds.countIcon, messages: ds.messages, diff --git a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/untrackSeriesModal.tsx b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/untrackSeriesModal.tsx index c7b0809a31a..4a123d942f5 100644 --- a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/untrackSeriesModal.tsx +++ b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/untrackSeriesModal.tsx @@ -12,8 +12,14 @@ export function UntrackSeriesModal({ hide, onConfirm, message }) {
- Cancel + + Cancel + { onConfirm(); hide(); diff --git a/extensions/test-extension/CHANGELOG.md b/extensions/test-extension/CHANGELOG.md index 80576ccb9f2..b6b2f3f8365 100644 --- a/extensions/test-extension/CHANGELOG.md +++ b/extensions/test-extension/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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-test @@ -11,7 +11,1096 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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-test + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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 @ohif/extension-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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 @ohif/extension-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [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-test + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/extension-test + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/extension-test diff --git a/extensions/test-extension/package.json b/extensions/test-extension/package.json index fec007ac8bd..71e58abc4da 100644 --- a/extensions/test-extension/package.json +++ b/extensions/test-extension/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-test", - "version": "3.11.1", + "version": "3.12.0", "description": "OHIF extension used inside e2e testing", "author": "OHIF", "license": "MIT", @@ -30,9 +30,9 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0", + "@ohif/ui": "3.12.0", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", diff --git a/extensions/tmtv/CHANGELOG.md b/extensions/tmtv/CHANGELOG.md index bb8d7bdc7c0..f8ba5a3e1d5 100644 --- a/extensions/tmtv/CHANGELOG.md +++ b/extensions/tmtv/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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-tmtv @@ -11,7 +11,1099 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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-tmtv + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [3.12.0-beta.51](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.50...v3.12.0-beta.51) (2025-10-07) + + +### Bug Fixes + +* validation for Percentage of Max SUV input field in TMTV module ([#5417](https://github.com/OHIF/Viewers/issues/5417)) ([5040c94](https://github.com/OHIF/Viewers/commit/5040c947d2f47e5ec66285fa353e3e0ffd127ba7)) + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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 @ohif/extension-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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 @ohif/extension-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [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-tmtv + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/extension-tmtv + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/extension-tmtv diff --git a/extensions/tmtv/package.json b/extensions/tmtv/package.json index dd53b0cf899..bfd28556fe8 100644 --- a/extensions/tmtv/package.json +++ b/extensions/tmtv/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/extension-tmtv", - "version": "3.11.1", + "version": "3.12.0", "description": "OHIF extension for Total Metabolic Tumor Volume", "author": "OHIF", "license": "MIT", @@ -30,9 +30,9 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/ui": "3.11.1", - "dcmjs": "0.43.1", + "@ohif/core": "3.12.0", + "@ohif/ui": "3.12.0", + "dcmjs": "0.49.4", "dicom-parser": "1.8.21", "hammerjs": "2.0.8", "prop-types": "15.8.1", diff --git a/extensions/tmtv/src/Panels/PanelPetSUV.tsx b/extensions/tmtv/src/Panels/PanelPetSUV.tsx index f74941f3eef..7b0a1ca1747 100644 --- a/extensions/tmtv/src/Panels/PanelPetSUV.tsx +++ b/extensions/tmtv/src/Panels/PanelPetSUV.tsx @@ -253,10 +253,10 @@ export default function PanelPetSUV() {
diff --git a/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/ROIThresholdConfiguration.tsx b/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/ROIThresholdConfiguration.tsx index c0375001387..98c4beca34f 100644 --- a/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/ROIThresholdConfiguration.tsx +++ b/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/ROIThresholdConfiguration.tsx @@ -14,14 +14,27 @@ import { useTranslation } from 'react-i18next'; export const ROI_STAT = 'roi_stat'; const RANGE = 'range'; -const options = [ - { value: ROI_STAT, label: 'Max', placeHolder: 'Max' }, - { value: RANGE, label: 'Range', placeHolder: 'Range' }, -]; - function ROIThresholdConfiguration({ config, dispatch, runCommand }) { const { t } = useTranslation('ROIThresholdConfiguration'); + const options = [ + { value: ROI_STAT, label: t('Max'), placeHolder: t('Max') }, + { value: RANGE, label: t('Range'), placeHolder: t('Range') }, + ]; + + const handlePercentageOfMaxSUVChange = (e: React.ChangeEvent) => { + let value = e.target.value; + + if (value === '.') { + value = '0.'; + } + + if (isNaN(Number(value)) || Number(value) < 0 || Number(value) > 1) { + return; + } + dispatch({ type: 'setWeight', payload: { weight: value } }); + }; + return (
@@ -32,12 +45,7 @@ function ROIThresholdConfiguration({ config, dispatch, runCommand }) { { - dispatch({ - type: 'setWeight', - payload: { - weight: e.target.value, - }, - }); - }} + onChange={handlePercentageOfMaxSUVChange} />
)} @@ -99,7 +101,7 @@ function ROIThresholdConfiguration({ config, dispatch, runCommand }) {
{/* Header */} - + {/* CT Row */}
@@ -113,12 +115,7 @@ function ROIThresholdConfiguration({ config, dispatch, runCommand }) { type="text" value={config.ctLower} onChange={e => { - dispatch({ - type: 'setThreshold', - payload: { - ctLower: e.target.value, - }, - }); + dispatch({ type: 'setThreshold', payload: { ctLower: e.target.value } }); }} />
@@ -128,12 +125,7 @@ function ROIThresholdConfiguration({ config, dispatch, runCommand }) { type="text" value={config.ctUpper} onChange={e => { - dispatch({ - type: 'setThreshold', - payload: { - ctUpper: e.target.value, - }, - }); + dispatch({ type: 'setThreshold', payload: { ctUpper: e.target.value } }); }} />
@@ -152,12 +144,7 @@ function ROIThresholdConfiguration({ config, dispatch, runCommand }) { type="text" value={config.ptLower} onChange={e => { - dispatch({ - type: 'setThreshold', - payload: { - ptLower: e.target.value, - }, - }); + dispatch({ type: 'setThreshold', payload: { ptLower: e.target.value } }); }} />
@@ -167,12 +154,7 @@ function ROIThresholdConfiguration({ config, dispatch, runCommand }) { type="text" value={config.ptUpper} onChange={e => { - dispatch({ - type: 'setThreshold', - payload: { - ptUpper: e.target.value, - }, - }); + dispatch({ type: 'setThreshold', payload: { ptUpper: e.target.value } }); }} />
diff --git a/extensions/tmtv/src/Panels/RectangleROIOptions.tsx b/extensions/tmtv/src/Panels/RectangleROIOptions.tsx index 390ddf0dbb1..d79800dc288 100644 --- a/extensions/tmtv/src/Panels/RectangleROIOptions.tsx +++ b/extensions/tmtv/src/Panels/RectangleROIOptions.tsx @@ -6,6 +6,7 @@ import ROIThresholdConfiguration, { import * as cs3dTools from '@cornerstonejs/tools'; import { useSystem } from '@ohif/core'; import { useSegmentations } from '@ohif/extension-cornerstone'; +import { useTranslation } from 'react-i18next'; const LOWER_CT_THRESHOLD_DEFAULT = -1024; const UPPER_CT_THRESHOLD_DEFAULT = 1024; @@ -46,6 +47,7 @@ function RectangleROIOptions() { const { commandsManager } = useSystem(); const segmentations = useSegmentations(); const activeSegmentation = segmentations[0]; + const { t } = useTranslation('ROIThresholdConfiguration'); const runCommand = useCallback( (commandName, commandOptions = {}) => { @@ -92,7 +94,7 @@ function RectangleROIOptions() { className="my-3 mr-auto w-20" onClick={handleROIThresholding} > - Run + {t('Run')} )}
diff --git a/extensions/tmtv/src/getPanelModule.tsx b/extensions/tmtv/src/getPanelModule.tsx index ec76c495ecc..e26e1940bae 100644 --- a/extensions/tmtv/src/getPanelModule.tsx +++ b/extensions/tmtv/src/getPanelModule.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { PanelPetSUV, PanelROIThresholdExport } from './Panels'; import { Toolbox } from '@ohif/extension-default'; import PanelTMTV from './Panels/PanelTMTV'; +import i18n from '@ohif/i18n'; function getPanelModule({ commandsManager, extensionManager, servicesManager }) { const { toolbarService } = servicesManager.services; @@ -14,7 +15,7 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager }) return ( ); }; @@ -28,7 +29,7 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager }) <> setDepthGuideCommand(!depthGuide)} > - Depth guide toggle + {t('Depth guide toggle')}
@@ -198,7 +200,7 @@ export default function USAnnotationPanel() { className="cursor-pointer" onClick={() => setShowPleuraPercentageCommand(!showPleuraPct)} > - Show pleura percentage + {t('Show pleura percentage')}
@@ -208,7 +210,7 @@ export default function USAnnotationPanel() { const renderSectorAnnotations = () => (
- +
- Pleura line + {t('Pleura line')} - B-line + {t('B-line')} @@ -239,7 +241,7 @@ export default function USAnnotationPanel() { } > - B-line annotation + {t('B-line annotation')} @@ -247,7 +249,7 @@ export default function USAnnotationPanel() { } > - Pleura annotation + {t('Pleura annotation')} @@ -261,7 +263,7 @@ export default function USAnnotationPanel() { className="data-[state=checked]:bg-blue-500" />
@@ -286,7 +288,7 @@ export default function USAnnotationPanel() { */}
diff --git a/lerna.json b/lerna.json index 8ff5b7365ca..5f9b3579523 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "3.11.1", + "version": "3.12.0", "packages": ["extensions/*", "platform/*", "modes/*", "addOns/externals/*"], "npmClient": "yarn" } diff --git a/modes/basic-dev-mode/CHANGELOG.md b/modes/basic-dev-mode/CHANGELOG.md index 401484eabaf..724e30d4e92 100644 --- a/modes/basic-dev-mode/CHANGELOG.md +++ b/modes/basic-dev-mode/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/mode-basic-dev-mode @@ -11,7 +11,1093 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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 @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25) + + +### Bug Fixes + +* **i18n:** Add and update i18n Translation for Toolbar Tool Names ([#5392](https://github.com/OHIF/Viewers/issues/5392)) ([7783d0f](https://github.com/OHIF/Viewers/commit/7783d0f558bbb7016c29513b6fe76ed069dbd75c)) + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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 @ohif/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [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/mode-basic-dev-mode + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/mode-basic-dev-mode + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/mode-basic-dev-mode diff --git a/modes/basic-dev-mode/package.json b/modes/basic-dev-mode/package.json index de9032430fc..6fe541a1096 100644 --- a/modes/basic-dev-mode/package.json +++ b/modes/basic-dev-mode/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/mode-basic-dev-mode", - "version": "3.11.1", + "version": "3.12.0", "description": "Basic OHIF Viewer Using Cornerstone", "author": "OHIF", "license": "MIT", @@ -31,12 +31,12 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-dicom-pdf": "3.11.1", - "@ohif/extension-dicom-video": "3.11.1" + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone": "3.12.0", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0", + "@ohif/extension-default": "3.12.0", + "@ohif/extension-dicom-pdf": "3.12.0", + "@ohif/extension-dicom-video": "3.12.0" }, "dependencies": { "@babel/runtime": "7.28.2", diff --git a/modes/basic-dev-mode/src/toolbarButtons.ts b/modes/basic-dev-mode/src/toolbarButtons.ts index 2ce8f84958f..5742f994086 100644 --- a/modes/basic-dev-mode/src/toolbarButtons.ts +++ b/modes/basic-dev-mode/src/toolbarButtons.ts @@ -1,4 +1,5 @@ import type { Button } from '@ohif/core/types'; +import i18n from 'i18next'; export const setToolActiveToolbar = { commandName: 'setToolActive', @@ -31,8 +32,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-length', - label: 'Length', - tooltip: 'Length Tool', + label: i18n.t('Buttons:Length'), + tooltip: i18n.t('Buttons:Length Tool'), commands: { ...setToolActiveToolbar, commandOptions: { @@ -48,8 +49,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-bidirectional', - label: 'Bidirectional', - tooltip: 'Bidirectional Tool', + label: i18n.t('Buttons:Bidirectional'), + tooltip: i18n.t('Buttons:Bidirectional Tool'), commands: { ...setToolActiveToolbar, commandOptions: { @@ -65,8 +66,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-ellipse', - label: 'Ellipse', - tooltip: 'Ellipse ROI', + label: i18n.t('Buttons:Ellipse'), + tooltip: i18n.t('Buttons:Ellipse ROI'), commands: { ...setToolActiveToolbar, commandOptions: { @@ -82,8 +83,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-circle', - label: 'Circle', - tooltip: 'Circle Tool', + label: i18n.t('Buttons:Circle'), + tooltip: i18n.t('Buttons:Circle Tool'), commands: { ...setToolActiveToolbar, commandOptions: { @@ -99,8 +100,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-zoom', - label: 'Zoom', - tooltip: 'Zoom', + label: i18n.t('Buttons:Zoom'), + tooltip: i18n.t('Buttons:Zoom'), commands: { ...setToolActiveToolbar, commandOptions: { @@ -116,8 +117,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-move', - label: 'Pan', - tooltip: 'Pan', + label: i18n.t('Buttons:Pan'), + tooltip: i18n.t('Buttons:Pan'), commands: { ...setToolActiveToolbar, commandOptions: { @@ -133,8 +134,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-capture', - label: 'Capture', - tooltip: 'Capture', + label: i18n.t('Buttons:Capture'), + tooltip: i18n.t('Buttons:Capture'), commands: 'showDownloadViewportModal', evaluate: [ 'evaluate.action', @@ -161,8 +162,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-reset', - label: 'Reset View', - tooltip: 'Reset View', + label: i18n.t('Buttons:Reset View'), + tooltip: i18n.t('Buttons:Reset View'), commands: 'resetViewport', evaluate: 'evaluate.action', }, @@ -172,8 +173,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-rotate-right', - label: 'Rotate Right', - tooltip: 'Rotate Right +90', + label: i18n.t('Buttons:Rotate Right'), + tooltip: i18n.t('Buttons:Rotate Right +90'), commands: 'rotateViewportCW', evaluate: 'evaluate.action', }, @@ -183,8 +184,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-flip-horizontal', - label: 'Flip Horizontally', - tooltip: 'Flip Horizontally', + label: i18n.t('Buttons:Flip Horizontally'), + tooltip: i18n.t('Buttons:Flip Horizontally'), commands: 'flipViewportHorizontal', evaluate: 'evaluate.action', }, @@ -194,8 +195,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-stack-scroll', - label: 'Stack Scroll', - tooltip: 'Stack Scroll', + label: i18n.t('Buttons:Stack Scroll'), + tooltip: i18n.t('Buttons:Stack Scroll'), commands: { ...setToolActiveToolbar, commandOptions: { @@ -211,8 +212,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-invert', - label: 'Invert Colors', - tooltip: 'Invert Colors', + label: i18n.t('Buttons:Invert Colors'), + tooltip: i18n.t('Buttons:Invert Colors'), commands: 'invertViewport', evaluate: 'evaluate.action', }, @@ -222,8 +223,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-calibration', - label: 'Calibration Line', - tooltip: 'Calibration Line', + label: i18n.t('Buttons:Calibration Line'), + tooltip: i18n.t('Buttons:Calibration Line'), commands: { ...setToolActiveToolbar, commandOptions: { diff --git a/modes/basic-test-mode/CHANGELOG.md b/modes/basic-test-mode/CHANGELOG.md index 3a50bddff90..91b10ef5716 100644 --- a/modes/basic-test-mode/CHANGELOG.md +++ b/modes/basic-test-mode/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/mode-test @@ -11,7 +11,1093 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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 @ohif/mode-test + + + + + +# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25) + + +### Bug Fixes + +* **i18n:** Add and update i18n Translation for Toolbar Tool Names ([#5392](https://github.com/OHIF/Viewers/issues/5392)) ([7783d0f](https://github.com/OHIF/Viewers/commit/7783d0f558bbb7016c29513b6fe76ed069dbd75c)) + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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 @ohif/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [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/mode-test + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/mode-test + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/mode-test diff --git a/modes/basic-test-mode/package.json b/modes/basic-test-mode/package.json index 58d255d9cd6..f3395299318 100644 --- a/modes/basic-test-mode/package.json +++ b/modes/basic-test-mode/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/mode-test", - "version": "3.11.1", + "version": "3.12.0", "description": "Basic mode for testing", "author": "OHIF", "license": "MIT", @@ -34,14 +34,14 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-dicom-pdf": "3.11.1", - "@ohif/extension-dicom-video": "3.11.1", - "@ohif/extension-measurement-tracking": "3.11.1", - "@ohif/extension-test": "3.11.1" + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone": "3.12.0", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0", + "@ohif/extension-default": "3.12.0", + "@ohif/extension-dicom-pdf": "3.12.0", + "@ohif/extension-dicom-video": "3.12.0", + "@ohif/extension-measurement-tracking": "3.12.0", + "@ohif/extension-test": "3.12.0" }, "dependencies": { "@babel/runtime": "7.28.2", diff --git a/modes/basic-test-mode/src/toolbarButtons.ts b/modes/basic-test-mode/src/toolbarButtons.ts index 7efc60cc01e..3c58935de77 100644 --- a/modes/basic-test-mode/src/toolbarButtons.ts +++ b/modes/basic-test-mode/src/toolbarButtons.ts @@ -5,6 +5,8 @@ import { EVENTS } from '@cornerstonejs/core'; import { ViewportGridService } from '@ohif/core'; import { defaults } from '@ohif/core'; +import i18n from 'i18next'; + const { windowLevelPresets } = defaults; /** @@ -79,8 +81,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.modalityLoadBadge', props: { icon: 'Status', - label: 'Status', - tooltip: 'Status', + label: i18n.t('Buttons:Status'), + tooltip: i18n.t('Buttons:Status'), evaluate: { name: 'evaluate.modalityLoadBadge', hideWhenDisabled: true, @@ -92,8 +94,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.navigationComponent', props: { icon: 'Navigation', - label: 'Navigation', - tooltip: 'Navigate between segments/measurements and manage their visibility', + label: i18n.t('Buttons:Navigation'), + tooltip: i18n.t('Buttons:Navigate between segments/measurements and manage their visibility'), evaluate: { name: 'evaluate.navigationComponent', hideWhenDisabled: true, @@ -105,8 +107,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.trackingStatus', props: { icon: 'TrackingStatus', - label: 'Tracking Status', - tooltip: 'View and manage tracking status of measurements and annotations', + label: i18n.t('Buttons:Tracking Status'), + tooltip: i18n.t('Buttons:View and manage tracking status of measurements and annotations'), evaluate: { name: 'evaluate.trackingStatus', hideWhenDisabled: true, @@ -118,8 +120,10 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.dataOverlayMenu', props: { icon: 'ViewportViews', - label: 'Data Overlay', - tooltip: 'Configure data overlay options and manage foreground/background display sets', + label: i18n.t('Buttons:Data Overlay'), + tooltip: i18n.t( + 'Buttons:Configure data overlay options and manage foreground/background display sets' + ), evaluate: 'evaluate.dataOverlayMenu', }, }, @@ -128,8 +132,10 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.orientationMenu', props: { icon: 'OrientationSwitch', - label: 'Orientation', - tooltip: 'Change viewport orientation between axial, sagittal, coronal and reformat planes', + label: i18n.t('Buttons:Orientation'), + tooltip: i18n.t( + 'Buttons:Change viewport orientation between axial, sagittal, coronal and reformat planes' + ), evaluate: { name: 'evaluate.orientationMenu', }, @@ -140,8 +146,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.windowLevelMenu', props: { icon: 'WindowLevel', - label: 'Window Level', - tooltip: 'Adjust window/level presets and customize image contrast settings', + label: i18n.t('Buttons:Window Level'), + tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'), evaluate: 'evaluate.windowLevelMenu', }, }, @@ -150,8 +156,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.voiManualControlMenu', props: { icon: 'WindowLevelAdvanced', - label: 'Advanced Window Level', - tooltip: 'Advanced window/level settings with manual controls and presets', + label: i18n.t('Buttons:Advanced Window Level'), + tooltip: i18n.t('Buttons:Advanced window/level settings with manual controls and presets'), evaluate: 'evaluate.voiManualControlMenu', }, }, @@ -160,8 +166,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.thresholdMenu', props: { icon: 'Threshold', - label: 'Threshold', - tooltip: 'Image threshold settings', + label: i18n.t('Buttons:Threshold'), + tooltip: i18n.t('Buttons:Image threshold settings'), evaluate: { name: 'evaluate.thresholdMenu', hideWhenDisabled: true, @@ -173,8 +179,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.opacityMenu', props: { icon: 'Opacity', - label: 'Opacity', - tooltip: 'Image opacity settings', + label: i18n.t('Buttons:Opacity'), + tooltip: i18n.t('Buttons:Image opacity settings'), evaluate: { name: 'evaluate.opacityMenu', hideWhenDisabled: true, @@ -186,7 +192,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.colorbar', props: { type: 'tool', - label: 'Colorbar', + label: i18n.t('Buttons:Colorbar'), }, }, _createWwwcPreset(1, 'Soft tissue', '400 / 40'), @@ -199,8 +205,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-window-level', - label: 'Window Level', - tooltip: 'Window Level', + label: i18n.t('Buttons:Window Level'), + tooltip: i18n.t('Buttons:Window Level'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -210,8 +216,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-length', - label: 'Length', - tooltip: 'Length Tool', + label: i18n.t('Buttons:Length'), + tooltip: i18n.t('Buttons:Length Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -221,8 +227,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-bidirectional', - label: 'Bidirectional', - tooltip: 'Bidirectional Tool', + label: i18n.t('Buttons:Bidirectional'), + tooltip: i18n.t('Buttons:Bidirectional Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -232,8 +238,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-annotate', - label: 'Annotation', - tooltip: 'Arrow Annotate', + label: i18n.t('Buttons:Annotation'), + tooltip: i18n.t('Buttons:Arrow Annotate'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -243,8 +249,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-ellipse', - label: 'Ellipse', - tooltip: 'Ellipse ROI', + label: i18n.t('Buttons:Ellipse'), + tooltip: i18n.t('Buttons:Ellipse ROI'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -254,8 +260,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-circle', - label: 'Circle', - tooltip: 'Circle Tool', + label: i18n.t('Buttons:Circle'), + tooltip: i18n.t('Buttons:Circle Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -265,8 +271,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-freehand-roi', - label: 'Freehand ROI', - tooltip: 'Freehand ROI', + label: i18n.t('Buttons:Freehand ROI'), + tooltip: i18n.t('Buttons:Freehand ROI'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -276,8 +282,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-spline-roi', - label: 'Spline ROI', - tooltip: 'Spline ROI', + label: i18n.t('Buttons:Spline ROI'), + tooltip: i18n.t('Buttons:Spline ROI'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -287,8 +293,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-livewire', - label: 'Livewire tool', - tooltip: 'Livewire tool', + label: i18n.t('Buttons:Livewire tool'), + tooltip: i18n.t('Buttons:Livewire tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -298,8 +304,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-zoom', - label: 'Zoom', - tooltip: 'Zoom', + label: i18n.t('Buttons:Zoom'), + tooltip: i18n.t('Buttons:Zoom'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -310,8 +316,8 @@ const toolbarButtons: Button[] = [ props: { type: 'tool', icon: 'tool-move', - label: 'Pan', - tooltip: 'Pan', + label: i18n.t('Buttons:Pan'), + tooltip: i18n.t('Buttons:Pan'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -321,8 +327,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-mpr', - label: 'MPR', - tooltip: 'MPR', + label: i18n.t('Buttons:MPR'), + tooltip: i18n.t('Buttons:MPR'), commands: { commandName: 'toggleHangingProtocol', commandOptions: { @@ -338,8 +344,8 @@ const toolbarButtons: Button[] = [ props: { type: 'tool', icon: 'tool-3d-rotate', - label: '3D Rotate', - tooltip: '3D Rotate', + label: i18n.t('Buttons:3D Rotate'), + tooltip: i18n.t('Buttons:3D Rotate'), commands: setToolActiveToolbar, }, }, @@ -348,8 +354,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-capture', - label: 'Capture', - tooltip: 'Capture', + label: i18n.t('Buttons:Capture'), + tooltip: i18n.t('Buttons:Capture'), commands: 'showDownloadViewportModal', evaluate: [ 'evaluate.action', @@ -376,8 +382,8 @@ const toolbarButtons: Button[] = [ props: { type: 'tool', icon: 'tool-crosshair', - label: 'Crosshairs', - tooltip: 'Crosshairs', + label: i18n.t('Buttons:Crosshairs'), + tooltip: i18n.t('Buttons:Crosshairs'), commands: { commandName: 'setToolActiveToolbar', commandOptions: { @@ -393,8 +399,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-reset', - label: 'Reset View', - tooltip: 'Reset View', + label: i18n.t('Buttons:Reset View'), + tooltip: i18n.t('Buttons:Reset View'), commands: 'resetViewport', evaluate: 'evaluate.action', }, @@ -404,8 +410,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-rotate-right', - label: 'Rotate Right', - tooltip: 'Rotate +90', + label: i18n.t('Buttons:Rotate Right'), + tooltip: i18n.t('Buttons:Rotate +90'), commands: 'rotateViewportCW', evaluate: 'evaluate.action', }, @@ -415,8 +421,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-flip-horizontal', - label: 'Flip Horizontal', - tooltip: 'Flip Horizontally', + label: i18n.t('Buttons:Flip Horizontal'), + tooltip: i18n.t('Buttons:Flip Horizontally'), commands: 'flipViewportHorizontal', evaluate: 'evaluate.viewportProperties.toggle', }, @@ -426,8 +432,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'link', - label: 'Image Slice Sync', - tooltip: 'Enable position synchronization on stack viewports', + label: i18n.t('Buttons:Image Slice Sync'), + tooltip: i18n.t('Buttons:Enable position synchronization on stack viewports'), commands: { commandName: 'toggleSynchronizer', commandOptions: { @@ -448,8 +454,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-referenceLines', - label: 'Reference Lines', - tooltip: 'Show Reference Lines', + label: i18n.t('Buttons:Reference Lines'), + tooltip: i18n.t('Buttons:Show Reference Lines'), commands: 'toggleEnabledDisabledToolbar', evaluate: 'evaluate.cornerstoneTool.toggle', }, @@ -459,8 +465,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'toggle-dicom-overlay', - label: 'Image Overlay', - tooltip: 'Toggle Image Overlay', + label: i18n.t('Buttons:Image Overlay'), + tooltip: i18n.t('Buttons:Toggle Image Overlay'), commands: 'toggleEnabledDisabledToolbar', evaluate: 'evaluate.cornerstoneTool.toggle', }, @@ -470,8 +476,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-stack-scroll', - label: 'Stack Scroll', - tooltip: 'Stack Scroll', + label: i18n.t('Buttons:Stack Scroll'), + tooltip: i18n.t('Buttons:Stack Scroll'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -481,8 +487,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-invert', - label: 'Invert', - tooltip: 'Invert Colors', + label: i18n.t('Buttons:Invert'), + tooltip: i18n.t('Buttons:Invert Colors'), commands: 'invertViewport', evaluate: 'evaluate.viewportProperties.toggle', }, @@ -492,8 +498,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-probe', - label: 'Probe', - tooltip: 'Probe', + label: i18n.t('Buttons:Probe'), + tooltip: i18n.t('Buttons:Probe'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -503,8 +509,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-cine', - label: 'Cine', - tooltip: 'Cine', + label: i18n.t('Buttons:Cine'), + tooltip: i18n.t('Buttons:Cine'), commands: 'toggleCine', evaluate: 'evaluate.cine', }, @@ -514,8 +520,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-angle', - label: 'Angle', - tooltip: 'Angle', + label: i18n.t('Buttons:Angle'), + tooltip: i18n.t('Buttons:Angle'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -525,8 +531,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-cobb-angle', - label: 'Cobb Angle', - tooltip: 'Cobb Angle', + label: i18n.t('Buttons:Cobb Angle'), + tooltip: i18n.t('Buttons:Cobb Angle'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -536,8 +542,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-magnify', - label: 'Zoom-in', - tooltip: 'Zoom-in', + label: i18n.t('Buttons:Zoom-in'), + tooltip: i18n.t('Buttons:Zoom-in'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -547,8 +553,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-rectangle', - label: 'Rectangle', - tooltip: 'Rectangle', + label: i18n.t('Buttons:Rectangle'), + tooltip: i18n.t('Buttons:Rectangle'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -558,8 +564,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-calibration', - label: 'Calibration', - tooltip: 'Calibration Line', + label: i18n.t('Buttons:Calibration'), + tooltip: i18n.t('Buttons:Calibration Line'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -569,8 +575,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'dicom-tag-browser', - label: 'Dicom Tag Browser', - tooltip: 'Dicom Tag Browser', + label: i18n.t('Buttons:Dicom Tag Browser'), + tooltip: i18n.t('Buttons:Dicom Tag Browser'), commands: 'openDICOMTagViewer', }, }, @@ -579,8 +585,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-loupe', - label: 'Magnify Probe', - tooltip: 'Magnify Probe', + label: i18n.t('Buttons:Magnify Probe'), + tooltip: i18n.t('Buttons:Magnify Probe'), commands: 'toggleActiveDisabledToolbar', evaluate: 'evaluate.cornerstoneTool.toggle.ifStrictlyDisabled', }, @@ -590,8 +596,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-ultrasound-bidirectional', - label: 'Ultrasound Directional', - tooltip: 'Ultrasound Directional', + label: i18n.t('Buttons:Ultrasound Directional'), + tooltip: i18n.t('Buttons:Ultrasound Directional'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstoneTool', @@ -607,8 +613,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-window-region', - label: 'Window Level Region', - tooltip: 'Window Level Region', + label: i18n.t('Buttons:Window Level Region'), + tooltip: i18n.t('Buttons:Window Level Region'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, diff --git a/modes/basic/.gitignore b/modes/basic/.gitignore new file mode 100644 index 00000000000..67045665db2 --- /dev/null +++ b/modes/basic/.gitignore @@ -0,0 +1,104 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and *not* Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port diff --git a/modes/basic/.prettierrc b/modes/basic/.prettierrc new file mode 100644 index 00000000000..ef83baaef93 --- /dev/null +++ b/modes/basic/.prettierrc @@ -0,0 +1,11 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"], + "trailingComma": "es5", + "printWidth": 100, + "proseWrap": "always", + "tabWidth": 2, + "semi": true, + "singleQuote": true, + "arrowParens": "avoid", + "endOfLine": "auto" +} diff --git a/modes/basic/.webpack/webpack.prod.js b/modes/basic/.webpack/webpack.prod.js new file mode 100644 index 00000000000..565614cf122 --- /dev/null +++ b/modes/basic/.webpack/webpack.prod.js @@ -0,0 +1,52 @@ +const webpack = require('webpack'); +const { merge } = require('webpack-merge'); +const path = require('path'); + +const pkg = require('./../package.json'); +const webpackCommon = require('./../../../.webpack/webpack.base.js'); + +const ROOT_DIR = path.join(__dirname, './../'); +const SRC_DIR = path.join(__dirname, '../src'); +const DIST_DIR = path.join(__dirname, '../dist'); +const ENTRY = { + app: `${SRC_DIR}/index.tsx`, +}; + +module.exports = (env, argv) => { + const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR, ENTRY }); + + return merge(commonConfig, { + stats: { + colors: true, + hash: true, + timings: true, + assets: true, + chunks: false, + chunkModules: false, + modules: false, + children: false, + warnings: true, + }, + optimization: { + minimize: true, + sideEffects: false, + }, + output: { + path: ROOT_DIR, + library: 'ohif-mode-basic', + libraryTarget: 'umd', + libraryExport: 'default', + filename: pkg.main, + }, + externals: [/\b(vtk.js)/, /\b(dcmjs)/, /\b(gl-matrix)/, /^@ohif/, /^@cornerstonejs/], + plugins: [ + new webpack.optimize.LimitChunkCountPlugin({ + maxChunks: 1, + }), + // new MiniCssExtractPlugin({ + // filename: './dist/[name].css', + // chunkFilename: './dist/[id].css', + // }), + ], + }); +}; diff --git a/modes/basic/CHANGELOG.md b/modes/basic/CHANGELOG.md new file mode 100644 index 00000000000..1b2f305e3b3 --- /dev/null +++ b/modes/basic/CHANGELOG.md @@ -0,0 +1,790 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [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/mode-basic + + + + + +# [3.12.0-beta.39](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.38...v3.12.0-beta.39) (2025-10-01) + + +### Features + +* Add inherit from basic/longitudinal modes ([#5435](https://github.com/OHIF/Viewers/issues/5435)) ([9ad0d7f](https://github.com/OHIF/Viewers/commit/9ad0d7fc8cf34867fa2cfc2bf2b2f63451ec03e5)) diff --git a/modes/basic/LICENSE b/modes/basic/LICENSE new file mode 100644 index 00000000000..1ec16de8cd6 --- /dev/null +++ b/modes/basic/LICENSE @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) 2025 basic () + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO +EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/modes/basic/README.md b/modes/basic/README.md new file mode 100644 index 00000000000..cfb643ba807 --- /dev/null +++ b/modes/basic/README.md @@ -0,0 +1,7 @@ +# basic +## Description +A basic mode used to build other modes on top of +## Author +OHIF Contributors +## License +MIT \ No newline at end of file diff --git a/modes/basic/babel.config.js b/modes/basic/babel.config.js new file mode 100644 index 00000000000..24adaea8d29 --- /dev/null +++ b/modes/basic/babel.config.js @@ -0,0 +1,43 @@ +module.exports = { + plugins: ['@babel/plugin-transform-class-properties'], + env: { + test: { + presets: [ + [ + // TODO: https://babeljs.io/blog/2019/03/19/7.4.0#migration-from-core-js-2 + '@babel/preset-env', + { + modules: 'commonjs', + debug: false, + }, + '@babel/preset-typescript', + ], + '@babel/preset-react', + ], + plugins: [ + '@babel/plugin-transform-object-rest-spread', + '@babel/plugin-syntax-dynamic-import', + '@babel/plugin-transform-regenerator', + '@babel/plugin-transform-runtime', + ], + }, + production: { + presets: [ + // WebPack handles ES6 --> Target Syntax + ['@babel/preset-env', { modules: false }], + '@babel/preset-react', + '@babel/preset-typescript', + ], + ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'], + }, + development: { + presets: [ + // WebPack handles ES6 --> Target Syntax + ['@babel/preset-env', { modules: false }], + '@babel/preset-react', + '@babel/preset-typescript', + ], + ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'], + }, + }, +}; diff --git a/modes/basic/package.json b/modes/basic/package.json new file mode 100644 index 00000000000..40ad8b8fea5 --- /dev/null +++ b/modes/basic/package.json @@ -0,0 +1,49 @@ +{ + "name": "@ohif/mode-basic", + "version": "3.12.0", + "description": "A basic mode used to build other modes on top of", + "author": "OHIF Contributors", + "license": "MIT", + "main": "dist/ohif-mode-basic.js", + "module": "src/index.tsx", + "files": [ + "dist/**", + "public/**", + "README.md" + ], + "repository": "OHIF/Viewers", + "keywords": [ + "ohif-mode" + ], + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1.16.0" + }, + "peerDependencies": { + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone": "3.12.0", + "@ohif/extension-cornerstone-dicom-rt": "3.12.0", + "@ohif/extension-cornerstone-dicom-seg": "3.12.0", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0", + "@ohif/extension-default": "3.12.0", + "@ohif/extension-dicom-pdf": "3.12.0", + "@ohif/extension-dicom-video": "3.12.0" + }, + "dependencies": { + "@babel/runtime": "7.28.2" + }, + "devDependencies": { + "webpack": "5.95.0", + "webpack-merge": "5.10.0" + }, + "scripts": { + "dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo", + "dev:cornerstone": "yarn run dev", + "build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js", + "build:package": "yarn run build", + "start": "yarn run dev", + "test:unit": "jest --watchAll", + "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" + } +} diff --git a/modes/basic/src/id.js b/modes/basic/src/id.js new file mode 100644 index 00000000000..ebe5acd98ae --- /dev/null +++ b/modes/basic/src/id.js @@ -0,0 +1,5 @@ +import packageJson from '../package.json'; + +const id = packageJson.name; + +export { id }; diff --git a/modes/basic/src/index.tsx b/modes/basic/src/index.tsx new file mode 100644 index 00000000000..8fcc859ad5c --- /dev/null +++ b/modes/basic/src/index.tsx @@ -0,0 +1,387 @@ +import update from 'immutability-helper'; +import { ToolbarService, utils } from '@ohif/core'; + +import initToolGroups from './initToolGroups'; +import toolbarButtons from './toolbarButtons'; +import { id } from './id'; + +const { TOOLBAR_SECTIONS } = ToolbarService; +const { structuredCloneWithFunctions } = utils; + +/** + * Define non-imaging modalities. + * This can be used to exclude modes which have only these modalities, + * or it can be used to not display thumbnails for some of these. + * This list used to include SM, for whole slide imaging, but this is now supported + * by cornerstone. Others of these may get added. + */ +export const NON_IMAGE_MODALITIES = ['ECG', 'SEG', 'RTSTRUCT', 'RTPLAN', 'PR', 'SR']; + +export const ohif = { + layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout', + sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack', + thumbnailList: '@ohif/extension-default.panelModule.seriesList', + hangingProtocol: '@ohif/extension-default.hangingProtocolModule.default', + wsiSopClassHandler: + '@ohif/extension-cornerstone.sopClassHandlerModule.DicomMicroscopySopClassHandler', +}; + +export const cornerstone = { + measurements: '@ohif/extension-cornerstone.panelModule.panelMeasurement', + labelMapSegmentationPanel: + '@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsLabelMap', + contourSegmentationPanel: + '@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsContour', + segmentation: '@ohif/extension-cornerstone.panelModule.panelSegmentation', + viewport: '@ohif/extension-cornerstone.viewportModule.cornerstone', +}; + +export const dicomsr = { + sopClassHandler: '@ohif/extension-cornerstone-dicom-sr.sopClassHandlerModule.dicom-sr', + sopClassHandler3D: '@ohif/extension-cornerstone-dicom-sr.sopClassHandlerModule.dicom-sr-3d', + viewport: '@ohif/extension-cornerstone-dicom-sr.viewportModule.dicom-sr', +}; + +export const dicomvideo = { + sopClassHandler: '@ohif/extension-dicom-video.sopClassHandlerModule.dicom-video', + viewport: '@ohif/extension-dicom-video.viewportModule.dicom-video', +}; + +export const dicompdf = { + sopClassHandler: '@ohif/extension-dicom-pdf.sopClassHandlerModule.dicom-pdf', + viewport: '@ohif/extension-dicom-pdf.viewportModule.dicom-pdf', +}; + +export const dicomSeg = { + sopClassHandler: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', + viewport: '@ohif/extension-cornerstone-dicom-seg.viewportModule.dicom-seg', +}; + +export const dicomPmap = { + sopClassHandler: '@ohif/extension-cornerstone-dicom-pmap.sopClassHandlerModule.dicom-pmap', + viewport: '@ohif/extension-cornerstone-dicom-pmap.viewportModule.dicom-pmap', +}; + +export const dicomRT = { + viewport: '@ohif/extension-cornerstone-dicom-rt.viewportModule.dicom-rt', + sopClassHandler: '@ohif/extension-cornerstone-dicom-rt.sopClassHandlerModule.dicom-rt', +}; + +export const segmentation = { + sopClassHandler: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', + viewport: '@ohif/extension-cornerstone-dicom-seg.viewportModule.dicom-seg', +}; + +export const extensionDependencies = { + // Can derive the versions at least process.env.from npm_package_version + '@ohif/extension-default': '^3.0.0', + '@ohif/extension-cornerstone': '^3.0.0', + '@ohif/extension-cornerstone-dicom-sr': '^3.0.0', + '@ohif/extension-cornerstone-dicom-seg': '^3.0.0', + '@ohif/extension-cornerstone-dicom-pmap': '^3.0.0', + '@ohif/extension-cornerstone-dicom-rt': '^3.0.0', + '@ohif/extension-dicom-pdf': '^3.0.1', + '@ohif/extension-dicom-video': '^3.0.1', +}; + +export const sopClassHandlers = [ + dicomvideo.sopClassHandler, + dicomSeg.sopClassHandler, + dicomPmap.sopClassHandler, + ohif.sopClassHandler, + ohif.wsiSopClassHandler, + dicompdf.sopClassHandler, + dicomsr.sopClassHandler3D, + dicomsr.sopClassHandler, + dicomRT.sopClassHandler, +]; + +/** + * Indicate this is a valid mode if: + * - it contains at least one of the modeModalities + * - it contains all of the array value in modeModalities + * Otherwise, if modeModalities is not defined: + * - it contains at least one modality other than the nonModeMOdalities. + */ +export function isValidMode({ modalities }) { + const modalities_list = modalities.split('\\'); + + if (this.modeModalities?.length) { + for (const modeModality of this.modeModalities) { + if (Array.isArray(modeModality) && modeModality.every(m => modalities.indexOf(m) !== -1)) { + return { valid: true, description: `Matches ${modeModality.join(', ')}` }; + } else if (modalities.indexOf(modeModality)) { + return { valid: true, description: `Matches ${modeModality}` }; + } + } + return { + valid: false, + description: `None of the mode modalities match: ${JSON.stringify(this.modeModalities)}`, + }; + } + + return { + valid: !!modalities_list.find(modality => this.nonModeModalities.indexOf(modality) === -1), + description: `The mode does not support studies that ONLY include the following modalities: ${this.nonModeModalities.join(', ')}`, + }; +} + +export function onModeEnter({ + servicesManager, + extensionManager, + commandsManager, + panelService, + segmentationService, +}: withAppTypes) { + const { measurementService, toolbarService, toolGroupService, customizationService } = + servicesManager.services; + + measurementService.clearMeasurements(); + + // Init Default and SR ToolGroups + initToolGroups(extensionManager, toolGroupService, commandsManager); + + toolbarService.register(this.toolbarButtons); + + for (const [key, section] of Object.entries(this.toolbarSections)) { + toolbarService.updateSection(key, section); + } + + if (!this.enableSegmentationEdit) { + customizationService.setCustomizations({ + 'panelSegmentation.disableEditing': { + $set: true, + }, + }); + } + + // // ActivatePanel event trigger for when a segmentation or measurement is added. + // // Do not force activation so as to respect the state the user may have left the UI in. + if (this.activatePanelTrigger) { + this._activatePanelTriggersSubscriptions = [ + ...panelService.addActivatePanelTriggers( + cornerstone.segmentation, + [ + { + sourcePubSubService: segmentationService, + sourceEvents: [segmentationService.EVENTS.SEGMENTATION_ADDED], + }, + ], + true + ), + ...panelService.addActivatePanelTriggers( + cornerstone.measurements, + [ + { + sourcePubSubService: measurementService, + sourceEvents: [ + measurementService.EVENTS.MEASUREMENT_ADDED, + measurementService.EVENTS.RAW_MEASUREMENT_ADDED, + ], + }, + ], + true + ), + true, + ]; + } +} + +export function onModeExit({ servicesManager }: withAppTypes) { + const { + toolGroupService, + syncGroupService, + segmentationService, + cornerstoneViewportService, + uiDialogService, + uiModalService, + } = servicesManager.services; + + this._activatePanelTriggersSubscriptions.forEach(sub => sub.unsubscribe()); + this._activatePanelTriggersSubscriptions.length = 0; + + uiDialogService.hideAll(); + uiModalService.hide(); + toolGroupService.destroy(); + syncGroupService.destroy(); + segmentationService.destroy(); + cornerstoneViewportService.destroy(); +} + +export const toolbarSections = { + [TOOLBAR_SECTIONS.primary]: [ + 'MeasurementTools', + 'Zoom', + 'Pan', + 'TrackballRotate', + 'WindowLevel', + 'Capture', + 'Layout', + 'Crosshairs', + 'MoreTools', + ], + + [TOOLBAR_SECTIONS.viewportActionMenu.topLeft]: ['orientationMenu', 'dataOverlayMenu'], + + [TOOLBAR_SECTIONS.viewportActionMenu.bottomMiddle]: ['AdvancedRenderingControls'], + + AdvancedRenderingControls: [ + 'windowLevelMenuEmbedded', + 'voiManualControlMenu', + 'Colorbar', + 'opacityMenu', + 'thresholdMenu', + ], + + [TOOLBAR_SECTIONS.viewportActionMenu.topRight]: [ + 'modalityLoadBadge', + 'trackingStatus', + 'navigationComponent', + ], + + [TOOLBAR_SECTIONS.viewportActionMenu.bottomLeft]: ['windowLevelMenu'], + + MeasurementTools: [ + 'Length', + 'Bidirectional', + 'ArrowAnnotate', + 'EllipticalROI', + 'RectangleROI', + 'CircleROI', + 'PlanarFreehandROI', + 'SplineROI', + 'LivewireContour', + ], + + MoreTools: [ + 'Reset', + 'rotate-right', + 'flipHorizontal', + 'ImageSliceSync', + 'ReferenceLines', + 'ImageOverlayViewer', + 'StackScroll', + 'invert', + 'Probe', + 'Cine', + 'Angle', + 'CobbAngle', + 'Magnify', + 'CalibrationLine', + 'TagBrowser', + 'AdvancedMagnify', + 'UltrasoundDirectionalTool', + 'WindowLevelRegion', + 'SegmentLabelTool', + ], +}; + +export const basicLayout = { + id: ohif.layout, + props: { + leftPanels: [ohif.thumbnailList], + leftPanelResizable: true, + rightPanels: [cornerstone.segmentation, cornerstone.measurements], + rightPanelClosed: true, + rightPanelResizable: true, + viewports: [ + { + namespace: cornerstone.viewport, + displaySetsToDisplay: [ + ohif.sopClassHandler, + dicomvideo.sopClassHandler, + ohif.wsiSopClassHandler, + ], + }, + { + namespace: dicomsr.viewport, + displaySetsToDisplay: [dicomsr.sopClassHandler, dicomsr.sopClassHandler3D], + }, + { + namespace: dicompdf.viewport, + displaySetsToDisplay: [dicompdf.sopClassHandler], + }, + { + namespace: dicomSeg.viewport, + displaySetsToDisplay: [dicomSeg.sopClassHandler], + }, + { + namespace: dicomPmap.viewport, + displaySetsToDisplay: [dicomPmap.sopClassHandler], + }, + { + namespace: dicomRT.viewport, + displaySetsToDisplay: [dicomRT.sopClassHandler], + }, + ], + }, +}; + +export function layoutTemplate() { + return structuredCloneWithFunctions(this.layoutInstance); +} + +export const basicRoute = { + path: 'basic', + layoutTemplate, + layoutInstance: basicLayout, +}; + +export const modeInstance = { + // TODO: We're using this as a route segment + // We should not be. + id, + routeName: 'basic', + // Don't hide this by default - see the registration later to hide the basic + // instance by default. + hide: false, + displayName: 'Non-Longitudinal Basic', + _activatePanelTriggersSubscriptions: [], + toolbarSections, + + /** + * Lifecycle hooks + */ + onModeEnter, + onModeExit, + validationTags: { + study: [], + series: [], + }, + + isValidMode, + routes: [basicRoute], + extensions: extensionDependencies, + // Default protocol gets self-registered by default in the init + hangingProtocol: 'default', + // Order is important in sop class handlers when two handlers both use + // the same sop class under different situations. In that case, the more + // general handler needs to come last. For this case, the dicomvideo must + // come first to remove video transfer syntax before ohif uses images + sopClassHandlers, + toolbarButtons, + enableSegmentationEdit: false, + nonModeModalities: NON_IMAGE_MODALITIES, +}; + +/** + * Creates a mode on this object, using immutability-helper to apply changes + * from modeConfiguration into the modeInstance. + */ +export function modeFactory({ modeConfiguration }) { + let modeInstance = this.modeInstance; + if (modeConfiguration) { + modeInstance = update(modeInstance, modeConfiguration); + } + return modeInstance; +} + +export const mode = { + id, + modeFactory, + modeInstance: { ...modeInstance, hide: true }, + extensionDependencies, +}; + +export default mode; +export { initToolGroups, toolbarButtons }; diff --git a/modes/longitudinal/src/initToolGroups.js b/modes/basic/src/initToolGroups.ts similarity index 98% rename from modes/longitudinal/src/initToolGroups.js rename to modes/basic/src/initToolGroups.ts index 110f5bdd069..6278f62619c 100644 --- a/modes/longitudinal/src/initToolGroups.js +++ b/modes/basic/src/initToolGroups.ts @@ -1,5 +1,3 @@ -import { toolNames as SRToolNames } from '@ohif/extension-cornerstone-dicom-sr'; - const colours = { 'viewport-0': 'rgb(200, 0, 0)', 'viewport-1': 'rgb(200, 200, 0)', @@ -87,9 +85,6 @@ function initDefaultToolGroup(extensionManager, toolGroupService, commandsManage enabled: [ { toolName: toolNames.ImageOverlayViewer }, { toolName: toolNames.ReferenceLines }, - { - toolName: SRToolNames.SRSCOORD3DPoint, - }, ], disabled: [ { diff --git a/modes/longitudinal/src/toolbarButtons.ts b/modes/basic/src/toolbarButtons.ts similarity index 74% rename from modes/longitudinal/src/toolbarButtons.ts rename to modes/basic/src/toolbarButtons.ts index c9db3f257b6..005674e58d0 100644 --- a/modes/longitudinal/src/toolbarButtons.ts +++ b/modes/basic/src/toolbarButtons.ts @@ -2,6 +2,7 @@ import type { Button } from '@ohif/core/types'; import { EVENTS } from '@cornerstonejs/core'; import { ViewportGridService } from '@ohif/core'; +import i18n from 'i18next'; const callbacks = (toolName: string) => [ { @@ -48,8 +49,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.modalityLoadBadge', props: { icon: 'Status', - label: 'Status', - tooltip: 'Status', + label: i18n.t('Buttons:Status'), + tooltip: i18n.t('Buttons:Status'), evaluate: { name: 'evaluate.modalityLoadBadge', hideWhenDisabled: true, @@ -61,8 +62,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.navigationComponent', props: { icon: 'Navigation', - label: 'Navigation', - tooltip: 'Navigate between segments/measurements and manage their visibility', + label: i18n.t('Buttons:Navigation'), + tooltip: i18n.t('Buttons:Navigate between segments/measurements and manage their visibility'), evaluate: { name: 'evaluate.navigationComponent', hideWhenDisabled: true, @@ -74,8 +75,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.trackingStatus', props: { icon: 'TrackingStatus', - label: 'Tracking Status', - tooltip: 'View and manage tracking status of measurements and annotations', + label: i18n.t('Buttons:Tracking Status'), + tooltip: i18n.t('Buttons:View and manage tracking status of measurements and annotations'), evaluate: { name: 'evaluate.trackingStatus', hideWhenDisabled: true, @@ -87,8 +88,10 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.dataOverlayMenu', props: { icon: 'ViewportViews', - label: 'Data Overlay', - tooltip: 'Configure data overlay options and manage foreground/background display sets', + label: i18n.t('Buttons:Data Overlay'), + tooltip: i18n.t( + 'Buttons:Configure data overlay options and manage foreground/background display sets' + ), evaluate: 'evaluate.dataOverlayMenu', }, }, @@ -97,8 +100,10 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.orientationMenu', props: { icon: 'OrientationSwitch', - label: 'Orientation', - tooltip: 'Change viewport orientation between axial, sagittal, coronal and reformat planes', + label: i18n.t('Buttons:Orientation'), + tooltip: i18n.t( + 'Buttons:Change viewport orientation between axial, sagittal, coronal and reformat planes' + ), evaluate: { name: 'evaluate.orientationMenu', // hideWhenDisabled: true, @@ -110,8 +115,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.windowLevelMenuEmbedded', props: { icon: 'WindowLevel', - label: 'Window Level', - tooltip: 'Adjust window/level presets and customize image contrast settings', + label: i18n.t('Buttons:Window Level'), + tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'), evaluate: { name: 'evaluate.windowLevelMenuEmbedded', hideWhenDisabled: true, @@ -123,8 +128,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.windowLevelMenu', props: { icon: 'WindowLevel', - label: 'Window Level', - tooltip: 'Adjust window/level presets and customize image contrast settings', + label: i18n.t('Buttons:Window Level'), + tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'), evaluate: { name: 'evaluate.windowLevelMenu', }, @@ -135,8 +140,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.voiManualControlMenu', props: { icon: 'WindowLevelAdvanced', - label: 'Advanced Window Level', - tooltip: 'Advanced window/level settings with manual controls and presets', + label: i18n.t('Buttons:Advanced Window Level'), + tooltip: i18n.t('Buttons:Advanced window/level settings with manual controls and presets'), evaluate: 'evaluate.voiManualControlMenu', }, }, @@ -145,8 +150,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.thresholdMenu', props: { icon: 'Threshold', - label: 'Threshold', - tooltip: 'Image threshold settings', + label: i18n.t('Buttons:Threshold'), + tooltip: i18n.t('Buttons:Image threshold settings'), evaluate: { name: 'evaluate.thresholdMenu', hideWhenDisabled: true, @@ -158,8 +163,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.opacityMenu', props: { icon: 'Opacity', - label: 'Opacity', - tooltip: 'Image opacity settings', + label: i18n.t('Buttons:Opacity'), + tooltip: i18n.t('Buttons:Image opacity settings'), evaluate: { name: 'evaluate.opacityMenu', hideWhenDisabled: true, @@ -171,7 +176,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.colorbar', props: { type: 'tool', - label: 'Colorbar', + label: i18n.t('Buttons:Colorbar'), }, }, { @@ -179,8 +184,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-reset', - label: 'Reset View', - tooltip: 'Reset View', + label: i18n.t('Buttons:Reset View'), + tooltip: i18n.t('Buttons:Reset View'), commands: 'resetViewport', evaluate: 'evaluate.action', }, @@ -190,8 +195,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-rotate-right', - label: 'Rotate Right', - tooltip: 'Rotate +90', + label: i18n.t('Buttons:Rotate Right'), + tooltip: i18n.t('Buttons:Rotate +90'), commands: 'rotateViewportCW', evaluate: [ 'evaluate.action', @@ -207,8 +212,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-flip-horizontal', - label: 'Flip Horizontal', - tooltip: 'Flip Horizontally', + label: i18n.t('Buttons:Flip Horizontal'), + tooltip: i18n.t('Buttons:Flip Horizontally'), commands: 'flipViewportHorizontal', evaluate: [ 'evaluate.viewportProperties.toggle', @@ -224,8 +229,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'link', - label: 'Image Slice Sync', - tooltip: 'Enable position synchronization on stack viewports', + label: i18n.t('Buttons:Image Slice Sync'), + tooltip: i18n.t('Buttons:Enable position synchronization on stack viewports'), commands: { commandName: 'toggleSynchronizer', commandOptions: { @@ -252,8 +257,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-referenceLines', - label: 'Reference Lines', - tooltip: 'Show Reference Lines', + label: i18n.t('Buttons:Reference Lines'), + tooltip: i18n.t('Buttons:Show Reference Lines'), commands: 'toggleEnabledDisabledToolbar', listeners: { [ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: callbacks('ReferenceLines'), @@ -273,8 +278,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'toggle-dicom-overlay', - label: 'Image Overlay', - tooltip: 'Toggle Image Overlay', + label: i18n.t('Buttons:Image Overlay'), + tooltip: i18n.t('Buttons:Toggle Image Overlay'), commands: 'toggleEnabledDisabledToolbar', evaluate: [ 'evaluate.cornerstoneTool.toggle', @@ -290,8 +295,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-stack-scroll', - label: 'Stack Scroll', - tooltip: 'Stack Scroll', + label: i18n.t('Buttons:Stack Scroll'), + tooltip: i18n.t('Buttons:Stack Scroll'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -301,8 +306,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-invert', - label: 'Invert', - tooltip: 'Invert Colors', + label: i18n.t('Buttons:Invert'), + tooltip: i18n.t('Buttons:Invert Colors'), commands: 'invertViewport', evaluate: [ 'evaluate.viewportProperties.toggle', @@ -318,8 +323,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-probe', - label: 'Probe', - tooltip: 'Probe', + label: i18n.t('Buttons:Probe'), + tooltip: i18n.t('Buttons:Probe'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -329,8 +334,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-cine', - label: 'Cine', - tooltip: 'Cine', + label: i18n.t('Buttons:Cine'), + tooltip: i18n.t('Buttons:Cine'), commands: 'toggleCine', evaluate: [ 'evaluate.cine', @@ -346,8 +351,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-angle', - label: 'Angle', - tooltip: 'Angle', + label: i18n.t('Buttons:Angle'), + tooltip: i18n.t('Buttons:Angle'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -357,8 +362,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-cobb-angle', - label: 'Cobb Angle', - tooltip: 'Cobb Angle', + label: i18n.t('Buttons:Cobb Angle'), + tooltip: i18n.t('Buttons:Cobb Angle'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -368,8 +373,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-magnify', - label: 'Zoom-in', - tooltip: 'Zoom-in', + label: i18n.t('Buttons:Zoom-in'), + tooltip: i18n.t('Buttons:Zoom-in'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstoneTool', @@ -385,8 +390,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-calibration', - label: 'Calibration', - tooltip: 'Calibration Line', + label: i18n.t('Buttons:Calibration'), + tooltip: i18n.t('Buttons:Calibration Line'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstoneTool', @@ -402,8 +407,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'dicom-tag-browser', - label: 'Dicom Tag Browser', - tooltip: 'Dicom Tag Browser', + label: i18n.t('Buttons:Dicom Tag Browser'), + tooltip: i18n.t('Buttons:Dicom Tag Browser'), commands: 'openDICOMTagViewer', }, }, @@ -412,8 +417,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-loupe', - label: 'Magnify Probe', - tooltip: 'Magnify Probe', + label: i18n.t('Buttons:Magnify Probe'), + tooltip: i18n.t('Buttons:Magnify Probe'), commands: 'toggleActiveDisabledToolbar', evaluate: [ 'evaluate.cornerstoneTool.toggle.ifStrictlyDisabled', @@ -429,8 +434,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-ultrasound-bidirectional', - label: 'Ultrasound Directional', - tooltip: 'Ultrasound Directional', + label: i18n.t('Buttons:Ultrasound Directional'), + tooltip: i18n.t('Buttons:Ultrasound Directional'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstoneTool', @@ -446,8 +451,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-window-region', - label: 'Window Level Region', - tooltip: 'Window Level Region', + label: i18n.t('Buttons:Window Level Region'), + tooltip: i18n.t('Buttons:Window Level Region'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstoneTool', @@ -463,8 +468,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-length', - label: 'Length', - tooltip: 'Length Tool', + label: i18n.t('Buttons:Length'), + tooltip: i18n.t('Buttons:Length Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -474,8 +479,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-bidirectional', - label: 'Bidirectional', - tooltip: 'Bidirectional Tool', + label: i18n.t('Buttons:Bidirectional'), + tooltip: i18n.t('Buttons:Bidirectional Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -485,8 +490,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-annotate', - label: 'Annotation', - tooltip: 'Arrow Annotate', + label: i18n.t('Buttons:Annotation'), + tooltip: i18n.t('Buttons:Arrow Annotate'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -496,8 +501,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-ellipse', - label: 'Ellipse', - tooltip: 'Ellipse ROI', + label: i18n.t('Buttons:Ellipse'), + tooltip: i18n.t('Buttons:Ellipse ROI'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -507,8 +512,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-rectangle', - label: 'Rectangle', - tooltip: 'Rectangle ROI', + label: i18n.t('Buttons:Rectangle'), + tooltip: i18n.t('Buttons:Rectangle ROI'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -518,8 +523,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-circle', - label: 'Circle', - tooltip: 'Circle Tool', + label: i18n.t('Buttons:Circle'), + tooltip: i18n.t('Buttons:Circle Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -529,8 +534,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-freehand-roi', - label: 'Freehand ROI', - tooltip: 'Freehand ROI', + label: i18n.t('Buttons:Freehand ROI'), + tooltip: i18n.t('Buttons:Freehand ROI'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -540,8 +545,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-spline-roi', - label: 'Spline ROI', - tooltip: 'Spline ROI', + label: i18n.t('Buttons:Spline ROI'), + tooltip: i18n.t('Buttons:Spline ROI'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -551,8 +556,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-livewire', - label: 'Livewire tool', - tooltip: 'Livewire tool', + label: i18n.t('Buttons:Livewire tool'), + tooltip: i18n.t('Buttons:Livewire tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -563,7 +568,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-window-level', - label: 'Window Level', + label: i18n.t('Buttons:Window Level'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstoneTool', @@ -580,7 +585,7 @@ const toolbarButtons: Button[] = [ props: { type: 'tool', icon: 'tool-move', - label: 'Pan', + label: i18n.t('Buttons:Pan'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -591,7 +596,7 @@ const toolbarButtons: Button[] = [ props: { type: 'tool', icon: 'tool-zoom', - label: 'Zoom', + label: i18n.t('Buttons:Zoom'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -602,11 +607,11 @@ const toolbarButtons: Button[] = [ props: { type: 'tool', icon: 'tool-3d-rotate', - label: '3D Rotate', + label: i18n.t('Buttons:3D Rotate'), commands: setToolActiveToolbar, evaluate: { name: 'evaluate.cornerstoneTool', - disabledText: 'Select a 3D viewport to enable this tool', + disabledText: i18n.t('Buttons:Select a 3D viewport to enable this tool'), }, }, }, @@ -615,7 +620,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-capture', - label: 'Capture', + label: i18n.t('Buttons:Capture'), commands: 'showDownloadViewportModal', evaluate: [ 'evaluate.action', @@ -641,7 +646,7 @@ const toolbarButtons: Button[] = [ props: { type: 'tool', icon: 'tool-crosshair', - label: 'Crosshairs', + label: i18n.t('Buttons:Crosshairs'), commands: { commandName: 'setToolActiveToolbar', commandOptions: { @@ -650,25 +655,19 @@ const toolbarButtons: Button[] = [ }, evaluate: { name: 'evaluate.cornerstoneTool', - disabledText: 'Select an MPR viewport to enable this tool', + disabledText: i18n.t('Buttons:Select an MPR viewport to enable this tool'), }, }, }, - // Section containers for the nested toolbox - { - id: 'SegmentationUtilities', - uiType: 'ohif.toolBoxButton', - props: { - buttonSection: true, - }, - }, { id: 'SegmentLabelTool', uiType: 'ohif.toolBoxButton', props: { icon: 'tool-segment-label', - label: 'Segment Label Display', - tooltip: 'Click to show or hide segment labels when hovering with your mouse.', + label: i18n.t('Buttons:Segment Label Display'), + tooltip: i18n.t( + 'Buttons:Click to show or hide segment labels when hovering with your mouse.' + ), commands: { commandName: 'toggleSegmentLabel' }, evaluate: [ 'evaluate.cornerstoneTool.toggle', diff --git a/modes/longitudinal/.webpack/webpack.prod.js b/modes/longitudinal/.webpack/webpack.prod.js index b60b8908ae2..23ec5108edf 100644 --- a/modes/longitudinal/.webpack/webpack.prod.js +++ b/modes/longitudinal/.webpack/webpack.prod.js @@ -1,7 +1,6 @@ const webpack = require('webpack'); const { merge } = require('webpack-merge'); const path = require('path'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const pkg = require('./../package.json'); const webpackCommon = require('./../../../.webpack/webpack.base.js'); diff --git a/modes/longitudinal/CHANGELOG.md b/modes/longitudinal/CHANGELOG.md index 32982b56557..37d6ec9684f 100644 --- a/modes/longitudinal/CHANGELOG.md +++ b/modes/longitudinal/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/mode-longitudinal @@ -11,7 +11,1102 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [3.12.0-beta.39](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.38...v3.12.0-beta.39) (2025-10-01) + + +### Features + +* Add inherit from basic/longitudinal modes ([#5435](https://github.com/OHIF/Viewers/issues/5435)) ([9ad0d7f](https://github.com/OHIF/Viewers/commit/9ad0d7fc8cf34867fa2cfc2bf2b2f63451ec03e5)) + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [3.12.0-beta.35](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.34...v3.12.0-beta.35) (2025-09-29) + + +### Bug Fixes + +* **segmentation:** Restored segment label tool in longitudinal mode. ([#5443](https://github.com/OHIF/Viewers/issues/5443)) ([dd86a8e](https://github.com/OHIF/Viewers/commit/dd86a8eabd0712d25e7652a242377fe09597e8d5)) + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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 @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25) + + +### Bug Fixes + +* **i18n:** Add and update i18n Translation for Toolbar Tool Names ([#5392](https://github.com/OHIF/Viewers/issues/5392)) ([7783d0f](https://github.com/OHIF/Viewers/commit/7783d0f558bbb7016c29513b6fe76ed069dbd75c)) + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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 @ohif/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [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/mode-longitudinal + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + + +### Bug Fixes + +* load 3dsr ([#5335](https://github.com/OHIF/Viewers/issues/5335)) ([becba78](https://github.com/OHIF/Viewers/commit/becba7861f99ae4e190caddea8b663e60a9883f5)) + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/mode-longitudinal + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/mode-longitudinal diff --git a/modes/longitudinal/package.json b/modes/longitudinal/package.json index b3a6e96fad8..d895d0976d1 100644 --- a/modes/longitudinal/package.json +++ b/modes/longitudinal/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/mode-longitudinal", - "version": "3.11.1", + "version": "3.12.0", "description": "Longitudinal Workflow", "author": "OHIF", "license": "MIT", @@ -34,15 +34,16 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-rt": "3.11.1", - "@ohif/extension-cornerstone-dicom-seg": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-dicom-pdf": "3.11.1", - "@ohif/extension-dicom-video": "3.11.1", - "@ohif/extension-measurement-tracking": "3.11.1" + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone": "3.12.0", + "@ohif/extension-cornerstone-dicom-rt": "3.12.0", + "@ohif/extension-cornerstone-dicom-seg": "3.12.0", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0", + "@ohif/extension-default": "3.12.0", + "@ohif/extension-dicom-pdf": "3.12.0", + "@ohif/extension-dicom-video": "3.12.0", + "@ohif/extension-measurement-tracking": "3.12.0", + "@ohif/mode-basic": "3.12.0" }, "dependencies": { "@babel/runtime": "7.28.2", diff --git a/modes/longitudinal/src/index.ts b/modes/longitudinal/src/index.ts index 59a265e38f4..cb8b9a18a0b 100644 --- a/modes/longitudinal/src/index.ts +++ b/modes/longitudinal/src/index.ts @@ -1,315 +1,74 @@ import i18n from 'i18next'; import { id } from './id'; -import initToolGroups from './initToolGroups'; -import toolbarButtons from './toolbarButtons'; - -// Allow this mode by excluding non-imaging modalities such as SR, SEG -// Also, SM is not a simple imaging modalities, so exclude it. -const NON_IMAGE_MODALITIES = ['ECG', 'SEG', 'RTSTRUCT', 'RTPLAN', 'PR']; - -const ohif = { - layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout', - sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack', - thumbnailList: '@ohif/extension-default.panelModule.seriesList', - wsiSopClassHandler: - '@ohif/extension-cornerstone.sopClassHandlerModule.DicomMicroscopySopClassHandler', -}; - -const cornerstone = { - measurements: '@ohif/extension-cornerstone.panelModule.panelMeasurement', - segmentation: '@ohif/extension-cornerstone.panelModule.panelSegmentation', -}; - -const tracked = { +import { initToolGroups, toolbarButtons, cornerstone, + ohif, + dicomsr, + dicomvideo, + basicLayout, + basicRoute, + extensionDependencies as basicDependencies, + mode as basicMode, + modeInstance as basicModeInstance, + } from '@ohif/mode-basic'; + +export const tracked = { measurements: '@ohif/extension-measurement-tracking.panelModule.trackedMeasurements', thumbnailList: '@ohif/extension-measurement-tracking.panelModule.seriesList', viewport: '@ohif/extension-measurement-tracking.viewportModule.cornerstone-tracked', }; -const dicomsr = { - sopClassHandler: '@ohif/extension-cornerstone-dicom-sr.sopClassHandlerModule.dicom-sr', - sopClassHandler3D: '@ohif/extension-cornerstone-dicom-sr.sopClassHandlerModule.dicom-sr-3d', - viewport: '@ohif/extension-cornerstone-dicom-sr.viewportModule.dicom-sr', -}; - -const dicomvideo = { - sopClassHandler: '@ohif/extension-dicom-video.sopClassHandlerModule.dicom-video', - viewport: '@ohif/extension-dicom-video.viewportModule.dicom-video', -}; - -const dicompdf = { - sopClassHandler: '@ohif/extension-dicom-pdf.sopClassHandlerModule.dicom-pdf', - viewport: '@ohif/extension-dicom-pdf.viewportModule.dicom-pdf', -}; - -const dicomSeg = { - sopClassHandler: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', - viewport: '@ohif/extension-cornerstone-dicom-seg.viewportModule.dicom-seg', +export const extensionDependencies = { + // Can derive the versions at least process.env.from npm_package_version + ...basicDependencies, + '@ohif/extension-measurement-tracking': '^3.0.0', }; -const dicomPmap = { - sopClassHandler: '@ohif/extension-cornerstone-dicom-pmap.sopClassHandlerModule.dicom-pmap', - viewport: '@ohif/extension-cornerstone-dicom-pmap.viewportModule.dicom-pmap', -}; +export const longitudinalInstance = { + ...basicLayout, + id: ohif.layout, + props: { + ...basicLayout.props, + leftPanels: [tracked.thumbnailList], + rightPanels: [cornerstone.segmentation, tracked.measurements], + viewports: [ + { + namespace: tracked.viewport, + // Re-use the display sets from basic + displaySetsToDisplay: basicLayout.props.viewports[0].displaySetsToDisplay, + }, + ...basicLayout.props.viewports, + ], + } + }; -const dicomRT = { - viewport: '@ohif/extension-cornerstone-dicom-rt.viewportModule.dicom-rt', - sopClassHandler: '@ohif/extension-cornerstone-dicom-rt.sopClassHandlerModule.dicom-rt', -}; -const extensionDependencies = { - // Can derive the versions at least process.env.from npm_package_version - '@ohif/extension-default': '^3.0.0', - '@ohif/extension-cornerstone': '^3.0.0', - '@ohif/extension-measurement-tracking': '^3.0.0', - '@ohif/extension-cornerstone-dicom-sr': '^3.0.0', - '@ohif/extension-cornerstone-dicom-seg': '^3.0.0', - '@ohif/extension-cornerstone-dicom-pmap': '^3.0.0', - '@ohif/extension-cornerstone-dicom-rt': '^3.0.0', - '@ohif/extension-dicom-pdf': '^3.0.1', - '@ohif/extension-dicom-video': '^3.0.1', -}; +export const longitudinalRoute = + { + ...basicRoute, + path: 'longitudinal', + /*init: ({ servicesManager, extensionManager }) => { + //defaultViewerRouteInit + },*/ + layoutInstance: longitudinalInstance, + }; -function modeFactory({ modeConfiguration }) { - let _activatePanelTriggersSubscriptions = []; - return { +export const modeInstance = { + ...basicModeInstance, // TODO: We're using this as a route segment // We should not be. id, routeName: 'viewer', displayName: i18n.t('Modes:Basic Viewer'), - /** - * Lifecycle hooks - */ - onModeEnter: function ({ servicesManager, extensionManager, commandsManager }: withAppTypes) { - const { measurementService, toolbarService, toolGroupService, customizationService } = - servicesManager.services; - - measurementService.clearMeasurements(); - - // Init Default and SR ToolGroups - initToolGroups(extensionManager, toolGroupService, commandsManager); - - toolbarService.register(toolbarButtons); - toolbarService.updateSection(toolbarService.sections.primary, [ - 'MeasurementTools', - 'Zoom', - 'Pan', - 'TrackballRotate', - 'WindowLevel', - 'Capture', - 'Layout', - 'Crosshairs', - 'MoreTools', - ]); - - toolbarService.updateSection(toolbarService.sections.viewportActionMenu.topLeft, [ - 'orientationMenu', - 'dataOverlayMenu', - ]); - - toolbarService.updateSection(toolbarService.sections.viewportActionMenu.bottomMiddle, [ - 'AdvancedRenderingControls', - ]); - - toolbarService.updateSection('AdvancedRenderingControls', [ - 'windowLevelMenuEmbedded', - 'voiManualControlMenu', - 'Colorbar', - 'opacityMenu', - 'thresholdMenu', - ]); - - toolbarService.updateSection(toolbarService.sections.viewportActionMenu.topRight, [ - 'modalityLoadBadge', - 'trackingStatus', - 'navigationComponent', - ]); - - toolbarService.updateSection(toolbarService.sections.viewportActionMenu.bottomLeft, [ - 'windowLevelMenu', - ]); - - toolbarService.updateSection('MeasurementTools', [ - 'Length', - 'Bidirectional', - 'ArrowAnnotate', - 'EllipticalROI', - 'RectangleROI', - 'CircleROI', - 'PlanarFreehandROI', - 'SplineROI', - 'LivewireContour', - ]); - - toolbarService.updateSection('MoreTools', [ - 'Reset', - 'rotate-right', - 'flipHorizontal', - 'ImageSliceSync', - 'ReferenceLines', - 'ImageOverlayViewer', - 'StackScroll', - 'invert', - 'Probe', - 'Cine', - 'Angle', - 'CobbAngle', - 'Magnify', - 'CalibrationLine', - 'TagBrowser', - 'AdvancedMagnify', - 'UltrasoundDirectionalTool', - 'WindowLevelRegion', - 'SegmentLabelTool', - ]); - - customizationService.setCustomizations({ - 'panelSegmentation.disableEditing': { - $set: true, - }, - }); - - // // ActivatePanel event trigger for when a segmentation or measurement is added. - // // Do not force activation so as to respect the state the user may have left the UI in. - // _activatePanelTriggersSubscriptions = [ - // ...panelService.addActivatePanelTriggers( - // cornerstone.segmentation, - // [ - // { - // sourcePubSubService: segmentationService, - // sourceEvents: [segmentationService.EVENTS.SEGMENTATION_ADDED], - // }, - // ], - // true - // ), - // ...panelService.addActivatePanelTriggers( - // tracked.measurements, - // [ - // { - // sourcePubSubService: measurementService, - // sourceEvents: [ - // measurementService.EVENTS.MEASUREMENT_ADDED, - // measurementService.EVENTS.RAW_MEASUREMENT_ADDED, - // ], - // }, - // ], - // true - // ), - // true, - // ]; - }, - onModeExit: ({ servicesManager }: withAppTypes) => { - const { - toolGroupService, - syncGroupService, - segmentationService, - cornerstoneViewportService, - uiDialogService, - uiModalService, - } = servicesManager.services; - - _activatePanelTriggersSubscriptions.forEach(sub => sub.unsubscribe()); - _activatePanelTriggersSubscriptions = []; - - uiDialogService.hideAll(); - uiModalService.hide(); - toolGroupService.destroy(); - syncGroupService.destroy(); - segmentationService.destroy(); - cornerstoneViewportService.destroy(); - }, - validationTags: { - study: [], - series: [], - }, - - isValidMode: function ({ modalities }) { - const modalities_list = modalities.split('\\'); - - // Exclude non-image modalities - return { - valid: !!modalities_list.filter(modality => NON_IMAGE_MODALITIES.indexOf(modality) === -1) - .length, - description: - 'The mode does not support studies that ONLY include the following modalities: SM, ECG, SEG, RTSTRUCT', - }; - }, routes: [ - { - path: 'longitudinal', - /*init: ({ servicesManager, extensionManager }) => { - //defaultViewerRouteInit - },*/ - layoutTemplate: () => { - return { - id: ohif.layout, - props: { - leftPanels: [tracked.thumbnailList], - leftPanelResizable: true, - rightPanels: [cornerstone.segmentation, tracked.measurements], - rightPanelClosed: true, - rightPanelResizable: true, - viewports: [ - { - namespace: tracked.viewport, - displaySetsToDisplay: [ - ohif.sopClassHandler, - dicomvideo.sopClassHandler, - ohif.wsiSopClassHandler, - ], - }, - { - namespace: dicomsr.viewport, - displaySetsToDisplay: [dicomsr.sopClassHandler, dicomsr.sopClassHandler3D], - }, - { - namespace: dicompdf.viewport, - displaySetsToDisplay: [dicompdf.sopClassHandler], - }, - { - namespace: dicomSeg.viewport, - displaySetsToDisplay: [dicomSeg.sopClassHandler], - }, - { - namespace: dicomPmap.viewport, - displaySetsToDisplay: [dicomPmap.sopClassHandler], - }, - { - namespace: dicomRT.viewport, - displaySetsToDisplay: [dicomRT.sopClassHandler], - }, - ], - }, - }; - }, - }, + longitudinalRoute ], extensions: extensionDependencies, - // Default protocol gets self-registered by default in the init - hangingProtocol: 'default', - // Order is important in sop class handlers when two handlers both use - // the same sop class under different situations. In that case, the more - // general handler needs to come last. For this case, the dicomvideo must - // come first to remove video transfer syntax before ohif uses images - sopClassHandlers: [ - dicomvideo.sopClassHandler, - dicomSeg.sopClassHandler, - dicomPmap.sopClassHandler, - ohif.sopClassHandler, - ohif.wsiSopClassHandler, - dicompdf.sopClassHandler, - dicomsr.sopClassHandler3D, - dicomsr.sopClassHandler, - dicomRT.sopClassHandler, - ], - ...modeConfiguration, }; -} const mode = { + ...basicMode, id, - modeFactory, + modeInstance, extensionDependencies, }; diff --git a/modes/microscopy/CHANGELOG.md b/modes/microscopy/CHANGELOG.md index 229f550c33d..89fdccf0244 100644 --- a/modes/microscopy/CHANGELOG.md +++ b/modes/microscopy/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/mode-microscopy @@ -11,7 +11,1096 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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 @ohif/mode-microscopy + + + + + +# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25) + + +### Bug Fixes + +* **i18n:** Add and update i18n Translation for Toolbar Tool Names ([#5392](https://github.com/OHIF/Viewers/issues/5392)) ([7783d0f](https://github.com/OHIF/Viewers/commit/7783d0f558bbb7016c29513b6fe76ed069dbd75c)) + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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 @ohif/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [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/mode-microscopy + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + + +### Features + +* Add WSI bulk data annotations support (ANN) ([#4972](https://github.com/OHIF/Viewers/issues/4972)) ([2ed6818](https://github.com/OHIF/Viewers/commit/2ed681817142b13971bb17b3058be26bbab6b90b)) + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/mode-microscopy + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/mode-microscopy diff --git a/modes/microscopy/package.json b/modes/microscopy/package.json index e5b8e967b9c..c84cef9e76c 100644 --- a/modes/microscopy/package.json +++ b/modes/microscopy/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/mode-microscopy", - "version": "3.11.1", + "version": "3.12.0", "description": "OHIF mode for DICOM microscopy", "author": "OHIF", "license": "MIT", @@ -35,8 +35,8 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-dicom-microscopy": "3.11.1" + "@ohif/core": "3.12.0", + "@ohif/extension-dicom-microscopy": "3.12.0" }, "dependencies": { "@babel/runtime": "7.28.2", diff --git a/modes/microscopy/src/index.tsx b/modes/microscopy/src/index.tsx index 470dfd8e1cc..ec789fa7a0c 100644 --- a/modes/microscopy/src/index.tsx +++ b/modes/microscopy/src/index.tsx @@ -103,6 +103,7 @@ function modeFactory({ modeConfiguration }) { // Share the sop class handler with cornerstone version of it '@ohif/extension-cornerstone.sopClassHandlerModule.DicomMicroscopySopClassHandler', '@ohif/extension-dicom-microscopy.sopClassHandlerModule.DicomMicroscopySRSopClassHandler', + '@ohif/extension-dicom-microscopy.sopClassHandlerModule.DicomMicroscopyANNSopClassHandler', ], }, { @@ -124,6 +125,7 @@ function modeFactory({ modeConfiguration }) { sopClassHandlers: [ '@ohif/extension-cornerstone.sopClassHandlerModule.DicomMicroscopySopClassHandler', '@ohif/extension-dicom-microscopy.sopClassHandlerModule.DicomMicroscopySRSopClassHandler', + '@ohif/extension-dicom-microscopy.sopClassHandlerModule.DicomMicroscopyANNSopClassHandler', dicomvideo.sopClassHandler, dicompdf.sopClassHandler, ], diff --git a/modes/microscopy/src/toolbarButtons.ts b/modes/microscopy/src/toolbarButtons.ts index 519791296ab..f4fac6c9349 100644 --- a/modes/microscopy/src/toolbarButtons.ts +++ b/modes/microscopy/src/toolbarButtons.ts @@ -1,4 +1,5 @@ import type { Button } from '@ohif/core/types'; +import i18n from 'i18next'; export const setToolActiveToolbar = { commandName: 'setToolActive', @@ -21,8 +22,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-length', - label: 'Line', - tooltip: 'Line', + label: i18n.t('Buttons:Line'), + tooltip: i18n.t('Buttons:Line Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.microscopyTool', }, @@ -32,8 +33,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-point', - label: 'Point', - tooltip: 'Point Tool', + label: i18n.t('Buttons:Point'), + tooltip: i18n.t('Buttons:Point Tool'), commands: { ...setToolActiveToolbar, commandOptions: { toolName: 'point' }, @@ -46,8 +47,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-polygon', - label: 'Polygon', - tooltip: 'Polygon Tool', + label: i18n.t('Buttons:Polygon'), + tooltip: i18n.t('Buttons:Polygon Tool'), commands: { ...setToolActiveToolbar, commandOptions: { toolName: 'polygon' }, @@ -60,8 +61,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-circle', - label: 'Circle', - tooltip: 'Circle Tool', + label: i18n.t('Buttons:Circle'), + tooltip: i18n.t('Buttons:Circle Tool'), commands: { ...setToolActiveToolbar, commandOptions: { toolName: 'circle' }, @@ -74,8 +75,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-rectangle', - label: 'Box', - tooltip: 'Box Tool', + label: i18n.t('Buttons:Box'), + tooltip: i18n.t('Buttons:Box Tool'), commands: { ...setToolActiveToolbar, commandOptions: { toolName: 'box' }, @@ -88,8 +89,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-freehand-polygon', - label: 'Freehand Polygon', - tooltip: 'Freehand Polygon Tool', + label: i18n.t('Buttons:Freehand Polygon'), + tooltip: i18n.t('Buttons:Freehand Polygon Tool'), commands: { ...setToolActiveToolbar, commandOptions: { toolName: 'freehandpolygon' }, @@ -102,8 +103,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-freehand-line', - label: 'Freehand Line', - tooltip: 'Freehand Line Tool', + label: i18n.t('Buttons:Freehand Line'), + tooltip: i18n.t('Buttons:Freehand Line Tool'), commands: { ...setToolActiveToolbar, commandOptions: { toolName: 'freehandline' }, @@ -116,7 +117,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-move', - label: 'Pan', + label: i18n.t('Buttons:Pan'), commands: { ...setToolActiveToolbar, commandOptions: { toolName: 'dragPan' }, @@ -129,8 +130,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'dicom-tag-browser', - label: 'Dicom Tag Browser', - tooltip: 'Dicom Tag Browser', + label: i18n.t('Buttons:Dicom Tag Browser'), + tooltip: i18n.t('Buttons:Dicom Tag Browser'), commands: 'openDICOMTagViewer', evaluate: 'evaluate.action', }, diff --git a/modes/preclinical-4d/CHANGELOG.md b/modes/preclinical-4d/CHANGELOG.md index 21b7f343ffa..6cbd77fb3b5 100644 --- a/modes/preclinical-4d/CHANGELOG.md +++ b/modes/preclinical-4d/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/mode-preclinical-4d @@ -11,7 +11,1096 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16) + + +### Bug Fixes + +* preclinical mode not working ([#5631](https://github.com/OHIF/Viewers/issues/5631)) ([2de81ef](https://github.com/OHIF/Viewers/commit/2de81efa7e6be534972cd763271c038b293d86f0)) + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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 @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25) + + +### Bug Fixes + +* **i18n:** Add and update i18n Translation for Toolbar Tool Names ([#5392](https://github.com/OHIF/Viewers/issues/5392)) ([7783d0f](https://github.com/OHIF/Viewers/commit/7783d0f558bbb7016c29513b6fe76ed069dbd75c)) + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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 @ohif/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [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/mode-preclinical-4d + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/mode-preclinical-4d + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/mode-preclinical-4d diff --git a/modes/preclinical-4d/package.json b/modes/preclinical-4d/package.json index 89b5ad2ee39..e138ea287f1 100644 --- a/modes/preclinical-4d/package.json +++ b/modes/preclinical-4d/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/mode-preclinical-4d", - "version": "3.11.1", + "version": "3.12.0", "description": "4D Workflow", "author": "OHIF", "license": "MIT", @@ -33,12 +33,12 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-seg": "3.11.1", - "@ohif/extension-cornerstone-dynamic-volume": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-tmtv": "3.11.1" + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone": "3.12.0", + "@ohif/extension-cornerstone-dicom-seg": "3.12.0", + "@ohif/extension-cornerstone-dynamic-volume": "3.12.0", + "@ohif/extension-default": "3.12.0", + "@ohif/extension-tmtv": "3.12.0" }, "dependencies": { "@babel/runtime": "7.28.2" diff --git a/modes/preclinical-4d/src/index.tsx b/modes/preclinical-4d/src/index.tsx index 029fcf89104..b1fbdde7855 100644 --- a/modes/preclinical-4d/src/index.tsx +++ b/modes/preclinical-4d/src/index.tsx @@ -111,9 +111,14 @@ function modeFactory({ modeConfiguration }) { cornerstoneViewportService.EVENTS.VIEWPORT_VOLUMES_CHANGED, () => { const viewportId = viewportGridService.getActiveViewportId(); - const csViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId); - cineService.playClip(csViewport.element, { viewportId }); - // cineService.setIsCineEnabled(true); + + if (!viewportId) { + return; + } + + const frameRate = 24; + cineService.setIsCineEnabled(true); + cineService.setCine({ id: viewportId, isPlaying: true, frameRate }); unsubscribe(); } diff --git a/modes/preclinical-4d/src/toolbarButtons.tsx b/modes/preclinical-4d/src/toolbarButtons.tsx index b0feb3b897a..f2d06940a29 100644 --- a/modes/preclinical-4d/src/toolbarButtons.tsx +++ b/modes/preclinical-4d/src/toolbarButtons.tsx @@ -1,5 +1,6 @@ import { toolGroupIds } from './initToolGroups'; import { ViewportGridService } from '@ohif/core'; +import i18n from 'i18next'; const setToolActiveToolbar = { commandName: 'setToolActiveToolbar', @@ -51,8 +52,8 @@ const toolbarButtons = [ uiType: 'ohif.modalityLoadBadge', props: { icon: 'Status', - label: 'Status', - tooltip: 'Status', + label: i18n.t('Buttons:Status'), + tooltip: i18n.t('Buttons:Status'), evaluate: { name: 'evaluate.modalityLoadBadge', hideWhenDisabled: true, @@ -64,8 +65,8 @@ const toolbarButtons = [ uiType: 'ohif.navigationComponent', props: { icon: 'Navigation', - label: 'Navigation', - tooltip: 'Navigate between segments/measurements and manage their visibility', + label: i18n.t('Buttons:Navigation'), + tooltip: i18n.t('Buttons:Navigate between segments/measurements and manage their visibility'), evaluate: { name: 'evaluate.navigationComponent', hideWhenDisabled: true, @@ -77,8 +78,8 @@ const toolbarButtons = [ uiType: 'ohif.windowLevelMenuEmbedded', props: { icon: 'WindowLevel', - label: 'Window Level', - tooltip: 'Adjust window/level presets and customize image contrast settings', + label: i18n.t('Buttons:Window Level'), + tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'), evaluate: { name: 'evaluate.windowLevelMenuEmbedded', hideWhenDisabled: true, @@ -90,8 +91,8 @@ const toolbarButtons = [ uiType: 'ohif.trackingStatus', props: { icon: 'TrackingStatus', - label: 'Tracking Status', - tooltip: 'View and manage tracking status of measurements and annotations', + label: i18n.t('Buttons:Tracking Status'), + tooltip: i18n.t('Buttons:View and manage tracking status of measurements and annotations'), evaluate: { name: 'evaluate.trackingStatus', hideWhenDisabled: true, @@ -103,8 +104,8 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-length', - label: 'Length', - tooltip: 'Length Tool', + label: i18n.t('Buttons:Length'), + tooltip: i18n.t('Buttons:Length Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -114,8 +115,8 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-bidirectional', - label: 'Bidirectional', - tooltip: 'Bidirectional Tool', + label: i18n.t('Buttons:Bidirectional'), + tooltip: i18n.t('Buttons:Bidirectional Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -125,8 +126,8 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-annotate', - label: 'Annotation', - tooltip: 'Arrow Annotate', + label: i18n.t('Buttons:Annotation'), + tooltip: i18n.t('Buttons:Arrow Annotate'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -136,8 +137,8 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-ellipse', - label: 'Ellipse', - tooltip: 'Ellipse ROI', + label: i18n.t('Buttons:Ellipse'), + tooltip: i18n.t('Buttons:Ellipse ROI'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -147,7 +148,7 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-zoom', - label: 'Zoom', + label: i18n.t('Buttons:Zoom'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -157,7 +158,7 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-window-level', - label: 'Window Level', + label: i18n.t('Buttons:Window Level'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -168,7 +169,7 @@ const toolbarButtons = [ props: { type: 'tool', icon: 'tool-move', - label: 'Pan', + label: i18n.t('Buttons:Pan'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -179,7 +180,7 @@ const toolbarButtons = [ props: { type: 'tool', icon: 'tool-3d-rotate', - label: '3D Rotate', + label: i18n.t('Buttons:3D Rotate'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -189,7 +190,7 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-capture', - label: 'Capture', + label: i18n.t('Buttons:Capture'), commands: 'showDownloadViewportModal', evaluate: [ 'evaluate.action', @@ -216,7 +217,7 @@ const toolbarButtons = [ props: { type: 'tool', icon: 'tool-crosshair', - label: 'Crosshairs', + label: i18n.t('Buttons:Crosshairs'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -230,7 +231,7 @@ const toolbarButtons = [ uiType: 'ohif.toolBoxButton', props: { icon: 'tool-create-threshold', - label: 'Rectangle ROI Threshold', + label: i18n.t('Buttons:Rectangle ROI Threshold'), commands: setToolActiveToolbar, evaluate: { name: 'evaluate.cornerstone.segmentation', @@ -244,7 +245,7 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-brush', - label: 'Brush', + label: i18n.t('Buttons:Brush'), evaluate: { name: 'evaluate.cornerstone.segmentation', toolNames: ['CircularBrush', 'SphereBrush'], @@ -264,13 +265,13 @@ const toolbarButtons = [ }, }, { - name: 'Shape', + name: i18n.t('Buttons:Shape'), type: 'radio', id: 'brush-mode', value: 'CircularBrush', values: [ - { value: 'CircularBrush', label: 'Circle' }, - { value: 'SphereBrush', label: 'Sphere' }, + { value: 'CircularBrush', label: i18n.t('Buttons:Circle') }, + { value: 'SphereBrush', label: i18n.t('Buttons:Sphere') }, ], commands: 'setToolActiveToolbar', }, @@ -282,14 +283,14 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-eraser', - label: 'Eraser', + label: i18n.t('Buttons:Eraser'), evaluate: { name: 'evaluate.cornerstone.segmentation', toolNames: ['CircularEraser', 'SphereEraser'], }, options: [ { - name: 'Radius (mm)', + name: i18n.t('Buttons:Radius (mm)'), id: 'eraser-radius', type: 'range', min: 0.5, @@ -302,13 +303,13 @@ const toolbarButtons = [ }, }, { - name: 'Shape', + name: i18n.t('Buttons:Shape'), type: 'radio', id: 'eraser-mode', value: 'CircularEraser', values: [ - { value: 'CircularEraser', label: 'Circle' }, - { value: 'SphereEraser', label: 'Sphere' }, + { value: 'CircularEraser', label: i18n.t('Buttons:Circle') }, + { value: 'SphereEraser', label: i18n.t('Buttons:Sphere') }, ], commands: 'setToolActiveToolbar', }, @@ -320,14 +321,14 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-threshold', - label: 'Threshold', + label: i18n.t('Buttons:Threshold'), evaluate: { name: 'evaluate.cornerstone.segmentation', toolNames: ['ThresholdCircularBrush', 'ThresholdSphereBrush'], }, options: [ { - name: 'Radius (mm)', + name: i18n.t('Buttons:Radius (mm)'), id: 'threshold-radius', type: 'range', min: 0.5, @@ -342,13 +343,13 @@ const toolbarButtons = [ }, }, { - name: 'Shape', + name: i18n.t('Buttons:Shape'), type: 'radio', id: 'eraser-mode', value: 'ThresholdCircularBrush', values: [ - { value: 'ThresholdCircularBrush', label: 'Circle' }, - { value: 'ThresholdSphereBrush', label: 'Sphere' }, + { value: 'ThresholdCircularBrush', label: i18n.t('Buttons:Circle') }, + { value: 'ThresholdSphereBrush', label: i18n.t('Buttons:Sphere') }, ], commands: 'setToolActiveToolbar', }, @@ -382,14 +383,14 @@ const toolbarButtons = [ icon: 'icon-tool-shape', options: [ { - name: 'Shape', + name: i18n.t('Buttons:Shape'), type: 'radio', value: 'CircleScissor', id: 'shape-mode', values: [ - { value: 'CircleScissor', label: 'Circle' }, - { value: 'SphereScissor', label: 'Sphere' }, - { value: 'RectangleScissor', label: 'Rectangle' }, + { value: 'CircleScissor', label: i18n.t('Buttons:Circle') }, + { value: 'SphereScissor', label: i18n.t('Buttons:Sphere') }, + { value: 'RectangleScissor', label: i18n.t('Buttons:Rectangle') }, ], commands: 'setToolActiveToolbar', }, @@ -401,8 +402,10 @@ const toolbarButtons = [ uiType: 'ohif.dataOverlayMenu', props: { icon: 'ViewportViews', - label: 'Data Overlay', - tooltip: 'Configure data overlay options and manage foreground/background display sets', + label: i18n.t('Buttons:Data Overlay'), + tooltip: i18n.t( + 'Buttons:Configure data overlay options and manage foreground/background display sets' + ), evaluate: 'evaluate.dataOverlayMenu', }, }, @@ -411,8 +414,10 @@ const toolbarButtons = [ uiType: 'ohif.orientationMenu', props: { icon: 'OrientationSwitch', - label: 'Orientation', - tooltip: 'Change viewport orientation between axial, sagittal, coronal and reformat planes', + label: i18n.t('Buttons:Orientation'), + tooltip: i18n.t( + 'Buttons:Change viewport orientation between axial, sagittal, coronal and reformat planes' + ), evaluate: { name: 'evaluate.orientationMenu', // hideWhenDisabled: true, @@ -424,8 +429,8 @@ const toolbarButtons = [ uiType: 'ohif.windowLevelMenu', props: { icon: 'WindowLevel', - label: 'Window Level', - tooltip: 'Adjust window/level presets and customize image contrast settings', + label: i18n.t('Buttons:Window Level'), + tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'), evaluate: 'evaluate.windowLevelMenu', }, }, @@ -434,8 +439,8 @@ const toolbarButtons = [ uiType: 'ohif.voiManualControlMenu', props: { icon: 'WindowLevelAdvanced', - label: 'Advanced Window Level', - tooltip: 'Advanced window/level settings with manual controls and presets', + label: i18n.t('Buttons:Advanced Window Level'), + tooltip: i18n.t('Buttons:Advanced window/level settings with manual controls and presets'), evaluate: 'evaluate.voiManualControlMenu', }, }, @@ -444,8 +449,8 @@ const toolbarButtons = [ uiType: 'ohif.thresholdMenu', props: { icon: 'Threshold', - label: 'Threshold', - tooltip: 'Image threshold settings', + label: i18n.t('Buttons:Threshold'), + tooltip: i18n.t('Buttons:Image threshold settings'), evaluate: { name: 'evaluate.thresholdMenu', hideWhenDisabled: true, @@ -457,8 +462,8 @@ const toolbarButtons = [ uiType: 'ohif.opacityMenu', props: { icon: 'Opacity', - label: 'Opacity', - tooltip: 'Image opacity settings', + label: i18n.t('Buttons:Opacity'), + tooltip: i18n.t('Buttons:Image opacity settings'), evaluate: { name: 'evaluate.opacityMenu', hideWhenDisabled: true, @@ -470,7 +475,7 @@ const toolbarButtons = [ uiType: 'ohif.colorbar', props: { type: 'tool', - label: 'Colorbar', + label: i18n.t('Buttons:Colorbar'), }, }, ]; diff --git a/modes/segmentation/.webpack/webpack.prod.js b/modes/segmentation/.webpack/webpack.prod.js index 95d7bfea576..513e1194d79 100644 --- a/modes/segmentation/.webpack/webpack.prod.js +++ b/modes/segmentation/.webpack/webpack.prod.js @@ -33,12 +33,24 @@ const config = { amd: '@ohif/core', root: '@ohif/core', }, + '@ohif/mode-basic': { + commonjs2: '@ohif/mode-basic', + commonjs: '@ohif/mode-basic', + amd: '@ohif/mode-basic', + root: '@ohif/mode-basic', + }, '@ohif/ui': { commonjs2: '@ohif/ui', commonjs: '@ohif/ui', amd: '@ohif/ui', root: '@ohif/ui', }, + i18next: { + commonjs2: 'i18next', + commonjs: 'i18next', + amd: 'i18next', + root: 'i18next', + }, }, ], module: { @@ -65,11 +77,11 @@ const config = { name: 'preset-default', params: { overrides: { - removeViewBox: false + removeViewBox: false, }, }, }, - ] + ], }, prettier: false, svgo: true, diff --git a/modes/segmentation/CHANGELOG.md b/modes/segmentation/CHANGELOG.md index 8527feedea6..f3caccc94f6 100644 --- a/modes/segmentation/CHANGELOG.md +++ b/modes/segmentation/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/mode-segmentation @@ -11,7 +11,1099 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13) + + +### Bug Fixes + +* **segmentation:** Allow for manually added label map and contour segmentations to be used as viewport data overlays. ([#5562](https://github.com/OHIF/Viewers/issues/5562)) ([02f6744](https://github.com/OHIF/Viewers/commit/02f6744dab993f16cf2516fd5f657e0743e3fa42)) + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05) + + +### Bug Fixes + +* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece)) + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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 @ohif/mode-segmentation + + + + + +# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25) + + +### Bug Fixes + +* **i18n:** Add and update i18n Translation for Toolbar Tool Names ([#5392](https://github.com/OHIF/Viewers/issues/5392)) ([7783d0f](https://github.com/OHIF/Viewers/commit/7783d0f558bbb7016c29513b6fe76ed069dbd75c)) + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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 @ohif/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [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/mode-segmentation + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/mode-segmentation + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/mode-segmentation diff --git a/modes/segmentation/package.json b/modes/segmentation/package.json index 4d0675933a3..0e82c96c868 100644 --- a/modes/segmentation/package.json +++ b/modes/segmentation/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/mode-segmentation", - "version": "3.11.1", + "version": "3.12.0", "description": "OHIF segmentation mode which enables labelmap segmentation read/edit/export", "author": "@ohif", "license": "MIT", @@ -35,14 +35,15 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-rt": "3.11.1", - "@ohif/extension-cornerstone-dicom-seg": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-dicom-pdf": "3.11.1", - "@ohif/extension-dicom-video": "3.11.1" + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone": "3.12.0", + "@ohif/extension-cornerstone-dicom-rt": "3.12.0", + "@ohif/extension-cornerstone-dicom-seg": "3.12.0", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0", + "@ohif/extension-default": "3.12.0", + "@ohif/extension-dicom-pdf": "3.12.0", + "@ohif/extension-dicom-video": "3.12.0", + "@ohif/mode-basic": "3.12.0" }, "dependencies": { "@babel/runtime": "7.28.2", diff --git a/modes/segmentation/src/constants.ts b/modes/segmentation/src/constants.ts new file mode 100644 index 00000000000..c8afca7e4cf --- /dev/null +++ b/modes/segmentation/src/constants.ts @@ -0,0 +1,2 @@ +export const MIN_SEGMENTATION_DRAWING_RADIUS = 0.5; +export const MAX_SEGMENTATION_DRAWING_RADIUS = 99.5; diff --git a/modes/segmentation/src/index.tsx b/modes/segmentation/src/index.tsx index 5962f56dfe0..51f08e2d767 100644 --- a/modes/segmentation/src/index.tsx +++ b/modes/segmentation/src/index.tsx @@ -1,41 +1,12 @@ import { id } from './id'; import toolbarButtons from './toolbarButtons'; import initToolGroups from './initToolGroups'; - -const ohif = { - layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout', - sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack', - hangingProtocol: '@ohif/extension-default.hangingProtocolModule.default', - leftPanel: '@ohif/extension-default.panelModule.seriesList', -}; - -const cornerstone = { - viewport: '@ohif/extension-cornerstone.viewportModule.cornerstone', - panelTool: '@ohif/extension-cornerstone.panelModule.panelSegmentationWithTools', - measurements: '@ohif/extension-cornerstone.panelModule.panelMeasurement', -}; - -const segmentation = { - sopClassHandler: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg', - viewport: '@ohif/extension-cornerstone-dicom-seg.viewportModule.dicom-seg', -}; - -const dicomRT = { - viewport: '@ohif/extension-cornerstone-dicom-rt.viewportModule.dicom-rt', - sopClassHandler: '@ohif/extension-cornerstone-dicom-rt.sopClassHandlerModule.dicom-rt', -}; -/** - * Just two dependencies to be able to render a viewport with panels in order - * to make sure that the mode is working. - */ -const extensionDependencies = { - '@ohif/extension-default': '^3.0.0', - '@ohif/extension-cornerstone': '^3.0.0', - '@ohif/extension-cornerstone-dicom-seg': '^3.0.0', - '@ohif/extension-cornerstone-dicom-rt': '^3.0.0', -}; +import setUpAutoTabSwitchHandler from './utils/setUpAutoTabSwitchHandler'; +import { ohif, cornerstone, extensionDependencies, dicomRT, segmentation } from '@ohif/mode-basic'; +export * from './toolbarButtons'; function modeFactory({ modeConfiguration }) { + const _unsubscriptions = []; return { /** * Mode ID, which should be unique among modes used by the viewer. This ID @@ -53,8 +24,14 @@ function modeFactory({ modeConfiguration }) { * Services and other resources. */ onModeEnter: ({ servicesManager, extensionManager, commandsManager }: withAppTypes) => { - const { measurementService, toolbarService, toolGroupService, customizationService } = - servicesManager.services; + const { + measurementService, + toolbarService, + toolGroupService, + segmentationService, + viewportGridService, + panelService, + } = servicesManager.services; measurementService.clearMeasurements(); @@ -114,23 +91,54 @@ function modeFactory({ modeConfiguration }) { 'TagBrowser', ]); - toolbarService.updateSection(toolbarService.sections.segmentationToolbox, [ - 'SegmentationUtilities', - 'SegmentationTools', + toolbarService.updateSection(toolbarService.sections.labelMapSegmentationToolbox, [ + 'LabelMapTools', ]); - toolbarService.updateSection('SegmentationUtilities', [ - 'LabelmapSlicePropagation', - 'InterpolateLabelmap', - 'SegmentBidirectional', - 'SegmentLabelTool', + toolbarService.updateSection(toolbarService.sections.contourSegmentationToolbox, [ + 'ContourTools', ]); - toolbarService.updateSection('SegmentationTools', [ + + toolbarService.updateSection('LabelMapTools', [ + 'LabelmapSlicePropagation', 'BrushTools', 'MarkerLabelmap', 'RegionSegmentPlus', 'Shapes', + 'LabelMapEditWithContour', + ]); + toolbarService.updateSection('ContourTools', [ + 'PlanarFreehandContourSegmentationTool', + 'SculptorTool', + 'SplineContourSegmentationTool', + 'LivewireContourSegmentationTool', + ]); + + toolbarService.updateSection(toolbarService.sections.labelMapSegmentationUtilities, [ + 'LabelMapUtilities', + ]); + toolbarService.updateSection(toolbarService.sections.contourSegmentationUtilities, [ + 'ContourUtilities', + ]); + + toolbarService.updateSection('LabelMapUtilities', [ + 'InterpolateLabelmap', + 'SegmentBidirectional', + ]); + toolbarService.updateSection('ContourUtilities', [ + 'LogicalContourOperations', + 'SimplifyContours', + 'SmoothContours', ]); + toolbarService.updateSection('BrushTools', ['Brush', 'Eraser', 'Threshold']); + + const { unsubscribeAutoTabSwitchEvents } = setUpAutoTabSwitchHandler({ + segmentationService, + viewportGridService, + panelService, + }); + + _unsubscriptions.push(...unsubscribeAutoTabSwitchEvents); }, onModeExit: ({ servicesManager }: withAppTypes) => { const { @@ -142,6 +150,9 @@ function modeFactory({ modeConfiguration }) { uiModalService, } = servicesManager.services; + _unsubscriptions.forEach(unsubscribe => unsubscribe()); + _unsubscriptions.length = 0; + uiDialogService.hideAll(); uiModalService.hide(); toolGroupService.destroy(); @@ -191,9 +202,12 @@ function modeFactory({ modeConfiguration }) { return { id: ohif.layout, props: { - leftPanels: [ohif.leftPanel], + leftPanels: [ohif.thumbnailList], leftPanelResizable: true, - rightPanels: [cornerstone.panelTool], + rightPanels: [ + cornerstone.labelMapSegmentationPanel, + cornerstone.contourSegmentationPanel, + ], rightPanelResizable: true, // leftPanelClosed: true, viewports: [ diff --git a/modes/segmentation/src/initToolGroups.ts b/modes/segmentation/src/initToolGroups.ts index 48a15303280..23509a8c965 100644 --- a/modes/segmentation/src/initToolGroups.ts +++ b/modes/segmentation/src/initToolGroups.ts @@ -1,3 +1,5 @@ +import { MIN_SEGMENTATION_DRAWING_RADIUS, MAX_SEGMENTATION_DRAWING_RADIUS } from './constants'; + const colours = { 'viewport-0': 'rgb(200, 0, 0)', 'viewport-1': 'rgb(200, 200, 0)', @@ -32,6 +34,8 @@ function createTools({ utilityModule, commandsManager }) { parentTool: 'Brush', configuration: { activeStrategy: 'FILL_INSIDE_CIRCLE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, { @@ -48,6 +52,8 @@ function createTools({ utilityModule, commandsManager }) { parentTool: 'Brush', configuration: { activeStrategy: 'ERASE_INSIDE_CIRCLE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, { @@ -55,6 +61,8 @@ function createTools({ utilityModule, commandsManager }) { parentTool: 'Brush', configuration: { activeStrategy: 'FILL_INSIDE_SPHERE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, { @@ -62,6 +70,8 @@ function createTools({ utilityModule, commandsManager }) { parentTool: 'Brush', configuration: { activeStrategy: 'ERASE_INSIDE_SPHERE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, { @@ -69,6 +79,8 @@ function createTools({ utilityModule, commandsManager }) { parentTool: 'Brush', configuration: { activeStrategy: 'THRESHOLD_INSIDE_CIRCLE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, { @@ -76,6 +88,8 @@ function createTools({ utilityModule, commandsManager }) { parentTool: 'Brush', configuration: { activeStrategy: 'THRESHOLD_INSIDE_SPHERE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, { @@ -83,6 +97,8 @@ function createTools({ utilityModule, commandsManager }) { parentTool: 'Brush', configuration: { activeStrategy: 'THRESHOLD_INSIDE_CIRCLE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, threshold: { isDynamic: true, dynamicRadius: 3, @@ -100,12 +116,17 @@ function createTools({ utilityModule, commandsManager }) { parentTool: 'Brush', configuration: { activeStrategy: 'THRESHOLD_INSIDE_SPHERE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, threshold: { isDynamic: true, dynamicRadius: 3, }, }, }, + { + toolName: toolNames.LabelMapEditWithContourTool, + }, { toolName: toolNames.CircleScissors }, { toolName: toolNames.RectangleScissors }, { toolName: toolNames.SphereScissors }, @@ -114,6 +135,42 @@ function createTools({ utilityModule, commandsManager }) { { toolName: toolNames.WindowLevelRegion }, { toolName: toolNames.UltrasoundDirectional }, + { + toolName: toolNames.PlanarFreehandContourSegmentation, + }, + { toolName: toolNames.LivewireContourSegmentation }, + { toolName: toolNames.SculptorTool }, + { toolName: toolNames.PlanarFreehandROI }, + { + toolName: 'CatmullRomSplineROI', + parentTool: toolNames.SplineContourSegmentation, + configuration: { + spline: { + type: 'CATMULLROM', + enableTwoPointPreview: true, + }, + }, + }, + { + toolName: 'LinearSplineROI', + parentTool: toolNames.SplineContourSegmentation, + configuration: { + spline: { + type: 'LINEAR', + enableTwoPointPreview: true, + }, + }, + }, + { + toolName: 'BSplineROI', + parentTool: toolNames.SplineContourSegmentation, + configuration: { + spline: { + type: 'BSPLINE', + enableTwoPointPreview: true, + }, + }, + }, ], disabled: [{ toolName: toolNames.ReferenceLines }, { toolName: toolNames.AdvancedMagnify }], }; diff --git a/modes/segmentation/src/toolbarButtons.ts b/modes/segmentation/src/toolbarButtons.ts index 72c17b34c36..ecf2a5b36b8 100644 --- a/modes/segmentation/src/toolbarButtons.ts +++ b/modes/segmentation/src/toolbarButtons.ts @@ -1,5 +1,8 @@ import type { Button } from '@ohif/core/types'; import { ViewportGridService } from '@ohif/core'; +import i18n from 'i18next'; + +import { MIN_SEGMENTATION_DRAWING_RADIUS, MAX_SEGMENTATION_DRAWING_RADIUS } from './constants'; const setToolActiveToolbar = { commandName: 'setToolActiveToolbar', @@ -17,7 +20,7 @@ const callbacks = (toolName: string) => [ }, ]; -const toolbarButtons: Button[] = [ +export const toolbarButtons: Button[] = [ { id: 'AdvancedRenderingControls', uiType: 'ohif.advancedRenderingControls', @@ -30,8 +33,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.modalityLoadBadge', props: { icon: 'Status', - label: 'Status', - tooltip: 'Status', + label: i18n.t('Buttons:Status'), + tooltip: i18n.t('Buttons:Status'), evaluate: { name: 'evaluate.modalityLoadBadge', hideWhenDisabled: true, @@ -43,8 +46,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.navigationComponent', props: { icon: 'Navigation', - label: 'Navigation', - tooltip: 'Navigate between segments/measurements and manage their visibility', + label: i18n.t('Buttons:Navigation'), + tooltip: i18n.t('Buttons:Navigate between segments/measurements and manage their visibility'), evaluate: { name: 'evaluate.navigationComponent', hideWhenDisabled: true, @@ -56,8 +59,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.trackingStatus', props: { icon: 'TrackingStatus', - label: 'Tracking Status', - tooltip: 'View and manage tracking status of measurements and annotations', + label: i18n.t('Buttons:Tracking Status'), + tooltip: i18n.t('Buttons:View and manage tracking status of measurements and annotations'), evaluate: { name: 'evaluate.trackingStatus', hideWhenDisabled: true, @@ -69,8 +72,10 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.dataOverlayMenu', props: { icon: 'ViewportViews', - label: 'Data Overlay', - tooltip: 'Configure data overlay options and manage foreground/background display sets', + label: i18n.t('Buttons:Data Overlay'), + tooltip: i18n.t( + 'Buttons:Configure data overlay options and manage foreground/background display sets' + ), evaluate: 'evaluate.dataOverlayMenu', }, }, @@ -79,8 +84,10 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.orientationMenu', props: { icon: 'OrientationSwitch', - label: 'Orientation', - tooltip: 'Change viewport orientation between axial, sagittal, coronal and reformat planes', + label: i18n.t('Buttons:Orientation'), + tooltip: i18n.t( + 'Buttons:Change viewport orientation between axial, sagittal, coronal and reformat planes' + ), evaluate: { name: 'evaluate.orientationMenu', // hideWhenDisabled: true, @@ -92,8 +99,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.windowLevelMenuEmbedded', props: { icon: 'WindowLevel', - label: 'Window Level', - tooltip: 'Adjust window/level presets and customize image contrast settings', + label: i18n.t('Buttons:Window Level'), + tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'), evaluate: { name: 'evaluate.windowLevelMenuEmbedded', hideWhenDisabled: true, @@ -105,8 +112,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.windowLevelMenu', props: { icon: 'WindowLevel', - label: 'Window Level', - tooltip: 'Adjust window/level presets and customize image contrast settings', + label: i18n.t('Buttons:Window Level'), + tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'), evaluate: 'evaluate.windowLevelMenu', }, }, @@ -115,8 +122,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.voiManualControlMenu', props: { icon: 'WindowLevelAdvanced', - label: 'Advanced Window Level', - tooltip: 'Advanced window/level settings with manual controls and presets', + label: i18n.t('Buttons:Advanced Window Level'), + tooltip: i18n.t('Buttons:Advanced window/level settings with manual controls and presets'), evaluate: 'evaluate.voiManualControlMenu', }, }, @@ -125,8 +132,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.thresholdMenu', props: { icon: 'Threshold', - label: 'Threshold', - tooltip: 'Image threshold settings', + label: i18n.t('Buttons:Threshold'), + tooltip: i18n.t('Buttons:Image threshold settings'), evaluate: { name: 'evaluate.thresholdMenu', hideWhenDisabled: true, @@ -138,8 +145,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.opacityMenu', props: { icon: 'Opacity', - label: 'Opacity', - tooltip: 'Image opacity settings', + label: i18n.t('Buttons:Opacity'), + tooltip: i18n.t('Buttons:Image opacity settings'), evaluate: { name: 'evaluate.opacityMenu', hideWhenDisabled: true, @@ -151,7 +158,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.colorbar', props: { type: 'tool', - label: 'Colorbar', + label: i18n.t('Buttons:Colorbar'), }, }, // sections @@ -169,17 +176,31 @@ const toolbarButtons: Button[] = [ buttonSection: true, }, }, - // Section containers for the nested toolbox + // Section containers for the nested toolboxes and toolbars. { - id: 'SegmentationUtilities', - uiType: 'ohif.toolBoxButton', + id: 'LabelMapUtilities', + uiType: 'ohif.Toolbar', props: { buttonSection: true, }, }, { - id: 'SegmentationTools', - uiType: 'ohif.toolBoxButton', + id: 'ContourUtilities', + uiType: 'ohif.Toolbar', + props: { + buttonSection: true, + }, + }, + { + id: 'LabelMapTools', + uiType: 'ohif.toolBoxButtonGroup', + props: { + buttonSection: true, + }, + }, + { + id: 'ContourTools', + uiType: 'ohif.toolBoxButtonGroup', props: { buttonSection: true, }, @@ -190,7 +211,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-zoom', - label: 'Zoom', + label: i18n.t('Buttons:Zoom'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -200,7 +221,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-window-level', - label: 'Window Level', + label: i18n.t('Buttons:Window Level'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -210,7 +231,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-move', - label: 'Pan', + label: i18n.t('Buttons:Pan'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -221,11 +242,11 @@ const toolbarButtons: Button[] = [ props: { type: 'tool', icon: 'tool-3d-rotate', - label: '3D Rotate', + label: i18n.t('Buttons:3D Rotate'), commands: setToolActiveToolbar, evaluate: { name: 'evaluate.cornerstoneTool', - disabledText: 'Select a 3D viewport to enable this tool', + disabledText: i18n.t('Buttons:Select a 3D viewport to enable this tool'), }, }, }, @@ -234,7 +255,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-capture', - label: 'Capture', + label: i18n.t('Buttons:Capture'), commands: 'showDownloadViewportModal', evaluate: [ 'evaluate.action', @@ -260,7 +281,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-crosshair', - label: 'Crosshairs', + label: i18n.t('Buttons:Crosshairs'), commands: { commandName: 'setToolActiveToolbar', commandOptions: { @@ -269,7 +290,7 @@ const toolbarButtons: Button[] = [ }, evaluate: { name: 'evaluate.cornerstoneTool', - disabledText: 'Select an MPR viewport to enable this tool', + disabledText: i18n.t('Buttons:Select an MPR viewport to enable this tool'), }, }, }, @@ -278,8 +299,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-reset', - label: 'Reset View', - tooltip: 'Reset View', + label: i18n.t('Buttons:Reset View'), + tooltip: i18n.t('Buttons:Reset View'), commands: 'resetViewport', evaluate: 'evaluate.action', }, @@ -289,8 +310,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-rotate-right', - label: 'Rotate Right', - tooltip: 'Rotate +90', + label: i18n.t('Buttons:Rotate Right'), + tooltip: i18n.t('Buttons:Rotate +90'), commands: 'rotateViewportCW', evaluate: 'evaluate.action', }, @@ -300,8 +321,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-flip-horizontal', - label: 'Flip Horizontal', - tooltip: 'Flip Horizontally', + label: i18n.t('Buttons:Flip Horizontal'), + tooltip: i18n.t('Buttons:Flip Horizontally'), commands: 'flipViewportHorizontal', evaluate: [ 'evaluate.viewportProperties.toggle', @@ -317,8 +338,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-referenceLines', - label: 'Reference Lines', - tooltip: 'Show Reference Lines', + label: i18n.t('Buttons:Reference Lines'), + tooltip: i18n.t('Buttons:Show Reference Lines'), commands: 'toggleEnabledDisabledToolbar', evaluate: 'evaluate.cornerstoneTool.toggle', }, @@ -328,8 +349,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'toggle-dicom-overlay', - label: 'Image Overlay', - tooltip: 'Toggle Image Overlay', + label: i18n.t('Buttons:Image Overlay'), + tooltip: i18n.t('Buttons:Toggle Image Overlay'), commands: 'toggleEnabledDisabledToolbar', evaluate: 'evaluate.cornerstoneTool.toggle', }, @@ -339,8 +360,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-stack-scroll', - label: 'Stack Scroll', - tooltip: 'Stack Scroll', + label: i18n.t('Buttons:Stack Scroll'), + tooltip: i18n.t('Buttons:Stack Scroll'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -350,8 +371,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-invert', - label: 'Invert', - tooltip: 'Invert Colors', + label: i18n.t('Buttons:Invert'), + tooltip: i18n.t('Buttons:Invert Colors'), commands: 'invertViewport', evaluate: 'evaluate.viewportProperties.toggle', }, @@ -361,8 +382,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-cine', - label: 'Cine', - tooltip: 'Cine', + label: i18n.t('Buttons:Cine'), + tooltip: i18n.t('Buttons:Cine'), commands: 'toggleCine', evaluate: [ 'evaluate.cine', @@ -378,8 +399,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-magnify', - label: 'Zoom-in', - tooltip: 'Zoom-in', + label: i18n.t('Buttons:Zoom-in'), + tooltip: i18n.t('Buttons:Zoom-in'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -389,82 +410,356 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'dicom-tag-browser', - label: 'Dicom Tag Browser', - tooltip: 'Dicom Tag Browser', + label: i18n.t('Buttons:Dicom Tag Browser'), + tooltip: i18n.t('Buttons:Dicom Tag Browser'), commands: 'openDICOMTagViewer', }, }, - + { + id: 'PlanarFreehandContourSegmentationTool', + uiType: 'ohif.toolBoxButton', + props: { + icon: 'icon-tool-freehand-roi', + label: i18n.t('Buttons:Freehand Segmentation'), + tooltip: i18n.t('Buttons:Freehand Segmentation'), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + toolNames: ['PlanarFreehandContourSegmentationTool'], + disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'), + }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Contour', + }, + ], + commands: [ + { + commandName: 'setToolActiveToolbar', + commandOptions: { + bindings: [ + { + mouseButton: 1, // Left Click + }, + { + mouseButton: 1, // Left Click+Shift to create a hole + modifierKey: 16, // Shift + }, + ], + }, + }, + { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Contour', + }, + }, + ], + options: [ + { + name: i18n.t('Buttons:Interpolate Contours'), + type: 'switch', + id: 'planarFreehandInterpolateContours', + value: false, + commands: { + commandName: 'setInterpolationToolConfiguration', + }, + }, + ], + }, + }, + { + id: 'LivewireContourSegmentationTool', + uiType: 'ohif.toolBoxButton', + props: { + icon: 'icon-tool-livewire', + label: i18n.t('Buttons:Livewire Contour'), + tooltip: i18n.t('Buttons:Livewire Contour'), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + toolNames: ['LivewireContourSegmentationTool'], + disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'), + }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Contour', + }, + ], + commands: [ + { + commandName: 'setToolActiveToolbar', + commandOptions: { + bindings: [ + { + mouseButton: 1, // Left Click + }, + { + mouseButton: 1, // Left Click+Shift to create a hole + modifierKey: 16, // Shift + }, + ], + }, + }, + { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Contour', + }, + }, + ], + options: [ + { + name: i18n.t('Buttons:Interpolate Contours'), + type: 'switch', + id: 'livewireInterpolateContours', + value: false, + commands: { + commandName: 'setInterpolationToolConfiguration', + }, + }, + ], + }, + }, + { + id: 'SplineContourSegmentationTool', + uiType: 'ohif.toolBoxButton', + props: { + icon: 'icon-tool-spline-roi', + label: i18n.t('Buttons:Spline Contour Segmentation Tool'), + tooltip: i18n.t('Buttons:Spline Contour Segmentation Tool'), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + toolNames: ['CatmullRomSplineROI', 'LinearSplineROI', 'BSplineROI'], + disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'), + }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Contour', + }, + ], + commands: [ + { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Contour', + }, + }, + ], + options: [ + { + name: i18n.t('Buttons:Spline Type'), + type: 'select', + id: 'splineTypeSelect', + value: 'CatmullRomSplineROI', + values: [ + { + id: 'CatmullRomSplineROI', + value: 'CatmullRomSplineROI', + label: i18n.t('Buttons:Catmull Rom Spline'), + }, + { id: 'LinearSplineROI', value: 'LinearSplineROI', label: i18n.t('Buttons:Linear Spline') }, + { id: 'BSplineROI', value: 'BSplineROI', label: i18n.t('Buttons:B-Spline') }, + ], + commands: { + commandName: 'setToolActiveToolbar', + commandOptions: { + bindings: [ + { + mouseButton: 1, // Left Click + }, + { + mouseButton: 1, // Left Click+Shift to create a hole + modifierKey: 16, // Shift + }, + ], + }, + }, + }, + { + name: i18n.t('Buttons:Simplified Spline'), + type: 'switch', + id: 'simplifiedSpline', + value: true, + commands: { + commandName: 'setSimplifiedSplineForSplineContourSegmentationTool', + }, + }, + { + name: i18n.t('Buttons:Interpolate Contours'), + type: 'switch', + id: 'splineInterpolateContours', + value: false, + commands: { + commandName: 'setInterpolationToolConfiguration', + commandOptions: { + toolNames: ['CatmullRomSplineROI', 'LinearSplineROI', 'BSplineROI'], + }, + }, + }, + ], + }, + }, + { + id: 'SculptorTool', + uiType: 'ohif.toolBoxButton', + props: { + icon: 'icon-tool-sculptor', + label: i18n.t('Buttons:Sculptor Tool'), + tooltip: i18n.t('Buttons:Sculptor Tool'), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + toolNames: ['SculptorTool'], + disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'), + }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Contour', + }, + ], + commands: [ + 'setToolActiveToolbar', + { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Contour', + }, + }, + ], + options: [ + { + name: i18n.t('Buttons:Dynamic Cursor Size'), + type: 'switch', + id: 'dynamicCursorSize', + value: true, + commands: { + commandName: 'setDynamicCursorSizeForSculptorTool', + }, + }, + ], + }, + }, { id: 'Brush', uiType: 'ohif.toolBoxButton', props: { icon: 'icon-tool-brush', - label: 'Brush', - evaluate: { - name: 'evaluate.cornerstone.segmentation', - toolNames: ['CircularBrush', 'SphereBrush'], - disabledText: 'Create new segmentation to enable this tool.', + label: i18n.t('Buttons:Brush'), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + toolNames: ['CircularBrush', 'SphereBrush'], + disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'), + }, + { + name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius', + radiusOptionId: 'brush-radius', + }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Labelmap', + }, + ], + commands: { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Labelmap', + }, }, options: [ { - name: 'Radius (mm)', + name: i18n.t('Buttons:Radius (mm)'), id: 'brush-radius', type: 'range', - min: 0.5, - max: 99.5, + explicitRunOnly: true, + min: MIN_SEGMENTATION_DRAWING_RADIUS, + max: MAX_SEGMENTATION_DRAWING_RADIUS, step: 0.5, value: 25, - commands: { - commandName: 'setBrushSize', - commandOptions: { toolNames: ['CircularBrush', 'SphereBrush'] }, - }, + commands: [ + { + commandName: 'setBrushSize', + commandOptions: { toolNames: ['CircularBrush', 'SphereBrush'] }, + }, + ], }, { - name: 'Shape', + name: i18n.t('Buttons:Shape'), type: 'radio', id: 'brush-mode', value: 'CircularBrush', values: [ - { value: 'CircularBrush', label: 'Circle' }, - { value: 'SphereBrush', label: 'Sphere' }, + { value: 'CircularBrush', label: i18n.t('Buttons:Circle') }, + { value: 'SphereBrush', label: i18n.t('Buttons:Sphere') }, ], - commands: 'setToolActiveToolbar', + commands: ['setToolActiveToolbar'], }, ], }, }, { id: 'InterpolateLabelmap', - uiType: 'ohif.toolBoxButton', + uiType: 'ohif.toolButton', props: { - icon: 'icon-tool-interpolation', - label: 'Interpolate Labelmap', - tooltip: - 'Automatically fill in missing slices between drawn segments. Use brush or threshold tools on at least two slices, then click to interpolate across slices. Works in any direction. Volume must be reconstructable.', + icon: 'actions-interpolate', + label: i18n.t('Buttons:Interpolate Labelmap'), + tooltip: i18n.t( + 'Buttons:Automatically fill in missing slices between drawn segments. Use brush or threshold tools on at least two slices, then click to interpolate across slices. Works in any direction. Volume must be reconstructable.' + ), evaluate: [ - 'evaluate.cornerstone.segmentation', + { + name: 'evaluate.cornerstone.segmentation', + }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Labelmap', + }, { name: 'evaluate.displaySetIsReconstructable', - disabledText: 'The current viewport cannot handle interpolation.', + disabledText: i18n.t('Buttons:The current viewport cannot handle interpolation.'), }, ], - commands: 'interpolateLabelmap', + commands: [ + { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Labelmap', + }, + }, + 'interpolateLabelmap', + ], }, }, { id: 'SegmentBidirectional', - uiType: 'ohif.toolBoxButton', + uiType: 'ohif.toolButton', props: { - icon: 'icon-tool-bidirectional-segment', - label: 'Segment Bidirectional', - tooltip: - 'Automatically detects the largest length and width across slices for the selected segment and displays a bidirectional measurement.', - evaluate: { - name: 'evaluate.cornerstone.segmentation', - disabledText: 'Create new segmentation to enable this tool.', - }, - commands: 'runSegmentBidirectional', + icon: 'actions-bidirectional', + label: i18n.t('Buttons:Segment Bidirectional'), + tooltip: i18n.t( + 'Buttons:Automatically detects the largest length and width across slices for the selected segment and displays a bidirectional measurement.' + ), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'), + }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Labelmap', + }, + ], + commands: [ + { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Labelmap', + }, + }, + 'runSegmentBidirectional', + ], }, }, { @@ -472,15 +767,30 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolBoxButton', props: { icon: 'icon-tool-click-segment', - label: 'One Click Segment', - tooltip: - 'Detects segmentable regions with one click. Hover for visual feedback—click when a plus sign appears to auto-segment the lesion.', - evaluate: { - name: 'evaluate.cornerstone.segmentation', - toolNames: ['RegionSegmentPlus'], - disabledText: 'Create new segmentation to enable this tool.', - }, - commands: 'setToolActiveToolbar', + label: i18n.t('Buttons:One Click Segment'), + tooltip: i18n.t( + 'Buttons:Detects segmentable regions with one click. Hover for visual feedback—click when a plus sign appears to auto-segment the lesion.' + ), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + toolNames: ['RegionSegmentPlus'], + disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'), + }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Labelmap', + }, + ], + commands: [ + 'setToolActiveToolbar', + { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Labelmap', + }, + }, + ], }, }, { @@ -488,13 +798,15 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolBoxButton', props: { icon: 'icon-labelmap-slice-propagation', - label: 'Labelmap Assist', - tooltip: - 'Toggle AI assistance for segmenting nearby slices. After drawing on a slice, scroll to preview predictions. Press Enter to accept or Esc to skip.', + label: i18n.t('Buttons:Labelmap Assist'), + tooltip: i18n.t( + 'Buttons:Toggle AI assistance for segmenting nearby slices. After drawing on a slice, scroll to preview predictions. Press Enter to accept or Esc to skip.' + ), evaluate: [ 'evaluate.cornerstoneTool.toggle', { - name: 'evaluate.cornerstone.hasSegmentation', + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Labelmap', }, ], listeners: { @@ -503,7 +815,15 @@ const toolbarButtons: Button[] = [ ), [ViewportGridService.EVENTS.VIEWPORTS_READY]: callbacks('LabelmapSlicePropagation'), }, - commands: 'toggleEnabledDisabledToolbar', + commands: [ + { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Labelmap', + }, + }, + 'toggleEnabledDisabledToolbar', + ], }, }, { @@ -511,29 +831,42 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolBoxButton', props: { icon: 'icon-marker-labelmap', - label: 'Marker Guided Labelmap', - tooltip: - 'Use include/exclude markers to guide AI (SAM) segmentation. Click to place markers, Enter to accept results, Esc to reject, and N to go to the next slice while keeping markers.', + label: i18n.t('Buttons:Marker Guided Labelmap'), + tooltip: i18n.t( + 'Buttons:Use include/exclude markers to guide AI (SAM) segmentation. Click to place markers, Enter to accept results, Esc to reject, and N to go to the next slice while keeping markers.' + ), evaluate: [ { name: 'evaluate.cornerstone.segmentation', toolNames: ['MarkerLabelmap', 'MarkerInclude', 'MarkerExclude'], }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Labelmap', + }, + ], + commands: [ + 'setToolActiveToolbar', + { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Labelmap', + }, + }, ], - commands: 'setToolActiveToolbar', listeners: { [ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: callbacks('MarkerLabelmap'), [ViewportGridService.EVENTS.VIEWPORTS_READY]: callbacks('MarkerLabelmap'), }, options: [ { - name: 'Marker Mode', + name: i18n.t('Buttons:Marker Mode'), type: 'radio', id: 'marker-mode', value: 'markerInclude', values: [ - { value: 'markerInclude', label: 'Include' }, - { value: 'markerExclude', label: 'Exclude' }, + { value: 'markerInclude', label: i18n.t('Buttons:Include') }, + { value: 'markerExclude', label: i18n.t('Buttons:Exclude') }, ], commands: ({ commandsManager, options }) => { const markerModeOption = options.find(option => option.id === 'marker-mode'); @@ -549,7 +882,7 @@ const toolbarButtons: Button[] = [ }, }, { - name: 'Clear Markers', + name: i18n.t('Buttons:Clear Markers'), type: 'button', id: 'clear-markers', commands: 'clearMarkersForMarkerLabelmap', @@ -562,18 +895,29 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolBoxButton', props: { icon: 'icon-tool-eraser', - label: 'Eraser', - evaluate: { - name: 'evaluate.cornerstone.segmentation', - toolNames: ['CircularEraser', 'SphereEraser'], - }, + label: i18n.t('Buttons:Eraser'), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + toolNames: ['CircularEraser', 'SphereEraser'], + }, + { + name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius', + radiusOptionId: 'eraser-radius', + }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Labelmap', + }, + ], options: [ { - name: 'Radius (mm)', + name: i18n.t('Buttons:Radius (mm)'), id: 'eraser-radius', type: 'range', - min: 0.5, - max: 99.5, + explicitRunOnly: true, + min: MIN_SEGMENTATION_DRAWING_RADIUS, + max: MAX_SEGMENTATION_DRAWING_RADIUS, step: 0.5, value: 25, commands: { @@ -582,17 +926,23 @@ const toolbarButtons: Button[] = [ }, }, { - name: 'Shape', + name: i18n.t('Buttons:Shape'), type: 'radio', id: 'eraser-mode', value: 'CircularEraser', values: [ - { value: 'CircularEraser', label: 'Circle' }, - { value: 'SphereEraser', label: 'Sphere' }, + { value: 'CircularEraser', label: i18n.t('Buttons:Circle') }, + { value: 'SphereEraser', label: i18n.t('Buttons:Sphere') }, ], commands: 'setToolActiveToolbar', }, ], + commands: { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Labelmap', + }, + }, }, }, { @@ -600,23 +950,40 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolBoxButton', props: { icon: 'icon-tool-threshold', - label: 'Threshold Tool', - evaluate: { - name: 'evaluate.cornerstone.segmentation', - toolNames: [ - 'ThresholdCircularBrush', - 'ThresholdSphereBrush', - 'ThresholdCircularBrushDynamic', - 'ThresholdSphereBrushDynamic', - ], + label: i18n.t('Buttons:Threshold Tool'), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + toolNames: [ + 'ThresholdCircularBrush', + 'ThresholdSphereBrush', + 'ThresholdCircularBrushDynamic', + 'ThresholdSphereBrushDynamic', + ], + }, + { + name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius', + radiusOptionId: 'threshold-radius', + }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Labelmap', + }, + ], + commands: { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Labelmap', + }, }, options: [ { - name: 'Radius (mm)', + name: i18n.t('Buttons:Radius (mm)'), id: 'threshold-radius', type: 'range', - min: 0.5, - max: 99.5, + explicitRunOnly: true, + min: MIN_SEGMENTATION_DRAWING_RADIUS, + max: MAX_SEGMENTATION_DRAWING_RADIUS, step: 0.5, value: 25, commands: { @@ -632,13 +999,13 @@ const toolbarButtons: Button[] = [ }, }, { - name: 'Shape', + name: i18n.t('Buttons:Shape'), type: 'radio', id: 'threshold-shape', value: 'ThresholdCircularBrush', values: [ - { value: 'ThresholdCircularBrush', label: 'Circle' }, - { value: 'ThresholdSphereBrush', label: 'Sphere' }, + { value: 'ThresholdCircularBrush', label: i18n.t('Buttons:Circle') }, + { value: 'ThresholdSphereBrush', label: i18n.t('Buttons:Sphere') }, ], commands: ({ value, commandsManager, options }) => { const optionsDynamic = options.find(option => option.id === 'dynamic-mode'); @@ -658,13 +1025,13 @@ const toolbarButtons: Button[] = [ }, }, { - name: 'Threshold', + name: i18n.t('Buttons:Threshold'), type: 'radio', id: 'dynamic-mode', value: 'ThresholdDynamic', values: [ - { value: 'ThresholdDynamic', label: 'Dynamic' }, - { value: 'ThresholdRange', label: 'Range' }, + { value: 'ThresholdDynamic', label: i18n.t('Buttons:Dynamic') }, + { value: 'ThresholdRange', label: i18n.t('Buttons:Range') }, ], commands: ({ value, commandsManager, options }) => { const thresholdRangeOption = options.find(option => option.id === 'threshold-shape'); @@ -717,22 +1084,34 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolBoxButton', props: { icon: 'icon-tool-shape', - label: 'Shapes', - evaluate: { - name: 'evaluate.cornerstone.segmentation', - toolNames: ['CircleScissor', 'SphereScissor', 'RectangleScissor'], - disabledText: 'Create new segmentation to enable shapes tool.', + label: i18n.t('Buttons:Shapes'), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + toolNames: ['CircleScissor', 'SphereScissor', 'RectangleScissor'], + disabledText: i18n.t('Buttons:Create new segmentation to enable shapes tool.'), + }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Labelmap', + }, + ], + commands: { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { + segmentationRepresentationType: 'Labelmap', + }, }, options: [ { - name: 'Shape', + name: i18n.t('Buttons:Shape'), type: 'radio', value: 'CircleScissor', id: 'shape-mode', values: [ - { value: 'CircleScissor', label: 'Circle' }, - { value: 'SphereScissor', label: 'Sphere' }, - { value: 'RectangleScissor', label: 'Rectangle' }, + { value: 'CircleScissor', label: i18n.t('Buttons:Circle') }, + { value: 'SphereScissor', label: i18n.t('Buttons:Sphere') }, + { value: 'RectangleScissor', label: i18n.t('Buttons:Rectangle') }, ], commands: 'setToolActiveToolbar', }, @@ -740,17 +1119,76 @@ const toolbarButtons: Button[] = [ }, }, { - id: 'SegmentLabelTool', + id: 'SimplifyContours', + uiType: 'ohif.toolButton', + props: { + icon: 'actions-simplify', + label: 'Simplify Contours', + tooltip: 'Simplify Contours', + commands: ['toggleActiveSegmentationUtility'], + evaluate: [ + { + name: 'cornerstone.isActiveSegmentationUtility', + }, + ], + options: 'cornerstone.SimplifyContourOptions', + }, + }, + { + id: 'SmoothContours', + uiType: 'ohif.toolButton', + props: { + icon: 'actions-smooth', + label: 'Smooth Contours', + tooltip: 'Smooth Contours', + commands: ['toggleActiveSegmentationUtility'], + evaluate: [ + { + name: 'cornerstone.isActiveSegmentationUtility', + }, + ], + options: 'cornerstone.SmoothContoursOptions', + }, + }, + { + id: 'LogicalContourOperations', + uiType: 'ohif.toolButton', + props: { + icon: 'actions-combine', + label: 'Combine Contours', + tooltip: 'Combine Contours', + commands: ['toggleActiveSegmentationUtility'], + evaluate: [ + { + name: 'cornerstone.isActiveSegmentationUtility', + }, + ], + options: 'cornerstone.LogicalContourOperationsOptions', + }, + }, + { + id: 'LabelMapEditWithContour', uiType: 'ohif.toolBoxButton', props: { - icon: 'tool-segment-label', - label: 'Segment Label Display', - tooltip: 'Click to show or hide segment labels when hovering with your mouse.', - commands: { commandName: 'toggleSegmentLabel' }, + icon: 'tool-labelmap-edit-with-contour', + label: i18n.t('Buttons:Labelmap Edit with Contour Tool'), + tooltip: i18n.t('Buttons:Labelmap Edit with Contour Tool'), + commands: [ + 'setToolActiveToolbar', + { + commandName: 'activateSelectedSegmentationOfType', + commandOptions: { segmentationRepresentationType: 'Labelmap' }, + }, + ], evaluate: [ - 'evaluate.cornerstoneTool.toggle', { - name: 'evaluate.cornerstone.hasSegmentation', + name: 'evaluate.cornerstone.segmentation', + toolNames: ['LabelMapEditWithContour'], + disabledText: 'Create new segmentation to enable this tool.', + }, + { + name: 'evaluate.cornerstone.hasSegmentationOfType', + segmentationRepresentationType: 'Labelmap', }, ], }, diff --git a/modes/segmentation/src/utils/setUpAutoTabSwitchHandler.ts b/modes/segmentation/src/utils/setUpAutoTabSwitchHandler.ts new file mode 100644 index 00000000000..a0594bace46 --- /dev/null +++ b/modes/segmentation/src/utils/setUpAutoTabSwitchHandler.ts @@ -0,0 +1,56 @@ +/** + * Sets up auto tab switching for when the first segmentation is added into the viewer. + */ +export default function setUpAutoTabSwitchHandler({ + segmentationService, + viewportGridService, + panelService, +}) { + const autoTabSwitchEvents = [ + segmentationService.EVENTS.SEGMENTATION_MODIFIED, + segmentationService.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED, + ]; + + // Initially there are no segmentations, so we should switch the tab whenever the first segmentation is added. + let shouldSwitchTab = true; + + const unsubscribeAutoTabSwitchEvents = autoTabSwitchEvents + .map(eventName => + segmentationService.subscribe(eventName, () => { + const segmentations = segmentationService.getSegmentations(); + + if (!segmentations.length) { + // If all the segmentations are removed, then the next time a segmentation is added, we should switch the tab. + shouldSwitchTab = true; + return; + } + + const activeViewportId = viewportGridService.getActiveViewportId(); + const activeRepresentation = segmentationService + .getSegmentationRepresentations(activeViewportId) + ?.find(representation => representation.active); + + if (activeRepresentation && shouldSwitchTab) { + shouldSwitchTab = false; + + switch (activeRepresentation.type) { + case 'Labelmap': + panelService.activatePanel( + '@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsLabelMap', + true + ); + break; + case 'Contour': + panelService.activatePanel( + '@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsContour', + true + ); + break; + } + } + }) + ) + .map(subscription => subscription.unsubscribe); + + return { unsubscribeAutoTabSwitchEvents }; +} diff --git a/modes/tmtv/CHANGELOG.md b/modes/tmtv/CHANGELOG.md index 825ddd5d761..4f8f25aee9d 100644 --- a/modes/tmtv/CHANGELOG.md +++ b/modes/tmtv/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/mode-tmtv @@ -11,7 +11,1096 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05) + + +### Bug Fixes + +* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece)) + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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 @ohif/mode-tmtv + + + + + +# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25) + + +### Bug Fixes + +* **i18n:** Add and update i18n Translation for Toolbar Tool Names ([#5392](https://github.com/OHIF/Viewers/issues/5392)) ([7783d0f](https://github.com/OHIF/Viewers/commit/7783d0f558bbb7016c29513b6fe76ed069dbd75c)) + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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 @ohif/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [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/mode-tmtv + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/mode-tmtv + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/mode-tmtv diff --git a/modes/tmtv/package.json b/modes/tmtv/package.json index 3b20ebb7a82..b79c97a5191 100644 --- a/modes/tmtv/package.json +++ b/modes/tmtv/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/mode-tmtv", - "version": "3.11.1", + "version": "3.12.0", "description": "Total Metabolic Tumor Volume Workflow", "author": "OHIF", "license": "MIT", @@ -34,13 +34,13 @@ "test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests" }, "peerDependencies": { - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-dicom-pdf": "3.11.1", - "@ohif/extension-dicom-video": "3.11.1", - "@ohif/extension-measurement-tracking": "3.11.1" + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone": "3.12.0", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0", + "@ohif/extension-default": "3.12.0", + "@ohif/extension-dicom-pdf": "3.12.0", + "@ohif/extension-dicom-video": "3.12.0", + "@ohif/extension-measurement-tracking": "3.12.0" }, "dependencies": { "@babel/runtime": "7.28.2", diff --git a/modes/tmtv/src/constants.ts b/modes/tmtv/src/constants.ts new file mode 100644 index 00000000000..c8afca7e4cf --- /dev/null +++ b/modes/tmtv/src/constants.ts @@ -0,0 +1,2 @@ +export const MIN_SEGMENTATION_DRAWING_RADIUS = 0.5; +export const MAX_SEGMENTATION_DRAWING_RADIUS = 99.5; diff --git a/modes/tmtv/src/initToolGroups.js b/modes/tmtv/src/initToolGroups.js index 4c959be00e6..f844839c364 100644 --- a/modes/tmtv/src/initToolGroups.js +++ b/modes/tmtv/src/initToolGroups.js @@ -1,3 +1,5 @@ +import { MIN_SEGMENTATION_DRAWING_RADIUS, MAX_SEGMENTATION_DRAWING_RADIUS } from './constants'; + export const toolGroupIds = { CT: 'ctToolGroup', PT: 'ptToolGroup', @@ -61,6 +63,8 @@ function _initToolGroups(toolNames, Enums, toolGroupService, commandsManager) { parentTool: 'Brush', configuration: { activeStrategy: 'FILL_INSIDE_CIRCLE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, { @@ -68,6 +72,8 @@ function _initToolGroups(toolNames, Enums, toolGroupService, commandsManager) { parentTool: 'Brush', configuration: { activeStrategy: 'ERASE_INSIDE_CIRCLE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, { @@ -75,6 +81,8 @@ function _initToolGroups(toolNames, Enums, toolGroupService, commandsManager) { parentTool: 'Brush', configuration: { activeStrategy: 'FILL_INSIDE_SPHERE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, { @@ -82,6 +90,8 @@ function _initToolGroups(toolNames, Enums, toolGroupService, commandsManager) { parentTool: 'Brush', configuration: { activeStrategy: 'ERASE_INSIDE_SPHERE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, { @@ -89,6 +99,8 @@ function _initToolGroups(toolNames, Enums, toolGroupService, commandsManager) { parentTool: 'Brush', configuration: { activeStrategy: 'THRESHOLD_INSIDE_CIRCLE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, { @@ -96,6 +108,8 @@ function _initToolGroups(toolNames, Enums, toolGroupService, commandsManager) { parentTool: 'Brush', configuration: { activeStrategy: 'THRESHOLD_INSIDE_SPHERE', + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, { @@ -110,6 +124,8 @@ function _initToolGroups(toolNames, Enums, toolGroupService, commandsManager) { isDynamic: true, dynamicRadius: 3, }, + minRadius: MIN_SEGMENTATION_DRAWING_RADIUS, + maxRadius: MAX_SEGMENTATION_DRAWING_RADIUS, }, }, ], diff --git a/modes/tmtv/src/toolbarButtons.ts b/modes/tmtv/src/toolbarButtons.ts index 56e41074c58..c5d8633e168 100644 --- a/modes/tmtv/src/toolbarButtons.ts +++ b/modes/tmtv/src/toolbarButtons.ts @@ -1,4 +1,7 @@ import { toolGroupIds } from './initToolGroups'; +import i18n from 'i18next'; + +import { MIN_SEGMENTATION_DRAWING_RADIUS, MAX_SEGMENTATION_DRAWING_RADIUS } from './constants'; const setToolActiveToolbar = { commandName: 'setToolActiveToolbar', @@ -41,8 +44,8 @@ const toolbarButtons = [ uiType: 'ohif.modalityLoadBadge', props: { icon: 'Status', - label: 'Status', - tooltip: 'Status', + label: i18n.t('Buttons:Status'), + tooltip: i18n.t('Buttons:Status'), evaluate: { name: 'evaluate.modalityLoadBadge', hideWhenDisabled: true, @@ -54,7 +57,7 @@ const toolbarButtons = [ uiType: 'ohif.colorbar', props: { type: 'tool', - label: 'Colorbar', + label: i18n.t('Buttons:Colorbar'), }, }, { @@ -62,8 +65,8 @@ const toolbarButtons = [ uiType: 'ohif.navigationComponent', props: { icon: 'Navigation', - label: 'Navigation', - tooltip: 'Navigate between segments/measurements and manage their visibility', + label: i18n.t('Buttons:Navigation'), + tooltip: i18n.t('Buttons:Navigate between segments/measurements and manage their visibility'), evaluate: { name: 'evaluate.navigationComponent', hideWhenDisabled: true, @@ -75,8 +78,8 @@ const toolbarButtons = [ uiType: 'ohif.windowLevelMenuEmbedded', props: { icon: 'WindowLevel', - label: 'Window Level', - tooltip: 'Adjust window/level presets and customize image contrast settings', + label: i18n.t('Buttons:Window Level'), + tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'), evaluate: { name: 'evaluate.windowLevelMenuEmbedded', hideWhenDisabled: true, @@ -88,8 +91,8 @@ const toolbarButtons = [ uiType: 'ohif.trackingStatus', props: { icon: 'TrackingStatus', - label: 'Tracking Status', - tooltip: 'View and manage tracking status of measurements and annotations', + label: i18n.t('Buttons:Tracking Status'), + tooltip: i18n.t('Buttons:View and manage tracking status of measurements and annotations'), evaluate: { name: 'evaluate.trackingStatus', hideWhenDisabled: true, @@ -101,8 +104,8 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-length', - label: 'Length', - tooltip: 'Length Tool', + label: i18n.t('Buttons:Length'), + tooltip: i18n.t('Buttons:Length Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -112,8 +115,8 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-bidirectional', - label: 'Bidirectional', - tooltip: 'Bidirectional Tool', + label: i18n.t('Buttons:Bidirectional'), + tooltip: i18n.t('Buttons:Bidirectional Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -123,8 +126,8 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-annotate', - label: 'Arrow Annotate', - tooltip: 'Arrow Annotate Tool', + label: i18n.t('Buttons:Arrow Annotate'), + tooltip: i18n.t('Buttons:Arrow Annotate Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -134,8 +137,8 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-ellipse', - label: 'Ellipse', - tooltip: 'Ellipse Tool', + label: i18n.t('Buttons:Ellipse'), + tooltip: i18n.t('Buttons:Ellipse Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -145,7 +148,7 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-zoom', - label: 'Zoom', + label: i18n.t('Buttons:Zoom'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -155,7 +158,7 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-window-level', - label: 'Window Level', + label: i18n.t('Buttons:Window Level'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -165,7 +168,7 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-crosshair', - label: 'Crosshairs', + label: i18n.t('Buttons:Crosshairs'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -175,7 +178,7 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'tool-move', - label: 'Pan', + label: i18n.t('Buttons:Pan'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -185,13 +188,13 @@ const toolbarButtons = [ uiType: 'ohif.toolBoxButton', props: { icon: 'tool-create-threshold', - label: 'Rectangle ROI Threshold', + label: i18n.t('Buttons:Rectangle ROI Threshold'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstone.segmentation', { name: 'evaluate.cornerstoneTool', - disabledText: 'Select the PT Axial to enable this tool', + disabledText: i18n.t('Buttons:Select the PT Axial to enable this tool'), }, ], options: 'tmtv.RectangleROIThresholdOptions', @@ -203,19 +206,26 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-brush', - label: 'Brush', - evaluate: { - name: 'evaluate.cornerstone.segmentation', - toolNames: ['CircularBrush', 'SphereBrush'], - disabledText: 'Create new segmentation to enable this tool.', - }, + label: i18n.t('Buttons:Brush'), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + toolNames: ['CircularBrush', 'SphereBrush'], + disabledText: i18n.t('Buttons:Create new segmentation to enable this tool.'), + }, + { + name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius', + radiusOptionId: 'brush-radius', + }, + ], options: [ { - name: 'Radius (mm)', + name: i18n.t('Buttons:Radius (mm)'), id: 'brush-radius', type: 'range', - min: 0.5, - max: 99.5, + explicitRunOnly: true, + min: MIN_SEGMENTATION_DRAWING_RADIUS, + max: MAX_SEGMENTATION_DRAWING_RADIUS, step: 0.5, value: 25, commands: { @@ -224,13 +234,13 @@ const toolbarButtons = [ }, }, { - name: 'Shape', + name: i18n.t('Buttons:Shape'), type: 'radio', id: 'brush-mode', value: 'CircularBrush', values: [ - { value: 'CircularBrush', label: 'Circle' }, - { value: 'SphereBrush', label: 'Sphere' }, + { value: 'CircularBrush', label: i18n.t('Buttons:Circle') }, + { value: 'SphereBrush', label: i18n.t('Buttons:Sphere') }, ], commands: 'setToolActiveToolbar', }, @@ -242,18 +252,25 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-eraser', - label: 'Eraser', - evaluate: { - name: 'evaluate.cornerstone.segmentation', - toolNames: ['CircularEraser', 'SphereEraser'], - }, + label: i18n.t('Buttons:Eraser'), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + toolNames: ['CircularEraser', 'SphereEraser'], + }, + { + name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius', + radiusOptionId: 'eraser-radius', + }, + ], options: [ { - name: 'Radius (mm)', + name: i18n.t('Buttons:Radius (mm)'), id: 'eraser-radius', type: 'range', - min: 0.5, - max: 99.5, + explicitRunOnly: true, + min: MIN_SEGMENTATION_DRAWING_RADIUS, + max: MAX_SEGMENTATION_DRAWING_RADIUS, step: 0.5, value: 25, commands: { @@ -262,13 +279,13 @@ const toolbarButtons = [ }, }, { - name: 'Shape', + name: i18n.t('Buttons:Shape'), type: 'radio', id: 'eraser-mode', value: 'CircularEraser', values: [ - { value: 'CircularEraser', label: 'Circle' }, - { value: 'SphereEraser', label: 'Sphere' }, + { value: 'CircularEraser', label: i18n.t('Buttons:Circle') }, + { value: 'SphereEraser', label: i18n.t('Buttons:Sphere') }, ], commands: 'setToolActiveToolbar', }, @@ -280,18 +297,25 @@ const toolbarButtons = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-threshold', - label: 'Threshold Tool', - evaluate: { - name: 'evaluate.cornerstone.segmentation', - toolNames: ['ThresholdCircularBrush', 'ThresholdSphereBrush'], - }, + label: i18n.t('Buttons:Threshold Tool'), + evaluate: [ + { + name: 'evaluate.cornerstone.segmentation', + toolNames: ['ThresholdCircularBrush', 'ThresholdSphereBrush'], + }, + { + name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius', + radiusOptionId: 'threshold-radius', + }, + ], options: [ { - name: 'Radius (mm)', + name: i18n.t('Buttons:Radius (mm)'), id: 'threshold-radius', type: 'range', - min: 0.5, - max: 99.5, + explicitRunOnly: true, + min: MIN_SEGMENTATION_DRAWING_RADIUS, + max: MAX_SEGMENTATION_DRAWING_RADIUS, step: 0.5, value: 25, commands: { @@ -306,13 +330,13 @@ const toolbarButtons = [ }, }, { - name: 'Threshold', + name: i18n.t('Buttons:Threshold'), type: 'radio', id: 'dynamic-mode', value: 'ThresholdRange', values: [ - { value: 'ThresholdDynamic', label: 'Dynamic' }, - { value: 'ThresholdRange', label: 'Range' }, + { value: 'ThresholdDynamic', label: i18n.t('Buttons:Dynamic') }, + { value: 'ThresholdRange', label: i18n.t('Buttons:Range') }, ], commands: ({ value, commandsManager }) => { if (value === 'ThresholdDynamic') { @@ -327,20 +351,20 @@ const toolbarButtons = [ }, }, { - name: 'Shape', + name: i18n.t('Buttons:Shape'), type: 'radio', id: 'eraser-mode', value: 'ThresholdCircularBrush', values: [ - { value: 'ThresholdCircularBrush', label: 'Circle' }, - { value: 'ThresholdSphereBrush', label: 'Sphere' }, + { value: 'ThresholdCircularBrush', label: i18n.t('Buttons:Circle') }, + { value: 'ThresholdSphereBrush', label: i18n.t('Buttons:Sphere') }, ], condition: ({ options }) => options.find(option => option.id === 'dynamic-mode').value === 'ThresholdRange', commands: 'setToolActiveToolbar', }, { - name: 'ThresholdRange', + name: i18n.t('ROIThresholdConfiguration:ThresholdRange'), type: 'double-range', id: 'threshold-range', min: 0, @@ -364,8 +388,10 @@ const toolbarButtons = [ uiType: 'ohif.dataOverlayMenu', props: { icon: 'ViewportViews', - label: 'Data Overlay', - tooltip: 'Configure data overlay options and manage foreground/background display sets', + label: i18n.t('Buttons:Data Overlay'), + tooltip: i18n.t( + 'Buttons:Configure data overlay options and manage foreground/background display sets' + ), evaluate: 'evaluate.dataOverlayMenu', }, }, @@ -374,8 +400,10 @@ const toolbarButtons = [ uiType: 'ohif.orientationMenu', props: { icon: 'OrientationSwitch', - label: 'Orientation', - tooltip: 'Change viewport orientation between axial, sagittal, coronal and reformat planes', + label: i18n.t('Buttons:Orientation'), + tooltip: i18n.t( + 'Buttons:Change viewport orientation between axial, sagittal, coronal and reformat planes' + ), evaluate: { name: 'evaluate.orientationMenu', // hideWhenDisabled: true, @@ -387,8 +415,8 @@ const toolbarButtons = [ uiType: 'ohif.windowLevelMenu', props: { icon: 'WindowLevel', - label: 'Window Level', - tooltip: 'Adjust window/level presets and customize image contrast settings', + label: i18n.t('Buttons:Window Level'), + tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'), evaluate: 'evaluate.windowLevelMenu', }, }, @@ -397,8 +425,8 @@ const toolbarButtons = [ uiType: 'ohif.voiManualControlMenu', props: { icon: 'WindowLevelAdvanced', - label: 'Advanced Window Level', - tooltip: 'Advanced window/level settings with manual controls and presets', + label: i18n.t('Buttons:Advanced Window Level'), + tooltip: i18n.t('Buttons:Advanced window/level settings with manual controls and presets'), evaluate: 'evaluate.voiManualControlMenu', }, }, @@ -407,8 +435,8 @@ const toolbarButtons = [ uiType: 'ohif.thresholdMenu', props: { icon: 'Threshold', - label: 'Threshold', - tooltip: 'Image threshold settings', + label: i18n.t('Buttons:Threshold'), + tooltip: i18n.t('Buttons:Image threshold settings'), evaluate: { name: 'evaluate.thresholdMenu', hideWhenDisabled: true, @@ -420,8 +448,8 @@ const toolbarButtons = [ uiType: 'ohif.opacityMenu', props: { icon: 'Opacity', - label: 'Opacity', - tooltip: 'Image opacity settings', + label: i18n.t('Buttons:Opacity'), + tooltip: i18n.t('Buttons:Image opacity settings'), evaluate: { name: 'evaluate.opacityMenu', hideWhenDisabled: true, diff --git a/modes/usAnnotation/CHANGELOG.md b/modes/usAnnotation/CHANGELOG.md index 90bf3e27d4d..20b2bee9e4b 100644 --- a/modes/usAnnotation/CHANGELOG.md +++ b/modes/usAnnotation/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/mode-ultrasound-pleura-bline @@ -11,7 +11,1144 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [3.12.0-beta.133](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.132...v3.12.0-beta.133) (2026-02-04) + + +### Bug Fixes + +* **jump-to-label-map:** Use `undefined` for viewportId for arrow navigation. ([#5774](https://github.com/OHIF/Viewers/issues/5774)) ([512aa3b](https://github.com/OHIF/Viewers/commit/512aa3b07f4c783040acd67e3fd2103559a06522)) + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.122](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.121...v3.12.0-beta.122) (2026-01-13) + + +### Bug Fixes + +* **segmentation:** List surface representations in the segmentation table for 3D views. ([#5700](https://github.com/OHIF/Viewers/issues/5700)) ([82e6a18](https://github.com/OHIF/Viewers/commit/82e6a18735b2e2278d6b95037d82bd3cb529104d)) + + + + + +# [3.12.0-beta.121](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.120...v3.12.0-beta.121) (2026-01-12) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix a segmentation exception after capture. ([#5701](https://github.com/OHIF/Viewers/issues/5701)) ([aba4855](https://github.com/OHIF/Viewers/commit/aba48552765d03cf82e6a92cdd443b246560c4fd)) + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.110...v3.12.0-beta.111) (2025-12-18) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix segment visibility issue. ([#5664](https://github.com/OHIF/Viewers/issues/5664)) ([3440f66](https://github.com/OHIF/Viewers/commit/3440f66ebd0900c0f5b1a5edbdf6f3b5fc72bbcf)) + + + + + +# [3.12.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.109...v3.12.0-beta.110) (2025-12-18) + + +### Bug Fixes + +* prevent annotation from appearing in active viewport when switching series with different Frame of Reference UID ([#5630](https://github.com/OHIF/Viewers/issues/5630)) ([658994c](https://github.com/OHIF/Viewers/commit/658994c854a03203054168dcfd383c0a6e320df0)) + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16) + + +### Bug Fixes + +* preclinical mode not working ([#5631](https://github.com/OHIF/Viewers/issues/5631)) ([2de81ef](https://github.com/OHIF/Viewers/commit/2de81efa7e6be534972cd763271c038b293d86f0)) + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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 @ohif/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19) + + +### Bug Fixes + +* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f)) + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11) + + +### Bug Fixes + +* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f)) + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05) + + +### Bug Fixes + +* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece)) + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28) + + +### Bug Fixes + +* **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](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190)) + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20) + + +### Bug Fixes + +* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404) + + + + + +# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18) + + +### Bug Fixes + +* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462)) + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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 @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25) + + +### Bug Fixes + +* **i18n:** Add and update i18n Translation for Toolbar Tool Names ([#5392](https://github.com/OHIF/Viewers/issues/5392)) ([7783d0f](https://github.com/OHIF/Viewers/commit/7783d0f558bbb7016c29513b6fe76ed069dbd75c)) + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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 @ohif/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [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/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + + +### Features + +* Add WSI bulk data annotations support (ANN) ([#4972](https://github.com/OHIF/Viewers/issues/4972)) ([2ed6818](https://github.com/OHIF/Viewers/commit/2ed681817142b13971bb17b3058be26bbab6b90b)) + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + + +### Bug Fixes + +* load 3dsr ([#5335](https://github.com/OHIF/Viewers/issues/5335)) ([becba78](https://github.com/OHIF/Viewers/commit/becba7861f99ae4e190caddea8b663e60a9883f5)) + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/mode-ultrasound-pleura-bline diff --git a/modes/usAnnotation/package.json b/modes/usAnnotation/package.json index 333fdbc741c..bb82254991d 100644 --- a/modes/usAnnotation/package.json +++ b/modes/usAnnotation/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/mode-ultrasound-pleura-bline", - "version": "3.11.1", + "version": "3.12.0", "description": "Allows users to annotate ultrasound images with pleura B-line annotations.", "author": "OHIF", "license": "MIT", @@ -34,11 +34,11 @@ }, "dependencies": { "@babel/runtime": "7.28.2", - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/tools": "4.5.13", - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-ultrasound-pleura-bline": "3.11.1", + "@cornerstonejs/core": "4.15.29", + "@cornerstonejs/tools": "4.15.29", + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0", + "@ohif/extension-ultrasound-pleura-bline": "3.12.0", "i18next": "17.3.1" }, "devDependencies": { diff --git a/modes/usAnnotation/src/initToolGroups.js b/modes/usAnnotation/src/initToolGroups.js index 5f1c9396d43..aebe679618f 100644 --- a/modes/usAnnotation/src/initToolGroups.js +++ b/modes/usAnnotation/src/initToolGroups.js @@ -88,9 +88,6 @@ function initDefaultToolGroup(extensionManager, toolGroupService, commandsManage enabled: [ { toolName: toolNames.ImageOverlayViewer }, { toolName: toolNames.ReferenceLines }, - { - toolName: SRToolNames.SRSCOORD3DPoint, - }, ], disabled: [ { diff --git a/modes/usAnnotation/src/toolbarButtons.ts b/modes/usAnnotation/src/toolbarButtons.ts index 5ab0cec2a78..c91382648ad 100644 --- a/modes/usAnnotation/src/toolbarButtons.ts +++ b/modes/usAnnotation/src/toolbarButtons.ts @@ -2,6 +2,7 @@ import type { Button } from '@ohif/core/types'; import { EVENTS } from '@cornerstonejs/core'; import { ViewportGridService } from '@ohif/core'; +import i18n from 'i18next'; const callbacks = (toolName: string) => [ { @@ -48,8 +49,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.modalityLoadBadge', props: { icon: 'Status', - label: 'Status', - tooltip: 'Status', + label: i18n.t('Buttons:Status'), + tooltip: i18n.t('Buttons:Status'), evaluate: { name: 'evaluate.modalityLoadBadge', hideWhenDisabled: true, @@ -61,8 +62,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.navigationComponent', props: { icon: 'Navigation', - label: 'Navigation', - tooltip: 'Navigate between segments/measurements and manage their visibility', + label: i18n.t('Buttons:Navigation'), + tooltip: i18n.t('Buttons:Navigate between segments/measurements and manage their visibility'), evaluate: { name: 'evaluate.navigationComponent', hideWhenDisabled: true, @@ -74,8 +75,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.trackingStatus', props: { icon: 'TrackingStatus', - label: 'Tracking Status', - tooltip: 'View and manage tracking status of measurements and annotations', + label: i18n.t('Buttons:Tracking Status'), + tooltip: i18n.t('Buttons:View and manage tracking status of measurements and annotations'), evaluate: { name: 'evaluate.trackingStatus', hideWhenDisabled: true, @@ -87,8 +88,10 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.dataOverlayMenu', props: { icon: 'ViewportViews', - label: 'Data Overlay', - tooltip: 'Configure data overlay options and manage foreground/background display sets', + label: i18n.t('Buttons:Data Overlay'), + tooltip: i18n.t( + 'Buttons:Configure data overlay options and manage foreground/background display sets' + ), evaluate: 'evaluate.dataOverlayMenu', }, }, @@ -97,8 +100,10 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.orientationMenu', props: { icon: 'OrientationSwitch', - label: 'Orientation', - tooltip: 'Change viewport orientation between axial, sagittal, coronal and reformat planes', + label: i18n.t('Buttons:Orientation'), + tooltip: i18n.t( + 'Buttons:Change viewport orientation between axial, sagittal, coronal and reformat planes' + ), evaluate: { name: 'evaluate.orientationMenu', // hideWhenDisabled: true, @@ -110,8 +115,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.windowLevelMenuEmbedded', props: { icon: 'WindowLevel', - label: 'Window Level', - tooltip: 'Adjust window/level presets and customize image contrast settings', + label: i18n.t('Buttons:Window Level'), + tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'), evaluate: { name: 'evaluate.windowLevelMenuEmbedded', hideWhenDisabled: true, @@ -123,8 +128,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.windowLevelMenu', props: { icon: 'WindowLevel', - label: 'Window Level', - tooltip: 'Adjust window/level presets and customize image contrast settings', + label: i18n.t('Buttons:Window Level'), + tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'), evaluate: { name: 'evaluate.windowLevelMenu', }, @@ -135,8 +140,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.voiManualControlMenu', props: { icon: 'WindowLevelAdvanced', - label: 'Advanced Window Level', - tooltip: 'Advanced window/level settings with manual controls and presets', + label: i18n.t('Buttons:Advanced Window Level'), + tooltip: i18n.t('Buttons:Advanced window/level settings with manual controls and presets'), evaluate: 'evaluate.voiManualControlMenu', }, }, @@ -145,8 +150,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.thresholdMenu', props: { icon: 'Threshold', - label: 'Threshold', - tooltip: 'Image threshold settings', + label: i18n.t('Buttons:Threshold'), + tooltip: i18n.t('Buttons:Image threshold settings'), evaluate: { name: 'evaluate.thresholdMenu', hideWhenDisabled: true, @@ -158,8 +163,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.opacityMenu', props: { icon: 'Opacity', - label: 'Opacity', - tooltip: 'Image opacity settings', + label: i18n.t('Buttons:Opacity'), + tooltip: i18n.t('Buttons:Image opacity settings'), evaluate: { name: 'evaluate.opacityMenu', hideWhenDisabled: true, @@ -171,7 +176,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.colorbar', props: { type: 'tool', - label: 'Colorbar', + label: i18n.t('Buttons:Colorbar'), }, }, { @@ -179,8 +184,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-reset', - label: 'Reset View', - tooltip: 'Reset View', + label: i18n.t('Buttons:Reset View'), + tooltip: i18n.t('Buttons:Reset View'), commands: 'resetViewport', evaluate: 'evaluate.action', }, @@ -190,8 +195,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-rotate-right', - label: 'Rotate Right', - tooltip: 'Rotate +90', + label: i18n.t('Buttons:Rotate Right'), + tooltip: i18n.t('Buttons:Rotate +90'), commands: 'rotateViewportCW', evaluate: [ 'evaluate.action', @@ -207,8 +212,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-flip-horizontal', - label: 'Flip Horizontal', - tooltip: 'Flip Horizontally', + label: i18n.t('Buttons:Flip Horizontal'), + tooltip: i18n.t('Buttons:Flip Horizontally'), commands: 'flipViewportHorizontal', evaluate: [ 'evaluate.viewportProperties.toggle', @@ -224,8 +229,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'link', - label: 'Image Slice Sync', - tooltip: 'Enable position synchronization on stack viewports', + label: i18n.t('Buttons:Image Slice Sync'), + tooltip: i18n.t('Buttons:Enable position synchronization on stack viewports'), commands: { commandName: 'toggleSynchronizer', commandOptions: { @@ -252,8 +257,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-referenceLines', - label: 'Reference Lines', - tooltip: 'Show Reference Lines', + label: i18n.t('Buttons:Reference Lines'), + tooltip: i18n.t('Buttons:Show Reference Lines'), commands: 'toggleEnabledDisabledToolbar', listeners: { [ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: callbacks('ReferenceLines'), @@ -273,8 +278,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'toggle-dicom-overlay', - label: 'Image Overlay', - tooltip: 'Toggle Image Overlay', + label: i18n.t('Buttons:Image Overlay'), + tooltip: i18n.t('Buttons:Toggle Image Overlay'), commands: 'toggleEnabledDisabledToolbar', evaluate: [ 'evaluate.cornerstoneTool.toggle', @@ -290,8 +295,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-stack-scroll', - label: 'Stack Scroll', - tooltip: 'Stack Scroll', + label: i18n.t('Buttons:Stack Scroll'), + tooltip: i18n.t('Buttons:Stack Scroll'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -301,8 +306,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-invert', - label: 'Invert', - tooltip: 'Invert Colors', + label: i18n.t('Buttons:Invert'), + tooltip: i18n.t('Buttons:Invert Colors'), commands: 'invertViewport', evaluate: [ 'evaluate.viewportProperties.toggle', @@ -318,8 +323,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-probe', - label: 'Probe', - tooltip: 'Probe', + label: i18n.t('Buttons:Probe'), + tooltip: i18n.t('Buttons:Probe'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -329,8 +334,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-cine', - label: 'Cine', - tooltip: 'Cine', + label: i18n.t('Buttons:Cine'), + tooltip: i18n.t('Buttons:Cine'), commands: 'toggleCine', evaluate: [ 'evaluate.cine', @@ -346,8 +351,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-angle', - label: 'Angle', - tooltip: 'Angle', + label: i18n.t('Buttons:Angle'), + tooltip: i18n.t('Buttons:Angle'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -357,8 +362,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-cobb-angle', - label: 'Cobb Angle', - tooltip: 'Cobb Angle', + label: i18n.t('Buttons:Cobb Angle'), + tooltip: i18n.t('Buttons:Cobb Angle'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -368,8 +373,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-magnify', - label: 'Zoom-in', - tooltip: 'Zoom-in', + label: i18n.t('Buttons:Zoom-in'), + tooltip: i18n.t('Buttons:Zoom-in'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstoneTool', @@ -385,8 +390,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-calibration', - label: 'Calibration', - tooltip: 'Calibration Line', + label: i18n.t('Buttons:Calibration'), + tooltip: i18n.t('Buttons:Calibration Line'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstoneTool', @@ -402,8 +407,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'dicom-tag-browser', - label: 'Dicom Tag Browser', - tooltip: 'Dicom Tag Browser', + label: i18n.t('Buttons:Dicom Tag Browser'), + tooltip: i18n.t('Buttons:Dicom Tag Browser'), commands: 'openDICOMTagViewer', }, }, @@ -412,8 +417,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-loupe', - label: 'Magnify Probe', - tooltip: 'Magnify Probe', + label: i18n.t('Buttons:Magnify Probe'), + tooltip: i18n.t('Buttons:Magnify Probe'), commands: 'toggleActiveDisabledToolbar', evaluate: [ 'evaluate.cornerstoneTool.toggle.ifStrictlyDisabled', @@ -429,8 +434,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-ultrasound-bidirectional', - label: 'Ultrasound Directional', - tooltip: 'Ultrasound Directional', + label: i18n.t('Buttons:Ultrasound Directional'), + tooltip: i18n.t('Buttons:Ultrasound Directional'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstoneTool', @@ -446,8 +451,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-ultrasound-bidirectional', - label: 'US Pleura B-line Annotation', - tooltip: 'US Pleura B-line Annotation', + label: i18n.t('Buttons:US Pleura B-line Annotation'), + tooltip: i18n.t('Buttons:US Pleura B-line Annotation'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstoneTool', @@ -463,8 +468,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-window-region', - label: 'Window Level Region', - tooltip: 'Window Level Region', + label: i18n.t('Buttons:Window Level Region'), + tooltip: i18n.t('Buttons:Window Level Region'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstoneTool', @@ -480,8 +485,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-length', - label: 'Length', - tooltip: 'Length Tool', + label: i18n.t('Buttons:Length'), + tooltip: i18n.t('Buttons:Length Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -491,8 +496,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-bidirectional', - label: 'Bidirectional', - tooltip: 'Bidirectional Tool', + label: i18n.t('Buttons:Bidirectional'), + tooltip: i18n.t('Buttons:Bidirectional Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -502,8 +507,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-annotate', - label: 'Annotation', - tooltip: 'Arrow Annotate', + label: i18n.t('Buttons:Annotation'), + tooltip: i18n.t('Buttons:Arrow Annotate'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -513,8 +518,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-ellipse', - label: 'Ellipse', - tooltip: 'Ellipse ROI', + label: i18n.t('Buttons:Ellipse'), + tooltip: i18n.t('Buttons:Ellipse ROI'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -524,8 +529,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-rectangle', - label: 'Rectangle', - tooltip: 'Rectangle ROI', + label: i18n.t('Buttons:Rectangle'), + tooltip: i18n.t('Buttons:Rectangle ROI'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -535,8 +540,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-circle', - label: 'Circle', - tooltip: 'Circle Tool', + label: i18n.t('Buttons:Circle'), + tooltip: i18n.t('Buttons:Circle Tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -546,8 +551,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-freehand-roi', - label: 'Freehand ROI', - tooltip: 'Freehand ROI', + label: i18n.t('Buttons:Freehand ROI'), + tooltip: i18n.t('Buttons:Freehand ROI'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -557,8 +562,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-spline-roi', - label: 'Spline ROI', - tooltip: 'Spline ROI', + label: i18n.t('Buttons:Spline ROI'), + tooltip: i18n.t('Buttons:Spline ROI'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -568,8 +573,8 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'icon-tool-livewire', - label: 'Livewire tool', - tooltip: 'Livewire tool', + label: i18n.t('Buttons:Livewire tool'), + tooltip: i18n.t('Buttons:Livewire tool'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -580,7 +585,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-window-level', - label: 'Window Level', + label: i18n.t('Buttons:Window Level'), commands: setToolActiveToolbar, evaluate: [ 'evaluate.cornerstoneTool', @@ -597,7 +602,7 @@ const toolbarButtons: Button[] = [ props: { type: 'tool', icon: 'tool-move', - label: 'Pan', + label: i18n.t('Buttons:Pan'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -608,7 +613,7 @@ const toolbarButtons: Button[] = [ props: { type: 'tool', icon: 'tool-zoom', - label: 'Zoom', + label: i18n.t('Buttons:Zoom'), commands: setToolActiveToolbar, evaluate: 'evaluate.cornerstoneTool', }, @@ -619,11 +624,11 @@ const toolbarButtons: Button[] = [ props: { type: 'tool', icon: 'tool-3d-rotate', - label: '3D Rotate', + label: i18n.t('Buttons:3D Rotate'), commands: setToolActiveToolbar, evaluate: { name: 'evaluate.cornerstoneTool', - disabledText: 'Select a 3D viewport to enable this tool', + disabledText: i18n.t('Buttons:Select a 3D viewport to enable this tool'), }, }, }, @@ -632,7 +637,7 @@ const toolbarButtons: Button[] = [ uiType: 'ohif.toolButton', props: { icon: 'tool-capture', - label: 'Capture', + label: i18n.t('Buttons:Capture'), commands: 'showDownloadViewportModal', evaluate: [ 'evaluate.action', @@ -658,7 +663,7 @@ const toolbarButtons: Button[] = [ props: { type: 'tool', icon: 'tool-crosshair', - label: 'Crosshairs', + label: i18n.t('Buttons:Crosshairs'), commands: { commandName: 'setToolActiveToolbar', commandOptions: { @@ -667,7 +672,7 @@ const toolbarButtons: Button[] = [ }, evaluate: { name: 'evaluate.cornerstoneTool', - disabledText: 'Select an MPR viewport to enable this tool', + disabledText: i18n.t('Buttons:Select an MPR viewport to enable this tool'), }, }, }, diff --git a/package.json b/package.json index a5abc6accff..9adb4ab0e34 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "cli": "node ./platform/cli/src/index.js", "build:ui:deploy-preview": "lerna run build:ui:deploy-preview --stream", "build:demo": "lerna run build:viewer:demo --stream", - "build:package-all": "lerna run build:package --parallel --stream", - "build:package-all-1": "lerna run build:package-1 --parallel --stream", + "build:package-all": "lerna run build:package --stream --concurrency 4", + "build:package-all-1": "lerna run build:package-1 --stream --concurrency 4", "dev:fast": "cd platform/app && yarn run dev:fast", "show:config": "echo Config is $APP_CONFIG on $PUBLIC_URL", "dev": "lerna run dev:viewer --stream", @@ -45,7 +45,11 @@ "dev:dcm4chee": "lerna run dev:dcm4chee --stream", "dev:static": "lerna run dev:static --stream", "orthanc:up": "docker compose -f platform/app/.recipes/Nginx-Orthanc/docker-compose.yml up", - "install:dev": "cp -f yarn.lock addOns/yarn.lock && cd addOns && yarn install --modules-folder ../node_modules", + "install:dev": "cp -f yarn.lock addOns/yarn.lock && cd addOns && yarn install --frozen-lockfile --modules-folder ../node_modules", + "install:update-lockfile": "yarn install && bun install --config=./bunfig.update-lockfile.toml", + "install:yarn:frozen-lockfile": "echo Deprecated - use install:frozen && yarn install --frozen-lockfile", + "install:frozen": "yarn install --frozen-lockfile", + "audit": "bun audit --ignore=GHSA-5j98-mcp5-4vw2", "preinstall": "node preinstall.js", "start": "yarn run dev", "test": "yarn run test:unit", @@ -135,7 +139,7 @@ "commander": "8.3.0", "cross-env": "7.0.3", "cross-spawn": "7.0.6", - "dcmjs": "0.43.1", + "dcmjs": "0.49.4", "path-to-regexp": "0.1.12", "nth-check": "2.1.1", "trim-newlines": "5.0.0", @@ -144,11 +148,13 @@ "package-json": "8.1.1", "rollup": "2.79.2", "body-parser": "1.20.3", - "form-data": ">=4.0.4", "axios": "1.12.0", "core-js": "3.45.1", "@babel/runtime-corejs2": "7.26.10", - "tapable": "2.2.2" + "tapable": "2.2.2", + "@cornerstonejs/codec-openjpeg": "1.3.0", + "node-forge": "1.3.2", + "qs": "6.14.1" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/platform/app/.recipes/Nginx-Dcm4chee-Keycloak/dockerfile b/platform/app/.recipes/Nginx-Dcm4chee-Keycloak/dockerfile index 4182de82bf3..b37eac632b3 100644 --- a/platform/app/.recipes/Nginx-Dcm4chee-Keycloak/dockerfile +++ b/platform/app/.recipes/Nginx-Dcm4chee-Keycloak/dockerfile @@ -13,7 +13,7 @@ COPY ./ /usr/src/app/ # Install node dependencies RUN yarn config set workspaces-experimental true -RUN yarn install +RUN yarn install --frozen-lockfile # Set the environment for the build ENV APP_CONFIG=config/docker-nginx-dcm4chee-keycloak.js diff --git a/platform/app/.recipes/Nginx-Dcm4chee/dockerfile b/platform/app/.recipes/Nginx-Dcm4chee/dockerfile index bbc6f89eb34..1edd75c42f6 100644 --- a/platform/app/.recipes/Nginx-Dcm4chee/dockerfile +++ b/platform/app/.recipes/Nginx-Dcm4chee/dockerfile @@ -16,7 +16,7 @@ COPY ./ /usr/src/app/ # Install node dependencies RUN yarn config set workspaces-experimental true -RUN yarn install +RUN yarn install --frozen-lockfile # Copy the rest of the application code diff --git a/platform/app/.recipes/Nginx-Orthanc-Keycloak/dockerfile b/platform/app/.recipes/Nginx-Orthanc-Keycloak/dockerfile index 7e86135298f..f4cadb9558d 100644 --- a/platform/app/.recipes/Nginx-Orthanc-Keycloak/dockerfile +++ b/platform/app/.recipes/Nginx-Orthanc-Keycloak/dockerfile @@ -19,7 +19,7 @@ COPY ./ /usr/src/app/ # Install node dependencies RUN yarn config set workspaces-experimental true -RUN yarn install +RUN yarn install --frozen-lockfile # Copy the rest of the application code diff --git a/platform/app/.recipes/Nginx-Orthanc/dockerfile b/platform/app/.recipes/Nginx-Orthanc/dockerfile index 846c74c313e..9408701c599 100644 --- a/platform/app/.recipes/Nginx-Orthanc/dockerfile +++ b/platform/app/.recipes/Nginx-Orthanc/dockerfile @@ -16,7 +16,7 @@ COPY ./ /usr/src/app/ # Install node dependencies RUN yarn config set workspaces-experimental true -RUN yarn install +RUN yarn install --frozen-lockfile # Copy the rest of the application code diff --git a/platform/app/.webpack/webpack.pwa.js b/platform/app/.webpack/webpack.pwa.js index 0a7a179ead1..2bbfbc916f3 100644 --- a/platform/app/.webpack/webpack.pwa.js +++ b/platform/app/.webpack/webpack.pwa.js @@ -31,6 +31,7 @@ const ENTRY_TARGET = process.env.ENTRY_TARGET || `${SRC_DIR}/index.js`; const Dotenv = require('dotenv-webpack'); const writePluginImportFile = require('./writePluginImportsFile.js'); // const MillionLint = require('@million/lint'); +const open = process.env.OHIF_OPEN !== 'false'; const copyPluginFromExtensions = writePluginImportFile(SRC_DIR, DIST_DIR); @@ -116,14 +117,6 @@ module.exports = (env, argv) => { from: `${PUBLIC_DIR}/${APP_CONFIG}`, to: `${DIST_DIR}/app-config.js`, }, - // Copy Dicom Microscopy Viewer build files - { - from: '../../../node_modules/dicom-microscopy-viewer/dist/dynamic-import', - to: DIST_DIR, - globOptions: { - ignore: ['**/*.min.js.map'], - }, - }, ], }), // Generate "index.html" w/ correct includes/imports @@ -155,7 +148,7 @@ module.exports = (env, argv) => { // compress: true, // http2: true, // https: true, - open: true, + open, port: OHIF_PORT, client: { overlay: { errors: true, warnings: false }, @@ -163,12 +156,6 @@ module.exports = (env, argv) => { proxy: [ { '/dicomweb': 'http://localhost:5000', - '/dicom-microscopy-viewer': { - target: 'http://localhost:3000', - pathRewrite: { - '^/dicom-microscopy-viewer': `/${PUBLIC_URL}/dicom-microscopy-viewer`, - }, - }, }, ], static: [ @@ -197,15 +184,16 @@ module.exports = (env, argv) => { if (hasProxy) { mergedConfig.devServer.proxy = mergedConfig.devServer.proxy || {}; - mergedConfig.devServer.proxy = { - [PROXY_TARGET]: { + mergedConfig.devServer.proxy = [ + { + context: [PROXY_PATH_REWRITE_FROM || '/dicomweb'], target: PROXY_DOMAIN, changeOrigin: true, pathRewrite: { [`^${PROXY_PATH_REWRITE_FROM}`]: PROXY_PATH_REWRITE_TO, }, }, - }; + ]; } if (isProdBuild) { diff --git a/platform/app/.webpack/writePluginImportsFile.js b/platform/app/.webpack/writePluginImportsFile.js index ad02e63f01a..ba00dacf6d6 100644 --- a/platform/app/.webpack/writePluginImportsFile.js +++ b/platform/app/.webpack/writePluginImportsFile.js @@ -123,7 +123,7 @@ const createCopyPluginToDistForLink = (srcDir, distDir, plugins, folderName) => return exists ? { from, - to: distDir, + to: `${distDir}${plugin.to || ''}`, toType: 'dir', } : undefined; diff --git a/platform/app/CHANGELOG.md b/platform/app/CHANGELOG.md index 4346594403c..4c20cf95a1c 100644 --- a/platform/app/CHANGELOG.md +++ b/platform/app/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/app @@ -11,7 +11,1177 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/app + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [3.12.0-beta.133](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.132...v3.12.0-beta.133) (2026-02-04) + + +### Bug Fixes + +* **jump-to-label-map:** Use `undefined` for viewportId for arrow navigation. ([#5774](https://github.com/OHIF/Viewers/issues/5774)) ([512aa3b](https://github.com/OHIF/Viewers/commit/512aa3b07f4c783040acd67e3fd2103559a06522)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.122](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.121...v3.12.0-beta.122) (2026-01-13) + + +### Bug Fixes + +* **segmentation:** List surface representations in the segmentation table for 3D views. ([#5700](https://github.com/OHIF/Viewers/issues/5700)) ([82e6a18](https://github.com/OHIF/Viewers/commit/82e6a18735b2e2278d6b95037d82bd3cb529104d)) + + + + + +# [3.12.0-beta.121](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.120...v3.12.0-beta.121) (2026-01-12) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix a segmentation exception after capture. ([#5701](https://github.com/OHIF/Viewers/issues/5701)) ([aba4855](https://github.com/OHIF/Viewers/commit/aba48552765d03cf82e6a92cdd443b246560c4fd)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.110...v3.12.0-beta.111) (2025-12-18) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix segment visibility issue. ([#5664](https://github.com/OHIF/Viewers/issues/5664)) ([3440f66](https://github.com/OHIF/Viewers/commit/3440f66ebd0900c0f5b1a5edbdf6f3b5fc72bbcf)) + + + + + +# [3.12.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.109...v3.12.0-beta.110) (2025-12-18) + + +### Bug Fixes + +* prevent annotation from appearing in active viewport when switching series with different Frame of Reference UID ([#5630](https://github.com/OHIF/Viewers/issues/5630)) ([658994c](https://github.com/OHIF/Viewers/commit/658994c854a03203054168dcfd383c0a6e320df0)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16) + + +### Bug Fixes + +* preclinical mode not working ([#5631](https://github.com/OHIF/Viewers/issues/5631)) ([2de81ef](https://github.com/OHIF/Viewers/commit/2de81efa7e6be534972cd763271c038b293d86f0)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.101...v3.12.0-beta.102) (2025-12-10) + + +### Bug Fixes + +* **routes:** display 404 feedback page for unmatched URLs ([#5627](https://github.com/OHIF/Viewers/issues/5627)) ([eef755e](https://github.com/OHIF/Viewers/commit/eef755ef05e38d5271b1c79e4dd191d723895470)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.98...v3.12.0-beta.99) (2025-12-04) + + +### Bug Fixes + +* Initial sort not consistent ([#5224](https://github.com/OHIF/Viewers/issues/5224)) ([77f9f8e](https://github.com/OHIF/Viewers/commit/77f9f8e1c4af6b5d22ecf9332b5edf503cd5b848)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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 @ohif/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21) + + +### Bug Fixes + +* **DicomUpload:** Switch to use ui-next components and colors ([#5576](https://github.com/OHIF/Viewers/issues/5576)) ([62d6bf4](https://github.com/OHIF/Viewers/commit/62d6bf437b167ac3e60a0f134869c9f96d199d0f)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19) + + +### Bug Fixes + +* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f)) + + + + + +# [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/app + + + + + +# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11) + + +### Bug Fixes + +* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f)) + + + + + +# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10) + + +### Bug Fixes + +* **dev:** update webpack proxy config syntax for Orthanc dev mode ([#5531](https://github.com/OHIF/Viewers/issues/5531)) ([58aa0d1](https://github.com/OHIF/Viewers/commit/58aa0d1eaae2e1a8103e6096864a4e5d7baf090e)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05) + + +### Bug Fixes + +* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28) + + +### Bug Fixes + +* **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](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190)) + + + + + +# [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/app + + + + + +# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24) + + +### Bug Fixes + +* **security:** For bun, enforce frozen lockfile. For yarn, strongly suggest using frozen lockfile. ([#5508](https://github.com/OHIF/Viewers/issues/5508)) ([1009c60](https://github.com/OHIF/Viewers/commit/1009c6091107d2db0768622120f916208a391343)) + + + + + +# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23) + + +### Features + +* **jpeg2000:** Add 16-bit RGB support to JPEG2000 decoder ([#5519](https://github.com/OHIF/Viewers/issues/5519)) ([a154443](https://github.com/OHIF/Viewers/commit/a1544432db42bf77864bb7df2f757cae819a4b4d)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20) + + +### Bug Fixes + +* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404) + + + + + +# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18) + + +### Bug Fixes + +* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462)) + + + + + +# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17) + + +### Bug Fixes + +* **ErrorBoundary:** Allow for details to be shown in production. ([#5504](https://github.com/OHIF/Viewers/issues/5504)) ([4620cc3](https://github.com/OHIF/Viewers/commit/4620cc3acf89f218c3feaaf4c153a3dc8b023b23)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.45](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.44...v3.12.0-beta.45) (2025-10-06) + + +### Bug Fixes + +* **router:** opt in to React Router v7 future flags ([#5461](https://github.com/OHIF/Viewers/issues/5461)) ([e2964bf](https://github.com/OHIF/Viewers/commit/e2964bf66947d18af06f292d9395b465545fdf9d)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.39](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.38...v3.12.0-beta.39) (2025-10-01) + + +### Features + +* Add inherit from basic/longitudinal modes ([#5435](https://github.com/OHIF/Viewers/issues/5435)) ([9ad0d7f](https://github.com/OHIF/Viewers/commit/9ad0d7fc8cf34867fa2cfc2bf2b2f63451ec03e5)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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 @ohif/app + + + + + +# [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 @ohif/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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 @ohif/app + + + + + +# [3.12.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.21...v3.12.0-beta.22) (2025-08-29) + + +### Bug Fixes + +* **oidc:** resolve navigation throttling and blank screen during sign-in/sign-out with Keycloak + dcm4chee + Traefik ([#5373](https://github.com/OHIF/Viewers/issues/5373)) ([79473c2](https://github.com/OHIF/Viewers/commit/79473c2ea4196b758614480860a8c596730b4147)) + + + + + +# [3.12.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.20...v3.12.0-beta.21) (2025-08-29) + + +### Bug Fixes + +* routerBasename declaration location ([#5347](https://github.com/OHIF/Viewers/issues/5347)) ([0f6f2bb](https://github.com/OHIF/Viewers/commit/0f6f2bb432d7021d3a92b38b58de51a380d9b0c9)) + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [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/app + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + + +### Bug Fixes + +* docker deploy ([#5359](https://github.com/OHIF/Viewers/issues/5359)) ([368a176](https://github.com/OHIF/Viewers/commit/368a176c093aa2ccf4197c46543209d5ff69939a)) + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + + +### Features + +* Add WSI bulk data annotations support (ANN) ([#4972](https://github.com/OHIF/Viewers/issues/4972)) ([2ed6818](https://github.com/OHIF/Viewers/commit/2ed681817142b13971bb17b3058be26bbab6b90b)) + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/app + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/app diff --git a/platform/app/README.md b/platform/app/README.md index f1e1dd135d7..9292d49534c 100644 --- a/platform/app/README.md +++ b/platform/app/README.md @@ -80,7 +80,7 @@ In your cloned repository's root folder, run: ```js // Restore dependencies -yarn install +yarn install --frozen-lockfile // Stands up local server to host Viewer. // Viewer connects to our public cloud PACS by default diff --git a/platform/app/package.json b/platform/app/package.json index 32d8a85dc5f..ccee24a264c 100644 --- a/platform/app/package.json +++ b/platform/app/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/app", - "version": "3.11.1", + "version": "3.12.0", "productVersion": "3.4.0", "description": "OHIF Viewer", "author": "OHIF Contributors", @@ -54,35 +54,35 @@ "@babel/runtime": "7.28.2", "@cornerstonejs/codec-charls": "1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2", - "@cornerstonejs/codec-openjpeg": "1.2.4", + "@cornerstonejs/codec-openjpeg": "1.3.0", "@cornerstonejs/codec-openjph": "2.4.7", - "@cornerstonejs/dicom-image-loader": "4.5.13", + "@cornerstonejs/dicom-image-loader": "4.15.29", "@emotion/serialize": "1.3.3", - "@ohif/core": "3.11.1", - "@ohif/extension-cornerstone": "3.11.1", - "@ohif/extension-cornerstone-dicom-rt": "3.11.1", - "@ohif/extension-cornerstone-dicom-seg": "3.11.1", - "@ohif/extension-cornerstone-dicom-sr": "3.11.1", - "@ohif/extension-default": "3.11.1", - "@ohif/extension-dicom-microscopy": "3.11.1", - "@ohif/extension-dicom-pdf": "3.11.1", - "@ohif/extension-dicom-video": "3.11.1", - "@ohif/extension-test": "3.11.1", - "@ohif/extension-ultrasound-pleura-bline": "3.11.1", - "@ohif/i18n": "3.11.1", - "@ohif/mode-basic-dev-mode": "3.11.1", - "@ohif/mode-longitudinal": "3.11.1", - "@ohif/mode-microscopy": "3.11.1", - "@ohif/mode-test": "3.11.1", - "@ohif/mode-ultrasound-pleura-bline": "3.11.1", - "@ohif/ui": "3.11.1", - "@ohif/ui-next": "3.11.1", + "@ohif/core": "3.12.0", + "@ohif/extension-cornerstone": "3.12.0", + "@ohif/extension-cornerstone-dicom-rt": "3.12.0", + "@ohif/extension-cornerstone-dicom-seg": "3.12.0", + "@ohif/extension-cornerstone-dicom-sr": "3.12.0", + "@ohif/extension-default": "3.12.0", + "@ohif/extension-dicom-microscopy": "3.12.0", + "@ohif/extension-dicom-pdf": "3.12.0", + "@ohif/extension-dicom-video": "3.12.0", + "@ohif/extension-test": "3.12.0", + "@ohif/extension-ultrasound-pleura-bline": "3.12.0", + "@ohif/i18n": "3.12.0", + "@ohif/mode-basic-dev-mode": "3.12.0", + "@ohif/mode-longitudinal": "3.12.0", + "@ohif/mode-microscopy": "3.12.0", + "@ohif/mode-test": "3.12.0", + "@ohif/mode-ultrasound-pleura-bline": "3.12.0", + "@ohif/ui": "3.12.0", + "@ohif/ui-next": "3.12.0", "@svgr/webpack": "8.1.0", "@types/react": "18.3.23", "classnames": "2.5.1", "core-js": "3.45.1", "cornerstone-math": "0.1.10", - "dcmjs": "0.43.1", + "dcmjs": "0.49.4", "detect-gpu": "4.0.50", "dicom-parser": "1.8.21", "dotenv-webpack": "1.8.0", @@ -101,8 +101,8 @@ "react-dropzone": "10.2.2", "react-i18next": "12.3.1", "react-resize-detector": "10.0.1", - "react-router": "6.30.1", - "react-router-dom": "6.30.1", + "react-router": "6.30.3", + "react-router-dom": "6.30.3", "react-shepherd": "6.1.1", "shepherd.js": "13.0.3", "url-loader": "4.1.1", diff --git a/platform/app/pluginConfig.json b/platform/app/pluginConfig.json index 24a9f1f05a0..e1f9c7e9908 100644 --- a/platform/app/pluginConfig.json +++ b/platform/app/pluginConfig.json @@ -78,6 +78,9 @@ { "packageName": "@ohif/mode-longitudinal" }, + { + "packageName": "@ohif/mode-basic" + }, { "packageName": "@ohif/mode-segmentation" }, @@ -115,9 +118,10 @@ }, { "packageName": "dicom-microscopy-viewer", - "importPath": "/dicom-microscopy-viewer/dicomMicroscopyViewer.min.js", + "importPath": "dicom-microscopy-viewer/dicomMicroscopyViewer.min.js", "globalName": "dicomMicroscopyViewer", - "directory": "./node_modules/dicom-microscopy-viewer/dist/dynamic-import" + "directory": "./node_modules/dicom-microscopy-viewer/dist/dynamic-import", + "to": "/dicom-microscopy-viewer/" } ] } diff --git a/platform/app/public/config/default.js b/platform/app/public/config/default.js index 3c906b2ce10..edd7e6fd508 100644 --- a/platform/app/public/config/default.js +++ b/platform/app/public/config/default.js @@ -183,7 +183,7 @@ window.config = { { friendlyName: 'dcmjs DICOMWeb Server', namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', - sourceName: 'dicomweb', + sourceName: 'ohif', configuration: { friendlyName: 'AWS S3 Static wado server', name: 'aws', diff --git a/platform/app/public/config/docker-nginx-orthanc.js b/platform/app/public/config/docker-nginx-orthanc.js index 90abe162edf..820ef8fb039 100644 --- a/platform/app/public/config/docker-nginx-orthanc.js +++ b/platform/app/public/config/docker-nginx-orthanc.js @@ -16,11 +16,11 @@ window.config = { maxNumPrefetchRequests: 10, order: 'closest', }, - defaultDataSourceName: 'dicomweb', + defaultDataSourceName: 'orthancProxy', dataSources: [ { namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', - sourceName: 'dicomweb', + sourceName: 'orthancProxy', configuration: { friendlyName: 'Orthanc Server', name: 'Orthanc', @@ -34,6 +34,59 @@ window.config = { omitQuotationForMultipartRequest: true, }, }, + { + namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', + sourceName: 'ohif', + configuration: { + friendlyName: 'AWS S3 Static wado server', + name: 'aws', + wadoUriRoot: 'https://d14fa38qiwhyfd.cloudfront.net/dicomweb', + qidoRoot: 'https://d14fa38qiwhyfd.cloudfront.net/dicomweb', + wadoRoot: 'https://d14fa38qiwhyfd.cloudfront.net/dicomweb', + qidoSupportsIncludeField: false, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: true, + supportsWildcard: false, + staticWado: true, + singlepart: 'bulkdata,video', + // whether the data source should use retrieveBulkData to grab metadata, + // and in case of relative path, what would it be relative to, options + // are in the series level or study level (some servers like series some study) + bulkDataURI: { + enabled: true, + relativeResolution: 'studies', + transform: url => url.replace('/pixeldata.mp4', '/rendered'), + }, + omitQuotationForMultipartRequest: true, + }, + }, + { + namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', + sourceName: 'local5000', + configuration: { + friendlyName: 'Static WADO Local Data', + name: 'DCM4CHEE', + qidoRoot: 'http://localhost:5000/dicomweb', + wadoRoot: 'http://localhost:5000/dicomweb', + qidoSupportsIncludeField: false, + supportsReject: true, + supportsStow: true, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: false, + supportsWildcard: true, + staticWado: true, + singlepart: 'video', + bulkDataURI: { + enabled: true, + relativeResolution: 'studies', + }, + }, + }, + { namespace: '@ohif/extension-default.dataSourcesModule.dicomjson', sourceName: 'dicomjson', diff --git a/platform/app/public/config/kheops.js b/platform/app/public/config/kheops.js index 154e957cf9d..4a92cf51429 100644 --- a/platform/app/public/config/kheops.js +++ b/platform/app/public/config/kheops.js @@ -27,6 +27,16 @@ window.config = { // Uses the ohif datasource as the default - this requires that KHEOPS be // configured with an OHIF path to .../viewer/dicomwebproxy defaultDataSourceName: 'ohif', + // Show basic as 'Basic' and hide the longiutdinal mode for kheops + modesConfiguration: { + '@ohif/mode-basic': { + hide: { $set: false }, + displayName: { $set: 'Basic' }, + }, + '@ohif/mode-longitudinal': { + hide: { $set: true }, + }, + }, /* Dynamic config allows user to pass "configUrl" query string this allows to load config without recompiling application. The regex will ensure valid configuration source */ // dangerouslyUseDynamicConfig: { // enabled: true, @@ -116,6 +126,29 @@ window.config = { }, }, }, + { + friendlyName: 'StaticWado default data', + namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', + sourceName: 'dicomweb', + configuration: { + name: 'DCM4CHEE', + wadoUriRoot: '/dicomweb', + qidoRoot: '/dicomweb', + wadoRoot: '/dicomweb', + qidoSupportsIncludeField: false, + supportsReject: false, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: false, + supportsWildcard: true, + staticWado: true, + bulkDataURI: { + enabled: true, + relativeResolution: 'studies', + }, + }, + }, { namespace: '@ohif/extension-default.dataSourcesModule.dicomwebproxy', diff --git a/platform/app/src/App.tsx b/platform/app/src/App.tsx index f87fd6a285b..446308d0092 100644 --- a/platform/app/src/App.tsx +++ b/platform/app/src/App.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import i18n from '@ohif/i18n'; import { I18nextProvider } from 'react-i18next'; -import { BrowserRouter } from 'react-router-dom'; +import { BrowserRouter, type BrowserRouterProps } from 'react-router-dom'; import Compose from './routes/Mode/Compose'; import { @@ -43,6 +43,11 @@ let commandsManager: CommandsManager, serviceProvidersManager: ServiceProvidersManager, hotkeysManager: HotkeysManager; +const routerFutureFlags: BrowserRouterProps['future'] = { + v7_startTransition: true, + v7_relativeSplatPath: true, +}; + function App({ config = { /** @@ -164,7 +169,10 @@ function App({ return ( - + {authRoutes} {appRoutes} diff --git a/platform/app/src/appInit.js b/platform/app/src/appInit.js index b4e510ccccd..717e7f172f1 100644 --- a/platform/app/src/appInit.js +++ b/platform/app/src/appInit.js @@ -25,6 +25,7 @@ import { } from '@ohif/core'; import loadModules, { loadModule as peerImport } from './pluginImports'; +import { publicUrl } from './utils/publicUrl'; /** * @param {object|func} appConfigOrFunc - application configuration, or a function that returns application configuration @@ -48,6 +49,7 @@ async function appInit(appConfigOrFunc, defaultExtensions, defaultModes) { // Default the peer import function appConfig.peerImport ||= peerImport; appConfig.measurementTrackingMode ||= 'standard'; + appConfig.routerBasename ||= publicUrl; const extensionManager = new ExtensionManager({ commandsManager, diff --git a/platform/app/src/components/ViewportGrid.tsx b/platform/app/src/components/ViewportGrid.tsx index 1bf35281bfb..9c0d326a4ba 100644 --- a/platform/app/src/components/ViewportGrid.tsx +++ b/platform/app/src/components/ViewportGrid.tsx @@ -273,6 +273,7 @@ function ViewerViewportGrid(props: withAppTypes) { >
{ if (config_json !== null) { window.config = config_json; } - window.config.routerBasename ||= publicUrl; /** * Combine our appConfiguration with installed extensions and modes. diff --git a/platform/app/src/routes/Mode/defaultRouteInit.ts b/platform/app/src/routes/Mode/defaultRouteInit.ts index fceb7573c9d..b7aeb844f31 100644 --- a/platform/app/src/routes/Mode/defaultRouteInit.ts +++ b/platform/app/src/routes/Mode/defaultRouteInit.ts @@ -1,8 +1,8 @@ -import getStudies from './studiesList'; import { DicomMetadataStore, log, utils, Enums } from '@ohif/core'; +import getStudies from './studiesList'; import isSeriesFilterUsed from '../../utils/isSeriesFilterUsed'; -const { getSplitParam } = utils; +const { seriesSortCriteria, getSplitParam } = utils; /** * Initialize the route. @@ -14,7 +14,12 @@ const { getSplitParam } = utils; * @returns array of subscriptions to cancel */ export async function defaultRouteInit( - { servicesManager, studyInstanceUIDs, dataSource, filters, appConfig }: withAppTypes, + { + servicesManager, + studyInstanceUIDs, + dataSource, + filters, + }: withAppTypes & { studyInstanceUIDs?: string[] }, hangingProtocolId, stageIndex ) { @@ -27,13 +32,17 @@ export async function defaultRouteInit( */ function applyHangingProtocol() { const displaySets = displaySetService.getActiveDisplaySets(); + // The display sets are not necessarily in load order, even though the + // series got started in load order, so re-sort them before hanging + const sortCriteria = seriesSortCriteria.default; if (!displaySets || !displaySets.length) { return; } + const sortedDisplaySets = [...displaySets].sort(sortCriteria); // Gets the studies list to use - const studies = getStudies(studyInstanceUIDs, displaySets); + const studies = getStudies(studyInstanceUIDs, sortedDisplaySets); // study being displayed, and is thus the "active" study. const activeStudy = studies[0]; diff --git a/platform/app/src/routes/NotFound/NotFound.tsx b/platform/app/src/routes/NotFound/NotFound.tsx index 4672b971aa9..e22c8214734 100644 --- a/platform/app/src/routes/NotFound/NotFound.tsx +++ b/platform/app/src/routes/NotFound/NotFound.tsx @@ -1,22 +1,36 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Link } from 'react-router-dom'; - +import { useNavigate } from 'react-router-dom'; +import { Button, Icons } from '@ohif/ui-next'; import { useAppConfig } from '@state'; -const NotFound = ({ message = 'Sorry, this page does not exist.', showGoBackButton = true }) => { +const NotFound = ({ + message = "We can't find the page you're looking for.", + showGoBackButton = true, +}) => { const [appConfig] = useAppConfig(); const { showStudyList } = appConfig; + const navigate = useNavigate(); return ( -
-
-

{message}

- {showGoBackButton && showStudyList && ( -
- Go back to the Study List -
- )} +
+
+
+ +
+
+
+

Error (404)

+

{message}

+ {showGoBackButton && showStudyList && ( + + )} +
); diff --git a/platform/app/src/routes/SignoutCallbackComponent.tsx b/platform/app/src/routes/SignoutCallbackComponent.tsx index f01a89a7d87..e3b6aadcf72 100644 --- a/platform/app/src/routes/SignoutCallbackComponent.tsx +++ b/platform/app/src/routes/SignoutCallbackComponent.tsx @@ -1,24 +1,28 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import PropTypes from 'prop-types'; function SignoutCallbackComponent({ userManager }) { const navigate = useNavigate(); - const onRedirectSuccess = (/* user */) => { - const { pathname, search = '' } = JSON.parse(sessionStorage.getItem('ohif-redirect-to')); + useEffect(() => { + const onRedirectSuccess = (/* user */) => { + const { pathname, search = '' } = JSON.parse( + sessionStorage.getItem('ohif-redirect-to') + ); - navigate(`${pathname}?${search}`); - }; + navigate(`${pathname}?${search}`); + }; - const onRedirectError = error => { - throw new Error(error); - }; + const onRedirectError = error => { + throw new Error(error); + }; - userManager - .signoutRedirectCallback() - .then(user => onRedirectSuccess(user)) - .catch(error => onRedirectError(error)); + userManager + .signoutRedirectCallback() + .then(user => onRedirectSuccess(user)) + .catch(error => onRedirectError(error)); + }, [navigate, userManager]); return null; } diff --git a/platform/app/src/routes/WorkList/WorkList.tsx b/platform/app/src/routes/WorkList/WorkList.tsx index 0ea85fa385d..9c97426ea6f 100644 --- a/platform/app/src/routes/WorkList/WorkList.tsx +++ b/platform/app/src/routes/WorkList/WorkList.tsx @@ -391,12 +391,21 @@ function WorkList({ }) : appConfig.loadedModes ).map((mode, i) => { + if (mode.hide) { + // Hide this mode from display + return null; + } const modalitiesToCheck = modalities.replaceAll('/', '\\'); const { valid: isValidMode, description: invalidModeDescription } = mode.isValidMode({ modalities: modalitiesToCheck, study, }); + if (isValidMode === null) { + // Hide this as a computed result. + return null; + } + // TODO: Modes need a default/target route? We mostly support a single one for now. // We should also be using the route path, but currently are not // mode.routeName @@ -428,7 +437,7 @@ function WorkList({ {/* TODO revisit the completely rounded style of buttons used for launching a mode from the worklist later */} @@ -515,6 +524,7 @@ function WorkList({ DicomUploadComponent && dataSource.getConfig()?.dicomUploadEnabled ? { title: 'Upload files', + containerClassName: DicomUploadComponent?.containerClassName, closeButton: true, shouldCloseOnEsc: false, shouldCloseOnOverlayClick: false, diff --git a/platform/app/src/routes/index.tsx b/platform/app/src/routes/index.tsx index d183563f16b..5a8ea561d5a 100644 --- a/platform/app/src/routes/index.tsx +++ b/platform/app/src/routes/index.tsx @@ -85,7 +85,7 @@ const bakedInRoutes = [ ]; // NOT FOUND (404) -const notFoundRoute = { component: NotFound }; +const notFoundRoute = { path: '*', children: NotFound }; const createRoutes = ({ modes, diff --git a/platform/app/src/utils/OpenIdConnectRoutes.tsx b/platform/app/src/utils/OpenIdConnectRoutes.tsx index 9517d76783a..b091f33c085 100644 --- a/platform/app/src/utils/OpenIdConnectRoutes.tsx +++ b/platform/app/src/utils/OpenIdConnectRoutes.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useEffect } from 'react'; +import { useEffect, useMemo } from 'react'; import { Route, Routes, useLocation, useNavigate } from 'react-router'; import CallbackPage from '../routes/CallbackPage'; import SignoutCallbackComponent from '../routes/SignoutCallbackComponent'; @@ -94,7 +94,7 @@ function LoginComponent(userManager) { } function OpenIdConnectRoutes({ oidc, routerBasename, userAuthenticationService }) { - const userManager = initUserManager(oidc, routerBasename); + const userManager = useMemo(() => initUserManager(oidc, routerBasename), [oidc, routerBasename]); const getAuthorizationHeader = () => { const user = userAuthenticationService.getUser(); diff --git a/platform/cli/CHANGELOG.md b/platform/cli/CHANGELOG.md index 8e87c0f82f3..b3ae9fe4065 100644 --- a/platform/cli/CHANGELOG.md +++ b/platform/cli/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/cli @@ -11,7 +11,1096 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24) + + +### Bug Fixes + +* **security:** For bun, enforce frozen lockfile. For yarn, strongly suggest using frozen lockfile. ([#5508](https://github.com/OHIF/Viewers/issues/5508)) ([1009c60](https://github.com/OHIF/Viewers/commit/1009c6091107d2db0768622120f916208a391343)) + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [3.12.0-beta.39](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.38...v3.12.0-beta.39) (2025-10-01) + + +### Features + +* Add inherit from basic/longitudinal modes ([#5435](https://github.com/OHIF/Viewers/issues/5435)) ([9ad0d7f](https://github.com/OHIF/Viewers/commit/9ad0d7fc8cf34867fa2cfc2bf2b2f63451ec03e5)) + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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 @ohif/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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 @ohif/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [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/cli + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/cli + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/cli diff --git a/platform/cli/package.json b/platform/cli/package.json index c795ee840bd..38d4d1dd7c7 100644 --- a/platform/cli/package.json +++ b/platform/cli/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/cli", - "version": "3.11.1", + "version": "3.12.0", "description": "A CLI to bootstrap new OHIF extension or mode", "type": "module", "main": "src/index.js", diff --git a/platform/cli/src/index.js b/platform/cli/src/index.js index d14445abb74..ac53bdaea73 100755 --- a/platform/cli/src/index.js +++ b/platform/cli/src/index.js @@ -156,7 +156,7 @@ program unlinkExtension(extensionName, { viewerDirectory }); console.log( chalk.green( - `Successfully unlinked Extension ${extensionName} from the Viewer, don't forget to run yarn install --force` + `Successfully unlinked Extension ${extensionName} from the Viewer, don't forget to run yarn install --frozen-lockfile --force` ) ); }); @@ -179,7 +179,7 @@ program unlinkMode(modeName, { viewerDirectory }); console.log( chalk.green( - `Successfully unlinked Mode ${modeName} from the Viewer, don't forget to run yarn install --force` + `Successfully unlinked Mode ${modeName} from the Viewer, don't forget to run yarn install --frozen-lockfile --force` ) ); }); diff --git a/platform/cli/templates/mode/src/index.tsx b/platform/cli/templates/mode/src/index.tsx index c0046a29c61..e8d73a59063 100644 --- a/platform/cli/templates/mode/src/index.tsx +++ b/platform/cli/templates/mode/src/index.tsx @@ -1,174 +1,22 @@ -import { hotkeys } from '@ohif/core'; -import { initToolGroups, toolbarButtons } from '@ohif/mode-longitudinal'; import { id } from './id'; +import { longitudinalMode, longitudinalModeInstance, longitudinalRoute } from '@ohif/mode-longitudinal'; -const ohif = { - layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout', - sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack', - hangingProtocol: '@ohif/extension-default.hangingProtocolModule.default', - leftPanel: '@ohif/extension-default.panelModule.seriesList', - rightPanel: '@ohif/extension-cornerstone.panelModule.panelMeasurement', -}; - -const cornerstone = { - viewport: '@ohif/extension-cornerstone.viewportModule.cornerstone', -}; - -/** - * Just two dependencies to be able to render a viewport with panels in order - * to make sure that the mode is working. - */ -const extensionDependencies = { - '@ohif/extension-default': '^3.0.0', - '@ohif/extension-cornerstone': '^3.0.0', -}; - -function modeFactory({ modeConfiguration }) { - return { - /** - * Mode ID, which should be unique among modes used by the viewer. This ID - * is used to identify the mode in the viewer's state. - */ +export const modeInstance = { + ...longitudinalModeInstance, + // TODO: We're using this as a route segment + // We should not be. id, routeName: 'template', - /** - * Mode name, which is displayed in the viewer's UI in the workList, for the - * user to select the mode. - */ displayName: 'Template Mode', - /** - * Runs when the Mode Route is mounted to the DOM. Usually used to initialize - * Services and other resources. - */ - onModeEnter: ({ servicesManager, extensionManager, commandsManager }: withAppTypes) => { - const { measurementService, toolbarService, toolGroupService } = servicesManager.services; - - measurementService.clearMeasurements(); - - // Init Default and SR ToolGroups - initToolGroups(extensionManager, toolGroupService, commandsManager); - - toolbarService.register([...toolbarButtons]); - toolbarService.updateSection('primary', [ - 'MeasurementTools', - 'Zoom', - 'Pan', - 'TrackballRotate', - 'WindowLevel', - 'Capture', - 'Layout', - 'Crosshairs', - 'MoreTools', - ]); - - toolbarService.updateSection('MeasurementTools', [ - 'Length', - 'Bidirectional', - 'ArrowAnnotate', - 'EllipticalROI', - 'RectangleROI', - 'CircleROI', - 'PlanarFreehandROI', - 'SplineROI', - 'LivewireContour', - ]); - - toolbarService.updateSection('MoreTools', [ - 'Reset', - 'rotate-right', - 'flipHorizontal', - 'ImageSliceSync', - 'ReferenceLines', - 'ImageOverlayViewer', - 'StackScroll', - 'invert', - 'Probe', - 'Cine', - 'Angle', - 'CobbAngle', - 'Magnify', - 'CalibrationLine', - 'TagBrowser', - 'AdvancedMagnify', - 'UltrasoundDirectionalTool', - 'WindowLevelRegion', - ]); - }, - onModeExit: ({ servicesManager }: withAppTypes) => { - const { - toolGroupService, - syncGroupService, - segmentationService, - cornerstoneViewportService, - uiDialogService, - uiModalService, - } = servicesManager.services; - - uiDialogService.hideAll(); - uiModalService.hide(); - toolGroupService.destroy(); - syncGroupService.destroy(); - segmentationService.destroy(); - cornerstoneViewportService.destroy(); - }, - /** */ - validationTags: { - study: [], - series: [], - }, - /** - * A boolean return value that indicates whether the mode is valid for the - * modalities of the selected studies. For instance a PET/CT mode should be - */ - isValidMode: ({ modalities }) => { - return { valid: true }; - }, - /** - * Mode Routes are used to define the mode's behavior. A list of Mode Route - * that includes the mode's path and the layout to be used. The layout will - * include the components that are used in the layout. For instance, if the - * default layoutTemplate is used (id: '@ohif/extension-default.layoutTemplateModule.viewerLayout') - * it will include the leftPanels, rightPanels, and viewports. However, if - * you define another layoutTemplate that includes a Footer for instance, - * you should provide the Footer component here too. Note: We use Strings - * to reference the component's ID as they are registered in the internal - * ExtensionManager. The template for the string is: - * `${extensionId}.{moduleType}.${componentId}`. - */ routes: [ - { - path: 'template', - layoutTemplate: ({ location, servicesManager }) => { - return { - id: ohif.layout, - props: { - leftPanels: [ohif.leftPanel], - rightPanels: [ohif.rightPanel], - viewports: [ - { - namespace: cornerstone.viewport, - displaySetsToDisplay: [ohif.sopClassHandler], - }, - ], - }, - }; - }, - }, + longitudinalRoute ], - /** List of extensions that are used by the mode */ - extensions: extensionDependencies, - /** HangingProtocol used by the mode */ - // hangingProtocol: [''], - /** SopClassHandlers used by the mode */ - sopClassHandlers: [ohif.sopClassHandler], - /** hotkeys for mode */ }; -} -const mode = { +export const mode = { + ...longitudinalMode, id, - modeFactory, - extensionDependencies, + modeInstance, }; export default mode; diff --git a/platform/core/CHANGELOG.md b/platform/core/CHANGELOG.md index 0af9fec99c4..4b4767dd82b 100644 --- a/platform/core/CHANGELOG.md +++ b/platform/core/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/core @@ -11,7 +11,1174 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/core + + + + + +# [3.12.0-beta.134](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.133...v3.12.0-beta.134) (2026-02-05) + + +### Bug Fixes + +* **HistoryMemo:** Segmentation delete wasn't remembered ([#5775](https://github.com/OHIF/Viewers/issues/5775)) ([48f2f6f](https://github.com/OHIF/Viewers/commit/48f2f6fb13f9ba0aa03ad51afd2812466bcb2f58)) + + + + + +# [3.12.0-beta.133](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.132...v3.12.0-beta.133) (2026-02-04) + + +### Bug Fixes + +* **jump-to-label-map:** Use `undefined` for viewportId for arrow navigation. ([#5774](https://github.com/OHIF/Viewers/issues/5774)) ([512aa3b](https://github.com/OHIF/Viewers/commit/512aa3b07f4c783040acd67e3fd2103559a06522)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [3.12.0-beta.128](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.127...v3.12.0-beta.128) (2026-01-27) + + +### Bug Fixes + +* **TMTV:** Consider blend mode when adding segmentation representations. OHIF-2416, OHIF-2369 ([#5735](https://github.com/OHIF/Viewers/issues/5735)) ([06612fe](https://github.com/OHIF/Viewers/commit/06612fe177e2c31c33fa5c81c092b830a337cac2)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [3.12.0-beta.122](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.121...v3.12.0-beta.122) (2026-01-13) + + +### Bug Fixes + +* **segmentation:** List surface representations in the segmentation table for 3D views. ([#5700](https://github.com/OHIF/Viewers/issues/5700)) ([82e6a18](https://github.com/OHIF/Viewers/commit/82e6a18735b2e2278d6b95037d82bd3cb529104d)) + + + + + +# [3.12.0-beta.121](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.120...v3.12.0-beta.121) (2026-01-12) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix a segmentation exception after capture. ([#5701](https://github.com/OHIF/Viewers/issues/5701)) ([aba4855](https://github.com/OHIF/Viewers/commit/aba48552765d03cf82e6a92cdd443b246560c4fd)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [3.12.0-beta.111](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.110...v3.12.0-beta.111) (2025-12-18) + + +### Bug Fixes + +* **segmentation:** Update cornerstone dependencies to fix segment visibility issue. ([#5664](https://github.com/OHIF/Viewers/issues/5664)) ([3440f66](https://github.com/OHIF/Viewers/commit/3440f66ebd0900c0f5b1a5edbdf6f3b5fc72bbcf)) + + + + + +# [3.12.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.109...v3.12.0-beta.110) (2025-12-18) + + +### Bug Fixes + +* prevent annotation from appearing in active viewport when switching series with different Frame of Reference UID ([#5630](https://github.com/OHIF/Viewers/issues/5630)) ([658994c](https://github.com/OHIF/Viewers/commit/658994c854a03203054168dcfd383c0a6e320df0)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16) + + +### Bug Fixes + +* preclinical mode not working ([#5631](https://github.com/OHIF/Viewers/issues/5631)) ([2de81ef](https://github.com/OHIF/Viewers/commit/2de81efa7e6be534972cd763271c038b293d86f0)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [3.12.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.99...v3.12.0-beta.100) (2025-12-05) + + +### Features + +* **notification:** add custom notification component support ([#5605](https://github.com/OHIF/Viewers/issues/5605)) ([c4e5a46](https://github.com/OHIF/Viewers/commit/c4e5a4616db87b39df5e16b75f1ba3c18407468f)) + + + + + +# [3.12.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.98...v3.12.0-beta.99) (2025-12-04) + + +### Bug Fixes + +* Initial sort not consistent ([#5224](https://github.com/OHIF/Viewers/issues/5224)) ([77f9f8e](https://github.com/OHIF/Viewers/commit/77f9f8e1c4af6b5d22ecf9332b5edf503cd5b848)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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 @ohif/core + + + + + +# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25) + + +### Features + +* tooltips for tool settings ([#5586](https://github.com/OHIF/Viewers/issues/5586)) ([411553e](https://github.com/OHIF/Viewers/commit/411553ee916b8a63b2a934a7e1274bc449041016)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19) + + +### Bug Fixes + +* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f)) + + + + + +# [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/core + + + + + +# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11) + + +### Bug Fixes + +* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f)) + + + + + +# [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/core + + + + + +# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06) + + +### Features + +* **metadata:** metadata access improvement ([#5292](https://github.com/OHIF/Viewers/issues/5292)) ([16233d9](https://github.com/OHIF/Viewers/commit/16233d980f17abfc30461b46c6b1888be10c7840)) + + + + + +# [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/core + + + + + +# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05) + + +### Bug Fixes + +* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28) + + +### Bug Fixes + +* **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](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190)) + + + + + +# [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/core + + + + + +# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24) + + +### Bug Fixes + +* **security:** For bun, enforce frozen lockfile. For yarn, strongly suggest using frozen lockfile. ([#5508](https://github.com/OHIF/Viewers/issues/5508)) ([1009c60](https://github.com/OHIF/Viewers/commit/1009c6091107d2db0768622120f916208a391343)) + + + + + +# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23) + + +### Features + +* **jpeg2000:** Add 16-bit RGB support to JPEG2000 decoder ([#5519](https://github.com/OHIF/Viewers/issues/5519)) ([a154443](https://github.com/OHIF/Viewers/commit/a1544432db42bf77864bb7df2f757cae819a4b4d)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20) + + +### Bug Fixes + +* **rendering:** Fix palette color LUT conversion causing black images ([#5509](https://github.com/OHIF/Viewers/issues/5509)) ([ffd9ec3](https://github.com/OHIF/Viewers/commit/ffd9ec32730580e266ffae365a10ac3e524dd6cf)) + + + + + +# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20) + + +### Bug Fixes + +* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404) + + + + + +# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18) + + +### Bug Fixes + +* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [3.12.0-beta.47](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.46...v3.12.0-beta.47) (2025-10-07) + + +### Bug Fixes + +* **toolbar:** prevent duplicate command execution on repeated interactions ([#5456](https://github.com/OHIF/Viewers/issues/5456)) ([4d04dd5](https://github.com/OHIF/Viewers/commit/4d04dd5651f4cf8dcaf4732baee5e218e92bd3c1)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [3.12.0-beta.44](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.43...v3.12.0-beta.44) (2025-10-06) + + +### Bug Fixes + +* Safely handle missing active viewport ([#5448](https://github.com/OHIF/Viewers/issues/5448)) ([792db26](https://github.com/OHIF/Viewers/commit/792db26ff1591f9e054486f1bee01c422e774101)) + + + + + +# [3.12.0-beta.43](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.42...v3.12.0-beta.43) (2025-10-06) + + +### Features + +* **MeasurementService:** add rendering of unmapped measurements ([#5416](https://github.com/OHIF/Viewers/issues/5416)) ([851e74d](https://github.com/OHIF/Viewers/commit/851e74d7b867a806befb5d85fd71ff9a75e9f2d2)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [3.12.0-beta.39](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.38...v3.12.0-beta.39) (2025-10-01) + + +### Features + +* Add inherit from basic/longitudinal modes ([#5435](https://github.com/OHIF/Viewers/issues/5435)) ([9ad0d7f](https://github.com/OHIF/Viewers/commit/9ad0d7fc8cf34867fa2cfc2bf2b2f63451ec03e5)) + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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 @ohif/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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 @ohif/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [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/core + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + + +### Bug Fixes + +* local ModalitiesInStudy check ([#5285](https://github.com/OHIF/Viewers/issues/5285)) ([d269404](https://github.com/OHIF/Viewers/commit/d2694048a47faf9028184d0272df12e2602ae674)) + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + + +### Features + +* Add WSI bulk data annotations support (ANN) ([#4972](https://github.com/OHIF/Viewers/issues/4972)) ([2ed6818](https://github.com/OHIF/Viewers/commit/2ed681817142b13971bb17b3058be26bbab6b90b)) + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/core + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/core diff --git a/platform/core/README.md b/platform/core/README.md index ff8f555467d..26ea6100216 100644 --- a/platform/core/README.md +++ b/platform/core/README.md @@ -80,7 +80,7 @@ to program in isolation without a complex setup, and has the added benefit of producing well-tested business logic. 1. Clone this repository -2. Navigate to the project directory, and `yarn install` +2. Navigate to the project directory, and `yarn install --frozen-lockfile` 3. To begin making changes, `yarn run dev` 4. To commit changes, run `yarn run cm` diff --git a/platform/core/package.json b/platform/core/package.json index 65c976614f6..d1094b0cc02 100644 --- a/platform/core/package.json +++ b/platform/core/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/core", - "version": "3.11.1", + "version": "3.12.0", "description": "Generic business logic for web-based medical imaging applications", "author": "OHIF Core Team", "license": "MIT", @@ -35,17 +35,17 @@ "peerDependencies": { "@cornerstonejs/codec-charls": "1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2", - "@cornerstonejs/codec-openjpeg": "1.2.4", + "@cornerstonejs/codec-openjpeg": "1.3.0", "@cornerstonejs/codec-openjph": "2.4.7", - "@cornerstonejs/core": "4.5.13", - "@cornerstonejs/dicom-image-loader": "4.5.13", - "@ohif/ui": "3.11.1", + "@cornerstonejs/core": "4.15.29", + "@cornerstonejs/dicom-image-loader": "4.15.29", + "@ohif/ui": "3.12.0", "cornerstone-math": "0.1.9", "dicom-parser": "1.8.21" }, "dependencies": { "@babel/runtime": "7.28.2", - "dcmjs": "0.43.1", + "dcmjs": "0.49.4", "dicomweb-client": "0.10.4", "gl-matrix": "3.4.3", "immutability-helper": "3.1.1", diff --git a/platform/core/src/DICOMWeb/getAuthorizationHeader.test.js b/platform/core/src/DICOMWeb/getAuthorizationHeader.test.ts similarity index 77% rename from platform/core/src/DICOMWeb/getAuthorizationHeader.test.js rename to platform/core/src/DICOMWeb/getAuthorizationHeader.test.ts index 9bf0b4636b5..2ac4c334a4d 100644 --- a/platform/core/src/DICOMWeb/getAuthorizationHeader.test.js +++ b/platform/core/src/DICOMWeb/getAuthorizationHeader.test.ts @@ -1,7 +1,8 @@ import getAuthorizationHeader from './getAuthorizationHeader'; import user from './../user'; +import { HeadersInterface, RequestOptions } from '../types/RequestHeaders'; -jest.mock('./../user.js'); +jest.mock('../user'); describe('getAuthorizationHeader', () => { it('should return a HTTP Basic Auth when server contains requestOptions.auth', () => { @@ -15,7 +16,7 @@ describe('getAuthorizationHeader', () => { Authorization: `Basic ${btoa(validServer.requestOptions.auth)}`, }; - const authentication = getAuthorizationHeader(validServer); + const authentication: HeadersInterface = getAuthorizationHeader(validServer); expect(authentication).toEqual(expectedAuthorizationHeader); }); @@ -31,13 +32,13 @@ describe('getAuthorizationHeader', () => { Authorization: `Basic ${btoa(validServerWithoutPassword.requestOptions.auth)}`, }; - const authentication = getAuthorizationHeader(validServerWithoutPassword); + const authentication: HeadersInterface = getAuthorizationHeader(validServerWithoutPassword); expect(authentication).toEqual(expectedAuthorizationHeader); }); it('should return a HTTP Basic Auth when server contains requestOptions.auth custom function', () => { - const validServerCustomAuth = { + const validServerCustomAuth: RequestOptions = { requestOptions: { auth: options => `Basic ${options.token}`, token: 'ZHVtbXlfdXNlcjpkdW1teV9wYXNzd29yZA==', @@ -48,13 +49,13 @@ describe('getAuthorizationHeader', () => { Authorization: `Basic ${validServerCustomAuth.requestOptions.token}`, }; - const authentication = getAuthorizationHeader(validServerCustomAuth); + const authentication: HeadersInterface = getAuthorizationHeader(validServerCustomAuth); expect(authentication).toEqual(expectedAuthorizationHeader); }); it('should return an empty object when there is no either server.requestOptions.auth or accessToken', () => { - const authentication = getAuthorizationHeader({}); + const authentication: HeadersInterface = getAuthorizationHeader({}); expect(authentication).toEqual({}); }); @@ -62,7 +63,7 @@ describe('getAuthorizationHeader', () => { it('should return an Authorization with accessToken when server is not defined and there is an accessToken', () => { user.getAccessToken.mockImplementationOnce(() => 'MOCKED_TOKEN'); - const authentication = getAuthorizationHeader({}, user); + const authentication: HeadersInterface = getAuthorizationHeader({}, user); const expectedHeaderBasedOnUserAccessToken = { Authorization: 'Bearer MOCKED_TOKEN', }; diff --git a/platform/core/src/DICOMWeb/getAuthorizationHeader.js b/platform/core/src/DICOMWeb/getAuthorizationHeader.ts similarity index 56% rename from platform/core/src/DICOMWeb/getAuthorizationHeader.js rename to platform/core/src/DICOMWeb/getAuthorizationHeader.ts index 4d996bb97d6..b7736bb5e83 100644 --- a/platform/core/src/DICOMWeb/getAuthorizationHeader.js +++ b/platform/core/src/DICOMWeb/getAuthorizationHeader.ts @@ -1,23 +1,29 @@ import 'isomorphic-base64'; -import user from '../user'; +import {UserAccountInterface} from '../user'; +import { HeadersInterface, RequestOptions } from '../types/RequestHeaders'; /** * Returns the Authorization header as part of an Object. * * @export * @param {Object} [server={}] - * @param {Object} [server.requestOptions] - * @param {string|function} [server.requestOptions.auth] + * @param {Object} [requestOptions] + * @param {string|function} [requestOptions.auth] + * @param {Object} [user] + * @param {function} [user.getAccessToken] * @returns {Object} { Authorization } */ -export default function getAuthorizationHeader({ requestOptions } = {}, user) { - const headers = {}; +export default function getAuthorizationHeader( + {requestOptions}: RequestOptions = {}, + user: UserAccountInterface = {}): HeadersInterface +{ + const headers: HeadersInterface = {}; // Check for OHIF.user since this can also be run on the server const accessToken = user && user.getAccessToken && user.getAccessToken(); // Auth for a specific server - if (requestOptions && requestOptions.auth) { + if (requestOptions?.auth) { if (typeof requestOptions.auth === 'function') { // Custom Auth Header headers.Authorization = requestOptions.auth(requestOptions); @@ -25,9 +31,8 @@ export default function getAuthorizationHeader({ requestOptions } = {}, user) { // HTTP Basic Auth (user:password) headers.Authorization = `Basic ${btoa(requestOptions.auth)}`; } - } - // Auth for the user's default - else if (accessToken) { + } else if (accessToken) { + // Auth for the user's default headers.Authorization = `Bearer ${accessToken}`; } diff --git a/platform/core/src/DICOMWeb/index.js b/platform/core/src/DICOMWeb/index.js index 0775d927492..f8d7bf23b9b 100644 --- a/platform/core/src/DICOMWeb/index.js +++ b/platform/core/src/DICOMWeb/index.js @@ -1,5 +1,5 @@ import getAttribute from './getAttribute.js'; -import getAuthorizationHeader from './getAuthorizationHeader.js'; +import getAuthorizationHeader from './getAuthorizationHeader'; import getModalities from './getModalities.js'; import getName from './getName.js'; import getNumber from './getNumber.js'; diff --git a/platform/core/src/classes/MetadataProvider.ts b/platform/core/src/classes/MetadataProvider.ts index 541d9ff30d2..1b8d4308534 100644 --- a/platform/core/src/classes/MetadataProvider.ts +++ b/platform/core/src/classes/MetadataProvider.ts @@ -6,7 +6,7 @@ import DicomMetadataStore from '../services/DicomMetadataStore'; import fetchPaletteColorLookupTableData from '../utils/metadataProvider/fetchPaletteColorLookupTableData'; import toNumber from '../utils/toNumber'; import combineFrameInstance from '../utils/combineFrameInstance'; -import formatPN from '../utils/formatPN'; + const { calibratedPixelSpacingMetadataProvider, getPixelSpacingInformation } = utilities; class MetadataProvider { @@ -123,33 +123,21 @@ class MetadataProvider { switch (wadoImageLoaderTag) { case WADO_IMAGE_LOADER_TAGS.GENERAL_SERIES_MODULE: - const { SeriesDate, SeriesTime } = instance; - - let seriesDate; - let seriesTime; - - if (SeriesDate) { - seriesDate = dicomParser.parseDA(SeriesDate); - } - - if (SeriesTime) { - seriesTime = dicomParser.parseTM(SeriesTime); - } - metadata = { modality: instance.Modality, seriesInstanceUID: instance.SeriesInstanceUID, seriesNumber: toNumber(instance.SeriesNumber), studyInstanceUID: instance.StudyInstanceUID, - seriesDate, - seriesTime, + seriesDescription: instance.SeriesDescription, + seriesDate: instance.SeriesDate, + seriesTime: instance.SeriesTime, }; break; case WADO_IMAGE_LOADER_TAGS.PATIENT_STUDY_MODULE: metadata = { - patientAge: toNumber(instance.PatientAge), - patientSize: toNumber(instance.PatientSize), - patientWeight: toNumber(instance.PatientWeight), + patientAge: instance.PatientAge, + patientSize: instance.PatientSize, + patientWeight: instance.PatientWeight, }; break; case WADO_IMAGE_LOADER_TAGS.PATIENT_DEMOGRAPHIC_MODULE: @@ -346,16 +334,13 @@ class MetadataProvider { break; case WADO_IMAGE_LOADER_TAGS.PATIENT_MODULE: - const { PatientName } = instance; - - let patientName; - if (PatientName) { - patientName = formatPN(PatientName); - } - metadata = { - patientName, + patientName: instance.PatientName, patientId: instance.PatientID, + patientSex: instance.PatientSex, + patientBirthDate: instance.PatientBirthDate, + issuerOfPatientId: instance.IssuerOfPatientID, + otherPatientIDsSequence: instance.OtherPatientIDsSequence, }; break; @@ -373,9 +358,11 @@ class MetadataProvider { case WADO_IMAGE_LOADER_TAGS.GENERAL_STUDY_MODULE: metadata = { studyDescription: instance.StudyDescription, + studyInstanceUID: instance.StudyInstanceUID, studyDate: instance.StudyDate, studyTime: instance.StudyTime, accessionNumber: instance.AccessionNumber, + studyId: instance.StudyID, }; break; @@ -512,9 +499,57 @@ class MetadataProvider { const metadataProvider = new MetadataProvider(); +DicomMetadataStore.setMetaDataProvider(metadataProvider); + export default metadataProvider; +const WADO_IMAGE_LOADER_TAGS = { + // dicomImageLoader specific + GENERAL_SERIES_MODULE: 'generalSeriesModule', + PATIENT_STUDY_MODULE: 'patientStudyModule', + IMAGE_PIXEL_MODULE: 'imagePixelModule', + VOI_LUT_MODULE: 'voiLutModule', + MODALITY_LUT_MODULE: 'modalityLutModule', + SOP_COMMON_MODULE: 'sopCommonModule', + PET_IMAGE_MODULE: 'petImageModule', + PET_ISOTOPE_MODULE: 'petIsotopeModule', + PET_SERIES_MODULE: 'petSeriesModule', + OVERLAY_PLANE_MODULE: 'overlayPlaneModule', + PATIENT_DEMOGRAPHIC_MODULE: 'patientDemographicModule', + + // react-cornerstone-viewport specific + PATIENT_MODULE: 'patientModule', + GENERAL_IMAGE_MODULE: 'generalImageModule', + GENERAL_STUDY_MODULE: 'generalStudyModule', + CINE_MODULE: 'cineModule', + CALIBRATION_MODULE: 'calibrationModule', + + // Computed tags for new data + // Note these get returned in naturalized format + IMAGE_SOP_INSTANCE_REFERENCE: 'ImageSopInstanceReference', +}; + const WADO_IMAGE_LOADER = { + /** Returns information on the current frame reference */ + frameModule: instance => { + const { + frameNumber = 1, + numberOfFrames = 1, + SOPClassUID: sopClassUID, + SOPInstanceUID: sopInstanceUID, + SeriesInstanceUID: seriesInstanceUID, + StudyInstanceUID: studyInstanceUID, + } = instance; + return { + frameNumber, + numberOfFrames, + sopClassUID, + sopInstanceUID, + seriesInstanceUID, + studyInstanceUID, + }; + }, + imagePlaneModule: instance => { const { ImageOrientationPatient, ImagePositionPatient } = instance; @@ -590,26 +625,4 @@ const WADO_IMAGE_LOADER = { }, }; -const WADO_IMAGE_LOADER_TAGS = { - // dicomImageLoader specific - GENERAL_SERIES_MODULE: 'generalSeriesModule', - PATIENT_STUDY_MODULE: 'patientStudyModule', - IMAGE_PIXEL_MODULE: 'imagePixelModule', - VOI_LUT_MODULE: 'voiLutModule', - MODALITY_LUT_MODULE: 'modalityLutModule', - SOP_COMMON_MODULE: 'sopCommonModule', - PET_IMAGE_MODULE: 'petImageModule', - PET_ISOTOPE_MODULE: 'petIsotopeModule', - PET_SERIES_MODULE: 'petSeriesModule', - OVERLAY_PLANE_MODULE: 'overlayPlaneModule', - PATIENT_DEMOGRAPHIC_MODULE: 'patientDemographicModule', - - // react-cornerstone-viewport specific - PATIENT_MODULE: 'patientModule', - GENERAL_IMAGE_MODULE: 'generalImageModule', - GENERAL_STUDY_MODULE: 'generalStudyModule', - CINE_MODULE: 'cineModule', - CALIBRATION_MODULE: 'calibrationModule', -}; - const INSTANCE = 'instance'; diff --git a/platform/core/src/hooks/__tests__/useToolbar.test.ts b/platform/core/src/hooks/__tests__/useToolbar.test.ts new file mode 100644 index 00000000000..01d85b94d69 --- /dev/null +++ b/platform/core/src/hooks/__tests__/useToolbar.test.ts @@ -0,0 +1,188 @@ +import { act } from 'react'; +import { renderHook } from '@testing-library/react'; +import { useToolbar } from '../useToolbar'; +import { useSystem } from '../../contextProviders/SystemProvider'; + +type MockSubscription = { unsubscribe: jest.Mock }; + +type MockToolbarService = { + EVENTS: Record; + getButtonSection: jest.Mock; + getButtonProps: jest.Mock; + recordInteraction: jest.Mock; + subscribe: jest.Mock void]>; + getButton: jest.Mock; + refreshToolbarState: jest.Mock; +}; + +type MockViewportGridService = { + EVENTS: Record; + subscribe: jest.Mock void]>; + getActiveViewportId: jest.Mock; +}; + +jest.mock('../../contextProviders/SystemProvider', () => ({ + useSystem: jest.fn(), +})); + +const mockedUseSystem = useSystem as jest.MockedFunction; + +const createToolbarService = (): MockToolbarService => ({ + EVENTS: { + TOOL_BAR_MODIFIED: 'TOOL_BAR_MODIFIED', + TOOL_BAR_STATE_MODIFIED: 'TOOL_BAR_STATE_MODIFIED', + }, + getButtonSection: jest.fn().mockReturnValue([]), + getButtonProps: jest.fn().mockReturnValue({}), + recordInteraction: jest.fn(), + subscribe: jest.fn(() => ({ unsubscribe: jest.fn() })), + getButton: jest.fn(), + refreshToolbarState: jest.fn(), +}); + +const createViewportGridService = (): MockViewportGridService => ({ + EVENTS: { + ACTIVE_VIEWPORT_ID_CHANGED: 'ACTIVE_VIEWPORT_ID_CHANGED', + VIEWPORTS_READY: 'VIEWPORTS_READY', + LAYOUT_CHANGED: 'LAYOUT_CHANGED', + }, + subscribe: jest.fn(() => ({ unsubscribe: jest.fn() })), + getActiveViewportId: jest.fn().mockReturnValue('ACTIVE_VIEWPORT'), +}); + +describe('useToolbar', () => { + let toolbarService: MockToolbarService; + let viewportGridService: MockViewportGridService; + let commandsManager: { run: jest.Mock }; + let servicesManager: { + services: { toolbarService: MockToolbarService; viewportGridService: MockViewportGridService }; + }; + + const renderToolbarHook = () => renderHook(() => useToolbar({ buttonSection: 'primary' } as any)); + + beforeEach(() => { + toolbarService = createToolbarService(); + viewportGridService = createViewportGridService(); + commandsManager = { run: jest.fn() }; + servicesManager = { services: { toolbarService, viewportGridService } }; + + mockedUseSystem.mockReset(); + mockedUseSystem.mockReturnValue({ + commandsManager, + servicesManager, + } as any); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('aggregates commands on interaction and defers execution to the commands manager', () => { + const stopPropagation = jest.fn(); + + const option = { + id: 'option-1', + value: 'opt-1', + commands: 'secondaryCommand', + label: 'Option 1', + }; + const ignoredOption = { + id: 'option-ignored', + value: 'ignored', + explicitRunOnly: true, + commands: ['ignoredCommand'], + }; + + toolbarService.getButtonProps.mockReturnValue({ + commands: ['primaryCommand'], + options: [option, ignoredOption], + }); + + const { result } = renderToolbarHook(); + + act(() => { + result.current.onInteraction({ + itemId: 'test-button', + viewportId: 'custom-viewport', + event: { stopPropagation }, + }); + }); + + expect(stopPropagation).toHaveBeenCalledTimes(1); + expect(toolbarService.getButtonProps).toHaveBeenCalledWith('test-button'); + + const [interactionArgs, interactionMeta] = toolbarService.recordInteraction.mock.calls[0]; + const { commands } = interactionArgs; + + expect(commands).toHaveLength(2); + commands.forEach(commandFn => expect(typeof commandFn).toBe('function')); + + (commands[0] as () => void)(); + expect(commandsManager.run).toHaveBeenLastCalledWith( + 'primaryCommand', + expect.objectContaining({ + itemId: 'test-button', + viewportId: 'custom-viewport', + event: expect.any(Object), + }) + ); + + (commands[1] as () => void)(); + expect(commandsManager.run).toHaveBeenLastCalledWith( + 'secondaryCommand', + expect.objectContaining({ + id: 'option-1', + value: 'opt-1', + options: expect.any(Array), + servicesManager, + commandsManager, + }) + ); + + expect(interactionMeta).toEqual({ refreshProps: { viewportId: 'custom-viewport' } }); + }); + + it('keeps button props immutable when aggregating commands', () => { + const stopPropagation = jest.fn(); + const option = { + id: 'option-1', + value: 'opt-1', + commands: 'secondaryCommand', + label: 'Option 1', + }; + const ignoredOption = { + id: 'option-ignored', + value: 'ignored', + explicitRunOnly: true, + commands: ['ignoredCommand'], + }; + const buttonProps = { + commands: ['primaryCommand'], + options: [option, ignoredOption], + }; + + toolbarService.getButtonProps.mockReturnValue(buttonProps); + + const { result } = renderToolbarHook(); + + act(() => { + result.current.onInteraction({ + itemId: 'test-button', + viewportId: 'custom-viewport', + event: { stopPropagation }, + }); + }); + + act(() => { + result.current.onInteraction({ + itemId: 'test-button', + viewportId: 'custom-viewport', + event: { stopPropagation }, + }); + }); + + expect(stopPropagation).toHaveBeenCalledTimes(2); + expect(buttonProps.commands).toHaveLength(1); + expect(toolbarService.recordInteraction).toHaveBeenCalledTimes(2); + }); +}); diff --git a/platform/core/src/hooks/index.ts b/platform/core/src/hooks/index.ts index eb47ffed43f..78db3cd3c8f 100644 --- a/platform/core/src/hooks/index.ts +++ b/platform/core/src/hooks/index.ts @@ -3,3 +3,5 @@ export * from './types'; export * from './useViewportRef'; export * from './useViewportSize'; export * from './useViewportMousePosition'; +export * from './useActiveToolOptions'; +export * from './useRunCommand'; diff --git a/platform/core/src/hooks/useActiveToolOptions.tsx b/platform/core/src/hooks/useActiveToolOptions.tsx new file mode 100644 index 00000000000..0aea85de223 --- /dev/null +++ b/platform/core/src/hooks/useActiveToolOptions.tsx @@ -0,0 +1,53 @@ +import { useSystem } from '../contextProviders/SystemProvider'; +import { ButtonProps } from '../services/ToolBarService/types'; +import { useToolbar } from './useToolbar'; + +type UseActiveToolOptionsProps = { + buttonSectionId: string; +}; + +export function useActiveToolOptions({ buttonSectionId }: UseActiveToolOptionsProps) { + const { servicesManager } = useSystem(); + const { toolbarService } = servicesManager.services; + + const { toolbarButtons } = useToolbar({ + buttonSection: buttonSectionId, + }); + + // Helper to check a list of buttons for an active tool. + const findActiveOptions = (buttons: any[]): unknown => { + for (const tool of buttons) { + if (tool.componentProps.isActive) { + return { + activeToolOptions: tool.componentProps.options, + activeToolButtonId: tool.componentProps.id, + }; + } + if (tool.componentProps.buttonSection) { + const nestedButtons = toolbarService.getButtonPropsInButtonSection( + tool.componentProps.buttonSection + ) as ButtonProps[]; + const activeNested = nestedButtons.find(nested => nested.isActive); + if (activeNested) { + return { + activeToolOptions: activeNested.options, + activeToolButtonId: activeNested.id, + }; + } + } + } + return null; + }; + + // Look for active tool options across all sections. + const activeOptions = toolbarButtons.reduce((activeOptions, button) => { + if (activeOptions) { + return activeOptions; + } + const sectionId = button.componentProps.buttonSection; + const buttons = sectionId ? toolbarService.getButtonSection(sectionId) : [button]; + return findActiveOptions(buttons); + }, null); + + return activeOptions ?? {}; +} diff --git a/platform/core/src/hooks/useRunCommand.tsx b/platform/core/src/hooks/useRunCommand.tsx new file mode 100644 index 00000000000..cb5c5a5928a --- /dev/null +++ b/platform/core/src/hooks/useRunCommand.tsx @@ -0,0 +1,19 @@ +import { useCallback } from 'react'; +import { useSystem } from '../contextProviders/SystemProvider'; + +/** + * Hook that provides a runCommand function for executing commands + * @returns A memoized runCommand function + */ +export function useRunCommand() { + const { commandsManager } = useSystem(); + + const runCommand = useCallback( + (commandName: string, commandOptions: Record = {}) => { + return commandsManager.runCommand(commandName, commandOptions); + }, + [commandsManager] + ); + + return runCommand; +} diff --git a/platform/core/src/hooks/useToolbar.tsx b/platform/core/src/hooks/useToolbar.tsx index 33c2fe011b0..f4a8c4bff34 100644 --- a/platform/core/src/hooks/useToolbar.tsx +++ b/platform/core/src/hooks/useToolbar.tsx @@ -1,6 +1,7 @@ import { useCallback, useEffect, useState, useMemo } from 'react'; import { useSystem } from '../contextProviders/SystemProvider'; import { ToolbarHookReturn } from './types'; +import { buildButtonCommands } from '../utils'; export function useToolbar({ buttonSection = 'primary' }: withAppTypes): ToolbarHookReturn { const { commandsManager, servicesManager } = useSystem(); @@ -24,48 +25,10 @@ export function useToolbar({ buttonSection = 'primary' }: withAppTypes): Toolbar const refreshProps = { viewportId }; const buttonProps = toolbarService.getButtonProps(args.itemId); + const commands = buildButtonCommands(buttonProps, args, { servicesManager, commandsManager }); + const computedProps = { ...buttonProps, commands }; - if (buttonProps.commands || buttonProps.options) { - const allCommands = []; - const options = buttonProps.options || []; - const itemCommands = buttonProps.commands || []; - - // Process item commands - if (itemCommands) { - Array.isArray(itemCommands) - ? allCommands.push(...itemCommands) - : allCommands.push(itemCommands); - } - - // Process commands from options - if (options.length > 0) { - options.forEach(option => { - if (!option.commands) { - return; - } - - const valueToUse = option.value; - const commands = Array.isArray(option.commands) ? option.commands : [option.commands]; - - commands.forEach(command => { - const commandOptions = { - ...option, - value: valueToUse, - options: buttonProps.options, - servicesManager: servicesManager, - commandsManager: commandsManager, - }; - - const processedCommand = () => commandsManager.run(command, commandOptions); - allCommands.push(processedCommand); - }); - }); - } - - buttonProps.commands = allCommands; - } - - toolbarService.recordInteraction({ ...args, ...buttonProps }, { refreshProps }); + toolbarService.recordInteraction({ ...args, ...computedProps }, { refreshProps }); }, [toolbarService, viewportGridService] ); diff --git a/platform/core/src/index.ts b/platform/core/src/index.ts index 134f471d25f..91c80df56c7 100644 --- a/platform/core/src/index.ts +++ b/platform/core/src/index.ts @@ -9,7 +9,7 @@ import errorHandler from './errorHandler.js'; import log from './log.js'; import object from './object.js'; import string from './string.js'; -import user from './user.js'; +import user from './user'; import utils from './utils'; import defaults from './defaults'; import * as Types from './types'; diff --git a/platform/core/src/services/CustomizationService/types.ts b/platform/core/src/services/CustomizationService/types.ts index a0e0a7c44af..71db214bdc0 100644 --- a/platform/core/src/services/CustomizationService/types.ts +++ b/platform/core/src/services/CustomizationService/types.ts @@ -45,7 +45,8 @@ export type Customization = | CommandCustomization | CodeCustomization | ComponentCustomization - | CallbackCustomization; + | CallbackCustomization + | string | number | boolean; export default Customization; diff --git a/platform/core/src/services/DicomMetadataStore/DicomMetadataStore.ts b/platform/core/src/services/DicomMetadataStore/DicomMetadataStore.ts index 79ac22b6b9f..eb321ed6805 100644 --- a/platform/core/src/services/DicomMetadataStore/DicomMetadataStore.ts +++ b/platform/core/src/services/DicomMetadataStore/DicomMetadataStore.ts @@ -2,6 +2,7 @@ import dcmjs from 'dcmjs'; import pubSubServiceInterface from '../_shared/pubSubServiceInterface'; import createStudyMetadata from './createStudyMetadata'; +import addProxyFields from '../../utils/addProxyFields'; const EVENTS = { STUDY_ADDED: 'event::dicomMetadataStore:studyAdded', @@ -49,6 +50,12 @@ const _model = { studies: [], }; +let metaDataProvider; + +function _setMetaDataProvider(metaData) { + metaDataProvider = metaData; +} + function _getStudyInstanceUIDs() { return _model.studies.map(aStudy => aStudy.StudyInstanceUID); } @@ -70,7 +77,7 @@ function _getStudy(StudyInstanceUID) { * @returns {*} A series object */ function _getSeries(StudyInstanceUID, SeriesInstanceUID) { - if(!StudyInstanceUID) { + if (!StudyInstanceUID) { const series = _model.studies.map(study => study.series).flat(); return series.find(aSeries => aSeries.SeriesInstanceUID === SeriesInstanceUID); } @@ -101,7 +108,17 @@ function _getInstance(StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID) { return series.getInstance(SOPInstanceUID); } +/** + * Gets the frame module from the OHIF metadata provider, and then + * uses the study/series/instance uids to get the instance data. + */ function _getInstanceByImageId(imageId) { + const metadataResult = metaDataProvider?.get('frameModule', imageId); + if (metadataResult) { + const { studyInstanceUID, seriesInstanceUID, sopInstanceUID } = metadataResult; + return _getInstance(studyInstanceUID, seriesInstanceUID, sopInstanceUID); + } + console.warn('No metadata result found for', imageId, 'looking through instances'); for (const study of _model.studies) { for (const series of study.series) { for (const instance of series.instances) { @@ -176,7 +193,7 @@ const BaseImplementation = { naturalizedDataset = dicomJSONDataset; } - const { StudyInstanceUID } = naturalizedDataset; + const { StudyInstanceUID, NumberOfFrames: numberOfFrames } = naturalizedDataset; let study = _model.studies.find(study => study.StudyInstanceUID === StudyInstanceUID); @@ -185,10 +202,20 @@ const BaseImplementation = { study = _model.studies[_model.studies.length - 1]; } + naturalizedDataset = + numberOfFrames > 1 ? addProxyFields(naturalizedDataset) : naturalizedDataset; + study.addInstanceToSeries(naturalizedDataset); }, addInstances(instances, madeInClient = false) { - const { StudyInstanceUID, SeriesInstanceUID } = instances[0]; + const { StudyInstanceUID, SeriesInstanceUID, NumberOfFrames } = instances[0]; + // For multiframe images (NumberOfFrames > 1), wrap each instance with a metadata proxy + // to enable fallback access for key image plane fields. This allows fields like + // ImagePositionPatient, ImageOrientationPatient, and PixelSpacing to be transparently + // resolved from parent or shared metadata if they are not directly present on the instance directly + if (NumberOfFrames > 1) { + instances = instances.map(instance => addProxyFields(instance)); + } let study = _model.studies.find(study => study.StudyInstanceUID === StudyInstanceUID); @@ -279,6 +306,7 @@ const BaseImplementation = { getInstance: _getInstance, getInstanceByImageId: _getInstanceByImageId, updateMetadataForSeries: _updateMetadataForSeries, + setMetaDataProvider: _setMetaDataProvider, }; const DicomMetadataStore = Object.assign( // get study diff --git a/platform/core/src/services/DisplaySetService/DisplaySetService.ts b/platform/core/src/services/DisplaySetService/DisplaySetService.ts index 3bcb79f0a6e..194eb00119f 100644 --- a/platform/core/src/services/DisplaySetService/DisplaySetService.ts +++ b/platform/core/src/services/DisplaySetService/DisplaySetService.ts @@ -1,5 +1,5 @@ import { ExtensionManager } from '../../extensions'; -import { DisplaySet, InstanceMetadata } from '../../types'; +import { DisplaySet, InstanceMetadata, ReferencedSeriesSequence } from '../../types'; import { PubSubService } from '../_shared/pubSubServiceInterface'; import EVENTS from './EVENTS'; @@ -115,16 +115,52 @@ export default class DisplaySetService extends PubSubService { return this.activeDisplaySets; } + /** + * Gets the set of display sets with this series instance UID + * + * WARNING: Do not use this method when you have a referenced series sequence + * as this method does NOT check sop instances. Instead, use getDisplaySetsForReference + * to get those with the correct sop instances in them. + */ public getDisplaySetsForSeries = (seriesInstanceUID: string): DisplaySet[] => { return [...displaySetCache.values()].filter( displaySet => displaySet.SeriesInstanceUID === seriesInstanceUID ); }; + /** + * Given a reference to a series/sop, returns the set of display sets + * containing an instance from the references. + */ + public getDisplaySetsForReferences = ( + references: ReferencedSeriesSequence | ReferencedSeriesSequence[] + ): DisplaySet[] => { + const mapSeriesReferences = new Map>(); + const referenceArr = Array.isArray(references) ? references : [references]; + for (const seriesRef of referenceArr) { + const { SeriesInstanceUID, ReferencedInstanceSequence } = seriesRef; + if (!mapSeriesReferences.has(SeriesInstanceUID)) { + mapSeriesReferences.set(SeriesInstanceUID, new Set()); + } + const sops = mapSeriesReferences.get(SeriesInstanceUID); + for (const sopReference of ReferencedInstanceSequence) { + sops.add(sopReference.ReferencedSOPInstanceUID); + } + } + + return [...displaySetCache.values()].filter(displaySet => { + const sopReferences = mapSeriesReferences.get(displaySet.SeriesInstanceUID); + if (!sopReferences || !displaySet.instances) { + return; + } + return displaySet.instances.some(instance => sopReferences.has(instance.SOPInstanceUID)); + }); + }; + public getDisplaySetForSOPInstanceUID( sopInstanceUID: string, seriesInstanceUID: string, - frameNumber?: number + _frameNumber?: number ): DisplaySet { const displaySets = seriesInstanceUID ? this.getDisplaySetsForSeries(seriesInstanceUID) @@ -428,12 +464,17 @@ export default class DisplaySetService extends PubSubService { /** * * @param sortFn function to sort the display sets - * @param direction direction to sort the display sets + * @param direction direction to sort the display sets. Ascending means + * increasing in value, which will typically put the lowest series numbers + * first, with low priority display sets last with newest first. + * The meaning of this flag may change to leave the image/non-image display + * set sorting alone and only affect sorting within groups, or have additional + * values for specific changes to the sort. * @returns void */ public sortDisplaySets( sortFn: (a: DisplaySet, b: DisplaySet) => number, - direction: string, + direction: 'ascending' | 'descending' = 'ascending', suppressEvent = false ): void { this.activeDisplaySets.sort(sortFn); diff --git a/platform/core/src/services/HangingProtocolService/HangingProtocolService.ts b/platform/core/src/services/HangingProtocolService/HangingProtocolService.ts index 474ee9f51ba..368992cf773 100644 --- a/platform/core/src/services/HangingProtocolService/HangingProtocolService.ts +++ b/platform/core/src/services/HangingProtocolService/HangingProtocolService.ts @@ -11,6 +11,7 @@ import { isDisplaySetFromUrl, sopInstanceLocation } from './custom-attribute/isD import numberOfDisplaySetsWithImages from './custom-attribute/numberOfDisplaySetsWithImages'; import seriesDescriptionsFromDisplaySets from './custom-attribute/seriesDescriptionsFromDisplaySets'; import uuidv4 from '../../utils/uuidv4'; +import { getUniqueAttributeFromList } from './lib/getUniqueAttributeFromList'; type Protocol = HangingProtocol.Protocol | HangingProtocol.ProtocolGenerator; @@ -77,15 +78,15 @@ export default class HangingProtocolService extends PubSubService { }, ModalitiesInStudy: { name: 'Gets the array of the modalities for the series', - callback: metadata => - metadata.ModalitiesInStudy ?? - (metadata.series || []).reduce((prev, curr) => { - const { Modality } = curr; - if (Modality && prev.indexOf(Modality) == -1) { - prev.push(Modality); - } - return prev; - }, []), + callback: metadata => { + if (metadata.ModalitiesInStudy?.length > 0) { + return metadata.ModalitiesInStudy; + } + if (Array.isArray(metadata.series)) { + return getUniqueAttributeFromList(metadata.series, 'Modality'); + } + return []; + }, }, isReconstructable: { name: 'Checks if the display set is reconstructable', diff --git a/platform/core/src/services/HangingProtocolService/lib/getUniqueAttributeFromList.ts b/platform/core/src/services/HangingProtocolService/lib/getUniqueAttributeFromList.ts new file mode 100644 index 00000000000..4ea3af8674a --- /dev/null +++ b/platform/core/src/services/HangingProtocolService/lib/getUniqueAttributeFromList.ts @@ -0,0 +1,19 @@ +/** + * Returns an array of unique values for the given attribute from a series array. + * If the attribute is not present on the series, attempts to get it from the first instance. + * @param {Array} series - The series array to extract attributes from. + * @param {string} attribute - The attribute name to extract. + * @returns {Array} Array of unique attribute values. + */ +export function getUniqueAttributeFromList(series, attribute) { + return series.reduce((prev, curr) => { + let value = curr[attribute]; + if (!value && curr.instances && curr.instances[0]) { + value = curr.instances[0][attribute]; + } + if (value && prev.indexOf(value) === -1) { + prev.push(value); + } + return prev; + }, []); +} diff --git a/platform/core/src/services/MeasurementService/MeasurementService.ts b/platform/core/src/services/MeasurementService/MeasurementService.ts index f69dbe7e318..7bde5c587a1 100644 --- a/platform/core/src/services/MeasurementService/MeasurementService.ts +++ b/platform/core/src/services/MeasurementService/MeasurementService.ts @@ -1,6 +1,7 @@ import log from '../../log'; import guid from '../../utils/guid'; import { PubSubService } from '../_shared/pubSubServiceInterface'; +import { DicomMetadataStore } from '../DicomMetadataStore/DicomMetadataStore'; /** * Measurement source schema @@ -101,6 +102,8 @@ enum MeasurementChangeType { export type MeasurementFilter = (measurement) => boolean; +const sourceMissing = new Set(); + /** * MeasurementService class that supports source management and measurement management. * Sources can be any library that can provide "annotations" (e.g. cornerstone-tools, cornerstone, etc.) @@ -127,7 +130,6 @@ class MeasurementService extends PubSubService { public readonly VALUE_TYPES = VALUE_TYPES; private measurements = new Map(); - private unmappedMeasurements = new Map(); private isMeasurementDeletedIndividually: boolean; private sources = {}; @@ -434,10 +436,13 @@ class MeasurementService extends PubSubService { const internalUID = data.uid || guid(); - const annotationData = data.annotation.data; + const { + annotation: { predecessorImageId, data: annotationData }, + } = data; const newMeasurement = { finding: annotationData.finding, + predecessorImageId, findingSites: annotationData.findingSites, site: annotationData.findingSites?.[0], ...measurement, @@ -494,7 +499,11 @@ class MeasurementService extends PubSubService { mapping => mapping.annotationType === annotationType ); if (!sourceMapping) { - console.log('No source mapping', source.uid, annotationType, source); + if (!sourceMissing.has(source.uid) ) { + console.log('No source mapping', source.uid, annotationType, source); + sourceMissing.add(source.uid); + } + this.addUnmappedMeasurement(sourceAnnotationDetail, source); return; } const { toMeasurementSchema } = sourceMapping; @@ -507,16 +516,7 @@ class MeasurementService extends PubSubService { measurement.source = source; } catch (error) { - // Todo: handle other - this.unmappedMeasurements.set(sourceAnnotationDetail.uid, { - ...sourceAnnotationDetail, - source: { - name: source.name, - version: source.version, - uid: source.uid, - }, - }); - + this.addUnmappedMeasurement(sourceAnnotationDetail, source); console.log('Failed to map', error); throw new Error( `Failed to map '${sourceInfo}' measurement for annotationType ${annotationType}: ${error.message}` @@ -577,14 +577,102 @@ class MeasurementService extends PubSubService { return newMeasurement.uid; } + /** + * Recursively searches for any attribute at any level in the object + * @param {any} obj The object to search + * @param {string} attributeName The name of the attribute to find + * @returns {any} The attribute value if found, undefined otherwise + */ + private findAttributeRecursively(obj: any, attributeName: string): any { + if (!obj || typeof obj !== 'object') { + return undefined; + } + if (obj[attributeName]) { + return obj[attributeName]; + } + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + const result = this.findAttributeRecursively(obj[key], attributeName); + if (result) { + return result; + } + } + } + return undefined; + } + + /** + * Adds an unmapped measurement to the measurement service. + * + * @param {any} sourceAnnotationDetail The source annotation detail + * @param {any} source The source + */ + private addUnmappedMeasurement(sourceAnnotationDetail: any, source: any) { + if (sourceAnnotationDetail.annotation?.invalidated === true) { + console.log('Measurement is invalidated, skipping...', sourceAnnotationDetail); + return; + } + + if (sourceAnnotationDetail.annotation?.isPreview === true) { + console.log('Measurement is preview, skipping...', sourceAnnotationDetail); + return; + } + + const metadata = this.findAttributeRecursively(sourceAnnotationDetail, 'metadata'); + const label = this.findAttributeRecursively(sourceAnnotationDetail, 'label'); + const referencedImageId = this.findAttributeRecursively( + sourceAnnotationDetail, + 'referencedImageId' + ); + const displaySetInstanceUID = this.findAttributeRecursively( + sourceAnnotationDetail, + 'displaySetInstanceUID' + ); + + const measurement = { + ...sourceAnnotationDetail, + isUnmapped: true, + statusTooltip: 'This measurement is not compatible with this application', + source: { + name: source.name, + version: source.version, + uid: source.uid, + }, + }; + + if (metadata) { + measurement.metadata = metadata; + } + + if (label) { + measurement.label = label; + } + + if (referencedImageId) { + measurement.referencedImageId = referencedImageId; + const instance = DicomMetadataStore.getInstanceByImageId(referencedImageId); + if (!instance) { + console.warn("Didn't find instance for", referencedImageId); + } else { + measurement.referenceStudyUID = instance.StudyInstanceUID; + measurement.referenceSeriesUID = instance.SeriesInstanceUID; + } + } + + if (displaySetInstanceUID) { + measurement.displaySetInstanceUID = displaySetInstanceUID; + } + + this.measurements.set(sourceAnnotationDetail.uid, measurement); + } + /** * Removes a measurement and broadcasts the removed event. * * @param {string} measurementUID The measurement uid */ remove(measurementUID: string): void { - const measurement = - this.measurements.get(measurementUID) || this.unmappedMeasurements.get(measurementUID); + const measurement = this.measurements.get(measurementUID); if (!measurementUID || !measurement) { console.debug(`No uid provided, or unable to find measurement by uid.`); @@ -593,7 +681,6 @@ class MeasurementService extends PubSubService { const source = measurement.source; - this.unmappedMeasurements.delete(measurementUID); this.measurements.delete(measurementUID); this.isMeasurementDeletedIndividually = true; this._broadcastEvent(this.EVENTS.MEASUREMENT_REMOVED, { @@ -608,15 +695,13 @@ class MeasurementService extends PubSubService { removeMany(measurementUIDs: string[]): void { const measurements = []; for (const measurementUID of measurementUIDs) { - const measurement = - this.measurements.get(measurementUID) || this.unmappedMeasurements.get(measurementUID); + const measurement = this.measurements.get(measurementUID); if (!measurementUID || !measurement) { console.debug(`No uid provided, or unable to find measurement by uid.`); continue; } - this.unmappedMeasurements.delete(measurementUID); this.measurements.delete(measurementUID); measurements.push(measurement); } @@ -634,11 +719,7 @@ class MeasurementService extends PubSubService { public clearMeasurements(filter?: MeasurementFilter) { // Make a copy of the measurements const toClear = this.getMeasurements(filter); - const unmappedClear = filter - ? [...this.unmappedMeasurements.values()].filter(filter) - : this.unmappedMeasurements; - const measurements = [...toClear, ...unmappedClear]; - unmappedClear.forEach(measurement => this.unmappedMeasurements.delete(measurement.uid)); + const measurements = [...toClear]; toClear.forEach(measurement => this.measurements.delete(measurement.uid)); this._broadcastEvent(this.EVENTS.MEASUREMENTS_CLEARED, { measurements }); } diff --git a/platform/core/src/services/StudyPrefetcherService/StudyPrefetcherService.ts b/platform/core/src/services/StudyPrefetcherService/StudyPrefetcherService.ts index 398974eb333..a6862f4859e 100644 --- a/platform/core/src/services/StudyPrefetcherService/StudyPrefetcherService.ts +++ b/platform/core/src/services/StudyPrefetcherService/StudyPrefetcherService.ts @@ -291,6 +291,11 @@ class StudyPrefetcherService extends PubSubService { } const activeViewport = viewports.get(activeViewportId); + + if (!activeViewport) { + return; + } + const displaySetUpdated = this._setActiveDisplaySetsUIDs(activeViewport.displaySetInstanceUIDs); if (forceRestart || displaySetUpdated) { diff --git a/platform/core/src/services/ToolBarService/ToolbarService.ts b/platform/core/src/services/ToolBarService/ToolbarService.ts index a020a45ab28..ed2a303d88d 100644 --- a/platform/core/src/services/ToolBarService/ToolbarService.ts +++ b/platform/core/src/services/ToolBarService/ToolbarService.ts @@ -2,7 +2,7 @@ import { CommandsManager } from '../../classes'; import { ExtensionManager } from '../../extensions'; import { PubSubService } from '../_shared/pubSubServiceInterface'; import type { RunCommand } from '../../types/Command'; -import { Button, ButtonProps, EvaluateFunction, EvaluatePublic } from './types'; +import { Button, ButtonOptions, ButtonProps, EvaluateFunction, EvaluatePublic } from './types'; const EVENTS = { TOOL_BAR_MODIFIED: 'event::toolBarService:toolBarModified', @@ -38,7 +38,10 @@ export const TOOLBAR_SECTIONS = { }, // mode specific - segmentationToolbox: 'segmentationToolbox', + labelMapSegmentationToolbox: 'labelMapSegmentationToolbox', + contourSegmentationToolbox: 'contourSegmentationToolbox', + labelMapSegmentationUtilities: 'labelMapSegmentationUtilities', + contourSegmentationUtilities: 'contourSegmentationUtilities', dynamicToolbox: 'dynamic-toolbox', roiThresholdToolbox: 'ROIThresholdToolbox', }; @@ -63,6 +66,8 @@ export default class ToolbarService extends PubSubService { }, }; + public static TOOLBAR_SECTIONS = TOOLBAR_SECTIONS; + /** * Access to predefined toolbar sections for autocomplete support */ @@ -312,12 +317,22 @@ export default class ToolbarService extends PubSubService { : undefined; // Check hideWhenDisabled at both evaluateProps level and props level const hideWhenDisabled = evaluateProps?.hideWhenDisabled || props.hideWhenDisabled; + + // Visibility is first determined by the evaluate function. If it is not returned from there, + // then we check hideWhenDisabled and disabled to determine visibility. + const visible = + evaluated?.visible === undefined + ? hideWhenDisabled && evaluated?.disabled + ? false + : true + : evaluated?.visible; + const updatedProps = { ...props, ...evaluated, disabled: evaluated?.disabled || false, - visible: hideWhenDisabled && evaluated?.disabled ? false : true, - className: evaluated?.className || '', + visible, + className: evaluated?.className || props?.className || '', isActive: evaluated?.isActive, // isActive will be undefined for buttons without this prop }; evaluationResults.set(button.id, updatedProps); @@ -568,7 +583,8 @@ export default class ToolbarService extends PubSubService { btn.component = buttonType?.defaultComponent; } - if (!buttonType) { + if (!buttonType && !btn.component) { + console.warn(`Neither button type nor a component found for button: ${id}`); return; } @@ -576,7 +592,7 @@ export default class ToolbarService extends PubSubService { const { id: buttonId, props: componentProps } = btn; - const createEnhancedOptions = (options, itemId) => { + const createEnhancedOptions = (options: ButtonOptions[], itemId) => { const optionsToUse = Array.isArray(options) ? options : [options]; const toolProps = this.getButtonProps(itemId); @@ -817,4 +833,14 @@ export default class ToolbarService extends PubSubService { return { align: 'start', side: 'bottom' }; } } + + /** + * Retrieves an option by its ID from a button's options array. + * @param button - The button object. + * @param optionId - The ID of the option to retrieve. + * @returns The option with the specified ID. + */ + public getOptionById(button: Button, optionId: string) { + return button.props.options?.find(option => option.id === optionId); + } } diff --git a/platform/core/src/services/ToolBarService/types.ts b/platform/core/src/services/ToolBarService/types.ts index 6e091f1f91d..df8a21b1bd3 100644 --- a/platform/core/src/services/ToolBarService/types.ts +++ b/platform/core/src/services/ToolBarService/types.ts @@ -34,21 +34,25 @@ export type EvaluateObject = { export type ButtonOptions = { id: string; - type: 'range' | 'radio' | 'double-range' | 'custom'; + type: 'range' | 'radio' | 'double-range' | 'custom' | 'checkbox' | 'select' | 'button'; name?: string; + tooltip?: string; min?: number; max?: number; step?: number; value?: number | number[] | string; - commands?: (value: unknown) => void; + values: Array<{ value: string; label: string }>; + commands?: RunCommand; condition?: (props: Record) => boolean; children?: React.ReactNode | (() => React.ReactNode); options?: Array<{ value: string; label: string }>; + explicitRunOnly?: boolean; }; export type ButtonProps = { id: string; icon: string; + isActive?: boolean; label: string; tooltip?: string; commands?: RunCommand; @@ -58,6 +62,7 @@ export type ButtonProps = { listeners?: Record; options?: ButtonOptions[]; buttonSection?: string | boolean; + isActive?: boolean; }; export type Button = { diff --git a/platform/core/src/services/UINotificationService/index.ts b/platform/core/src/services/UINotificationService/index.ts index 8e88374a3af..8bfd5b3ffae 100644 --- a/platform/core/src/services/UINotificationService/index.ts +++ b/platform/core/src/services/UINotificationService/index.ts @@ -4,6 +4,7 @@ const serviceImplementation = { console.debug('show() NOT IMPLEMENTED'); return null; }, + _customComponent: null, }; type ToastType = 'success' | 'error' | 'info' | 'warning' | 'loading'; @@ -17,21 +18,38 @@ class UINotificationService { }, }; + /** + * This provides flexibility in customizing the Notification default component + * + * @returns {React.Component} + */ + getCustomComponent() { + return serviceImplementation._customComponent; + } + /** * * * @param {*} { * hide: hideImplementation, * show: showImplementation, + * component: componentImplementation * } */ - public setServiceImplementation({ hide: hideImplementation, show: showImplementation }): void { + public setServiceImplementation({ + hide: hideImplementation, + show: showImplementation, + customComponent: customComponentImplementation, + }): void { if (hideImplementation) { serviceImplementation._hide = hideImplementation; } if (showImplementation) { serviceImplementation._show = showImplementation; } + if (customComponentImplementation) { + serviceImplementation._customComponent = customComponentImplementation; + } } /** diff --git a/platform/core/src/types/DisplaySet.ts b/platform/core/src/types/DisplaySet.ts index c5ec90038a7..3e39e9aeae6 100644 --- a/platform/core/src/types/DisplaySet.ts +++ b/platform/core/src/types/DisplaySet.ts @@ -1,5 +1,15 @@ import { InstanceMetadata } from './StudyMetadata'; +export type ReferencedInstance = { + ReferencedSOPClassUID: string; + ReferencedSOPInstanceUID: string; +}; + +export type ReferencedSeriesSequence = { + SeriesInstanceUID: string; + ReferencedInstanceSequence: ReferencedInstance[]; +}; + export type DisplaySet = { displaySetInstanceUID: string; instances: InstanceMetadata[]; @@ -16,6 +26,8 @@ export type DisplaySet = { label?: string; /** Flag indicating if this is an overlay display set (e.g., SEG, RTSTRUCT) */ isOverlayDisplaySet?: boolean; + /** Flag indicating this is a derived dataset */ + isDerived?: boolean; /** flag indicating if it supports window level */ supportsWindowLevel?: boolean; @@ -49,15 +61,29 @@ export type DisplaySet = { SeriesTime?: string; instance?: InstanceMetadata; + /** + * The predecessor image id refers to the SOP instance that is currently loaded + * into this display set for SEG/SR/RTSTRUCT type values. The name is chosen + * for consistency when this value is used as the origin instance + * for saving a new instance intended to replace this instance where the + * new instance has a "predecessor sequence". + */ + predecessorImageId?: string; + + /** + * isLoaded is used for display sets containing a load operation that + * is required before the display set can be shown. This is separate from + * isHydrated, which means it is loaded into view. + */ + isLoaded?: boolean; isHydrated?: boolean; isRehydratable?: boolean; /** - * Loads the display set. - * @param headers - The headers to use for the request. - * @returns A promise that resolves when the display set is loaded. + * The name of the comparison function (for sort) to use when comparing display + * sets that are coming from same series instanceUID. */ - load: ({ headers }: { headers?: unknown }) => Promise; + compareSameSeries?: string; }; export type DisplaySetSeriesMetadataInvalidatedEvent = { diff --git a/platform/core/src/types/RequestHeaders.ts b/platform/core/src/types/RequestHeaders.ts new file mode 100644 index 00000000000..2ed11722b36 --- /dev/null +++ b/platform/core/src/types/RequestHeaders.ts @@ -0,0 +1,35 @@ +/** + * Interface to clearly present the expected fields to linters when building a request header. + */ +export interface HeadersInterface { + /** + * Request Accept options. For example, + * `['multipart/related; type=application/octet-stream; transfer-syntax=1.2.840.10008.1.2.1.99',]`. + * + * Defines to the server the formats it can use to deliver data to us. + */ + Accept?: string[]; + /** + * Request Authorization field. It can be overridden with the `requestOptions.auth` config item. + * Contains the authorization credentials or tokens necessary to authorize the request with the + * server. + */ + Authorization?: string; +} + +/** + * Interface to clearly present the expected fields to linters when passing the configuration's + * requestOptions struct. + */ +export interface RequestOptions { + requestOptions?: { + /** + * Authentication options to include. Can be a function. + */ + auth?: Function | string; + /** + * Authentication token. Satisfies the test requirement? + */ + token?: string; + } +} diff --git a/platform/core/src/types/index.ts b/platform/core/src/types/index.ts index e01e64f710f..42a9a356578 100644 --- a/platform/core/src/types/index.ts +++ b/platform/core/src/types/index.ts @@ -8,6 +8,7 @@ import type { BaseDataSourceConfigurationAPIItem, } from './DataSourceConfigurationAPI'; +export type * from '../services/ToolBarService/types'; export type * from '../services/ViewportGridService'; export type * from '../services/CustomizationService/types'; // Separate out some generic types diff --git a/platform/core/src/user.js b/platform/core/src/user.js deleted file mode 100644 index 9e5df8d0690..00000000000 --- a/platform/core/src/user.js +++ /dev/null @@ -1,13 +0,0 @@ -// These should be overridden by the implementation -let user = { - userLoggedIn: () => false, - getUserId: () => null, - getName: () => null, - getAccessToken: () => null, - login: () => new Promise((resolve, reject) => reject()), - logout: () => new Promise((resolve, reject) => reject()), - getData: key => null, - setData: (key, value) => null, -}; - -export default user; diff --git a/platform/core/src/user.ts b/platform/core/src/user.ts new file mode 100644 index 00000000000..f7acd7d05e5 --- /dev/null +++ b/platform/core/src/user.ts @@ -0,0 +1,31 @@ +/** + * Global user information, to be replaced with a specific version which + * applies the methods. + */ +export let user = { + userLoggedIn: (): boolean => false, + getUserId: () => null, + getName: () => null, + getAccessToken: () => null, + login: () => new Promise((resolve, reject) => reject()), + logout: () => new Promise((resolve, reject) => reject()), + getData: key => null, + setData: (key, value) => null, +}; + +/** + * Interface to clearly present the expected fields to linters when passing the user account + * struct. + */ +export interface UserAccountInterface { + userLoggedIn?: () => boolean; + getUserId?: () => null; + getName?: () => null; + getAccessToken?: () => null; + login?: () => Promise; + logout?: () => Promise; + getData?: (key: any) => null; + setData?: (key: any, value: any) => null; +} + +export default user; diff --git a/platform/core/src/utils/addProxyFields.js b/platform/core/src/utils/addProxyFields.js new file mode 100644 index 00000000000..2d5dfb798da --- /dev/null +++ b/platform/core/src/utils/addProxyFields.js @@ -0,0 +1,64 @@ +/** + * fieldProxies has the set of fields which are proxied on a per-field basis + * to the child element. + */ +export const fieldProxies = [ + 'ImagePositionPatient', + 'ImageOrientationPatient', + 'PixelSpacing', + // more fields as necessary +]; + +const METADATA_PROXY_FLAG = Symbol('isMetadataProxy'); + +/** + * Adds proxy properties for a limited number of values which are sometimes + * defined in multiframe instance data. + * + * If a requested field is in `metadataFieldsToWrap`: + * - It first tries to return the value from the current instance. + * - If not found, it attempts to retrieve the value from the `_parentInstance`. + * - If still not found, it checks `_parentInstance._shared`. + * - If none exist, it returns `undefined`. + * + * For all other properties, it behaves like a regular property access. + * + * This allows graceful fallback access for DICOM metadata values that might be spread + * across nested or shared metadata structures. + * + * @param {Object} instance - The target instance object to wrap. + * @returns {Proxy} A proxy-wrapped instance with custom field resolution behavior. + */ +export function addProxyFields(instance) { + // Skip wrapping if already wrapped + if (!instance || instance[METADATA_PROXY_FLAG]) { + return instance; + } + + for(const fieldProxy of fieldProxies) { + if( fieldProxy in instance) { + continue; + } + Object.defineProperty(instance,fieldProxy, { + configurable: true, + enumerable: true, + get: () => { + return instance._parentInstance?.[fieldProxy] ?? instance._parentInstance?._shared?.[fieldProxy]; + }, + set: (value) => { + Object.defineProperty(instance,fieldProxy, { + writable: true, + enumerable: true, + value, + }) + } + }); + } + + // Mark this proxy to avoid double wrapping + Object.defineProperty(instance,METADATA_PROXY_FLAG, { value: true }); + + return instance; +} + +export default addProxyFields; diff --git a/platform/core/src/utils/buildButtonCommands.ts b/platform/core/src/utils/buildButtonCommands.ts new file mode 100644 index 00000000000..033dfdb8229 --- /dev/null +++ b/platform/core/src/utils/buildButtonCommands.ts @@ -0,0 +1,40 @@ +import { ButtonProps, RunCommand } from '../types'; + +const toArray = (value?: T | T[]): T[] => + Array.isArray(value) ? value : value != null ? [value] : []; + +export const buildButtonCommands = ( + buttonProps: ButtonProps, + baseArgs: Record, + { servicesManager, commandsManager }: AppTypes.Managers +): Array<() => unknown> => { + const allCommands: Array<() => unknown> = []; + + // 1) normalize item-level commands + for (const command of toArray(buttonProps.commands as RunCommand)) { + allCommands.push(() => commandsManager.run(command, baseArgs)); + } + + // 2) normalize option-level commands + for (const option of toArray(buttonProps.options)) { + const shouldSkip = !option?.commands || option.explicitRunOnly; + if (shouldSkip) { + continue; + } + + const valueToUse = option.value; + for (const command of toArray(option.commands)) { + const commandOptions = { + ...option, + value: valueToUse, + options: buttonProps.options, + servicesManager, + commandsManager, + }; + + allCommands.push(() => commandsManager.run(command, commandOptions)); + } + } + + return allCommands; +}; diff --git a/platform/core/src/utils/createStudyBrowserTabs.ts b/platform/core/src/utils/createStudyBrowserTabs.ts index 09bdf5afa46..aea2a5e5d63 100644 --- a/platform/core/src/utils/createStudyBrowserTabs.ts +++ b/platform/core/src/utils/createStudyBrowserTabs.ts @@ -1,4 +1,22 @@ import { useSystem } from '../contextProviders/SystemProvider'; +import i18n from 'i18next'; +import { seriesSortCriteria } from './sortStudy'; + + +/** + * Tab properties that drive which tab group is used for thumbnail display. + */ +export type TabProp = { + name: string; + label: string; + studies: any[]; +}; + +/** + * Collection of tab properties with studies presorted depending on tab mod. + * This is used in deciding what thumbnails to show. + */ +export type TabsProps = TabProp[]; /** * @@ -11,7 +29,7 @@ import { useSystem } from '../contextProviders/SystemProvider'; * @param {number} studyDisplayList.numInstances * @param {object[]} displaySets * @param {number} recentTimeframe - The number of milliseconds to consider a study recent - * @returns tabs - The prop object expected by the StudyBrowser component + * @returns {TabsProps} tabs - The prop object expected by the StudyBrowser component */ export function createStudyBrowserTabs( @@ -19,9 +37,9 @@ export function createStudyBrowserTabs( studyDisplayList, displaySets, recentTimeframeMS = 31536000000 -) { +): TabsProps { const { servicesManager } = useSystem(); - const { displaySetService } = servicesManager.services; + const { displaySetService, customizationService } = servicesManager.services; const shouldSortBySeriesUID = process.env.TEST_ENV === 'true'; const primaryStudies = []; @@ -33,17 +51,16 @@ export function createStudyBrowserTabs( ); // sort them by seriesInstanceUID - let sortedDisplaySets; - if (shouldSortBySeriesUID) { - sortedDisplaySets = displaySetsForStudy.sort((a, b) => { - const displaySetA = displaySetService.getDisplaySetByUID(a.displaySetInstanceUID); - const displaySetB = displaySetService.getDisplaySetByUID(b.displaySetInstanceUID); - - return displaySetA.SeriesInstanceUID.localeCompare(displaySetB.SeriesInstanceUID); - }); - } else { - sortedDisplaySets = displaySetsForStudy; - } + const sortCriteria = shouldSortBySeriesUID + ? seriesSortCriteria.compareSeriesUID + : (customizationService.getCustomization('sortingCriteria') as (a, b) => number); + const sortedDisplaySets = displaySetsForStudy.sort((a, b) => { + const displaySetA = displaySetService.getDisplaySetByUID(a.displaySetInstanceUID); + const displaySetB = displaySetService.getDisplaySetByUID(b.displaySetInstanceUID); + return sortCriteria(displaySetA, displaySetB); + }); + + // return displaySetA.SeriesInstanceUID.localeCompare(displaySetB.SeriesInstanceUID); const tabStudy = Object.assign({}, study, { displaySets: sortedDisplaySets, @@ -83,17 +100,17 @@ export function createStudyBrowserTabs( const tabs = [ { name: 'primary', - label: 'Primary', + label: i18n.t('StudyBrowser:Primary'), studies: primaryStudies.sort((studyA, studyB) => _byDate(studyA.date, studyB.date)), }, { name: 'recent', - label: 'Recent', + label: i18n.t('StudyBrowser:Recent'), studies: recentStudies.sort((studyA, studyB) => _byDate(studyA.date, studyB.date)), }, { name: 'all', - label: 'All', + label: i18n.t('StudyBrowser:All'), studies: allStudies.sort((studyA, studyB) => _byDate(studyA.date, studyB.date)), }, ]; diff --git a/platform/core/src/utils/downloadBlob.ts b/platform/core/src/utils/downloadBlob.ts new file mode 100644 index 00000000000..753886470b4 --- /dev/null +++ b/platform/core/src/utils/downloadBlob.ts @@ -0,0 +1,36 @@ +/** + * Converts a blob to a URL and downloads immediate + */ +export function downloadBlob(content, options?) { + const url = URL.createObjectURL(content); + downloadUrl(url, options); + URL.revokeObjectURL(url); +} + +/** + * Trigger file download from an array buffer + * @param buffer + * @param filename + */ +export function downloadDicom(buffer: ArrayBuffer, options) { + const blob = new Blob([buffer], { type: 'application/dicom' }); + downloadBlob(blob, options); +} + +/** + * Downloads a URL + */ +export function downloadUrl(url, options?) { + const link = document.createElement('a'); + link.setAttribute('href', url); + const filename = options?.filename || 'file.dcm'; + link.setAttribute('download', filename); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); +} + +export function downloadCsv(csvString: string, options?) { + const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;' }); + downloadBlob(blob, options); +} diff --git a/platform/core/src/utils/downloadCSVReport.js b/platform/core/src/utils/downloadCSVReport.js index 70adf9f3b21..3c7026f9a43 100644 --- a/platform/core/src/utils/downloadCSVReport.js +++ b/platform/core/src/utils/downloadCSVReport.js @@ -1,3 +1,4 @@ +import { downloadUrl } from './downloadBlob'; import { DicomMetadataStore } from '../services/DicomMetadataStore/DicomMetadataStore'; import formatPN from './formatPN'; @@ -97,10 +98,5 @@ function _getCommonRowItems(measurement, seriesMetadata) { function _createAndDownloadFile(csvContent) { const encodedUri = encodeURI(csvContent); - - const link = document.createElement('a'); - link.setAttribute('href', encodedUri); - link.setAttribute('download', 'MeasurementReport.csv'); - document.body.appendChild(link); - link.click(); + downloadUrl(encodedUri, { filename: 'MeasurementReport.csv' }); } diff --git a/platform/core/src/utils/formatDate.js b/platform/core/src/utils/formatDate.js index 866a9285af9..759317518fd 100644 --- a/platform/core/src/utils/formatDate.js +++ b/platform/core/src/utils/formatDate.js @@ -8,7 +8,17 @@ import i18n from 'i18next'; * @param {string} format Desired date format * @returns {string} Formatted date */ -export default (date, format = i18n.t('Common:localDateFormat','DD-MMM-YYYY')) => { - // moment(undefined) returns the current date, so return the empty string instead - return date ? moment(date).format(format) : ''; +export default (date, format = i18n.t('Common:localDateFormat', 'DD-MMM-YYYY')) => { + if (!date) { + return ''; + } + + const locale = i18n.language || 'en'; + const parsed = moment(date, ['YYYYMMDD', 'YYYY.MM.DD'], true); + + if (!parsed.isValid()) { + return moment(date).locale(locale).format(format); + } + + return parsed.locale(locale).format(format); }; diff --git a/platform/core/src/utils/generateAcceptHeader.ts b/platform/core/src/utils/generateAcceptHeader.ts index c384411a3f2..c20778c4fb3 100644 --- a/platform/core/src/utils/generateAcceptHeader.ts +++ b/platform/core/src/utils/generateAcceptHeader.ts @@ -1,5 +1,5 @@ const generateAcceptHeader = ( - configAcceptHeader = [], + configAcceptHeader: string[] = [], requestTransferSyntaxUID = '*', //default to accept all transfer syntax omitQuotationForMultipartRequest = false ): string[] => { diff --git a/platform/core/src/utils/index.ts b/platform/core/src/utils/index.ts index 0955686ca8a..4ba12460215 100644 --- a/platform/core/src/utils/index.ts +++ b/platform/core/src/utils/index.ts @@ -45,6 +45,11 @@ import * as MeasurementFilters from './measurementFilters'; import getClosestOrientationFromIOP from './getClosestOrientationFromIOP'; import calculateScanAxisNormal from './calculateScanAxisNormal'; import areAllImageOrientationsEqual from './areAllImageOrientationsEqual'; +import { structuredCloneWithFunctions } from './structuredCloneWithFunctions'; +import { buildButtonCommands } from './buildButtonCommands'; + +import { downloadBlob, downloadUrl, downloadCsv, downloadDicom } from './downloadBlob'; + // Commented out unused functionality. // Need to implement new mechanism for derived displaySets using the displaySetManager. @@ -71,6 +76,7 @@ const utils = { //loadAndCacheDerivedDisplaySets, makeDeferred, makeCancelable, + structuredCloneWithFunctions, hotkeys, Queue, isDicomUid, @@ -95,6 +101,10 @@ const utils = { getClosestOrientationFromIOP, calculateScanAxisNormal, areAllImageOrientationsEqual, + downloadBlob, + downloadUrl, + downloadCsv, + downloadDicom, }; export { @@ -109,6 +119,7 @@ export { //loadAndCacheDerivedDisplaySets, makeDeferred, makeCancelable, + structuredCloneWithFunctions, hotkeys, Queue, isDicomUid, @@ -131,6 +142,11 @@ export { createStudyBrowserTabs, MeasurementFilters, getClosestOrientationFromIOP, + buildButtonCommands, + downloadBlob, + downloadUrl, + downloadCsv, + downloadDicom, }; export default utils; diff --git a/platform/core/src/utils/measurementFilters.ts b/platform/core/src/utils/measurementFilters.ts index ef1a887f6ef..72e847b0b8f 100644 --- a/platform/core/src/utils/measurementFilters.ts +++ b/platform/core/src/utils/measurementFilters.ts @@ -10,6 +10,7 @@ export function filterMeasurementsBySeriesUID(selectedSeries: string[]) { return measurement => selectedSeries.includes(measurement.referenceSeriesUID); } +/** A filter that filters for measurements belonging to the study */ export function filterMeasurementsByStudyUID(studyUID) { return measurement => measurement.referenceStudyUID == studyUID; } diff --git a/platform/core/src/utils/metadataProvider/fetchPaletteColorLookupTableData.js b/platform/core/src/utils/metadataProvider/fetchPaletteColorLookupTableData.js index a3d5a7aeaf4..8dd053cbdcd 100644 --- a/platform/core/src/utils/metadataProvider/fetchPaletteColorLookupTableData.js +++ b/platform/core/src/utils/metadataProvider/fetchPaletteColorLookupTableData.js @@ -30,18 +30,15 @@ function _getPaletteColor(paletteColorLookupTableData, lutDescriptor) { } const arrayBufferToPaletteColorLUT = arraybuffer => { + // Handle both ArrayBuffer and TypedArray inputs + const buffer = arraybuffer.buffer || arraybuffer; + const data = bits === 16 ? new Uint16Array(buffer) : new Uint8Array(buffer); const lut = []; - if (bits === 16) { - let j = 0; - for (let i = 0; i < numLutEntries; i++) { - lut[i] = (arraybuffer[j++] + arraybuffer[j++]) << 8; - } - } else { - for (let i = 0; i < numLutEntries; i++) { - lut[i] = arraybuffer[i]; - } + for (let i = 0; i < numLutEntries; i++) { + lut[i] = data[i]; } + return lut; }; @@ -51,10 +48,10 @@ function _getPaletteColor(paletteColorLookupTableData, lutDescriptor) { if (paletteColorLookupTableData.InlineBinary) { try { - const arraybuffer = Uint8Array.from(atob(paletteColorLookupTableData.InlineBinary), c => + const uint8Array = Uint8Array.from(atob(paletteColorLookupTableData.InlineBinary), c => c.charCodeAt(0) ); - return (paletteColorLookupTableData.palette = arrayBufferToPaletteColorLUT(arraybuffer)); + return (paletteColorLookupTableData.palette = arrayBufferToPaletteColorLUT(uint8Array)); } catch (e) { console.log("Couldn't decode", paletteColorLookupTableData.InlineBinary, e); return undefined; diff --git a/platform/core/src/utils/sortStudy.test.js b/platform/core/src/utils/sortStudy.test.js new file mode 100644 index 00000000000..b599342900a --- /dev/null +++ b/platform/core/src/utils/sortStudy.test.js @@ -0,0 +1,44 @@ +import { compareSeriesUID, addSameSeriesCompare, compare } from './sortStudy'; + +addSameSeriesCompare('default', (a,b) => compare(a.default,b.default), 5); +const altCompare = 'altCompare' +addSameSeriesCompare(altCompare, (a,b) => compare(a.altCompare,b.altCompare), 3); + +const ds1 = { + SeriesInstanceUID: '1', + default: 'ds1', +} + +const ds2 = { + ...ds1, + default: 'ds2', +} + +const ds3 = { + ...ds2, + altCompare: 3, + compareSameSeries: altCompare, +} + +const ds4 = { + ...ds1, + altCompare:4, + compareSameSeries: altCompare, +} + +const ds5 = { + ...ds1, + SeriesInstanceUID: '3', +} + +describe('sortStudy', () => { + test('compareSameSeries',() => { + const initial = [ds5, ds4,ds3,ds2,ds1]; + initial.sort(compareSeriesUID); + expect(initial[0]).toBe(ds1); + expect(initial[1]).toBe(ds2); + expect(initial[2]).toBe(ds3); + expect(initial[3]).toBe(ds4); + expect(initial[4]).toBe(ds5); + }) +}) diff --git a/platform/core/src/utils/sortStudy.ts b/platform/core/src/utils/sortStudy.ts index 404a4853dfc..f3c32694b72 100644 --- a/platform/core/src/utils/sortStudy.ts +++ b/platform/core/src/utils/sortStudy.ts @@ -3,19 +3,77 @@ import isLowPriorityModality from './isLowPriorityModality'; import calculateScanAxisNormal from './calculateScanAxisNormal'; import areAllImageOrientationsEqual from './areAllImageOrientationsEqual'; -const compareSeriesDateTime = (a, b) => { - const seriesDateA = Date.parse(`${a.seriesDate ?? a.SeriesDate} ${a.seriesTime ?? a.SeriesTime}`); - const seriesDateB = Date.parse(`${b.seriesDate ?? b.SeriesDate} ${b.seriesTime ?? b.SeriesTime}`); - return seriesDateA - seriesDateB; +export const compare = (a, b) => { + if (a == b) return 0; + if (!a && b) return -1; + if (!b && a) return 1; + if (a < b) return -1; + return 1; }; -const defaultSeriesSort = (a, b) => { +type CompareSameSeries = { + priority: number; + compare: (a, b) => number; +}; + +const mapCompareSameSeries = new Map(); + +/** + * Adds a comparison for same series display sets. + * Supply null for compareF to delete the function. + */ +export function addSameSeriesCompare(name: string, compareF: (a, b) => number, priority: number) { + if (!compareF) { + mapCompareSameSeries.delete(name); + } else { + mapCompareSameSeries.set(name, { compare: compareF, priority }); + } +} + +/** + * When the "series" sort is used on display sets, it is possible to get the + * same series twice. This method compares two display sets from the same series + * + * If both display sets have the same compareSameSeries name, then the + * function registered for that name will be used. + * + * If they differ, then the priority between the two functions will be used. + * + * Otherwise, the instance compare will be used on the default instance. + * + * This provides a configurable well defined sorting order. + */ +export const compareSameSeriesDisplaySet = (a, b) => { + const { compareSameSeries: compareAName = 'default' } = a; + const { compareSameSeries: compareBName = 'default' } = b; + const compareA = mapCompareSameSeries.get(compareAName); + const compareB = mapCompareSameSeries.get(compareBName); + if (compareA && compareB) { + const compareValue = + compareA === compareB + ? compareA.compare(a, b) + : compare(compareA.priority, compareB.priority); + if (!compareValue) { + return compareValue; + } + } + return sortByInstanceNumber(a.instance, b.instance); +}; + +export const compareSeriesUID = (a, b) => + compare(a.SeriesInstanceUID, b.SeriesInstanceUID) || compareSameSeriesDisplaySet(a, b); + +export const compareSeriesDateTime = (a, b) => { + // Natural order of string is good enough here + const seriesDateA = `${a.seriesDate ?? a.SeriesDate} ${a.seriesTime ?? a.SeriesTime}`; + const seriesDateB = `${b.seriesDate ?? b.SeriesDate} ${b.seriesTime ?? b.SeriesTime}`; + return compare(seriesDateA, seriesDateB) || compareSeriesUID(a, b); +}; + +export const defaultSeriesSort = (a, b) => { const seriesNumberA = a.SeriesNumber ?? a.seriesNumber; const seriesNumberB = b.SeriesNumber ?? b.seriesNumber; - if (seriesNumberA === seriesNumberB) { - return compareSeriesDateTime(a, b); - } - return seriesNumberA - seriesNumberB; + return compare(seriesNumberA, seriesNumberB) || compareSeriesDateTime(a, b); }; /** @@ -24,14 +82,14 @@ const defaultSeriesSort = (a, b) => { * @param {Object} firstSeries * @param {Object} secondSeries */ -function seriesInfoSortingCriteria(firstSeries, secondSeries) { +export function seriesInfoSortingCriteria(firstSeries, secondSeries) { const aLowPriority = isLowPriorityModality(firstSeries.Modality ?? firstSeries.modality); const bLowPriority = isLowPriorityModality(secondSeries.Modality ?? secondSeries.modality); if (aLowPriority) { // Use the reverse sort order for low priority modalities so that the // most recent one comes up first as usually that is the one of interest. - return bLowPriority ? defaultSeriesSort(secondSeries, firstSeries) : 1; + return bLowPriority ? compareSeriesDateTime(secondSeries, firstSeries) : 1; } else if (bLowPriority) { return -1; } @@ -39,44 +97,50 @@ function seriesInfoSortingCriteria(firstSeries, secondSeries) { return defaultSeriesSort(firstSeries, secondSeries); } -const seriesSortCriteria = { +export const seriesSortCriteria = { default: seriesInfoSortingCriteria, seriesInfoSortingCriteria, + compareSameSeries: compareSameSeriesDisplaySet, + compareSeriesDateTime, + compareSeriesUID, }; -const sortByInstanceNumber = (a, b) => { - // Sort by InstanceNumber (0020,0013) +/** + * Compares two instances first by instance number, and then by + * sop and frame numbers. + * Handles undefined values for use with display set comparison. + */ +export const sortByInstanceNumber = (a, b) => { + if (!a || !b) { + return (!a && !b && 0) || (!a && -1) || 1; + } const aInstance = parseInt(a.InstanceNumber) || 0; const bInstance = parseInt(b.InstanceNumber) || 0; if (aInstance !== bInstance) { return (parseInt(a.InstanceNumber) || 0) - (parseInt(b.InstanceNumber) || 0); } - // Fallback rule to enable consistent sorting - if (a.SOPInstanceUID === b.SOPInstanceUID) { - return 0; - } - return a.SOPInstanceUID < b.SOPInstanceUID ? -1 : 1; + return compare(a.SOPInstanceUID, b.SOPInstanceUID) || compare(a.frameNumber, b.frameNumber); }; -const instancesSortCriteria = { +export const instancesSortCriteria = { default: sortByInstanceNumber, sortByInstanceNumber, }; -const sortingCriteria = { +export const sortingCriteria = { seriesSortCriteria, instancesSortCriteria, }; /** - * Sorts given series (given param is modified) + * Sorts given series or display sets * The default criteria is based on series number in ascending order. * - * @param {Array} series List of series - * @param {function} seriesSortingCriteria method for sorting - * @returns {Array} sorted series object + * @param series - List of series (modified in place) + * @param seriesSortingCriteria - method for sorting + * @returns sorted series object */ -const sortStudySeries = ( +export const sortStudySeries = ( series, seriesSortingCriteria = seriesSortCriteria.default, sortFunction = null @@ -96,7 +160,7 @@ const sortStudySeries = ( * @param {function} instancesSortingCriteria method for sorting * @returns {Array} sorted instancesList object */ -const sortStudyInstances = ( +export const sortStudyInstances = ( instancesList, instancesSortingCriteria = instancesSortCriteria.default ) => { @@ -113,7 +177,7 @@ const sortStudyInstances = ( * @param {function} [instancesSortingCriteria = instancesSortCriteria.default] method for sorting instances * @returns {Object} sorted study object */ -export default function sortStudy( +export function sortStudy( study, deepSort = true, seriesSortingCriteria = seriesSortCriteria.default, @@ -134,7 +198,7 @@ export default function sortStudy( return study; } -function isValidForPositionSort(images): boolean { +export function isValidForPositionSort(images): boolean { if (images.length <= 1) { return false; // No need to sort if there's only one image } @@ -161,7 +225,7 @@ function isValidForPositionSort(images): boolean { * * @returns images - reference to images after sorting */ -const sortImagesByPatientPosition = images => { +export const sortImagesByPatientPosition = images => { const referenceImagePositionPatient = images[0].ImagePositionPatient; const imageOrientationPatient = images[0].ImageOrientationPatient; @@ -188,13 +252,4 @@ const sortImagesByPatientPosition = images => { return images; }; -export { - sortStudy, - sortStudySeries, - sortStudyInstances, - sortingCriteria, - seriesSortCriteria, - instancesSortCriteria, - isValidForPositionSort, - sortImagesByPatientPosition, -}; +export default sortStudy; diff --git a/platform/core/src/utils/structuredCloneWithFunctions.ts b/platform/core/src/utils/structuredCloneWithFunctions.ts new file mode 100644 index 00000000000..e76698714c0 --- /dev/null +++ b/platform/core/src/utils/structuredCloneWithFunctions.ts @@ -0,0 +1,40 @@ +/** + * Clones the object, incorporating functions as functions in the result. + */ +export function structuredCloneWithFunctions(obj, seen = new WeakMap()) { + // Handle null, primitives, and functions + if (obj === null || typeof obj !== 'object') { + return obj; + } + if (typeof obj === 'function') { + return obj; // copy function by reference + } + + // Handle circular references + if (seen.has(obj)) { + return seen.get(obj); + } + + // Handle Date + if (obj instanceof Date) { + return new Date(obj.getTime()); + } + + // Handle Array + if (Array.isArray(obj)) { + const arrCopy = []; + seen.set(obj, arrCopy); + for (const item of obj) { + arrCopy.push(structuredCloneWithFunctions(item, seen)); + } + return arrCopy; + } + + // Handle Object + const copy = {}; + seen.set(obj, copy); + for (const key of Object.keys(obj)) { + copy[key] = structuredCloneWithFunctions(obj[key], seen); + } + return copy; +} diff --git a/platform/docs/CHANGELOG.md b/platform/docs/CHANGELOG.md index 85a6437a4be..b1989e0390c 100644 --- a/platform/docs/CHANGELOG.md +++ b/platform/docs/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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-docs @@ -11,7 +11,1135 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [3.12.0-beta.127](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.126...v3.12.0-beta.127) (2026-01-22) + + +### Bug Fixes + +* Navigate to contour should work ([#5718](https://github.com/OHIF/Viewers/issues/5718)) ([0fd517d](https://github.com/OHIF/Viewers/commit/0fd517d6ab583652a3667f9af49413648f2755db)) + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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 ohif-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [3.12.0-beta.114](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.113...v3.12.0-beta.114) (2026-01-05) + + +### Bug Fixes + +* **security:** update qs package to fix vulnerability CVE-2025-15284 ([#5686](https://github.com/OHIF/Viewers/issues/5686)) ([ca364a3](https://github.com/OHIF/Viewers/commit/ca364a3af7a29dc98d5c5b8e4c73abebc107c7ce)) + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [3.12.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.99...v3.12.0-beta.100) (2025-12-05) + + +### Features + +* **notification:** add custom notification component support ([#5605](https://github.com/OHIF/Viewers/issues/5605)) ([c4e5a46](https://github.com/OHIF/Viewers/commit/c4e5a4616db87b39df5e16b75f1ba3c18407468f)) + + + + + +# [3.12.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.98...v3.12.0-beta.99) (2025-12-04) + + +### Bug Fixes + +* Initial sort not consistent ([#5224](https://github.com/OHIF/Viewers/issues/5224)) ([77f9f8e](https://github.com/OHIF/Viewers/commit/77f9f8e1c4af6b5d22ecf9332b5edf503cd5b848)) + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19) + + +### Bug Fixes + +* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f)) + + + + + +# [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-docs + + + + + +# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11) + + +### Bug Fixes + +* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f)) + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05) + + +### Bug Fixes + +* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece)) + + + + + +# [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-docs + + + + + +# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30) + + +### Bug Fixes + +* **security:** Exact versioning and docs dependabot alerts ([#5536](https://github.com/OHIF/Viewers/issues/5536)) ([000e42e](https://github.com/OHIF/Viewers/commit/000e42e9a6eaa4f0878cbf877f03b09a459ad18e)) + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24) + + +### Bug Fixes + +* **security:** For bun, enforce frozen lockfile. For yarn, strongly suggest using frozen lockfile. ([#5508](https://github.com/OHIF/Viewers/issues/5508)) ([1009c60](https://github.com/OHIF/Viewers/commit/1009c6091107d2db0768622120f916208a391343)) + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17) + + +### Bug Fixes + +* **ErrorBoundary:** Allow for details to be shown in production. ([#5504](https://github.com/OHIF/Viewers/issues/5504)) ([4620cc3](https://github.com/OHIF/Viewers/commit/4620cc3acf89f218c3feaaf4c153a3dc8b023b23)) + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [3.12.0-beta.61](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.60...v3.12.0-beta.61) (2025-10-15) + + +### Bug Fixes + +* **docs:** Set exact version for glob. ([#5497](https://github.com/OHIF/Viewers/issues/5497)) ([0d2f4e7](https://github.com/OHIF/Viewers/commit/0d2f4e715a941fa458ebf8e64972088580a04952)) + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [3.12.0-beta.39](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.38...v3.12.0-beta.39) (2025-10-01) + + +### Features + +* Add inherit from basic/longitudinal modes ([#5435](https://github.com/OHIF/Viewers/issues/5435)) ([9ad0d7f](https://github.com/OHIF/Viewers/commit/9ad0d7fc8cf34867fa2cfc2bf2b2f63451ec03e5)) + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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 ohif-docs + + + + + +# [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-docs + + + + + +# [3.12.0-beta.29](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.28...v3.12.0-beta.29) (2025-09-22) + + +### Features + +* **Segmentation:** Segmentation highlight animation function selection ([#5401](https://github.com/OHIF/Viewers/issues/5401)) ([69dbe27](https://github.com/OHIF/Viewers/commit/69dbe2778493cdfc7a8da5e757c95217bb31edc2)) + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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 ohif-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [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-docs + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + + +### Bug Fixes + +* Segmentation stats customization module ([#5324](https://github.com/OHIF/Viewers/issues/5324)) ([b9d6f55](https://github.com/OHIF/Viewers/commit/b9d6f558cbe5957f83e6d92305a78662a0b186ed)) + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package ohif-docs + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package ohif-docs diff --git a/platform/docs/README.md b/platform/docs/README.md index 231a499c0d6..668b7eb0d2f 100644 --- a/platform/docs/README.md +++ b/platform/docs/README.md @@ -5,7 +5,7 @@ This website is built using [Docusaurus 2](https://docusaurus.io/), a modern sta ## Installation ```console -yarn install +yarn install --frozen-lockfile ``` ## Local Development diff --git a/platform/docs/docs/configuration/dataSources/dicom-json.md b/platform/docs/docs/configuration/dataSources/dicom-json.md index a183c47d1bb..17dce2bed7a 100644 --- a/platform/docs/docs/configuration/dataSources/dicom-json.md +++ b/platform/docs/docs/configuration/dataSources/dicom-json.md @@ -156,7 +156,7 @@ inside your `public` folder. Since files are served from your local server the the dicom files will be `dicomweb:http://localhost:3000/LIDC-IDRI-0001/01-01-2000-30178/3000566.000000-03192/1-001.dcm`. -After `yarn install` and running `yarn dev` and opening the browser at +After `yarn install --frozen-lockfile` and running `yarn dev` and opening the browser at `http://localhost:3000/viewer/dicomjson?url=http://localhost:3000/LIDC-IDRI-0001.json` will display the viewer. diff --git a/platform/docs/docs/configuration/dataSources/dicom-web.md b/platform/docs/docs/configuration/dataSources/dicom-web.md index b04c1198314..1f7fab230e8 100644 --- a/platform/docs/docs/configuration/dataSources/dicom-web.md +++ b/platform/docs/docs/configuration/dataSources/dicom-web.md @@ -88,7 +88,7 @@ this repository's root directory, and run: yarn config set workspaces-experimental true # Restore dependencies -yarn install +yarn install --frozen-lockfile # Run our dev command, but with the local orthanc config yarn run dev:orthanc diff --git a/platform/docs/docs/configuration/dataSources/static-files.md b/platform/docs/docs/configuration/dataSources/static-files.md index 7746935bfea..19d2274a7c7 100644 --- a/platform/docs/docs/configuration/dataSources/static-files.md +++ b/platform/docs/docs/configuration/dataSources/static-files.md @@ -37,7 +37,7 @@ This project contains two main components: 2. **Install Dependencies:** ```bash - yarn install + yarn install --frozen-lockfile ``` ## Generating Static DICOMweb Files diff --git a/platform/docs/docs/deployment/azure.md b/platform/docs/docs/deployment/azure.md index 983eafa4ca5..b88ced6843f 100644 --- a/platform/docs/docs/deployment/azure.md +++ b/platform/docs/docs/deployment/azure.md @@ -162,7 +162,7 @@ Update the data source configuration file with your Azure Healthcare APIs detail ```bash cd OHIFViewer - yarn install + yarn install --frozen-lockfile APP_CONFIG=config/azure.js yarn run dev ``` @@ -175,4 +175,3 @@ Update the data source configuration file with your Azure Healthcare APIs detail - The `qidoRoot`, `wadoUriRoot`, and `wadoRoot` should point to your Azure DICOM service URL. Replace `{your-dicom-instance}` with your actual instance name. This setup allows OHIF to interact seamlessly with Azure's Healthcare APIs, enabling robust DICOM management and visualization. - diff --git a/platform/docs/docs/deployment/build-for-production.md b/platform/docs/docs/deployment/build-for-production.md index f4521327f52..4d9e608b4a1 100644 --- a/platform/docs/docs/deployment/build-for-production.md +++ b/platform/docs/docs/deployment/build-for-production.md @@ -38,7 +38,7 @@ Next run these commands: yarn config set workspaces-experimental true # Restore dependencies -yarn install +yarn install --frozen-lockfile # Build source code for production yarn run build diff --git a/platform/docs/docs/deployment/google-cloud-healthcare.md b/platform/docs/docs/deployment/google-cloud-healthcare.md index 6fef07c6a7a..bee2a0aea39 100644 --- a/platform/docs/docs/deployment/google-cloud-healthcare.md +++ b/platform/docs/docs/deployment/google-cloud-healthcare.md @@ -127,7 +127,7 @@ Images can even be transcoded on the fly if this is desired. ```bash cd OHIFViewer -yarn install +yarn install --frozen-lockfile APP_CONFIG=config/google.js yarn run dev ``` diff --git a/platform/docs/docs/development/getting-started.md b/platform/docs/docs/development/getting-started.md index c23bc9bcf26..4b814b34e76 100644 --- a/platform/docs/docs/development/getting-started.md +++ b/platform/docs/docs/development/getting-started.md @@ -80,11 +80,17 @@ following commands: ```bash # Restore dependencies -yarn install +yarn install --frozen-lockfile # Start local development server yarn run dev ``` +:::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. +::: You should see the following output: @@ -113,6 +119,31 @@ You should see the following output: yarn run build ``` +### Updating Dependencies +In general you will typically not be updating the various `package.json` files. +But for the case when you do, you will have to also update the various OHIF lock files +and as such you will have to do both a `yarn` and `bun` `install` without +the `--frozen-lockfile` flag. + +:::danger +Updating the package.json must be done with care so as to avoid incorporating +vulnerable, third-party packages and/or versions. Please research the added +packages and/or versions for vulnerabilities. + +Here is what you should do when adding new packages and/or versions prior to +committing and pushing your code: +1. Do your due diligence researching the added packages and/or versions for vulnerabilities. +2. Update the `package.json` files. +3. Execute `yarn run install:update-lockfile`. This updates both the `yarn.lock` and +the `bun.lock` files. +4. Execute `yarn run audit` for a last security check. This runs both `yarn audit` and +`bun audit`. +6. Include both the `yarn.lock` and `bun.lock` files as part of your commit. + +If any of your research or auditing for vulnerabilities find HIGH risk vulnerabilities +do NOT commit or push your changes! Low and moderate risk vulnerabilities are acceptable. +::: + ## Troubleshooting - If you receive a _"No Studies Found"_ message and do not see your studies, try diff --git a/platform/docs/docs/development/link.md b/platform/docs/docs/development/link.md index 92d93eb6a42..5e369287110 100644 --- a/platform/docs/docs/development/link.md +++ b/platform/docs/docs/development/link.md @@ -19,10 +19,12 @@ code linking and execution. The method to link locally using `bun` differs sligh :::tip -Linking locally with `bun` provides for running the [playwright tests](./playwright-testing.md) locally so as to include (and test) local changes from Cornerstone3D! +Linking locally with `bun` provides for running the [playwright tests](./playwright-testing.md) locally so as to include (and test) local changes from Cornerstone3D and other libraries like dicom-microscopy-viewer. ::: +### Linking Cornerstone3D + In the local Cornerstone3D, simply replace `yarn` with `bun` for each of the commands. For example, in `cornerstone/packages/core` the following would be done. ``` @@ -49,4 +51,29 @@ cornerstone packages to link locally in the `resolutions`. ... ``` -In OHIF, run `bun install -f` and then run OHIF using either `bun dev` or `bun dev:fast`. +In OHIF, run `bun install -f --config=.\bunfig.update-lockfile.toml` and then run OHIF using either `bun dev` or `bun dev:fast`. + +### Linking dicom-microscopy-viewer + +The process for linking other libraries such as the dicom-microscopy-viewer is similar to that of linking Cornerstone3D. + +``` +# In the local dicom-microscopy-viewer directory +bun unlink +bun link +bun webpack:dynamic-import:watch +``` +In OHIF, edit the root `package.json` file to include the dicom-microscopy-viewer package in the `resolutions`. The following is an example of this... + +``` + ... + "resolutions": { + "commander": "8.3.0", + "path-to-regexp": "0.1.12", + "dicom-microscopy-viewer": "link:dicom-microscopy-viewer", + ... + }, + ... +``` + +In OHIF, run `bun install -f --config=.\bunfig.update-lockfile.toml` and then run OHIF using either `bun dev` or `bun dev:fast`. diff --git a/platform/docs/docs/development/notes-requirements.md b/platform/docs/docs/development/notes-requirements.md new file mode 100644 index 00000000000..ee32a232903 --- /dev/null +++ b/platform/docs/docs/development/notes-requirements.md @@ -0,0 +1,68 @@ + +--- +sidebar_position: 14 +sidebar_label: Notes and Requirements +title: Notes and Requirements for general OHIF behaviour +summary: Specifies some of the expected behavior of OHIF generally +--- + +# Notes and Requirements + +This document just lists general notes and requirements for how OHIF behaves. +The plan is to break this document down into a new sub-category once there +are sufficient notes/requirements. + + +## Series and Display Set Sorting `sortStudy.ts` + +Often a user will want to see a sorted list of series, or more generally +a sorted list of display sets. Series are the original data and can be split +up into several display sets, although they are the same general sort of concept + +For example, an MR series might contain both T1 and T2 echos, and the T2 echo +should occur after the T1. Or, a single series might contain 4 mammography views: +`LCC`, `RCC`, `LMLO`, `RMLO` with all the `CC` views shown first, and within +that all the left views first for a given sub-type of CC view. + +To allow controlling that, the `sortStudy` can register sort functions +that user used when two display sets come from the same series. Between +those display sets, the registration also registers a default ordering +for that compare function. Thus, the registration might look like: + +```javascript + addSameSeriesCompare('mammographyCompare', mammographyCompare, 5) + addSameSeriesCompare('mrT1T2Compare', mrT1T2Compare, 7); +``` + +Then, the display set for mr and mammography need to set the field `compareSameSeries` +to the value `mammographyCompare`. + +```javascript + makeDisplaySet + ... + displaySet = { + ..., + compareSameSeries: 'mammographyCompare', +``` + +### Specifying Sort Order from Series Split + +The series split rules (`getSopClassHandlerModule`) can specify a custom order +of display sets for the same series by adding a `sortVector` +to the display set created. Display sets which match on series instance uid +are then compared using the sort vector. The first element is the general sort +order for this type of value among all other sort types, and must be numeric. +The remainder of the values in the vector should be consistent for all +sort vectors whose first value is the same value. + +For example, the mammography sort vector might have a primary value of '25', +and then use the next three values for `view type`, `sub type` and `side`. +It might also be true that "both" side views sort before everything and would be assigned +a value less than `25` here. + +``` + // LCC view + [25, 'CC', 'L', 'XO'] + // BCC view + [24, 'CC', 'B'] +``` diff --git a/platform/docs/docs/development/testing.md b/platform/docs/docs/development/testing.md index fb7322696db..28b5ff8e9bd 100644 --- a/platform/docs/docs/development/testing.md +++ b/platform/docs/docs/development/testing.md @@ -20,7 +20,7 @@ To run the unit test: yarn run test:unit:ci ``` -Note: You should have already installed all the packages with `yarn install`. +Note: You should have already installed all the packages with `yarn install --frozen-lockfile`. Running unit test will generate a report at the end showing the successful and unsuccessful tests with detailed explanations. diff --git a/platform/docs/docs/migration-guide/3p10-to-3p11/index.md b/platform/docs/docs/migration-guide/3p10-to-3p11/index.md index d60fdfd764e..90c2dfc5bb7 100644 --- a/platform/docs/docs/migration-guide/3p10-to-3p11/index.md +++ b/platform/docs/docs/migration-guide/3p10-to-3p11/index.md @@ -1,8 +1,15 @@ --- +id: index sidebar_position: 1 -sidebar_label: 3.10 -> 3.11 beta +sidebar_label: 3.10 -> 3.11 +title: 3.10 to 3.11 Migration Guide --- -# Migration Guide +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; -This guide provides information about migrating from OHIF version 3.10 to version 3.11. +# 3.10 to 3.11 Migration Guide + +Here you can find the migration guides for upgrading from OHIF version 3.10 to version 3.11. + + item.docId !== 'migration-guide/3p10-to-3p11/index')}/> diff --git a/platform/docs/docs/migration-guide/3p10-to-3p11/viewport-action-menu.md b/platform/docs/docs/migration-guide/3p10-to-3p11/viewport-action-menu.md index b2b1fd307f4..7a546763e1c 100644 --- a/platform/docs/docs/migration-guide/3p10-to-3p11/viewport-action-menu.md +++ b/platform/docs/docs/migration-guide/3p10-to-3p11/viewport-action-menu.md @@ -5,8 +5,6 @@ summary: Migration guide for OHIF 3.11's viewport corners customization changes, --- -Okay, here's a migration guide based on the provided diff, focusing on the introduction of `TrackingStatus`, `ModalityLoadBadge`, and `NavigationComponent`. - **Key Changes:** * **Deprecated `ViewportActionCornersService`**: The `ViewportActionCornersService` and its associated provider (`ViewportActionCornersProvider`) have been removed. UI elements previously managed by this service are now typically handled by dedicated components integrated via the `ToolbarService`. diff --git a/platform/docs/docs/migration-guide/3p11-to-3p12/index.md b/platform/docs/docs/migration-guide/3p11-to-3p12/index.md new file mode 100644 index 00000000000..48db144c5da --- /dev/null +++ b/platform/docs/docs/migration-guide/3p11-to-3p12/index.md @@ -0,0 +1,68 @@ +--- +sidebar_position: 1 +sidebar_label: 3.11 -> 3.12 beta +--- + +# Migration Guide + +This guide provides information about migrating from OHIF version 3.11 to version 3.12 beta + +## Optional: Migrate modes to extend `modes/basic` + +There is a lot of support for the basic mode definition contained in the +`modes/basic` module. Using this framework will allow your mode to avoid +creating a lot of boilerplate code that may not upgrade very well. + +This is an OPTIONAL change - your existing mode definitions will continue to work, +but using the new basic mode as a basis will reduce the amount of effort when +there are changes unrelated to your custom mode. + + +## ui button with text size + +Using the class text size with the ui-button is inconsistent as to whether +it will apply or not. Instead, create a new size value to assign the desired size. +To support this, a new size enum is created, smallTall, which is used in the worklist +for an over-ride. + +## `createReportDialogPrompt` + +The create report dialog prompt (which is MOSTLY an internal component) has +change the API a bit. The input is now: + + - `title` shown in the dialog + - `modality` being stored, used to query existing series + - `minSeriesNumber` is the start of new series of this modality type. + Will get set to 4000 if not determined by the modality + - predecessorImageId is the image id that this series was currently loaded + from. That allows defaulting the dialog to show the specified series instead + of always creating a new series. + +The minSeries and predecessor are both optional, so the input doesn't have to be +updated. + +The output has been enhanced with: + + - `series`, is the series to store do, as referenced by a predecessorImageId value. + This allows exactly specifying which item to replace, which allows selecting it in the + menu by default instead of just guessing what value is being replaced. + - `priorSeriesNumber` is the previously lowest series number at least minSeriesNumber + of all the seris of the given modality type. This allows adding an instance to the + `next` series number by adding 1 to this value. + +The priorSeriesNumber will default to 4000 for an unknown modality type, or +3000 for sr, 3100 for seg and 3200 for rtstruct. + +## metadataProvider and formatted metadata + +The metadata provider has been formatting some fields, which causes inconsistency +between different metadata providers and use of the instance object. The +specific fields that have changed are: + +- `patientName` +- `studyDate`, `studyTime` +- `seriesDate`, `seriesTime` + +If these fields need formatted versions, it is recommended to add a secondary/computed +metadata provider which simply gets the base metadata module and adds the +formatting. That way different metadata providers are all handled identically. diff --git a/platform/docs/docs/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md b/platform/docs/docs/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md index a13cd718f83..c4195c5986a 100644 --- a/platform/docs/docs/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md +++ b/platform/docs/docs/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md @@ -346,7 +346,7 @@ jumpToSegmentCenter( highlightSegment = true, animationLength = 750, highlightHideOthers = false, - highlightFunctionType = 'ease-in-out' + animationFunctionType = 'ease-in-out' ) ``` diff --git a/platform/docs/docs/migration-guide/3p9-to-3p10/1-General/general-m.md b/platform/docs/docs/migration-guide/3p9-to-3p10/1-General/general-m.md index 6ff64c94108..093244f396c 100644 --- a/platform/docs/docs/migration-guide/3p9-to-3p10/1-General/general-m.md +++ b/platform/docs/docs/migration-guide/3p9-to-3p10/1-General/general-m.md @@ -57,7 +57,7 @@ yarn run dev After: ```bash -yarn install +yarn install --frozen-lockfile yarn run dev ``` diff --git a/platform/docs/docs/platform/extensions/modules/toolbar.md b/platform/docs/docs/platform/extensions/modules/toolbar.md index 63197d6b2a9..b45500400d2 100644 --- a/platform/docs/docs/platform/extensions/modules/toolbar.md +++ b/platform/docs/docs/platform/extensions/modules/toolbar.md @@ -117,6 +117,13 @@ Let's look at one of the evaluators (for `evaluate.cornerstoneTool`) as you can see the job of this evaluator is to determine if the button should be disabled or not. It does so by checking the `toolGroup` and the `toolName` and then returns an object with `disabled` and `className` properties. +Various information can be returned by an evaluator. In particular... +- `disabled`: flag indicating if the tool should be disabled or not +- `disabledText`: the text or tooltip to show if the `disabled` flag above is set to `true` +- `visible`: flag indicating if the tool show be visible or not; this is a convenient way to force a tool to hide based on some custom (evaluator) logic +- `isActive`: flag to indicating where the tool is currently active or not +- `className`: custom CSS class names to add to the tool's component + The following evaluators are provided by us: - `evaluate.cornerstoneTool`: If assigned to a button (see next), it will make the button react to the active viewport state based on its toolGroup. diff --git a/platform/docs/docs/platform/modes/index.md b/platform/docs/docs/platform/modes/index.md index 7fdd3e39abb..a85900343e2 100644 --- a/platform/docs/docs/platform/modes/index.md +++ b/platform/docs/docs/platform/modes/index.md @@ -52,9 +52,12 @@ The mode configuration specifies which `extensions` the mode requires, which template this defines which `side panels` will be available, as well as what `viewports` and which `displaySets` they may hang. -Mode's config is composed of three elements: +Mode's config is composed of these elements: - `id`: the mode `id` - `modeFactory`: the function that returns the mode specific configuration +- `modeInstance`: An optional configuration used by the default modeFactory + exported by the basic mode. This allows specifying or updating the + default mode values. - `extensionDependencies`: the list of extensions that the mode requires @@ -62,10 +65,18 @@ that return a config object with certain properties, the high-level view of this config object is: ```js title="modes/example/src/index.js" -function modeFactory() { - return { +function modeFactory({modeConfiguration}) { + return { ...this.modeInstance, ...modeConfiguration }; + +} + +const mode = { + id, + modeFactory, + modeInstance: { id: '', version: '', + hide: true, displayName: '', onModeEnter: () => {}, onModeExit: () => {}, @@ -75,19 +86,27 @@ function modeFactory() { { path: '', init: () => {}, - layoutTemplate: () => {}, + layoutInstance: { + id, + props: { + leftPanels: [], + leftPanelResizable: true, + rightPanels: [], + rightPanelClosed: true, + rightPanelResizable: true, + viewports: [], + }, + }, + layoutTemplate: function() { return this.layoutInstance }, }, ], extensions: extensionDependencies, hangingProtocol: [], sopClassHandlers: [], - hotkeys: [] - }; -} - -const mode = { - id, - modeFactory, + hotkeys: [], + nonModeModalities: [], + modeModalities: [], + }, extensionDependencies, }; @@ -108,6 +127,12 @@ export default mode; unique mode id used to refer to the mode + + + hide + + Set to true to hide this mode on the worklist, but allow it in the path + displayName @@ -168,7 +193,7 @@ export default mode; hanging protocol - list of hanging protocols that the mode should have access to + list of hanging protocols that the mode applies initially, choosing the highest scoring match @@ -186,10 +211,44 @@ export default mode; hotkeys + + + modeModalities + + If non-empty, then the default isValidMode will only return true when the modalities list has all of the elements of one of the mode modalities. Eg `[` [CT,PT], [MR,PT] ]` would mean that the mode supports a CT AND a PT, OR an MR and a PT + + + + nonModeModalities + + Enable the mode if the modalities list contains a modality OTHER than those in the array + + + + enableSegmentationEdit + + Boolean to skip the segmentation edit capabilities + + + + toolbarSections + + An object containing toolbar section definitions to register + + +### Extending Modes + +The `basic` mode provides support for creating mode extensions without having +to redeclare the entire mode. See `longitudinal/src/index.ts` for an example +mode that builds on top of the basic mode. Also see `basic/src/index.tsx` for +some default functions which can be used to create your own modes. Doing a mode +this way makes the definition of new modes based on your existing mode much easier, +and the upgrade to new versions of modes tends to be more consistent. + ### Consuming Extensions As mentioned in the [Extensions](../extensions/index.md) section, in `OHIF-v3` @@ -345,7 +404,6 @@ const myHotkeys = [ function modeFactory() { return { - id: '', id: '', displayName: '', /* @@ -413,3 +471,50 @@ rightPanels: [[dicomSeg.panel, tracked.measurements], [dicomSeg.panel, tracked.m This will result in two panels, one with `dicomSeg.panel` and `tracked.measurements` and the other with `dicomSeg.panel` and `tracked.measurements` stacked on top of each other. ::: + +## APP Configuration of Modes + +Modes based on the `basic` mode allow for customization using the `immutability-helper` +api within the `app-config.js` file as specified by the build process. For example, +to list the `basic` mode by default, and hide the `longitudinal` mode, the following +configuration from `config/kheops.js` can be used: + +``` +... app config file + modesConfiguration: { + '@ohif/mode-basic': { + hide: { $set: false }, + displayName: { $set: 'Basic' }, + }, + '@ohif/mode-longitudinal': { + hide: { $set: true }, + }, + }, +``` + +## Default Modes + +There are a number of modes provided in a default OHIF installation. These +are described here, along with some amount of information about extending/configuration +of those modes. + +Modes which are loaded by default, but which are hidden can be activated by +using a direct URL launch. For example, to show a study in the `basic` mode, +use the URL for the `longitudinal` mode, and replace the `/viewer` with `/basic` + +### Basic (NOT `Basic Viewer`, which got assigned to `longitudinal`) + +The basic mode is a mode that demonstrates the base capabilities of the OHIF +system, without including features such as longitudinal tracking, segmentation editing +or other custom capabilities. The left hand panel uses the study browser thumbnails +without tracking, and the right hand panel uses the basic segmentation panel and the +measurements without tracking (longitudinal) layouts. This makes it a good overall +base for using when the tracking behaviour of longitudinal mode is not desired. + +It can be used in a default install by direct URL launch to the `/basic` endpoint +instead of the `/viewer` endpoint. + +### Longitudinal (The `Basic Viewer` label in OHIF) + +The longitudinal mode adds the tracking for measurements in the study browser +and in the measurements panel, and is otherwise identical to the `basic` mode. diff --git a/platform/docs/docs/platform/modes/validity.md b/platform/docs/docs/platform/modes/validity.md index f95421b8005..3c5c650992e 100644 --- a/platform/docs/docs/platform/modes/validity.md +++ b/platform/docs/docs/platform/modes/validity.md @@ -11,6 +11,8 @@ summary: Documentation for OHIF Mode validity checks, which determine when speci There are two mechanism for checking the validity of a mode for a study. - `isValidMode`: which is called on a selected study in the workList. + - The basic mode exports an `isValidMode` function which selects + validity based on the modalities in the study. - `validTags` @@ -22,15 +24,16 @@ validity of the mode based on `StudyInstanceUID` and `modalities` that are in th For instance, for pet-ct mode, both `PT` and 'CT' modalities should be available inside the study. ```js +import { isValidMode } from '@ohif/mode-basic'; + function modeFactory() { return { id: '', displayName: '', - isValidMode: ({ modalities, StudyInstanceUID }) => { - const modalities_list = modalities.split('\\'); - const validMode = ['CT', 'PT'].every(modality => modalities_list.includes(modality)); - return validMode; - }, + // Select either `CT & PT | MR & PT` + modeModalities: [ ['CT', 'PT'], ['MR', 'PT'] ], + // Just re-use the existing function + isValidMode, /* ... */ diff --git a/platform/docs/docs/platform/services/customization-service/sampleCustomizations.tsx b/platform/docs/docs/platform/services/customization-service/sampleCustomizations.tsx index 8ac060dcbce..c420bf46c15 100644 --- a/platform/docs/docs/platform/services/customization-service/sampleCustomizations.tsx +++ b/platform/docs/docs/platform/services/customization-service/sampleCustomizations.tsx @@ -1310,6 +1310,53 @@ window.config = { }, }, ], +}; + `, + }, + { + id: 'panelSegmentation.disableUpdateSegmentationStats', + description: 'Disables the automatic update of segmentation statistics in the panel.', + default: false, + image: [], + configuration: ` +window.config = { + // rest of window config + customizationService: [ + { + 'panelSegmentation.disableUpdateSegmentationStats': { + $set: true, // Disables the automatic update of segmentation statistics in the panel + }, + }, + ], +}; + `, + }, + { + id: 'panelSegmentation.jumpToSegmentHighlightAnimationConfig', + description: + 'Customize the highlight animation when clicking on a segment at the segmentation panel and jumping to it.', + default: { + highlightAlpha: 0.9, + highlightSegment: true, + animationLength: 750, + animationFunctionType: 'ease-in-out', + }, + image: [], + configuration: ` +window.config = { + // rest of window config + customizationService: [ + { + 'panelSegmentation.jumpToSegmentHighlightAnimationConfig': { + $set: { + highlightAlpha: 1.0, + highlightSegment: true, + animationLength: 900, + animationFunctionType: 'linear', // one of 'ease-in-out', 'ease-in', 'ease-out', 'ease', 'linear' + }, + }, + }, + ], }; `, }, diff --git a/platform/docs/docs/platform/services/data/SegmentationService.md b/platform/docs/docs/platform/services/data/SegmentationService.md index 031594862f6..47e132612f5 100644 --- a/platform/docs/docs/platform/services/data/SegmentationService.md +++ b/platform/docs/docs/platform/services/data/SegmentationService.md @@ -11,12 +11,14 @@ summary: Documentation for OHIF's SegmentationService, which provides tools for ## Events ```typescript -SEGMENTATION_MODIFIED // When a segmentation is updated -SEGMENTATION_DATA_MODIFIED // When segmentation data changes -SEGMENTATION_ADDED // When new segmentation is added -SEGMENTATION_REMOVED // When segmentation is removed -SEGMENT_LOADING_COMPLETE // When segment group adds pixel data to volume -SEGMENTATION_LOADING_COMPLETE // When full segmentation volume is filled +SEGMENTATION_MODIFIED // When a segmentation is updated +SEGMENTATION_DATA_MODIFIED // When segmentation data changes +SEGMENTATION_ADDED // When new segmentation is added +SEGMENTATION_REMOVED // When segmentation is removed +SEGMENT_LOADING_COMPLETE // When segment group adds pixel data to volume +SEGMENTATION_LOADING_COMPLETE // When full segmentation volume is filled +SEGMENTATION_ANNOTATION_CUT_MERGE_PROCESS_COMPLETED // When a segmentation's annotation cut merge process is completed. +SEGMENTATION_STYLE_MODIFIED // When a segmentation style is modified. ``` ## Core APIs @@ -34,6 +36,17 @@ createLabelmapForDisplaySet( } } ) + +createContourForDisplaySet( + displaySet, + { + segmentationId?: string, + label: string, + segments?: { + [segmentIndex: number]: Partial + } + } +) ``` ### Segmentation Management @@ -43,6 +56,7 @@ setActiveSegmentation(viewportId, segmentationId) getSegmentations() getSegmentation(segmentationId) jumpToSegmentCenter(segmentationId, segmentIndex, viewportId) +jumpToSegmentNext(segmentationId, segmentIndex, forViewportId?, direction?, highlightAlpha?, highlightSegment?, animationLength?, highlightHideOthers?, animationFunctionType?) highlightSegment(segmentationId, segmentIndex, viewportId) ``` @@ -85,17 +99,33 @@ interface Segmentation { ## Code Examples -### Creating a Segmentation +### Creating a Label Map Segmentation ```typescript const displaySet = displaySetService.getDisplaySetByUID(displaySetUID); const segmentationId = await segmentationService.createLabelmapForDisplaySet( displaySet, { - label: 'New Segmentation', + label: 'New Label Map Segmentation', + segments: { + 1: { + label: 'First Label Map Segment', + active: true + } + } + } +); + +### Creating a Label Map Segmentation + +const displaySet = displaySetService.getDisplaySetByUID(displaySetUID); +const segmentationId = await segmentationService.createContourForDisplaySet( + displaySet, + { + label: 'New Contour Segmentation', segments: { 1: { - label: 'First Segment', + label: 'First Contour Segment', active: true } } @@ -145,3 +175,59 @@ segmentationService.setSegmentColor( [255, 0, 0, 255] // RGBA ); ``` + +### Navigation + +#### jumpToSegmentNext + +Jumps to the next or previous slice that contains the specified segment in the viewport. This method handles both labelmap and contour segmentations: +- For **labelmaps**: Jumps to the segment center +- For **contours**: Cycles through all slices that contain contour data for the segment in the specified direction + +```typescript +segmentationService.jumpToSegmentNext( + segmentationId: string, + segmentIndex: number, + forViewportId?: string, // Optional viewport ID. If not provided, applies to all viewports with this segmentation + direction?: number, // 1 for forward (default), -1 for backward + highlightAlpha?: number, // Alpha value for highlighting (0-1), default: 0.9 + highlightSegment?: boolean, // Whether to highlight the segment after jumping, default: true + animationLength?: number, // Length of highlight animation in milliseconds, default: 750 + highlightHideOthers?: boolean, // Whether to hide other segments during highlight, default: false + animationFunctionType?: EasingFunctionEnum // The easing function to use for animation, default: EASE_IN_OUT +) +``` + +**Navigation Behavior:** +- **Forward (direction = 1)**: Finds the next slice after the current one that contains the segment. If no slice is found after the current one, wraps around to the first slice with the segment. +- **Backward (direction = -1)**: Finds the previous slice before the current one that contains the segment. If no slice is found before the current one, wraps around to the last slice with the segment. + +**Example Usage:** + +```typescript +// Jump to next slice with segment 1 +segmentationService.jumpToSegmentNext( + segmentationId, + 1, // segmentIndex + 'viewport-1' +); + +// Jump to previous slice with segment 2, with custom highlighting +segmentationService.jumpToSegmentNext( + segmentationId, + 2, // segmentIndex + 'viewport-1', + -1, // backward direction + 0.95, // highlightAlpha + true, // highlightSegment + 1000, // animationLength in ms + true // highlightHideOthers +); + +// Apply to all viewports with this segmentation +segmentationService.jumpToSegmentNext( + segmentationId, + 1 // segmentIndex + // forViewportId omitted - applies to all viewports +); +``` diff --git a/platform/docs/docs/platform/services/ui/ui-notification-service.md b/platform/docs/docs/platform/services/ui/ui-notification-service.md index 5f700b58f9f..f05ffc56fcc 100644 --- a/platform/docs/docs/platform/services/ui/ui-notification-service.md +++ b/platform/docs/docs/platform/services/ui/ui-notification-service.md @@ -34,12 +34,14 @@ is expected to support, [check out it's interface in `@ohif/core`][interface] | ---------- | --------------------------------------- | | `hide()` | Hides the specified notification | | `show()` | Creates and displays a new notification | +| `customComponent()` | Overrides the default Notification component | ## Implementations | Implementation | Consumer | | ---------------------------------------- | ----------------------------------------- | | [Snackbar Provider][snackbar-provider]\* | [SnackbarContainer][snackbar-container]\* | +| customComponent | user extensions via `setServiceImplementation({customComponent: Snackbar})` | `*` - Denotes maintained by OHIF diff --git a/platform/docs/docusaurus.config.js b/platform/docs/docusaurus.config.js index d6138690111..38332df7ed7 100644 --- a/platform/docs/docusaurus.config.js +++ b/platform/docs/docusaurus.config.js @@ -3,6 +3,11 @@ const fs = require('fs'); const versions = fs.readFileSync('../../version.txt', 'utf8').split('\n'); const ArchivedVersionsDropdownItems = [ + { + version: '3.10', + href: 'https://v3p10.docs.ohif.org', + isExternal: true, + }, { version: '3.9', href: 'https://v3p9.docs.ohif.org', @@ -124,9 +129,9 @@ module.exports = { // respectPrefersColorScheme: true, }, announcementBar: { - id: 'ohif310_segmentation_ui_update', + id: 'ohif311_multimodality_rt_ultrasound', content: - '🛠️ OHIF 3.10 is here! Explore powerful new segmentation tools, including local AI-assisted workflows, real-time 3D GrowCut, segment statistics, and undo/redo. Plus, enjoy a polished new UI library and smoother interactions across the board. Read the release notes here! 🔍✨', + 'OHIF v3.11 is here! New features include multimodality fusion with viewport overlays, RT Dose visualization, dedicated ultrasound mode, DICOM Labelmap support, and advanced RT Structure Set visualization. Read the release notes here!', }, prism: { theme: require('prism-react-renderer').themes.github, @@ -177,9 +182,9 @@ module.exports = { position: 'left', }, { - to: '/migration-guide/3p9-to-3p10/', + to: '/migration-guide/3p10-to-3p11/', //activeBaseRegex: '(^/help$)|(/help)', - label: '3.10 Migration Guides', + label: '3.11 Migration Guides', position: 'left', }, { diff --git a/platform/docs/package.json b/platform/docs/package.json index 447245dddc9..59baf860a89 100644 --- a/platform/docs/package.json +++ b/platform/docs/package.json @@ -1,6 +1,6 @@ { "name": "ohif-docs", - "version": "3.11.1", + "version": "3.12.0", "private": true, "scripts": { "docusaurus": "docusaurus", @@ -76,7 +76,7 @@ "docusaurus-plugin-image-zoom": "1.0.1", "file-loader": "6.2.0", "framer-motion": "6.2.4", - "glob": "10.4.5", + "glob": "10.5.0", "lucide-react": "0.379.0", "next-themes": "0.3.0", "postcss": "8.5.6", @@ -95,5 +95,8 @@ "tailwindcss-animate": "1.0.7", "typescript": "5.5.4", "url-loader": "4.1.1" + }, + "resolutions": { + "qs": "6.14.1" } } diff --git a/platform/docs/versioned_docs/version-3.10/development/link.md b/platform/docs/versioned_docs/version-3.10/development/link.md deleted file mode 100644 index 5d8882fd1f8..00000000000 --- a/platform/docs/versioned_docs/version-3.10/development/link.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -sidebar_position: 9 -sidebar_label: Local Linking ---- - -# Introduction - -Local linking allows you to develop and test a library in the context of an application before it's published or when you encounter -a bug that you suspect is related to a library. With Yarn, this can be achieved through the yarn link command. - -You can take a look at the Cornerstonejs tutorial for linking https://www.cornerstonejs.org/docs/contribute/linking diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/index.md b/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/index.md deleted file mode 100644 index b72e7aa44f6..00000000000 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/index.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -sidebar_position: 1 -sidebar_label: 3.9 -> 3.10 ---- - -# Migration Guide - - -## General diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/Measurements.md b/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/Measurements.md deleted file mode 100644 index 7d8a6980133..00000000000 --- a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/Measurements.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Measurements ---- - - - -import { measurementsCustomizations, TableGenerator } from './sampleCustomizations'; - -{TableGenerator(measurementsCustomizations)} diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/Segmentation.md b/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/Segmentation.md deleted file mode 100644 index 7953b39b3cc..00000000000 --- a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/Segmentation.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Segmentation ---- - - - -import { segmentationCustomizations, TableGenerator } from './sampleCustomizations'; - -{TableGenerator(segmentationCustomizations)} diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/StudyBrowser.md b/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/StudyBrowser.md deleted file mode 100644 index fbbf3830e18..00000000000 --- a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/StudyBrowser.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Study Browser ---- - -# Study Browser - -The Study Browser is a component that allows users to browse and manage studies. - -import { studyBrowserCustomizations, TableGenerator } from './sampleCustomizations'; - -{TableGenerator(studyBrowserCustomizations)} diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/MultiMonitorService.md b/platform/docs/versioned_docs/version-3.10/platform/services/data/MultiMonitorService.md deleted file mode 100644 index eb74d28af80..00000000000 --- a/platform/docs/versioned_docs/version-3.10/platform/services/data/MultiMonitorService.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -sidebar_position: 5 -sidebar_label: Multi Monitor Service ---- - - -# Multi Monitor Service - -::: info - -We plan to enhance this service in the future. Currently, it offers a basic implementation of multi-monitor support, allowing you to manually open multiple windows on the same monitor. It is not yet a full multi-monitor solution! - -::: - - - - -The multi-monitor service provides detection, launch and communication support -for multiple monitors or windows/screens within a single monitor. - -:::info - -The multi-monitor service is currently applied via configuration file. - -```js -customizationService: ['@ohif/extension-default.customizationModule.multimonitor'], -``` - -::: - - - -## Configurations -The service supports two predefined configurations: - -1. **Split Screen (`multimonitor=split`)** - Splits the primary monitor into two windows. - -2. **Multi-Monitor (`multimonitor=2`)** - Opens windows across separate physical monitors. - -### Launch Methods -- Specify `&screenNumber=0` to designate the first window explicitly. -- Omit `screenNumber` to let the service handle window assignments dynamically. -- Use `launchAll` in the query parameters to launch all configured screens simultaneously. - -#### Example URLs: -- **Split Screen:** - `http://viewer.ohif.org/.....&multimonitor=split` - Splits the primary monitor into two windows when a study is viewed. - -- **Multi-Monitor with All Screens:** - `http://viewer.ohif.org/.....&multimonitor=2&screenNumber=0&launchAll` - Launches two monitors and opens all configured screens. - ---- - -## Behavior - -### Refresh, Close and Open -If you refresh the base/original window, then all the other windows will also -refresh. However, you can safely refresh any single other window, and on the next -command to the other windows, it will re-create the other window links without -losing content in the other windows. You can also close any other window and -it will be reopened the next time you try to call to it. - - -## Executing Commands -The MultiMonitorService adds the ability to run commands on other specified windows. -This allows opening up a study on another window without needing to refresh -it's contents. The command below shows an example of how this can be done: diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/ui/cine-service.md b/platform/docs/versioned_docs/version-3.10/platform/services/ui/cine-service.md deleted file mode 100644 index 707b5d97f7f..00000000000 --- a/platform/docs/versioned_docs/version-3.10/platform/services/ui/cine-service.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -sidebar_position: 7 -sidebar_label: CINE Service ---- - -# CINE Service - -TODO... diff --git a/platform/docs/versioned_docs/version-3.10/README.md b/platform/docs/versioned_docs/version-3.11/README.md similarity index 100% rename from platform/docs/versioned_docs/version-3.10/README.md rename to platform/docs/versioned_docs/version-3.11/README.md diff --git a/platform/docs/versioned_docs/version-3.10/assets/designs/architecture-diagram b/platform/docs/versioned_docs/version-3.11/assets/designs/architecture-diagram similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/designs/architecture-diagram rename to platform/docs/versioned_docs/version-3.11/assets/designs/architecture-diagram diff --git a/platform/docs/versioned_docs/version-3.10/assets/designs/canny-full.fig b/platform/docs/versioned_docs/version-3.11/assets/designs/canny-full.fig similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/designs/canny-full.fig rename to platform/docs/versioned_docs/version-3.11/assets/designs/canny-full.fig diff --git a/platform/docs/versioned_docs/version-3.10/assets/designs/cloud.svg b/platform/docs/versioned_docs/version-3.11/assets/designs/cloud.svg similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/designs/cloud.svg rename to platform/docs/versioned_docs/version-3.11/assets/designs/cloud.svg diff --git a/platform/docs/versioned_docs/version-3.10/assets/designs/embedded-viewer-diagram b/platform/docs/versioned_docs/version-3.11/assets/designs/embedded-viewer-diagram similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/designs/embedded-viewer-diagram rename to platform/docs/versioned_docs/version-3.11/assets/designs/embedded-viewer-diagram diff --git a/platform/docs/versioned_docs/version-3.10/assets/designs/nginx-image-archive.fig b/platform/docs/versioned_docs/version-3.11/assets/designs/nginx-image-archive.fig similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/designs/nginx-image-archive.fig rename to platform/docs/versioned_docs/version-3.11/assets/designs/nginx-image-archive.fig diff --git a/platform/docs/versioned_docs/version-3.10/assets/designs/npm-logo-red.svg b/platform/docs/versioned_docs/version-3.11/assets/designs/npm-logo-red.svg similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/designs/npm-logo-red.svg rename to platform/docs/versioned_docs/version-3.11/assets/designs/npm-logo-red.svg diff --git a/platform/docs/versioned_docs/version-3.10/assets/designs/scope-of-project.fig b/platform/docs/versioned_docs/version-3.11/assets/designs/scope-of-project.fig similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/designs/scope-of-project.fig rename to platform/docs/versioned_docs/version-3.11/assets/designs/scope-of-project.fig diff --git a/platform/docs/versioned_docs/version-3.10/assets/designs/user-access-control-request-flow.fig b/platform/docs/versioned_docs/version-3.11/assets/designs/user-access-control-request-flow.fig similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/designs/user-access-control-request-flow.fig rename to platform/docs/versioned_docs/version-3.11/assets/designs/user-access-control-request-flow.fig diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/Loading-Indicator.png b/platform/docs/versioned_docs/version-3.11/assets/img/Loading-Indicator.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/Loading-Indicator.png rename to platform/docs/versioned_docs/version-3.11/assets/img/Loading-Indicator.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/OHIF-e2e-test-studies.png b/platform/docs/versioned_docs/version-3.11/assets/img/OHIF-e2e-test-studies.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/OHIF-e2e-test-studies.png rename to platform/docs/versioned_docs/version-3.11/assets/img/OHIF-e2e-test-studies.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/SR-exported.png b/platform/docs/versioned_docs/version-3.11/assets/img/SR-exported.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/SR-exported.png rename to platform/docs/versioned_docs/version-3.11/assets/img/SR-exported.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/WORKFLOW_DEPLOY.png b/platform/docs/versioned_docs/version-3.11/assets/img/WORKFLOW_DEPLOY.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/WORKFLOW_DEPLOY.png rename to platform/docs/versioned_docs/version-3.11/assets/img/WORKFLOW_DEPLOY.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/WORKFLOW_PR_CHECKS.png b/platform/docs/versioned_docs/version-3.11/assets/img/WORKFLOW_PR_CHECKS.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/WORKFLOW_PR_CHECKS.png rename to platform/docs/versioned_docs/version-3.11/assets/img/WORKFLOW_PR_CHECKS.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png b/platform/docs/versioned_docs/version-3.11/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png rename to platform/docs/versioned_docs/version-3.11/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/WORKFLOW_RELEASE.png b/platform/docs/versioned_docs/version-3.11/assets/img/WORKFLOW_RELEASE.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/WORKFLOW_RELEASE.png rename to platform/docs/versioned_docs/version-3.11/assets/img/WORKFLOW_RELEASE.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/aboutModal.png b/platform/docs/versioned_docs/version-3.11/assets/img/aboutModal.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/aboutModal.png rename to platform/docs/versioned_docs/version-3.11/assets/img/aboutModal.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/add-extension.png b/platform/docs/versioned_docs/version-3.11/assets/img/add-extension.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/add-extension.png rename to platform/docs/versioned_docs/version-3.11/assets/img/add-extension.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/add-mode.png b/platform/docs/versioned_docs/version-3.11/assets/img/add-mode.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/add-mode.png rename to platform/docs/versioned_docs/version-3.11/assets/img/add-mode.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/azure1.png b/platform/docs/versioned_docs/version-3.11/assets/img/azure1.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/azure1.png rename to platform/docs/versioned_docs/version-3.11/assets/img/azure1.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/azure10.png b/platform/docs/versioned_docs/version-3.11/assets/img/azure10.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/azure10.png rename to platform/docs/versioned_docs/version-3.11/assets/img/azure10.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/azure2.png b/platform/docs/versioned_docs/version-3.11/assets/img/azure2.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/azure2.png rename to platform/docs/versioned_docs/version-3.11/assets/img/azure2.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/azure3.png b/platform/docs/versioned_docs/version-3.11/assets/img/azure3.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/azure3.png rename to platform/docs/versioned_docs/version-3.11/assets/img/azure3.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/azure4.png b/platform/docs/versioned_docs/version-3.11/assets/img/azure4.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/azure4.png rename to platform/docs/versioned_docs/version-3.11/assets/img/azure4.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/azure5.png b/platform/docs/versioned_docs/version-3.11/assets/img/azure5.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/azure5.png rename to platform/docs/versioned_docs/version-3.11/assets/img/azure5.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/azure6.png b/platform/docs/versioned_docs/version-3.11/assets/img/azure6.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/azure6.png rename to platform/docs/versioned_docs/version-3.11/assets/img/azure6.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/azure7.png b/platform/docs/versioned_docs/version-3.11/assets/img/azure7.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/azure7.png rename to platform/docs/versioned_docs/version-3.11/assets/img/azure7.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/azure8.png b/platform/docs/versioned_docs/version-3.11/assets/img/azure8.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/azure8.png rename to platform/docs/versioned_docs/version-3.11/assets/img/azure8.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/azure9.png b/platform/docs/versioned_docs/version-3.11/assets/img/azure9.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/azure9.png rename to platform/docs/versioned_docs/version-3.11/assets/img/azure9.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/browser-console-non-secure-context.png b/platform/docs/versioned_docs/version-3.11/assets/img/browser-console-non-secure-context.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/browser-console-non-secure-context.png rename to platform/docs/versioned_docs/version-3.11/assets/img/browser-console-non-secure-context.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/captureViewportModal.png b/platform/docs/versioned_docs/version-3.11/assets/img/captureViewportModal.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/captureViewportModal.png rename to platform/docs/versioned_docs/version-3.11/assets/img/captureViewportModal.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/cli-search-no-verbose.png b/platform/docs/versioned_docs/version-3.11/assets/img/cli-search-no-verbose.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/cli-search-no-verbose.png rename to platform/docs/versioned_docs/version-3.11/assets/img/cli-search-no-verbose.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/cli-search-with-verbose.png b/platform/docs/versioned_docs/version-3.11/assets/img/cli-search-with-verbose.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/cli-search-with-verbose.png rename to platform/docs/versioned_docs/version-3.11/assets/img/cli-search-with-verbose.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/clock-mode.png b/platform/docs/versioned_docs/version-3.11/assets/img/clock-mode.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/clock-mode.png rename to platform/docs/versioned_docs/version-3.11/assets/img/clock-mode.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/clock-mode1.png b/platform/docs/versioned_docs/version-3.11/assets/img/clock-mode1.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/clock-mode1.png rename to platform/docs/versioned_docs/version-3.11/assets/img/clock-mode1.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/colorbarImage.png b/platform/docs/versioned_docs/version-3.11/assets/img/colorbarImage.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/colorbarImage.png rename to platform/docs/versioned_docs/version-3.11/assets/img/colorbarImage.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/context-menu.jpg b/platform/docs/versioned_docs/version-3.11/assets/img/context-menu.jpg similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/context-menu.jpg rename to platform/docs/versioned_docs/version-3.11/assets/img/context-menu.jpg diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/cornerstone-tools-link.gif b/platform/docs/versioned_docs/version-3.11/assets/img/cornerstone-tools-link.gif similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/cornerstone-tools-link.gif rename to platform/docs/versioned_docs/version-3.11/assets/img/cornerstone-tools-link.gif diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/cors-browser-console-errors.png b/platform/docs/versioned_docs/version-3.11/assets/img/cors-browser-console-errors.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/cors-browser-console-errors.png rename to platform/docs/versioned_docs/version-3.11/assets/img/cors-browser-console-errors.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/cors-network-panel-errors.png b/platform/docs/versioned_docs/version-3.11/assets/img/cors-network-panel-errors.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/cors-network-panel-errors.png rename to platform/docs/versioned_docs/version-3.11/assets/img/cors-network-panel-errors.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/create-extension.png b/platform/docs/versioned_docs/version-3.11/assets/img/create-extension.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/create-extension.png rename to platform/docs/versioned_docs/version-3.11/assets/img/create-extension.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/create-mode.png b/platform/docs/versioned_docs/version-3.11/assets/img/create-mode.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/create-mode.png rename to platform/docs/versioned_docs/version-3.11/assets/img/create-mode.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/custom-logo.png b/platform/docs/versioned_docs/version-3.11/assets/img/custom-logo.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/custom-logo.png rename to platform/docs/versioned_docs/version-3.11/assets/img/custom-logo.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/customizable-overlay.jpeg b/platform/docs/versioned_docs/version-3.11/assets/img/customizable-overlay.jpeg similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/customizable-overlay.jpeg rename to platform/docs/versioned_docs/version-3.11/assets/img/customizable-overlay.jpeg diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/data-source-configuration-ui.png b/platform/docs/versioned_docs/version-3.11/assets/img/data-source-configuration-ui.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/data-source-configuration-ui.png rename to platform/docs/versioned_docs/version-3.11/assets/img/data-source-configuration-ui.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/dcm4chee-upload.gif b/platform/docs/versioned_docs/version-3.11/assets/img/dcm4chee-upload.gif similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/dcm4chee-upload.gif rename to platform/docs/versioned_docs/version-3.11/assets/img/dcm4chee-upload.gif diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/demo-4d.webp b/platform/docs/versioned_docs/version-3.11/assets/img/demo-4d.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/demo-4d.webp rename to platform/docs/versioned_docs/version-3.11/assets/img/demo-4d.webp diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/demo-measurements.webp b/platform/docs/versioned_docs/version-3.11/assets/img/demo-measurements.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/demo-measurements.webp rename to platform/docs/versioned_docs/version-3.11/assets/img/demo-measurements.webp diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/demo-pdf.webp b/platform/docs/versioned_docs/version-3.11/assets/img/demo-pdf.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/demo-pdf.webp rename to platform/docs/versioned_docs/version-3.11/assets/img/demo-pdf.webp diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/demo-ptct.webp b/platform/docs/versioned_docs/version-3.11/assets/img/demo-ptct.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/demo-ptct.webp rename to platform/docs/versioned_docs/version-3.11/assets/img/demo-ptct.webp diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/demo-rtstruct.webp b/platform/docs/versioned_docs/version-3.11/assets/img/demo-rtstruct.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/demo-rtstruct.webp rename to platform/docs/versioned_docs/version-3.11/assets/img/demo-rtstruct.webp diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/demo-segmentation.webp b/platform/docs/versioned_docs/version-3.11/assets/img/demo-segmentation.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/demo-segmentation.webp rename to platform/docs/versioned_docs/version-3.11/assets/img/demo-segmentation.webp diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/demo-video.webp b/platform/docs/versioned_docs/version-3.11/assets/img/demo-video.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/demo-video.webp rename to platform/docs/versioned_docs/version-3.11/assets/img/demo-video.webp diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/demo-volume-rendering.webp b/platform/docs/versioned_docs/version-3.11/assets/img/demo-volume-rendering.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/demo-volume-rendering.webp rename to platform/docs/versioned_docs/version-3.11/assets/img/demo-volume-rendering.webp diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/demo-volumeRendering.png b/platform/docs/versioned_docs/version-3.11/assets/img/demo-volumeRendering.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/demo-volumeRendering.png rename to platform/docs/versioned_docs/version-3.11/assets/img/demo-volumeRendering.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/dicom-json-public.png b/platform/docs/versioned_docs/version-3.11/assets/img/dicom-json-public.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/dicom-json-public.png rename to platform/docs/versioned_docs/version-3.11/assets/img/dicom-json-public.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/dicom-json.png b/platform/docs/versioned_docs/version-3.11/assets/img/dicom-json.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/dicom-json.png rename to platform/docs/versioned_docs/version-3.11/assets/img/dicom-json.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/docker-pacs.png b/platform/docs/versioned_docs/version-3.11/assets/img/docker-pacs.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/docker-pacs.png rename to platform/docs/versioned_docs/version-3.11/assets/img/docker-pacs.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/e2e-cypress-final.png b/platform/docs/versioned_docs/version-3.11/assets/img/e2e-cypress-final.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/e2e-cypress-final.png rename to platform/docs/versioned_docs/version-3.11/assets/img/e2e-cypress-final.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/e2e-cypress.png b/platform/docs/versioned_docs/version-3.11/assets/img/e2e-cypress.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/e2e-cypress.png rename to platform/docs/versioned_docs/version-3.11/assets/img/e2e-cypress.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/embedded-viewer-diagram.png b/platform/docs/versioned_docs/version-3.11/assets/img/embedded-viewer-diagram.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/embedded-viewer-diagram.png rename to platform/docs/versioned_docs/version-3.11/assets/img/embedded-viewer-diagram.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/filtering-worklist.png b/platform/docs/versioned_docs/version-3.11/assets/img/filtering-worklist.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/filtering-worklist.png rename to platform/docs/versioned_docs/version-3.11/assets/img/filtering-worklist.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/github-readme-branches-Jun2024.png b/platform/docs/versioned_docs/version-3.11/assets/img/github-readme-branches-Jun2024.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/github-readme-branches-Jun2024.png rename to platform/docs/versioned_docs/version-3.11/assets/img/github-readme-branches-Jun2024.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/google-create-credentials.png b/platform/docs/versioned_docs/version-3.11/assets/img/google-create-credentials.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/google-create-credentials.png rename to platform/docs/versioned_docs/version-3.11/assets/img/google-create-credentials.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/google-enable-apis.png b/platform/docs/versioned_docs/version-3.11/assets/img/google-enable-apis.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/google-enable-apis.png rename to platform/docs/versioned_docs/version-3.11/assets/img/google-enable-apis.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/google-healthcare-service-agent-warning.png b/platform/docs/versioned_docs/version-3.11/assets/img/google-healthcare-service-agent-warning.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/google-healthcare-service-agent-warning.png rename to platform/docs/versioned_docs/version-3.11/assets/img/google-healthcare-service-agent-warning.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/google-manually-add-scopes.png b/platform/docs/versioned_docs/version-3.11/assets/img/google-manually-add-scopes.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/google-manually-add-scopes.png rename to platform/docs/versioned_docs/version-3.11/assets/img/google-manually-add-scopes.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/google-oauth-consent-steps.png b/platform/docs/versioned_docs/version-3.11/assets/img/google-oauth-consent-steps.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/google-oauth-consent-steps.png rename to platform/docs/versioned_docs/version-3.11/assets/img/google-oauth-consent-steps.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/google-projects-drop-down.png b/platform/docs/versioned_docs/version-3.11/assets/img/google-projects-drop-down.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/google-projects-drop-down.png rename to platform/docs/versioned_docs/version-3.11/assets/img/google-projects-drop-down.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/google-provided-accounts-checkbox.png b/platform/docs/versioned_docs/version-3.11/assets/img/google-provided-accounts-checkbox.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/google-provided-accounts-checkbox.png rename to platform/docs/versioned_docs/version-3.11/assets/img/google-provided-accounts-checkbox.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/hangingProtocolExample.png b/platform/docs/versioned_docs/version-3.11/assets/img/hangingProtocolExample.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/hangingProtocolExample.png rename to platform/docs/versioned_docs/version-3.11/assets/img/hangingProtocolExample.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/iframe-basic.png b/platform/docs/versioned_docs/version-3.11/assets/img/iframe-basic.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/iframe-basic.png rename to platform/docs/versioned_docs/version-3.11/assets/img/iframe-basic.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/iframe-headers.png b/platform/docs/versioned_docs/version-3.11/assets/img/iframe-headers.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/iframe-headers.png rename to platform/docs/versioned_docs/version-3.11/assets/img/iframe-headers.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/jwt-explained.png b/platform/docs/versioned_docs/version-3.11/assets/img/jwt-explained.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/jwt-explained.png rename to platform/docs/versioned_docs/version-3.11/assets/img/jwt-explained.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/keycloak-default-theme.png b/platform/docs/versioned_docs/version-3.11/assets/img/keycloak-default-theme.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/keycloak-default-theme.png rename to platform/docs/versioned_docs/version-3.11/assets/img/keycloak-default-theme.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/keycloak-ohif-theme.png b/platform/docs/versioned_docs/version-3.11/assets/img/keycloak-ohif-theme.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/keycloak-ohif-theme.png rename to platform/docs/versioned_docs/version-3.11/assets/img/keycloak-ohif-theme.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/labelling-flow.png b/platform/docs/versioned_docs/version-3.11/assets/img/labelling-flow.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/labelling-flow.png rename to platform/docs/versioned_docs/version-3.11/assets/img/labelling-flow.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/large-pt-ct.jpeg b/platform/docs/versioned_docs/version-3.11/assets/img/large-pt-ct.jpeg similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/large-pt-ct.jpeg rename to platform/docs/versioned_docs/version-3.11/assets/img/large-pt-ct.jpeg diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/layoutSelectorAdvancedPresetGeneratorImage.png b/platform/docs/versioned_docs/version-3.11/assets/img/layoutSelectorAdvancedPresetGeneratorImage.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/layoutSelectorAdvancedPresetGeneratorImage.png rename to platform/docs/versioned_docs/version-3.11/assets/img/layoutSelectorAdvancedPresetGeneratorImage.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/layoutSelectorCommonPresetsImage.png b/platform/docs/versioned_docs/version-3.11/assets/img/layoutSelectorCommonPresetsImage.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/layoutSelectorCommonPresetsImage.png rename to platform/docs/versioned_docs/version-3.11/assets/img/layoutSelectorCommonPresetsImage.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/loading-indicator-icon.png b/platform/docs/versioned_docs/version-3.11/assets/img/loading-indicator-icon.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/loading-indicator-icon.png rename to platform/docs/versioned_docs/version-3.11/assets/img/loading-indicator-icon.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/loading-indicator-percent.png b/platform/docs/versioned_docs/version-3.11/assets/img/loading-indicator-percent.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/loading-indicator-percent.png rename to platform/docs/versioned_docs/version-3.11/assets/img/loading-indicator-percent.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/locizeSponsor.svg b/platform/docs/versioned_docs/version-3.11/assets/img/locizeSponsor.svg similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/locizeSponsor.svg rename to platform/docs/versioned_docs/version-3.11/assets/img/locizeSponsor.svg diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/locked-sr.png b/platform/docs/versioned_docs/version-3.11/assets/img/locked-sr.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/locked-sr.png rename to platform/docs/versioned_docs/version-3.11/assets/img/locked-sr.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/measurement-labels-auto.png b/platform/docs/versioned_docs/version-3.11/assets/img/measurement-labels-auto.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/measurement-labels-auto.png rename to platform/docs/versioned_docs/version-3.11/assets/img/measurement-labels-auto.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/measurement-panel-1.png b/platform/docs/versioned_docs/version-3.11/assets/img/measurement-panel-1.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/measurement-panel-1.png rename to platform/docs/versioned_docs/version-3.11/assets/img/measurement-panel-1.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/measurement-panel-prompt.png b/platform/docs/versioned_docs/version-3.11/assets/img/measurement-panel-prompt.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/measurement-panel-prompt.png rename to platform/docs/versioned_docs/version-3.11/assets/img/measurement-panel-prompt.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/measurement-panel-tracked.png b/platform/docs/versioned_docs/version-3.11/assets/img/measurement-panel-tracked.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/measurement-panel-tracked.png rename to platform/docs/versioned_docs/version-3.11/assets/img/measurement-panel-tracked.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/measurement-temporary.png b/platform/docs/versioned_docs/version-3.11/assets/img/measurement-temporary.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/measurement-temporary.png rename to platform/docs/versioned_docs/version-3.11/assets/img/measurement-temporary.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/measurements-prevNext.png b/platform/docs/versioned_docs/version-3.11/assets/img/measurements-prevNext.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/measurements-prevNext.png rename to platform/docs/versioned_docs/version-3.11/assets/img/measurements-prevNext.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/memory-profiling-regular.png b/platform/docs/versioned_docs/version-3.11/assets/img/memory-profiling-regular.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/memory-profiling-regular.png rename to platform/docs/versioned_docs/version-3.11/assets/img/memory-profiling-regular.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/microscopy.webp b/platform/docs/versioned_docs/version-3.11/assets/img/microscopy.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/microscopy.webp rename to platform/docs/versioned_docs/version-3.11/assets/img/microscopy.webp diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/migration-modes.png b/platform/docs/versioned_docs/version-3.11/assets/img/migration-modes.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/migration-modes.png rename to platform/docs/versioned_docs/version-3.11/assets/img/migration-modes.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/migration-split-button.png b/platform/docs/versioned_docs/version-3.11/assets/img/migration-split-button.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/migration-split-button.png rename to platform/docs/versioned_docs/version-3.11/assets/img/migration-split-button.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/mode-archs.png b/platform/docs/versioned_docs/version-3.11/assets/img/mode-archs.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/mode-archs.png rename to platform/docs/versioned_docs/version-3.11/assets/img/mode-archs.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/mode-clock.png b/platform/docs/versioned_docs/version-3.11/assets/img/mode-clock.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/mode-clock.png rename to platform/docs/versioned_docs/version-3.11/assets/img/mode-clock.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/mode-template.png b/platform/docs/versioned_docs/version-3.11/assets/img/mode-template.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/mode-template.png rename to platform/docs/versioned_docs/version-3.11/assets/img/mode-template.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/nginx-image-archive.png b/platform/docs/versioned_docs/version-3.11/assets/img/nginx-image-archive.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/nginx-image-archive.png rename to platform/docs/versioned_docs/version-3.11/assets/img/nginx-image-archive.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/ohif-cli-list.png b/platform/docs/versioned_docs/version-3.11/assets/img/ohif-cli-list.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/ohif-cli-list.png rename to platform/docs/versioned_docs/version-3.11/assets/img/ohif-cli-list.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/ohif-non-secure-context.png b/platform/docs/versioned_docs/version-3.11/assets/img/ohif-non-secure-context.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/ohif-non-secure-context.png rename to platform/docs/versioned_docs/version-3.11/assets/img/ohif-non-secure-context.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/ohif-pacs-keycloak.png b/platform/docs/versioned_docs/version-3.11/assets/img/ohif-pacs-keycloak.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/ohif-pacs-keycloak.png rename to platform/docs/versioned_docs/version-3.11/assets/img/ohif-pacs-keycloak.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/open-graph.png b/platform/docs/versioned_docs/version-3.11/assets/img/open-graph.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/open-graph.png rename to platform/docs/versioned_docs/version-3.11/assets/img/open-graph.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/overview.png b/platform/docs/versioned_docs/version-3.11/assets/img/overview.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/overview.png rename to platform/docs/versioned_docs/version-3.11/assets/img/overview.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/panel-module-left-right.png b/platform/docs/versioned_docs/version-3.11/assets/img/panel-module-left-right.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/panel-module-left-right.png rename to platform/docs/versioned_docs/version-3.11/assets/img/panel-module-left-right.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/panel-module-v3.png b/platform/docs/versioned_docs/version-3.11/assets/img/panel-module-v3.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/panel-module-v3.png rename to platform/docs/versioned_docs/version-3.11/assets/img/panel-module-v3.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/panelmodule-icon.png b/platform/docs/versioned_docs/version-3.11/assets/img/panelmodule-icon.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/panelmodule-icon.png rename to platform/docs/versioned_docs/version-3.11/assets/img/panelmodule-icon.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/preferSizeOverAccuracy.png b/platform/docs/versioned_docs/version-3.11/assets/img/preferSizeOverAccuracy.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/preferSizeOverAccuracy.png rename to platform/docs/versioned_docs/version-3.11/assets/img/preferSizeOverAccuracy.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/progressDropdown.png b/platform/docs/versioned_docs/version-3.11/assets/img/progressDropdown.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/progressDropdown.png rename to platform/docs/versioned_docs/version-3.11/assets/img/progressDropdown.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/promptAddSeriesContent.png b/platform/docs/versioned_docs/version-3.11/assets/img/promptAddSeriesContent.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/promptAddSeriesContent.png rename to platform/docs/versioned_docs/version-3.11/assets/img/promptAddSeriesContent.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/promptBeginTrackingContent.png b/platform/docs/versioned_docs/version-3.11/assets/img/promptBeginTrackingContent.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/promptBeginTrackingContent.png rename to platform/docs/versioned_docs/version-3.11/assets/img/promptBeginTrackingContent.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/promptDiscardDirtyContent.png b/platform/docs/versioned_docs/version-3.11/assets/img/promptDiscardDirtyContent.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/promptDiscardDirtyContent.png rename to platform/docs/versioned_docs/version-3.11/assets/img/promptDiscardDirtyContent.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/promptDiscardSeriesContent.png b/platform/docs/versioned_docs/version-3.11/assets/img/promptDiscardSeriesContent.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/promptDiscardSeriesContent.png rename to platform/docs/versioned_docs/version-3.11/assets/img/promptDiscardSeriesContent.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/promptDiscardStudyContent.png b/platform/docs/versioned_docs/version-3.11/assets/img/promptDiscardStudyContent.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/promptDiscardStudyContent.png rename to platform/docs/versioned_docs/version-3.11/assets/img/promptDiscardStudyContent.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/promptRtstructContent.png b/platform/docs/versioned_docs/version-3.11/assets/img/promptRtstructContent.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/promptRtstructContent.png rename to platform/docs/versioned_docs/version-3.11/assets/img/promptRtstructContent.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/promptSRTrackingContent.png b/platform/docs/versioned_docs/version-3.11/assets/img/promptSRTrackingContent.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/promptSRTrackingContent.png rename to platform/docs/versioned_docs/version-3.11/assets/img/promptSRTrackingContent.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/promptTrackStudyContent.png b/platform/docs/versioned_docs/version-3.11/assets/img/promptTrackStudyContent.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/promptTrackStudyContent.png rename to platform/docs/versioned_docs/version-3.11/assets/img/promptTrackStudyContent.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/promptsegmentationContent.png b/platform/docs/versioned_docs/version-3.11/assets/img/promptsegmentationContent.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/promptsegmentationContent.png rename to platform/docs/versioned_docs/version-3.11/assets/img/promptsegmentationContent.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/reference-lines-from-start.png b/platform/docs/versioned_docs/version-3.11/assets/img/reference-lines-from-start.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/reference-lines-from-start.png rename to platform/docs/versioned_docs/version-3.11/assets/img/reference-lines-from-start.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/restore-exported-sr.png b/platform/docs/versioned_docs/version-3.11/assets/img/restore-exported-sr.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/restore-exported-sr.png rename to platform/docs/versioned_docs/version-3.11/assets/img/restore-exported-sr.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/scope-of-project.png b/platform/docs/versioned_docs/version-3.11/assets/img/scope-of-project.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/scope-of-project.png rename to platform/docs/versioned_docs/version-3.11/assets/img/scope-of-project.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/segDisplayEditingFalse.png b/platform/docs/versioned_docs/version-3.11/assets/img/segDisplayEditingFalse.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/segDisplayEditingFalse.png rename to platform/docs/versioned_docs/version-3.11/assets/img/segDisplayEditingFalse.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/segDisplayEditingTrue.png b/platform/docs/versioned_docs/version-3.11/assets/img/segDisplayEditingTrue.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/segDisplayEditingTrue.png rename to platform/docs/versioned_docs/version-3.11/assets/img/segDisplayEditingTrue.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/segmentation-overlay.png b/platform/docs/versioned_docs/version-3.11/assets/img/segmentation-overlay.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/segmentation-overlay.png rename to platform/docs/versioned_docs/version-3.11/assets/img/segmentation-overlay.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/segmentationShowAddSegmentImage.png b/platform/docs/versioned_docs/version-3.11/assets/img/segmentationShowAddSegmentImage.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/segmentationShowAddSegmentImage.png rename to platform/docs/versioned_docs/version-3.11/assets/img/segmentationShowAddSegmentImage.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/segmentationTableModeImage.png b/platform/docs/versioned_docs/version-3.11/assets/img/segmentationTableModeImage.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/segmentationTableModeImage.png rename to platform/docs/versioned_docs/version-3.11/assets/img/segmentationTableModeImage.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/segmentationTableModeImage2.png b/platform/docs/versioned_docs/version-3.11/assets/img/segmentationTableModeImage2.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/segmentationTableModeImage2.png rename to platform/docs/versioned_docs/version-3.11/assets/img/segmentationTableModeImage2.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/self-signed-cert-advanced-warning.png b/platform/docs/versioned_docs/version-3.11/assets/img/self-signed-cert-advanced-warning.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/self-signed-cert-advanced-warning.png rename to platform/docs/versioned_docs/version-3.11/assets/img/self-signed-cert-advanced-warning.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/self-signed-cert-warning.png b/platform/docs/versioned_docs/version-3.11/assets/img/self-signed-cert-warning.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/self-signed-cert-warning.png rename to platform/docs/versioned_docs/version-3.11/assets/img/self-signed-cert-warning.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/seriesSort.png b/platform/docs/versioned_docs/version-3.11/assets/img/seriesSort.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/seriesSort.png rename to platform/docs/versioned_docs/version-3.11/assets/img/seriesSort.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/services-data.png b/platform/docs/versioned_docs/version-3.11/assets/img/services-data.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/services-data.png rename to platform/docs/versioned_docs/version-3.11/assets/img/services-data.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/services-measurements.png b/platform/docs/versioned_docs/version-3.11/assets/img/services-measurements.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/services-measurements.png rename to platform/docs/versioned_docs/version-3.11/assets/img/services-measurements.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/services-ui.png b/platform/docs/versioned_docs/version-3.11/assets/img/services-ui.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/services-ui.png rename to platform/docs/versioned_docs/version-3.11/assets/img/services-ui.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/services.png b/platform/docs/versioned_docs/version-3.11/assets/img/services.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/services.png rename to platform/docs/versioned_docs/version-3.11/assets/img/services.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/static-dicom-web.png b/platform/docs/versioned_docs/version-3.11/assets/img/static-dicom-web.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/static-dicom-web.png rename to platform/docs/versioned_docs/version-3.11/assets/img/static-dicom-web.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/studyMenuItemsImage.png b/platform/docs/versioned_docs/version-3.11/assets/img/studyMenuItemsImage.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/studyMenuItemsImage.png rename to platform/docs/versioned_docs/version-3.11/assets/img/studyMenuItemsImage.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/surge-deploy.gif b/platform/docs/versioned_docs/version-3.11/assets/img/surge-deploy.gif similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/surge-deploy.gif rename to platform/docs/versioned_docs/version-3.11/assets/img/surge-deploy.gif diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/template-extension-files.png b/platform/docs/versioned_docs/version-3.11/assets/img/template-extension-files.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/template-extension-files.png rename to platform/docs/versioned_docs/version-3.11/assets/img/template-extension-files.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/template-mode-files.png b/platform/docs/versioned_docs/version-3.11/assets/img/template-mode-files.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/template-mode-files.png rename to platform/docs/versioned_docs/version-3.11/assets/img/template-mode-files.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/template-mode-ui.png b/platform/docs/versioned_docs/version-3.11/assets/img/template-mode-ui.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/template-mode-ui.png rename to platform/docs/versioned_docs/version-3.11/assets/img/template-mode-ui.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/thumbnailMenuItemsImage.png b/platform/docs/versioned_docs/version-3.11/assets/img/thumbnailMenuItemsImage.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/thumbnailMenuItemsImage.png rename to platform/docs/versioned_docs/version-3.11/assets/img/thumbnailMenuItemsImage.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/toolbar-module.png b/platform/docs/versioned_docs/version-3.11/assets/img/toolbar-module.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/toolbar-module.png rename to platform/docs/versioned_docs/version-3.11/assets/img/toolbar-module.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/toolbarModule-layout.png b/platform/docs/versioned_docs/version-3.11/assets/img/toolbarModule-layout.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/toolbarModule-layout.png rename to platform/docs/versioned_docs/version-3.11/assets/img/toolbarModule-layout.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/toolbarModule-nested-buttons.png b/platform/docs/versioned_docs/version-3.11/assets/img/toolbarModule-nested-buttons.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/toolbarModule-nested-buttons.png rename to platform/docs/versioned_docs/version-3.11/assets/img/toolbarModule-nested-buttons.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/toolbarModule-zoom.png b/platform/docs/versioned_docs/version-3.11/assets/img/toolbarModule-zoom.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/toolbarModule-zoom.png rename to platform/docs/versioned_docs/version-3.11/assets/img/toolbarModule-zoom.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/toolbox-modal.png b/platform/docs/versioned_docs/version-3.11/assets/img/toolbox-modal.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/toolbox-modal.png rename to platform/docs/versioned_docs/version-3.11/assets/img/toolbox-modal.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/tracked-not-tracked.png b/platform/docs/versioned_docs/version-3.11/assets/img/tracked-not-tracked.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/tracked-not-tracked.png rename to platform/docs/versioned_docs/version-3.11/assets/img/tracked-not-tracked.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/tracking-workflow1.png b/platform/docs/versioned_docs/version-3.11/assets/img/tracking-workflow1.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/tracking-workflow1.png rename to platform/docs/versioned_docs/version-3.11/assets/img/tracking-workflow1.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/tracking-workflow2.png b/platform/docs/versioned_docs/version-3.11/assets/img/tracking-workflow2.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/tracking-workflow2.png rename to platform/docs/versioned_docs/version-3.11/assets/img/tracking-workflow2.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/tracking-workflow3.png b/platform/docs/versioned_docs/version-3.11/assets/img/tracking-workflow3.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/tracking-workflow3.png rename to platform/docs/versioned_docs/version-3.11/assets/img/tracking-workflow3.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/ui-modal.gif b/platform/docs/versioned_docs/version-3.11/assets/img/ui-modal.gif similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/ui-modal.gif rename to platform/docs/versioned_docs/version-3.11/assets/img/ui-modal.gif diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/ui-services.png b/platform/docs/versioned_docs/version-3.11/assets/img/ui-services.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/ui-services.png rename to platform/docs/versioned_docs/version-3.11/assets/img/ui-services.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/uploader.gif b/platform/docs/versioned_docs/version-3.11/assets/img/uploader.gif similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/uploader.gif rename to platform/docs/versioned_docs/version-3.11/assets/img/uploader.gif diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-access-control-request-flow.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-access-control-request-flow.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-access-control-request-flow.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-access-control-request-flow.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-hotkeys-default.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-hotkeys-default.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-hotkeys-default.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-hotkeys-default.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-hotkeys.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-hotkeys.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-hotkeys.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-hotkeys.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-measurement-export.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-measurement-export.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-measurement-export.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-measurement-export.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-open-viewer.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-open-viewer.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-open-viewer.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-open-viewer.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-study-filter.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-study-filter.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-study-filter.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-study-filter.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-study-list.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-study-list.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-study-list.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-study-list.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-study-next.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-study-next.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-study-next.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-study-next.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-study-panel.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-study-panel.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-study-panel.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-study-panel.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-study-summary.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-study-summary.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-study-summary.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-study-summary.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-studyist-modespecific.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-studyist-modespecific.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-studyist-modespecific.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-studyist-modespecific.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-toolbar-download-icon.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-toolbar-download-icon.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-toolbar-download-icon.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-toolbar-download-icon.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-toolbar-extra.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-toolbar-extra.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-toolbar-extra.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-toolbar-extra.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-toolbar-preset.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-toolbar-preset.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-toolbar-preset.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-toolbar-preset.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-toolbarDownload.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-toolbarDownload.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-toolbarDownload.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-toolbarDownload.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-viewer-layout.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-viewer-layout.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-viewer-layout.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-viewer-layout.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-viewer-main.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-viewer-main.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-viewer-main.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-viewer-main.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-viewer-toolbar-measurements.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-viewer-toolbar-measurements.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-viewer-toolbar-measurements.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-viewer-toolbar-measurements.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-viewer-toolbar.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-viewer-toolbar.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-viewer-toolbar.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-viewer-toolbar.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/user-viewer.png b/platform/docs/versioned_docs/version-3.11/assets/img/user-viewer.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/user-viewer.png rename to platform/docs/versioned_docs/version-3.11/assets/img/user-viewer.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/viewport-action-corners.png b/platform/docs/versioned_docs/version-3.11/assets/img/viewport-action-corners.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/viewport-action-corners.png rename to platform/docs/versioned_docs/version-3.11/assets/img/viewport-action-corners.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/viewport-download-warning.png b/platform/docs/versioned_docs/version-3.11/assets/img/viewport-download-warning.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/viewport-download-warning.png rename to platform/docs/versioned_docs/version-3.11/assets/img/viewport-download-warning.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/viewport-notification.png b/platform/docs/versioned_docs/version-3.11/assets/img/viewport-notification.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/viewport-notification.png rename to platform/docs/versioned_docs/version-3.11/assets/img/viewport-notification.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/viewportModule-layout.png b/platform/docs/versioned_docs/version-3.11/assets/img/viewportModule-layout.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/viewportModule-layout.png rename to platform/docs/versioned_docs/version-3.11/assets/img/viewportModule-layout.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/viewportModule.png b/platform/docs/versioned_docs/version-3.11/assets/img/viewportModule.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/viewportModule.png rename to platform/docs/versioned_docs/version-3.11/assets/img/viewportModule.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/viewportOverlay-customization.png b/platform/docs/versioned_docs/version-3.11/assets/img/viewportOverlay-customization.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/viewportOverlay-customization.png rename to platform/docs/versioned_docs/version-3.11/assets/img/viewportOverlay-customization.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/webgl-int16.png b/platform/docs/versioned_docs/version-3.11/assets/img/webgl-int16.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/webgl-int16.png rename to platform/docs/versioned_docs/version-3.11/assets/img/webgl-int16.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/webgl-report-norm16.png b/platform/docs/versioned_docs/version-3.11/assets/img/webgl-report-norm16.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/webgl-report-norm16.png rename to platform/docs/versioned_docs/version-3.11/assets/img/webgl-report-norm16.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/windowLevelActionMenu.png b/platform/docs/versioned_docs/version-3.11/assets/img/windowLevelActionMenu.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/windowLevelActionMenu.png rename to platform/docs/versioned_docs/version-3.11/assets/img/windowLevelActionMenu.png diff --git a/platform/docs/versioned_docs/version-3.10/assets/img/windowLevelPresets.png b/platform/docs/versioned_docs/version-3.11/assets/img/windowLevelPresets.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/assets/img/windowLevelPresets.png rename to platform/docs/versioned_docs/version-3.11/assets/img/windowLevelPresets.png diff --git a/platform/docs/versioned_docs/version-3.10/configuration/_category_.json b/platform/docs/versioned_docs/version-3.11/configuration/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/configuration/_category_.json rename to platform/docs/versioned_docs/version-3.11/configuration/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/configuration/configurationFiles.md b/platform/docs/versioned_docs/version-3.11/configuration/configurationFiles.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/configuration/configurationFiles.md rename to platform/docs/versioned_docs/version-3.11/configuration/configurationFiles.md index c02e2955dcf..0bdeb276507 100644 --- a/platform/docs/versioned_docs/version-3.10/configuration/configurationFiles.md +++ b/platform/docs/versioned_docs/version-3.11/configuration/configurationFiles.md @@ -1,6 +1,8 @@ --- sidebar_position: 1 sidebar_label: Configuration Files +title: Configuration Files +summary: Comprehensive guide to configuring OHIF Viewer, including data sources, environment variables, advanced options like study prefetching, and detailed explanations of configuration parameters for performance optimization and feature customization. --- # Config files @@ -184,6 +186,7 @@ if auth headers are used, a preflight request is required. } ``` - `showLoadingIndicator`: (default to true), if set to false, the loading indicator will not be shown when navigating between studies. +- `showStudyList`: (default to false), if set to false, the OHIF search (or work list) page will not be shown nor will there be a back button (chevron) in the viewer to navigate to it - `useNorm16Texture`: (default to false), if set to true, it will use 16 bit data type for the image data wherever possible which has significant impact on reducing the memory usage. However, the 16Bit textures require EXT_texture_norm16 extension in webGL 2.0 (you can check if you have it here https://webglreport.com/?v=2). In addition to the extension, there are reported problems for Intel Macs that might cause the viewer to crash. In summary, it is great a configuration if you have support for it. - `useSharedArrayBuffer` (default to 'TRUE', options: 'AUTO', 'FALSE', 'TRUE', note that these are strings), for volume loading we use sharedArrayBuffer to be able to diff --git a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/_category_.json b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/configuration/dataSources/_category_.json rename to platform/docs/versioned_docs/version-3.11/configuration/dataSources/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/configuration-ui.md b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/configuration-ui.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/configuration/dataSources/configuration-ui.md rename to platform/docs/versioned_docs/version-3.11/configuration/dataSources/configuration-ui.md index 200fc167f69..0b76d58fcb4 100644 --- a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/configuration-ui.md +++ b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/configuration-ui.md @@ -1,6 +1,8 @@ --- sidebar_position: 6 sidebar_label: Configuration UI +title: Configuration UI +summary: Describes interfaces for implementing configurable data sources in OHIF, with details on BaseDataSourceConfigurationAPI and BaseDataSourceConfigurationAPIItem to enable generic UIs for hierarchical data source configuration. --- # Configuration UI diff --git a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/dicom-json.md b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-json.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/configuration/dataSources/dicom-json.md rename to platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-json.md index 84e7be228ff..a183c47d1bb 100644 --- a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/dicom-json.md +++ b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-json.md @@ -1,6 +1,8 @@ --- sidebar_position: 3 sidebar_label: DICOM JSON +title: DICOM JSON +summary: Explains how to configure and use the DICOM JSON data source format in OHIF, including structure of the JSON file with study/series/instance metadata, implementation examples, and local deployment instructions. --- # DICOM JSON diff --git a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/dicom-web-proxy.md b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web-proxy.md similarity index 88% rename from platform/docs/versioned_docs/version-3.10/configuration/dataSources/dicom-web-proxy.md rename to platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web-proxy.md index faeea85e619..8f96bc96de1 100644 --- a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/dicom-web-proxy.md +++ b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web-proxy.md @@ -1,6 +1,8 @@ --- sidebar_position: 4 sidebar_label: DICOMweb Proxy +title: DICOMweb Proxy +summary: Documents the DICOMweb Proxy data source which constructs a dynamic DICOMweb datasource from a configuration JSON file, allowing OHIF to delegate subsequent requests for metadata and images to the configured server. --- # DICOMweb Proxy diff --git a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/dicom-web.md b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/configuration/dataSources/dicom-web.md rename to platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md index 2707370feb8..1f7fab230e8 100644 --- a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/dicom-web.md +++ b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md @@ -1,6 +1,8 @@ --- sidebar_position: 2 sidebar_label: DICOMweb +title: DICOMweb +summary: Comprehensive guide to configuring DICOMweb data sources in OHIF, including setting up local DICOM servers (Orthanc, DCM4CHEE), configurations for PDF, video, and bulkdata handling, and detailed instructions for development environments. --- # DICOMweb @@ -86,7 +88,7 @@ this repository's root directory, and run: yarn config set workspaces-experimental true # Restore dependencies -yarn install +yarn install --frozen-lockfile # Run our dev command, but with the local orthanc config yarn run dev:orthanc diff --git a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/introduction.md b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/introduction.md similarity index 76% rename from platform/docs/versioned_docs/version-3.10/configuration/dataSources/introduction.md rename to platform/docs/versioned_docs/version-3.11/configuration/dataSources/introduction.md index 2a0add51532..e66c647ace9 100644 --- a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/introduction.md +++ b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/introduction.md @@ -1,6 +1,8 @@ --- sidebar_position: 1 sidebar_label: Introduction +title: Data Source Introduction +summary: Introduces OHIF's data structure based on naturalized DICOM JSON format, providing an overview of common data sources available in OHIF and how custom data sources can be implemented. --- # Data Source diff --git a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/static-files.md b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/static-files.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/configuration/dataSources/static-files.md rename to platform/docs/versioned_docs/version-3.11/configuration/dataSources/static-files.md index ec460db2b15..7746935bfea 100644 --- a/platform/docs/versioned_docs/version-3.10/configuration/dataSources/static-files.md +++ b/platform/docs/versioned_docs/version-3.11/configuration/dataSources/static-files.md @@ -1,6 +1,8 @@ --- sidebar_position: 5 sidebar_label: Static Files +title: Static DICOMweb Files +summary: Details how to use the static-wado project to generate and serve optimized static DICOMweb files for enhanced OHIF Viewer performance, with step-by-step instructions for installation, file generation, and viewer configuration. --- diff --git a/platform/docs/versioned_docs/version-3.10/configuration/tour-demo.gif b/platform/docs/versioned_docs/version-3.11/configuration/tour-demo.gif similarity index 100% rename from platform/docs/versioned_docs/version-3.10/configuration/tour-demo.gif rename to platform/docs/versioned_docs/version-3.11/configuration/tour-demo.gif diff --git a/platform/docs/versioned_docs/version-3.10/configuration/tours.md b/platform/docs/versioned_docs/version-3.11/configuration/tours.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/configuration/tours.md rename to platform/docs/versioned_docs/version-3.11/configuration/tours.md index 340800053d8..2bcfb8329dd 100644 --- a/platform/docs/versioned_docs/version-3.10/configuration/tours.md +++ b/platform/docs/versioned_docs/version-3.11/configuration/tours.md @@ -1,6 +1,8 @@ --- sidebar_position: 3 sidebar_label: Tours +title: Configuring Tours in OHIF +summary: Guide to implementing interactive guided tours in OHIF using Shepherd.js, including detailed configuration options, step definitions, customization parameters, and examples for creating effective user onboarding experiences. --- # Configuring Tours in OHIF with Shepherd.js diff --git a/platform/docs/versioned_docs/version-3.10/configuration/url.md b/platform/docs/versioned_docs/version-3.11/configuration/url.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/configuration/url.md rename to platform/docs/versioned_docs/version-3.11/configuration/url.md index cac7e5ad4d2..2fa3a736dd9 100644 --- a/platform/docs/versioned_docs/version-3.10/configuration/url.md +++ b/platform/docs/versioned_docs/version-3.11/configuration/url.md @@ -1,6 +1,8 @@ --- sidebar_position: 3 sidebar_label: URL +title: URL Parameters +summary: Reference for OHIF Viewer URL parameters that control WorkList filtering, study/series loading, and viewer behavior, including parameters for filtering by patient name, modality, data sources, specifying initial series/instances, and selecting hanging protocols. --- # URL diff --git a/platform/docs/versioned_docs/version-3.10/conformance.md b/platform/docs/versioned_docs/version-3.11/conformance.md similarity index 56% rename from platform/docs/versioned_docs/version-3.10/conformance.md rename to platform/docs/versioned_docs/version-3.11/conformance.md index ac34b46dee9..f83322a8577 100644 --- a/platform/docs/versioned_docs/version-3.10/conformance.md +++ b/platform/docs/versioned_docs/version-3.11/conformance.md @@ -2,6 +2,7 @@ sidebar_position: 12 sidebar_label: DICOM Conformance Statement title: DICOM Conformance Statement +summary: Access information about the OHIF Viewer's compliance with DICOM standards through an open-sourced document provided by Radical Imaging, which details the application's implementation of DICOM protocols and services. --- You can find a version that has been open sourced by Radical Imaging [in this link](https://docs.google.com/document/d/1hbDlUApX4svX33gAUGxGfD7fXXZNaBsX0hSePbc-hNA/edit?usp=sharing) diff --git a/platform/docs/versioned_docs/version-3.10/deployment/_category_.json b/platform/docs/versioned_docs/version-3.11/deployment/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/deployment/_category_.json rename to platform/docs/versioned_docs/version-3.11/deployment/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/deployment/authorization.md b/platform/docs/versioned_docs/version-3.11/deployment/authorization.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/deployment/authorization.md rename to platform/docs/versioned_docs/version-3.11/deployment/authorization.md index b3589b1a97d..76abd915b0a 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/authorization.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/authorization.md @@ -1,6 +1,8 @@ --- sidebar_position: 6 sidebar_label: Auth +title: Authorization and Authentication +summary: Guide to configuring OpenID-Connect authentication in OHIF Viewer, including setup of authorization flows, token handling, and implementation details for securing access to medical imaging data. --- # Authorization and Authentication diff --git a/platform/docs/versioned_docs/version-3.10/deployment/azure.md b/platform/docs/versioned_docs/version-3.11/deployment/azure.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/deployment/azure.md rename to platform/docs/versioned_docs/version-3.11/deployment/azure.md index 219532d9088..983eafa4ca5 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/azure.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/azure.md @@ -1,5 +1,7 @@ --- sidebar_position: 12 +title: Microsoft Azure Integration +summary: Comprehensive guide for configuring OHIF with Microsoft Azure Healthcare APIs, including step-by-step instructions for Azure AD registration, DICOM service setup, CORS configuration, and OAuth authentication implementation. --- # Microsoft Azure diff --git a/platform/docs/versioned_docs/version-3.10/deployment/build-for-production.md b/platform/docs/versioned_docs/version-3.11/deployment/build-for-production.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/deployment/build-for-production.md rename to platform/docs/versioned_docs/version-3.11/deployment/build-for-production.md index dbd89a8a906..f4521327f52 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/build-for-production.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/build-for-production.md @@ -1,5 +1,7 @@ --- sidebar_position: 2 +title: Build for Production +summary: Step-by-step guide to building a production-ready version of the OHIF Viewer, including environment setup, code acquisition, dependency restoration, production build creation, and configuration options for deployment. --- # Build for Production diff --git a/platform/docs/versioned_docs/version-3.10/deployment/cors.md b/platform/docs/versioned_docs/version-3.11/deployment/cors.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/deployment/cors.md rename to platform/docs/versioned_docs/version-3.11/deployment/cors.md index 7035ab213d8..315e10fd5f3 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/cors.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/cors.md @@ -1,5 +1,7 @@ --- sidebar_position: 8 +title: Cross-Origin Resource Sharing +summary: Detailed explanation of cross-origin security configurations for OHIF Viewer, covering CORS requirements for data source access, iframe embedding, secure contexts, and troubleshooting techniques for proper implementation. --- # Cross-Origin Information for OHIF diff --git a/platform/docs/versioned_docs/version-3.10/deployment/custom-url-access.md b/platform/docs/versioned_docs/version-3.11/deployment/custom-url-access.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/deployment/custom-url-access.md rename to platform/docs/versioned_docs/version-3.11/deployment/custom-url-access.md index c4639aedfc2..7ec16626f73 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/custom-url-access.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/custom-url-access.md @@ -1,6 +1,7 @@ --- sidebar_position: 4 title: Custom URL Access/Build +summary: "Instructions for hosting the OHIF Viewer on custom URL paths, with two deployment approaches: simple setup for serving the viewer from a subpath with assets at the root, and advanced setup for custom asset paths with detailed configuration steps." --- diff --git a/platform/docs/versioned_docs/version-3.10/deployment/docker/docker.md b/platform/docs/versioned_docs/version-3.11/deployment/docker/docker.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/deployment/docker/docker.md rename to platform/docs/versioned_docs/version-3.11/deployment/docker/docker.md index e9c7e212f76..327782fc2dd 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/docker/docker.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/docker/docker.md @@ -1,5 +1,7 @@ --- sidebar_position: 4 +title: Docker Deployment +summary: Comprehensive guide for deploying OHIF Viewer using Docker, covering pre-built images from Docker Hub, custom image building, configuration options through build arguments and environment variables, and runtime container management. --- # Docker diff --git a/platform/docs/versioned_docs/version-3.10/deployment/docker/ssl.md b/platform/docs/versioned_docs/version-3.11/deployment/docker/ssl.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/deployment/docker/ssl.md rename to platform/docs/versioned_docs/version-3.11/deployment/docker/ssl.md index 1942468ecf2..b6650ba0aeb 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/docker/ssl.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/docker/ssl.md @@ -1,6 +1,7 @@ --- sidebar_position: 2 -title: SSL +title: SSL Configuration for Docker +summary: Guide to configuring SSL for OHIF Viewer in Docker deployments, including environment variable setup, certificate mounting, permissions management, and instructions for both CA-signed and self-signed certificate implementation. --- # SSL diff --git a/platform/docs/versioned_docs/version-3.10/deployment/google-cloud-healthcare.md b/platform/docs/versioned_docs/version-3.11/deployment/google-cloud-healthcare.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/deployment/google-cloud-healthcare.md rename to platform/docs/versioned_docs/version-3.11/deployment/google-cloud-healthcare.md index 54df8bb029e..6fef07c6a7a 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/google-cloud-healthcare.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/google-cloud-healthcare.md @@ -1,5 +1,7 @@ --- sidebar_position: 9 +title: Google Cloud Healthcare Integration +summary: Guide to setting up Google Cloud Healthcare API as a DICOM data source for OHIF, including project creation, API configuration, OAuth authentication setup, and implementation details for connecting the viewer to Google-hosted medical imaging data. --- # Google Cloud Healthcare diff --git a/platform/docs/versioned_docs/version-3.10/deployment/iframe.md b/platform/docs/versioned_docs/version-3.11/deployment/iframe.md similarity index 89% rename from platform/docs/versioned_docs/version-3.10/deployment/iframe.md rename to platform/docs/versioned_docs/version-3.11/deployment/iframe.md index 671756911ca..c4460052ef6 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/iframe.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/iframe.md @@ -1,6 +1,8 @@ --- sidebar_position: 7 sidebar_label: iframe +title: Embedding OHIF in an iframe +summary: Guidelines for embedding OHIF Viewer within other applications using iframe integration, explaining configuration requirements for path settings, static builds, and proper setup to ensure WebWorkers, WASM, and WebGL features function correctly. --- # iframe diff --git a/platform/docs/versioned_docs/version-3.10/deployment/index.md b/platform/docs/versioned_docs/version-3.11/deployment/index.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/deployment/index.md rename to platform/docs/versioned_docs/version-3.11/deployment/index.md index 6698c607fe6..7b8ca88105e 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/index.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/index.md @@ -1,6 +1,8 @@ --- sidebar_position: 1 sidebar_label: Overview +title: Deployment Overview +summary: Comprehensive guide to deploying the OHIF Viewer as a standalone web application or embedded iframe, covering data source configuration, security considerations, and various deployment options with examples for different hosting environments. --- # Deployment diff --git a/platform/docs/versioned_docs/version-3.10/deployment/nginx--image-archive.md b/platform/docs/versioned_docs/version-3.11/deployment/nginx--image-archive.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/deployment/nginx--image-archive.md rename to platform/docs/versioned_docs/version-3.11/deployment/nginx--image-archive.md index b1a2fd34b89..1f77a5f3ad8 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/nginx--image-archive.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/nginx--image-archive.md @@ -1,5 +1,7 @@ --- sidebar_position: 10 +title: Nginx + Image Archive Setup +summary: Tutorial for setting up OHIF Viewer with Nginx and PACS (Orthanc or DCM4CHEE), using Docker for a production-ready system with reverse proxy configuration to securely handle medical imaging data, including installation steps and troubleshooting tips. --- # Nginx + Image Archive diff --git a/platform/docs/versioned_docs/version-3.10/deployment/static-assets.md b/platform/docs/versioned_docs/version-3.11/deployment/static-assets.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/deployment/static-assets.md rename to platform/docs/versioned_docs/version-3.11/deployment/static-assets.md index 4186a1b3cca..d04d7af0f0b 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/static-assets.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/static-assets.md @@ -1,5 +1,7 @@ --- sidebar_position: 3 +title: Deploy Static Assets +summary: Guide to deploying OHIF Viewer static assets using various hosting options, from simple drag-and-drop methods like Netlify to more advanced cloud platforms like AWS, GCP, and Azure, with step-by-step instructions for each deployment approach. --- # Deploy Static Assets diff --git a/platform/docs/versioned_docs/version-3.10/deployment/user-account-control.md b/platform/docs/versioned_docs/version-3.11/deployment/user-account-control.md similarity index 98% rename from platform/docs/versioned_docs/version-3.10/deployment/user-account-control.md rename to platform/docs/versioned_docs/version-3.11/deployment/user-account-control.md index 87f0235bead..91fa965d020 100644 --- a/platform/docs/versioned_docs/version-3.10/deployment/user-account-control.md +++ b/platform/docs/versioned_docs/version-3.11/deployment/user-account-control.md @@ -1,5 +1,7 @@ --- sidebar_position: 11 +title: User Account Control +summary: Comprehensive guide for implementing user authentication in OHIF using Keycloak, covering setup with both Orthanc and DCM4CHEE, configuration with OAuth2 proxy, SSL implementation, and detailed steps for local and production deployment scenarios. --- # User Account Control diff --git a/platform/docs/versioned_docs/version-3.10/development/_category_.json b/platform/docs/versioned_docs/version-3.11/development/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/development/_category_.json rename to platform/docs/versioned_docs/version-3.11/development/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/development/android-ios-debugging.md b/platform/docs/versioned_docs/version-3.11/development/android-ios-debugging.md similarity index 92% rename from platform/docs/versioned_docs/version-3.10/development/android-ios-debugging.md rename to platform/docs/versioned_docs/version-3.11/development/android-ios-debugging.md index 67c42734ae9..1e5ebe8a7a4 100644 --- a/platform/docs/versioned_docs/version-3.10/development/android-ios-debugging.md +++ b/platform/docs/versioned_docs/version-3.11/development/android-ios-debugging.md @@ -1,6 +1,8 @@ --- sidebar_position: 12 sidebar_label: Android & iOS Debugging +title: Android & iOS Debugging +summary: Comprehensive guide for debugging OHIF Viewer on mobile devices, covering setup of Android and iOS emulators, step-by-step instructions for connecting Chrome DevTools and Safari Web Inspector, and video tutorials demonstrating the debugging workflow. --- # Android & iOS Debugging for OHIF using Emulators diff --git a/platform/docs/versioned_docs/version-3.10/development/architecture.md b/platform/docs/versioned_docs/version-3.11/development/architecture.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/development/architecture.md rename to platform/docs/versioned_docs/version-3.11/development/architecture.md index f191f16ed20..ea7587a42ef 100644 --- a/platform/docs/versioned_docs/version-3.10/development/architecture.md +++ b/platform/docs/versioned_docs/version-3.11/development/architecture.md @@ -1,6 +1,8 @@ --- sidebar_position: 2 sidebar_label: Architecture +title: OHIF Architecture +summary: Detailed explanation of OHIF Viewer's architecture, covering the platform's core components, extensions system, modes concept, and the relationships between these elements that enable building flexible and extensible medical imaging applications. --- # Architecture diff --git a/platform/docs/versioned_docs/version-3.10/development/continuous-integration.md b/platform/docs/versioned_docs/version-3.11/development/continuous-integration.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/development/continuous-integration.md rename to platform/docs/versioned_docs/version-3.11/development/continuous-integration.md index 48ad4c4d097..15055a374b2 100644 --- a/platform/docs/versioned_docs/version-3.10/development/continuous-integration.md +++ b/platform/docs/versioned_docs/version-3.11/development/continuous-integration.md @@ -1,6 +1,8 @@ --- sidebar_position: 8 sidebar_label: Continuous Integration +title: Continuous Integration +summary: Overview of OHIF's CI/CD setup using CircleCI and Netlify, detailing workflows for pull request checks, optional Docker image publishing, deployment to development/staging/production environments, and the release process for npm packages and documentation. --- # Continuous Integration (CI) diff --git a/platform/docs/versioned_docs/version-3.10/development/contributing.md b/platform/docs/versioned_docs/version-3.11/development/contributing.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/development/contributing.md rename to platform/docs/versioned_docs/version-3.11/development/contributing.md index 5e513b7c34c..a0d9f0c9eab 100644 --- a/platform/docs/versioned_docs/version-3.10/development/contributing.md +++ b/platform/docs/versioned_docs/version-3.11/development/contributing.md @@ -1,6 +1,8 @@ --- sidebar_position: 5 sidebar_label: Contributing +title: Contributing to OHIF +summary: Guidelines for contributing to the OHIF project, including best practices for submitting pull requests, working with multiple repositories, and advice on creating changes that are more likely to be accepted by the core team. --- # Contributing diff --git a/platform/docs/versioned_docs/version-3.10/development/getting-started.md b/platform/docs/versioned_docs/version-3.11/development/getting-started.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/development/getting-started.md rename to platform/docs/versioned_docs/version-3.11/development/getting-started.md index 32214b66fa6..c23bc9bcf26 100644 --- a/platform/docs/versioned_docs/version-3.10/development/getting-started.md +++ b/platform/docs/versioned_docs/version-3.11/development/getting-started.md @@ -1,6 +1,8 @@ --- sidebar_position: 1 sidebar_label: Getting Started +title: Getting Started with OHIF Development +summary: Quick start guide for OHIF development, covering repository setup options, branch organization, development environment requirements, project initialization, and steps for building production-ready assets. --- # Getting Started diff --git a/platform/docs/versioned_docs/version-3.11/development/link.md b/platform/docs/versioned_docs/version-3.11/development/link.md new file mode 100644 index 00000000000..92d93eb6a42 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/development/link.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 9 +sidebar_label: Local Linking +title: Local Library Linking +summary: Brief introduction to the concept of local linking for library development in OHIF, explaining how to test libraries in application context before publishing, with reference to Cornerstonejs linking documentation. +--- + +# Introduction + +Local linking allows you to develop and test a library in the context of an application before it's published or when you encounter +a bug that you suspect is related to a library. With Yarn, this can be achieved through the yarn link command. + +You can take a look at the Cornerstonejs tutorial for linking https://www.cornerstonejs.org/docs/contribute/linking + +## Linking with `bun` + +The [instructions](#introduction) above describe how to use `yarn` for local linking. Some use `bun` for day-to-day +code linking and execution. The method to link locally using `bun` differs slightly. + +:::tip + +Linking locally with `bun` provides for running the [playwright tests](./playwright-testing.md) locally so as to include (and test) local changes from Cornerstone3D! + +::: + +In the local Cornerstone3D, simply replace `yarn` with `bun` for each of the commands. For example, in `cornerstone/packages/core` the following would be done. + +``` +# In cornerstone/packages/core +bun unlink +bun link +bun dev +``` + +In OHIF, edit the root `package.json` file to include the cornerstone3D packages to link and add them to the `resolutions`. + +For example here is a snippet of the `package.json` file linking cornerstone core and tools locally. Add whichever +cornerstone packages to link locally in the `resolutions`. + +``` + ... + "resolutions": { + "commander": "8.3.0", + "path-to-regexp": "0.1.12", + "@cornerstonejs/core": "link:@cornerstonejs/core", + "@cornerstonejs/tools": "link:@cornerstonejs/tools", + ... + }, + ... +``` + +In OHIF, run `bun install -f` and then run OHIF using either `bun dev` or `bun dev:fast`. diff --git a/platform/docs/versioned_docs/version-3.10/development/ohif-cli.md b/platform/docs/versioned_docs/version-3.11/development/ohif-cli.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/development/ohif-cli.md rename to platform/docs/versioned_docs/version-3.11/development/ohif-cli.md index 038f331a962..1cc90db3e36 100644 --- a/platform/docs/versioned_docs/version-3.10/development/ohif-cli.md +++ b/platform/docs/versioned_docs/version-3.11/development/ohif-cli.md @@ -1,6 +1,8 @@ --- sidebar_position: 3 sidebar_label: OHIF CLI +title: OHIF Command Line Interface +summary: Comprehensive guide to the OHIF CLI tool for managing extensions and modes, including commands for creating, linking, adding, removing, and publishing components, with detailed examples and explanations of the underlying configuration system. --- # OHIF Command Line Interface diff --git a/platform/docs/versioned_docs/version-3.10/development/our-process.md b/platform/docs/versioned_docs/version-3.11/development/our-process.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/development/our-process.md rename to platform/docs/versioned_docs/version-3.11/development/our-process.md index 263135b1f17..cda269eef90 100644 --- a/platform/docs/versioned_docs/version-3.10/development/our-process.md +++ b/platform/docs/versioned_docs/version-3.11/development/our-process.md @@ -1,6 +1,8 @@ --- sidebar_position: 6 sidebar_label: Issue & PR Triage Process +title: OHIF Development Process +summary: Detailed explanation of OHIF's issue triage, backlog management, contribution evaluation, and release processes, including categorization of issues, pull request workflow, quality assurance procedures, and continuous integration practices. --- # Our Process diff --git a/platform/docs/versioned_docs/version-3.10/development/playwright-testing.md b/platform/docs/versioned_docs/version-3.11/development/playwright-testing.md similarity index 94% rename from platform/docs/versioned_docs/version-3.10/development/playwright-testing.md rename to platform/docs/versioned_docs/version-3.11/development/playwright-testing.md index f85f02d33aa..e52e475d19a 100644 --- a/platform/docs/versioned_docs/version-3.10/development/playwright-testing.md +++ b/platform/docs/versioned_docs/version-3.11/development/playwright-testing.md @@ -1,19 +1,22 @@ --- sidebar_position: 11 sidebar_label: Playwright Testing +title: Playwright End-to-End Testing +summary: Guide to writing and running end-to-end tests for OHIF Viewer using Playwright, covering test configuration, screenshot verification, simulating user interactions, accessing application services, and using the VSCode extension for test recording. --- :::note -You might need to run the `yarn playwright install ` for the first time if you have not +You might need to run the `bun playwright install ` for the first time if you have not ::: # Running the tests ```bash +# # run the tests -yarn test:e2e:ui +bun test:e2e:ui ``` diff --git a/platform/docs/versioned_docs/version-3.10/development/testing.md b/platform/docs/versioned_docs/version-3.11/development/testing.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/development/testing.md rename to platform/docs/versioned_docs/version-3.11/development/testing.md index 23f2397553f..fb7322696db 100644 --- a/platform/docs/versioned_docs/version-3.10/development/testing.md +++ b/platform/docs/versioned_docs/version-3.11/development/testing.md @@ -1,6 +1,8 @@ --- sidebar_position: 7 sidebar_label: Testing +title: Testing OHIF +summary: Comprehensive guide to OHIF's testing approach, covering unit tests and end-to-end tests with detailed instructions for running them, explanation of test data management, and an overview of the project's testing philosophy and best practices. --- # Running Tests for OHIF diff --git a/platform/docs/versioned_docs/version-3.10/development/types.md b/platform/docs/versioned_docs/version-3.11/development/types.md similarity index 91% rename from platform/docs/versioned_docs/version-3.10/development/types.md rename to platform/docs/versioned_docs/version-3.11/development/types.md index a6af5df12ab..e048c98bba1 100644 --- a/platform/docs/versioned_docs/version-3.10/development/types.md +++ b/platform/docs/versioned_docs/version-3.11/development/types.md @@ -1,6 +1,8 @@ --- sidebar_position: 10 sidebar_label: Global Types +title: Global Types and Service Extensions +summary: Guide to using and extending TypeScript types in OHIF applications, focusing on the withAppTypes utility for component typing, adding custom properties, and properly integrating new services into the global namespace for type safety and code completion. --- # Extending App Types and Services in Your Application diff --git a/platform/docs/versioned_docs/version-3.10/development/video-tutorials.md b/platform/docs/versioned_docs/version-3.11/development/video-tutorials.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/development/video-tutorials.md rename to platform/docs/versioned_docs/version-3.11/development/video-tutorials.md index 482967e26fd..15ffa4f5cac 100644 --- a/platform/docs/versioned_docs/version-3.10/development/video-tutorials.md +++ b/platform/docs/versioned_docs/version-3.11/development/video-tutorials.md @@ -1,6 +1,8 @@ --- sidebar_position: 4 sidebar_label: Video Tutorials +title: OHIF Development Video Tutorials +summary: Collection of instructional videos demonstrating key OHIF development workflows, including creating, linking, and publishing modes and extensions, with step-by-step demonstrations of the OHIF CLI and NPM publishing process. --- # Video Tutorials diff --git a/platform/docs/versioned_docs/version-3.10/development/webWorkers.md b/platform/docs/versioned_docs/version-3.11/development/webWorkers.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/development/webWorkers.md rename to platform/docs/versioned_docs/version-3.11/development/webWorkers.md index 7fa3bed1a4f..c90e6af2371 100644 --- a/platform/docs/versioned_docs/version-3.10/development/webWorkers.md +++ b/platform/docs/versioned_docs/version-3.11/development/webWorkers.md @@ -1,6 +1,8 @@ --- sidebar_position: 13 sidebar_label: Web Workers +title: Web Workers Implementation Guide +summary: Step-by-step guide for implementing web workers in OHIF to handle computationally intensive tasks in background threads, including worker creation, registration, task execution, progress tracking, and best practices for code organization. --- # Web Worker Implementation Guide diff --git a/platform/docs/versioned_docs/version-3.10/faq/_category_.json b/platform/docs/versioned_docs/version-3.11/faq/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/faq/_category_.json rename to platform/docs/versioned_docs/version-3.11/faq/_category_.json diff --git a/platform/docs/versioned_docs/version-3.11/faq/add-viewport-icon.md b/platform/docs/versioned_docs/version-3.11/faq/add-viewport-icon.md new file mode 100644 index 00000000000..11c35cdc209 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/faq/add-viewport-icon.md @@ -0,0 +1,178 @@ +--- +id: add-viewport-icon +title: How to add a custom icon to the viewport corners +summary: Learn how to add a custom icon or dropdown to one of the viewport corners in OHIF. +--- + +# How to add a custom icon to the viewport corners + +## Question +How can I add a custom icon or dropdown to one of the viewport corners in OHIF? + +## Answer +OHIF provides a customizable viewport action menu system that allows you to add icons, buttons, or dropdowns to any of the four corners of a viewport (topLeft, topRight, bottomLeft, bottomRight). This is done through the `customizationService` and the viewport action corners API. + +Here's a complete example that shows how to add a mode switch dropdown to the top-left corner of the viewport: + +```tsx +import React from 'react'; +import { Icons } from '@ohif/ui-next'; +import { useSystem } from '@ohif/core'; +import { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + Button, +} from '@ohif/ui-next'; + +// This is a complete, self-contained component that shows a dropdown menu +// for switching modes when clicked +function getModeSwitchMenu({ viewportId, element, location }) { + const ModeSwitchMenu = () => { + const { servicesManager } = useSystem(); + const { router } = servicesManager.services; + const { viewportActionCornersService } = servicesManager.services; + + const handleModeSwitch = (mode) => { + const currentStudyInstanceUID = router.query.StudyInstanceUIDs; + // Navigate to the selected mode with the current study + router.navigate(`/${mode}?StudyInstanceUIDs=${currentStudyInstanceUID}`); + }; + + // Get proper alignment based on the location + let align = 'center'; + let side = 'bottom'; + + if (location !== undefined) { + const positioning = viewportActionCornersService.getAlignAndSide(location); + align = positioning.align; + side = positioning.side; + } + + return ( +
+ + + + + + Mode + handleModeSwitch('longitudinal')}> + Longitudinal + + handleModeSwitch('segmentation')}> + Segmentation + + handleModeSwitch('tmtv')}> + TMTV + + handleModeSwitch('microscopy')}> + Microscopy + + + +
+ ); + }; + + return ; +} + +// In your mode or extension, add this to the customizations +// This example shows how to add it in the onModeEnter lifecycle hook +function onModeEnter({ servicesManager }) { + const { customizationService } = servicesManager.services; + + // Add the mode switch icon to the top-left corner of the viewport + customizationService.setCustomizations({ + 'viewportActionMenu.topLeft': { + // Use $push to add to existing items or $set to replace all items + $push: [ + { + id: 'modeSwitch', + enabled: true, + component: getModeSwitchMenu, + }, + ], + }, + }); +} +``` + +## Key Concepts + +1. **Location-based customization**: The viewport is divided into four corners identified by: + - `viewportActionMenu.topLeft` + - `viewportActionMenu.topRight` + - `viewportActionMenu.bottomLeft` + - `viewportActionMenu.bottomRight` + +2. **Component structure**: + - `id` - A unique identifier for your component + - `enabled` - Boolean to control if the component should be displayed + - `component` - A function that returns a React component to render + +3. **Component positioning**: The component's position within a corner is determined by its order in the array. Components are rendered in the order they appear. + +4. **Dropdown positioning**: Use the `viewportActionCornersService.getAlignAndSide()` method to get the correct alignment for your dropdown menu based on its location. + +## Adding Multiple Components + +If you want to add multiple components to the same corner or different corners, you can do it in a single customization: + +```js +customizationService.setCustomizations({ + 'viewportActionMenu.topLeft': { + $push: [ + { + id: 'modeSwitch', + enabled: true, + component: getModeSwitchMenu, + }, + ], + }, + 'viewportActionMenu.topRight': { + $push: [ + { + id: 'anotherComponent', + enabled: true, + component: getAnotherComponent, + }, + ], + }, +}); +``` + +## Replacing Existing Components + +If you want to replace all components in a corner instead of adding to them, use `$set` instead of `$push`: + +```js +customizationService.setCustomizations({ + 'viewportActionMenu.topLeft': { + $set: [ + { + id: 'modeSwitch', + enabled: true, + component: getModeSwitchMenu, + }, + // This will be the only components in the top-left corner + ], + }, +}); +``` + +Remember that this customization will affect all viewports in the active mode. If you need different behavior for different viewports, you should check the `viewportId` parameter in your component's logic. diff --git a/platform/docs/versioned_docs/version-3.10/faq/faq-measure-1.png b/platform/docs/versioned_docs/version-3.11/faq/faq-measure-1.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/faq/faq-measure-1.png rename to platform/docs/versioned_docs/version-3.11/faq/faq-measure-1.png diff --git a/platform/docs/versioned_docs/version-3.10/faq/faq-measure-2.png b/platform/docs/versioned_docs/version-3.11/faq/faq-measure-2.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/faq/faq-measure-2.png rename to platform/docs/versioned_docs/version-3.11/faq/faq-measure-2.png diff --git a/platform/docs/versioned_docs/version-3.10/faq/faq-measure-4.png b/platform/docs/versioned_docs/version-3.11/faq/faq-measure-4.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/faq/faq-measure-4.png rename to platform/docs/versioned_docs/version-3.11/faq/faq-measure-4.png diff --git a/platform/docs/versioned_docs/version-3.10/faq/faq-measure-5.png b/platform/docs/versioned_docs/version-3.11/faq/faq-measure-5.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/faq/faq-measure-5.png rename to platform/docs/versioned_docs/version-3.11/faq/faq-measure-5.png diff --git a/platform/docs/versioned_docs/version-3.10/faq/faq-measure3.png b/platform/docs/versioned_docs/version-3.11/faq/faq-measure3.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/faq/faq-measure3.png rename to platform/docs/versioned_docs/version-3.11/faq/faq-measure3.png diff --git a/platform/docs/versioned_docs/version-3.10/faq/general.md b/platform/docs/versioned_docs/version-3.11/faq/general.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/faq/general.md rename to platform/docs/versioned_docs/version-3.11/faq/general.md index 2c01de7d125..7e0619b7230 100644 --- a/platform/docs/versioned_docs/version-3.10/faq/general.md +++ b/platform/docs/versioned_docs/version-3.11/faq/general.md @@ -1,5 +1,7 @@ --- id: general +title: General FAQ +summary: Answers to common questions about OHIF Viewer, including how to report bugs, request features, contact for support or academic collaborations, regulatory compliance status (FDA/CE/HIPAA), and obtaining sample studies. --- diff --git a/platform/docs/versioned_docs/version-3.10/faq/index.md b/platform/docs/versioned_docs/version-3.11/faq/index.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/faq/index.md rename to platform/docs/versioned_docs/version-3.11/faq/index.md index e44af0f3043..30a56b43468 100644 --- a/platform/docs/versioned_docs/version-3.10/faq/index.md +++ b/platform/docs/versioned_docs/version-3.11/faq/index.md @@ -1,5 +1,7 @@ --- id: index +title: OHIF Viewer FAQ +summary: Comprehensive list of frequently asked questions about the OHIF Viewer, covering common inquiries about reporting bugs, requesting features, academic collaborations, regulatory status, and obtaining demo studies. --- diff --git a/platform/docs/versioned_docs/version-3.10/faq/study-sorting.png b/platform/docs/versioned_docs/version-3.11/faq/study-sorting.png similarity index 100% rename from platform/docs/versioned_docs/version-3.10/faq/study-sorting.png rename to platform/docs/versioned_docs/version-3.11/faq/study-sorting.png diff --git a/platform/docs/versioned_docs/version-3.10/faq/technical.md b/platform/docs/versioned_docs/version-3.11/faq/technical.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/faq/technical.md rename to platform/docs/versioned_docs/version-3.11/faq/technical.md index bee5b8f7edc..78513807619 100644 --- a/platform/docs/versioned_docs/version-3.10/faq/technical.md +++ b/platform/docs/versioned_docs/version-3.11/faq/technical.md @@ -1,5 +1,12 @@ +--- +id: technical +title: Technical FAQ +summary: Technical explanations and solutions for OHIF Viewer implementation challenges, including required metadata fields, handling large volumes for MPR/rendering, dynamically loading measurements, customizing series sorting, and addressing common rendering issues. +--- + # Technical FAQ +* [How to add a custom icon to the viewport corners](./add-viewport-icon.md) ## Viewer opens but does not show any thumbnails diff --git a/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/commands.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/commands.md new file mode 100644 index 00000000000..d38b52f52cb --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/commands.md @@ -0,0 +1,72 @@ +--- +sidebar_position: 4 +sidebar_label: Commands +summary: Migration guide for OHIF 3.11's different commands +--- + + + +## updateStoredPositionPresentation + +now uses displaySetInstanceUIDs instead of displaySetInstanceUID as a parameter. + + +## `loadSRMeasurements` Command + +**Key Changes:** + +* The `loadSRMeasurements` command, previously part of the `@ohif/extension-cornerstone-dicom-sr` extension, has been **removed**. +* Its functionality of hydrating a Structured Report (SR) and displaying its referenced series in a viewport is now primarily handled by the new `hydrateSecondaryDisplaySet` command available in the `@ohif/extension-cornerstone` extension. +* The `hydrateStructuredReport` command (from `@ohif/extension-cornerstone-dicom-sr`) now solely focuses on hydrating the SR and returning its data, without directly manipulating viewports. + +**Migration Steps:** + +If you were previously using the `loadSRMeasurements` command to load and display SR measurements, you should update your code to use the `hydrateSecondaryDisplaySet` command. + +1. **Identify `loadSRMeasurements` Usage:** + Locate where your code calls `commandsManager.runCommand('loadSRMeasurements', ...)`. + +2. **Update to `hydrateSecondaryDisplaySet`:** + Replace the call to `loadSRMeasurements` with `hydrateSecondaryDisplaySet`. You will need to pass the full `displaySet` object for the SR and the target `viewportId`. + + ```diff + - // Old way: using loadSRMeasurements + - commandsManager.runCommand('loadSRMeasurements', { + - displaySetInstanceUID: srDisplaySetInstanceUID, + - // viewportId was implicitly the active one or not directly specifiable here + - }); + - + + // New way: using hydrateSecondaryDisplaySet + + const { displaySetService, viewportGridService } = servicesManager.services; + + + + // 1. Get the SR displaySet object + + const srDisplaySet = displaySetService.getDisplaySetByUID(srDisplaySetInstanceUID); + + + + // 2. Determine the target viewportId (e.g., active viewport) + + const viewportId = viewportGridService.getActiveViewportId(); // Or your specific viewportId + + + + if (srDisplaySet && viewportId) { + + commandsManager.runCommand('hydrateSecondaryDisplaySet', { + + displaySet: srDisplaySet, + + viewportId: viewportId, + + }); + + } else { + + console.warn('SR DisplaySet or ViewportId not found, cannot hydrate.'); + + } + ``` + +**Explanation:** + +* The `loadSRMeasurements` command was responsible for both hydrating the SR (getting its measurement data and referenced series UIDs) and then updating the viewport to show the referenced series. +* The new `hydrateSecondaryDisplaySet` command, when given an SR `displaySet` (`displaySet.Modality === 'SR'`), will: + 1. Internally call the `hydrateStructuredReport` command to parse the SR and get its details (including `SeriesInstanceUIDs` of referenced images). + 2. Then, it will automatically find the corresponding image display sets for those `SeriesInstanceUIDs`. + 3. Finally, it will update the specified `viewportId` to display the primary referenced image series. +* This change centralizes the logic for hydrating secondary display sets (like SR, SEG, RTSTRUCT) and updating viewports into the `hydrateSecondaryDisplaySet` command within the core Cornerstone extension. + +**Note on UI/Button Changes:** +The UI button typically associated with "Load SR" (often seen in viewport corners or specific contexts) has also been refactored. The hydration of SRs is now often triggered by: +* The `TrackedMeasurementsContext` if the `@ohif/extension-measurement-tracking` is in use. +* The new `ModalityLoadBadge` component, which can appear in viewports containing SR, SEG, or RTSTRUCT display sets, offering a "LOAD" action that calls `hydrateSecondaryDisplaySet`. + +If you had custom UI invoking `loadSRMeasurements`, you'll need to adapt it to call `hydrateSecondaryDisplaySet` as described above. diff --git a/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/hydration.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/hydration.md new file mode 100644 index 00000000000..0f82524f4a2 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/hydration.md @@ -0,0 +1,85 @@ +--- +title: Hydration +sidebar_position: 3 +sidebar_label: Hydration +summary: Migration guide for OHIF 3.11's hydration system changes, including the transition to a centralized hydration dialog and command-based hydration for secondary display sets. +--- + +## Update Hydration Logic: + * The `promptHydrateSEG` and `promptHydrateRT` functions have been updated to use the generic `utils.promptHydrationDialog` from `@ohif/extension-cornerstone`. + * The actual hydration is now often triggered by the `hydrateSecondaryDisplaySet` command. + + *Example: `promptHydrateRT` change* + + ```diff + // extensions/cornerstone-dicom-rt/src/utils/promptHydrateRT.ts + - const RESPONSE = { + - NO_NEVER: -1, + - CANCEL: 0, + - HYDRATE_SEG: 5, + - }; + + import { utils, Types } from '@ohif/extension-cornerstone'; + + function promptHydrateRT({ + servicesManager, + rtDisplaySet, + viewportId, + preHydrateCallbacks, + hydrateRTDisplaySet, + -}: withAppTypes) { + - const { uiViewportDialogService, customizationService } = servicesManager.services; + - // ... lots of old promise and dialog logic + - return new Promise(async function (resolve, reject) { + - // ... + - }); + -} + - + -function _askHydrate( + - // ... + -) { + - // ... + -} + +}: { + + servicesManager: AppTypes.ServicesManager; + + rtDisplaySet: AppTypes.DisplaySet; + + viewportId: string; + + preHydrateCallbacks?: Types.HydrationCallback[]; + + hydrateRTDisplaySet: Types.HydrationCallback; + +}) { + + return utils.promptHydrationDialog({ + + servicesManager, + + viewportId, + + displaySet: rtDisplaySet, + + preHydrateCallbacks, + + hydrateCallback: hydrateRTDisplaySet, + + type: 'RTSTRUCT', + + }); + } + ``` + The `hydrateRTDisplaySet` callback passed to this function would now typically involve the `hydrateSecondaryDisplaySet` command. + ```diff + // extensions/cornerstone-dicom-rt/src/viewports/OHIFCornerstoneRTViewport.tsx + useEffect(() => { + if (rtIsLoading) { + return; + } + promptHydrateRT({ + servicesManager, + viewportId, + rtDisplaySet, + - preHydrateCallbacks: [storePresentationState], + - hydrateRTDisplaySet, + - }).then(isHydrated => { + - if (isHydrated) { + - setIsHydrated(true); + - } + + hydrateRTDisplaySet: async () => { + + return commandsManager.runCommand('hydrateSecondaryDisplaySet', { + + displaySet: rtDisplaySet, + + viewportId, + + }); + + }, + }); + - }, [servicesManager, viewportId, rtDisplaySet, rtIsLoading]); + + }, [servicesManager, viewportId, rtDisplaySet, rtIsLoading, commandsManager]); + ``` diff --git a/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/index.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/index.md new file mode 100644 index 00000000000..d9149850425 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/index.md @@ -0,0 +1,8 @@ +--- +sidebar_position: 1 +sidebar_label: 3.10 -> 3.11 +--- + +# Migration Guide + +This guide provides information about migrating from OHIF version 3.10 to version 3.11. diff --git a/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/other.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/other.md new file mode 100644 index 00000000000..c1643195126 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/other.md @@ -0,0 +1,46 @@ +--- +sidebar_position: 2 +sidebar_label: Other Changes +summary: Migration guide for OHIF 3.11 additional changes +--- + + +**Key Changes:** + +* **`connectToolsToMeasurementService` parameters:** The `connectToolsToMeasurementService` function from the `@ohif/cornerstone-extensions` now take different arguments. +* **`data-viewportId`** The `data-viewportId` naming was not compliant with react and was causing warnings. Rename references to `data-viewportid`. +* **`setIsReferenceViewable`** is no longer available or required by ViewportGridService. Instead, the cornerstone viewports + themselves provide the isReferenceViewable. This occurs because there were a lot more deciding issues to navigate to viewports than could be added to viewport grid service. +* **`JUMP_TO_MEASUREMENT_VIEWPORT` and `JUMP_TO_MEASUREMENT_LAYOUT`** are combined into `JUMP_TO_MEASUREMENT` with no + consume event. Only the single event is fired. This will need to be handled to redirect the changes to the appropriate viewport type as it was not possible to figure that out with generic information available in `ViewportGridService` + +**Migration Steps:** + +1. **Update `connectToolsToMeasurementService` Method Calls:** + * Now the connectToolsToMeasurementService receives all service object arguments (servicesManager, commandsManager and extensionManager). + + ```diff + // Before + - connectToolsToMeasurementService(servicesManager); + + // After + + connectToolsToMeasurementService({ + + servicesManager, + + commandsManager, + + extensionsManager + + }); + ``` + +**Images sort by position patient** + +The ImageSet sort has been modified: images are now sorted by ImagePositionPatient by default. If ImagePositionPatient is not available, the sort will fall back to InstanceNumber. + +**To revert to the previous sorting method, you can add the customization instanceSortingCriteria as shown below:** + +``` + customizationService.setCustomizations({ + 'instanceSortingCriteria': { + $set: {defaultSortFunctionName: 'default'}, + }, + }); +``` diff --git a/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/toolbarService.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/toolbarService.md new file mode 100644 index 00000000000..15e53ac9e2d --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/toolbarService.md @@ -0,0 +1,178 @@ +--- +sidebar_position: 2 +sidebar_label: Toolbar Service +summary: Migration guide for OHIF 3.11's toolbar service changes, including the transition from `ViewportActionCornersService` to `ToolbarService` +--- + + +**Key Changes:** + +* **`ViewportActionCornersService` Removed:** The `ViewportActionCornersService` and its associated provider (`ViewportActionCornersProvider`) and hook (`useViewportActionCorners`) have been removed. Functionality for viewport corner items is now integrated into the `ToolbarService` and standard toolbar components. +* **Viewport Corner Items as Toolbar Buttons:** Items previously managed by `ViewportActionCornersService` are now configured as regular toolbar buttons. They are assigned to specific toolbar sections (e.g., `viewportActionMenu.topLeft`) and rendered using `Toolbar` components within the viewport corners. +* **`ToolbarService` API Updates:** + * `ToolbarService.addButtons()` has been renamed to `ToolbarService.register()` to better reflect its purpose of registering button definitions rather than just adding them. + * `ToolbarService.createButtonSection()` has been renamed to `ToolbarService.updateSection()` to better reflect that it is not about creating new sections but updating existing ones. + * `ToolbarService` now has a `sections` property (e.g., `toolbarService.sections.viewportActionMenu.topLeft`) providing predefined section names. +* **Enhanced `useToolbar` Hook:** + * The `useToolbar` hook now returns additional state management functions for toolbar items: + * `openItem`, `closeItem`, `isItemOpen` (for managing dropdown/popover states). + * `lockItem`, `unlockItem`, `toggleLock`, `isItemLocked`. + * `showItem`, `hideItem`, `toggleVisibility`, `isItemVisible`. + * The `onInteraction` callback now receives `itemId` and `viewportId`. +* **Toolbar Button Configuration:** + * The `groupId` prop in button configurations (e.g., for `ohif.toolButtonList`, `ohif.toolBoxButtonGroup`) is generally replaced by directly using `buttonSection` to define the set of buttons. + * Button `evaluate` functions can now leverage `evaluateProps.hideWhenDisabled` to automatically hide a button when it's disabled. +* **New UI Components & Hooks for Viewport Corners:** + * Specialized components like `ModalityLoadBadge`, `NavigationComponent`, `TrackingStatus`, `ViewportDataOverlayMenuWrapper`, `ViewportOrientationMenuWrapper`, `WindowLevelActionMenuWrapper` are now used as toolbar buttons, typically in viewport action menu sections. + * `useViewportHover` hook can be used to determine if a viewport is hovered or active, controlling the visibility of corner toolbars. +* **`IconPresentationProvider`:** A new `IconPresentationProvider` and `useIconPresentation` hook have been introduced to standardize icon sizing and styling within toolbars and related components. +* **Legacy Toolbar Components Removed:** `ToolbarSplitButtonWithServicesLegacy` and `ToolbarButtonGroupWithServicesLegacy` have been removed. + +**Migration Steps:** + +1. **Update `ToolbarService` Method Calls:** + * Although the previous method also works but gives warning in the console when used. + * Replace all instances of `toolbarService.addButtons(...)` with `toolbarService.register(...)`. + * Replace all instances of `toolbarService.createButtonSection(...)` with `toolbarService.updateSection(...)`. + + ```diff + // Before + - toolbarService.addButtons(toolbarButtons); + - toolbarService.createButtonSection('primary', ['Zoom', 'Pan']); + + // After + + toolbarService.register(toolbarButtons); + + toolbarService.updateSection('primary', ['Zoom', 'Pan']); + ``` + +2. **Migrate Viewport Action Corner Items:** + * Remove any direct usage of the old `ViewportActionCornersService`, `useViewportActionCorners`, or `ViewportActionCornersProvider`. + * Define your viewport corner items (like orientation menu, W/L menu, data overlay menu) as standard toolbar buttons using `toolbarService.register()`. + * Assign these buttons to the new dedicated viewport action menu sections. You can access these section names via `toolbarService.sections.viewportActionMenu.`, e.g., `toolbarService.sections.viewportActionMenu.topLeft`. + + ```diff + // Before: Customization in viewportActionMenuCustomizations.ts (now deleted) + // or direct use of ViewportActionCornersService.addComponent + - // Example: viewportActionCornersService.addComponent({ viewportId, id: 'orientationMenu', component: MyOrientationMenu, location: 'topLeft' }); + + // After: In your mode's onModeEnter or similar setup + + const myViewportCornerButtons = [ + + { + + id: 'orientationMenu', + + uiType: 'ohif.orientationMenu', // Or your custom component registered as a UI type + + props: { /* ... props for your component ... */ } + + }, + + // ... other corner buttons + + ]; + + toolbarService.register(myViewportCornerButtons); + + toolbarService.updateSection( + + toolbarService.sections.viewportActionMenu.topLeft, + + ['orientationMenu', /* other button IDs */] + + ); + ``` + * The `OHIFViewportActionCorners.tsx` component now internally uses `Toolbar` components for each corner, which are populated by these sections. + * For custom components that act as menus (e.g., popovers), use the `onOpen`, `onClose`, `isOpen` props passed down by the `Toolbar` component (which get these from `useToolbar`). + + ```diff + // Before: Custom component might have managed its own open state + - // const [isMenuOpen, setIsMenuOpen] = useState(false); + - // const handleOpenChange = (open) => setIsMenuOpen(open); + + // After: Custom component receives isOpen, onOpen, onClose from Toolbar + + function MyCustomMenuButton({ isOpen, onOpen, onClose, ...rest }) { + + const handleOpenChange = (openState: boolean) => { + + if (openState) { + + onOpen?.(); + + } else { + + onClose?.(); + + } + + }; + + + + return ( + + + + {/* ... PopoverTrigger and PopoverContent ... */} + + + + ); + + } + ``` + +3. **Adapt Toolbar Button and Component Configurations:** + + The configuration of toolbar buttons, especially how they relate to sections + + * **Button Section Association via `props.buttonSection`:** + + The toolbar service now offers two ways to define this association: + + * **A. Simple Approach: `buttonSection: true` (Implicitly Uses Button's Own ID)** + + If a button definition includes `props: { buttonSection: true }`, the `ToolbarService` automatically sets the effective `buttonSection` ID to be the same as the button's own `id`. + + ```javascript + // Example: A ToolButtonList component's definition in toolbarButtons.ts + // { + // id: 'MeasurementTools', // ID of this ToolButtonList component + // uiType: 'ohif.toolButtonList', + // props: { + // buttonSection: true // This ToolButtonList will render the section named 'MeasurementTools' + // } + // } + ``` + + later you can use it like + + + ```javascript + toolbarService.updateSection('MeasurementTools', ['Length', 'Bidirectional', ...]); + ``` + + * **B. Flexible Approach: `buttonSection: 'customSectionName'` (Explicit Section ID)** + + You can explicitly provide a string for `props.buttonSection` if the button should be associated with a section ID that is different from its own `id`, or if you prefer explicit naming. + + ```javascript + // Example: A ToolButtonList component's definition + // { + // id: 'MySpecialToolList', // ID of this ToolButtonList component + // uiType: 'ohif.toolButtonList', + // props: { + // buttonSection: 'toolsForAdvancedUsers', // This list renders 'toolsForAdvancedUsers' section + // } + // } + ``` + + * **`evaluate` Function Enhancement:** + * Button `evaluate` functions can now leverage `evaluateProps: { hideWhenDisabled: true }` in your button definition to automatically hide a button when it's disabled. + + * **Wrapper Component `onInteraction` (e.g., `ToolButtonListWrapper`):** + * Update wrappers like `ToolBoxButtonGroupWrapper` and `ToolButtonListWrapper`: + * The `groupId` prop is replaced by `id` (which is the ID of the wrapper button component itself). + * The `onInteraction` callback in these wrappers now provides `id` (the wrapper's ID) instead of `groupId`. + + +4. **Adopt `IconPresentationProvider` (Optional but Recommended):** + * For consistent icon styling across your application's toolbars, wrap a high-level component (like your main `Header` or layout component) with ``. + * Custom tool button components can then use the `useIconPresentation` hook to get appropriate class names for icons or a pre-styled `IconContainer`. + + ```diff + // In your main App.tsx or Header.tsx + + import { IconPresentationProvider, ToolButton } from '@ohif/ui-next'; + // ... + + + {/* Your Header content including Toolbars */} + + + + // In a custom tool button using icons + + import { useIconPresentation, Icons } from '@ohif/ui-next'; + + function MyCustomToolButton({ iconName }) { + + const { className: iconClassName } = useIconPresentation(); + + return ; + + } + ``` + +5. **Remove Legacy Component Usage:** + * Replace any usage of `ToolbarSplitButtonWithServicesLegacy` and `ToolbarButtonGroupWithServicesLegacy` with the newer patterns, typically by configuring individual buttons and using `ToolButtonList` or `ButtonGroup` from `@ohif/ui-next` directly, driven by `useToolbar`. diff --git a/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/ui.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/ui.md new file mode 100644 index 00000000000..074790e5d81 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/ui.md @@ -0,0 +1,60 @@ +--- +sidebar_position: 5 +sidebar_label: UI +summary: Migration guide for OHIF 3.11's UI changes, including the transition from `ViewportActionCornersService` to `ToolbarService` and the introduction of `useToolbar` hook. +--- + + +## New useIconPresentation hook + +This section details the introduction of the `IconPresentationProvider` and `useIconPresentation` hook, offering an optional way to manage icon size and container styling within the UI. + +### Key Changes: + +* **New Context and Hook:** Introduction of `IconPresentationProvider` and `useIconPresentation` to provide a standardized way to control the size and potentially the container component/props for icons and related interactive elements (like `ToolButton`). + +### Migration Steps: + +Using the `IconPresentationProvider` is **entirely optional**. If you do not wrap your components with this provider, components like `ToolButton` will continue to use their explicit `size` prop and default styling. + +However, if you wish to centrally manage the presentation of icons within a specific part of your application's component tree, follow these steps: + +1. **Identify the component subtree:** Determine which section of your UI you want to apply consistent icon styling to. + +2. **Wrap with `IconPresentationProvider`:** Wrap the root of that component subtree with the `IconPresentationProvider`. Pass the desired `size` prop. You can also optionally provide a custom `IconContainer` component and `containerProps` if you want to change the wrapper around the icon itself (e.g., switching from a `Button` to a `ToolButton` or applying specific styling). + + ```jsx + import { IconPresentationProvider, ToolButton } from '@ohif/ui-next'; + + function MyComponentTree() { + return ( + // Icons and ToolButtons within this provider will inherit 'large' size + + {/* Any components inside that consume the context */} + + {/* ... other components ... */} + + ); + } + ``` + +3. **Consume the context in components (if building custom components):** If you are building a custom component that renders an icon and want it to respect the provider's settings, use the `useIconPresentation` hook within that component. This hook provides the configured size, a calculated CSS class name (`className`), the specified `IconContainer` component, and its `containerProps`. + + ```jsx + import React from 'react'; + import { useIconPresentation, Icons } from '@ohif/ui-next'; + + function MyCustomIconButton({ iconName, ...rest }) { + // This hook reads the nearest IconPresentationProvider context + const { className, IconContainer, containerProps } = useIconPresentation(); + + // Use the provided IconContainer and its props + return ( + + {/* Use the calculated className for the icon */} + + + ); + } + ``` + *Note: Built-in components like `ToolButton` in `@ohif/ui-next` have been updated internally to consume this context automatically if a provider is available.* diff --git a/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/viewport-action-menu.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/viewport-action-menu.md new file mode 100644 index 00000000000..b2b1fd307f4 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p10-to-3p11/viewport-action-menu.md @@ -0,0 +1,124 @@ +--- +sidebar_position: 1 +sidebar_label: Viewport Corners +summary: Migration guide for OHIF 3.11's viewport corners customization changes, including the transition from individual item configurations to location-based arrays and the removal of index priorities. +--- + + +Okay, here's a migration guide based on the provided diff, focusing on the introduction of `TrackingStatus`, `ModalityLoadBadge`, and `NavigationComponent`. + +**Key Changes:** + +* **Deprecated `ViewportActionCornersService`**: The `ViewportActionCornersService` and its associated provider (`ViewportActionCornersProvider`) have been removed. UI elements previously managed by this service are now typically handled by dedicated components integrated via the `ToolbarService`. +* **New Centralized UI Components**: + * `ModalityLoadBadge`: A new component in `@ohif/extension-cornerstone` that displays the status (e.g., SEG/RT/SR loaded or requiring hydration) and a "LOAD" button for secondary display sets (SEG, RTSTRUCT, SR). This replaces the inline status and load logic within individual viewport components like `OHIFCornerstoneSEGViewport` and `OHIFCornerstoneRTViewport`. + * `TrackingStatus`: A new component in `@ohif/extension-cornerstone` to indicate if measurements in a viewport are being tracked. This replaces inline tracking status indicators previously in `OHIFCornerstoneSRMeasurementViewport` and `TrackedCornerstoneViewport`. + * `NavigationComponent`: A new component in `@ohif/extension-cornerstone` that provides navigation arrows (e.g., for segments in SEG/RT or measurements in SR/tracked series). This replaces the `ViewportActionArrows` previously instantiated directly within viewport components. +* **Viewport Simplification**: Viewport components like `OHIFCornerstoneSEGViewport`, `OHIFCornerstoneRTViewport`, and `OHIFCornerstoneSRMeasurementViewport` have been simplified. They no longer manage their own status indicators, load buttons, or navigation arrows. They now primarily delegate rendering to `OHIFCornerstoneViewport`. +* **Refactored Hydration Prompts**: Utility functions like `promptHydrateSEG` and `promptHydrateRT` now use a centralized `utils.promptHydrationDialog` from `@ohif/extension-cornerstone`. +* **Centralized Hydration Command**: A new command `hydrateSecondaryDisplaySet` has been added to `@ohif/extension-cornerstone` to handle the hydration logic for SEG, RTSTRUCT, and SR display sets. +* **New Hooks**: Several new hooks have been introduced in `@ohif/extension-cornerstone` (e.g., `useViewportDisplaySets`, `useMeasurementTracking`, `useViewportSegmentations`, `useViewportHover`) to provide data and state for these new UI components. + +**Migration Steps:** + +1. **Remove `ViewportActionCornersService` Usage**: + * If you were using `ViewportActionCornersService` to add custom components to viewport corners, you will need to refactor this. The recommended approach is to define these components as toolbar buttons and place them in designated viewport action menu sections (e.g., `viewportActionMenu.topLeft`) using the `ToolbarService`. + * The internal status components (`_getStatusComponent`) and `ViewportActionArrows` within specific viewports (SEG, RT, SR) have been removed. Their functionality is now provided by `ModalityLoadBadge`, `TrackingStatus`, and `NavigationComponent`. + + +3. **Integrate New UI Components via `ToolbarService`**: + * The `ModalityLoadBadge`, `TrackingStatus`, and `NavigationComponent` are now registered with the `ToolbarService` within the `@ohif/extension-cornerstone`'s `getToolbarModule`. + * Modes (e.g., `longitudinal`) should define toolbar sections for viewport corners and add these components to those sections. + + *Example: Adding components to viewport corners in `longitudinal` mode* + ```diff + // modes/longitudinal/src/index.ts + function modeFactory({ modeConfiguration }) { + return { + // ... + onModeEnter: ({ servicesManager, extensionManager, commandsManager }: withAppTypes) => { + // ... + toolbarService.addButtons(toolbarButtons); + toolbarService.createButtonSection('primary', [ + // ... primary tools + ]); + + + toolbarService.updateSection(toolbarService.sections.viewportActionMenu.topLeft, [ + + 'orientationMenu', + + 'dataOverlayMenu', + + 'windowLevelMenu', + + ]); + + toolbarService.updateSection(toolbarService.sections.viewportActionMenu.topRight, [ + + 'modalityLoadBadge', + + 'trackingStatus', + + 'navigationComponent', + + ]); + // ... + }, + ``` + And ensure these buttons are defined in your mode's `toolbarButtons.ts`: + ```diff + // modes/longitudinal/src/toolbarButtons.ts + + { + + id: 'modalityLoadBadge', + + uiType: 'ohif.modalityLoadBadge', + + props: { + + // ... props like icon, label, tooltip, evaluate + + evaluate: { + + name: 'evaluate.modalityLoadBadge', + + hideWhenDisabled: true, + + }, + + }, + + }, + + { + + id: 'navigationComponent', + + uiType: 'ohif.navigationComponent', + + props: { + + // ... props + + evaluate: { + + name: 'evaluate.navigationComponent', + + hideWhenDisabled: true, + + }, + + }, + + }, + + { + + id: 'trackingStatus', + + uiType: 'ohif.trackingStatus', + + props: { + + // ... props + + evaluate: { + + name: 'evaluate.trackingStatus', + + hideWhenDisabled: true, + + }, + + }, + + }, + ``` + + +5. **Direct Import of `OHIFCornerstoneViewport`**: + * Extensions that were previously getting the cornerstone viewport component dynamically via `extensionManager.getModuleEntry('@ohif/extension-cornerstone.viewportModule.cornerstone')` should now import `OHIFCornerstoneViewport` directly from `@ohif/extension-cornerstone`. + + ```diff + // extensions/cornerstone-dicom-pmap/src/viewports/OHIFCornerstonePMAPViewport.tsx + import PropTypes from 'prop-types'; + import React, { useCallback, useEffect, useRef, useState } from 'react'; + import { useViewportGrid } from '@ohif/ui-next'; + +import { OHIFCornerstoneViewport } from '@ohif/extension-cornerstone'; + + function OHIFCornerstonePMAPViewport(props: withAppTypes) { + // ... + const getCornerstoneViewport = useCallback(() => { + - const { component: Component } = extensionManager.getModuleEntry( + - '@ohif/extension-cornerstone.viewportModule.cornerstone' + - ); + // ... + return ( + - + + /> + ); + // ... + ``` diff --git a/platform/docs/versioned_docs/version-3.11/migration-guide/3p11-to-3p12/index.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p11-to-3p12/index.md new file mode 100644 index 00000000000..24c79d59668 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p11-to-3p12/index.md @@ -0,0 +1,8 @@ +--- +sidebar_position: 1 +sidebar_label: 3.11 -> 3.12 beta +--- + +# Migration Guide + +This guide provides information about migrating from OHIF version 3.11 to version 3.12 beta diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/0-general.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/0-general.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/0-general.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/0-general.md index 3439456b0c3..5159f5249b5 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/0-general.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/0-general.md @@ -1,6 +1,7 @@ --- id: 0-general title: General +summary: General migration changes from OHIF 3.8 to 3.9, including removing SharedArrayBuffer requirements, React 18 updates, Polyfill removal, webpack changes, scroll utility relocation, Crosshairs improvements, and toolbar button evaluation enhancements. --- import Tabs from '@theme/Tabs'; diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/1-Architecture.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/1-Architecture.md similarity index 87% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/1-Architecture.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/1-Architecture.md index c8a92efa023..2f89a7b4d66 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/1-Architecture.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/1-Architecture.md @@ -1,6 +1,7 @@ --- id: seg-new-arch title: New Architecture +summary: Overview of the new viewport-centric segmentation architecture in OHIF 3.9, which replaces the previous toolGroup-centric approach and introduces clearer separation between segmentation data and its visual representation. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/2-segmentationService-basic.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/2-segmentationService-basic.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/2-segmentationService-basic.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/2-segmentationService-basic.md index 06601b7cd51..3bb7356a6af 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/2-segmentationService-basic.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/2-segmentationService-basic.md @@ -1,6 +1,7 @@ --- id: seg-api title: SegmentationService API +summary: Detailed guide to the SegmentationService API changes in OHIF 3.9, covering the transition from toolGroup to viewport-centric segmentation management, updates to key methods like getActiveSegmentation, setActiveSegmentation, and addSegment. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/3-segmentationserice-representation.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/3-segmentationserice-representation.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/3-segmentationserice-representation.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/3-segmentationserice-representation.md index 5222592767e..4e6e6f8f3ff 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/3-segmentationserice-representation.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/3-segmentationserice-representation.md @@ -1,6 +1,7 @@ --- id: seg-representation title: Segmentation Representations +summary: Migration guide for segmentation representation management in OHIF 3.9, explaining the transition from toolGroup-based to viewport-centric representation handling, and introducing the new specifier pattern for more flexible API usage. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-creation.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-creation.md similarity index 94% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-creation.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-creation.md index c70021e33d8..7d3b65b7f1f 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-creation.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-creation.md @@ -1,6 +1,7 @@ --- id: seg-creation -title: Segmentation Creation +title: Segmentation Creation +summary: Migration guide for segmentation creation methods in OHIF 3.9, covering renamed methods like createLabelmapForViewport and createLabelmapForDisplaySet, updated parameter structures, and transition from tool group to viewport-centric approach. --- ## createEmptySegmentationForViewport diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-modification.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-modification.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-modification.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-modification.md index ebe16f7c16a..f63f41df247 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-modification.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-modification.md @@ -1,6 +1,7 @@ --- id: seg-service-mod title: SegmentationService Modifications +summary: Migration guide for segmentation representation management API changes in OHIF 3.9, showing how to transition from tool group-based methods to viewport-centric approaches for adding, removing, and querying segmentation representations. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/5-segmentationserice-style.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/5-segmentationserice-style.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/5-segmentationserice-style.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/5-segmentationserice-style.md index 266206bf178..44afef24c21 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/5-segmentationserice-style.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/5-segmentationserice-style.md @@ -1,6 +1,7 @@ --- id: seg-style title: SegmentationService Style +summary: Migration guide for segmentation styling API changes in OHIF 3.9, covering the transition to viewport-specific visibility controls, new style specification system using the specifier pattern, and updated methods for setting colors and toggling visibility. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md index 1236787e142..c4195c5986a 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md @@ -1,6 +1,7 @@ --- id: seg-other title: Other Changes +summary: Migration guide for additional segmentation service changes in OHIF 3.9, covering updates to addOrUpdateSegmentation with new data structures, loadSegmentationsForViewport, highlightSegment, and jumpToSegmentCenter methods with viewport-centric approach. --- @@ -345,7 +346,7 @@ jumpToSegmentCenter( highlightSegment = true, animationLength = 750, highlightHideOthers = false, - highlightFunctionType = 'ease-in-out' + animationFunctionType = 'ease-in-out' ) ``` diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/index.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/index.md similarity index 72% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/index.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/index.md index f4a7a674447..b6fa29f88b4 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/1-segmentation/index.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/index.md @@ -2,6 +2,7 @@ id: segmentation-index title: Segmentation sidebar_position: 1 +summary: Migration guide for segmentation architecture changes in OHIF 3.9, covering the shift from tool group-centric to viewport-centric architecture to support advanced visualization capabilities and more flexible segmentation handling. --- import DocCardList from '@theme/DocCardList'; diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/2-Renamings.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/2-Renamings.md similarity index 80% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/2-Renamings.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/2-Renamings.md index 6c0763f3cea..95866fe742c 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/2-Renamings.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/2-Renamings.md @@ -2,6 +2,7 @@ id: 2-renamings title: Renamings sidebar_position: 2 +summary: Migration guide for renamed components in OHIF 3.9, including the Panel Measurements name change from 'measure' to 'panelMeasurement' and addIcon utility changes to support both UI packages. --- import Tabs from '@theme/Tabs'; diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/3-DataSources.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/3-DataSources.md similarity index 82% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/3-DataSources.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/3-DataSources.md index 2d14adf530d..26f7c97cf6b 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/3-DataSources.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/3-DataSources.md @@ -2,6 +2,7 @@ id: 3-data-sources title: Data Sources sidebar_position: 3 +summary: Migration guide for BulkDataURI configuration changes in OHIF 3.9, explaining the transition from a simple boolean flag to a more flexible configuration object with additional control options. --- import Tabs from '@theme/Tabs'; diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/4-Measurements.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/4-Measurements.md similarity index 89% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/4-Measurements.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/4-Measurements.md index d4b89faf359..8db5dd77459 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/4-Measurements.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/4-Measurements.md @@ -1,5 +1,6 @@ --- title: Measurements +summary: Migration guide for measurement changes in OHIF 3.9, covering the new structured 'displayText' object with primary and secondary arrays for better organization, and the renaming of 'selected' property to 'isSelected'. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/4-ViewportActionCorner.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/4-ViewportActionCorner.md similarity index 88% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/4-ViewportActionCorner.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/4-ViewportActionCorner.md index 7b85dafc092..74a02af61c8 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/4-ViewportActionCorner.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/4-ViewportActionCorner.md @@ -1,6 +1,7 @@ --- id: viewport-action-corner title: ViewportActionCorner +summary: Migration guide for ViewportActionCornerService in OHIF 3.9, introducing the new addComponent and addComponents methods that provide more reliable positioning of multiple components within viewport corners. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/5-StateSyncService.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/5-StateSyncService.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/5-StateSyncService.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/5-StateSyncService.md index 230683c857e..d1214440610 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/5-StateSyncService.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/5-StateSyncService.md @@ -1,6 +1,7 @@ --- id: state-sync-service title: StateSyncService +summary: Migration guide for transitioning from StateSyncService to Zustand stores in OHIF 3.9, covering all store types including LutPresentationStore, PositionPresentationStore, ViewportGridStore, and more with examples of old and new API usage. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/6-RTSTRUCT.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/6-RTSTRUCT.md similarity index 71% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/6-RTSTRUCT.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/6-RTSTRUCT.md index f13bf9130b3..4ecc3ad6e40 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/6-RTSTRUCT.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/6-RTSTRUCT.md @@ -2,6 +2,7 @@ id: 6-rtstruct title: RTSTRUCT sidebar_position: 6 +summary: Migration guide for RT Structure Set rendering in OHIF 3.9, explaining the transition from VTK-based rendering to SVG-based rendering for improved stability and speed while maintaining stack viewports instead of converting to volume viewports. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/7-UI.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/7-UI.md similarity index 94% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/7-UI.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/7-UI.md index 2be4fabf017..74fd307e7b2 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/7-UI.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/7-UI.md @@ -1,5 +1,6 @@ --- title: UI +summary: Migration guide for UI changes in OHIF 3.9, covering the new UI components from @ohif/ui-next, UINotificationService updates with Sonner integration, viewport pane Tailwind class changes, Header component refactoring, and managing both UI libraries. --- ## New Components diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/8-Refactorings.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/8-Refactorings.md similarity index 90% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/8-Refactorings.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/8-Refactorings.md index c90717d8859..2a6fab36286 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/8-Refactorings.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/8-Refactorings.md @@ -1,5 +1,6 @@ --- title: Refactoring +summary: Migration guide for refactored components in OHIF 3.9, including the move of PanelSegmentation from cornerstone-dicom-seg extension to cornerstone extension, centralization of dialog utilities, and improved customization ID structure for better modularity. --- import Tabs from '@theme/Tabs'; diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/9-other.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/9-other.md similarity index 94% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/9-other.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/9-other.md index 5bd8b735d13..8c2a3cebdba 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/9-other.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/9-other.md @@ -1,5 +1,6 @@ --- title: Other Changes +summary: Migration guide for additional changes in OHIF 3.9, covering external library loading with browserImport function, the pluginConfig.json format for dynamic imports, and improvements to viewport navigation using ViewReference methods. --- import Tabs from '@theme/Tabs'; diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/index.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/index.md similarity index 51% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/index.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/index.md index c962a5d558a..c6f3393c379 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p8-to-3p9/index.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/index.md @@ -2,6 +2,7 @@ id: 3p8-to-3p9 title: 3.8 -> 3.9 sidebar_position: 2 +summary: Migration guide for upgrading from OHIF 3.8 to 3.9, covering segmentation architecture changes, renamings, data sources, measurements, viewport action corners, state sync service, RT structure improvements, UI changes, and other refactorings. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/1-General/commands.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/1-General/commands.md similarity index 88% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/1-General/commands.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/1-General/commands.md index 81469266918..0f2198be5f5 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/1-General/commands.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/1-General/commands.md @@ -1,5 +1,6 @@ --- title: Commands +summary: Migration guide for commands in OHIF 3.10, covering the replacement of deleteMeasurement with removeMeasurement and setSourceViewportForReferenceLinesTool with the more generic setViewportForToolConfiguration command. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/1-General/general-m.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/1-General/general-m.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/1-General/general-m.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/1-General/general-m.md index 22b7512ac5e..6ff64c94108 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/1-General/general-m.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/1-General/general-m.md @@ -1,6 +1,7 @@ --- sidebar_position: 1 title: General +summary: General migration changes from OHIF 3.9 to 3.10, including Node.js version update, HTML template modifications, bundled Google Fonts, docs updates, faster development builds with rsbuild, and webpack configuration changes for AI segmentation support. --- ## Node.js Version Update diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/1-General/hotkeys.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/1-General/hotkeys.md similarity index 94% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/1-General/hotkeys.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/1-General/hotkeys.md index 80fc94a3184..b2a01d617ee 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/1-General/hotkeys.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/1-General/hotkeys.md @@ -1,6 +1,7 @@ --- sidebar_position: 2 title: Hotkeys +summary: Migration guide for hotkeys management in OHIF 3.10, explaining the transition from defining hotkeys in mode factory to using the customizationService, with examples of replacing, adding, and modifying hotkey bindings. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/1-General/routerBaseName.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/1-General/routerBaseName.md similarity index 92% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/1-General/routerBaseName.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/1-General/routerBaseName.md index de39916ac36..e6eb0b3c43e 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/1-General/routerBaseName.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/1-General/routerBaseName.md @@ -1,5 +1,6 @@ --- title: routerBaseName +summary: Migration guide for router configuration in OHIF 3.10, covering the updated default behavior of routerBasename and its interaction with PUBLIC_URL, with scenario-based examples for both root and subpath hosting. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/2-CustomizationService/index.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/2-CustomizationService/index.md similarity index 98% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/2-CustomizationService/index.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/2-CustomizationService/index.md index ad3252fc6af..9d2db05b067 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/2-CustomizationService/index.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/2-CustomizationService/index.md @@ -1,6 +1,7 @@ --- sidebar_position: 2 title: Customization Service +summary: Migration guide for OHIF's Customization Service from 3.9 to 3.10, covering unified customization getters, simplified registration, new commands, renamed customizations, and updated patterns for modifying UI components. --- # CustomizationService diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/1-Introduction.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/1-Introduction.md similarity index 73% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/1-Introduction.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/1-Introduction.md index 230c78d3aa7..39c2335e066 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/1-Introduction.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/1-Introduction.md @@ -1,6 +1,7 @@ --- title: Introduction position: 1 +summary: Introduction to UI changes in OHIF 3.10, covering the migration of the image viewer to the @ohif/ui-next library, a complete rewrite of UI components offering extensibility, accessibility, and a modern design, with guidance on updating custom panels. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/10-Migration-3p10-Tests.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/10-Migration-3p10-Tests.md similarity index 89% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/10-Migration-3p10-Tests.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/10-Migration-3p10-Tests.md index 17e8a968909..45c4cef3c62 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/10-Migration-3p10-Tests.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/10-Migration-3p10-Tests.md @@ -1,5 +1,6 @@ --- title: Tests +summary: Migration guide for test updates in OHIF 3.10, covering changes to how tools are identified and tested, including the transition from class-based to data-attribute-based selection in Cypress tests. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/1a-Colors.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/1a-Colors.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/1a-Colors.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/1a-Colors.md index ea98850b06d..14b7313b48d 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/1a-Colors.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/1a-Colors.md @@ -1,5 +1,6 @@ --- title: Colors +summary: Migration guide for OHIF 3.10's new color system, explaining the transition from custom color names to a semantic color palette using CSS variables, with detailed mapping of old color classes to new Tailwind equivalents. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/2-Migration-3p10-Icons.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/2-Migration-3p10-Icons.md similarity index 89% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/2-Migration-3p10-Icons.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/2-Migration-3p10-Icons.md index 7a2afed4d5e..fab2d398257 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/2-Migration-3p10-Icons.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/2-Migration-3p10-Icons.md @@ -1,5 +1,6 @@ --- title: Icons +summary: Migration guide for OHIF 3.10's Icon component updates, covering the transition from @ohif/ui to @ohif/ui-next with new PascalCase naming conventions, legacy fallback options, and a comprehensive renaming table for all icons. --- ## Migration Guide: Icon Component Updates @@ -132,6 +133,67 @@ import { Icons } from '@ohif/ui-next'; ``` + + +### Creating New Custom Icons + +This section explains how to migrate your custom icons from the old SVG import method to the new React component-based system in `@ohif/ui-next`. The new approach improves consistency, allows for better tree-shaking, and provides type safety. + +The process involves converting your existing SVG files into React components and then registering them. + +#### 1. Convert SVG to a React Component + +First, take your raw SVG file and convert it into a `.tsx` React functional component. + +**Before: Raw SVG File** + +Previously, you might have had a file like `Baseline.svg`: + +```xml +// Baseline.svg + + < some svg content> + +``` + +**After: React Icon Component** + +Now, create a `.tsx` file that exports a React component. Note the following changes: +- SVG attributes like `text-anchor` and `stroke-width` are converted to camel case (i.e. `textAnchor`, `strokeWidth`). +- The component accepts `IconProps` and spreads them onto the root `` element. + +```typescript +// Baseline.tsx +import React from 'react'; +import type { IconProps } from '../types'; + +export const Baseline = (props: IconProps) => ( + + < some svg content> + +); + +export default Baseline; +``` + +#### 2. Register the New Icon + +After creating the component, you must register it with the `Icons` service from `@ohif/ui-next`. This is typically done in a centralized location where you initialize your UI components (for example, an extension's preRegistration is a good spot for this). You'll need a unique name for the icon, which can be managed with an enum for consistency. + +```typescript +// Example: in your setup/initialization code +import { Icons, IconNameEnum } from '@ohif/ui-next'; +import Baseline from './sources/Baseline'; // Import your new icon component + +// Add the icon to the registry +Icons.addIcon(IconNameEnum.BASELINE, Baseline); +``` + +By following these steps, your custom icon will be available for use throughout the application just like any of the default icons. + + + + ### Detailed Renaming Table | Old Icon Name | New Icon Component Name | Example Usage (`Icons.`) | Notes | diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/2a-Migration-3p10-Button.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/2a-Migration-3p10-Button.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/2a-Migration-3p10-Button.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/2a-Migration-3p10-Button.md index 887966a141b..a1acdb7398a 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/2a-Migration-3p10-Button.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/2a-Migration-3p10-Button.md @@ -1,5 +1,6 @@ --- title: Button +summary: Migration guide for Button components in OHIF 3.10, explaining the transition from @ohif/ui to @ohif/ui-next, the replacement of ButtonEnums with string-based variants, and changes to IconButton, ButtonGroup, and styling approach. --- ## Key Changes: diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/3-Migration-3p10-Input.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/3-Migration-3p10-Input.md similarity index 98% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/3-Migration-3p10-Input.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/3-Migration-3p10-Input.md index bc4816cea5f..a2b2e57aa92 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/3-Migration-3p10-Input.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/3-Migration-3p10-Input.md @@ -1,5 +1,6 @@ --- title: Input +summary: Migration guide for input components in OHIF 3.10, covering the transition from Input, InputNumber, InputRange, InputDoubleRange and others to the new Numeric component system and updated input patterns in @ohif/ui-next. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/4-Migration-3p10-Tooltip.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/4-Migration-3p10-Tooltip.md similarity index 87% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/4-Migration-3p10-Tooltip.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/4-Migration-3p10-Tooltip.md index 80a33acaa26..90796a2ca51 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/4-Migration-3p10-Tooltip.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/4-Migration-3p10-Tooltip.md @@ -1,5 +1,6 @@ --- title: Tooltip +summary: Migration guide for Tooltip components in OHIF 3.10, explaining the new composable structure with TooltipTrigger and TooltipContent, and replacement of TooltipClipboard with the Clipboard component. --- ## Tooltip Updates diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/4a-Migration-3p10-Select.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/4a-Migration-3p10-Select.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/4a-Migration-3p10-Select.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/4a-Migration-3p10-Select.md index 27e5cdf286e..ee708385f30 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/4a-Migration-3p10-Select.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/4a-Migration-3p10-Select.md @@ -1,5 +1,6 @@ --- -title: Tooltip +title: Select +summary: Migration guide for Select components in OHIF 3.10, explaining the transition from @ohif/ui to @ohif/ui-next, with details on the new Select.Root, Select.Trigger, Select.Content, and Select.Item structure replacing the old dropdown approach. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/4a-Migration-3p10-Switch.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/4a-Migration-3p10-Switch.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/4a-Migration-3p10-Switch.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/4a-Migration-3p10-Switch.md index 981958eee4d..1f26ea69fb6 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/4a-Migration-3p10-Switch.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/4a-Migration-3p10-Switch.md @@ -1,5 +1,6 @@ --- title: Switch +summary: Migration guide for Switch component in OHIF 3.10, covering the transition from custom Toggle and CinePlayPauseButton components to the standardized Switch component, with details on prop changes and usage patterns. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/5-Migration-3p10-Toolbar.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/5-Migration-3p10-Toolbar.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/5-Migration-3p10-Toolbar.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/5-Migration-3p10-Toolbar.md index ae4041cc0b5..4c6c8ee1b38 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/5-Migration-3p10-Toolbar.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/5-Migration-3p10-Toolbar.md @@ -1,5 +1,6 @@ --- title: Toolbar +summary: Migration guide for toolbar components in OHIF 3.10, covering the new uiType values (ohif.toolButton and ohif.toolButtonList), section-based definitions replacing nested structures, ToolBox updates, and changes to tool option handlers. --- # Toolbar diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/6-Migration-3p10-SegmentationTable.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/6-Migration-3p10-SegmentationTable.md similarity index 92% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/6-Migration-3p10-SegmentationTable.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/6-Migration-3p10-SegmentationTable.md index 713f44ec3af..a02fef59c18 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/6-Migration-3p10-SegmentationTable.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/6-Migration-3p10-SegmentationTable.md @@ -1,5 +1,6 @@ --- title: Segmentation Table +summary: Migration guide for the refactored SegmentationTable component in OHIF 3.10, covering changes to the context system, compound component pattern adoption, replacement of SelectorHeader, and introduction of segment statistics features. --- diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/7-Migration-3p10-Tours.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/7-Migration-3p10-Tours.md similarity index 85% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/7-Migration-3p10-Tours.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/7-Migration-3p10-Tours.md index 16ac94663bf..d18dce99ec5 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/7-Migration-3p10-Tours.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/7-Migration-3p10-Tours.md @@ -1,5 +1,6 @@ --- title: Tours and Onboarding +summary: Migration guide for Tours and Onboarding features in OHIF 3.10, explaining the transition from defining tours directly in window.config.tours to using the customization service, enabling mode-specific tours and better organization. --- ## Migration Guide: Tours diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/8-Migration-3p10-DialogService.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/8-Migration-3p10-DialogService.md similarity index 65% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/8-Migration-3p10-DialogService.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/8-Migration-3p10-DialogService.md index 960029bda24..835cd828c00 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/8-Migration-3p10-DialogService.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/8-Migration-3p10-DialogService.md @@ -1,5 +1,6 @@ --- title: uiDialogService +summary: Migration guide for uiDialogService in OHIF 3.10, covering the transition from create/dismiss to show/hide methods, changes to dialog definition structure, and updates to utility functions like callInputDialog and colorPickerDialog. --- @@ -59,6 +60,32 @@ This guide details the migration steps for the `uiDialogService` API changes, ba | `shouldCloseOnOverlayClick` | Default off for dialogs - Controls whether clicking the overlay background will close the dialog. | +### Frequently Asked Questions + +**Q: Why did my dialog's background color change?** + +A: This can happen if you were previously setting the background color on the dialog's content directly. With the new API, the dialog's content is wrapped in a container. You should now pass any background or text color classes using the `containerClassName` property. + +For example: +```diff +- containerClassName: 'w-[70%] max-w-[900px]', ++ containerClassName: 'w-[70%] max-w-[900px] bg-primary-dark text-foreground', +``` + +**Q: How do I create a dialog without the default container, like for a context menu?** + +A: If you need to render dialog content without the standard dialog container (e.g., for a context menu), you can use the `unstyled: true` prop. This will render your component without the default dialog wrapper, giving you full control over its appearance. + +```javascript +uiDialogService.show({ + id: 'context-menu', + content: MyContextMenuComponent, + contentProps: { /* ... */ }, + unstyled: true, +}); +``` + + **Migration Steps:** 1. **Replace `.create()` with `.show()`:** @@ -117,48 +144,110 @@ This guide details the migration steps for the `uiDialogService` API changes, ba } ``` +6. **Update Footer Action Buttons:** + + Previously, footer buttons might have been implemented using generic `

{message}

, - onClose: () => uiDialogService.dismiss({ id: dialogId }), + title: 'Action Completed', + noCloseButton: true, + onClose: dismiss, + onSubmit: dismiss, + actions: [ + { id: 'proceed', text: 'Proceed', type: 'primary' }, + ], + body: () => ( +
{successMessage}
+ ), }, }); }; +``` + +**After: Using a Dedicated Component** + +The new pattern involves creating a dedicated React component for your dialog's content. This component encapsulates its own layout, logic, and actions, leading to cleaner and more maintainable code. + +**1. Create a dedicated component for your dialog's content.** + +This component receives `hide` and any custom data via props. It manages its own UI, including the footer buttons, and handles the logic for what happens when a user interacts with it. + +```javascript +// After: MyCompletionDialog.tsx +function MyCompletionDialog({ hide, successMessage }) { + const closeAndProceed = () => { + hide(); + // You can now handle any post-dialog logic here, + // such as navigating to a different page. + // navigate('/next-page'); + }; -// After (using new API) -function AlertDialog({ message, hide }) { return ( -
-

{message}

- +
+
{successMessage}
+ + + + Proceed + + +
); } +``` + +**2. Call `uiDialogService.show` with the new component.** -const showAlert = (message) => { +The call to show the dialog is now much simpler. You pass the component itself to the `content` property and any necessary data through `contentProps`. + +```javascript +// After: Calling the service +const showCompletionDialog = (successMessage) => { uiDialogService.show({ - id: 'alert-dialog', - title: 'Alert', - content: AlertDialog, - contentProps: { message }, + id: 'my-complex-dialog', + title: 'Action Completed', + content: MyCompletionDialog, + contentProps: { + successMessage, + }, }); }; - ``` - --- ## Components diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/9-Migration-3p10-ModalService.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/9-Migration-3p10-ModalService.md similarity index 59% rename from platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/9-Migration-3p10-ModalService.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/9-Migration-3p10-ModalService.md index 1cd2d90223e..09746de95b2 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/3p9-to-3p10/3-UI/9-Migration-3p10-ModalService.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/3-UI/9-Migration-3p10-ModalService.md @@ -1,5 +1,6 @@ --- title: uiModalService +summary: Migration guide for the uiModalService in OHIF 3.10, covering props that remain unchanged, renamed props (containerDimensions to containerClassName), removed props (movable, isOpen, contentDimensions), and automatic handling of modal closing. --- @@ -35,6 +36,17 @@ title: uiModalService | `closeButton` | The component now manages modal closing internally. If you need a close button, you can add one, perhaps by checking out the `FooterActions` component. | +### Frequently Asked Questions + +**Q: Why did my dialog's background color change?** + +A: This can happen if you were previously setting the background color on the dialog's content directly. With the new API, the dialog's content is wrapped in a container. You should now pass any background or text color classes using the `containerClassName` property. + +For example: +```diff +- containerClassName: 'w-[70%] max-w-[900px]', ++ containerClassName: 'w-[70%] max-w-[900px] bg-primary-dark text-foreground', +``` **Migration Steps:** @@ -102,3 +114,60 @@ Previously, you had to pass in the `onClose` as `hide` function automatically ad + }, + }); ``` + +6. **Update Footer Action Buttons:** + + Previously, footer buttons might have been implemented using generic ` + + + + + + Cancel + + + + + ``` + +**Example: Simple Alert Modal** + +This example shows how to migrate a simple alert modal. + +*Before:* + +```js +uiModalService.show({ + title: 'Untrack Series', + content: UntrackSeriesModal, + contentProps: { + onConfirm, + hide, // passed in automatically in the background + }, +}); +``` + +*After:* + +```js +function UntrackSeriesModal({ onConfirm, hide }) { + return ( +
+ {/* Modal content */} +
+ ); +} + +// Show the modal +uiModalService.show({ + title: 'Untrack Series', + content: UntrackSeriesModal, + contentProps: { + onConfirm, + hide, // passed in automatically in the background + }, +}); +``` diff --git a/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/index.md b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/index.md new file mode 100644 index 00000000000..e2da5ae3226 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/3p9-to-3p10/index.md @@ -0,0 +1,11 @@ +--- +sidebar_position: 1 +sidebar_label: 3.9 -> 3.10 +title: Migration Guide from 3.9 to 3.10 beta +summary: Migration guide for upgrading from OHIF 3.9 to 3.10 beta, covering general changes, customization service improvements, UI component upgrades, command handling, hotkey updates, routing changes, and testing strategies. +--- + +# Migration Guide + + +## General diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/_category_.json b/platform/docs/versioned_docs/version-3.11/migration-guide/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/migration-guide/_category_.json rename to platform/docs/versioned_docs/version-3.11/migration-guide/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/from-3p7-to-3p8.md b/platform/docs/versioned_docs/version-3.11/migration-guide/from-3p7-to-3p8.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/migration-guide/from-3p7-to-3p8.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/from-3p7-to-3p8.md index b07d8ffe0e4..0b460dc333c 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/from-3p7-to-3p8.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/from-3p7-to-3p8.md @@ -1,6 +1,8 @@ --- sidebar_position: 2 sidebar_label: 3.7 -> 3.8 +title: Migration Guide from 3.7 to 3.8 +summary: Migration guide for upgrading from OHIF 3.7 to 3.8, covering changes to toolbar button definitions, active tool handling, button evaluators, tool listeners, leftPanel/rightPanel naming, URL parameters, UI components, and refactoring of enums. --- # Migration Guide diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/from-v2.md b/platform/docs/versioned_docs/version-3.11/migration-guide/from-v2.md similarity index 99% rename from platform/docs/versioned_docs/version-3.10/migration-guide/from-v2.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/from-v2.md index 920f4d095b4..aa5c531ed1f 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/from-v2.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/from-v2.md @@ -1,6 +1,8 @@ --- sidebar_position: 3 sidebar_label: 2.x -> 3.5 +title: Migration Guide from 2.x to 3.5 +summary: Comprehensive migration guide for upgrading from OHIF 2.x to 3.5, covering architectural changes, UI improvements, extensions, modes, cornerstone3D integration, configuration changes, and build process updates. --- # Migration Guide diff --git a/platform/docs/versioned_docs/version-3.10/migration-guide/index.md b/platform/docs/versioned_docs/version-3.11/migration-guide/index.md similarity index 58% rename from platform/docs/versioned_docs/version-3.10/migration-guide/index.md rename to platform/docs/versioned_docs/version-3.11/migration-guide/index.md index 85d2af849cc..a293b5cf442 100644 --- a/platform/docs/versioned_docs/version-3.10/migration-guide/index.md +++ b/platform/docs/versioned_docs/version-3.11/migration-guide/index.md @@ -1,5 +1,7 @@ --- id: index +title: Migration Guides Overview +summary: Introduction to OHIF migration guides covering the upgrade paths between different versions of the platform, with links to version-specific guides for migrating from one OHIF version to another. --- diff --git a/platform/docs/versioned_docs/version-3.10/platform/_category_.json b/platform/docs/versioned_docs/version-3.11/platform/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/platform/_category_.json rename to platform/docs/versioned_docs/version-3.11/platform/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/platform/browser-support.md b/platform/docs/versioned_docs/version-3.11/platform/browser-support.md similarity index 86% rename from platform/docs/versioned_docs/version-3.10/platform/browser-support.md rename to platform/docs/versioned_docs/version-3.11/platform/browser-support.md index fa31439c38d..e98426213d8 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/browser-support.md +++ b/platform/docs/versioned_docs/version-3.11/platform/browser-support.md @@ -1,5 +1,7 @@ --- sidebar_position: 2 +title: Browser Support +summary: Documentation of OHIF's browser compatibility standards, including supported browsers (IE11, Firefox, Chrome, Safari, Edge), transpilation and polyfill strategies, and development priorities focused on modern evergreen browsers. --- # Browser Support diff --git a/platform/docs/versioned_docs/version-3.10/platform/environment-variables.md b/platform/docs/versioned_docs/version-3.11/platform/environment-variables.md similarity index 76% rename from platform/docs/versioned_docs/version-3.10/platform/environment-variables.md rename to platform/docs/versioned_docs/version-3.11/platform/environment-variables.md index df5da9e0381..a316c9ed916 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/environment-variables.md +++ b/platform/docs/versioned_docs/version-3.11/platform/environment-variables.md @@ -1,6 +1,8 @@ --- sidebar_position: 3 sidebar_label: Environment Variables +title: Environment Variables +summary: Documentation of the environment variables used in OHIF during build time to configure application behavior, including application settings, internationalization options, and methods for setting these variables. --- # Environment Variables diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/_category_.json b/platform/docs/versioned_docs/version-3.11/platform/extensions/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/_category_.json rename to platform/docs/versioned_docs/version-3.11/platform/extensions/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/extension.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/extension.md similarity index 86% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/extension.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/extension.md index 148b82a9faf..706a7b0495c 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/extension.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/extension.md @@ -1,6 +1,8 @@ --- sidebar_position: 4 sidebar_label: Extension Manager +title: OHIF Extension Manager +summary: Documentation for OHIF's ExtensionManager class, which manages the registration and access of extensions, providing methods to retrieve module entries, access data sources, and handle extension configurations. --- # Extension Manager diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/index.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/index.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/index.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/index.md index d9c630ecb1c..1a1801ae46f 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/index.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/index.md @@ -1,6 +1,8 @@ --- sidebar_position: 1 sidebar_label: Introduction +title: OHIF Extensions Introduction +summary: Comprehensive introduction to the OHIF viewer extension system, explaining how extensions provide modular functionality through different module types, and how they're configured and accessed in modes to build customized viewer experiences. --- # Introduction diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/installation.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/installation.md similarity index 58% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/installation.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/installation.md index 2e5fb81c2a1..af49ed17cf5 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/installation.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/installation.md @@ -1,6 +1,8 @@ --- sidebar_position: 5 sidebar_label: Installation +title: Extension Installation +summary: Instructions for installing external OHIF extensions using the OHIF CLI tool, which allows adding both local and published extensions from NPM to the viewer application. --- # Extension: Installation diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/lifecycle.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/lifecycle.md similarity index 92% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/lifecycle.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/lifecycle.md index 27868888cb6..489694c9cdc 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/lifecycle.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/lifecycle.md @@ -1,6 +1,8 @@ --- sidebar_position: 3 sidebar_label: Lifecycle Hooks +title: Extension Lifecycle Hooks +summary: Documentation for OHIF extension lifecycle hooks including preRegistration, onModeEnter, and onModeExit, which allow extensions to initialize resources, handle mode transitions, and clean up data when needed. --- # Extensions: Lifecycle Hooks diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/_category_.json b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/modules/_category_.json rename to platform/docs/versioned_docs/version-3.11/platform/extensions/modules/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/commands.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/commands.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/modules/commands.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/modules/commands.md index b37202bf61e..840c5bc48cd 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/commands.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/commands.md @@ -1,6 +1,8 @@ --- sidebar_position: 2 sidebar_label: Commands +title: Commands Module +summary: Documentation for OHIF Commands Module, which allows extensions to register functions that can be called by tools, UI components, or hotkeys, with context-specific implementations and flexible command definitions. --- # Module: Commands diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/contextModule.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/contextModule.md similarity index 75% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/modules/contextModule.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/modules/contextModule.md index 35de381a3da..f1bfe48b6bc 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/contextModule.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/contextModule.md @@ -1,6 +1,8 @@ --- sidebar_position: 9 sidebar_label: Context +title: Context Module +summary: Documentation for OHIF Context Module, which enables component communication via React Context, allowing viewport and panel components to share state and synchronize through a common provider mechanism. --- # Module: Context diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/data-source.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/data-source.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/modules/data-source.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/modules/data-source.md index 5f682ff9c20..3c800421945 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/data-source.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/data-source.md @@ -1,6 +1,8 @@ --- sidebar_position: 3 sidebar_label: Data Source +title: Data Source Module +summary: Documentation for OHIF Data Source Module, which defines ways to fetch and map data into OHIF's native format, covering built-in sources like dicomweb and dicomjson, and explaining how to create custom data sources for proprietary backends. --- # Module: Data Source diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/hpModule.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/hpModule.md similarity index 98% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/modules/hpModule.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/modules/hpModule.md index 99dbe1f0c34..a8ac4682832 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/hpModule.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/hpModule.md @@ -1,6 +1,8 @@ --- sidebar_position: 8 sidebar_label: Hanging Protocol +title: Hanging Protocol Module +summary: Documentation for OHIF Hanging Protocol Module, which controls image arrangement in viewports based on matching rules, with capabilities for layout configuration, viewport settings, synchronization, and advanced study comparison workflows. --- # Module: Hanging Protocol diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/layout-template.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/layout-template.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/modules/layout-template.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/modules/layout-template.md index 821444975b8..f5d1fcfdd09 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/layout-template.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/layout-template.md @@ -1,6 +1,8 @@ --- sidebar_position: 7 sidebar_label: Layout Template +title: Layout Template Module +summary: Documentation for OHIF Layout Template Module, which defines the structural organization of the viewer interface, controlling toolbar positioning, panel arrangement, and viewport grid placement through React components. --- # Module: Layout Template diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/panel.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/panel.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/modules/panel.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/modules/panel.md index 9a9235ceefb..2b48ebd4f3b 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/panel.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/panel.md @@ -1,6 +1,8 @@ --- sidebar_position: 6 sidebar_label: Panel +title: Panel Module +summary: Documentation for OHIF Panel Module, which provides UI components for the application's side panels, allowing extensions to contribute content that appears in left and right sidebars with proper event triggering and state management. --- # Module: Panel diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/sop-class-handler.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/sop-class-handler.md similarity index 76% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/modules/sop-class-handler.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/modules/sop-class-handler.md index cf70fb42fbf..4145b8c680e 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/sop-class-handler.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/sop-class-handler.md @@ -1,6 +1,8 @@ --- sidebar_position: 4 sidebar_label: SOP Class Handler +title: SOP Class Handler Module +summary: Documentation for OHIF SOP Class Handler Module, which processes specific DICOM SOP classes into displayable sets, supporting various medical imaging formats and defining how different types of DICOM data are prepared for viewing. --- # Module: SOP Class Handler @@ -37,6 +39,31 @@ return { } ``` +## Replacing Instances in Series + +Series such as segmentations and annotations often have the concept of replacing +an earlier instance with a newer one. The official way to do this is using the +`predecessor document sequence` to reference the old object in the new one. +It isn't that the old one is deleted (never delete data), but rather that the new +one is used in preference to the old one. This results in code that looks like: + +``` + utils.sortStudyInstances(instances); + // Choose the LAST instance in the list as the most recently created one. + const instance = instances[instances.length - 1]; + + ... + const displaySet = { + ... + predecessorImageId: instance.imageId, + numImageFrames: instances.length, + } +``` + +so that the last/latest SOP instance is used by default, and that it is +recorded what imageId was used to load that instance. This then allows saving +new instances in the same series by using a new predecessor sequence. + ## Example SOP Class Handler Module ```js diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/toolbar.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/toolbar.md similarity index 98% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/modules/toolbar.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/modules/toolbar.md index 6d772b9307d..63197d6b2a9 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/toolbar.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/toolbar.md @@ -1,6 +1,8 @@ --- sidebar_position: 1 sidebar_label: Toolbar +title: Toolbar Module +summary: Documentation for OHIF Toolbar Module, which provides UI components and evaluators for the application's toolbar, supporting custom button types, advanced state management, and conditional rendering based on viewport and modality requirements. --- # Module: Toolbar diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/utility.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/utility.md similarity index 85% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/modules/utility.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/modules/utility.md index fbb975d1a07..bc44a9f9cb6 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/utility.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/utility.md @@ -1,6 +1,8 @@ --- sidebar_position: 9 sidebar_label: Utility +title: Utility Module +summary: Documentation for OHIF Utility Module, which exposes reusable functionality from one extension to others, allowing for sharing of internal methods, services, and enumerations across the application architecture. --- # Module: Utility diff --git a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/viewport.md b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/viewport.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/platform/extensions/modules/viewport.md rename to platform/docs/versioned_docs/version-3.11/platform/extensions/modules/viewport.md index b64a13e33e8..782594c07d0 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/extensions/modules/viewport.md +++ b/platform/docs/versioned_docs/version-3.11/platform/extensions/modules/viewport.md @@ -1,6 +1,8 @@ --- sidebar_position: 5 sidebar_label: Viewport +title: Viewport Module +summary: Documentation for OHIF Viewport Module, which provides React components that consume displaySets for viewing medical images and documents, with examples of 2D image viewing, structured reports, PDFs, and performance optimization techniques. --- # Module: Viewport diff --git a/platform/docs/versioned_docs/version-3.11/platform/hooks/index.md b/platform/docs/versioned_docs/version-3.11/platform/hooks/index.md new file mode 100644 index 00000000000..52cc5ee47b9 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/hooks/index.md @@ -0,0 +1,33 @@ +--- +title: Hooks +summary: List of React hooks available in the platform, these are custom hooks that are used to access the state of the platform +--- + +# Hooks + +## [useMeasurements](./useMeasurements.md) +A React hook that provides mapped measurements from the measurement service with automatic updates when measurements change. + +## [useViewportSegmentations](./useViewportSegmentations.md) +A React hook that provides segmentation data and representations for the active viewport with automatic updates when segmentations change. + +## [useMeasurementTracking](./useMeasurementTracking.md) +A React hook that provides measurement tracking information for a specific viewport, including tracking state and tracked measurement UIDs. + +## [useViewportDisplaySets](./useViewportDisplaySets.md) +A React hook that provides access to display sets associated with a viewport, including background, foreground, overlay, and potential display sets. + +## [useViewportHover](./useViewportHover.md) +A React hook that tracks mouse hover state and active status for a specific viewport. + +## [usePatientInfo](./usePatientInfo.md) +A React hook that provides patient information from the active display sets and detects when multiple patients are loaded. + +## [useSearchParams](./useSearchParams.md) +A React hook that provides access to URL search parameters from both the query string and hash fragment. + +## [useDynamicMaxHeight](./useDynamicMaxHeight.md) +A React hook that calculates the maximum height for an element based on its position in the viewport, with automatic recalculation on window resize or data changes. + +## [useSessionStorage](./useSessionStorage.md) +A React hook that provides sessionStorage access with automatic JSON parsing/stringifying and an option to clear data when the page unloads. \ No newline at end of file diff --git a/platform/docs/versioned_docs/version-3.11/platform/hooks/useDynamicMaxHeight.md b/platform/docs/versioned_docs/version-3.11/platform/hooks/useDynamicMaxHeight.md new file mode 100644 index 00000000000..95854c173af --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/hooks/useDynamicMaxHeight.md @@ -0,0 +1,65 @@ +--- +title: useDynamicMaxHeight +summary: A React hook that calculates the maximum height for an element based on its position in the viewport, with automatic recalculation on window resize or data changes. +--- + +# useDynamicMaxHeight + +The `useDynamicMaxHeight` hook calculates the maximum height an element can have based on its position relative to the bottom of the viewport, ensuring it doesn't overflow or get cut off by the viewport edge. + +## Overview + +This hook is useful for creating responsive UI elements that need to fit within the visible area of the screen without causing scrolling or content overflow. It automatically recalculates the maximum height when the window is resized or when specified data changes, ensuring the element always fits properly. + +## Import + +```js +import { useDynamicMaxHeight } from '@ohif/ui-next'; +``` + +## Usage + +```jsx +function DynamicHeightPanel({ data, children }) { + const { ref, maxHeight } = useDynamicMaxHeight(data, 30, 200); + + return ( +
+ {children} +
+ ); +} +``` + +## Parameters + +- `data` (required): Any data that, when changed, should trigger a recalculation of the maximum height. This can be an array, object, or primitive value. +- `buffer` (optional): Additional space (in pixels) to leave below the element. Defaults to 20px. +- `minHeight` (optional): Minimum height (in pixels) for the element. Defaults to 100px. + +## Returns + +An object containing: + +- `ref`: A React ref object that must be attached to the target DOM element +- `maxHeight`: A CSS-compatible string value for the calculated maximum height (e.g., "500px") + +## Implementation Details + +- The hook uses `window.innerHeight` and the element's position to calculate the available space between the element's top edge and the bottom of the viewport. +- It subtracts the specified buffer from the available height to ensure there's space below the element. +- The calculated height is constrained by the specified minimum height to prevent the element from becoming too small. +- The hook uses `requestAnimationFrame` to ensure the initial calculation happens after the component has been rendered and positioned in the DOM. +- It automatically recalculates the maximum height when: + - The window is resized + - The `data`, `buffer`, or `minHeight` dependencies change +- The hook properly cleans up event listeners and animation frame requests when the component unmounts. + +This hook is particularly useful for panels, menus, and content areas that need to dynamically adjust their height based on their position in the viewport, ensuring a good user experience without unexpected scrolling or content clipping. \ No newline at end of file diff --git a/platform/docs/versioned_docs/version-3.11/platform/hooks/useMeasurementTracking.md b/platform/docs/versioned_docs/version-3.11/platform/hooks/useMeasurementTracking.md new file mode 100644 index 00000000000..b488e86d03b --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/hooks/useMeasurementTracking.md @@ -0,0 +1,79 @@ +--- +title: useMeasurementTracking +summary: A React hook that provides measurement tracking information for a specific viewport, including tracking state and tracked measurement UIDs. +--- + +# useMeasurementTracking + +The `useMeasurementTracking` hook provides measurement tracking information for a specific viewport, including the tracking state and UIDs of tracked measurements associated with the viewport's series. + +## Overview + +This hook gives components access to tracking information for measurements in a viewport. It monitors the tracked measurements service and measurement service to provide up-to-date tracking states and the list of measurement UIDs associated with the series displayed in the viewport. + +## Import + +```js +import { useMeasurementTracking } from '@ohif/extension-cornerstone'; +``` + +## Usage + +```jsx +function MeasurementTrackingInfo({ viewportId }) { + const { + isTracked, + isLocked, + seriesInstanceUID, + trackedMeasurementUIDs + } = useMeasurementTracking({ + viewportId, + }); + + return ( +
+
Series UID: {seriesInstanceUID}
+
Tracking Status: {isTracked ? 'Tracked' : 'Not Tracked'}
+
Locked: {isLocked ? 'Yes' : 'No'}
+
Tracked Measurements: {trackedMeasurementUIDs.length}
+
    + {trackedMeasurementUIDs.map(uid => ( +
  • {uid}
  • + ))} +
+
+ ); +} +``` + +## Parameters + +- `options` - Configuration options: + - `viewportId` (required): The ID of the viewport to track + +## Returns + +An object containing the following properties: + +- `isTracked`: Boolean indicating if the series in the viewport is currently tracked +- `isLocked`: Boolean indicating if tracking is enabled (locked) globally +- `seriesInstanceUID`: The Series Instance UID of the background display set in the viewport +- `trackedMeasurementUIDs`: Array of measurement UIDs that are associated with the tracked series in the viewport + +## Events + +The hook automatically updates when any of these events occur: + +From the tracked measurements service: +- `TRACKING_ENABLED` +- `TRACKING_DISABLED` +- `TRACKED_SERIES_CHANGED` +- `SERIES_ADDED` +- `SERIES_REMOVED` + +From the measurement service: +- `MEASUREMENT_ADDED` +- `RAW_MEASUREMENT_ADDED` +- `MEASUREMENT_UPDATED` +- `MEASUREMENT_REMOVED` +- `MEASUREMENTS_CLEARED` diff --git a/platform/docs/versioned_docs/version-3.11/platform/hooks/useMeasurements.md b/platform/docs/versioned_docs/version-3.11/platform/hooks/useMeasurements.md new file mode 100644 index 00000000000..d1336d0c72c --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/hooks/useMeasurements.md @@ -0,0 +1,68 @@ +--- +title: useMeasurements +summary: A React hook that provides mapped measurements from the measurement service with automatic updates when measurements change. +--- + +# useMeasurements + +The `useMeasurements` hook provides access to measurements from the measurement service, with automatic updates when measurements are added, updated, or removed. + +## Overview + +This hook retrieves measurements from the measurement service and maps them to a display-friendly format. It monitors various measurement service events and updates the returned measurements automatically when changes occur. + +## Import + +```js +import { useMeasurements } from '@ohif/extension-cornerstone'; +``` + +## Usage + +```jsx +function MeasurementPanel() { + const measurementFilter = measurements => measurements.someFilter; + + const measurements = useMeasurements({ + measurementFilter, + }); + + return ( +
+ {measurements.map(measurement => ( +
+ {measurement.label} +
+ {measurement.displayText.primary.map((text, i) => ( +

{text}

+ ))} +
+
+ ))} +
+ ); +} +``` + +## Parameters + +- `options` - Configuration options: + - `measurementFilter` - Optional function to filter measurements returned by the measurement service. + +## Returns + +An array of mapped measurements with the following structure: + + +## Events + +The hook automatically updates when any of these measurement service events occur: +- `MEASUREMENT_ADDED` +- `RAW_MEASUREMENT_ADDED` +- `MEASUREMENT_UPDATED` +- `MEASUREMENT_REMOVED` +- `MEASUREMENTS_CLEARED` + +## Implementation Details + +The hook uses debouncing to prevent excessive re-renders when multiple measurement events occur in rapid succession. It also performs a deep comparison of the measurements to avoid unnecessary state updates when the data hasn't actually changed. diff --git a/platform/docs/versioned_docs/version-3.11/platform/hooks/usePatientInfo.md b/platform/docs/versioned_docs/version-3.11/platform/hooks/usePatientInfo.md new file mode 100644 index 00000000000..62bc9b9aec8 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/hooks/usePatientInfo.md @@ -0,0 +1,68 @@ +--- +title: usePatientInfo +summary: A React hook that provides patient information from the active display sets and detects when multiple patients are loaded. +--- + +# usePatientInfo + +The `usePatientInfo` hook provides access to basic patient demographic information from the active display sets and also detects when display sets from multiple patients are loaded simultaneously. + +## Overview + +This hook retrieves patient information from the first instance of the first display set added to the viewer. It monitors when new display sets are added and updates the patient information accordingly. It also checks if any of the active display sets are from different patients and provides this information through the `isMixedPatients` flag. + +## Import + +```js +import { usePatientInfo } from '@ohif/extension-default'; +``` + +## Usage + +```jsx +function PatientBanner() { + const { patientInfo, isMixedPatients } = usePatientInfo(); + + return ( +
+ {isMixedPatients && ( +
Multiple patients loaded
+ )} +
{patientInfo.PatientName}
+
+ ID: {patientInfo.PatientID} + Sex: {patientInfo.PatientSex} + DOB: {patientInfo.PatientDOB} +
+
+ ); +} +``` + +## Parameters + +This hook doesn't take any parameters. + +## Returns + +An object containing: + +- `patientInfo`: Object with the following properties: + - `PatientName`: Formatted patient name + - `PatientID`: Patient identifier + - `PatientSex`: Patient sex + - `PatientDOB`: Formatted patient date of birth +- `isMixedPatients`: Boolean indicating whether multiple patients are loaded in the viewer + +## Events + +The hook subscribes to the following display set service events: + +- `DISPLAY_SETS_ADDED`: Updates patient information when new display sets are added to the viewer + +## Implementation Details + +- Patient name and date of birth are formatted using OHIF utility functions. +- The hook checks all active display sets to determine if they belong to different patients. +- Patient information is initialized with empty strings and updated when display sets are added. +- When no instances are available in a display set, the hook attempts to get information from the `instance` property as a fallback. \ No newline at end of file diff --git a/platform/docs/versioned_docs/version-3.11/platform/hooks/useSearchParams.md b/platform/docs/versioned_docs/version-3.11/platform/hooks/useSearchParams.md new file mode 100644 index 00000000000..909c1b0bc7a --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/hooks/useSearchParams.md @@ -0,0 +1,69 @@ +--- +title: useSearchParams +summary: A React hook that provides access to URL search parameters from both the query string and hash fragment. +--- + +# useSearchParams + +The `useSearchParams` hook provides access to URL search parameters, combining both query string parameters and hash fragment parameters into a single URLSearchParams object. + +## Overview + +This hook extends the standard React Router `useLocation` functionality by merging search parameters from both the query string and the hash fragment of the URL. It also provides an option to normalize parameter keys to lowercase for case-insensitive parameter handling. + +## Import + +```js +import { useSearchParams } from '@ohif/app'; +``` + +## Usage + +```jsx +function RouteParameterReader() { + const searchParams = useSearchParams(); + // Or with lowercase keys option + const lowerCaseParams = useSearchParams({ lowerCaseKeys: true }); + + const studyInstanceUID = searchParams.get('StudyInstanceUID'); + // With lowerCaseKeys: true + const sameStudyUID = lowerCaseParams.get('studyinstanceuid'); + + return ( +
+
Study UID: {studyInstanceUID}
+
All Parameters:
+
    + {Array.from(searchParams.entries()).map(([key, value]) => ( +
  • + {key}: {value} +
  • + ))} +
+
+ ); +} +``` + +## Parameters + +- `options` (optional): Configuration options + - `lowerCaseKeys` - Boolean indicating whether to convert all parameter keys to lowercase (default: false) + +## Returns + +A `URLSearchParams` object containing all parameters from both: +- The query string (e.g., `?param1=value1¶m2=value2`) +- The hash fragment (e.g., `#param3=value3¶m4=value4`) + +If parameters with the same key exist in both the query string and hash fragment, the hash fragment values take precedence. + +## Implementation Details + +- The hook uses React Router's `useLocation` to access the current URL. +- It first creates a URLSearchParams object from the location's search property. +- It then creates another URLSearchParams object from the location's hash property (excluding the leading '#'). +- The parameters from the hash are added to the search parameters, overriding any duplicate keys. +- If the `lowerCaseKeys` option is enabled, it creates a new URLSearchParams object with all keys converted to lowercase. + +This is particularly useful in OHIF where parameters may be specified in either the query string or hash fragment, and where case-sensitivity of parameter names might vary across different systems. diff --git a/platform/docs/versioned_docs/version-3.11/platform/hooks/useSessionStorage.md b/platform/docs/versioned_docs/version-3.11/platform/hooks/useSessionStorage.md new file mode 100644 index 00000000000..8eed706d5f4 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/hooks/useSessionStorage.md @@ -0,0 +1,84 @@ +--- +title: useSessionStorage +summary: A React hook that provides sessionStorage access with automatic JSON parsing/stringifying and an option to clear data when the page unloads. +--- + +# useSessionStorage + +The `useSessionStorage` hook provides a convenient way to store and retrieve data from the browser's sessionStorage with automatic JSON serialization and deserialization. It also offers the option to automatically clear the stored data when a page unloads. + +## Overview + +This hook wraps the browser's sessionStorage API to provide a more React-friendly interface. It handles JSON serialization/deserialization automatically and maintains the stored values in local state for reactive updates. The hook also includes a unique feature to clear specific items from sessionStorage when the page unloads, which is useful for temporary session data that shouldn't persist. + +## Import + +```js +import { useSessionStorage } from '@ohif/ui-next'; +``` + +## Usage + +```jsx +function UserPreferencesPanel() { + const [preferences, setPreferences] = useSessionStorage({ + key: 'viewer-preferences', + defaultValue: { theme: 'dark', fontSize: 'medium' }, + clearOnUnload: false, + }); + + const updateTheme = (theme) => { + setPreferences({ ...preferences, theme }); + }; + + return ( +
+

User Preferences

+
Current Theme: {preferences.theme}
+ + +
+ ); +} + +function TemporaryWorkspace() { + const [workspace, setWorkspace] = useSessionStorage({ + key: 'temp-workspace', + defaultValue: { annotations: [] }, + clearOnUnload: true, // This data will be cleared when the page unloads + }); + + return ( +
+

Temporary Workspace

+

This workspace will be cleared when you leave the page

+ {/* Workspace UI components */} +
+ ); +} +``` + +## Parameters + +An options object with the following properties: + +- `key` (required): The sessionStorage key under which to store the data +- `defaultValue` (optional): The default value to use if no data exists in sessionStorage for the given key. Defaults to an empty object (`{}`) +- `clearOnUnload` (optional): Whether to clear this item from sessionStorage when the page unloads. Defaults to `false` + +## Returns + +An array containing: + +1. The current value from sessionStorage (parsed from JSON) +2. A function to update the value in both state and sessionStorage + +The update function automatically handles JSON stringification of the data. + +## Implementation Details + +- The hook uses a global Map (`sessionItemsToClearOnUnload`) to track items that should be cleared when the page unloads. +- It utilizes the browser's `visibilitychange` event to implement the clearOnUnload feature. When the page becomes hidden (which happens both when switching tabs and when unloading), items marked for clearing are removed from sessionStorage. +- If the page later becomes visible again (e.g., when switching back to the tab), the items are restored from the Map. +- The hook's update function merges the new value with the current state using the spread operator, maintaining a similar behavior to React's `setState`. +- All data is automatically serialized to JSON before storing in sessionStorage and deserialized when retrieving. diff --git a/platform/docs/versioned_docs/version-3.11/platform/hooks/useViewportDisplaySets.md b/platform/docs/versioned_docs/version-3.11/platform/hooks/useViewportDisplaySets.md new file mode 100644 index 00000000000..7c3a99f5ea7 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/hooks/useViewportDisplaySets.md @@ -0,0 +1,98 @@ +--- +title: useViewportDisplaySets +summary: A React hook that provides access to display sets associated with a viewport, including background, foreground, overlay, and potential display sets. +--- + +# useViewportDisplaySets + +The `useViewportDisplaySets` hook provides access to display sets associated with a viewport, organized into categories based on their role and potential usage. + +## Overview + +This hook retrieves all display sets associated with a specific viewport and categorizes them into background, foreground, overlays, and potential display sets that could be added to the viewport in different roles. It allows components to efficiently access and manage viewport display sets for various UI interactions like layer menus and display set selectors. + +## Import + +```js +import { useViewportDisplaySets } from '@ohif/extension-cornerstone'; +``` + +## Usage + +```jsx +function ViewportLayerControls({ viewportId }) { + const { + backgroundDisplaySet, + foregroundDisplaySets, + overlayDisplaySets, + potentialOverlayDisplaySets, + potentialForegroundDisplaySets, + potentialBackgroundDisplaySets, + } = useViewportDisplaySets(viewportId); + + return ( +
+
+

Background

+
{backgroundDisplaySet?.SeriesDescription}
+
+ +
+

Foreground ({foregroundDisplaySets.length})

+ {foregroundDisplaySets.map(ds => ( +
{ds.SeriesDescription}
+ ))} +
+ +
+

Overlays ({overlayDisplaySets.length})

+ {overlayDisplaySets.map(ds => ( +
{ds.SeriesDescription}
+ ))} +
+ +
+

Available Overlays ({potentialOverlayDisplaySets.length})

+ {potentialOverlayDisplaySets.map(ds => ( +
{ds.SeriesDescription}
+ ))} +
+
+ ); +} +``` + +## Parameters + +- `viewportId` (optional): The ID of the viewport to get display sets for. If not provided, uses the active viewport. +- `options` (optional): Configuration options to control which display sets to include: + - `includeBackground`: Whether to include the background display set (default: true) + - `includeForeground`: Whether to include foreground display sets (default: true) + - `includeOverlay`: Whether to include overlay display sets (default: true) + - `includePotentialOverlay`: Whether to include potential overlay display sets (default: true) + - `includePotentialForeground`: Whether to include potential foreground display sets (default: true) + - `includePotentialBackground`: Whether to include potential background display sets (default: true) + +## Returns + +An object containing requested display set collections based on options: + +- `allDisplaySets`: All display sets in the viewer (only if requested) +- `viewportDisplaySets`: The display sets currently in the viewport +- `backgroundDisplaySet`: The primary display set for the viewport (base image) +- `foregroundDisplaySets`: Display sets currently shown with background (non-overlay layers) +- `overlayDisplaySets`: Segmentation display sets currently applied as overlays +- `potentialOverlayDisplaySets`: Display sets that could be toggled on as overlays (derived modalities) +- `potentialForegroundDisplaySets`: Display sets that could be added as foreground layers +- `potentialBackgroundDisplaySets`: Display sets that could replace the current background + +Each property is only included if the corresponding option is true. + +## Implementation Details + +- The hook automatically adapts to viewport changes through the `useViewportGrid` hook. +- It only fetches and processes display sets that are needed based on the provided options. +- Display sets are categorized based on their modality and properties. +- Potential display sets are sorted by priority to present the most relevant options first. +- Derived overlay modalities (like SEG, SR) are treated differently than other display sets. +- The hook uses memoization extensively to optimize performance and prevent unnecessary recalculations. diff --git a/platform/docs/versioned_docs/version-3.11/platform/hooks/useViewportHover.md b/platform/docs/versioned_docs/version-3.11/platform/hooks/useViewportHover.md new file mode 100644 index 00000000000..8677782842d --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/hooks/useViewportHover.md @@ -0,0 +1,57 @@ +--- +title: useViewportHover +summary: A React hook that tracks mouse hover state and active status for a specific viewport. +--- + +# useViewportHover + +The `useViewportHover` hook provides information about whether the mouse is currently hovering over a specific viewport and whether that viewport is active. + +## Overview + +This hook monitors mouse movement to track hover state over a viewport element by its ID. It also checks whether the viewport is currently active (selected) in the viewer. This is useful for implementing conditional UI elements or behaviors that depend on user interaction with viewports. + +## Import + +```js +import { useViewportHover } from '@ohif/extension-cornerstone'; +``` + +## Usage + +```jsx +function ViewportOverlay({ viewportId }) { + const { isHovered, isActive } = useViewportHover(viewportId); + + return ( +
+ {isHovered && !isActive && ( + + )} + {isActive && ( +
Active viewport controls
+ )} +
+ ); +} +``` + +## Parameters + +- `viewportId` (required): The ID of the viewport to track hover state for + +## Returns + +An object containing the following properties: + +- `isHovered`: Boolean indicating if the mouse is currently hovering over the viewport +- `isActive`: Boolean indicating if the viewport is currently the active viewport in the grid + +## Implementation Details + +- The hook uses the DOM to find the viewport element by its `data-viewportid` attribute. +- It calculates and maintains the viewport's bounding rectangle to efficiently determine if the mouse is within the viewport's bounds. +- The viewport element's rectangle is updated when the window is resized. +- Global mouse movement is tracked to determine hover state, rather than relying on traditional mouseenter/mouseleave events. +- The hook automatically cleans up event listeners when the component unmounts or the viewport ID changes. +- Active viewport state is derived from the viewport grid state using the `useViewportGrid` hook. diff --git a/platform/docs/versioned_docs/version-3.11/platform/hooks/useViewportSegmentations.md b/platform/docs/versioned_docs/version-3.11/platform/hooks/useViewportSegmentations.md new file mode 100644 index 00000000000..26d59e931a9 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/hooks/useViewportSegmentations.md @@ -0,0 +1,95 @@ +--- +title: useViewportSegmentations +summary: A React hook that provides segmentation data and representations for the active viewport with automatic updates when segmentations change. +--- + +# useViewportSegmentations + +The `useViewportSegmentations` hook provides access to segmentation data and their representations for a specific viewport, with automatic updates when segmentations are modified, removed, or representations change. + +## Overview + +This hook retrieves all segmentations and their representations for a given viewport from the segmentation service. It maps the segmentation data to a display-friendly format, including readable text for statistics. The hook monitors various segmentation and viewport events to update automatically when changes occur. + +## Import + +```js +import { useViewportSegmentations } from '@ohif/extension-cornerstone'; +``` + +## Usage + +```jsx +function SegmentationPanel({ viewportId }) { + const { segmentationsWithRepresentations, disabled } = useViewportSegmentations({ + viewportId, + subscribeToDataModified: true, + debounceTime: 100, + }); + + if (disabled) { + return
Segmentations not available for this modality
; + } + + if (!segmentationsWithRepresentations.length) { + return
No segmentations available
; + } + + return ( +
+ {segmentationsWithRepresentations.map(({ segmentation, representation }) => ( +
+

{segmentation.label}

+ {Object.entries(segmentation.segments).map(([segmentIndex, segment]) => ( +
+

{segment.label}

+ {segment.displayText.primary.map((text, i) => ( +

{text}

+ ))} +
+ ))} +
+ ))} +
+ ); +} +``` + +## Parameters + +- `options` - Configuration options: + - `viewportId` (required): The ID of the viewport to get segmentations for + - `subscribeToDataModified` (optional): Whether to subscribe to segmentation data modifications (default: false) + - `debounceTime` (optional): Debounce time in milliseconds for updates (default: 0) + +## Returns + +An object with the following properties: + +- `segmentationsWithRepresentations`: An array of objects with each containing: + - `representation`: The segmentation representation in the viewport + - `segmentation`: The mapped segmentation data with display-friendly properties: + - `label`: The segmentation label + - `segments`: Object mapping segment indices to segment data: + - `label`: Segment label + - `color`: Segment color + - `displayText`: Organized text for display with `primary` and `secondary` arrays + +- `disabled`: Boolean indicating if segmentations are disabled for the current modality + +## Events + +The hook automatically updates when any of these events occur: +- `SEGMENTATION_MODIFIED` +- `SEGMENTATION_REMOVED` +- `SEGMENTATION_REPRESENTATION_MODIFIED` +- `ACTIVE_VIEWPORT_ID_CHANGED` +- `GRID_STATE_CHANGED` +- `SEGMENTATION_DATA_MODIFIED` (only if `subscribeToDataModified` is true) + +## Implementation Details + +- The hook excludes certain modalities from segmentation display: 'SM', 'OT', 'DOC', 'ECG'. +- It uses debouncing to prevent excessive re-renders when multiple segmentation events occur in rapid succession. +- Segmentation statistics are automatically mapped to readable text using the customization service. +- Nested statistics are displayed with indentation to maintain hierarchy. diff --git a/platform/docs/versioned_docs/version-3.10/platform/internationalization.md b/platform/docs/versioned_docs/version-3.11/platform/internationalization.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/platform/internationalization.md rename to platform/docs/versioned_docs/version-3.11/platform/internationalization.md index 8caf9a729eb..c849f6fde0f 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/internationalization.md +++ b/platform/docs/versioned_docs/version-3.11/platform/internationalization.md @@ -1,6 +1,8 @@ --- sidebar_position: 4 sidebar_label: Internationalization +title: Internationalization +summary: Documentation of OHIF's internationalization (i18n) implementation, including how to change languages, extend existing languages, add new languages, and contribute translations, using the @ohif/i18n package powered by i18next. --- # Viewer: Internationalization diff --git a/platform/docs/versioned_docs/version-3.10/platform/managers/_category_.json b/platform/docs/versioned_docs/version-3.11/platform/managers/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/platform/managers/_category_.json rename to platform/docs/versioned_docs/version-3.11/platform/managers/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/platform/managers/commands.md b/platform/docs/versioned_docs/version-3.11/platform/managers/commands.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/platform/managers/commands.md rename to platform/docs/versioned_docs/version-3.11/platform/managers/commands.md index 8727f98932a..bb5957bd1e5 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/managers/commands.md +++ b/platform/docs/versioned_docs/version-3.11/platform/managers/commands.md @@ -1,6 +1,8 @@ --- sidebar_position: 4 sidebar_label: Commands Manager +title: Commands Manager +summary: Documentation for the CommandsManager class which tracks named functions scoped to specific contexts, allowing OHIF to execute commands with appropriate context prioritization across the application. --- # Commands Manager diff --git a/platform/docs/versioned_docs/version-3.10/platform/managers/extension.md b/platform/docs/versioned_docs/version-3.11/platform/managers/extension.md similarity index 90% rename from platform/docs/versioned_docs/version-3.10/platform/managers/extension.md rename to platform/docs/versioned_docs/version-3.11/platform/managers/extension.md index a31f2244219..69e5668501c 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/managers/extension.md +++ b/platform/docs/versioned_docs/version-3.11/platform/managers/extension.md @@ -1,6 +1,8 @@ --- sidebar_position: 2 sidebar_label: Extension Manager +title: Extension Manager +summary: Documentation for the ExtensionManager class which aggregates and exposes extension modules throughout the OHIF application, manages data sources, and provides a centralized registry for accessing extension functionality. --- # Extension Manager diff --git a/platform/docs/versioned_docs/version-3.10/platform/managers/hotkeys.md b/platform/docs/versioned_docs/version-3.11/platform/managers/hotkeys.md similarity index 89% rename from platform/docs/versioned_docs/version-3.10/platform/managers/hotkeys.md rename to platform/docs/versioned_docs/version-3.11/platform/managers/hotkeys.md index 75d4ead3060..81b54f2d87d 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/managers/hotkeys.md +++ b/platform/docs/versioned_docs/version-3.11/platform/managers/hotkeys.md @@ -1,6 +1,8 @@ --- sidebar_position: 5 sidebar_label: Hotkeys Manager +title: Hotkeys Manager +summary: Documentation for the HotkeysManager class which handles keyboard shortcut bindings to commands, allowing configuration of global and mode-specific keyboard shortcuts with user customization capabilities. --- # Hotkeys Managers diff --git a/platform/docs/versioned_docs/version-3.10/platform/managers/index.md b/platform/docs/versioned_docs/version-3.11/platform/managers/index.md similarity index 87% rename from platform/docs/versioned_docs/version-3.10/platform/managers/index.md rename to platform/docs/versioned_docs/version-3.11/platform/managers/index.md index ee6d6b2fc8c..7638a0bd8de 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/managers/index.md +++ b/platform/docs/versioned_docs/version-3.11/platform/managers/index.md @@ -1,6 +1,8 @@ --- sidebar_position: 1 sidebar_label: Introduction +title: Managers Introduction +summary: Overview of OHIF's manager system, which handles critical application infrastructure including extension registration, service management, command execution, and hotkey binding to coordinate functionality across the platform. --- # Managers diff --git a/platform/docs/versioned_docs/version-3.10/platform/managers/service.md b/platform/docs/versioned_docs/version-3.11/platform/managers/service.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/platform/managers/service.md rename to platform/docs/versioned_docs/version-3.11/platform/managers/service.md index d9269e46c0f..9e185f225cd 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/managers/service.md +++ b/platform/docs/versioned_docs/version-3.11/platform/managers/service.md @@ -1,6 +1,8 @@ --- sidebar_position: 3 sidebar_label: Service Manager +title: Service Manager +summary: Documentation for the ServicesManager class which provides central registration and access to application services, with details on built-in services, service architecture, lifecycle management, and implementation of custom services. --- # Services Manager diff --git a/platform/docs/versioned_docs/version-3.10/platform/modes/_category_.json b/platform/docs/versioned_docs/version-3.11/platform/modes/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/platform/modes/_category_.json rename to platform/docs/versioned_docs/version-3.11/platform/modes/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/platform/modes/index.md b/platform/docs/versioned_docs/version-3.11/platform/modes/index.md similarity index 98% rename from platform/docs/versioned_docs/version-3.10/platform/modes/index.md rename to platform/docs/versioned_docs/version-3.11/platform/modes/index.md index aded22bd071..7fdd3e39abb 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/modes/index.md +++ b/platform/docs/versioned_docs/version-3.11/platform/modes/index.md @@ -1,6 +1,8 @@ --- sidebar_position: 1 sidebar_label: Introduction +title: Modes Introduction +summary: Overview of OHIF Modes, which are specialized viewer configurations for specific clinical workflows, allowing multiple purpose-specific applications to coexist within a single OHIF deployment. --- # Modes diff --git a/platform/docs/versioned_docs/version-3.10/platform/modes/installation.md b/platform/docs/versioned_docs/version-3.11/platform/modes/installation.md similarity index 57% rename from platform/docs/versioned_docs/version-3.10/platform/modes/installation.md rename to platform/docs/versioned_docs/version-3.11/platform/modes/installation.md index 08c456d29e1..20af3d0b249 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/modes/installation.md +++ b/platform/docs/versioned_docs/version-3.11/platform/modes/installation.md @@ -1,6 +1,8 @@ --- sidebar_position: 5 sidebar_label: Installation +title: Mode Installation +summary: Documentation for installing OHIF Modes, including how to use the OHIF CLI to add both local and NPM-published modes to extend the viewer with custom clinical workflows. --- # Modes: Installation diff --git a/platform/docs/versioned_docs/version-3.10/platform/modes/lifecycle.md b/platform/docs/versioned_docs/version-3.11/platform/modes/lifecycle.md similarity index 92% rename from platform/docs/versioned_docs/version-3.10/platform/modes/lifecycle.md rename to platform/docs/versioned_docs/version-3.11/platform/modes/lifecycle.md index 8c0ec41cee6..c3bd0e70766 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/modes/lifecycle.md +++ b/platform/docs/versioned_docs/version-3.11/platform/modes/lifecycle.md @@ -1,6 +1,8 @@ --- sidebar_position: 2 sidebar_label: Lifecycle Hooks +title: Mode Lifecycle Hooks +summary: Documentation for OHIF Mode's lifecycle hooks (onModeInit, onModeEnter, and onModeExit), which allow customization of initialization, resource setup, and cleanup when entering or exiting viewer modes. --- # Modes: Lifecycle Hooks diff --git a/platform/docs/versioned_docs/version-3.10/platform/modes/routes.md b/platform/docs/versioned_docs/version-3.11/platform/modes/routes.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/platform/modes/routes.md rename to platform/docs/versioned_docs/version-3.11/platform/modes/routes.md index 5a3bdc6f311..1668142eea2 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/modes/routes.md +++ b/platform/docs/versioned_docs/version-3.11/platform/modes/routes.md @@ -1,6 +1,8 @@ --- sidebar_position: 3 sidebar_label: Routes +title: Mode Routes +summary: Documentation for OHIF Mode routes, which define URL paths, initialization logic, and layout templates for specific viewer workflows, allowing multiple modes to coexist within a single application. --- # Mode: Routes diff --git a/platform/docs/versioned_docs/version-3.10/platform/modes/validity.md b/platform/docs/versioned_docs/version-3.11/platform/modes/validity.md similarity index 76% rename from platform/docs/versioned_docs/version-3.10/platform/modes/validity.md rename to platform/docs/versioned_docs/version-3.11/platform/modes/validity.md index e3f70af7668..f95421b8005 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/modes/validity.md +++ b/platform/docs/versioned_docs/version-3.11/platform/modes/validity.md @@ -1,6 +1,8 @@ --- sidebar_position: 4 sidebar_label: Validity +title: Mode Validity +summary: Documentation for OHIF Mode validity checks, which determine when specific modes should be available for particular studies based on modalities, tags, or custom logic to ensure appropriate workflows for different types of imaging studies. --- # Mode: Validity diff --git a/platform/docs/versioned_docs/version-3.10/platform/pwa-vs-packaged.md b/platform/docs/versioned_docs/version-3.11/platform/pwa-vs-packaged.md similarity index 80% rename from platform/docs/versioned_docs/version-3.10/platform/pwa-vs-packaged.md rename to platform/docs/versioned_docs/version-3.11/platform/pwa-vs-packaged.md index bdc411e6cb1..0e6c4ebb091 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/pwa-vs-packaged.md +++ b/platform/docs/versioned_docs/version-3.11/platform/pwa-vs-packaged.md @@ -1,5 +1,7 @@ --- sidebar_position: 3 +title: PWA vs Packaged Application +summary: Explanation of OHIF's primary build process for Progressive Web Applications (PWAs), detailing how the application is built as a set of static assets with service worker support for enhanced performance across devices and network conditions. --- # PWA vs Packaged diff --git a/platform/docs/versioned_docs/version-3.10/platform/scope-of-project.md b/platform/docs/versioned_docs/version-3.11/platform/scope-of-project.md similarity index 90% rename from platform/docs/versioned_docs/version-3.10/platform/scope-of-project.md rename to platform/docs/versioned_docs/version-3.11/platform/scope-of-project.md index 95460e4bcb5..089803c4004 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/scope-of-project.md +++ b/platform/docs/versioned_docs/version-3.11/platform/scope-of-project.md @@ -1,5 +1,7 @@ --- sidebar_position: 1 +title: Scope of Project +summary: Overview of the OHIF Viewer's scope as a web-based medical imaging viewer that functions as a "dumb client," explaining its capabilities as a Progressive Web Application (PWA), the separation of data from the viewer, and clarification about offline capabilities. --- # Scope of Project diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/_category_.json b/platform/docs/versioned_docs/version-3.11/platform/services/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/platform/services/_category_.json rename to platform/docs/versioned_docs/version-3.11/platform/services/_category_.json diff --git a/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/Measurements.md b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/Measurements.md new file mode 100644 index 00000000000..2558e1f0ae9 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/Measurements.md @@ -0,0 +1,11 @@ +--- +title: Measurements Customization +summary: Documentation for customizing OHIF's measurement tools and displays, including how to modify display formats, annotation behaviors, and tracking interfaces for medical imaging measurements. +sidebar_position: 4 +--- + + + +import { measurementsCustomizations, TableGenerator } from './sampleCustomizations'; + +{TableGenerator(measurementsCustomizations)} diff --git a/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/Segmentation.md b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/Segmentation.md new file mode 100644 index 00000000000..c363ab365c7 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/Segmentation.md @@ -0,0 +1,11 @@ +--- +title: Segmentation Customization +summary: Documentation for customizing OHIF's segmentation tools and visualization options, including configuration for segment rendering, color schemes, and interaction behaviors for medical image segmentation workflows. +sidebar_position: 5 +--- + + + +import { segmentationCustomizations, TableGenerator } from './sampleCustomizations'; + +{TableGenerator(segmentationCustomizations)} diff --git a/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/StudyBrowser.md b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/StudyBrowser.md new file mode 100644 index 00000000000..30d65bbdc19 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/StudyBrowser.md @@ -0,0 +1,13 @@ +--- +title: Study Browser Customization +summary: Documentation for customizing OHIF's Study Browser component, including configuration options for thumbnails, sorting functions, and display formats to enhance study navigation and selection workflows. +sidebar_position: 6 +--- + +# Study Browser + +The Study Browser is a component that allows users to browse and manage studies. + +import { studyBrowserCustomizations, TableGenerator } from './sampleCustomizations'; + +{TableGenerator(studyBrowserCustomizations)} diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/_category_.json b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/platform/services/customization-service/_category_.json rename to platform/docs/versioned_docs/version-3.11/platform/services/customization-service/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/advanced.md b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/advanced.md similarity index 93% rename from platform/docs/versioned_docs/version-3.10/platform/services/customization-service/advanced.md rename to platform/docs/versioned_docs/version-3.11/platform/services/customization-service/advanced.md index 7efe64df912..6f14b76c476 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/advanced.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/advanced.md @@ -1,5 +1,7 @@ --- title: Advanced Customization +summary: Documentation for advanced OHIF customization techniques, including inheritance patterns, transform functions, and dynamic assembly of customizations to create sophisticated configurations across the platform. +sidebar_position: 8 --- diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/contextMenu.md b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/contextMenu.md similarity index 73% rename from platform/docs/versioned_docs/version-3.10/platform/services/customization-service/contextMenu.md rename to platform/docs/versioned_docs/version-3.11/platform/services/customization-service/contextMenu.md index 345531087c8..278bdb74996 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/contextMenu.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/contextMenu.md @@ -1,6 +1,8 @@ --- sidebar_label: Context Menu sidebar_position: 3 +title: Context Menu Customization +summary: Documentation for customizing OHIF's context menus, including defining menu structures and click interactions specific to different contexts like the Cornerstone viewport. --- # Context Menu diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/customRoutes.md b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/customRoutes.md similarity index 91% rename from platform/docs/versioned_docs/version-3.10/platform/services/customization-service/customRoutes.md rename to platform/docs/versioned_docs/version-3.11/platform/services/customization-service/customRoutes.md index cc52cae6cff..24134dc0d3b 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/customRoutes.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/customRoutes.md @@ -1,6 +1,8 @@ --- sidebar_label: Custom Routes sidebar_position: 2 +title: Custom Routes +summary: Documentation for creating custom routes in OHIF, allowing extensions to define new URL paths and React components to extend the application with additional pages and navigation options. --- # customRoutes diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/customizationService.md b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/customizationService.md similarity index 98% rename from platform/docs/versioned_docs/version-3.10/platform/services/customization-service/customizationService.md rename to platform/docs/versioned_docs/version-3.11/platform/services/customization-service/customizationService.md index afb57f2ff54..b8284785fcc 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/customizationService.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/customizationService.md @@ -1,6 +1,8 @@ --- sidebar_label: Introduction sidebar_position: 1 +title: Customization Service +summary: Documentation for OHIF's Customization Service, which provides a flexible framework for configuring and overriding viewer components across different scopes (default, mode, global), supporting dynamic customization of UI elements and behaviors. --- import { customizations, TableGenerator } from './sampleCustomizations'; diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/sampleCustomizations.tsx b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/sampleCustomizations.tsx similarity index 96% rename from platform/docs/versioned_docs/version-3.10/platform/services/customization-service/sampleCustomizations.tsx rename to platform/docs/versioned_docs/version-3.11/platform/services/customization-service/sampleCustomizations.tsx index 6ab5b84ee06..8ac060dcbce 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/sampleCustomizations.tsx +++ b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/sampleCustomizations.tsx @@ -23,7 +23,6 @@ import segDisplayEditingTrue from '../../../assets/img/segDisplayEditingTrue.png import segDisplayEditingFalse from '../../../assets/img/segDisplayEditingFalse.png'; import thumbnailMenuItemsImage from '../../../assets/img/thumbnailMenuItemsImage.png'; import studyMenuItemsImage from '../../../assets/img/studyMenuItemsImage.png'; -import windowLevelActionMenu from '../../../assets/img/windowLevelActionMenu.png'; import viewPortNotificationImage from '../../../assets/img/viewport-notification.png'; import captureViewportModal from '../../../assets/img/captureViewportModal.png'; import aboutModal from '../../../assets/img/aboutModal.png'; @@ -911,56 +910,6 @@ window.config = { }; `, }, - { - id: 'viewportActionMenu.windowLevelActionMenu', - description: - 'Configures the display and location of the window level action menu in the viewport.', - image: windowLevelActionMenu, - default: null, - configuration: ` - window.config = { - // rest of window config - customizationService: [ - { - 'viewportActionMenu.windowLevelActionMenu': { - $merge: { - location: 0, // Set the location of the menu in the viewport. - // 0: topLeft - // 1: topRight - // 2: bottomLeft - // 3: bottomRight - } - }, - }, - ], - }; - `, - }, - { - id: 'viewportActionMenu.segmentationOverlay', - description: 'Configures the display and location of the segmentation overlay in the viewport.', - image: segmentationOverlay, - default: null, - configuration: ` - window.config = { - // rest of window config - customizationService: [ - { - 'viewportActionMenu.segmentationOverlay': { - $merge: { - enabled: true, - location: 1, // Set the location of the overlay in the viewport. - // 0: topLeft - // 1: topRight - // 2: bottomLeft - // 3: bottomRight - } - }, - }, - ], - }; - `, - }, { id: 'viewportNotification.beginTrackingMessage', description: 'Define the content to be displayed in begin tracking prompt', @@ -1869,6 +1818,31 @@ window.config = { }; }, }], +}; + `, + }, + { + id: 'instanceSortingCriteria', + description: 'Defines the instance sorting criteria to sort the images', + default: `{ + sortFunctions: {}, + defaultSortFunctionName: '', + }`, + configuration: ` +window.config = { + // rest of window config + customizationService: [ + { + 'instanceSortingCriteria': { + $set: { + sortFunctions: { + sort: (a, b) => {} + }, + defaultSortFunctionName: 'sort', + }, + }, + } + ], }; `, }, diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/viewportOverlay.md b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/viewportOverlay.md similarity index 63% rename from platform/docs/versioned_docs/version-3.10/platform/services/customization-service/viewportOverlay.md rename to platform/docs/versioned_docs/version-3.11/platform/services/customization-service/viewportOverlay.md index 4804617b9a2..68af62c1c28 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/customization-service/viewportOverlay.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/customization-service/viewportOverlay.md @@ -1,5 +1,8 @@ --- +sidebar_label: Viewport Overlay sidebar_position: 1 +title: Viewport Overlay Customization +summary: Documentation for customizing OHIF's viewport overlays, allowing configuration of the information displayed in each corner (top-right, top-left, bottom-left, bottom-right) of medical imaging viewports. --- import { viewportOverlayCustomizations , TableGenerator } from './sampleCustomizations'; diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/DicomMetadataStore.md b/platform/docs/versioned_docs/version-3.11/platform/services/data/DicomMetadataStore.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/platform/services/data/DicomMetadataStore.md rename to platform/docs/versioned_docs/version-3.11/platform/services/data/DicomMetadataStore.md index 927b8b8e541..841be46f7b7 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/data/DicomMetadataStore.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/data/DicomMetadataStore.md @@ -1,6 +1,8 @@ --- sidebar_position: 2 sidebar_label: DICOM Metadata Store +title: DICOM Metadata Store +summary: Documentation for OHIF's DicomMetadataStore service, which provides centralized storage and retrieval functions for DICOM metadata with hierarchical organization of studies, series, and instances. --- # DICOM Metadata Store diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/DisplaySetService.md b/platform/docs/versioned_docs/version-3.11/platform/services/data/DisplaySetService.md similarity index 80% rename from platform/docs/versioned_docs/version-3.10/platform/services/data/DisplaySetService.md rename to platform/docs/versioned_docs/version-3.11/platform/services/data/DisplaySetService.md index 8b8bcae671d..3ed57273d0c 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/data/DisplaySetService.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/data/DisplaySetService.md @@ -1,10 +1,11 @@ --- sidebar_position: 3 sidebar_label: DisplaySet Service +title: DisplaySet Service +summary: Documentation for OHIF's DisplaySetService, which converts instance metadata into displayable sets for visualization, allowing dynamic creation, updating, and management of display sets from various data sources. --- # DisplaySet Service - ## Overview `DisplaySetService` handles converting the `instanceMetadata` into `DisplaySet` that `OHIF` uses for the visualization. `DisplaySetService` gets initialized at service startup time, but is then cleared in the `Mode.jsx`. During the initialization `SOPClassHandlerIds` of the `modes` gets registered with the `DisplaySetService`. @@ -12,7 +13,9 @@ sidebar_label: DisplaySet Service DisplaySet is a general set of entities and contains links to bunch of displayable objects (images, etc.) Some series might get split up into different displaySets e.g., MG might have mixed views in a single series, but users might want to have separate LCC, RCC, etc. for hanging protocol usage. A viewport renders a display set into a displayable object. -imageSet is a particular implementation of image displays. +An imageSet is a particular implementation of image displays. +- Learn more about Study (https://www.dicomstandard.org/standards/view/information-object-definitions#sect_A.1.2.2) +- Learn more about Series (https://www.dicomstandard.org/standards/view/information-object-definitions#sect_A.1.2.3) ::: @@ -27,6 +30,18 @@ Then, the same process is used as was originally done to create new display sets NOTE: Any instances not matched are NOT added to any display set and will not be displayed. +:::::info[Clarification of Terminology] + +Display Sets, which are custom to OHIF, are often confused with different DICOM terms, including study, series, and instances. The following are definitions for these terms to alleviate confusion. +

+ +DICOM Terms: +* **Study**: A collection of series +* **Series**: A collection of images or objects +* **Instance**: Single image or object +* **Display Set**: Set of displayable objects (Can be anything shown to the user) +::::: + ## Adding `madeInClient` display sets It is possible to filter or combine display sets from different series by performing the filter operation desired, and then calling the `addActiveDisplaySets` diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/HangingProtocolService.md b/platform/docs/versioned_docs/version-3.11/platform/services/data/HangingProtocolService.md similarity index 97% rename from platform/docs/versioned_docs/version-3.10/platform/services/data/HangingProtocolService.md rename to platform/docs/versioned_docs/version-3.11/platform/services/data/HangingProtocolService.md index de33e9bbd5d..d12920e66cd 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/data/HangingProtocolService.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/data/HangingProtocolService.md @@ -1,6 +1,8 @@ --- sidebar_position: 4 sidebar_label: Hanging Protocol Service +title: Hanging Protocol Service +summary: Documentation for OHIF's HangingProtocolService, which manages the automatic arrangement of medical images in viewports based on matching criteria, with support for custom attributes, synchronization, and display set selection. --- # Hanging Protocol Service diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/MeasurementService.md b/platform/docs/versioned_docs/version-3.11/platform/services/data/MeasurementService.md similarity index 94% rename from platform/docs/versioned_docs/version-3.10/platform/services/data/MeasurementService.md rename to platform/docs/versioned_docs/version-3.11/platform/services/data/MeasurementService.md index f5ede2f14f3..f13f09bf8a2 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/data/MeasurementService.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/data/MeasurementService.md @@ -1,6 +1,8 @@ --- sidebar_position: 6 sidebar_label: Measurement Service +title: Measurement Service +summary: Documentation for OHIF's MeasurementService, which handles annotation representation with extensible sources and mappers, supporting various measurement tools and allowing custom implementations beyond the built-in CornerstoneTools integration. --- # Measurement Service @@ -29,8 +31,7 @@ There are seven events that get publish in `MeasurementService`: | RAW_MEASUREMENT_ADDED | Fires when a raw measurement is added (e.g., dicom-sr) | | MEASUREMENT_REMOVED | Fires when a measurement is removed | | MEASUREMENTS_CLEARED | Fires when all measurements are deleted | -| JUMP_TO_MEASUREMENT_VIEWPORT | Fires when a measurement is requested to be jumped to, applying to individual viewports. | -| JUMP_TO_MEASUREMENT_LAYOUT | Fires when a measurement is requested to be jumped to, applying to the overall layout. | +| JUMP_TO_MEASUREMENT | Fires when a measurement is requested to be jumped to | ## API diff --git a/platform/docs/versioned_docs/version-3.11/platform/services/data/MultiMonitorService.md b/platform/docs/versioned_docs/version-3.11/platform/services/data/MultiMonitorService.md new file mode 100644 index 00000000000..97eb43686e9 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/services/data/MultiMonitorService.md @@ -0,0 +1,163 @@ +--- +sidebar_position: 5 +sidebar_label: Multi Monitor Service +title: Multi Monitor Service +summary: Documentation for OHIF's MultiMonitorService, which provides basic support for opening and managing multiple viewer windows, including split-screen options and cross-window communication capabilities. +--- + + +# Multi Monitor Service + +::: info + +We plan to enhance this service in the future. Currently, it offers a basic implementation of multi-monitor support, allowing you to manually open multiple windows on the same monitor. It is not yet a full multi-monitor solution! + +::: + + + + +The multi-monitor service provides detection, launch and communication support +for multiple monitors or windows/screens within a single monitor. + +:::info + +The multi-monitor service is currently applied via configuration file. + +```js +customizationService: ['@ohif/extension-default.customizationModule.multimonitor'], +``` + +::: + + + +## Configurations +The service supports two predefined configurations: + +1. **Split Screen (`multimonitor=split`)** + Splits the primary monitor into two windows. + +2. **Multi-Monitor (`multimonitor=2`)** + Opens windows across separate physical monitors. + +### Launch Methods +- Specify `&screenNumber=0` to designate the first window explicitly. +- Omit `screenNumber` to let the service handle window assignments dynamically. +- Use `launchAll` in the query parameters to launch all configured screens simultaneously. + +#### Example URLs: +- **Split Screen:** + `http://viewer.ohif.org/.....&multimonitor=split` + Splits the primary monitor into two windows when a study is viewed. + +- **Multi-Monitor with All Screens:** + `http://viewer.ohif.org/.....&multimonitor=2&screenNumber=0&launchAll` + Launches two monitors and opens all configured screens. + +--- + +## Configuration File Setting + +The `multimonitor` configuration setting in the +[configuration file](../../../configuration/configurationFiles.md) specifies various +properties for each of the OHIF windows opened when multimonitor is launched. The setting is +an array of properties for each [configuration](#configurations) defined. +Each entry in the array is an object with the following properties. + +- `id`: The id for the array item. Typically it corresponds to one of the +[configurations](#configurations) (e.g. `split` or `2`) +- `test`: A function that takes a single object argument containing the property +`multimontior` (i.e. the [configuration](#configurations)). The function should return `true` +if this array entry should be applied to the [configuration](#configurations). +- `screens`: An array of objects that define each of the OHIF screens to be opened + +Each `screen` array entry above has the following properties. + +- `id`: The unique screen identifier (e.g. `ohif0` or `radScreen0`) +- `screen`: The index of the physical screen. For the `split` configuration this can +be `null` because there is no specific physical screen for it. +- `location`: The normalized top, left position and size of the window +(i.e. `x`, `y`, `width`, `height`) relative to the physical screen for the window +- `options` Standard comma delimited string of popup +[window options](https://developer.mozilla.org/en-US/docs/Web/API/Window/open#windowfeatures). + +Below is a snippet from a configuration file for multimonitor. +``` + ... + multimonitor: [ + { + id: 'split', + test: ({ multimonitor }) => multimonitor === 'split', // applies to the split multimonitor configuration + screens: [ + { + id: 'ohif0', + screen: null, + location: { + width: 0.5, + height: 1, + left: 0, + top: 0, + }, + options: 'location=no,menubar=no,scrollbars=no,status=no,titlebar=no', + }, + { + id: 'ohif1', + screen: null, + location: { + width: 0.5, + height: 1, + left: 0.5, + top: 0, + }, + options: 'location=no,menubar=no,scrollbars=no,status=no,titlebar=no', + }, + ], + }, + + { + id: '2', + test: ({ multimonitor }) => multimonitor === '2', // applies to the `2` multimonitor configuration + screens: [ + { + id: 'ohif0', + screen: 0, + location: { + width: 1, + height: 1, + left: 0, + top: 0, + }, + options: 'fullscreen=yes,location=no,menubar=no,scrollbars=no,status=no,titlebar=no', + }, + { + id: 'ohif1', + screen: 1, + location: { + width: 1, + height: 1, + left: 0, + top: 0, + }, + options: 'fullscreen=yes,location=no,menubar=no,scrollbars=no,status=no,titlebar=no', + }, + ], + }, + ], + ... +``` + +## Behavior + +### Refresh, Close and Open +If you refresh the base/original window, then all the other windows will also +refresh. However, you can safely refresh any single other window, and on the next +command to the other windows, it will re-create the other window links without +losing content in the other windows. You can also close any other window and +it will be reopened the next time you try to call to it. + + +## Executing Commands +The MultiMonitorService adds the ability to run commands on other specified windows. +This allows opening up a study on another window without needing to refresh +it's contents. The command below shows an example of how this can be done: diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/PanelService.md b/platform/docs/versioned_docs/version-3.11/platform/services/data/PanelService.md similarity index 89% rename from platform/docs/versioned_docs/version-3.10/platform/services/data/PanelService.md rename to platform/docs/versioned_docs/version-3.11/platform/services/data/PanelService.md index b0a530b8509..d3bd7bca3fe 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/data/PanelService.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/data/PanelService.md @@ -1,6 +1,8 @@ --- sidebar_position: 8 sidebar_label: Panel Service +title: Panel Service +summary: Documentation for OHIF's PanelService, which manages the activation and display of side panels registered via extension modules, with support for explicit activation or event-triggered display of panels. --- # Panel Service diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/SegmentationService.md b/platform/docs/versioned_docs/version-3.11/platform/services/data/SegmentationService.md similarity index 92% rename from platform/docs/versioned_docs/version-3.10/platform/services/data/SegmentationService.md rename to platform/docs/versioned_docs/version-3.11/platform/services/data/SegmentationService.md index c2eeb6080dc..031594862f6 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/data/SegmentationService.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/data/SegmentationService.md @@ -1,6 +1,8 @@ --- sidebar_position: 7 sidebar_label: Segmentation Service +title: Segmentation Service +summary: Documentation for OHIF's SegmentationService, which provides tools for creating, managing, and interacting with image segmentations, including labelmap creation, segment operations, and visualization controls. --- # Segmentation Service diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/SyncGroupService.md b/platform/docs/versioned_docs/version-3.11/platform/services/data/SyncGroupService.md similarity index 94% rename from platform/docs/versioned_docs/version-3.10/platform/services/data/SyncGroupService.md rename to platform/docs/versioned_docs/version-3.11/platform/services/data/SyncGroupService.md index 835fd5621e8..13c7cb65f81 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/data/SyncGroupService.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/data/SyncGroupService.md @@ -1,6 +1,8 @@ --- sidebar_position: 8 sidebar_label: SyncGroup Service +title: SyncGroup Service +summary: Documentation for OHIF's SyncGroupService, which manages synchronization between multiple viewports based on various criteria like camera position, window level, and slice position, with both hanging protocol and manual configuration options. --- # Sync Group Service diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/ToolGroupService.md b/platform/docs/versioned_docs/version-3.11/platform/services/data/ToolGroupService.md similarity index 94% rename from platform/docs/versioned_docs/version-3.10/platform/services/data/ToolGroupService.md rename to platform/docs/versioned_docs/version-3.11/platform/services/data/ToolGroupService.md index 0d7a04cf08b..37854e92955 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/data/ToolGroupService.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/data/ToolGroupService.md @@ -1,6 +1,8 @@ --- sidebar_position: 7 sidebar_label: ToolGroup Service +title: ToolGroup Service +summary: Documentation for OHIF's ToolGroupService, which manages tool groups and their associated tools, allowing organization and control of measurement, annotation, and manipulation tools across different viewports. --- # Tool Group Service diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/ToolbarService.md b/platform/docs/versioned_docs/version-3.11/platform/services/data/ToolbarService.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/platform/services/data/ToolbarService.md rename to platform/docs/versioned_docs/version-3.11/platform/services/data/ToolbarService.md index 3c8aff77790..e952e730856 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/data/ToolbarService.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/data/ToolbarService.md @@ -1,6 +1,8 @@ --- sidebar_position: 5 sidebar_label: Toolbar Service +title: Toolbar Service +summary: Documentation for OHIF's ToolbarService, which manages the viewer's toolbar UI components, including button creation, section organization, and command execution, with support for nested dropdowns and state management. --- # Toolbar **Service** @@ -32,8 +34,7 @@ The `ToolBarService` is a straightforward service designed to handle the toolbar - `setButtons`: sets the buttons defined in the service. It overrides all the previous buttons - - +- `getOptionById(button, optionId)`: from inside a given button, will search through its props and grab the one with the given optionId ## Button Definitions diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/WorkflowStepService.md b/platform/docs/versioned_docs/version-3.11/platform/services/data/WorkflowStepService.md similarity index 95% rename from platform/docs/versioned_docs/version-3.10/platform/services/data/WorkflowStepService.md rename to platform/docs/versioned_docs/version-3.11/platform/services/data/WorkflowStepService.md index 7070cade4cc..68ae609ed5e 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/data/WorkflowStepService.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/data/WorkflowStepService.md @@ -1,6 +1,8 @@ --- sidebar_position: 9 sidebar_label: WorkflowStep Service +title: WorkflowStep Service +summary: Documentation for OHIF's WorkflowStepService, which enables structured multi-step clinical workflows with customizable hanging protocols, layouts, and toolbars for each step, providing guided navigation through complex imaging tasks. --- # Workflow Step Service diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/_category_.json b/platform/docs/versioned_docs/version-3.11/platform/services/data/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/platform/services/data/_category_.json rename to platform/docs/versioned_docs/version-3.11/platform/services/data/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/data/index.md b/platform/docs/versioned_docs/version-3.11/platform/services/data/index.md similarity index 88% rename from platform/docs/versioned_docs/version-3.10/platform/services/data/index.md rename to platform/docs/versioned_docs/version-3.11/platform/services/data/index.md index 3253ba9ee5b..6a30a6fbc4a 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/data/index.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/data/index.md @@ -1,6 +1,8 @@ --- sidebar_position: 1 sidebar_label: Overview +title: Data Services Overview +summary: Overview of OHIF's data services architecture, which replaced the Redux store with a more modular approach using service-specific state management and a pub/sub pattern for communication between components. --- # Overview diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/index.md b/platform/docs/versioned_docs/version-3.11/platform/services/index.md similarity index 94% rename from platform/docs/versioned_docs/version-3.10/platform/services/index.md rename to platform/docs/versioned_docs/version-3.11/platform/services/index.md index 1096ba6ff28..814bd446b33 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/index.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/index.md @@ -1,6 +1,8 @@ --- sidebar_position: 1 sidebar_label: Introduction +title: OHIF Services +summary: Overview of OHIF's services architecture, which provides modular, concern-specific code components that manage state and operations throughout the application, including both data services and UI services that follow a pub/sub pattern to reduce coupling. --- # Services diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/pubsub.md b/platform/docs/versioned_docs/version-3.11/platform/services/pubsub.md similarity index 91% rename from platform/docs/versioned_docs/version-3.10/platform/services/pubsub.md rename to platform/docs/versioned_docs/version-3.11/platform/services/pubsub.md index 036e592efbd..b1143b6550f 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/pubsub.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/pubsub.md @@ -1,6 +1,8 @@ --- sidebar_position: 4 sidebar_label: Pub Sub +title: Publish-Subscribe Pattern +summary: Documentation of the publish-subscribe pattern implementation in OHIF, which enables event-based communication between services, allowing components to subscribe to events and receive updates when they occur, reducing direct dependencies between modules. --- # Pub sub diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/ui/_category_.json b/platform/docs/versioned_docs/version-3.11/platform/services/ui/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/platform/services/ui/_category_.json rename to platform/docs/versioned_docs/version-3.11/platform/services/ui/_category_.json diff --git a/platform/docs/versioned_docs/version-3.11/platform/services/ui/cine-service.md b/platform/docs/versioned_docs/version-3.11/platform/services/ui/cine-service.md new file mode 100644 index 00000000000..2908c02a779 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/platform/services/ui/cine-service.md @@ -0,0 +1,10 @@ +--- +sidebar_position: 7 +sidebar_label: CINE Service +title: CINE Service +summary: Documentation for OHIF's CINE Service, which provides animation capabilities for scrolling through image stacks at configurable frame rates, enabling dynamic review of multi-frame image series. +--- + +# CINE Service + +TODO... diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/ui/index.md b/platform/docs/versioned_docs/version-3.11/platform/services/ui/index.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/platform/services/ui/index.md rename to platform/docs/versioned_docs/version-3.11/platform/services/ui/index.md index 2a0203a743f..ad872203376 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/ui/index.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/ui/index.md @@ -1,6 +1,8 @@ --- sidebar_position: 1 sidebar_label: Overview +title: UI Services Overview +summary: Overview of OHIF's UI services architecture, which enables consistent UI interactions across the application through React context providers and service implementations, allowing extensions to leverage components like modals, notifications, and dialogs. --- # Overview diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/ui/ui-dialog-service.md b/platform/docs/versioned_docs/version-3.11/platform/services/ui/ui-dialog-service.md similarity index 86% rename from platform/docs/versioned_docs/version-3.10/platform/services/ui/ui-dialog-service.md rename to platform/docs/versioned_docs/version-3.11/platform/services/ui/ui-dialog-service.md index 152841ae625..09d9cc54276 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/ui/ui-dialog-service.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/ui/ui-dialog-service.md @@ -1,6 +1,8 @@ --- sidebar_position: 4 sidebar_label: UI Dialog Service +title: UI Dialog Service +summary: Documentation for OHIF's UI Dialog Service, which provides streamlined dialog windows for user attention, input collection, and information display, with methods for creating and dismissing multiple dialogs throughout the application. --- # UI Dialog Service diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/ui/ui-modal-service.md b/platform/docs/versioned_docs/version-3.11/platform/services/ui/ui-modal-service.md similarity index 90% rename from platform/docs/versioned_docs/version-3.10/platform/services/ui/ui-modal-service.md rename to platform/docs/versioned_docs/version-3.11/platform/services/ui/ui-modal-service.md index 5fe5a643db6..02512d5a68e 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/ui/ui-modal-service.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/ui/ui-modal-service.md @@ -1,6 +1,8 @@ --- sidebar_position: 3 sidebar_label: UI Modal Service +title: UI Modal Service +summary: Documentation for OHIF's UI Modal Service, which enables displaying centered, non-draggable modal windows for focused user interaction, with support for custom components and content throughout the application. --- # UI Modal Service diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/ui/ui-notification-service.md b/platform/docs/versioned_docs/version-3.11/platform/services/ui/ui-notification-service.md similarity index 90% rename from platform/docs/versioned_docs/version-3.10/platform/services/ui/ui-notification-service.md rename to platform/docs/versioned_docs/version-3.11/platform/services/ui/ui-notification-service.md index 815ac6d8d00..5f700b58f9f 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/ui/ui-notification-service.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/ui/ui-notification-service.md @@ -1,6 +1,8 @@ --- sidebar_position: 2 sidebar_label: UI Notification Service +title: UI Notification Service +summary: Documentation for OHIF's UI Notification Service, which provides a standardized way to display non-intrusive, contextual notifications for timely and relevant information to users throughout the application. --- # UI Notification Service diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/ui/ui-viewport-dialog-service.md b/platform/docs/versioned_docs/version-3.11/platform/services/ui/ui-viewport-dialog-service.md similarity index 89% rename from platform/docs/versioned_docs/version-3.10/platform/services/ui/ui-viewport-dialog-service.md rename to platform/docs/versioned_docs/version-3.11/platform/services/ui/ui-viewport-dialog-service.md index b50e3e7e477..0d95b0c1409 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/ui/ui-viewport-dialog-service.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/ui/ui-viewport-dialog-service.md @@ -1,6 +1,8 @@ --- sidebar_position: 5 sidebar_label: UI Viewport Dialog Service +title: UI Viewport Dialog Service +summary: Documentation for OHIF's UI Viewport Dialog Service, which creates modal dialogs inside specific viewports for contextual user interactions, supporting different types of messages and action buttons for user input. --- # UI Viewport Dialog Service diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/ui/viewport-action-menu.md b/platform/docs/versioned_docs/version-3.11/platform/services/ui/viewport-action-menu.md similarity index 83% rename from platform/docs/versioned_docs/version-3.10/platform/services/ui/viewport-action-menu.md rename to platform/docs/versioned_docs/version-3.11/platform/services/ui/viewport-action-menu.md index 5a05925354b..84b87f5c4bd 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/ui/viewport-action-menu.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/ui/viewport-action-menu.md @@ -1,6 +1,8 @@ --- sidebar_position: 8 sidebar_label: Viewport Action Corners +title: Viewport Action Corners Service +summary: Documentation for OHIF's Viewport Action Corners Service, which manages interactive UI components placed in viewport corners, enabling flexible positioning, priority ordering, and dynamic updates of components for viewport-specific control. --- # Viewport Action Corners Service diff --git a/platform/docs/versioned_docs/version-3.10/platform/services/ui/viewport-grid-service.md b/platform/docs/versioned_docs/version-3.11/platform/services/ui/viewport-grid-service.md similarity index 91% rename from platform/docs/versioned_docs/version-3.10/platform/services/ui/viewport-grid-service.md rename to platform/docs/versioned_docs/version-3.11/platform/services/ui/viewport-grid-service.md index 186afc4197c..e197cb50451 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/services/ui/viewport-grid-service.md +++ b/platform/docs/versioned_docs/version-3.11/platform/services/ui/viewport-grid-service.md @@ -1,6 +1,8 @@ --- sidebar_position: 6 sidebar_label: Viewport Grid Service +title: Viewport Grid Service +summary: Documentation for OHIF's Viewport Grid Service, which manages the layout and configuration of the viewer's viewport grid, handling active viewport selection, display set assignment, and layout changes with event broadcasting capabilities. --- # Viewport Grid Service diff --git a/platform/docs/versioned_docs/version-3.10/platform/themeing.md b/platform/docs/versioned_docs/version-3.11/platform/themeing.md similarity index 92% rename from platform/docs/versioned_docs/version-3.10/platform/themeing.md rename to platform/docs/versioned_docs/version-3.11/platform/themeing.md index 48a881c20a7..b74fe20b34a 100644 --- a/platform/docs/versioned_docs/version-3.10/platform/themeing.md +++ b/platform/docs/versioned_docs/version-3.11/platform/themeing.md @@ -1,6 +1,8 @@ --- sidebar_position: 2 sidebar_label: Theming +title: Theming the OHIF Viewer +summary: Documentation on customizing the visual appearance of the OHIF Viewer using Tailwind CSS and white-labeling capabilities, including how to modify the color scheme, add custom layouts via the LayoutTemplateModule, and replace the application logo with custom branding. --- # Viewer: Theming diff --git a/platform/docs/versioned_docs/version-3.10/release-notes.md b/platform/docs/versioned_docs/version-3.11/release-notes.md similarity index 51% rename from platform/docs/versioned_docs/version-3.10/release-notes.md rename to platform/docs/versioned_docs/version-3.11/release-notes.md index 48491be16db..6898e651e35 100644 --- a/platform/docs/versioned_docs/version-3.10/release-notes.md +++ b/platform/docs/versioned_docs/version-3.11/release-notes.md @@ -1,6 +1,8 @@ --- sidebar_position: 2 sidebar_label: Release Notes +title: OHIF Viewer Release Notes +summary: Information about OHIF Viewer version releases, including new features, improvements, and bug fixes, which can be found in detail on the official OHIF website. --- # Release Notes diff --git a/platform/docs/versioned_docs/version-3.10/resources.md b/platform/docs/versioned_docs/version-3.11/resources.md similarity index 96% rename from platform/docs/versioned_docs/version-3.10/resources.md rename to platform/docs/versioned_docs/version-3.11/resources.md index bbdd4a18f80..edcedf76001 100644 --- a/platform/docs/versioned_docs/version-3.10/resources.md +++ b/platform/docs/versioned_docs/version-3.11/resources.md @@ -1,6 +1,8 @@ --- sidebar_position: 13 sidebar_label: Resources +title: OHIF Viewer Educational Resources +summary: A comprehensive collection of presentations, slides, posters, and videos from conferences, hackathons, and community meetings where the OHIF team has shared knowledge about the viewer's development, features, and capabilities, organized chronologically from 2016 to present. --- # Resources diff --git a/platform/docs/versioned_docs/version-3.11/test-coverage.md b/platform/docs/versioned_docs/version-3.11/test-coverage.md new file mode 100644 index 00000000000..169fed8ca80 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/test-coverage.md @@ -0,0 +1,17 @@ +--- +sidebar_position: 14 +sidebar_label: Test Coverage +title: OHIF Viewer Test Coverage +summary: Information about the automated testing infrastructure for the OHIF Viewer, with a focus on Playwright tests and access to the latest test coverage reports, which help ensure reliability and quality of the codebase. +--- + +# Test Coverage + +## Playwright + + +Here's the test coverage report for our Playwright tests. Keep in mind that this doesn't include our Cypress tests, so our actual test coverage is likely higher than what's shown. We're focusing on Playwright for future OHIF testing, and we're really pushing to improve that coverage number. + + +You can view our latest test coverage report here: +- [OHIF Playwright Test Coverage Report](https://docs.ohif.org/coverage) diff --git a/platform/docs/versioned_docs/version-3.10/user-guide/_category_.json b/platform/docs/versioned_docs/version-3.11/user-guide/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/user-guide/_category_.json rename to platform/docs/versioned_docs/version-3.11/user-guide/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/user-guide/index.md b/platform/docs/versioned_docs/version-3.11/user-guide/index.md similarity index 100% rename from platform/docs/versioned_docs/version-3.10/user-guide/index.md rename to platform/docs/versioned_docs/version-3.11/user-guide/index.md diff --git a/platform/docs/versioned_docs/version-3.10/user-guide/viewer/Language.md b/platform/docs/versioned_docs/version-3.11/user-guide/viewer/Language.md similarity index 100% rename from platform/docs/versioned_docs/version-3.10/user-guide/viewer/Language.md rename to platform/docs/versioned_docs/version-3.11/user-guide/viewer/Language.md diff --git a/platform/docs/versioned_docs/version-3.10/user-guide/viewer/_category_.json b/platform/docs/versioned_docs/version-3.11/user-guide/viewer/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.10/user-guide/viewer/_category_.json rename to platform/docs/versioned_docs/version-3.11/user-guide/viewer/_category_.json diff --git a/platform/docs/versioned_docs/version-3.10/user-guide/viewer/hotkeys.md b/platform/docs/versioned_docs/version-3.11/user-guide/viewer/hotkeys.md similarity index 100% rename from platform/docs/versioned_docs/version-3.10/user-guide/viewer/hotkeys.md rename to platform/docs/versioned_docs/version-3.11/user-guide/viewer/hotkeys.md diff --git a/platform/docs/versioned_docs/version-3.10/user-guide/viewer/index.md b/platform/docs/versioned_docs/version-3.11/user-guide/viewer/index.md similarity index 100% rename from platform/docs/versioned_docs/version-3.10/user-guide/viewer/index.md rename to platform/docs/versioned_docs/version-3.11/user-guide/viewer/index.md diff --git a/platform/docs/versioned_docs/version-3.10/user-guide/viewer/measurement-panel.md b/platform/docs/versioned_docs/version-3.11/user-guide/viewer/measurement-panel.md similarity index 100% rename from platform/docs/versioned_docs/version-3.10/user-guide/viewer/measurement-panel.md rename to platform/docs/versioned_docs/version-3.11/user-guide/viewer/measurement-panel.md diff --git a/platform/docs/versioned_docs/version-3.10/user-guide/viewer/measurement-tracking.md b/platform/docs/versioned_docs/version-3.11/user-guide/viewer/measurement-tracking.md similarity index 100% rename from platform/docs/versioned_docs/version-3.10/user-guide/viewer/measurement-tracking.md rename to platform/docs/versioned_docs/version-3.11/user-guide/viewer/measurement-tracking.md diff --git a/platform/docs/versioned_docs/version-3.11/user-guide/viewer/mpr.md b/platform/docs/versioned_docs/version-3.11/user-guide/viewer/mpr.md new file mode 100644 index 00000000000..fc7892a9814 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.11/user-guide/viewer/mpr.md @@ -0,0 +1,18 @@ +# Multiplanar Reconstruction + + +## Crosshairs + + +
+ +
+ + + +# MPR Slab Oblique + + +
+ +
diff --git a/platform/docs/versioned_docs/version-3.10/user-guide/viewer/study-panel.md b/platform/docs/versioned_docs/version-3.11/user-guide/viewer/study-panel.md similarity index 100% rename from platform/docs/versioned_docs/version-3.10/user-guide/viewer/study-panel.md rename to platform/docs/versioned_docs/version-3.11/user-guide/viewer/study-panel.md diff --git a/platform/docs/versioned_docs/version-3.10/user-guide/viewer/toolbar.md b/platform/docs/versioned_docs/version-3.11/user-guide/viewer/toolbar.md similarity index 100% rename from platform/docs/versioned_docs/version-3.10/user-guide/viewer/toolbar.md rename to platform/docs/versioned_docs/version-3.11/user-guide/viewer/toolbar.md diff --git a/platform/docs/versioned_docs/version-3.10/user-guide/viewer/viewport.md b/platform/docs/versioned_docs/version-3.11/user-guide/viewer/viewport.md similarity index 100% rename from platform/docs/versioned_docs/version-3.10/user-guide/viewer/viewport.md rename to platform/docs/versioned_docs/version-3.11/user-guide/viewer/viewport.md diff --git a/platform/docs/versioned_sidebars/version-3.10-sidebars.json b/platform/docs/versioned_sidebars/version-3.11-sidebars.json similarity index 100% rename from platform/docs/versioned_sidebars/version-3.10-sidebars.json rename to platform/docs/versioned_sidebars/version-3.11-sidebars.json diff --git a/platform/docs/versions.json b/platform/docs/versions.json index 75608e3214c..a8dc0cebf5f 100644 --- a/platform/docs/versions.json +++ b/platform/docs/versions.json @@ -1 +1 @@ -["3.10"] +["3.11"] diff --git a/platform/docs/yarn.lock b/platform/docs/yarn.lock index 52a54b6484c..d291f0b6caf 100644 --- a/platform/docs/yarn.lock +++ b/platform/docs/yarn.lock @@ -266,11 +266,6 @@ dependencies: "@algolia/client-common" "5.20.0" -"@alloc/quick-lru@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" - integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== - "@ampproject/remapping@^2.2.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" @@ -302,7 +297,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.5.tgz#df93ac37f4417854130e21d72c66ff3d4b897fc7" integrity sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg== -"@babel/core@^7.12.3", "@babel/core@^7.21.3", "@babel/core@^7.24.4", "@babel/core@^7.25.9": +"@babel/core@^7.21.3", "@babel/core@^7.24.4", "@babel/core@^7.25.9": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== @@ -878,7 +873,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-react-constant-elements@^7.12.1", "@babel/plugin-transform-react-constant-elements@^7.21.3": +"@babel/plugin-transform-react-constant-elements@^7.21.3": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.9.tgz#08a1de35a301929b60fdf2788a54b46cd8ecd0ef" integrity sha512-Ncw2JFsJVuvfRsa2lSHiC55kETQVLSnsYGQ1JDDwkUeWGTL/8Tom8aLTnlqgoeuopWrbbGndrc9AlLYrIosrow== @@ -1031,7 +1026,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.25.9" "@babel/helper-plugin-utils" "^7.25.9" -"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.20.2", "@babel/preset-env@^7.25.9": +"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.20.2", "@babel/preset-env@^7.25.9": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.26.0.tgz#30e5c6bc1bcc54865bff0c5a30f6d4ccdc7fa8b1" integrity sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw== @@ -1115,7 +1110,7 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.18.6", "@babel/preset-react@^7.25.9": +"@babel/preset-react@^7.18.6", "@babel/preset-react@^7.25.9": version "7.26.3" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.26.3.tgz#7c5e028d623b4683c1f83a0bd4713b9100560caa" integrity sha512-Nl03d6T9ky516DGK2YMxrTqvnpUW63TnJMOMonj+Zae0JiPC5BC9xPMSL6L8fiSpA5vP88qfygavVQvnLp+6Cw== @@ -1184,7 +1179,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.12.6", "@babel/types@^7.21.3", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.5", "@babel/types@^7.4.4": +"@babel/types@^7.21.3", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.5", "@babel/types@^7.4.4": version "7.26.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.5.tgz#7a1e1c01d28e26d1fe7f8ec9567b3b92b9d07747" integrity sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg== @@ -2519,519 +2514,532 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73" integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw== -"@radix-ui/number@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.1.0.tgz#1e95610461a09cdf8bb05c152e76ca1278d5da46" - integrity sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ== - -"@radix-ui/primitive@1.1.1": +"@radix-ui/number@1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.1.tgz#fc169732d755c7fbad33ba8d0cd7fd10c90dc8e3" - integrity sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA== + resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.1.1.tgz#7b2c9225fbf1b126539551f5985769d0048d9090" + integrity sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g== -"@radix-ui/react-accordion@^1.2.0": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.2.2.tgz#96ac3de896189553219e342d5e773589eb119dce" - integrity sha512-b1oh54x4DMCdGsB4/7ahiSrViXxaBwRPotiZNnYXjLha9vfuURSAZErki6qjDoSIV0eXx5v57XnTGVtGwnfp2g== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-collapsible" "1.1.2" - "@radix-ui/react-collection" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-direction" "1.1.0" - "@radix-ui/react-id" "1.1.0" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-controllable-state" "1.1.0" - -"@radix-ui/react-arrow@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz#2103721933a8bfc6e53bbfbdc1aaad5fc8ba0dd7" - integrity sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w== - dependencies: - "@radix-ui/react-primitive" "2.0.1" - -"@radix-ui/react-arrow@1.1.2": +"@radix-ui/primitive@1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz#30c0d574d7bb10eed55cd7007b92d38b03c6b2ab" - integrity sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg== - dependencies: - "@radix-ui/react-primitive" "2.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.2.tgz#83f415c4425f21e3d27914c12b3272a32e3dae65" + integrity sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA== -"@radix-ui/react-checkbox@^1.1.1": +"@radix-ui/primitive@1.1.3": version "1.1.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.1.3.tgz#0e2ab913fddf3c88603625f7a9457d73882c8a32" - integrity sha512-HD7/ocp8f1B3e6OHygH0n7ZKjONkhciy1Nh0yuBgObqThc3oyx+vuMfFHKAknXRHHWVE9XvXStxJFyjUmB8PIw== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-presence" "1.1.2" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-controllable-state" "1.1.0" - "@radix-ui/react-use-previous" "1.1.0" - "@radix-ui/react-use-size" "1.1.0" - -"@radix-ui/react-collapsible@1.1.2": + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.3.tgz#e2dbc13bdc5e4168f4334f75832d7bdd3e2de5ba" + integrity sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg== + +"@radix-ui/react-accordion@1.2.11": + version "1.2.11" + resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.2.11.tgz#7837dd4d44aeed56aabad2b098727b8b4f89ae4c" + integrity sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-collapsible" "1.1.11" + "@radix-ui/react-collection" "1.1.7" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-direction" "1.1.1" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-controllable-state" "1.2.2" + +"@radix-ui/react-arrow@1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz#e14a2657c81d961598c5e72b73dd6098acc04f09" + integrity sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w== + dependencies: + "@radix-ui/react-primitive" "2.1.3" + +"@radix-ui/react-checkbox@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.3.2.tgz#28097244d968aa8f93249b0d3df02a172fd4bee5" + integrity sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-controllable-state" "1.2.2" + "@radix-ui/react-use-previous" "1.1.1" + "@radix-ui/react-use-size" "1.1.1" + +"@radix-ui/react-collapsible@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.11.tgz#a2d132d5baa6f14551f15b1fff29f925cae46b83" + integrity sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-controllable-state" "1.2.2" + "@radix-ui/react-use-layout-effect" "1.1.1" + +"@radix-ui/react-collection@1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.7.tgz#d05c25ca9ac4695cc19ba91f42f686e3ea2d9aec" + integrity sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw== + dependencies: + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-slot" "1.2.3" + +"@radix-ui/react-compose-refs@1.1.2", "@radix-ui/react-compose-refs@^1.1.1": version "1.1.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.2.tgz#42477c428bb0d2eec35b9b47601c5ff0a6210165" - integrity sha512-PliMB63vxz7vggcyq0IxNYk8vGDrLXVWw4+W4B8YnwI1s18x7YZYqlG9PLX7XxAJUi0g2DxP4XKJMFHh/iVh9A== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-id" "1.1.0" - "@radix-ui/react-presence" "1.1.2" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-controllable-state" "1.1.0" - "@radix-ui/react-use-layout-effect" "1.1.0" - -"@radix-ui/react-collection@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.1.tgz#be2c7e01d3508e6d4b6d838f492e7d182f17d3b0" - integrity sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA== - dependencies: - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-slot" "1.1.1" - -"@radix-ui/react-compose-refs@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz#6f766faa975f8738269ebb8a23bad4f5a8d2faec" - integrity sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw== + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz#a2c4c47af6337048ee78ff6dc0d090b390d2bb30" + integrity sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg== -"@radix-ui/react-context@1.1.1": +"@radix-ui/react-context@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.2.tgz#61628ef269a433382c364f6f1e3788a6dc213a36" + integrity sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA== + +"@radix-ui/react-dialog@1.1.14": + version "1.1.14" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz#4c69c80c258bc6561398cfce055202ea11075107" + integrity sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-dismissable-layer" "1.1.10" + "@radix-ui/react-focus-guards" "1.1.2" + "@radix-ui/react-focus-scope" "1.1.7" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-portal" "1.1.9" + "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-slot" "1.2.3" + "@radix-ui/react-use-controllable-state" "1.2.2" + aria-hidden "^1.2.4" + react-remove-scroll "^2.6.3" + +"@radix-ui/react-dialog@^1.1.6": + version "1.1.15" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz#1de3d7a7e9a17a9874d29c07f5940a18a119b632" + integrity sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw== + dependencies: + "@radix-ui/primitive" "1.1.3" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-dismissable-layer" "1.1.11" + "@radix-ui/react-focus-guards" "1.1.3" + "@radix-ui/react-focus-scope" "1.1.7" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-portal" "1.1.9" + "@radix-ui/react-presence" "1.1.5" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-slot" "1.2.3" + "@radix-ui/react-use-controllable-state" "1.2.2" + aria-hidden "^1.2.4" + react-remove-scroll "^2.6.3" + +"@radix-ui/react-direction@1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.1.tgz#82074aa83a472353bb22e86f11bcbd1c61c4c71a" - integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q== + resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.1.tgz#39e5a5769e676c753204b792fbe6cf508e550a14" + integrity sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw== -"@radix-ui/react-dialog@^1.1.1", "@radix-ui/react-dialog@^1.1.2": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.4.tgz#d68e977acfcc0d044b9dab47b6dd2c179d2b3191" - integrity sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-dismissable-layer" "1.1.3" - "@radix-ui/react-focus-guards" "1.1.1" - "@radix-ui/react-focus-scope" "1.1.1" - "@radix-ui/react-id" "1.1.0" - "@radix-ui/react-portal" "1.1.3" - "@radix-ui/react-presence" "1.1.2" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-slot" "1.1.1" - "@radix-ui/react-use-controllable-state" "1.1.0" - aria-hidden "^1.1.1" - react-remove-scroll "^2.6.1" - -"@radix-ui/react-direction@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc" - integrity sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg== - -"@radix-ui/react-dismissable-layer@1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz#4ee0f0f82d53bf5bd9db21665799bb0d1bad5ed8" - integrity sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-callback-ref" "1.1.0" - "@radix-ui/react-use-escape-keydown" "1.1.0" - -"@radix-ui/react-dismissable-layer@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz#96dde2be078c694a621e55e047406c58cd5fe774" - integrity sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg== +"@radix-ui/react-dismissable-layer@1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz#429b9bada3672c6895a5d6a642aca6ecaf4f18c3" + integrity sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ== dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-primitive" "2.0.2" - "@radix-ui/react-use-callback-ref" "1.1.0" - "@radix-ui/react-use-escape-keydown" "1.1.0" + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-escape-keydown" "1.1.1" -"@radix-ui/react-dropdown-menu@^2.1.1": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.4.tgz#5e1f453296dd9ae99224a26c36851832d26cf507" - integrity sha512-iXU1Ab5ecM+yEepGAWK8ZhMyKX4ubFdCNtol4sT9D0OVErG9PNElfx3TQhjw7n7BC5nFVz68/5//clWy+8TXzA== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-id" "1.1.0" - "@radix-ui/react-menu" "2.1.4" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-controllable-state" "1.1.0" - -"@radix-ui/react-focus-guards@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz#8635edd346304f8b42cae86b05912b61aef27afe" - integrity sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg== - -"@radix-ui/react-focus-scope@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz#5c602115d1db1c4fcfa0fae4c3b09bb8919853cb" - integrity sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA== - dependencies: - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-callback-ref" "1.1.0" +"@radix-ui/react-dismissable-layer@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz#e33ab6f6bdaa00f8f7327c408d9f631376b88b37" + integrity sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg== + dependencies: + "@radix-ui/primitive" "1.1.3" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-escape-keydown" "1.1.1" + +"@radix-ui/react-dropdown-menu@2.1.15": + version "2.1.15" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz#f507320de8e11bc1e671a6ec0c27a7a89e725131" + integrity sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-menu" "2.1.15" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-controllable-state" "1.2.2" + +"@radix-ui/react-focus-guards@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz#4ec9a7e50925f7fb661394460045b46212a33bed" + integrity sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA== -"@radix-ui/react-hover-card@^1.1.6": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@radix-ui/react-hover-card/-/react-hover-card-1.1.6.tgz#94fb87c047e1bb3bfd70439cf7ee48165ea4efa5" - integrity sha512-E4ozl35jq0VRlrdc4dhHrNSV0JqBb4Jy73WAhBEK7JoYnQ83ED5r0Rb/XdVKw89ReAJN38N492BAPBZQ57VmqQ== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-dismissable-layer" "1.1.5" - "@radix-ui/react-popper" "1.2.2" - "@radix-ui/react-portal" "1.1.4" - "@radix-ui/react-presence" "1.1.2" - "@radix-ui/react-primitive" "2.0.2" - "@radix-ui/react-use-controllable-state" "1.1.0" - -"@radix-ui/react-icons@^1.3.0": +"@radix-ui/react-focus-guards@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz#2a5669e464ad5fde9f86d22f7fdc17781a4dfa7f" + integrity sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw== + +"@radix-ui/react-focus-scope@1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz#dfe76fc103537d80bf42723a183773fd07bfb58d" + integrity sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw== + dependencies: + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-callback-ref" "1.1.1" + +"@radix-ui/react-hover-card@1.1.14": + version "1.1.14" + resolved "https://registry.yarnpkg.com/@radix-ui/react-hover-card/-/react-hover-card-1.1.14.tgz#a557cda6470e214e744e46ede839496e8b291843" + integrity sha512-CPYZ24Mhirm+g6D8jArmLzjYu4Eyg3TTUHswR26QgzXBHBe64BO/RHOJKzmF/Dxb4y4f9PKyJdwm/O/AhNkb+Q== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-dismissable-layer" "1.1.10" + "@radix-ui/react-popper" "1.2.7" + "@radix-ui/react-portal" "1.1.9" + "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-controllable-state" "1.2.2" + +"@radix-ui/react-icons@1.3.2": version "1.3.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.3.2.tgz#09be63d178262181aeca5fb7f7bc944b10a7f441" integrity sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g== -"@radix-ui/react-id@1.1.0", "@radix-ui/react-id@^1.1.0": +"@radix-ui/react-id@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.1.tgz#1404002e79a03fe062b7e3864aa01e24bd1471f7" + integrity sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.1" + +"@radix-ui/react-id@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.0.tgz#de47339656594ad722eb87f94a6b25f9cffae0ed" integrity sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA== dependencies: "@radix-ui/react-use-layout-effect" "1.1.0" -"@radix-ui/react-label@^2.1.0": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-2.1.1.tgz#f30bd577b26873c638006e4f65761d4c6b80566d" - integrity sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw== - dependencies: - "@radix-ui/react-primitive" "2.0.1" - -"@radix-ui/react-menu@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.1.4.tgz#ac7aec296f29608206a7c6ef6335d8f102edaa95" - integrity sha512-BnOgVoL6YYdHAG6DtXONaR29Eq4nvbi8rutrV/xlr3RQCMMb3yqP85Qiw/3NReozrSW+4dfLkK+rc1hb4wPU/A== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-collection" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-direction" "1.1.0" - "@radix-ui/react-dismissable-layer" "1.1.3" - "@radix-ui/react-focus-guards" "1.1.1" - "@radix-ui/react-focus-scope" "1.1.1" - "@radix-ui/react-id" "1.1.0" - "@radix-ui/react-popper" "1.2.1" - "@radix-ui/react-portal" "1.1.3" - "@radix-ui/react-presence" "1.1.2" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-roving-focus" "1.1.1" - "@radix-ui/react-slot" "1.1.1" - "@radix-ui/react-use-callback-ref" "1.1.0" - aria-hidden "^1.1.1" - react-remove-scroll "^2.6.1" - -"@radix-ui/react-popover@^1.0.7": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.1.4.tgz#d83104e5fb588870a673b55f3387da4844e5836e" - integrity sha512-aUACAkXx8LaFymDma+HQVji7WhvEhpFJ7+qPz17Nf4lLZqtreGOFRiNQWQmhzp7kEWg9cOyyQJpdIMUMPc/CPw== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-dismissable-layer" "1.1.3" - "@radix-ui/react-focus-guards" "1.1.1" - "@radix-ui/react-focus-scope" "1.1.1" - "@radix-ui/react-id" "1.1.0" - "@radix-ui/react-popper" "1.2.1" - "@radix-ui/react-portal" "1.1.3" - "@radix-ui/react-presence" "1.1.2" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-slot" "1.1.1" - "@radix-ui/react-use-controllable-state" "1.1.0" - aria-hidden "^1.1.1" - react-remove-scroll "^2.6.1" - -"@radix-ui/react-popper@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.1.tgz#2fc66cfc34f95f00d858924e3bee54beae2dff0a" - integrity sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw== - dependencies: - "@floating-ui/react-dom" "^2.0.0" - "@radix-ui/react-arrow" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-callback-ref" "1.1.0" - "@radix-ui/react-use-layout-effect" "1.1.0" - "@radix-ui/react-use-rect" "1.1.0" - "@radix-ui/react-use-size" "1.1.0" - "@radix-ui/rect" "1.1.0" - -"@radix-ui/react-popper@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.2.tgz#d2e1ee5a9b24419c5936a1b7f6f472b7b412b029" - integrity sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA== +"@radix-ui/react-label@2.1.7": + version "2.1.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-2.1.7.tgz#ad959ff9c6e4968d533329eb95696e1ba8ad72ab" + integrity sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ== + dependencies: + "@radix-ui/react-primitive" "2.1.3" + +"@radix-ui/react-menu@2.1.15": + version "2.1.15" + resolved "https://registry.yarnpkg.com/@radix-ui/react-menu/-/react-menu-2.1.15.tgz#a1a8f06cab3c309f9998cdbd2b3ad279e42ed483" + integrity sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-collection" "1.1.7" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-direction" "1.1.1" + "@radix-ui/react-dismissable-layer" "1.1.10" + "@radix-ui/react-focus-guards" "1.1.2" + "@radix-ui/react-focus-scope" "1.1.7" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-popper" "1.2.7" + "@radix-ui/react-portal" "1.1.9" + "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-roving-focus" "1.1.10" + "@radix-ui/react-slot" "1.2.3" + "@radix-ui/react-use-callback-ref" "1.1.1" + aria-hidden "^1.2.4" + react-remove-scroll "^2.6.3" + +"@radix-ui/react-popover@1.1.14": + version "1.1.14" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.1.14.tgz#5496d1986f0287cdfc77e73f70a887e4cb77ad08" + integrity sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-dismissable-layer" "1.1.10" + "@radix-ui/react-focus-guards" "1.1.2" + "@radix-ui/react-focus-scope" "1.1.7" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-popper" "1.2.7" + "@radix-ui/react-portal" "1.1.9" + "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-slot" "1.2.3" + "@radix-ui/react-use-controllable-state" "1.2.2" + aria-hidden "^1.2.4" + react-remove-scroll "^2.6.3" + +"@radix-ui/react-popper@1.2.7": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.7.tgz#531cf2eebb3d3270d58f7d8136e4517646429978" + integrity sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ== dependencies: "@floating-ui/react-dom" "^2.0.0" - "@radix-ui/react-arrow" "1.1.2" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-primitive" "2.0.2" - "@radix-ui/react-use-callback-ref" "1.1.0" - "@radix-ui/react-use-layout-effect" "1.1.0" - "@radix-ui/react-use-rect" "1.1.0" - "@radix-ui/react-use-size" "1.1.0" - "@radix-ui/rect" "1.1.0" - -"@radix-ui/react-portal@1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.3.tgz#b0ea5141103a1671b715481b13440763d2ac4440" - integrity sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw== + "@radix-ui/react-arrow" "1.1.7" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-use-rect" "1.1.1" + "@radix-ui/react-use-size" "1.1.1" + "@radix-ui/rect" "1.1.1" + +"@radix-ui/react-portal@1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.9.tgz#14c3649fe48ec474ac51ed9f2b9f5da4d91c4472" + integrity sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ== dependencies: - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-layout-effect" "1.1.1" -"@radix-ui/react-portal@1.1.4": +"@radix-ui/react-presence@1.1.4": version "1.1.4" - resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.4.tgz#ff5401ff63c8a825c46eea96d3aef66074b8c0c8" - integrity sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA== + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.4.tgz#253ac0ad4946c5b4a9c66878335f5cf07c967ced" + integrity sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA== dependencies: - "@radix-ui/react-primitive" "2.0.2" - "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-use-layout-effect" "1.1.1" -"@radix-ui/react-presence@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.2.tgz#bb764ed8a9118b7ec4512da5ece306ded8703cdc" - integrity sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg== - dependencies: - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-use-layout-effect" "1.1.0" - -"@radix-ui/react-primitive@2.0.1", "@radix-ui/react-primitive@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz#6d9efc550f7520135366f333d1e820cf225fad9e" - integrity sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg== +"@radix-ui/react-presence@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.5.tgz#5d8f28ac316c32f078afce2996839250c10693db" + integrity sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ== dependencies: - "@radix-ui/react-slot" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-use-layout-effect" "1.1.1" -"@radix-ui/react-primitive@2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz#ac8b7854d87b0d7af388d058268d9a7eb64ca8ef" - integrity sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w== +"@radix-ui/react-primitive@2.1.3", "@radix-ui/react-primitive@^2.0.2": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz#db9b8bcff49e01be510ad79893fb0e4cda50f1bc" + integrity sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ== + dependencies: + "@radix-ui/react-slot" "1.2.3" + +"@radix-ui/react-roving-focus@1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz#46030496d2a490c4979d29a7e1252465e51e4b0b" + integrity sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-collection" "1.1.7" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-direction" "1.1.1" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-controllable-state" "1.2.2" + +"@radix-ui/react-scroll-area@1.2.9": + version "1.2.9" + resolved "https://registry.yarnpkg.com/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.9.tgz#90c49bd3231d7f0796d5d12dabc065afa829cf07" + integrity sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A== + dependencies: + "@radix-ui/number" "1.1.1" + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-direction" "1.1.1" + "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-layout-effect" "1.1.1" + +"@radix-ui/react-select@2.2.5": + version "2.2.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-2.2.5.tgz#9e2fa5b8f4cc99b86ef5bba3cb9b73828afb51f0" + integrity sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA== + dependencies: + "@radix-ui/number" "1.1.1" + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-collection" "1.1.7" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-direction" "1.1.1" + "@radix-ui/react-dismissable-layer" "1.1.10" + "@radix-ui/react-focus-guards" "1.1.2" + "@radix-ui/react-focus-scope" "1.1.7" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-popper" "1.2.7" + "@radix-ui/react-portal" "1.1.9" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-slot" "1.2.3" + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-controllable-state" "1.2.2" + "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-use-previous" "1.1.1" + "@radix-ui/react-visually-hidden" "1.2.3" + aria-hidden "^1.2.4" + react-remove-scroll "^2.6.3" + +"@radix-ui/react-separator@1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.1.7.tgz#a18bd7fd07c10fda1bba14f2a3032e7b1a2b3470" + integrity sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA== + dependencies: + "@radix-ui/react-primitive" "2.1.3" + +"@radix-ui/react-slider@1.3.5": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slider/-/react-slider-1.3.5.tgz#f9c074dc0dd2850aa42609e72de74642a4851b79" + integrity sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw== + dependencies: + "@radix-ui/number" "1.1.1" + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-collection" "1.1.7" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-direction" "1.1.1" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-controllable-state" "1.2.2" + "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-use-previous" "1.1.1" + "@radix-ui/react-use-size" "1.1.1" + +"@radix-ui/react-slot@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.3.tgz#502d6e354fc847d4169c3bc5f189de777f68cfe1" + integrity sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A== + dependencies: + "@radix-ui/react-compose-refs" "1.1.2" + +"@radix-ui/react-switch@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.2.5.tgz#56c15a4cd219e00b0745ec6b2ea1c0feeb0b21d0" + integrity sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-controllable-state" "1.2.2" + "@radix-ui/react-use-previous" "1.1.1" + "@radix-ui/react-use-size" "1.1.1" + +"@radix-ui/react-tabs@1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz#99b3522c73db9263f429a6d0f5a9acb88df3b129" + integrity sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-direction" "1.1.1" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-roving-focus" "1.1.10" + "@radix-ui/react-use-controllable-state" "1.2.2" + +"@radix-ui/react-toggle@1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle/-/react-toggle-1.1.9.tgz#9cb99a29bc7cd15186ba3ba797808a013a726fba" + integrity sha512-ZoFkBBz9zv9GWer7wIjvdRxmh2wyc2oKWw6C6CseWd6/yq1DK/l5lJ+wnsmFwJZbBYqr02mrf8A2q/CVCuM3ZA== dependencies: - "@radix-ui/react-slot" "1.1.2" - -"@radix-ui/react-roving-focus@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.1.tgz#3b3abb1e03646937f28d9ab25e96343667ca6520" - integrity sha512-QE1RoxPGJ/Nm8Qmk0PxP8ojmoaS67i0s7hVssS7KuI2FQoc/uzVlZsqKfQvxPE6D8hICCPHJ4D88zNhT3OOmkw== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-collection" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-direction" "1.1.0" - "@radix-ui/react-id" "1.1.0" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-callback-ref" "1.1.0" - "@radix-ui/react-use-controllable-state" "1.1.0" - -"@radix-ui/react-scroll-area@^1.1.0": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.2.tgz#28e34fd4d83e9de5d987c5e8914a7bd8be9546a5" - integrity sha512-EFI1N/S3YxZEW/lJ/H1jY3njlvTd8tBmgKEn4GHi51+aMm94i6NmAJstsm5cu3yJwYqYc93gpCPm21FeAbFk6g== - dependencies: - "@radix-ui/number" "1.1.0" - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-direction" "1.1.0" - "@radix-ui/react-presence" "1.1.2" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-callback-ref" "1.1.0" - "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-controllable-state" "1.2.2" -"@radix-ui/react-select@^2.1.1": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-2.1.4.tgz#8957050203640b668a883a225260c403514b3772" - integrity sha512-pOkb2u8KgO47j/h7AylCj7dJsm69BXcjkrvTqMptFqsE2i0p8lHkfgneXKjAgPzBMivnoMyt8o4KiV4wYzDdyQ== - dependencies: - "@radix-ui/number" "1.1.0" - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-collection" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-direction" "1.1.0" - "@radix-ui/react-dismissable-layer" "1.1.3" - "@radix-ui/react-focus-guards" "1.1.1" - "@radix-ui/react-focus-scope" "1.1.1" - "@radix-ui/react-id" "1.1.0" - "@radix-ui/react-popper" "1.2.1" - "@radix-ui/react-portal" "1.1.3" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-slot" "1.1.1" - "@radix-ui/react-use-callback-ref" "1.1.0" - "@radix-ui/react-use-controllable-state" "1.1.0" - "@radix-ui/react-use-layout-effect" "1.1.0" - "@radix-ui/react-use-previous" "1.1.0" - "@radix-ui/react-visually-hidden" "1.1.1" - aria-hidden "^1.1.1" - react-remove-scroll "^2.6.1" - -"@radix-ui/react-separator@^1.1.0": +"@radix-ui/react-tooltip@1.2.7": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.2.7.tgz#23612ac7a5e8e1f6829e46d0e0ad94afe3976c72" + integrity sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw== + dependencies: + "@radix-ui/primitive" "1.1.2" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-dismissable-layer" "1.1.10" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-popper" "1.2.7" + "@radix-ui/react-portal" "1.1.9" + "@radix-ui/react-presence" "1.1.4" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-slot" "1.2.3" + "@radix-ui/react-use-controllable-state" "1.2.2" + "@radix-ui/react-visually-hidden" "1.2.3" + +"@radix-ui/react-use-callback-ref@1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.1.1.tgz#dd60621553c858238d876be9b0702287424866d2" - integrity sha512-RRiNRSrD8iUiXriq/Y5n4/3iE8HzqgLHsusUSg5jVpU2+3tqcUFPJXHDymwEypunc2sWxDUS3UC+rkZRlHedsw== - dependencies: - "@radix-ui/react-primitive" "2.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz#62a4dba8b3255fdc5cc7787faeac1c6e4cc58d40" + integrity sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg== -"@radix-ui/react-slider@^1.2.0": +"@radix-ui/react-use-controllable-state@1.2.2": version "1.2.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-slider/-/react-slider-1.2.2.tgz#4ca883e3f0dea7b97d43c6cbc6c4305c64e75a86" - integrity sha512-sNlU06ii1/ZcbHf8I9En54ZPW0Vil/yPVg4vQMcFNjrIx51jsHbFl1HYHQvCIWJSr1q0ZmA+iIs/ZTv8h7HHSA== - dependencies: - "@radix-ui/number" "1.1.0" - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-collection" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-direction" "1.1.0" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-controllable-state" "1.1.0" - "@radix-ui/react-use-layout-effect" "1.1.0" - "@radix-ui/react-use-previous" "1.1.0" - "@radix-ui/react-use-size" "1.1.0" - -"@radix-ui/react-slot@1.1.1", "@radix-ui/react-slot@^1.0.2": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.1.tgz#ab9a0ffae4027db7dc2af503c223c978706affc3" - integrity sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g== + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz#905793405de57d61a439f4afebbb17d0645f3190" + integrity sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg== dependencies: - "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-use-effect-event" "0.0.2" + "@radix-ui/react-use-layout-effect" "1.1.1" -"@radix-ui/react-slot@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz#daffff7b2bfe99ade63b5168407680b93c00e1c6" - integrity sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ== +"@radix-ui/react-use-effect-event@0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz#090cf30d00a4c7632a15548512e9152217593907" + integrity sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA== dependencies: - "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-use-layout-effect" "1.1.1" -"@radix-ui/react-switch@^1.1.0": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.1.2.tgz#61323f4cccf25bf56c95fceb3b56ce1407bc9aec" - integrity sha512-zGukiWHjEdBCRyXvKR6iXAQG6qXm2esuAD6kDOi9Cn+1X6ev3ASo4+CsYaD6Fov9r/AQFekqnD/7+V0Cs6/98g== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-controllable-state" "1.1.0" - "@radix-ui/react-use-previous" "1.1.0" - "@radix-ui/react-use-size" "1.1.0" - -"@radix-ui/react-tabs@^1.1.0": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.1.2.tgz#a72da059593cba30fccb30a226d63af686b32854" - integrity sha512-9u/tQJMcC2aGq7KXpGivMm1mgq7oRJKXphDwdypPd/j21j/2znamPU8WkXgnhUaTrSFNIt8XhOyCAupg8/GbwQ== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-direction" "1.1.0" - "@radix-ui/react-id" "1.1.0" - "@radix-ui/react-presence" "1.1.2" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-roving-focus" "1.1.1" - "@radix-ui/react-use-controllable-state" "1.1.0" - -"@radix-ui/react-toggle@^1.1.0": +"@radix-ui/react-use-escape-keydown@1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle/-/react-toggle-1.1.1.tgz#939162f87d2c6cfba912a9908ed5ee651bd1ce8f" - integrity sha512-i77tcgObYr743IonC1hrsnnPmszDRn8p+EGUsUt+5a/JFn28fxaM88Py6V2mc8J5kELMWishI0rLnuGLFD/nnQ== + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz#b3fed9bbea366a118f40427ac40500aa1423cc29" + integrity sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g== dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-use-controllable-state" "1.1.0" - -"@radix-ui/react-tooltip@^1.1.2": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.1.6.tgz#eab98e9a5c876ef0abfae3cfeee229870528ed06" - integrity sha512-TLB5D8QLExS1uDn7+wH/bjEmRurNMTzNrtq7IjaS4kjion9NtzsTGkvR5+i7yc9q01Pi2KMM2cN3f8UG4IvvXA== - dependencies: - "@radix-ui/primitive" "1.1.1" - "@radix-ui/react-compose-refs" "1.1.1" - "@radix-ui/react-context" "1.1.1" - "@radix-ui/react-dismissable-layer" "1.1.3" - "@radix-ui/react-id" "1.1.0" - "@radix-ui/react-popper" "1.2.1" - "@radix-ui/react-portal" "1.1.3" - "@radix-ui/react-presence" "1.1.2" - "@radix-ui/react-primitive" "2.0.1" - "@radix-ui/react-slot" "1.1.1" - "@radix-ui/react-use-controllable-state" "1.1.0" - "@radix-ui/react-visually-hidden" "1.1.1" - -"@radix-ui/react-use-callback-ref@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1" - integrity sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw== - -"@radix-ui/react-use-controllable-state@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz#1321446857bb786917df54c0d4d084877aab04b0" - integrity sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw== - dependencies: - "@radix-ui/react-use-callback-ref" "1.1.0" - -"@radix-ui/react-use-escape-keydown@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz#31a5b87c3b726504b74e05dac1edce7437b98754" - integrity sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw== - dependencies: - "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-callback-ref" "1.1.1" "@radix-ui/react-use-layout-effect@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz#3c2c8ce04827b26a39e442ff4888d9212268bd27" integrity sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w== -"@radix-ui/react-use-previous@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz#d4dd37b05520f1d996a384eb469320c2ada8377c" - integrity sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og== +"@radix-ui/react-use-layout-effect@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz#0c4230a9eed49d4589c967e2d9c0d9d60a23971e" + integrity sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ== -"@radix-ui/react-use-rect@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz#13b25b913bd3e3987cc9b073a1a164bb1cf47b88" - integrity sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ== - dependencies: - "@radix-ui/rect" "1.1.0" +"@radix-ui/react-use-previous@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz#1a1ad5568973d24051ed0af687766f6c7cb9b5b5" + integrity sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ== -"@radix-ui/react-use-size@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz#b4dba7fbd3882ee09e8d2a44a3eed3a7e555246b" - integrity sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw== +"@radix-ui/react-use-rect@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz#01443ca8ed071d33023c1113e5173b5ed8769152" + integrity sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w== dependencies: - "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/rect" "1.1.1" -"@radix-ui/react-visually-hidden@1.1.1": +"@radix-ui/react-use-size@1.1.1": version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.1.tgz#f7b48c1af50dfdc366e92726aee6d591996c5752" - integrity sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg== + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz#6de276ffbc389a537ffe4316f5b0f24129405b37" + integrity sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ== dependencies: - "@radix-ui/react-primitive" "2.0.1" + "@radix-ui/react-use-layout-effect" "1.1.1" -"@radix-ui/rect@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.0.tgz#f817d1d3265ac5415dadc67edab30ae196696438" - integrity sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg== +"@radix-ui/react-visually-hidden@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz#a8c38c8607735dc9f05c32f87ab0f9c2b109efbf" + integrity sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug== + dependencies: + "@radix-ui/react-primitive" "2.1.3" + +"@radix-ui/rect@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.1.tgz#78244efe12930c56fd255d7923865857c41ac8cb" + integrity sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw== "@rollup/plugin-babel@^5.2.0": version "5.3.1" @@ -3228,81 +3236,41 @@ resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22" integrity sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g== -"@svgr/babel-plugin-add-jsx-attribute@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906" - integrity sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg== - "@svgr/babel-plugin-remove-jsx-attribute@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz#69177f7937233caca3a1afb051906698f2f59186" integrity sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA== -"@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz#6b2c770c95c874654fd5e1d5ef475b78a0a962ef" - integrity sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg== - "@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz#c2c48104cfd7dcd557f373b70a56e9e3bdae1d44" integrity sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA== -"@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz#25621a8915ed7ad70da6cea3d0a6dbc2ea933efd" - integrity sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA== - "@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz#8fbb6b2e91fa26ac5d4aa25c6b6e4f20f9c0ae27" integrity sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ== -"@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz#0b221fc57f9fcd10e91fe219e2cd0dd03145a897" - integrity sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ== - "@svgr/babel-plugin-svg-dynamic-title@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz#1d5ba1d281363fc0f2f29a60d6d936f9bbc657b0" integrity sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og== -"@svgr/babel-plugin-svg-dynamic-title@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz#139b546dd0c3186b6e5db4fefc26cb0baea729d7" - integrity sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg== - "@svgr/babel-plugin-svg-em-dimensions@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz#35e08df300ea8b1d41cb8f62309c241b0369e501" integrity sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g== -"@svgr/babel-plugin-svg-em-dimensions@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz#6543f69526632a133ce5cabab965deeaea2234a0" - integrity sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw== - "@svgr/babel-plugin-transform-react-native-svg@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz#90a8b63998b688b284f255c6a5248abd5b28d754" integrity sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q== -"@svgr/babel-plugin-transform-react-native-svg@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz#00bf9a7a73f1cad3948cdab1f8dfb774750f8c80" - integrity sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q== - "@svgr/babel-plugin-transform-svg-component@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz#013b4bfca88779711f0ed2739f3f7efcefcf4f7e" integrity sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw== -"@svgr/babel-plugin-transform-svg-component@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz#583a5e2a193e214da2f3afeb0b9e8d3250126b4a" - integrity sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ== - "@svgr/babel-preset@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-8.1.0.tgz#0e87119aecdf1c424840b9d4565b7137cabf9ece" @@ -3317,20 +3285,6 @@ "@svgr/babel-plugin-transform-react-native-svg" "8.1.0" "@svgr/babel-plugin-transform-svg-component" "8.0.0" -"@svgr/babel-preset@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-5.5.0.tgz#8af54f3e0a8add7b1e2b0fcd5a882c55393df327" - integrity sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig== - dependencies: - "@svgr/babel-plugin-add-jsx-attribute" "^5.4.0" - "@svgr/babel-plugin-remove-jsx-attribute" "^5.4.0" - "@svgr/babel-plugin-remove-jsx-empty-expression" "^5.0.1" - "@svgr/babel-plugin-replace-jsx-attribute-value" "^5.0.1" - "@svgr/babel-plugin-svg-dynamic-title" "^5.4.0" - "@svgr/babel-plugin-svg-em-dimensions" "^5.4.0" - "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" - "@svgr/babel-plugin-transform-svg-component" "^5.5.0" - "@svgr/core@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/core/-/core-8.1.0.tgz#41146f9b40b1a10beaf5cc4f361a16a3c1885e88" @@ -3342,15 +3296,6 @@ cosmiconfig "^8.1.3" snake-case "^3.0.4" -"@svgr/core@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/core/-/core-5.5.0.tgz#82e826b8715d71083120fe8f2492ec7d7874a579" - integrity sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ== - dependencies: - "@svgr/plugin-jsx" "^5.5.0" - camelcase "^6.2.0" - cosmiconfig "^7.0.0" - "@svgr/hast-util-to-babel-ast@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz#6952fd9ce0f470e1aded293b792a2705faf4ffd4" @@ -3359,13 +3304,6 @@ "@babel/types" "^7.21.3" entities "^4.4.0" -"@svgr/hast-util-to-babel-ast@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz#5ee52a9c2533f73e63f8f22b779f93cd432a5461" - integrity sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ== - dependencies: - "@babel/types" "^7.12.6" - "@svgr/plugin-jsx@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz#96969f04a24b58b174ee4cd974c60475acbd6928" @@ -3376,16 +3314,6 @@ "@svgr/hast-util-to-babel-ast" "8.0.0" svg-parser "^2.0.4" -"@svgr/plugin-jsx@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz#1aa8cd798a1db7173ac043466d7b52236b369000" - integrity sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA== - dependencies: - "@babel/core" "^7.12.3" - "@svgr/babel-preset" "^5.5.0" - "@svgr/hast-util-to-babel-ast" "^5.5.0" - svg-parser "^2.0.2" - "@svgr/plugin-svgo@8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz#b115b7b967b564f89ac58feae89b88c3decd0f00" @@ -3395,30 +3323,7 @@ deepmerge "^4.3.1" svgo "^3.0.2" -"@svgr/plugin-svgo@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz#02da55d85320549324e201c7b2e53bf431fcc246" - integrity sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ== - dependencies: - cosmiconfig "^7.0.0" - deepmerge "^4.2.2" - svgo "^1.2.2" - -"@svgr/webpack@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-5.5.0.tgz#aae858ee579f5fa8ce6c3166ef56c6a1b381b640" - integrity sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g== - dependencies: - "@babel/core" "^7.12.3" - "@babel/plugin-transform-react-constant-elements" "^7.12.1" - "@babel/preset-env" "^7.12.1" - "@babel/preset-react" "^7.12.5" - "@svgr/core" "^5.5.0" - "@svgr/plugin-jsx" "^5.5.0" - "@svgr/plugin-svgo" "^5.5.0" - loader-utils "^2.0.0" - -"@svgr/webpack@^8.1.0": +"@svgr/webpack@8.1.0", "@svgr/webpack@^8.1.0": version "8.1.0" resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-8.1.0.tgz#16f1b5346f102f89fda6ec7338b96a701d8be0c2" integrity sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA== @@ -3829,11 +3734,6 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.14.tgz#1433419d73b2a7ebfc6918dcefd2ec0d5cd698f2" integrity sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ== -"@types/q@^1.5.1": - version "1.5.8" - resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.8.tgz#95f6c6a08f2ad868ba230ead1d2d7f7be3db3837" - integrity sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw== - "@types/qs@*": version "6.9.17" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.17.tgz#fc560f60946d0aeff2f914eb41679659d3310e1a" @@ -3877,10 +3777,10 @@ dependencies: csstype "^3.0.2" -"@types/react@^18.2.29": - version "18.3.18" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.18.tgz#9b382c4cd32e13e463f97df07c2ee3bbcd26904b" - integrity sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ== +"@types/react@18.3.23": + version "18.3.23" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.23.tgz#86ae6f6b95a48c418fecdaccc8069e0fbb63696a" + integrity sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w== dependencies: "@types/prop-types" "*" csstype "^3.0.2" @@ -4126,6 +4026,20 @@ acorn-jsx@^5.0.0, acorn-jsx@^5.0.1: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== +acorn-node@^1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" + integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== + dependencies: + acorn "^7.0.0" + acorn-walk "^7.0.0" + xtend "^4.0.2" + +acorn-walk@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + acorn-walk@^8.0.0: version "8.3.4" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" @@ -4138,6 +4052,11 @@ acorn@^6.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== +acorn@^7.0.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + acorn@^8.0.0, acorn@^8.0.4, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.8.2: version "8.14.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" @@ -4333,10 +4252,10 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-hidden@^1.1.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.4.tgz#b78e383fdbc04d05762c78b4a25a501e736c4522" - integrity sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A== +aria-hidden@^1.2.4: + version "1.2.6" + resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.6.tgz#73051c9b088114c795b1ea414e9c0fff874ffc1a" + integrity sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA== dependencies: tslib "^2.0.0" @@ -4369,19 +4288,6 @@ array.prototype.find@^2.1.1: es-object-atoms "^1.0.0" es-shim-unscopables "^1.0.2" -array.prototype.reduce@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.7.tgz#6aadc2f995af29cb887eb866d981dc85ab6f7dc7" - integrity sha512-mzmiUCVwtiD4lgxYP8g7IYy8El8p2CSMePvIbTS7gchKir/L1fgJrk0yDKmAX6mnRQFKNADYIk8nNlTris5H1Q== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-array-method-boxes-properly "^1.0.0" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - is-string "^1.0.7" - arraybuffer.prototype.slice@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" @@ -4410,7 +4316,19 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -autoprefixer@^10.4.13, autoprefixer@^10.4.19, autoprefixer@^10.4.20: +autoprefixer@10.4.21: + version "10.4.21" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.21.tgz#77189468e7a8ad1d9a37fbc08efc9f480cf0a95d" + integrity sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ== + dependencies: + browserslist "^4.24.4" + caniuse-lite "^1.0.30001702" + fraction.js "^4.3.7" + normalize-range "^0.1.2" + picocolors "^1.1.1" + postcss-value-parser "^4.2.0" + +autoprefixer@^10.4.13, autoprefixer@^10.4.19: version "10.4.20" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" integrity sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g== @@ -4521,6 +4439,11 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +baseline-browser-mapping@^2.8.19: + version "2.8.21" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.21.tgz#2f9cccde871bfa4aec9dbf92d0ee746e4f1892e4" + integrity sha512-JU0h5APyQNsHOlAM7HnQnPToSDQoEBZqzu/YBlqDnEeymPnZDREeXJA3KBMQee+dKteAxZ2AtvQEvVYdZf241Q== + batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" @@ -4571,7 +4494,7 @@ bonjour-service@^1.0.11: fast-deep-equal "^3.1.3" multicast-dns "^7.2.5" -boolbase@^1.0.0, boolbase@~1.0.0: +boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== @@ -4636,6 +4559,17 @@ browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.4, browserslist@^4 node-releases "^2.0.19" update-browserslist-db "^1.1.1" +browserslist@^4.24.4: + version "4.27.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.27.0.tgz#755654744feae978fbb123718b2f139bc0fa6697" + integrity sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw== + dependencies: + baseline-browser-mapping "^2.8.19" + caniuse-lite "^1.0.30001751" + electron-to-chromium "^1.5.238" + node-releases "^2.0.26" + update-browserslist-db "^1.1.4" + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -4762,12 +4696,17 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001616, caniuse-lite@^1.0.30001646, can resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz#4585729d95e6b95be5b439da6ab55250cd125bf9" integrity sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A== +caniuse-lite@^1.0.30001702, caniuse-lite@^1.0.30001751: + version "1.0.30001751" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz#dacd5d9f4baeea841641640139d2b2a4df4226ad" + integrity sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw== + ccount@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== -chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -4839,7 +4778,7 @@ cheerio@1.0.0-rc.12: parse5 "^7.0.0" parse5-htmlparser2-tree-adapter "^7.0.0" -chokidar@^3.4.2, chokidar@^3.5.3, chokidar@^3.6.0: +chokidar@^3.4.2, chokidar@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -4869,14 +4808,14 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== -class-variance-authority@^0.7.0: +class-variance-authority@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz#4008a798a0e4553a781a57ac5177c9fb5d043787" integrity sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg== dependencies: clsx "^2.1.1" -classnames@^2.3.2: +classnames@2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== @@ -4916,34 +4855,25 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clsx@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" - integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== - -clsx@^2.0.0, clsx@^2.1.1: +clsx@2.1.1, clsx@^2.0.0, clsx@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== -cmdk@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cmdk/-/cmdk-1.0.4.tgz#cbddef6f5ade2378f85c80a0b9ad9a8a712779b5" - integrity sha512-AnsjfHyHpQ/EFeAnG216WY7A5LiYCoZzCSygiLvfXC3H3LFGCprErteUcszaVluGOhuOTbJS3jWHrSDYPBBygg== - dependencies: - "@radix-ui/react-dialog" "^1.1.2" - "@radix-ui/react-id" "^1.1.0" - "@radix-ui/react-primitive" "^2.0.0" - use-sync-external-store "^1.2.2" +clsx@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== -coa@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== +cmdk@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/cmdk/-/cmdk-1.1.1.tgz#b8524272699ccaa37aaf07f36850b376bf3d58e5" + integrity sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg== dependencies: - "@types/q" "^1.5.1" - chalk "^2.4.1" - q "^1.1.2" + "@radix-ui/react-compose-refs" "^1.1.1" + "@radix-ui/react-dialog" "^1.1.6" + "@radix-ui/react-id" "^1.1.0" + "@radix-ui/react-primitive" "^2.0.2" collapse-white-space@^2.0.0: version "2.1.0" @@ -4969,7 +4899,7 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@^1.0.0, color-name@~1.1.4: +color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -5191,17 +5121,6 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" -cosmiconfig@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" - integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" - cosmiconfig@^8.1.3, cosmiconfig@^8.3.5: version "8.3.6" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" @@ -5304,21 +5223,6 @@ css-prefers-color-scheme@^6.0.3: resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz#ca8a22e5992c10a5b9d315155e7caee625903349" integrity sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA== -css-select-base-adapter@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== - -css-select@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" - integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== - dependencies: - boolbase "^1.0.0" - css-what "^3.2.1" - domutils "^1.7.0" - nth-check "^1.0.2" - css-select@^4.1.3: version "4.3.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" @@ -5341,22 +5245,6 @@ css-select@^5.1.0: domutils "^3.0.1" nth-check "^2.0.1" -css-tree@1.0.0-alpha.37: - version "1.0.0-alpha.37" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" - integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== - dependencies: - mdn-data "2.0.4" - source-map "^0.6.1" - -css-tree@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== - dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" - css-tree@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" @@ -5373,11 +5261,6 @@ css-tree@~2.2.0: mdn-data "2.0.28" source-map-js "^1.0.1" -css-what@^3.2.1: - version "3.4.2" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" - integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== - css-what@^6.0.1, css-what@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" @@ -5460,13 +5343,6 @@ cssnano@^6.0.1, cssnano@^6.1.2: cssnano-preset-default "^6.1.2" lilconfig "^3.1.1" -csso@^4.0.2: - version "4.2.0" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" - integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== - dependencies: - css-tree "^1.1.2" - csso@^5.0.5: version "5.0.5" resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" @@ -5506,7 +5382,7 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -date-fns@^3.6.0: +date-fns@3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf" integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww== @@ -5594,6 +5470,11 @@ define-properties@^1.1.3, define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +defined@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf" + integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q== + del@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" @@ -5664,6 +5545,15 @@ detect-port@^1.5.1: address "^1.0.1" debug "4" +detective@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" + integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw== + dependencies: + acorn-node "^1.8.2" + defined "^1.0.0" + minimist "^1.2.6" + devlop@^1.0.0, devlop@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" @@ -5702,7 +5592,7 @@ document.contains@^1.0.1: dependencies: define-properties "^1.1.3" -docusaurus-plugin-image-zoom@^1.0.1: +docusaurus-plugin-image-zoom@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/docusaurus-plugin-image-zoom/-/docusaurus-plugin-image-zoom-1.0.1.tgz#17afec39f2e630cac50a4ed3a8bbdad8d0aa8b9d" integrity sha512-96IpSKUx2RWy3db9aZ0s673OQo5DWgV9UVWouS+CPOSIVEdCWh6HKmWf6tB9rsoaiIF3oNn9keiyv6neEyKb1Q== @@ -5717,14 +5607,6 @@ dom-converter@^0.2.0: dependencies: utila "~0.4" -dom-serializer@0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" @@ -5743,11 +5625,6 @@ dom-serializer@^2.0.0: domhandler "^5.0.2" entities "^4.2.0" -domelementtype@1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" @@ -5767,14 +5644,6 @@ domhandler@^5.0.2, domhandler@^5.0.3: dependencies: domelementtype "^2.3.0" -domutils@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - dependencies: - dom-serializer "0" - domelementtype "1" - domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" @@ -5839,6 +5708,11 @@ ejs@^3.1.6: dependencies: jake "^10.8.5" +electron-to-chromium@^1.5.238: + version "1.5.243" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.243.tgz#b13b4a046f49f46574d643d4e2ec2ea33ce8cfe7" + integrity sha512-ZCphxFW3Q1TVhcgS9blfut1PX8lusVi2SvXQgmEEnK4TCmE1JhH2JkjJN+DNt0pJJwfBri5AROBnz2b/C+YU9g== + electron-to-chromium@^1.5.73: version "1.5.80" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz#ca7a8361d7305f0ec9e203ce4e633cbb8a8ef1b1" @@ -5911,7 +5785,7 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.2, es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9: +es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9: version "1.23.9" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.9.tgz#5b45994b7de78dada5c1bebf1379646b32b9d606" integrity sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA== @@ -5968,11 +5842,6 @@ es-abstract@^1.17.2, es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23 unbox-primitive "^1.1.0" which-typed-array "^1.1.18" -es-array-method-boxes-properly@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" - integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== - es-define-property@^1.0.0, es-define-property@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" @@ -6291,7 +6160,7 @@ fast-fifo@^1.2.0, fast-fifo@^1.3.2: resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== -fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.2: +fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.0: version "3.3.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== @@ -6347,7 +6216,7 @@ figures@^3.2.0: dependencies: escape-string-regexp "^1.0.5" -file-loader@^6.2.0: +file-loader@6.2.0, file-loader@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== @@ -6666,10 +6535,10 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^10.3.10: - version "10.4.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" - integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== +glob@10.5.0, glob@^10.3.10: + version "10.5.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" + integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== dependencies: foreground-child "^3.1.0" jackspeak "^3.1.2" @@ -6832,7 +6701,7 @@ has-proto@^1.2.0: dependencies: dunder-proto "^1.0.0" -has-symbols@^1.0.1, has-symbols@^1.0.3, has-symbols@^1.1.0: +has-symbols@^1.0.3, has-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== @@ -7194,7 +7063,7 @@ immer@^9.0.7: resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== -import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0: +import-fresh@^3.1.0, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -7550,7 +7419,7 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-string@^1.0.7, is-string@^1.1.1: +is-string@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== @@ -7691,7 +7560,7 @@ jest-worker@^29.4.3: merge-stream "^2.0.0" supports-color "^8.0.0" -jiti@^1.20.0, jiti@^1.21.6: +jiti@^1.20.0: version "1.21.7" resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.7.tgz#9dd81043424a3d28458b193d965f0d18a2300ba9" integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A== @@ -7891,7 +7760,12 @@ lightningcss@^1.27.0: lightningcss-win32-arm64-msvc "1.29.1" lightningcss-win32-x64-msvc "1.29.1" -lilconfig@^3.0.0, lilconfig@^3.1.1, lilconfig@^3.1.3: +lilconfig@^2.0.5, lilconfig@^2.0.6: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lilconfig@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== @@ -8003,7 +7877,7 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lucide-react@^0.379.0: +lucide-react@0.379.0: version "0.379.0" resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.379.0.tgz#29e34eeffae7fb241b64b09868cbe3ab888ef7cc" integrity sha512-KcdeVPqmhRldldAAgptb8FjIunM2x2Zy26ZBh1RsEUcdLIvsEmbcw7KpzFYUy5BbpGeWhPu9Z9J5YXfStiXwhg== @@ -8218,9 +8092,9 @@ mdast-util-phrasing@^4.0.0: unist-util-is "^6.0.0" mdast-util-to-hast@^13.0.0: - version "13.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" - integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== + version "13.2.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz#d7ff84ca499a57e2c060ae67548ad950e689a053" + integrity sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA== dependencies: "@types/hast" "^3.0.0" "@types/mdast" "^4.0.0" @@ -8254,11 +8128,6 @@ mdast-util-to-string@^4.0.0: dependencies: "@types/mdast" "^4.0.0" -mdn-data@2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== - mdn-data@2.0.28: version "2.0.28" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" @@ -8269,11 +8138,6 @@ mdn-data@2.0.30: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== -mdn-data@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -8835,13 +8699,6 @@ mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@~0.5.1: - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - mrmime@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" @@ -8874,6 +8731,11 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + nanoid@^3.3.7: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" @@ -8899,7 +8761,7 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -next-themes@^0.3.0: +next-themes@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.3.0.tgz#b4d2a866137a67d42564b07f3a3e720e2ff3871a" integrity sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w== @@ -8935,15 +8797,20 @@ node-emoji@^2.1.0: skin-tone "^2.0.0" node-forge@^1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + version "1.3.2" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.2.tgz#d0d2659a26eef778bf84d73e7f55c08144ee7750" + integrity sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw== node-releases@^2.0.19: version "2.0.19" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== +node-releases@^2.0.26: + version "2.0.27" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" + integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -8976,13 +8843,6 @@ nprogress@^0.2.0: resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA== -nth-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - nth-check@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" @@ -9048,19 +8908,6 @@ object.entries@^1.1.2: define-properties "^1.2.1" es-object-atoms "^1.1.1" -object.getownpropertydescriptors@^2.1.0: - version "2.1.8" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz#2f1fe0606ec1a7658154ccd4f728504f69667923" - integrity sha512-qkHIGe4q0lSYMv0XI4SsBTJz3WaURhLvd0lKSgtVuOsJ2krg4SgMw3PIRQFMp07yi++UR3se2mkcLqsBNpBb/A== - dependencies: - array.prototype.reduce "^1.0.6" - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - gopd "^1.0.1" - safe-array-concat "^1.1.2" - object.values@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" @@ -9684,7 +9531,7 @@ postcss-image-set-function@^7.0.0: "@csstools/utilities" "^2.0.0" postcss-value-parser "^4.2.0" -postcss-import@^14.0.2: +postcss-import@14.1.0, postcss-import@^14.1.0: version "14.1.0" resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0" integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw== @@ -9693,24 +9540,15 @@ postcss-import@^14.0.2: read-cache "^1.0.0" resolve "^1.1.7" -postcss-import@^15.1.0: - version "15.1.0" - resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" - integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== - dependencies: - postcss-value-parser "^4.0.0" - read-cache "^1.0.0" - resolve "^1.1.7" - postcss-initial@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-4.0.1.tgz#529f735f72c5724a0fb30527df6fb7ac54d7de42" integrity sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ== -postcss-js@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" - integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== +postcss-js@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.1.0.tgz#003b63c6edde948766e40f3daf7e997ae43a5ce6" + integrity sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw== dependencies: camelcase-css "^2.0.1" @@ -9733,13 +9571,13 @@ postcss-lab-function@^7.0.7: "@csstools/postcss-progressive-custom-properties" "^4.0.0" "@csstools/utilities" "^2.0.0" -postcss-load-config@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" - integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== +postcss-load-config@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" + integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== dependencies: - lilconfig "^3.0.0" - yaml "^2.3.4" + lilconfig "^2.0.5" + yaml "^1.10.2" postcss-loader@^7.3.3: version "7.3.4" @@ -9853,12 +9691,12 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-nested@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.2.0.tgz#4c2d22ab5f20b9cb61e2c5c5915950784d068131" - integrity sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ== +postcss-nested@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.0.tgz#1572f1984736578f360cffc7eb7dca69e30d1735" + integrity sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w== dependencies: - postcss-selector-parser "^6.1.1" + postcss-selector-parser "^6.0.10" postcss-nesting@^10.2.0: version "10.2.0" @@ -9990,6 +9828,61 @@ postcss-place@^7.0.5: dependencies: postcss-value-parser "^4.2.0" +postcss-preset-env@7.8.3: + version "7.8.3" + resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz#2a50f5e612c3149cc7af75634e202a5b2ad4f1e2" + integrity sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag== + dependencies: + "@csstools/postcss-cascade-layers" "^1.1.1" + "@csstools/postcss-color-function" "^1.1.1" + "@csstools/postcss-font-format-keywords" "^1.0.1" + "@csstools/postcss-hwb-function" "^1.0.2" + "@csstools/postcss-ic-unit" "^1.0.1" + "@csstools/postcss-is-pseudo-class" "^2.0.7" + "@csstools/postcss-nested-calc" "^1.0.0" + "@csstools/postcss-normalize-display-values" "^1.0.1" + "@csstools/postcss-oklab-function" "^1.1.1" + "@csstools/postcss-progressive-custom-properties" "^1.3.0" + "@csstools/postcss-stepped-value-functions" "^1.0.1" + "@csstools/postcss-text-decoration-shorthand" "^1.0.0" + "@csstools/postcss-trigonometric-functions" "^1.0.2" + "@csstools/postcss-unset-value" "^1.0.2" + autoprefixer "^10.4.13" + browserslist "^4.21.4" + css-blank-pseudo "^3.0.3" + css-has-pseudo "^3.0.4" + css-prefers-color-scheme "^6.0.3" + cssdb "^7.1.0" + postcss-attribute-case-insensitive "^5.0.2" + postcss-clamp "^4.1.0" + postcss-color-functional-notation "^4.2.4" + postcss-color-hex-alpha "^8.0.4" + postcss-color-rebeccapurple "^7.1.1" + postcss-custom-media "^8.0.2" + postcss-custom-properties "^12.1.10" + postcss-custom-selectors "^6.0.3" + postcss-dir-pseudo-class "^6.0.5" + postcss-double-position-gradients "^3.1.2" + postcss-env-function "^4.0.6" + postcss-focus-visible "^6.0.4" + postcss-focus-within "^5.0.4" + postcss-font-variant "^5.0.0" + postcss-gap-properties "^3.0.5" + postcss-image-set-function "^4.0.7" + postcss-initial "^4.0.1" + postcss-lab-function "^4.2.1" + postcss-logical "^5.0.4" + postcss-media-minmax "^5.0.0" + postcss-nesting "^10.2.0" + postcss-opacity-percentage "^1.1.2" + postcss-overflow-shorthand "^3.0.4" + postcss-page-break "^3.0.4" + postcss-place "^7.0.5" + postcss-pseudo-class-any-link "^7.1.6" + postcss-replace-overflow-wrap "^4.0.0" + postcss-selector-not "^6.0.1" + postcss-value-parser "^4.2.0" + postcss-preset-env@^10.1.0: version "10.1.3" resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-10.1.3.tgz#7d07adef2237a643162e751b00eb1e339aa3b82e" @@ -10059,61 +9952,6 @@ postcss-preset-env@^10.1.0: postcss-replace-overflow-wrap "^4.0.0" postcss-selector-not "^8.0.1" -postcss-preset-env@^7.4.3: - version "7.8.3" - resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz#2a50f5e612c3149cc7af75634e202a5b2ad4f1e2" - integrity sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag== - dependencies: - "@csstools/postcss-cascade-layers" "^1.1.1" - "@csstools/postcss-color-function" "^1.1.1" - "@csstools/postcss-font-format-keywords" "^1.0.1" - "@csstools/postcss-hwb-function" "^1.0.2" - "@csstools/postcss-ic-unit" "^1.0.1" - "@csstools/postcss-is-pseudo-class" "^2.0.7" - "@csstools/postcss-nested-calc" "^1.0.0" - "@csstools/postcss-normalize-display-values" "^1.0.1" - "@csstools/postcss-oklab-function" "^1.1.1" - "@csstools/postcss-progressive-custom-properties" "^1.3.0" - "@csstools/postcss-stepped-value-functions" "^1.0.1" - "@csstools/postcss-text-decoration-shorthand" "^1.0.0" - "@csstools/postcss-trigonometric-functions" "^1.0.2" - "@csstools/postcss-unset-value" "^1.0.2" - autoprefixer "^10.4.13" - browserslist "^4.21.4" - css-blank-pseudo "^3.0.3" - css-has-pseudo "^3.0.4" - css-prefers-color-scheme "^6.0.3" - cssdb "^7.1.0" - postcss-attribute-case-insensitive "^5.0.2" - postcss-clamp "^4.1.0" - postcss-color-functional-notation "^4.2.4" - postcss-color-hex-alpha "^8.0.4" - postcss-color-rebeccapurple "^7.1.1" - postcss-custom-media "^8.0.2" - postcss-custom-properties "^12.1.10" - postcss-custom-selectors "^6.0.3" - postcss-dir-pseudo-class "^6.0.5" - postcss-double-position-gradients "^3.1.2" - postcss-env-function "^4.0.6" - postcss-focus-visible "^6.0.4" - postcss-focus-within "^5.0.4" - postcss-font-variant "^5.0.0" - postcss-gap-properties "^3.0.5" - postcss-image-set-function "^4.0.7" - postcss-initial "^4.0.1" - postcss-lab-function "^4.2.1" - postcss-logical "^5.0.4" - postcss-media-minmax "^5.0.0" - postcss-nesting "^10.2.0" - postcss-opacity-percentage "^1.1.2" - postcss-overflow-shorthand "^3.0.4" - postcss-page-break "^3.0.4" - postcss-place "^7.0.5" - postcss-pseudo-class-any-link "^7.1.6" - postcss-replace-overflow-wrap "^4.0.0" - postcss-selector-not "^6.0.1" - postcss-value-parser "^4.2.0" - postcss-pseudo-class-any-link@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-10.0.1.tgz#06455431171bf44b84d79ebaeee9fd1c05946544" @@ -10169,7 +10007,7 @@ postcss-selector-not@^8.0.1: dependencies: postcss-selector-parser "^7.0.0" -postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.16, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.9, postcss-selector-parser@^6.1.1, postcss-selector-parser@^6.1.2: +postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.16, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.9: version "6.1.2" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== @@ -10217,7 +10055,16 @@ postcss-zindex@^6.0.2: resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-6.0.2.tgz#e498304b83a8b165755f53db40e2ea65a99b56e1" integrity sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg== -postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.33, postcss@^8.4.38, postcss@^8.4.47: +postcss@8.5.6, postcss@^8.4.18: + version "8.5.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +postcss@^8.4.21, postcss@^8.4.24, postcss@^8.4.26, postcss@^8.4.33, postcss@^8.4.38: version "8.4.49" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19" integrity sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA== @@ -10262,7 +10109,15 @@ pretty-time@^1.1.0: resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== -prism-react-renderer@^2.1.0, prism-react-renderer@^2.3.0, prism-react-renderer@^2.4.0: +prism-react-renderer@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-2.1.0.tgz#a2f418451647412ea73d18cfe363fea20e419f9d" + integrity sha512-I5cvXHjA1PVGbGm1MsWCpvBCRrYyxEri0MC7/JbfIfYfcXAxHyO5PaUjs3A8H5GW6kJcLhTHxxMaOZZpRZD2iQ== + dependencies: + "@types/prismjs" "^1.26.0" + clsx "^1.2.1" + +prism-react-renderer@^2.3.0, prism-react-renderer@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-2.4.1.tgz#ac63b7f78e56c8f2b5e76e823a976d5ede77e35f" integrity sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig== @@ -10347,17 +10202,12 @@ pupa@^3.1.0: dependencies: escape-goat "^4.0.0" -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== - -qs@6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== +qs@6.13.0, qs@6.14.1: + version "6.14.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.1.tgz#a41d85b9d3902f31d27861790506294881871159" + integrity sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ== dependencies: - side-channel "^1.0.6" + side-channel "^1.1.0" queue-microtask@^1.2.2: version "1.2.3" @@ -10418,7 +10268,7 @@ rc@1.2.8, rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-day-picker@^8.10.1: +react-day-picker@8.10.1: version "8.10.1" resolved "https://registry.yarnpkg.com/react-day-picker/-/react-day-picker-8.10.1.tgz#4762ec298865919b93ec09ba69621580835b8e80" integrity sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA== @@ -10453,7 +10303,7 @@ react-dev-utils@^12.0.1: strip-ansi "^6.0.1" text-table "^0.2.0" -react-dom@^18.3.1: +react-dom@18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== @@ -10520,7 +10370,7 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1: dependencies: "@types/react" "*" -react-outside-click-handler@^1.3.0: +react-outside-click-handler@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/react-outside-click-handler/-/react-outside-click-handler-1.3.0.tgz#3831d541ac059deecd38ec5423f81e80ad60e115" integrity sha512-Te/7zFU0oHpAnctl//pP3hEAeobfeHMyygHB8MnjP6sX5OR8KHT1G3jmLsV3U9RnIYo+Yn+peJYWu+D5tUS8qQ== @@ -10539,16 +10389,16 @@ react-remove-scroll-bar@^2.3.7: react-style-singleton "^2.2.2" tslib "^2.0.0" -react-remove-scroll@^2.6.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.6.2.tgz#2518d2c5112e71ea8928f1082a58459b5c7a2a97" - integrity sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw== +react-remove-scroll@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz#d2101d414f6d81d7d3bf033f3c1cb4785789f753" + integrity sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA== dependencies: react-remove-scroll-bar "^2.3.7" - react-style-singleton "^2.2.1" + react-style-singleton "^2.2.3" tslib "^2.1.0" use-callback-ref "^1.3.3" - use-sidecar "^1.1.2" + use-sidecar "^1.1.3" react-router-config@^5.1.1: version "5.1.1" @@ -10592,7 +10442,7 @@ react-shepherd@6.1.1: dependencies: shepherd.js "13.0.3" -react-style-singleton@^2.2.1, react-style-singleton@^2.2.2: +react-style-singleton@^2.2.2, react-style-singleton@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.3.tgz#4265608be69a4d70cfe3047f2c6c88b2c3ace388" integrity sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ== @@ -10610,7 +10460,7 @@ react-waypoint@^10.3.0: prop-types "^15.0.0" react-is "^17.0.1 || ^18.0.0" -react@^18.3.1: +react@18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== @@ -10988,7 +10838,7 @@ resolve-pathname@^3.0.0: resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.14.2, resolve@^1.22.1, resolve@^1.22.8: +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.14.2, resolve@^1.22.1: version "1.22.10" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== @@ -11045,7 +10895,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-array-concat@^1.1.2, safe-array-concat@^1.1.3: +safe-array-concat@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== @@ -11093,11 +10943,6 @@ sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== -sax@~1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - scheduler@^0.23.2: version "0.23.2" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" @@ -11364,7 +11209,7 @@ side-channel-weakmap@^1.0.2: object-inspect "^1.13.3" side-channel-map "^1.0.1" -side-channel@^1.0.6, side-channel@^1.1.0: +side-channel@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== @@ -11469,10 +11314,10 @@ sockjs@^0.3.24: uuid "^8.3.2" websocket-driver "^0.7.4" -sonner@^1.4.41: - version "1.7.1" - resolved "https://registry.yarnpkg.com/sonner/-/sonner-1.7.1.tgz#737110a3e6211d8d766442076f852ddde1725205" - integrity sha512-b6LHBfH32SoVasRFECrdY8p8s7hXPDn3OHUFbZZbiB1ctLS9Gdh6rpX2dVrpQA0kiL5jcRzDDldwwLkSKk3+QQ== +sonner@1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/sonner/-/sonner-1.7.4.tgz#4c39820db86623800a17115c8970796aa862133a" + integrity sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw== sort-css-media-queries@2.2.0: version "2.2.0" @@ -11492,7 +11337,7 @@ source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0: +source-map@^0.6.0, source-map@~0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -11552,11 +11397,6 @@ srcset@^4.0.0: resolved "https://registry.yarnpkg.com/srcset/-/srcset-4.0.0.tgz#336816b665b14cd013ba545b6fe62357f86e65f4" integrity sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw== -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -11800,30 +11640,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -svg-parser@^2.0.2, svg-parser@^2.0.4: +svg-parser@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== -svgo@^1.2.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" - integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== - dependencies: - chalk "^2.4.1" - coa "^2.0.2" - css-select "^2.0.0" - css-select-base-adapter "^0.1.1" - css-tree "1.0.0-alpha.37" - csso "^4.0.2" - js-yaml "^3.13.1" - mkdirp "~0.5.1" - object.values "^1.1.0" - sax "~1.2.4" - stable "^0.1.8" - unquote "~1.1.1" - util.promisify "~1.0.0" - svgo@^3.0.2, svgo@^3.2.0: version "3.3.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.3.2.tgz#ad58002652dffbb5986fc9716afe52d869ecbda8" @@ -11844,43 +11665,44 @@ swc-loader@^0.2.6: dependencies: "@swc/counter" "^0.1.3" -tailwind-merge@^2.3.0: +tailwind-merge@2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.6.0.tgz#ac5fb7e227910c038d458f396b7400d93a3142d5" integrity sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA== -tailwindcss-animate@^1.0.7: +tailwindcss-animate@1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4" integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA== -tailwindcss@^3.4.13: - version "3.4.17" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.17.tgz#ae8406c0f96696a631c790768ff319d46d5e5a63" - integrity sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og== +tailwindcss@3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.4.tgz#afe3477e7a19f3ceafb48e4b083e292ce0dc0250" + integrity sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ== dependencies: - "@alloc/quick-lru" "^5.2.0" arg "^5.0.2" - chokidar "^3.6.0" + chokidar "^3.5.3" + color-name "^1.1.4" + detective "^5.2.1" didyoumean "^1.2.2" dlv "^1.1.3" - fast-glob "^3.3.2" + fast-glob "^3.2.12" glob-parent "^6.0.2" is-glob "^4.0.3" - jiti "^1.21.6" - lilconfig "^3.1.3" - micromatch "^4.0.8" + lilconfig "^2.0.6" + micromatch "^4.0.5" normalize-path "^3.0.0" object-hash "^3.0.0" - picocolors "^1.1.1" - postcss "^8.4.47" - postcss-import "^15.1.0" - postcss-js "^4.0.1" - postcss-load-config "^4.0.2" - postcss-nested "^6.2.0" - postcss-selector-parser "^6.1.2" - resolve "^1.22.8" - sucrase "^3.35.0" + picocolors "^1.0.0" + postcss "^8.4.18" + postcss-import "^14.1.0" + postcss-js "^4.0.0" + postcss-load-config "^3.1.4" + postcss-nested "6.0.0" + postcss-selector-parser "^6.0.10" + postcss-value-parser "^4.2.0" + quick-lru "^5.1.1" + resolve "^1.22.1" tapable@^1.0.0: version "1.1.3" @@ -11893,7 +11715,7 @@ tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== tar-fs@^2.0.0: - version "2.1.3" + version "2.1.4" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.3.tgz#fb3b8843a26b6f13a08e606f7922875eb1fbbf92" integrity sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg== dependencies: @@ -11903,7 +11725,7 @@ tar-fs@^2.0.0: tar-stream "^2.1.4" tar-fs@^3.0.4: - version "3.0.9" + version "3.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.9.tgz#d570793c6370d7078926c41fa422891566a0b617" integrity sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA== dependencies: @@ -12141,10 +11963,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@~5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" - integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== +typescript@5.5.4: + version "5.5.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" + integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== unbox-primitive@^1.1.0: version "1.1.0" @@ -12271,11 +12093,6 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -unquote@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" - integrity sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg== - upath@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" @@ -12289,6 +12106,14 @@ update-browserslist-db@^1.1.1: escalade "^3.2.0" picocolors "^1.1.1" +update-browserslist-db@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz#7802aa2ae91477f255b86e0e46dbc787a206ad4a" + integrity sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + update-notifier@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-6.0.2.tgz#a6990253dfe6d5a02bd04fbb6a61543f55026b60" @@ -12316,7 +12141,7 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -url-loader@^4.1.1: +url-loader@4.1.1, url-loader@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== @@ -12337,7 +12162,7 @@ use-editable@^2.3.3: resolved "https://registry.yarnpkg.com/use-editable/-/use-editable-2.3.3.tgz#a292fe9ba4c291cd28d1cc2728c75a5fc8d9a33f" integrity sha512-7wVD2JbfAFJ3DK0vITvXBdpd9JAz5BcKAAolsnLBuBn6UDDwBGuCIAGvR3yA2BNKm578vAMVHFCWaOcA+BhhiA== -use-sidecar@^1.1.2: +use-sidecar@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.3.tgz#10e7fd897d130b896e2c546c63a5e8233d00efdb" integrity sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ== @@ -12345,26 +12170,11 @@ use-sidecar@^1.1.2: detect-node-es "^1.1.0" tslib "^2.0.0" -use-sync-external-store@^1.2.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz#adbc795d8eeb47029963016cefdf89dc799fcebc" - integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw== - util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -util.promisify@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" - integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.2" - has-symbols "^1.0.1" - object.getownpropertydescriptors "^2.1.0" - utila@~0.4: version "0.4.0" resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" @@ -12906,21 +12716,21 @@ xml-js@^1.6.11: dependencies: sax "^1.2.4" +xtend@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yaml@^1.10.0, yaml@^1.7.2: +yaml@^1.10.2, yaml@^1.7.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.3.4: - version "2.7.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" - integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== - yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" diff --git a/platform/i18n/CHANGELOG.md b/platform/i18n/CHANGELOG.md index c8918bd1321..ba04aae1014 100644 --- a/platform/i18n/CHANGELOG.md +++ b/platform/i18n/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/i18n @@ -11,7 +11,1096 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22) + + +### Bug Fixes + +* Fixed the tooltip text of the window button, it was mistakenly written in Chinese in the en-us json ([#5513](https://github.com/OHIF/Viewers/issues/5513)) ([8b9445c](https://github.com/OHIF/Viewers/commit/8b9445c442d0a40b7e4393614313192bd006461b)) + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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 @ohif/i18n + + + + + +# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25) + + +### Bug Fixes + +* **i18n:** Add and update i18n Translation for Toolbar Tool Names ([#5392](https://github.com/OHIF/Viewers/issues/5392)) ([7783d0f](https://github.com/OHIF/Viewers/commit/7783d0f558bbb7016c29513b6fe76ed069dbd75c)) + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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 @ohif/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [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/i18n + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/i18n + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/i18n diff --git a/platform/i18n/package.json b/platform/i18n/package.json index cb8a7718a4a..22354b3a7f1 100644 --- a/platform/i18n/package.json +++ b/platform/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/i18n", - "version": "3.11.1", + "version": "3.12.0", "description": "Internationalization library for The OHIF Viewer", "author": "OHIF", "license": "MIT", diff --git a/platform/i18n/src/locales/ar/DataRow.json b/platform/i18n/src/locales/ar/DataRow.json new file mode 100644 index 00000000000..ffb924ef80f --- /dev/null +++ b/platform/i18n/src/locales/ar/DataRow.json @@ -0,0 +1,10 @@ +{ + "Hide": "إخفاء", + "Show": "إظهار", + "Rename": "إعادة تسمية", + "Duplicate": "تكرار", + "Delete": "حذف", + "Change Color": "تغيير اللون", + "Lock": "قفل", + "Unlock": "إلغاء القفل" +} diff --git a/platform/i18n/src/locales/ar/SegmentationTable.json b/platform/i18n/src/locales/ar/SegmentationPanel.json similarity index 68% rename from platform/i18n/src/locales/ar/SegmentationTable.json rename to platform/i18n/src/locales/ar/SegmentationPanel.json index aa023c3ed91..cab16c5a23b 100644 --- a/platform/i18n/src/locales/ar/SegmentationTable.json +++ b/platform/i18n/src/locales/ar/SegmentationPanel.json @@ -14,5 +14,8 @@ "Outline": "حدود", "Rename": "إعادة تسمية", "Segmentation": "تجزئة", - "Size": "الحجم" -} \ No newline at end of file + "Size": "الحجم", + "No segmentations available": "لا توجد تجزئات متاحة", + "Not available on the current viewport": "غير متاح في نافذة العرض الحالية", + "Add segment to enable this tool": "أضف مقطعًا لتمكين هذه الأداة" +} diff --git a/platform/i18n/src/locales/ar/index.js b/platform/i18n/src/locales/ar/index.js index 9f7fc73cabb..dce0afd1b5a 100644 --- a/platform/i18n/src/locales/ar/index.js +++ b/platform/i18n/src/locales/ar/index.js @@ -8,8 +8,9 @@ import ErrorBoundary from './ErrorBoundary.json'; import Header from './Header.json'; import HotkeysValidators from './HotkeysValidators.json'; import MeasurementTable from './MeasurementTable.json'; +import DataRow from './DataRow.json'; import Modes from './Modes.json'; -import SegmentationTable from './SegmentationTable.json'; +import SegmentationPanel from './SegmentationPanel.json'; import SidePanel from './SidePanel.json'; import StudyBrowser from './StudyBrowser.json'; import StudyItem from './StudyItem.json'; @@ -34,8 +35,9 @@ export default { Header, HotkeysValidators, MeasurementTable, + DataRow, Modes, - SegmentationTable, + SegmentationPanel, SidePanel, StudyBrowser, StudyItem, diff --git a/platform/i18n/src/locales/de/DataRow.json b/platform/i18n/src/locales/de/DataRow.json new file mode 100644 index 00000000000..deec77dd117 --- /dev/null +++ b/platform/i18n/src/locales/de/DataRow.json @@ -0,0 +1,10 @@ +{ + "Hide": "Verbergen", + "Show": "Anzeigen", + "Rename": "Umbenennen", + "Duplicate": "Duplizieren", + "Delete": "Löschen", + "Change Color": "Farbe ändern", + "Lock": "Sperren", + "Unlock": "Entsperren" +} diff --git a/platform/i18n/src/locales/de/index.js b/platform/i18n/src/locales/de/index.js index a265fa42add..ca97007f35c 100644 --- a/platform/i18n/src/locales/de/index.js +++ b/platform/i18n/src/locales/de/index.js @@ -5,6 +5,7 @@ import Common from './Common.json'; import DatePicker from './DatePicker.json'; import Header from './Header.json'; import MeasurementTable from './MeasurementTable.json'; +import DataRow from './DataRow.json'; import StudyList from './StudyList.json'; import UserPreferencesModal from './UserPreferencesModal.json'; import ViewportDownloadForm from './ViewportDownloadForm.json'; @@ -18,6 +19,7 @@ export default { DatePicker, Header, MeasurementTable, + DataRow, StudyList, UserPreferencesModal, ViewportDownloadForm, diff --git a/platform/i18n/src/locales/en-US/AboutModal.json b/platform/i18n/src/locales/en-US/AboutModal.json index aa00fc0b67f..fa32db2e449 100644 --- a/platform/i18n/src/locales/en-US/AboutModal.json +++ b/platform/i18n/src/locales/en-US/AboutModal.json @@ -3,6 +3,8 @@ "Browser": "Browser", "Build number": "Build Number", "Commit hash": "Commit hash", + "Commit Hash": "Commit Hash", + "Current Browser & OS": "Current Browser & OS", "Data citation": "Data citation", "Important links": "Important links", "Last master commits": "Latest Master Commits", diff --git a/platform/i18n/src/locales/en-US/Buttons.json b/platform/i18n/src/locales/en-US/Buttons.json index 4a2d259a732..7c979e29d4b 100644 --- a/platform/i18n/src/locales/en-US/Buttons.json +++ b/platform/i18n/src/locales/en-US/Buttons.json @@ -6,10 +6,10 @@ "Bidirectional": "Bidirectional", "Brush": "Brush", "Cine": "Cine", - "CINE": "CINE", "Cancel": "Cancel", "Capture": "Capture", "Circle": "Circle", + "Sphere": "Sphere", "Clear": "Clear", "Coronal": "Coronal", "Crosshairs": "Crosshairs", @@ -39,7 +39,7 @@ "Rectangle": "Rectangle", "Reference Lines": "Reference Lines", "Reset": "$t(Common:Reset)", - "Reset to defaults": "$t(Common:Reset) to Defaults", + "Reset to defaults": "Reset to Defaults", "Rotate Right": "Rotate Right", "Sagittal": "Sagittal", "Save": "Save", @@ -47,5 +47,114 @@ "Stack Image Sync": "Stack Image Sync", "Stop": "$t(Common:Stop)", "Themes": "Themes", - "Zoom": "Zoom" + "Zoom": "Zoom", + "Adjust window/level presets and customize image contrast settings": "Adjust window/level presets and customize image contrast settings", + "Zoom-in": "Zoom-in", + "Cobb Angle": "Cobb Angle", + "Calibration": "Calibration", + "Dicom Tag Browser": "DICOM Tag Browser", + "Magnify Probe": "Magnify Probe", + "Ultrasound Directional": "Ultrasound Directional", + "Window Level Region": "Window Level Region", + "Image Overlay": "Image Overlay", + "Image Slice Sync": "Image Slice Sync", + "Freehand ROI": "Freehand ROI", + "Spline ROI": "Spline ROI", + "Livewire tool": "Livewire tool", + "Livewire Contour": "Livewire Contour", + "Segment Label Display": "Segment Label Display", + "Data Overlay": "Data Overlay", + "Configure data overlay options and manage foreground/background display sets": "Configure data overlay options and manage foreground/background display sets", + "Orientation": "Orientation", + "Change viewport orientation between axial, sagittal, coronal and reformat planes": "Change viewport orientation between axial, sagittal, coronal and reformat planes", + "Status": "Status", + "Navigation": "Navigation", + "Navigate between segments/measurements and manage their visibility": "Navigate between segments/measurements and manage their visibility", + "Tracking Status": "Tracking Status", + "View and manage tracking status of measurements and annotations": "View and manage tracking status of measurements and annotations", + "Advanced Window Level": "Advanced Window Level", + "Advanced window/level settings with manual controls and presets": "Advanced window/level settings with manual controls and presets", + "Threshold": "Threshold", + "Image threshold settings": "Image threshold settings", + "Opacity": "Opacity", + "Image opacity settings": "Image opacity settings", + "Colorbar": "Colorbar", + "Enable position synchronization on stack viewports": "Enable position synchronization on stack viewports", + "Show Reference Lines": "Show Reference Lines", + "Toggle Image Overlay": "Toggle Image Overlay", + "Calibration Line": "Calibration Line", + "Ellipse ROI": "Ellipse ROI", + "Rectangle ROI": "Rectangle ROI", + "Circle Tool": "Circle Tool", + "Click to show or hide segment labels when hovering with your mouse.": "Click to show or hide segment labels when hovering with your mouse.", + "Arrow Annotate": "Arrow Annotate", + "Bidirectional Tool": "Bidirectional Tool", + "Bone": "Bone", + "Brain": "Brain", + "Dismiss Aspect": "Dismiss Aspect", + "Ellipse Tool": "Ellipse Tool", + "Flip Horizontal": "Flip Horizontal", + "Invert Colors": "Invert Colors", + "Keep Aspect": "Keep Aspect", + "Length Tool": "Length Tool", + "Liver": "Liver", + "Lung": "Lung", + "More Measure Tools": "More Measure Tools", + "Reset View": "Reset View", + "Rotate +90": "Rotate +90", + "Soft tissue": "Soft tissue", + "W/L Presets": "W/L Presets", + "Window Level": "Window Level", + "Point": "Point", + "Point Tool": "Point Tool", + "Polygon": "Polygon", + "Polygon Tool": "Polygon Tool", + "Box": "Box", + "Box Tool": "Box Tool", + "Freehand Polygon": "Freehand Polygon", + "Freehand Polygon Tool": "Freehand Polygon Tool", + "Freehand Line": "Freehand Line", + "Freehand Line Tool": "Freehand Line Tool", + "Line": "Line", + "Line Tool": "Line Tool", + "3D Rotate": "3D Rotate", + "Shape": "Shape", + "MPR": "MPR", + "Rectangle ROI Threshold": "Rectangle ROI Threshold", + "Select the PT Axial to enable this tool": "Select the PT Axial to enable this tool", + "Create new segmentation to enable this tool.": "Create new segmentation to enable this tool.", + "Freehand Segmentation": "Freehand Segmentation", + "Spline Contour Segmentation Tool": "Spline Contour Segmentation Tool", + "Sculptor Tool": "Sculptor Tool", + "Interpolate Contours": "Interpolate Contours", + "Marker Mode": "Marker Mode", + "Include": "Include", + "Exclude": "Exclude", + "Clear Markers": "Clear Markers", + "Radius (mm)": "Radius (mm)", + "Dynamic": "Dynamic", + "Range": "Range", + "Spline Type": "Spline Type", + "Catmull Rom Spline": "Catmull Rom Spline", + "Linear Spline": "Linear Spline", + "B-Spline": "B-Spline", + "Simplified Spline": "Simplified Spline", + "Dynamic Cursor Size": "Dynamic Cursor Size", + "No segmentations available": "No segmentations available", + "Threshold Tool": "Threshold Tool", + "Threshold Tools": "Threshold Tools", + "Select a 3D viewport to enable this tool": "Select a 3D viewport to enable this tool", + "Not available on the current viewport": "Not available on the current viewport", + "Select an MPR viewport to enable this tool": "Select an MPR viewport to enable this tool", + "Interpolate Labelmap": "Interpolate Labelmap", + "Segment Bidirectional": "Segment Bidirectional", + "Tool not available for this modality": "Tool not available for this modality", + "One Click Segment": "One Click Segment", + "Labelmap Assist": "Labelmap Assist", + "Marker Guided Labelmap": "Marker Guided Labelmap", + "Eraser": "Eraser", + "Shapes": "Shapes", + "Create new segmentation to enable shapes tool.": "Create new segmentation to enable shapes tool.", + "US Pleura B-line Annotation": "US Pleura B-line Annotation", + "Arrow Annotate Tool": "Arrow Annotate Tool" } diff --git a/platform/i18n/src/locales/en-US/CaptureViewportModal.json b/platform/i18n/src/locales/en-US/CaptureViewportModal.json new file mode 100644 index 00000000000..1a321517c75 --- /dev/null +++ b/platform/i18n/src/locales/en-US/CaptureViewportModal.json @@ -0,0 +1,9 @@ +{ + "File name": "File name", + "Image size": "Image size", + "Image size in pixels": "Image size in pixels", + "Include annotations": "Include annotations", + "Include warning message": "Include warning message", + "Width": "Width", + "Height": "Height" +} diff --git a/platform/i18n/src/locales/en-US/Colormaps.json b/platform/i18n/src/locales/en-US/Colormaps.json new file mode 100644 index 00000000000..99ec4d9d529 --- /dev/null +++ b/platform/i18n/src/locales/en-US/Colormaps.json @@ -0,0 +1,15 @@ +{ + "Grayscale": "Grayscale", + "X Ray": "X Ray", + "Isodose": "Isodose", + "hsv": "HSV", + "hot_iron": "Hot Iron", + "red_hot": "Red Hot", + "s_pet": "PET", + "perfusion": "Perfusion", + "rainbow_2": "Rainbow 2", + "suv": "SUV", + "ge_256": "GE 256", + "ge": "GE", + "siemens": "Siemens" +} diff --git a/platform/i18n/src/locales/en-US/Common.json b/platform/i18n/src/locales/en-US/Common.json index a0b5ce3b436..5f6a773218d 100644 --- a/platform/i18n/src/locales/en-US/Common.json +++ b/platform/i18n/src/locales/en-US/Common.json @@ -18,5 +18,12 @@ "Show": "Show", "Stop": "Stop", "StudyDate": "Study Date", - "Yes": "Yes" + "Yes": "Yes", + "Cancel": "Cancel", + "Save": "Save", + "localDateFormat": "DD-MMM-YYYY", + "Back": "Back", + "Foreground": "Foreground", + "SELECT A FOREGROUND": "SELECT A FOREGROUND", + "SELECT A SEGMENTATION": "SELECT A SEGMENTATION" } diff --git a/platform/i18n/src/locales/en-US/DataRow.json b/platform/i18n/src/locales/en-US/DataRow.json new file mode 100644 index 00000000000..de106fe4fe9 --- /dev/null +++ b/platform/i18n/src/locales/en-US/DataRow.json @@ -0,0 +1,10 @@ +{ + "Hide": "Hide", + "Show": "Show", + "Rename": "Rename", + "Duplicate": "Duplicate", + "Delete": "Delete", + "Change Color": "Change Color", + "Lock": "Lock", + "Unlock": "Unlock" +} diff --git a/platform/i18n/src/locales/en-US/DatePicker.json b/platform/i18n/src/locales/en-US/DatePicker.json index 3add53f9e71..4b880353313 100644 --- a/platform/i18n/src/locales/en-US/DatePicker.json +++ b/platform/i18n/src/locales/en-US/DatePicker.json @@ -5,5 +5,9 @@ "Last 7 days": "Last 7 days", "Last 30 days": "Last 30 days", "Start Date": "Start Date", - "Today": "Today" + "Today": "Today", + "Previous Month": "Previous Month", + "Next Month": "Next Month", + "Select Month": "Select Month", + "Select Year": "Select Year" } diff --git a/platform/i18n/src/locales/en-US/Hps.json b/platform/i18n/src/locales/en-US/Hps.json new file mode 100644 index 00000000000..e5329329ab3 --- /dev/null +++ b/platform/i18n/src/locales/en-US/Hps.json @@ -0,0 +1,14 @@ +{ + "MPR": "MPR", + "3D four up": "3D four up", + "Frame View": "Frame View", + "Frame view for the active series": "Frame view for the active series", + "3D main": "3D main", + "mpr": "mpr", + "3D only": "3D only", + "3D primary": "3D primary", + "Axial Primary": "Axial Primary", + "Compare Two Studies": "Compare Two Studies", + "Compare two studies in various layouts": "Compare two studies in various layouts", + "Mammography Breast Screening": "Mammography Breast Screening" +} diff --git a/platform/i18n/src/locales/en-US/MeasurementTable.json b/platform/i18n/src/locales/en-US/MeasurementTable.json index 424317274f8..7fd54073e87 100644 --- a/platform/i18n/src/locales/en-US/MeasurementTable.json +++ b/platform/i18n/src/locales/en-US/MeasurementTable.json @@ -8,6 +8,15 @@ "NonTargets": "NonTargets", "Relabel": "Relabel", "Targets": "Targets", + "Rename": "Rename", + "Duplicate": "Duplicate", + "Change Color": "Change Color", + "Lock": "Lock", + "Unlock": "Unlock", + "Hide": "Hide", + "Show": "Show", + "Create SR": "Create SR", + "empty": "(empty)", "Track measurements for this series?": "Track measurements for this series?", "Do you want to add this measurement to the existing report?": "Do you want to add this measurement to the existing report?", "You have existing tracked measurements. What would you like to do with your existing tracked measurements?": "You have existing tracked measurements. What would you like to do with your existing tracked measurements?", diff --git a/platform/i18n/src/locales/en-US/Modes.json b/platform/i18n/src/locales/en-US/Modes.json index 813dfe9d63b..29d39c36e49 100644 --- a/platform/i18n/src/locales/en-US/Modes.json +++ b/platform/i18n/src/locales/en-US/Modes.json @@ -4,5 +4,6 @@ "Basic Viewer": "Basic Viewer", "Microscopy": "Microscopy", "Segmentation": "Segmentation", - "Total Metabolic Tumor Volume": "Total Metabolic Tumor Volume" + "Total Metabolic Tumor Volume": "Total Metabolic Tumor Volume", + "US Pleura B-line Annotations": "US Pleura B-line Annotations" } diff --git a/platform/i18n/src/locales/en-US/Onboarding.json b/platform/i18n/src/locales/en-US/Onboarding.json new file mode 100644 index 00000000000..d3e8e7de1ef --- /dev/null +++ b/platform/i18n/src/locales/en-US/Onboarding.json @@ -0,0 +1,27 @@ +{ + "Scrolling Through Images": "Scrolling Through Images", + "You can scroll through the images using the mouse wheel or scrollbar.": "You can scroll through the images using the mouse wheel or scrollbar.", + "Zooming In and Out": "Zooming In and Out", + "You can zoom the images using the right click.": "You can zoom the images using the right click.", + "Panning the Image": "Panning the Image", + "You can pan the images using the middle click.": "You can pan the images using the middle click.", + "Adjusting Window Level": "Adjusting Window Level", + "You can modify the window level using the left click.": "You can modify the window level using the left click.", + "Using the Measurement Tools": "Using the Measurement Tools", + "You can measure the length of a region using the Length tool.": "You can measure the length of a region using the Length tool.", + "Drawing Length Annotations": "Drawing Length Annotations", + "Use the length tool on the viewport to measure the length of a region.": "Use the length tool on the viewport to measure the length of a region.", + "Tracking Measurements in the Panel": "Tracking Measurements in the Panel", + "Click yes to track the measurements in the measurement panel.": "Click yes to track the measurements in the measurement panel.", + "Opening the Measurements Panel": "Opening the Measurements Panel", + "Click the measurements button to open the measurements panel.": "Click the measurements button to open the measurements panel.", + "Scrolling Away from a Measurement": "Scrolling Away from a Measurement", + "Scroll the images using the mouse wheel away from the measurement.": "Scroll the images using the mouse wheel away from the measurement.", + "Jumping to Measurements in the Panel": "Jumping to Measurements in the Panel", + "Click the measurement in the measurement panel to jump to it.": "Click the measurement in the measurement panel to jump to it.", + "Changing Layout": "Changing Layout", + "You can change the layout of the viewer using the layout button.": "You can change the layout of the viewer using the layout button.", + "Selecting the MPR Layout": "Selecting the MPR Layout", + "Select the MPR layout to view the images in MPR mode.": "Select the MPR layout to view the images in MPR mode.", + "Skip all": "Skip all" +} diff --git a/platform/i18n/src/locales/en-US/PanelSUV.json b/platform/i18n/src/locales/en-US/PanelSUV.json new file mode 100644 index 00000000000..7a280d699e0 --- /dev/null +++ b/platform/i18n/src/locales/en-US/PanelSUV.json @@ -0,0 +1,10 @@ +{ + "Patient Information": "Patient Information", + "Patient Sex": "Patient Sex", + "Weight": "Weight", + "Total Dose": "Total Dose", + "Half Life": "Half Life", + "Injection Time": "Injection Time", + "Acquisition Time": "Acquisition Time", + "Reload Data": "Reload Data" +} diff --git a/platform/i18n/src/locales/en-US/ROIThresholdConfiguration.json b/platform/i18n/src/locales/en-US/ROIThresholdConfiguration.json new file mode 100644 index 00000000000..d0f095ecbeb --- /dev/null +++ b/platform/i18n/src/locales/en-US/ROIThresholdConfiguration.json @@ -0,0 +1,11 @@ +{ + "Max": "Max", + "Range": "Range", + "Start": "Start", + "End": "End", + "Percentage of Max SUV": "Percentage of Max SUV", + "Lower & Upper Ranges": "Lower & Upper Ranges", + "Run": "Run", + "Threshold Tools": "Threshold Tools", + "ThresholdRange": "Threshold Range" +} diff --git a/platform/i18n/src/locales/en-US/SegmentationPanel.json b/platform/i18n/src/locales/en-US/SegmentationPanel.json new file mode 100644 index 00000000000..52f0928e6b8 --- /dev/null +++ b/platform/i18n/src/locales/en-US/SegmentationPanel.json @@ -0,0 +1,67 @@ +{ + "Active": "Active", + "Add new segmentation": "Add new segmentation", + "Add segment": "Add segment", + "Add segmentation": "Add segmentation", + "Contour": "Contour", + "Contour tools": "Contour tools", + "Contour Segmentations": "Contour segmentations", + "Delete": "Delete", + "Display inactive segmentations": "Display inactive segmentations", + "Add Segment": "Add Segment", + "Download DICOM RTSS": "Download DICOM RTSS", + "Export DICOM SEG": "Export DICOM SEG", + "Download DICOM SEG": "Download DICOM SEG", + "Download DICOM RTSTRUCT": "Download DICOM RTSTRUCT", + "Fill": "Fill", + "Inactive segmentations": "Inactive segmentations", + "Labelmap": "Label map", + "Labelmap tools": "Label map tools", + "Labelmap Segmentations": "Label map segmentations", + "Opacity": "Opacity", + "Border": "Border", + "Show": "Show", + "Fill & Outline": "Fill & Outline", + "Outline Only": "Outline Only", + "Fill Only": "Fill Only", + "Outline": "Outline", + "Rename": "Rename", + "Segmentation": "Segmentation", + "Segmentations": "Segmentations", + "Segmentation not supported": "Segmentation not supported", + "Size": "Size", + "Preview edits before creating": "Preview edits before creating", + "Use center as segment index": "Use center as segment index", + "Hover on segment border to activate": "Hover on segment border to activate", + "Show segment name on hover": "Show segment name on hover", + "Create New Segmentation": "Create New Segmentation", + "Manage Current Segmentation": "Manage Current Segmentation", + "Remove from Viewport": "Remove from Viewport", + "Download & Export": "Download & Export", + "Download": "Download", + "Export": "Export", + "CSV Report": "CSV Report", + "DICOM SEG": "DICOM SEG", + "DICOM RTSS": "DICOM RTSS" + , + "Fill contour holes": "Fill contour holes", + "Fill Holes": "Fill Holes", + "Remove Small Contours": "Remove Small Contours", + "Area Threshold": "Area Threshold", + "Create New Segment from Holes": "Create New Segment from Holes", + "Create New Segment": "Create New Segment", + "Smooth all edges": "Smooth all edges", + "Smooth Edges": "Smooth Edges", + "Remove extra points": "Remove extra points", + "Remove Points": "Remove Points", + "Merge": "Merge", + "Intersect": "Intersect", + "Subtract": "Subtract", + "Select a segment": "Select a segment", + "Select a segmentation": "Select a segmentation", + "Create a new segment": "Create a new segment", + "New segment name": "New segment name", + "No segmentations available": "No segmentations available", + "Not available on the current viewport": "Not available on the current viewport", + "Add segment to enable this tool": "Add segment to enable this tool" +} diff --git a/platform/i18n/src/locales/en-US/SegmentationTable.json b/platform/i18n/src/locales/en-US/SegmentationTable.json deleted file mode 100644 index 8d73016f326..00000000000 --- a/platform/i18n/src/locales/en-US/SegmentationTable.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "Active": "Active", - "Add new segmentation": "Add new segmentation", - "Add segment": "Add segment", - "Add segmentation": "Add segmentation", - "Delete": "Delete", - "Display inactive segmentations": "Display inactive segmentations", - "Export DICOM SEG": "Export DICOM SEG", - "Download DICOM SEG": "Download DICOM SEG", - "Download DICOM RTSTRUCT": "Download DICOM RTSTRUCT", - "Fill": "Fill", - "Inactive segmentations": "Inactive segmentations", - "Opacity": "Opacity", - "Outline": "Outline", - "Rename": "Rename", - "Segmentation": "Segmentation", - "Size": "Size" -} diff --git a/platform/i18n/src/locales/en-US/StudyBrowser.json b/platform/i18n/src/locales/en-US/StudyBrowser.json index b12c087f518..3fe466e34c6 100644 --- a/platform/i18n/src/locales/en-US/StudyBrowser.json +++ b/platform/i18n/src/locales/en-US/StudyBrowser.json @@ -1,5 +1,12 @@ { "Primary": "Primary", "Recent": "Recent", - "All": "All" + "All": "All", + "Tracked Series": "Tracked Series", + "Tag Browser": "Tag Browser", + "Add as Layer": "Add as Layer", + "Series Number": "Series Number", + "Series Date": "Series Date", + "Thumbnail Double Click": "Thumbnail Double Click", + "The selected display sets could not be added to the viewport.": "The selected display sets could not be added to the viewport." } diff --git a/platform/i18n/src/locales/en-US/StudyList.json b/platform/i18n/src/locales/en-US/StudyList.json index c64afdb7a3c..37dc064f4a2 100644 --- a/platform/i18n/src/locales/en-US/StudyList.json +++ b/platform/i18n/src/locales/en-US/StudyList.json @@ -14,6 +14,7 @@ "PatientName": "Patient Name", "Previous": "< Back", "Results per page": "Results per page", + "Studies": "Studies", "StudyDate": "Study Date", "StudyList": "Study List", "Upload": "Upload" diff --git a/platform/i18n/src/locales/en-US/ToolbarLayoutSelector.json b/platform/i18n/src/locales/en-US/ToolbarLayoutSelector.json new file mode 100644 index 00000000000..c0faf0eecc4 --- /dev/null +++ b/platform/i18n/src/locales/en-US/ToolbarLayoutSelector.json @@ -0,0 +1,9 @@ +{ + "Change layout": "Change layout", + "Common": "Common", + "Advanced": "Advanced", + "Custom": "Custom", + "Hover to select": "Hover to select", + "rows and columns": "rows and columns", + "Click to apply": "Click to apply" +} diff --git a/platform/i18n/src/locales/en-US/Tools.json b/platform/i18n/src/locales/en-US/Tools.json new file mode 100644 index 00000000000..9033d5ddda3 --- /dev/null +++ b/platform/i18n/src/locales/en-US/Tools.json @@ -0,0 +1,13 @@ +{ + "Segmentation": "Segmentation", + "Segment": "Segment", + "Download High Quality Image": "Download High Quality Image", + "Edit Segment Label": "Edit Segment Label", + "Enter new label": "Enter new label", + "Segment Color": "Segment Color", + "Edit Measurement Label": "Edit Measurement Label", + "Edit Arrow Text": "Edit Arrow Text", + "Enter new text": "Enter new text", + "Download Image": "Download Image", + "Image cannot be downloaded": "Image cannot be downloaded" +} diff --git a/platform/i18n/src/locales/en-US/USAnnotationPanel.json b/platform/i18n/src/locales/en-US/USAnnotationPanel.json new file mode 100644 index 00000000000..d540f083d44 --- /dev/null +++ b/platform/i18n/src/locales/en-US/USAnnotationPanel.json @@ -0,0 +1,17 @@ +{ + "Workflow": "Workflow", + "Depth guide toggle": "Depth guide toggle", + "Show pleura percentage": "Show pleura percentage", + "Sector Annotations": "Sector Annotations", + "Pleura line": "Pleura line", + "B-line": "B-line", + "B-line annotation": "B-line annotation", + "Pleura annotation": "Pleura annotation", + "Show Overlay": "Show Overlay", + "Annotated Frames": "Annotated Frames", + "Frame": "Frame", + "Pleura lines": "Pleura lines", + "B-lines": "B-lines", + "JSON": "JSON", + "Annotations": "Annotations" +} diff --git a/platform/i18n/src/locales/en-US/UserPreferencesModal.json b/platform/i18n/src/locales/en-US/UserPreferencesModal.json index 5e733546818..0d5091c391b 100644 --- a/platform/i18n/src/locales/en-US/UserPreferencesModal.json +++ b/platform/i18n/src/locales/en-US/UserPreferencesModal.json @@ -5,5 +5,80 @@ "ResetDefaultMessage": "Preferences successfully reset to default.
You must Save to perform this action.", "Save": "$t(Buttons:Save)", "SaveMessage": "Preferences saved", - "User preferences": "User Preferences" + "User preferences": "User Preferences", + "Language": "Language", + "Select language": "Select language", + "Hotkeys": "Hotkeys", + "Zoom": "Zoom", + "Zoom In": "Zoom In", + "Zoom Out": "Zoom Out", + "Zoom to Fit": "Zoom to Fit", + "Rotate Right": "Rotate Right", + "Rotate Left": "Rotate Left", + "Flip Horizontally": "Flip Horizontally", + "Flip Vertically": "Flip Vertically", + "Cine": "Cine", + "Invert": "Invert", + "Next Image Viewport": "Next Image Viewport", + "Previous Image Viewport": "Previous Image Viewport", + "Previous Series": "Previous Series", + "Next Series": "Next Series", + "Next Stage": "Next Stage", + "Previous Stage": "Previous Stage", + "Next Image": "Next Image", + "Previous Image": "Previous Image", + "First Image": "First Image", + "Last Image": "Last Image", + "Reset": "Reset", + "Cancel Measurement": "Cancel Measurement", + "W/L Preset 1": "W/L Preset 1", + "W/L Preset 2": "W/L Preset 2", + "W/L Preset 3": "W/L Preset 3", + "W/L Preset 4": "W/L Preset 4", + "Delete Annotation": "Delete Annotation", + "Accept Preview": "Accept Preview", + "Reject Preview": "Reject Preview", + "Undo": "Undo", + "Redo": "Redo", + "Interpolate Scroll": "Interpolate Scroll", + "Increase Brush Size": "Increase Brush Size", + "Decrease Brush Size": "Decrease Brush Size", + "Eraser": "Eraser", + "Brush": "Brush", + "Add New Segment": "Add New Segment", + "Press keys": "Press keys...", + "LanguageName.en-US": "English (US)", + "LanguageName.fr": "French", + "HotkeyKeys.ctrl": "Ctrl", + "HotkeyKeys.shift": "Shift", + "HotkeyKeys.alt": "Alt", + "HotkeyKeys.option": "Option", + "HotkeyKeys.meta": "Cmd", + "HotkeyKeys.enter": "Enter", + "HotkeyKeys.esc": "Esc", + "HotkeyKeys.space": "Space", + "HotkeyKeys.tab": "Tab", + "HotkeyKeys.backspace": "Backspace", + "HotkeyKeys.delete": "Delete", + "HotkeyKeys.insert": "Insert", + "HotkeyKeys.home": "Home", + "HotkeyKeys.end": "End", + "HotkeyKeys.pageup": "Page Up", + "HotkeyKeys.pagedown": "Page Down", + "HotkeyKeys.up": "Up Arrow", + "HotkeyKeys.down": "Down Arrow", + "HotkeyKeys.left": "Left Arrow", + "HotkeyKeys.right": "Right Arrow", + "HotkeyKeys.capslock": "Caps Lock", + "HotkeyKeys.plus": "Plus", + "HotkeyKeys.minus": "Minus", + "HotkeyKeys.comma": "Comma", + "HotkeyKeys.period": "Period", + "HotkeyKeys.slash": "Slash", + "HotkeyKeys.backslash": "Backslash", + "HotkeyKeys.semicolon": "Semicolon", + "HotkeyKeys.quote": "Quote", + "HotkeyKeys.backquote": "Backtick", + "HotkeyKeys.bracketleft": "Left Bracket", + "HotkeyKeys.bracketright": "Right Bracket" } diff --git a/platform/i18n/src/locales/en-US/WindowLevelActionMenu.json b/platform/i18n/src/locales/en-US/WindowLevelActionMenu.json index 9eba89b7eaf..21db62e8ce7 100644 --- a/platform/i18n/src/locales/en-US/WindowLevelActionMenu.json +++ b/platform/i18n/src/locales/en-US/WindowLevelActionMenu.json @@ -1,5 +1,19 @@ { "Back to Display Options": "Back to Display Options", - "Modality Presets": "{{modality}} Presets", - "Modality Window Presets": "{{modality}} Window Presets" + "Modality Presets": "Modality Presets", + "Modality Window Presets": "Modality Window Presets", + "Display Color bar": "Display Color bar", + "Color LUT": "Color LUT", + "Preview in viewport": "Preview in viewport", + "Grayscale": "Grayscale", + "Rendering Options": "Rendering Options", + "Rendering Presets": "Rendering Presets", + "Search all": "Search all", + "Quality": "Quality", + "Lighting": "Lighting", + "Shade": "Shade", + "Ambient": "Ambient", + "Diffuse": "Diffuse", + "Specular": "Specular", + "Shift": "Shift" } diff --git a/platform/i18n/src/locales/en-US/index.js b/platform/i18n/src/locales/en-US/index.js index 6004ac65520..eab6880903f 100644 --- a/platform/i18n/src/locales/en-US/index.js +++ b/platform/i18n/src/locales/en-US/index.js @@ -8,8 +8,9 @@ import ErrorBoundary from './ErrorBoundary.json'; import Header from './Header.json'; import HotkeysValidators from './HotkeysValidators.json'; import MeasurementTable from './MeasurementTable.json'; +import DataRow from './DataRow.json'; import Modes from './Modes.json'; -import SegmentationTable from './SegmentationTable.json'; +import SegmentationPanel from './SegmentationPanel.json'; import SidePanel from './SidePanel.json'; import StudyBrowser from './StudyBrowser.json'; import StudyItem from './StudyItem.json'; @@ -21,6 +22,15 @@ import UserPreferencesModal from './UserPreferencesModal.json'; import ViewportDownloadForm from './ViewportDownloadForm.json'; import Messages from './Messages.json'; import WindowLevelActionMenu from './WindowLevelActionMenu.json'; +import CaptureViewportModal from './CaptureViewportModal.json'; +import Hps from './Hps.json'; +import ToolbarLayoutSelector from './ToolbarLayoutSelector.json'; +import Tools from './Tools.json'; +import Onboarding from './Onboarding.json'; +import Colormaps from './Colormaps.json'; +import PanelSUV from './PanelSUV.json'; +import ROIThresholdConfiguration from './ROIThresholdConfiguration.json'; +import USAnnotationPanel from './USAnnotationPanel.json'; export default { 'en-US': { @@ -34,8 +44,9 @@ export default { Header, HotkeysValidators, MeasurementTable, + DataRow, Modes, - SegmentationTable, + SegmentationPanel, SidePanel, StudyBrowser, StudyItem, @@ -47,5 +58,14 @@ export default { ViewportDownloadForm, Messages, WindowLevelActionMenu, + CaptureViewportModal, + Hps, + ToolbarLayoutSelector, + Tools, + Onboarding, + Colormaps, + PanelSUV, + ROIThresholdConfiguration, + USAnnotationPanel, }, }; diff --git a/platform/i18n/src/locales/es/DataRow.json b/platform/i18n/src/locales/es/DataRow.json new file mode 100644 index 00000000000..c7ec178edc3 --- /dev/null +++ b/platform/i18n/src/locales/es/DataRow.json @@ -0,0 +1,10 @@ +{ + "Hide": "Ocultar", + "Show": "Mostrar", + "Rename": "Renombrar", + "Duplicate": "Duplicar", + "Delete": "Borrar", + "Change Color": "Cambiar color", + "Lock": "Bloquear", + "Unlock": "Desbloquear" +} diff --git a/platform/i18n/src/locales/es/index.js b/platform/i18n/src/locales/es/index.js index 80a1aaa94a6..b501e8597b8 100644 --- a/platform/i18n/src/locales/es/index.js +++ b/platform/i18n/src/locales/es/index.js @@ -5,6 +5,7 @@ import Common from './Common.json'; import DatePicker from './DatePicker.json'; import Header from './Header.json'; import MeasurementTable from './MeasurementTable.json'; +import DataRow from './DataRow.json'; import SidePanel from './SidePanel.json'; import StudyBrowser from './StudyBrowser.json'; import StudyList from './StudyList.json'; @@ -20,6 +21,7 @@ export default { DatePicker, Header, MeasurementTable, + DataRow, SidePanel, StudyBrowser, StudyList, diff --git a/platform/i18n/src/locales/fr/AboutModal.json b/platform/i18n/src/locales/fr/AboutModal.json new file mode 100644 index 00000000000..2158fddbd4b --- /dev/null +++ b/platform/i18n/src/locales/fr/AboutModal.json @@ -0,0 +1,16 @@ +{ + "About OHIF Viewer": "À propos d'OHIF Viewer", + "Browser": "Navigateur", + "Build number": "Numéro de build", + "Last master commits": "Derniers commits sur master", + "More details": "Plus de détails", + "Name": "Nom", + "OS": "Système d'exploitation", + "Report an issue": "Signaler un problème", + "Repository URL": "URL du dépôt", + "Value": "Valeur", + "Version information": "Informations sur la version", + "Visit the forum": "Visiter le forum", + "Commit Hash": "Numéro hash du commit", + "Current Browser & OS": "Navigateur et système d'exploitation actuels" +} diff --git a/platform/i18n/src/locales/fr/Buttons.json b/platform/i18n/src/locales/fr/Buttons.json index c97dfaac667..460b7aa1a74 100644 --- a/platform/i18n/src/locales/fr/Buttons.json +++ b/platform/i18n/src/locales/fr/Buttons.json @@ -1,42 +1,131 @@ { "Acquired": "Acquis", "Angle": "Angle", + "Annotation": "Annotation", + "Arrow Annotate": "Annotation en flèche", "Axial": "Axial", - "Bidirectional": "Bi-directionel", + "Bidirectional Tool": "Outil bidirectionnel", + "Bidirectional": "Bidirectionnel", + "Bone": "Osseux", + "Brain": "Cérébral", "Brush": "Brosse", - "CINE": "Ciné", "Cancel": "Annuler", + "Capture": "Capturer", + "Cine": "Ciné", + "CINE": "Ciné", "Circle": "Cercle", + "Sphere": "Sphère", + "Cobb Angle": "Angle Cobb", "Clear": "Effacer", "Coronal": "Coronal", "Crosshairs": "Repère", + "Dicom Tag Browser": "Explorateur DICOM", + "Dismiss Aspect": "Désactiver l'aspect", + "Ellipse Tool": "Outil ellipse", "Ellipse": "Ellipse", "Elliptical": "Elliptique", - "Flip H": "Flip H", - "Flip V": "Flip V", + "Flip H": "Retourner H", + "Flip Horizontal": "Retourner horizontalement", + "Flip Horizontally": "Retourner horizontalement", + "Flip V": "Retourner V", "Freehand": "Main levée", + "Freehand ROI": "ROI à main levée", + "Spline ROI": "ROI spline", + "Livewire tool": "Outil Livewire", + "Livewire Contour": "Contour Livewire", + "Grid Layout": "Disposition en grille", + "Invert Colors": "Inverser les couleurs", "Invert": "Inverser", + "Image Overlay": "Superposition d'image", + "Image Slice Sync": "Synchronisation des coupes d'image", + "Keep Aspect": "Conserver les proportions", "Layout": "$t(Common:Layout)", + "Length Tool": "Outil de longueur", "Length": "Longueur", "Levels": "Niveaux", + "Liver": "Foie", + "Lung": "Poumon", "Magnify": "Agrandir", + "Magnify Probe": "Sonde de grossissement", "Manual": "Manuel", "Measurements": "Mesures", + "More Measure Tools": "Plus d'outils de mesure", + "More Tools": "Plus d'outils", "More": "$t(Common:More)", "Next": "$t(Common:Next)", "Pan": "Déplacer", "Play": "$t(Common:Play)", "Previous": "$t(Common:Previous)", "Probe": "Sonde", - "ROI Window": "ROI fenêtrage", + "Colorbar": "Barre de couleurs", "Rectangle": "Rectangle", + "Rectangle ROI Threshold": "Seuil ROI rectangle", + "Reference Lines": "Lignes de référence", + "Reset to defaults": "Réinitialiser les valeurs par défaut", + "Reset View": "Réinitialiser la vue", "Reset": "$t(Common:Reset)", - "Reset to defaults": "Valeurs d'usine", + "ROI Window": "Fenêtre ROI", + "Rotate +90": "Rotation +90°", "Rotate Right": "Tourner à droite", "Sagittal": "Sagittal", "Save": "Sauvegarder", - "Stack Scroll": "Défilement", + "Soft tissue": "Tissus mous", + "Stack Image Sync": "Synchronisation des piles", + "Stack Scroll": "Défilement de pile", "Stop": "$t(Common:Stop)", - "Themes": "Themes", - "Zoom": "Zoom" + "Themes": "Thèmes", + "Toggle Image Overlay": "Basculer la superposition d'image", + "Click to show or hide segment labels when hovering with your mouse.": "Cliquez pour afficher ou masquer les étiquettes de segment lorsque vous survolez avec la souris.", + "Ultrasound Directional": "Ultrason directionnel", + "Window Level Region": "Fenêtre/Niveau régional", + "W/L Presets": "Préréglages W/L", + "Window Level": "Fenêtre/Niveau", + "Segment Label Display": "Affichage des étiquettes de segment", + "Zoom": "Zoom", + "3D Rotate": "Rotation 3D", + "Shape": "Forme", + "Eraser": "Gomme", + "Threshold Tool": "Outil seuil", + "Threshold Tools": "Outils", + "Threshold": "Seuil", + "Shapes": "Formes", + "Not available on the current viewport": "Non disponible sur la fenêtre d'affichage actuelle", + "Select an MPR viewport to enable this tool": "Sélectionnez une vue MPR pour activer cet outil", + "Select a 3D viewport to enable this tool": "Sélectionnez une vue 3D pour activer cet outil", + "Select the PT Axial to enable this tool": "Sélectionnez la coupe axiale PT pour activer cet outil", + "Tool not available for this modality": "Outil non disponible pour cette modalité", + "Create new segmentation to enable this tool.": "Créez une segmentation pour activer cet outil.", + "Freehand Segmentation": "Segmentation à main levée", + "Spline Contour Segmentation Tool": "Outil de segmentation de contour spline", + "Sculptor Tool": "Outil de sculpture", + "Interpolate Contours": "Interpoler les contours", + "Spline Type": "Type de spline", + "Catmull Rom Spline": "Spline Catmull-Rom", + "Linear Spline": "Spline linéaire", + "B-Spline": "B-spline", + "Simplified Spline": "Spline simplifiée", + "Dynamic Cursor Size": "Taille du curseur dynamique", + "No segmentations available": "Aucune segmentation disponible", + "US Pleura B-line Annotation": "US Pleura B-line Annotation", + "Arrow Annotate Tool": "Arrow Annotate Tool", + "Labelmap Assist": "Assistance Labelmap", + "Interpolate Labelmap": "Interpolation du labelmap", + "One Click Segment": "Segmentation en un clic", + "Marker Guided Labelmap": "Labelmap guidé par marqueurs", + "Labelmap Edit with Contour Tool": "Outil d'édition de labelmap avec contour", + "Toggle AI assistance for segmenting nearby slices. After drawing on a slice, scroll to preview predictions. Press Enter to accept or Esc to skip.": "Activer l'assistance IA pour segmenter les coupes voisines. Après avoir dessiné sur une coupe, faites défiler pour prévisualiser les prédictions. Appuyez sur Entrée pour accepter ou Échap pour ignorer.", + "Automatically detects the largest length and width across slices for the selected segment and displays a bidirectional measurement.": "Détecte automatiquement la plus grande longueur et la plus grande largeur à travers les coupes pour le segment sélectionné et affiche une mesure bidirectionnelle.", + "Detects segmentable regions with one click. Hover for visual feedback—click when a plus sign appears to auto-segment the lesion.": "Détecte les régions segmentables en un clic. Survolez pour un retour visuel — cliquez lorsque le signe plus apparaît pour segmenter automatiquement la lésion.", + "Use include/exclude markers to guide AI (SAM) segmentation. Click to place markers, Enter to accept results, Esc to reject, and N to go to the next slice while keeping markers.": "Utilisez des marqueurs d'inclusion/exclusion pour guider la segmentation IA (SAM). Cliquez pour placer des marqueurs, Entrée pour accepter les résultats, Échap pour rejeter, et N pour passer à la coupe suivante en conservant les marqueurs." + , + "Automatically fill in missing slices between drawn segments. Use brush or threshold tools on at least two slices, then click to interpolate across slices. Works in any direction. Volume must be reconstructable.": "Remplit automatiquement les coupes manquantes entre les segments dessinés. Utilisez les outils brosse ou seuillage sur au moins deux coupes, puis cliquez pour interpoler entre les coupes. Fonctionne dans toutes les directions. Le volume doit être reconstruisible." + , + "Marker Mode": "Mode marqueur", + "Include": "Inclure", + "Exclude": "Exclure", + "Clear Markers": "Effacer les marqueurs" + , + "Radius (mm)": "Rayon (mm)", + "Dynamic": "Dynamique", + "Range": "Plage" } diff --git a/platform/i18n/src/locales/fr/CaptureViewportModal.json b/platform/i18n/src/locales/fr/CaptureViewportModal.json new file mode 100644 index 00000000000..48e669ae02e --- /dev/null +++ b/platform/i18n/src/locales/fr/CaptureViewportModal.json @@ -0,0 +1,54 @@ +{ + "Capture": "Capture", + "Cancel": "$t(Common:Cancel)", + "Download": "$t(Common:Download)", + "Image size": "Taille de l'image", + "Image size in pixels": "Taille de l'image en pixels", + "File name": "Nom du fichier", + "Viewport to capture": "Fenêtre à capturer", + "Viewport Settings": "Paramètres de la fenêtre", + "General Settings": "Paramètres généraux", + "Resolution": "Résolution", + "Aspect Ratio": "Rapport d'aspect", + "Region of interest": "Région d'intérêt", + "Include annotations": "Inclure les annotations", + "Include measurements": "Inclure les mesures", + "Include patient info": "Inclure les informations patient", + "Include warning message": "Inclure un message d'avertissement", + "Width": "Largeur", + "Height": "Hauteur", + "Annotate on the capture": "Annoter sur la capture", + "Reset to defaults": "$t(Common:Reset)", + "Capture Options": "Options de capture", + "Add capture to reports": "Ajouter la capture aux rapports", + "Name": "Nom", + "Description": "Description", + "Create a new report or select an existing one to add the capture to": "Créer un nouveau rapport ou en sélectionner un existant pour y ajouter la capture", + "Create new report": "Créer un nouveau rapport", + "Add to existing report": "Ajouter à un rapport existant", + "Select report": "Sélectionner un rapport", + "Add capture": "Ajouter la capture", + "New report name": "Nom du nouveau rapport", + "New report description": "Description du nouveau rapport", + "Add capture to": "Ajouter la capture à", + "Selected report": "Rapport sélectionné", + "Capture options": "Options de capture", + "Fill background color": "Remplir avec la couleur de fond", + "Set to black": "Définir sur noir", + "Set to white": "Définir sur blanc", + "Set to image": "Définir selon l'image", + "Reset": "$t(Common:Reset)", + "Invert colors": "Inverser les couleurs", + "Apply changes": "Appliquer les modifications", + "Active viewport": "Fenêtre active", + "Thumbnail": "Vignette", + "Capture viewport": "Capturer la fenêtre", + "Apply ROI": "Appliquer le ROI", + "Actions": "Actions", + "Reset capture options": "Réinitialiser les options de capture", + "Clear selection": "Effacer la sélection", + "Add capture to report": "Ajouter la capture au rapport", + "Report name": "Nom du rapport", + "Report description": "Description du rapport", + "Error capturing viewport": "Erreur lors de la capture de la fenêtre" +} diff --git a/platform/i18n/src/locales/fr/Colormaps.json b/platform/i18n/src/locales/fr/Colormaps.json new file mode 100644 index 00000000000..f5d97f39bf6 --- /dev/null +++ b/platform/i18n/src/locales/fr/Colormaps.json @@ -0,0 +1,15 @@ +{ + "Grayscale": "Niveaux de gris", + "X Ray": "Rayons X", + "Isodose": "Isodose", + "hsv": "HSV", + "hot_iron": "Fer chaud", + "red_hot": "Rouge incandescent", + "s_pet": "TEP", + "perfusion": "Perfusion", + "rainbow_2": "Arc-en-ciel", + "suv": "SUV", + "ge_256": "GE 256", + "ge": "GE", + "siemens": "Siemens" +} diff --git a/platform/i18n/src/locales/fr/Common.json b/platform/i18n/src/locales/fr/Common.json index e1220f51987..1860244d85d 100644 --- a/platform/i18n/src/locales/fr/Common.json +++ b/platform/i18n/src/locales/fr/Common.json @@ -1,10 +1,29 @@ { + "Back to": "Retour à {{location}}", + "Cancel": "Annuler", + "Close": "Fermer", + "Enter your annotation": "Saisissez votre annotation", "Image": "Image", "Layout": "Disposition", + "LOAD": "CHARGER", + "Measurements": "Mesures", + "mm": "mm", "More": "Plus", "Next": "Suivant", - "Play": "Play", + "No": "Non", + "NoStudyDate": "Pas de date d'étude", + "localDateFormat": "DD MMM YYYY", + "Play": "Lecture", "Previous": "Précédent", - "Reset": "Reset", - "Stop": "Stop" + "Reset": "Réinitialiser", + "RowsPerPage": "Lignes par page", + "Save": "Enregistrer", + "Series": "Séries", + "Show": "Afficher", + "Stop": "Arrêter", + "StudyDate": "Date d'étude", + "Yes": "Oui", + "Foreground": "Avant-plan", + "SELECT A FOREGROUND": "SÉLECTIONNER UN AVANT-PLAN", + "SELECT A SEGMENTATION": "SÉLECTIONNER UNE SEGMENTATION" } diff --git a/platform/i18n/src/locales/fr/ContextMenu.json b/platform/i18n/src/locales/fr/ContextMenu.json new file mode 100644 index 00000000000..40642f3b24c --- /dev/null +++ b/platform/i18n/src/locales/fr/ContextMenu.json @@ -0,0 +1,4 @@ +{ + "Add Label": "Ajouter une étiquette", + "Delete measurement": "Supprimer la mesure" +} diff --git a/platform/i18n/src/locales/fr/DataRow.json b/platform/i18n/src/locales/fr/DataRow.json new file mode 100644 index 00000000000..e8e40d230d4 --- /dev/null +++ b/platform/i18n/src/locales/fr/DataRow.json @@ -0,0 +1,10 @@ +{ + "Hide": "Masquer", + "Show": "Afficher", + "Rename": "Renommer", + "Duplicate": "Dupliquer", + "Delete": "Supprimer", + "Change Color": "Changer la couleur", + "Lock": "Verrouiller", + "Unlock": "Déverrouiller" +} diff --git a/platform/i18n/src/locales/fr/DatePicker.json b/platform/i18n/src/locales/fr/DatePicker.json new file mode 100644 index 00000000000..f9725829a1c --- /dev/null +++ b/platform/i18n/src/locales/fr/DatePicker.json @@ -0,0 +1,13 @@ +{ + "Clear dates": "Effacer les dates", + "Close": "$t(Common:Close)", + "End Date": "Date de fin", + "Last 7 days": "7 derniers jours", + "Last 30 days": "30 derniers jours", + "Start Date": "Date de début", + "Today": "Aujourd'hui", + "Previous Month": "Mois précédent", + "Next Month": "Mois suivant", + "Select Month": "Sélectionner un mois", + "Select Year": "Sélectionner une année" +} diff --git a/platform/i18n/src/locales/fr/Dialog.json b/platform/i18n/src/locales/fr/Dialog.json new file mode 100644 index 00000000000..89aec176ffa --- /dev/null +++ b/platform/i18n/src/locales/fr/Dialog.json @@ -0,0 +1,6 @@ +{ + "Enter your annotation": "$t(Common:Enter your annotation)", + "Cancel": "$t(Common:Cancel)", + "Save": "$t(Common:Save)", + "Provide a name for your report": "Indiquez un nom pour votre rapport" +} diff --git a/platform/i18n/src/locales/fr/ErrorBoundary.json b/platform/i18n/src/locales/fr/ErrorBoundary.json new file mode 100644 index 00000000000..d4c13294916 --- /dev/null +++ b/platform/i18n/src/locales/fr/ErrorBoundary.json @@ -0,0 +1,7 @@ +{ + "Sorry, something went wrong there. Try again.": "Désolé, une erreur est survenue. Réessayez.", + "Context": "Contexte", + "Error Message": "Message d'erreur", + "Stack": "Pile", + "Something went wrong": "Une erreur s'est produite" +} diff --git a/platform/i18n/src/locales/fr/Hps.json b/platform/i18n/src/locales/fr/Hps.json new file mode 100644 index 00000000000..330651e09c8 --- /dev/null +++ b/platform/i18n/src/locales/fr/Hps.json @@ -0,0 +1,14 @@ +{ + "MPR": "MPR", + "3D four up": "3D quatre vues", + "Frame View": "Vue par image", + "Frame view for the active series": "Vue image par image pour la série active", + "3D main": "Vue principale 3D", + "mpr": "mpr", + "3D only": "3D seulement", + "3D primary": "3D principale", + "Axial Primary": "Axial principal", + "Compare Two Studies": "Comparer deux études", + "Compare two studies in various layouts": "Comparer deux études avec différentes dispositions", + "Mammography Breast Screening": "Dépistage mammographique" +} diff --git a/platform/i18n/src/locales/fr/Local.json b/platform/i18n/src/locales/fr/Local.json new file mode 100644 index 00000000000..72e5d5dc21e --- /dev/null +++ b/platform/i18n/src/locales/fr/Local.json @@ -0,0 +1,4 @@ +{ + "Load files": "Charger des fichiers", + "Load folders": "Charger des dossiers" +} diff --git a/platform/i18n/src/locales/fr/MeasurementTable.json b/platform/i18n/src/locales/fr/MeasurementTable.json new file mode 100644 index 00000000000..3e5de6202d5 --- /dev/null +++ b/platform/i18n/src/locales/fr/MeasurementTable.json @@ -0,0 +1,26 @@ +{ + "Criteria nonconformities": "Non-conformités aux critères", + "Delete": "Supprimer", + "Description": "Description", + "MAX": "MAX", + "NonTargets": "Non-cibles", + "Relabel": "Renommer", + "Measurements": "Mesures", + "Targets": "Cibles", + "Rename": "Renommer", + "Duplicate": "Dupliquer", + "Change Color": "Changer la couleur", + "Lock": "Verrouiller", + "Unlock": "Déverrouiller", + "Hide": "Masquer", + "Show": "Afficher", + "No, do not ask again": "Non, ne plus demander", + "Create SR": "Créer SR", + "empty": "(vide)", + "Export CSV": "Exporter CSV", + "No tracked measurements": "Aucune mesure suivie", + "Export": "Exporter", + "Create Report": "Créer un rapport", + "Do you want to add this measurement to the existing report?": "Souhaitez-vous ajouter cette mesure au rapport existant ?", + "Track measurements for this series?": "Suivre les mesures pour cette série ?" +} diff --git a/platform/i18n/src/locales/fr/Modals.json b/platform/i18n/src/locales/fr/Modals.json new file mode 100644 index 00000000000..980155e0acb --- /dev/null +++ b/platform/i18n/src/locales/fr/Modals.json @@ -0,0 +1,19 @@ +{ + "Active viewport has no displayed image": "La vue active n'affiche aucune image", + "Cancel": "$t(Common:Cancel)", + "Download": "Télécharger", + "The file name cannot be empty.": "Le nom du fichier ne peut pas être vide.", + "File Type": "Type de fichier", + "File Name": "Nom du fichier", + "formTitle": "Veuillez préciser les dimensions, le nom de fichier et le type souhaité pour l'image exportée.", + "Image height (px)": "Hauteur de l'image (px)", + "Image Preview": "Aperçu de l'image", + "Image preview": "Aperçu de l'image", + "Image width (px)": "Largeur de l'image (px)", + "keepAspectRatio": "Conserver les proportions", + "loadingPreview": "Chargement de l'aperçu de l'image...", + "The minimum valid height is 100px.": "La hauteur minimale autorisée est de 100 px.", + "The minimum valid width is 100px.": "La largeur minimale autorisée est de 100 px.", + "Show Annotations": "Afficher les annotations", + "Please specify the dimensions, filename, and desired type for the output image.": "Veuillez préciser les dimensions, le nom de fichier et le type souhaité pour l'image exportée." +} diff --git a/platform/i18n/src/locales/fr/Modes.json b/platform/i18n/src/locales/fr/Modes.json new file mode 100644 index 00000000000..9069c3c0e11 --- /dev/null +++ b/platform/i18n/src/locales/fr/Modes.json @@ -0,0 +1,6 @@ +{ + "Basic Viewer": "Visionneuse de base", + "Total Metabolic Tumor Volume": "Volume tumoral métabolique total", + "Download High Quality Image": "Télécharger une image haute qualité", + "US Pleura B-line Annotations": "Annotations de plèvre et lignes B en échographie" +} diff --git a/platform/i18n/src/locales/fr/Notification.json b/platform/i18n/src/locales/fr/Notification.json new file mode 100644 index 00000000000..3ab9610271b --- /dev/null +++ b/platform/i18n/src/locales/fr/Notification.json @@ -0,0 +1,18 @@ +{ + "Do you want to add this measurement to the existing report?": "Souhaitez-vous ajouter cette mesure au rapport existant ?", + "Create new report": "Créer un nouveau rapport", + "Add to existing report": "Ajouter au rapport existant", + "Discard": "Ignorer", + "You have existing tracked measurements. What would you like to do with your existing tracked measurements?": "Des mesures suivies existent déjà. Que souhaitez-vous en faire ?", + "No, do not ask again for this series": "Non, ne plus demander pour cette série", + "No": "$t(Common:No)", + "Track measurements for this series?": "Suivre les mesures pour cette série ?", + "Yes": "$t(Common:Yes)", + "Cancel": "$t(Common:Cancel)", + "Measurements cannot span across multiple studies. Do you want to save your tracked measurements?": "Les mesures ne peuvent pas couvrir plusieurs études. Voulez-vous enregistrer vos mesures suivies ?", + "No, discard previously tracked series & measurements": "Non, abandonner les séries et mesures suivies précédemment", + "Do you want to continue tracking measurements for this study?": "Souhaitez-vous continuer à suivre les mesures pour cette étude ?", + "Create Report": "Créer un rapport", + "Measurements saved successfully": "Mesures enregistrées avec succès", + "Failed to store measurements": "Échec de l'enregistrement des mesures" +} diff --git a/platform/i18n/src/locales/fr/Onboarding.json b/platform/i18n/src/locales/fr/Onboarding.json new file mode 100644 index 00000000000..8e59ff31bcc --- /dev/null +++ b/platform/i18n/src/locales/fr/Onboarding.json @@ -0,0 +1,27 @@ +{ + "Scrolling Through Images": "Défilement des images", + "You can scroll through the images using the mouse wheel or scrollbar.": "Vous pouvez faire défiler les images avec la molette de la souris ou la barre de défilement.", + "Zooming In and Out": "Zoom avant et arrière", + "You can zoom the images using the right click.": "Vous pouvez zoomer sur les images avec le clic droit.", + "Panning the Image": "Déplacement de l'image", + "You can pan the images using the middle click.": "Vous pouvez déplacer l'image avec le clic du milieu.", + "Adjusting Window Level": "Ajuster la fenêtre et le niveau", + "You can modify the window level using the left click.": "Vous pouvez modifier la fenêtre et le niveau avec le clic gauche.", + "Using the Measurement Tools": "Utiliser les outils de mesure", + "You can measure the length of a region using the Length tool.": "Vous pouvez mesurer la longueur d'une région avec l'outil Longueur.", + "Drawing Length Annotations": "Tracer des annotations de longueur", + "Use the length tool on the viewport to measure the length of a region.": "Utilisez l'outil Longueur dans la vue pour mesurer une région.", + "Tracking Measurements in the Panel": "Suivre les mesures dans le panneau", + "Click yes to track the measurements in the measurement panel.": "Cliquez sur Oui pour suivre les mesures dans le panneau des mesures.", + "Opening the Measurements Panel": "Ouvrir le panneau des mesures", + "Click the measurements button to open the measurements panel.": "Cliquez sur le bouton Mesures pour ouvrir le panneau des mesures.", + "Scrolling Away from a Measurement": "Défiler loin d'une mesure", + "Scroll the images using the mouse wheel away from the measurement.": "Défilez avec la molette pour vous éloigner de la mesure.", + "Jumping to Measurements in the Panel": "Aller à une mesure depuis le panneau", + "Click the measurement in the measurement panel to jump to it.": "Cliquez sur la mesure dans le panneau pour y accéder.", + "Changing Layout": "Changer de disposition", + "You can change the layout of the viewer using the layout button.": "Vous pouvez changer la disposition via le bouton Disposition.", + "Selecting the MPR Layout": "Sélectionner la disposition MPR", + "Select the MPR layout to view the images in MPR mode.": "Sélectionnez la disposition MPR pour afficher les images en mode MPR.", + "Skip all": "Tout passer" +} diff --git a/platform/i18n/src/locales/fr/PanelSUV.json b/platform/i18n/src/locales/fr/PanelSUV.json new file mode 100644 index 00000000000..e92fb4bb666 --- /dev/null +++ b/platform/i18n/src/locales/fr/PanelSUV.json @@ -0,0 +1,10 @@ +{ + "Patient Information": "Informations patient", + "Patient Sex": "Sexe du patient", + "Weight": "Poids", + "Total Dose": "Dose totale", + "Half Life": "Demi-vie", + "Injection Time": "Heure d'injection", + "Acquisition Time": "Heure d'acquisition", + "Reload Data": "Recharger les données" +} diff --git a/platform/i18n/src/locales/fr/PatientInfo.json b/platform/i18n/src/locales/fr/PatientInfo.json new file mode 100644 index 00000000000..03f3b04e4dd --- /dev/null +++ b/platform/i18n/src/locales/fr/PatientInfo.json @@ -0,0 +1,8 @@ +{ + "Sex": "Sexe", + "Age": "Âge", + "MRN": "MRN", + "Thickness": "Épaisseur", + "Spacing": "Espacement", + "Scanner": "Scanner" +} diff --git a/platform/i18n/src/locales/fr/ROIThresholdConfiguration.json b/platform/i18n/src/locales/fr/ROIThresholdConfiguration.json new file mode 100644 index 00000000000..11559e74c77 --- /dev/null +++ b/platform/i18n/src/locales/fr/ROIThresholdConfiguration.json @@ -0,0 +1,11 @@ +{ + "Max": "Max", + "Range": "Plage", + "Start": "Début", + "End": "Fin", + "Percentage of Max SUV": "Pourcentage du SUV max", + "Lower & Upper Ranges": "Plages inférieure et supérieure", + "Run": "Exécuter", + "Threshold Tools": "Outils de seuillage", + "ThresholdRange": "Seuillage" +} diff --git a/platform/i18n/src/locales/fr/SegmentationPanel.json b/platform/i18n/src/locales/fr/SegmentationPanel.json new file mode 100644 index 00000000000..30081ef9b98 --- /dev/null +++ b/platform/i18n/src/locales/fr/SegmentationPanel.json @@ -0,0 +1,52 @@ +{ + "Opacity": "Opacité", + "Border": "Bordure", + "Show": "Afficher", + "Fill & Outline": "Remplissage et contour", + "Outline Only": "Contour uniquement", + "Fill Only": "Remplissage uniquement", + "Display inactive segmentations": "Afficher les segmentations inactives", + "Select a segmentation": "Sélectionner une segmentation", + "Segmentation": "Segmentation", + "Segmentations": "Segmentations", + "Add Segment": "Ajouter un segment", + "Contour tools": "Outils contour", + "Preview edits before creating": "Prévisualiser les modifications avant la création", + "Use center as segment index": "Utiliser le centre comme indice de segment", + "Hover on segment border to activate": "Survoler le bord du segment pour activer", + "Show segment name on hover": "Afficher le nom du segment au survol", + "Add segmentation": "Ajouter une segmentation", + "Create New Segmentation": "Créer une nouvelle segmentation", + "Manage Current Segmentation": "Gérer la segmentation actuelle", + "Remove from Viewport": "Retirer de la vue", + "Rename": "Renommer", + "Delete": "Supprimer", + "Labelmap tools": "Outils labelmap", + "Labelmap Segmentations": "Segmentations du labelmap", + "Download & Export": "Télécharger et exporter", + "Download": "Télécharger", + "Export": "Exporter", + "CSV Report": "Rapport CSV", + "DICOM SEG": "DICOM SEG", + "DICOM RTSS": "DICOM RTSS", + "Download DICOM RTSS": "Télécharger DICOM RTSS", + "Fill contour holes": "Remplir les trous de contour", + "Fill Holes": "Remplir les trous", + "Remove Small Contours": "Supprimer les petits contours", + "Area Threshold": "Seuil de surface", + "Create New Segment from Holes": "Créer un nouveau segment à partir des trous", + "Create New Segment": "Créer un nouveau segment", + "Smooth all edges": "Lisser tous les bords", + "Smooth Edges": "Lisser les bords", + "Remove extra points": "Supprimer les points supplémentaires", + "Remove Points": "Supprimer les points", + "Merge": "Fusionner", + "Intersect": "Intersection", + "Subtract": "Soustraire", + "Select a segment": "Sélectionner un segment", + "Create a new segment": "Créer un nouveau segment", + "New segment name": "Nom du nouveau segment", + "No segmentations available": "Aucune segmentation disponible", + "Not available on the current viewport": "Non disponible sur la fenêtre d'affichage actuelle", + "Add segment to enable this tool": "Ajoutez un segment pour activer cet outil" +} diff --git a/platform/i18n/src/locales/fr/SidePanel.json b/platform/i18n/src/locales/fr/SidePanel.json new file mode 100644 index 00000000000..e9523f9f3f9 --- /dev/null +++ b/platform/i18n/src/locales/fr/SidePanel.json @@ -0,0 +1,6 @@ +{ + "Studies": "Études", + "Measurements": "Mesures", + "Measure": "Mesurer", + "Segmentation": "Segmentation" +} diff --git a/platform/i18n/src/locales/fr/StudyBrowser.json b/platform/i18n/src/locales/fr/StudyBrowser.json new file mode 100644 index 00000000000..7c9da9318c5 --- /dev/null +++ b/platform/i18n/src/locales/fr/StudyBrowser.json @@ -0,0 +1,12 @@ +{ + "Primary": "Principal", + "Recent": "Récents", + "All": "Tous", + "Tracked Series": "Séries suivies", + "Tag Browser": "Explorateur DICOM", + "Add as Layer": "Ajouter en tant que couche", + "Series Number": "Numéro de série", + "Series Date": "Date de la série", + "Thumbnail Double Click": "Double-clic sur la vignette", + "The selected display sets could not be added to the viewport.": "Les ensembles d'affichage sélectionnés n'ont pas pu être ajoutés à la fenêtre." +} diff --git a/platform/i18n/src/locales/fr/StudyList.json b/platform/i18n/src/locales/fr/StudyList.json new file mode 100644 index 00000000000..556bfdd575c --- /dev/null +++ b/platform/i18n/src/locales/fr/StudyList.json @@ -0,0 +1,29 @@ +{ + "Empty": "Vide", + "Filter list to 100 studies or less to enable sorting": "Filtrez la liste à 100 études ou moins pour activer le tri", + "Modality": "Modalité", + "PatientName": "Nom du patient", + "StudyDate": "Date de l'étude", + "StudyList": "Liste d'études", + "Patient Name": "Nom du patient", + "MRN": "Numéro DSN", + "Study date": "Date de l'étude", + "Description": "Description", + "Study list": "Liste d'études", + "Clear filters": "Effacer les filtres", + "ClearFilters": "Effacer les filtres", + "Number of studies": "Nombre d'études", + "Instances": "Instances", + "Accession": "Numéro d'accès", + "Results per page": "Résultats par page", + "Previous": "$t(Common:Previous)", + "Next": "$t(Common:Next)", + "Page": "Page", + "Start Date": "Date de début", + "Series": "Séries", + "Studies": "Études", + "No studies available": "Aucune étude disponible", + "Loading...": "Chargement...", + "Select...": "Sélectionner...", + "InstitutionName": "Établissement" +} diff --git a/platform/i18n/src/locales/fr/ToolbarLayoutSelector.json b/platform/i18n/src/locales/fr/ToolbarLayoutSelector.json new file mode 100644 index 00000000000..93c8eddb9bb --- /dev/null +++ b/platform/i18n/src/locales/fr/ToolbarLayoutSelector.json @@ -0,0 +1,9 @@ +{ + "Change layout": "Changer la disposition", + "Common": "Général", + "Advanced": "Avancé", + "Custom": "Personnalisé", + "Hover to select": "Survolez pour sélectionner", + "rows and columns": "lignes et colonnes", + "Click to apply": "Cliquez pour appliquer" +} diff --git a/platform/i18n/src/locales/fr/Tools.json b/platform/i18n/src/locales/fr/Tools.json new file mode 100644 index 00000000000..352aa47d087 --- /dev/null +++ b/platform/i18n/src/locales/fr/Tools.json @@ -0,0 +1,13 @@ +{ + "Segmentation": "Segmentation", + "Segment": "Segment", + "Download High Quality Image": "Télécharger une image", + "Edit Segment Label": "Modifier l'étiquette de segment", + "Enter new label": "Saisir une nouvelle étiquette", + "Segment Color": "Couleur du segment", + "Edit Measurement Label": "Modifier l'étiquette de mesure", + "Edit Arrow Text": "Modifier le texte de la flèche", + "Enter new text": "Saisir un nouveau texte", + "Download Image": "Télécharger l'image", + "Image cannot be downloaded": "Impossible de télécharger l'image" +} diff --git a/platform/i18n/src/locales/fr/USAnnotationPanel.json b/platform/i18n/src/locales/fr/USAnnotationPanel.json new file mode 100644 index 00000000000..615812bb253 --- /dev/null +++ b/platform/i18n/src/locales/fr/USAnnotationPanel.json @@ -0,0 +1,17 @@ +{ + "Workflow": "Flux de travail", + "Depth guide toggle": "Activer/désactiver le guide de profondeur", + "Show pleura percentage": "Afficher le pourcentage de plèvre", + "Sector Annotations": "Annotations de secteur", + "Pleura line": "Ligne de plèvre", + "B-line": "Ligne B", + "B-line annotation": "Annotation de ligne B", + "Pleura annotation": "Annotation de plèvre", + "Show Overlay": "Afficher la superposition", + "Annotated Frames": "Images annotées", + "Frame": "Image", + "Pleura lines": "Lignes de plèvre", + "B-lines": "Lignes B", + "JSON": "JSON", + "Annotations": "Annotations" +} diff --git a/platform/i18n/src/locales/fr/UserPreferencesModal.json b/platform/i18n/src/locales/fr/UserPreferencesModal.json index 424cfe98ef9..6fd0148270d 100644 --- a/platform/i18n/src/locales/fr/UserPreferencesModal.json +++ b/platform/i18n/src/locales/fr/UserPreferencesModal.json @@ -1,6 +1,81 @@ { - "Cancel": "$t(Buttons:Cancel)", - "Reset to defaults": "$t(Buttons:Reset to defaults)", - "Save": "$t(Buttons:Save)", - "User preferences": "Préférences utilisateur" + "Cancel": "$t(Common:Cancel)", + "Reset to defaults": "Réinitialiser aux valeurs par défaut", + "Save": "$t(Common:Save)", + "User preferences": "Préférences utilisateur", + "Language": "Langue", + "Select language": "Sélectionner une langue", + "Hotkeys": "Raccourcis clavier", + "Zoom": "$t(Buttons:Zoom)", + "Zoom In": "Zoom avant", + "Zoom Out": "Zoom arrière", + "Zoom to Fit": "Adapter à la fenêtre", + "Rotate Right": "$t(Buttons:Rotate Right)", + "Rotate Left": "Rotation à gauche", + "Flip Horizontally": "$t(Buttons:Flip Horizontally)", + "Flip Vertically": "Miroir vertical", + "Cine": "$t(Buttons:Cine)", + "Invert": "$t(Buttons:Invert)", + "Next Image Viewport": "Fenêtre image suivante", + "Previous Image Viewport": "Fenêtre image précédente", + "Previous Series": "Série précédente", + "Next Series": "Série suivante", + "Next Stage": "Étape suivante", + "Previous Stage": "Étape précédente", + "Next Image": "Image suivante", + "Previous Image": "Image précédente", + "First Image": "Première image", + "Last Image": "Dernière image", + "Reset": "$t(Common:Reset)", + "Cancel Measurement": "Annuler la mesure", + "W/L Preset 1": "Préréglage W/L 1", + "W/L Preset 2": "Préréglage W/L 2", + "W/L Preset 3": "Préréglage W/L 3", + "W/L Preset 4": "Préréglage W/L 4", + "Delete Annotation": "Supprimer l'annotation", + "Accept Preview": "Accepter l'aperçu", + "Reject Preview": "Rejeter l'aperçu", + "Undo": "Annuler", + "Redo": "Rétablir", + "Interpolate Scroll": "Interpolation du défilement", + "Increase Brush Size": "Augmenter la taille du pinceau", + "Decrease Brush Size": "Diminuer la taille du pinceau", + "Eraser": "Gomme", + "Brush": "$t(Buttons:Brush)", + "Add New Segment": "Ajouter un segment", + "Press keys": "Appuyez sur les touches...", + "LanguageName.en-US": "Anglais (États-Unis)", + "LanguageName.fr": "Français", + "HotkeyKeys.ctrl": "Ctrl", + "HotkeyKeys.shift": "Maj", + "HotkeyKeys.alt": "Alt", + "HotkeyKeys.option": "Option", + "HotkeyKeys.meta": "Cmd", + "HotkeyKeys.enter": "⏎", + "HotkeyKeys.esc": "esc", + "HotkeyKeys.space": "Espace", + "HotkeyKeys.tab": "Tab", + "HotkeyKeys.backspace": "⌫", + "HotkeyKeys.delete": "Suppr", + "HotkeyKeys.insert": "Inser", + "HotkeyKeys.home": "Origine", + "HotkeyKeys.end": "Fin", + "HotkeyKeys.pageup": "Page ↓", + "HotkeyKeys.pagedown": "Page ↑", + "HotkeyKeys.up": "↑", + "HotkeyKeys.down": "↓", + "HotkeyKeys.left": "←", + "HotkeyKeys.right": "→", + "HotkeyKeys.capslock": "Verr. maj", + "HotkeyKeys.plus": "Plus", + "HotkeyKeys.minus": "Moins", + "HotkeyKeys.comma": "Virgule", + "HotkeyKeys.period": "Point", + "HotkeyKeys.slash": "Barre oblique", + "HotkeyKeys.backslash": "Barre oblique inverse", + "HotkeyKeys.semicolon": "Point-virgule", + "HotkeyKeys.quote": "Guillemet", + "HotkeyKeys.backquote": "Accent grave", + "HotkeyKeys.bracketleft": "Crochet gauche", + "HotkeyKeys.bracketright": "Crochet droit" } diff --git a/platform/i18n/src/locales/fr/ViewportDownloadForm.json b/platform/i18n/src/locales/fr/ViewportDownloadForm.json new file mode 100644 index 00000000000..6652af18a81 --- /dev/null +++ b/platform/i18n/src/locales/fr/ViewportDownloadForm.json @@ -0,0 +1,14 @@ +{ + "emptyFilenameError": "Le nom du fichier ne peut pas être vide.", + "fileType": "Type de fichier", + "filename": "Nom du fichier", + "formTitle": "Veuillez préciser les dimensions, le nom de fichier et le type souhaité pour l'image exportée.", + "imageHeight": "Hauteur de l'image (px)", + "imagePreview": "Aperçu de l'image", + "imageWidth": "Largeur de l'image (px)", + "keepAspectRatio": "Conserver les proportions", + "loadingPreview": "Chargement de l'aperçu de l'image...", + "minHeightError": "La hauteur minimale autorisée est de 100 px.", + "minWidthError": "La largeur minimale autorisée est de 100 px.", + "showAnnotations": "Afficher les annotations" +} diff --git a/platform/i18n/src/locales/fr/WindowLevelActionMenu.json b/platform/i18n/src/locales/fr/WindowLevelActionMenu.json new file mode 100644 index 00000000000..8363701b1a1 --- /dev/null +++ b/platform/i18n/src/locales/fr/WindowLevelActionMenu.json @@ -0,0 +1,24 @@ +{ + "Back to Display Options": "Retour aux options d'affichage", + "Modality Presets": "Préréglages de modalité", + "Modality Window Presets": "Préréglages de fenêtre par modalité", + "Display Color bar": "Afficher la barre de couleurs", + "Color LUT": "LUT de couleur", + "Preview in viewport": "Prévisualiser dans la fenêtre", + "Grayscale": "Niveaux de gris", + "Rendering Options": "Options de rendu", + "Rendering Presets": "Préréglages de rendu", + "Search all": "Tout rechercher", + "Quality": "Qualité", + "Lighting": "Éclairage", + "Shade": "Ombrage", + "Ambient": "Ambiant", + "Diffuse": "Diffus", + "Specular": "Spéculaire", + "Shift": "Décalage", + "Soft tissue": "Tissus mous", + "Lung": "Poumon", + "Liver": "Foie", + "Bone": "Osseux", + "Brain": "Cérébral" +} diff --git a/platform/i18n/src/locales/fr/index.js b/platform/i18n/src/locales/fr/index.js index 2fabaff3a33..7c695a58d42 100644 --- a/platform/i18n/src/locales/fr/index.js +++ b/platform/i18n/src/locales/fr/index.js @@ -1,15 +1,69 @@ +import AboutModal from './AboutModal.json'; import Buttons from './Buttons.json'; import CineDialog from './CineDialog.json'; import Common from './Common.json'; +import DatePicker from './DatePicker.json'; import Header from './Header.json'; +import MeasurementTable from './MeasurementTable.json'; +import DataRow from './DataRow.json'; +import StudyList from './StudyList.json'; import UserPreferencesModal from './UserPreferencesModal.json'; +import ViewportDownloadForm from './ViewportDownloadForm.json'; +import StudyBrowser from './StudyBrowser.json'; +import SidePanel from './SidePanel.json'; +import Modes from './Modes.json'; +import PatientInfo from './PatientInfo.json'; +import Notification from './Notification.json'; +import ContextMenu from './ContextMenu.json'; +import Dialog from './Dialog.json'; +import Modals from './Modals.json'; +import Local from './Local.json'; +import ErrorBoundary from './ErrorBoundary.json'; +import Hps from './Hps.json'; +import ToolbarLayoutSelector from './ToolbarLayoutSelector.json'; +import WindowLevelActionMenu from './WindowLevelActionMenu.json'; +import CaptureViewportModal from './CaptureViewportModal.json'; +import Tools from './Tools.json'; +import SegmentationPanel from './SegmentationPanel.json'; +import Onboarding from './Onboarding.json'; +import Colormaps from './Colormaps.json'; +import PanelSUV from './PanelSUV.json'; +import ROIThresholdConfiguration from './ROIThresholdConfiguration.json'; +import USAnnotationPanel from './USAnnotationPanel.json'; export default { fr: { + AboutModal, Buttons, CineDialog, Common, + DatePicker, Header, + MeasurementTable, + DataRow, + StudyList, UserPreferencesModal, + ViewportDownloadForm, + StudyBrowser, + SidePanel, + Modes, + PatientInfo, + Notification, + ContextMenu, + Dialog, + Modals, + Local, + ErrorBoundary, + Hps, + ToolbarLayoutSelector, + WindowLevelActionMenu, + CaptureViewportModal, + Tools, + SegmentationPanel, + Onboarding, + Colormaps, + PanelSUV, + ROIThresholdConfiguration, + USAnnotationPanel, }, }; diff --git a/platform/i18n/src/locales/nl/AboutModal.json b/platform/i18n/src/locales/nl/AboutModal.json new file mode 100644 index 00000000000..609314db865 --- /dev/null +++ b/platform/i18n/src/locales/nl/AboutModal.json @@ -0,0 +1,20 @@ +{ + "About OHIF Viewer": "Over OHIF Viewer", + "Browser": "Browser", + "Build number": "Buildnummer", + "Commit hash": "Commit hash", + "Commit Hash": "Commit Hash", + "Current Browser & OS": "Huidige Browser & OS", + "Data citation": "Data citatie", + "Important links": "Belangrijke links", + "Last master commits": "Laatste master commits", + "More details": "Meer details", + "Name": "Naam", + "OS": "OS", + "Report an issue": "Rapporteer een probleem", + "Repository URL": "Repository URL", + "Value": "Waarde", + "Version information": "Versie informatie", + "Version number": "Versienummer", + "Visit the forum": "Bezoek het forum" +} diff --git a/platform/i18n/src/locales/nl/Buttons.json b/platform/i18n/src/locales/nl/Buttons.json index 0443cffd184..3bb70b24293 100644 --- a/platform/i18n/src/locales/nl/Buttons.json +++ b/platform/i18n/src/locales/nl/Buttons.json @@ -1,6 +1,160 @@ { + "Acquired": "Verworven", + "Angle": "Hoek", + "Annotation": "Annotatie", + "Axial": "Axiaal", + "Bidirectional": "Bidirectioneel", + "Brush": "Penseel", + "Cine": "Cine", + "Cancel": "Annuleren", + "Capture": "Vastleggen", "Circle": "Cirkel", - "More": "Meer", - "Pan": "Pan", - "Zoom": "Inzoomen" + "Sphere": "Bol", + "Clear": "Wissen", + "Coronal": "Coronaal", + "Crosshairs": "Kruisdraden", + "Download": "Downloaden", + "Ellipse": "Ellips", + "Elliptical": "Elliptisch", + "Flip H": "H omdraaien", + "Flip Horizontally": "Horizontaal omdraaien", + "Flip V": "V omdraaien", + "Freehand": "Vrije hand", + "Grid Layout": "Rasterindeling", + "Invert": "Omkeren", + "Layout": "$t(Common:Layout)", + "Length": "Lengte", + "Levels": "Niveaus", + "Magnify": "Vergroten", + "Manual": "Handmatig", + "Measurements": "Metingen", + "More": "$t(Common:More)", + "More Tools": "Meer gereedschappen", + "Next": "$t(Common:Next)", + "Pan": "Verschuiven", + "Play": "$t(Common:Play)", + "Previous": "$t(Common:Previous)", + "Probe": "Sonde", + "ROI Window": "ROI Venster", + "Rectangle": "Rechthoek", + "Reference Lines": "Referentielijnen", + "Reset": "$t(Common:Reset)", + "Reset to defaults": "Herstellen naar standaard", + "Rotate Right": "Rechtsom draaien", + "Sagittal": "Sagittaal", + "Save": "Opslaan", + "Stack Scroll": "Stack scrollen", + "Stack Image Sync": "Stack afbeelding synchronisatie", + "Stop": "$t(Common:Stop)", + "Themes": "Thema's", + "Zoom": "Inzoomen", + "Adjust window/level presets and customize image contrast settings": "Venster/niveau voorinstellingen aanpassen en afbeelding contrastinstellingen aanpassen", + "Zoom-in": "Inzoomen", + "Cobb Angle": "Cobb hoek", + "Calibration": "Kalibratie", + "Dicom Tag Browser": "DICOM Tag Browser", + "Magnify Probe": "Vergroot sonde", + "Ultrasound Directional": "Ultrasoon directioneel", + "Window Level Region": "Venster/Niveau regio", + "Image Overlay": "Afbeelding overlay", + "Image Slice Sync": "Afbeelding slice synchronisatie", + "Freehand ROI": "Vrije hand ROI", + "Spline ROI": "Spline ROI", + "Livewire tool": "Livewire gereedschap", + "Livewire Contour": "Livewire contour", + "Segment Label Display": "Segment label weergave", + "Data Overlay": "Data overlay", + "Configure data overlay options and manage foreground/background display sets": "Data overlay opties configureren en voorgrond/achtergrond weergave sets beheren", + "Orientation": "Oriëntatie", + "Change viewport orientation between axial, sagittal, coronal and reformat planes": "Viewport oriëntatie wijzigen tussen axiaal, sagittaal, coronaal en herformateer vlakken", + "Status": "Status", + "Navigation": "Navigatie", + "Navigate between segments/measurements and manage their visibility": "Navigeren tussen segmenten/metingen en hun zichtbaarheid beheren", + "Tracking Status": "Tracking status", + "View and manage tracking status of measurements and annotations": "Tracking status van metingen en annotaties bekijken en beheren", + "Advanced Window Level": "Geavanceerd venster/niveau", + "Advanced window/level settings with manual controls and presets": "Geavanceerde venster/niveau instellingen met handmatige bediening en voorinstellingen", + "Threshold": "Drempel", + "Image threshold settings": "Afbeelding drempelinstellingen", + "Opacity": "Doorzichtigheid", + "Image opacity settings": "Afbeelding doorzichtigheidsinstellingen", + "Colorbar": "Kleurenbalk", + "Enable position synchronization on stack viewports": "Positiesynchronisatie inschakelen op stack viewports", + "Show Reference Lines": "Referentielijnen tonen", + "Toggle Image Overlay": "Afbeelding overlay omschakelen", + "Calibration Line": "Kalibratielijn", + "Ellipse ROI": "Ellips ROI", + "Rectangle ROI": "Rechthoek ROI", + "Circle Tool": "Cirkel gereedschap", + "Click to show or hide segment labels when hovering with your mouse.": "Klik om segment labels te tonen of verbergen bij het zweven met uw muis.", + "Arrow Annotate": "Pijl annotatie", + "Bidirectional Tool": "Bidirectioneel gereedschap", + "Bone": "Bot", + "Brain": "Hersenen", + "Dismiss Aspect": "Aspect negeren", + "Ellipse Tool": "Ellips gereedschap", + "Flip Horizontal": "Horizontaal omdraaien", + "Invert Colors": "Kleuren omkeren", + "Keep Aspect": "Aspect behouden", + "Length Tool": "Lengte gereedschap", + "Liver": "Lever", + "Lung": "Long", + "More Measure Tools": "Meer meetgereedschappen", + "Reset View": "Weergave herstellen", + "Rotate +90": "Draaien +90", + "Soft tissue": "Zacht weefsel", + "W/L Presets": "W/L Voorinstellingen", + "Window Level": "Venster/Niveau", + "Point": "Punt", + "Point Tool": "Punt gereedschap", + "Polygon": "Veelhoek", + "Polygon Tool": "Veelhoek gereedschap", + "Box": "Doos", + "Box Tool": "Doos gereedschap", + "Freehand Polygon": "Vrije hand veelhoek", + "Freehand Polygon Tool": "Vrije hand veelhoek gereedschap", + "Freehand Line": "Vrije hand lijn", + "Freehand Line Tool": "Vrije hand lijn gereedschap", + "Line": "Lijn", + "Line Tool": "Lijn gereedschap", + "3D Rotate": "3D Draaien", + "Shape": "Vorm", + "MPR": "MPR", + "Rectangle ROI Threshold": "Rechthoek ROI drempel", + "Select the PT Axial to enable this tool": "Selecteer de PT Axiaal om dit gereedschap in te schakelen", + "Create new segmentation to enable this tool.": "Maak een nieuwe segmentatie om dit gereedschap in te schakelen.", + "Freehand Segmentation": "Vrije hand segmentatie", + "Spline Contour Segmentation Tool": "Spline contour segmentatie gereedschap", + "Sculptor Tool": "Beeldhouwer gereedschap", + "Interpolate Contours": "Contouren interpoleren", + "Marker Mode": "Markeermodus", + "Include": "Opnemen", + "Exclude": "Uitsluiten", + "Clear Markers": "Markeringen wissen", + "Radius (mm)": "Straal (mm)", + "Dynamic": "Dynamisch", + "Range": "Bereik", + "Spline Type": "Spline type", + "Catmull Rom Spline": "Catmull Rom Spline", + "Linear Spline": "Lineaire Spline", + "B-Spline": "B-Spline", + "Simplified Spline": "Vereenvoudigde Spline", + "Dynamic Cursor Size": "Dynamische cursor grootte", + "No segmentations available": "Geen segmentaties beschikbaar", + "Threshold Tool": "Drempel gereedschap", + "Threshold Tools": "Drempel gereedschappen", + "Select a 3D viewport to enable this tool": "Selecteer een 3D viewport om dit gereedschap in te schakelen", + "Not available on the current viewport": "Niet beschikbaar op de huidige viewport", + "Select an MPR viewport to enable this tool": "Selecteer een MPR viewport om dit gereedschap in te schakelen", + "Interpolate Labelmap": "Labelmap interpoleren", + "Segment Bidirectional": "Segment bidirectioneel", + "Tool not available for this modality": "Gereedschap niet beschikbaar voor deze modaliteit", + "One Click Segment": "Eén klik segment", + "Labelmap Assist": "Labelmap assistentie", + "Marker Guided Labelmap": "Marker geleide labelmap", + "Eraser": "Gum", + "Shapes": "Vormen", + "Create new segmentation to enable shapes tool.": "Maak een nieuwe segmentatie om vormen gereedschap in te schakelen.", + "US Pleura B-line Annotation": "US Pleura B-lijn annotatie", + "Arrow Annotate Tool": "Pijl annotatie gereedschap" } diff --git a/platform/i18n/src/locales/nl/CaptureViewportModal.json b/platform/i18n/src/locales/nl/CaptureViewportModal.json new file mode 100644 index 00000000000..8aa546bf29a --- /dev/null +++ b/platform/i18n/src/locales/nl/CaptureViewportModal.json @@ -0,0 +1,9 @@ +{ + "File name": "Bestandsnaam", + "Image size": "Afbeelding grootte", + "Image size in pixels": "Afbeelding grootte in pixels", + "Include annotations": "Annotaties opnemen", + "Include warning message": "Waarschuwingsbericht opnemen", + "Width": "Breedte", + "Height": "Hoogte" +} diff --git a/platform/i18n/src/locales/nl/CineDialog.json b/platform/i18n/src/locales/nl/CineDialog.json new file mode 100644 index 00000000000..ec56e11e4a2 --- /dev/null +++ b/platform/i18n/src/locales/nl/CineDialog.json @@ -0,0 +1,8 @@ +{ + "Next image": "$t(Common:Next) $t(Common:Image)", + "Play / Stop": "$t(Common:Play) / $t(Common:Stop)", + "Previous image": "$t(Common:Previous) $t(Common:Image)", + "Skip to first image": "Spring naar eerste $t(Common:Image)", + "Skip to last image": "Spring naar laatste $t(Common:Image)", + "fps": "fps" +} diff --git a/platform/i18n/src/locales/nl/Colormaps.json b/platform/i18n/src/locales/nl/Colormaps.json new file mode 100644 index 00000000000..f432e06e956 --- /dev/null +++ b/platform/i18n/src/locales/nl/Colormaps.json @@ -0,0 +1,15 @@ +{ + "Grayscale": "Grijstinten", + "X Ray": "Röntgen", + "Isodose": "Isodose", + "hsv": "HSV", + "hot_iron": "Hete ijzer", + "red_hot": "Rood heet", + "s_pet": "PET", + "perfusion": "Perfusie", + "rainbow_2": "Regenboog 2", + "suv": "SUV", + "ge_256": "GE 256", + "ge": "GE", + "siemens": "Siemens" +} diff --git a/platform/i18n/src/locales/nl/Common.json b/platform/i18n/src/locales/nl/Common.json index d35764a7f41..a914509f43a 100644 --- a/platform/i18n/src/locales/nl/Common.json +++ b/platform/i18n/src/locales/nl/Common.json @@ -1,3 +1,29 @@ { - "More": "Meer" + "Back to": "Terug naar {{location}}", + "Close": "Sluiten", + "Image": "Afbeelding", + "Layout": "Indeling", + "LOAD": "LADEN", + "Measurements": "Metingen", + "mm": "mm", + "More": "Meer", + "Next": "Volgende", + "No": "Nee", + "NoStudyDate": "Geen studiedatum", + "Play": "Afspelen", + "Previous": "Vorige", + "Reset": "Herstellen", + "RowsPerPage": "rijen per pagina", + "Series": "Serie", + "Show": "Tonen", + "Stop": "Stoppen", + "StudyDate": "Studiedatum", + "Yes": "Ja", + "Cancel": "Annuleren", + "Save": "Opslaan", + "localDateFormat": "DD-MMM-YYYY", + "Back": "Terug", + "Foreground": "Voorgrond", + "SELECT A FOREGROUND": "SELECTEER EEN VOORGROND", + "SELECT A SEGMENTATION": "SELECTEER EEN SEGMENTATIE" } diff --git a/platform/i18n/src/locales/nl/DataRow.json b/platform/i18n/src/locales/nl/DataRow.json new file mode 100644 index 00000000000..2b9aff6b895 --- /dev/null +++ b/platform/i18n/src/locales/nl/DataRow.json @@ -0,0 +1,10 @@ +{ + "Hide": "Verbergen", + "Show": "Tonen", + "Rename": "Hernoemen", + "Duplicate": "Dupliceren", + "Delete": "Verwijderen", + "Change Color": "Kleur wijzigen", + "Lock": "Vergrendelen", + "Unlock": "Ontgrendelen" +} diff --git a/platform/i18n/src/locales/nl/DataSourceConfiguration.json b/platform/i18n/src/locales/nl/DataSourceConfiguration.json new file mode 100644 index 00000000000..85c2f0b26aa --- /dev/null +++ b/platform/i18n/src/locales/nl/DataSourceConfiguration.json @@ -0,0 +1,24 @@ +{ + "Configure Data Source": "Gegevensbron configureren", + "Data set": "Gegevensset", + "DICOM store": "DICOM opslag", + "Location": "Locatie", + "Project": "Project", + "Error fetching Data set list": "Fout bij ophalen van gegevenssets", + "Error fetching DICOM store list": "Fout bij ophalen van DICOM opslag", + "Error fetching Location list": "Fout bij ophalen van locaties", + "Error fetching Project list": "Fout bij ophalen van projecten", + "No Project available": "Geen projecten beschikbaar", + "No Location available": "Geen locaties beschikbaar", + "No Data set available": "Geen gegevenssets beschikbaar", + "No DICOM store available": "Geen DICOM opslag beschikbaar", + "Select": "Selecteren", + "Search Data set list": "Gegevenssets zoeken", + "Search DICOM store list": "DICOM opslag zoeken", + "Search Location list": "Locaties zoeken", + "Search Project list": "Projecten zoeken", + "Select Data set": "Selecteer een gegevensset", + "Select DICOM store": "Selecteer een DICOM opslag", + "Select Location": "Selecteer een locatie", + "Select Project": "Selecteer een project" +} diff --git a/platform/i18n/src/locales/nl/DatePicker.json b/platform/i18n/src/locales/nl/DatePicker.json new file mode 100644 index 00000000000..853fe40f1ac --- /dev/null +++ b/platform/i18n/src/locales/nl/DatePicker.json @@ -0,0 +1,13 @@ +{ + "Clear dates": "Datums wissen", + "Close": "$t(Common:Close)", + "End Date": "Einddatum", + "Last 7 days": "Laatste 7 dagen", + "Last 30 days": "Laatste 30 dagen", + "Start Date": "Startdatum", + "Today": "Vandaag", + "Previous Month": "Vorige maand", + "Next Month": "Volgende maand", + "Select Month": "Selecteer maand", + "Select Year": "Selecteer jaar" +} diff --git a/platform/i18n/src/locales/nl/ErrorBoundary.json b/platform/i18n/src/locales/nl/ErrorBoundary.json new file mode 100644 index 00000000000..bdcefa7d86c --- /dev/null +++ b/platform/i18n/src/locales/nl/ErrorBoundary.json @@ -0,0 +1,8 @@ +{ + "Context": "Context", + "Error Message": "Foutmelding", + "Something went wrong": "Er is iets misgegaan", + "in": "in", + "Sorry, something went wrong there. Try again.": "Sorry, er is iets misgegaan. Probeer opnieuw.", + "Stack Trace": "Stack Trace" +} diff --git a/platform/i18n/src/locales/nl/Header.json b/platform/i18n/src/locales/nl/Header.json index 38995e7b628..a3f7cf7dd8a 100644 --- a/platform/i18n/src/locales/nl/Header.json +++ b/platform/i18n/src/locales/nl/Header.json @@ -1,7 +1,9 @@ { "About": "Over", + "Back to Viewer": "Terug naar Viewer", "INVESTIGATIONAL USE ONLY": "ALLEEN VOOR ONDERZOEK", "Options": "Opties", "Preferences": "Voorkeuren", - "Study list": "Studie Overzicht" + "Study list": "Studie overzicht", + "Logout": "Uitloggen" } diff --git a/platform/i18n/src/locales/nl/HotkeysValidators.json b/platform/i18n/src/locales/nl/HotkeysValidators.json new file mode 100644 index 00000000000..0a1b6ccd879 --- /dev/null +++ b/platform/i18n/src/locales/nl/HotkeysValidators.json @@ -0,0 +1,6 @@ +{ + "Field can't be empty": "Veld mag niet leeg zijn", + "Hotkey is already in use": "\"{{action}}\" gebruikt al de \"{{pressedKeys}}\" sneltoets.", + "It's not possible to define only modifier keys (ctrl, alt and shift) as a shortcut": "Het is niet mogelijk om alleen modifier toetsen (ctrl, alt en shift) als sneltoets te definiëren", + "Shortcut combination is not allowed": "{{pressedKeys}} sneltoets combinatie is niet toegestaan" +} diff --git a/platform/i18n/src/locales/nl/Hps.json b/platform/i18n/src/locales/nl/Hps.json new file mode 100644 index 00000000000..6e2a280498d --- /dev/null +++ b/platform/i18n/src/locales/nl/Hps.json @@ -0,0 +1,14 @@ +{ + "MPR": "MPR", + "3D four up": "3D vier weergaven", + "Frame View": "Frame weergave", + "Frame view for the active series": "Frame weergave voor de actieve serie", + "3D main": "3D hoofd", + "mpr": "mpr", + "3D only": "Alleen 3D", + "3D primary": "3D primair", + "Axial Primary": "Axiaal primair", + "Compare Two Studies": "Twee studies vergelijken", + "Compare two studies in various layouts": "Twee studies vergelijken in verschillende indelingen", + "Mammography Breast Screening": "Mammografie borstscreening" +} diff --git a/platform/i18n/src/locales/nl/MeasurementTable.json b/platform/i18n/src/locales/nl/MeasurementTable.json new file mode 100644 index 00000000000..1e4e02ec849 --- /dev/null +++ b/platform/i18n/src/locales/nl/MeasurementTable.json @@ -0,0 +1,27 @@ +{ + "Criteria nonconformities": "Criteria niet-conformiteiten", + "Delete": "Verwijderen", + "Description": "Beschrijving", + "MAX": "MAX", + "Measurements": "Metingen", + "No, do not ask again": "Nee, vraag niet opnieuw", + "NonTargets": "Niet-doelwitten", + "Relabel": "Hernoemen", + "Targets": "Doelwitten", + "Rename": "Hernoemen", + "Duplicate": "Dupliceren", + "Change Color": "Kleur wijzigen", + "Lock": "Vergrendelen", + "Unlock": "Ontgrendelen", + "Hide": "Verbergen", + "Show": "Tonen", + "Create SR": "SR aanmaken", + "empty": "(leeg)", + "Track measurements for this series?": "Metingen voor deze serie volgen?", + "Do you want to add this measurement to the existing report?": "Wilt u deze meting toevoegen aan het bestaande rapport?", + "You have existing tracked measurements. What would you like to do with your existing tracked measurements?": "U heeft bestaande gevolgde metingen. Wat wilt u doen met uw bestaande gevolgde metingen?", + "Measurements cannot span across multiple studies. Do you want to save your tracked measurements?": "Metingen kunnen niet over meerdere studies verspreid zijn. Wilt u uw gevolgde metingen opslaan?", + "Do you want to continue tracking measurements for this study?": "Wilt u doorgaan met het volgen van metingen voor deze studie?", + "Do you want to open this Segmentation?": "Wilt u deze segmentatie openen?", + "There are unsaved measurements. Do you want to save it?": "Er zijn niet-opgeslagen metingen. Wilt u deze opslaan?" +} diff --git a/platform/i18n/src/locales/nl/Messages.json b/platform/i18n/src/locales/nl/Messages.json new file mode 100644 index 00000000000..85984d01c9b --- /dev/null +++ b/platform/i18n/src/locales/nl/Messages.json @@ -0,0 +1,18 @@ +{ + "Display Set Messages": "Weergave set berichten", + "1": "Geen geldige instanties gevonden in serie.", + "2": "Weergave set heeft ontbrekende positie-informatie.", + "3": "Weergave set is geen reconstrueerbaar 3D volume.", + "4": "Multi-frame weergave sets hebben geen pixelmeting informatie.", + "5": "Multi-frame weergave sets hebben geen oriëntatie-informatie.", + "6": "Multi-frame weergave sets hebben geen positie-informatie.", + "7": "Weergave set heeft ontbrekende frames.", + "8": "Weergave set heeft onregelmatige afstand.", + "9": "Weergave set heeft inconsistente afmetingen tussen frames.", + "10": "Weergave set heeft frames met inconsistente aantal componenten.", + "11": "Weergave set heeft frames met inconsistente oriëntaties.", + "12": "Weergave set heeft inconsistente positie-informatie.", + "13": "Niet-ondersteunde weergave set.", + "14": "SOP Class UID {{ sopClassUid }} wordt niet ondersteund.", + "15": "Weergave set mist een SOP Class UID. Controleer het bestand." +} diff --git a/platform/i18n/src/locales/nl/Modes.json b/platform/i18n/src/locales/nl/Modes.json new file mode 100644 index 00000000000..d1a9a10f46b --- /dev/null +++ b/platform/i18n/src/locales/nl/Modes.json @@ -0,0 +1,8 @@ +{ + "Basic Dev Viewer": "Basis Dev Viewer", + "Basic Test Mode": "Basis Test Modus", + "Basic Viewer": "Basis Viewer", + "Microscopy": "Microscopie", + "Segmentation": "Segmentatie", + "Total Metabolic Tumor Volume": "Totaal Metabool Tumor Volume" +} diff --git a/platform/i18n/src/locales/nl/Onboarding.json b/platform/i18n/src/locales/nl/Onboarding.json new file mode 100644 index 00000000000..ade538fd412 --- /dev/null +++ b/platform/i18n/src/locales/nl/Onboarding.json @@ -0,0 +1,27 @@ +{ + "Scrolling Through Images": "Scrollen door afbeeldingen", + "You can scroll through the images using the mouse wheel or scrollbar.": "U kunt door de afbeeldingen scrollen met het muiswiel of de scrollbalk.", + "Zooming In and Out": "In- en uitzoomen", + "You can zoom the images using the right click.": "U kunt de afbeeldingen zoomen met rechts klikken.", + "Panning the Image": "Het beeld verschuiven", + "You can pan the images using the middle click.": "U kunt het beeld verschuiven met de middelste klik.", + "Adjusting Window Level": "Venster/niveau aanpassen", + "You can modify the window level using the left click.": "U kunt het venster/niveau aanpassen met links klikken.", + "Using the Measurement Tools": "Meetgereedschappen gebruiken", + "You can measure the length of a region using the Length tool.": "U kunt de lengte van een regio meten met het Lengte gereedschap.", + "Drawing Length Annotations": "Lengte annotaties tekenen", + "Use the length tool on the viewport to measure the length of a region.": "Gebruik het lengte gereedschap op de viewport om de lengte van een regio te meten.", + "Tracking Measurements in the Panel": "Metingen volgen in het paneel", + "Click yes to track the measurements in the measurement panel.": "Klik ja om de metingen te volgen in het meetpaneel.", + "Opening the Measurements Panel": "Het meetpaneel openen", + "Click the measurements button to open the measurements panel.": "Klik op de metingen knop om het meetpaneel te openen.", + "Scrolling Away from a Measurement": "Wegscrollen van een meting", + "Scroll the images using the mouse wheel away from the measurement.": "Scroll de afbeeldingen met het muiswiel weg van de meting.", + "Jumping to Measurements in the Panel": "Springen naar metingen in het paneel", + "Click the measurement in the measurement panel to jump to it.": "Klik op de meting in het meetpaneel om ernaartoe te springen.", + "Changing Layout": "Indeling wijzigen", + "You can change the layout of the viewer using the layout button.": "U kunt de indeling van de viewer wijzigen met de indeling knop.", + "Selecting the MPR Layout": "MPR indeling selecteren", + "Select the MPR layout to view the images in MPR mode.": "Selecteer de MPR indeling om de afbeeldingen in MPR modus te bekijken.", + "Skip all": "Alles overslaan" +} diff --git a/platform/i18n/src/locales/nl/PanelSUV.json b/platform/i18n/src/locales/nl/PanelSUV.json new file mode 100644 index 00000000000..c163aee563c --- /dev/null +++ b/platform/i18n/src/locales/nl/PanelSUV.json @@ -0,0 +1,10 @@ +{ + "Patient Information": "Patiënt informatie", + "Patient Sex": "Patiënt geslacht", + "Weight": "Gewicht", + "Total Dose": "Totale dosis", + "Half Life": "Halfwaardetijd", + "Injection Time": "Injectietijd", + "Acquisition Time": "Verwervingstijd", + "Reload Data": "Gegevens herladen" +} diff --git a/platform/i18n/src/locales/nl/ROIThresholdConfiguration.json b/platform/i18n/src/locales/nl/ROIThresholdConfiguration.json new file mode 100644 index 00000000000..e25606c5982 --- /dev/null +++ b/platform/i18n/src/locales/nl/ROIThresholdConfiguration.json @@ -0,0 +1,11 @@ +{ + "Max": "Max", + "Range": "Bereik", + "Start": "Start", + "End": "Einde", + "Percentage of Max SUV": "Percentage van max SUV", + "Lower & Upper Ranges": "Onderste en bovenste bereiken", + "Run": "Uitvoeren", + "Threshold Tools": "Drempel gereedschappen", + "ThresholdRange": "Drempelbereik" +} diff --git a/platform/i18n/src/locales/nl/SegmentationPanel.json b/platform/i18n/src/locales/nl/SegmentationPanel.json new file mode 100644 index 00000000000..adab261b195 --- /dev/null +++ b/platform/i18n/src/locales/nl/SegmentationPanel.json @@ -0,0 +1,65 @@ +{ + "Active": "Actief", + "Add new segmentation": "Nieuwe segmentatie toevoegen", + "Add segment": "Segment toevoegen", + "Add segmentation": "Segmentatie toevoegen", + "Contour": "Contour", + "Contour tools": "Contour gereedschappen", + "Contour Segmentations": "Contour segmentaties", + "Delete": "Verwijderen", + "Display inactive segmentations": "Inactieve segmentaties weergeven", + "Add Segment": "Segment toevoegen", + "Download DICOM RTSS": "DICOM RTSS downloaden", + "Export DICOM SEG": "DICOM SEG exporteren", + "Download DICOM SEG": "DICOM SEG downloaden", + "Download DICOM RTSTRUCT": "DICOM RTSTRUCT downloaden", + "Fill": "Vullen", + "Inactive segmentations": "Inactieve segmentaties", + "Labelmap": "Labelmap", + "Labelmap tools": "Labelmap gereedschappen", + "Labelmap Segmentations": "Labelmap segmentaties", + "Opacity": "Doorzichtigheid", + "Border": "Rand", + "Show": "Tonen", + "Fill & Outline": "Vullen en omlijning", + "Outline Only": "Alleen omlijning", + "Fill Only": "Alleen vullen", + "Outline": "Omlijning", + "Rename": "Hernoemen", + "Segmentation": "Segmentatie", + "Segmentations": "Segmentaties", + "Segmentation not supported": "Segmentatie niet ondersteund", + "Size": "Grootte", + "Preview edits before creating": "Bewerkingen voorvertonen voor aanmaken", + "Use center as segment index": "Gebruik centrum als segment index", + "Hover on segment border to activate": "Beweeg over segment rand om te activeren", + "Show segment name on hover": "Toon segment naam bij zweven", + "Create New Segmentation": "Nieuwe segmentatie aanmaken", + "Manage Current Segmentation": "Huidige segmentatie beheren", + "Remove from Viewport": "Verwijderen uit viewport", + "Download & Export": "Downloaden & Exporteren", + "Download": "Downloaden", + "Export": "Exporteren", + "CSV Report": "CSV Rapport", + "DICOM SEG": "DICOM SEG", + "DICOM RTSS": "DICOM RTSS", + "Fill contour holes": "Contour gaten vullen", + "Fill Holes": "Gaten vullen", + "Remove Small Contours": "Kleine contouren verwijderen", + "Area Threshold": "Oppervlakte drempel", + "Create New Segment from Holes": "Nieuw segment aanmaken van gaten", + "Create New Segment": "Nieuw segment aanmaken", + "Smooth all edges": "Alle randen glad maken", + "Smooth Edges": "Randen glad maken", + "Remove extra points": "Extra punten verwijderen", + "Remove Points": "Punten verwijderen", + "Merge": "Samenvoegen", + "Intersect": "Snijden", + "Subtract": "Aftrekken", + "Select a segment": "Selecteer een segment", + "Create a new segment": "Nieuw segment aanmaken", + "New segment name": "Nieuw segment naam", + "No segmentations available": "Geen segmentaties beschikbaar", + "Not available on the current viewport": "Niet beschikbaar op de huidige viewport", + "Add segment to enable this tool": "Voeg een segment toe om dit gereedschap in te schakelen" +} diff --git a/platform/i18n/src/locales/nl/SidePanel.json b/platform/i18n/src/locales/nl/SidePanel.json new file mode 100644 index 00000000000..46462512c59 --- /dev/null +++ b/platform/i18n/src/locales/nl/SidePanel.json @@ -0,0 +1,4 @@ +{ + "Measurements": "Metingen", + "Studies": "Studieën" +} diff --git a/platform/i18n/src/locales/nl/StudyBrowser.json b/platform/i18n/src/locales/nl/StudyBrowser.json new file mode 100644 index 00000000000..cec8f827ce4 --- /dev/null +++ b/platform/i18n/src/locales/nl/StudyBrowser.json @@ -0,0 +1,12 @@ +{ + "Primary": "Primair", + "Recent": "Recent", + "All": "Alles", + "Tracked Series": "Gevolgde serie", + "Tag Browser": "Tag Browser", + "Add as Layer": "Toevoegen als laag", + "Series Number": "Serienummer", + "Series Date": "Seriedatum", + "Thumbnail Double Click": "Miniatuur dubbelklik", + "The selected display sets could not be added to the viewport.": "De geselecteerde weergave sets konden niet aan de viewport worden toegevoegd." +} diff --git a/platform/i18n/src/locales/nl/StudyItem.json b/platform/i18n/src/locales/nl/StudyItem.json new file mode 100644 index 00000000000..9963b468785 --- /dev/null +++ b/platform/i18n/src/locales/nl/StudyItem.json @@ -0,0 +1,3 @@ +{ + "Tracked series": "{{trackedSeries}} Gevolgde serie" +} diff --git a/platform/i18n/src/locales/nl/StudyList.json b/platform/i18n/src/locales/nl/StudyList.json new file mode 100644 index 00000000000..ac5ab0b83c8 --- /dev/null +++ b/platform/i18n/src/locales/nl/StudyList.json @@ -0,0 +1,21 @@ +{ + "AccessionNumber": "Accessie #", + "ClearFilters": "Filters wissen", + "Description": "Beschrijving", + "Empty": "Leeg", + "Filter list to 100 studies or less to enable sorting": "Filter de lijst tot 100 studies of minder om sorteren in te schakelen", + "Instances": "Instanties", + "Modality": "Modaliteit", + "MRN": "MRN", + "Next": "Volgende >", + "No studies available": "Geen studies beschikbaar", + "Number of studies": "Aantal studies", + "Page": "Pagina", + "PatientName": "Patiëntnaam", + "Previous": "< Terug", + "Results per page": "Resultaten per pagina", + "Studies": "Studieën", + "StudyDate": "Studiedatum", + "StudyList": "Studie lijst", + "Upload": "Uploaden" +} diff --git a/platform/i18n/src/locales/nl/ThumbnailTracked.json b/platform/i18n/src/locales/nl/ThumbnailTracked.json new file mode 100644 index 00000000000..8d6d7af0d28 --- /dev/null +++ b/platform/i18n/src/locales/nl/ThumbnailTracked.json @@ -0,0 +1,5 @@ +{ + "Series is tracked": "Serie wordt gevolgd", + "Series is untracked": "Serie wordt niet gevolgd", + "Viewport": "Viewport" +} diff --git a/platform/i18n/src/locales/nl/ToolbarLayoutSelector.json b/platform/i18n/src/locales/nl/ToolbarLayoutSelector.json new file mode 100644 index 00000000000..1004623f2ba --- /dev/null +++ b/platform/i18n/src/locales/nl/ToolbarLayoutSelector.json @@ -0,0 +1,9 @@ +{ + "Change layout": "Indeling wijzigen", + "Common": "Algemeen", + "Advanced": "Geavanceerd", + "Custom": "Aangepast", + "Hover to select": "Zweven om te selecteren", + "rows and columns": "rijen en kolommen", + "Click to apply": "Klik om toe te passen" +} diff --git a/platform/i18n/src/locales/nl/Tools.json b/platform/i18n/src/locales/nl/Tools.json new file mode 100644 index 00000000000..413b6d86c12 --- /dev/null +++ b/platform/i18n/src/locales/nl/Tools.json @@ -0,0 +1,13 @@ +{ + "Segmentation": "Segmentatie", + "Segment": "Segment", + "Download High Quality Image": "Hoge kwaliteit afbeelding downloaden", + "Edit Segment Label": "Segment label bewerken", + "Enter new label": "Voer nieuw label in", + "Segment Color": "Segment kleur", + "Edit Measurement Label": "Meting label bewerken", + "Edit Arrow Text": "Pijl tekst bewerken", + "Enter new text": "Voer nieuwe tekst in", + "Download Image": "Afbeelding downloaden", + "Image cannot be downloaded": "Afbeelding kan niet worden gedownload" +} diff --git a/platform/i18n/src/locales/nl/TooltipClipboard.json b/platform/i18n/src/locales/nl/TooltipClipboard.json new file mode 100644 index 00000000000..3ea4b7c6c9c --- /dev/null +++ b/platform/i18n/src/locales/nl/TooltipClipboard.json @@ -0,0 +1,4 @@ +{ + "Copied": "Gekopieerd", + "Failed to copy": "Kopiëren mislukt" +} diff --git a/platform/i18n/src/locales/nl/TrackedCornerstoneViewport.json b/platform/i18n/src/locales/nl/TrackedCornerstoneViewport.json new file mode 100644 index 00000000000..1b45e8ace8f --- /dev/null +++ b/platform/i18n/src/locales/nl/TrackedCornerstoneViewport.json @@ -0,0 +1,4 @@ +{ + "Series is tracked and can be viewed in the measurement panel": "Serie wordt gevolgd en kan worden bekeken in het meetpaneel", + "Measurements for untracked series will not be shown in the measurements panel": "Metingen voor niet-gevolgde series worden niet getoond in het meetpaneel" +} diff --git a/platform/i18n/src/locales/nl/UserPreferencesModal.json b/platform/i18n/src/locales/nl/UserPreferencesModal.json new file mode 100644 index 00000000000..7022c59cccf --- /dev/null +++ b/platform/i18n/src/locales/nl/UserPreferencesModal.json @@ -0,0 +1,84 @@ +{ + "Cancel": "$t(Buttons:Cancel)", + "No hotkeys found": "Geen sneltoetsen geconfigureerd voor deze applicatie. Sneltoetsen kunnen worden geconfigureerd in het app-config.js bestand van de applicatie.", + "Reset to defaults": "$t(Buttons:Reset to defaults)", + "ResetDefaultMessage": "Voorkeuren succesvol hersteld naar standaard.
U moet Opslaan om deze actie uit te voeren.", + "Save": "$t(Buttons:Save)", + "SaveMessage": "Voorkeuren opgeslagen", + "User preferences": "Gebruikersvoorkeuren", + "Language": "Taal", + "Select language": "Selecteer taal", + "Hotkeys": "Sneltoetsen", + "Zoom": "Inzoomen", + "Zoom In": "Inzoomen", + "Zoom Out": "Uitzoomen", + "Zoom to Fit": "Aanpassen aan scherm", + "Rotate Right": "Rechtsom draaien", + "Rotate Left": "Linksom draaien", + "Flip Horizontally": "Horizontaal omdraaien", + "Flip Vertically": "Verticaal omdraaien", + "Cine": "Cine", + "Invert": "Omkeren", + "Next Image Viewport": "Volgende afbeelding viewport", + "Previous Image Viewport": "Vorige afbeelding viewport", + "Previous Series": "Vorige serie", + "Next Series": "Volgende serie", + "Next Stage": "Volgende fase", + "Previous Stage": "Vorige fase", + "Next Image": "Volgende afbeelding", + "Previous Image": "Vorige afbeelding", + "First Image": "Eerste afbeelding", + "Last Image": "Laatste afbeelding", + "Reset": "Herstellen", + "Cancel Measurement": "Meting annuleren", + "W/L Preset 1": "W/L Voorinstelling 1", + "W/L Preset 2": "W/L Voorinstelling 2", + "W/L Preset 3": "W/L Voorinstelling 3", + "W/L Preset 4": "W/L Voorinstelling 4", + "Delete Annotation": "Annotatie verwijderen", + "Accept Preview": "Voorvertoning accepteren", + "Reject Preview": "Voorvertoning afwijzen", + "Undo": "Ongedaan maken", + "Redo": "Opnieuw doen", + "Interpolate Scroll": "Scroll interpoleren", + "Increase Brush Size": "Penseel grootte vergroten", + "Decrease Brush Size": "Penseel grootte verkleinen", + "Eraser": "Gum", + "Brush": "Penseel", + "Add New Segment": "Nieuw segment toevoegen", + "Press keys": "Druk toetsen...", + "LanguageName.en-US": "Engels (VS)", + "LanguageName.fr": "Frans", + "HotkeyKeys.ctrl": "Ctrl", + "HotkeyKeys.shift": "Shift", + "HotkeyKeys.alt": "Alt", + "HotkeyKeys.option": "Option", + "HotkeyKeys.meta": "Cmd", + "HotkeyKeys.enter": "Enter", + "HotkeyKeys.esc": "Esc", + "HotkeyKeys.space": "Spatie", + "HotkeyKeys.tab": "Tab", + "HotkeyKeys.backspace": "Backspace", + "HotkeyKeys.delete": "Delete", + "HotkeyKeys.insert": "Insert", + "HotkeyKeys.home": "Home", + "HotkeyKeys.end": "End", + "HotkeyKeys.pageup": "Pagina omhoog", + "HotkeyKeys.pagedown": "Pagina omlaag", + "HotkeyKeys.up": "Pijltje omhoog", + "HotkeyKeys.down": "Pijltje omlaag", + "HotkeyKeys.left": "Pijltje links", + "HotkeyKeys.right": "Pijltje rechts", + "HotkeyKeys.capslock": "Caps Lock", + "HotkeyKeys.plus": "Plus", + "HotkeyKeys.minus": "Minus", + "HotkeyKeys.comma": "Komma", + "HotkeyKeys.period": "Punt", + "HotkeyKeys.slash": "Schuine streep", + "HotkeyKeys.backslash": "Backslash", + "HotkeyKeys.semicolon": "Puntkomma", + "HotkeyKeys.quote": "Aanhalingsteken", + "HotkeyKeys.backquote": "Accent grave", + "HotkeyKeys.bracketleft": "Linker haak", + "HotkeyKeys.bracketright": "Rechter haak" +} diff --git a/platform/i18n/src/locales/nl/ViewportDownloadForm.json b/platform/i18n/src/locales/nl/ViewportDownloadForm.json new file mode 100644 index 00000000000..487d36790d9 --- /dev/null +++ b/platform/i18n/src/locales/nl/ViewportDownloadForm.json @@ -0,0 +1,14 @@ +{ + "emptyFilenameError": "De bestandsnaam kan niet leeg zijn.", + "fileType": "Bestandstype", + "filename": "Bestandsnaam", + "formTitle": "Geef de afmetingen, bestandsnaam en gewenst type op voor de uitvoerafbeelding.", + "imageHeight": "Afbeelding hoogte (px)", + "imagePreview": "Afbeelding voorvertoning", + "imageWidth": "Afbeelding breedte (px)", + "keepAspectRatio": "Behoud aspectverhouding", + "loadingPreview": "Afbeelding voorvertoning laden...", + "minHeightError": "De minimum geldige hoogte is 100px.", + "minWidthError": "De minimum geldige breedte is 100px.", + "showAnnotations": "Annotaties tonen" +} diff --git a/platform/i18n/src/locales/nl/WindowLevelActionMenu.json b/platform/i18n/src/locales/nl/WindowLevelActionMenu.json new file mode 100644 index 00000000000..f7ac4738c77 --- /dev/null +++ b/platform/i18n/src/locales/nl/WindowLevelActionMenu.json @@ -0,0 +1,19 @@ +{ + "Back to Display Options": "Terug naar weergave opties", + "Modality Presets": "Modaliteit voorinstellingen", + "Modality Window Presets": "Modaliteit venster voorinstellingen", + "Display Color bar": "Kleurenbalk weergeven", + "Color LUT": "Kleur LUT", + "Preview in viewport": "Voorvertoning in viewport", + "Grayscale": "Grijstinten", + "Rendering Options": "Render opties", + "Rendering Presets": "Render voorinstellingen", + "Search all": "Alles zoeken", + "Quality": "Kwaliteit", + "Lighting": "Verlichting", + "Shade": "Schaduw", + "Ambient": "Omgevingslicht", + "Diffuse": "Diffuus", + "Specular": "Spiegelend", + "Shift": "Verschuiving" +} diff --git a/platform/i18n/src/locales/nl/index.js b/platform/i18n/src/locales/nl/index.js index de42f561e8d..d1703aca0af 100644 --- a/platform/i18n/src/locales/nl/index.js +++ b/platform/i18n/src/locales/nl/index.js @@ -1,11 +1,69 @@ +import AboutModal from './AboutModal.json'; import Buttons from './Buttons.json'; +import CineDialog from './CineDialog.json'; import Common from './Common.json'; +import DataSourceConfiguration from './DataSourceConfiguration.json'; +import DatePicker from './DatePicker.json'; +import ErrorBoundary from './ErrorBoundary.json'; import Header from './Header.json'; +import HotkeysValidators from './HotkeysValidators.json'; +import MeasurementTable from './MeasurementTable.json'; +import DataRow from './DataRow.json'; +import Modes from './Modes.json'; +import SegmentationPanel from './SegmentationPanel.json'; +import SidePanel from './SidePanel.json'; +import StudyBrowser from './StudyBrowser.json'; +import StudyItem from './StudyItem.json'; +import StudyList from './StudyList.json'; +import TooltipClipboard from './TooltipClipboard.json'; +import ThumbnailTracked from './ThumbnailTracked.json'; +import TrackedCornerstoneViewport from './TrackedCornerstoneViewport.json'; +import UserPreferencesModal from './UserPreferencesModal.json'; +import ViewportDownloadForm from './ViewportDownloadForm.json'; +import Messages from './Messages.json'; +import WindowLevelActionMenu from './WindowLevelActionMenu.json'; +import CaptureViewportModal from './CaptureViewportModal.json'; +import Hps from './Hps.json'; +import ToolbarLayoutSelector from './ToolbarLayoutSelector.json'; +import Tools from './Tools.json'; +import Onboarding from './Onboarding.json'; +import Colormaps from './Colormaps.json'; +import PanelSUV from './PanelSUV.json'; +import ROIThresholdConfiguration from './ROIThresholdConfiguration.json'; export default { nl: { + AboutModal, Buttons, + CineDialog, Common, + DataSourceConfiguration, + DatePicker, + ErrorBoundary, Header, + HotkeysValidators, + MeasurementTable, + DataRow, + Modes, + SegmentationPanel, + SidePanel, + StudyBrowser, + StudyItem, + StudyList, + TooltipClipboard, + ThumbnailTracked, + TrackedCornerstoneViewport, + UserPreferencesModal, + ViewportDownloadForm, + Messages, + WindowLevelActionMenu, + CaptureViewportModal, + Hps, + ToolbarLayoutSelector, + Tools, + Onboarding, + Colormaps, + PanelSUV, + ROIThresholdConfiguration, }, }; diff --git a/platform/i18n/src/locales/pt-BR/DataRow.json b/platform/i18n/src/locales/pt-BR/DataRow.json new file mode 100644 index 00000000000..ae47e568479 --- /dev/null +++ b/platform/i18n/src/locales/pt-BR/DataRow.json @@ -0,0 +1,10 @@ +{ + "Hide": "Ocultar", + "Show": "Mostrar", + "Rename": "Renomear", + "Duplicate": "Duplicar", + "Delete": "Excluir", + "Change Color": "Alterar cor", + "Lock": "Bloquear", + "Unlock": "Desbloquear" +} diff --git a/platform/i18n/src/locales/pt-BR/index.js b/platform/i18n/src/locales/pt-BR/index.js index 2dcc04c2b17..6b3bd64d31b 100644 --- a/platform/i18n/src/locales/pt-BR/index.js +++ b/platform/i18n/src/locales/pt-BR/index.js @@ -6,6 +6,7 @@ import DatePicker from './DatePicker.json'; import Header from './Header.json'; import UserPreferencesModal from './UserPreferencesModal.json'; import MeasurementTable from './MeasurementTable.json'; +import DataRow from './DataRow.json'; import Messages from './Messages.json'; export default { @@ -18,6 +19,7 @@ export default { Header, UserPreferencesModal, MeasurementTable, + DataRow, Messages, }, }; diff --git a/platform/i18n/src/locales/ru/DataRow.json b/platform/i18n/src/locales/ru/DataRow.json new file mode 100644 index 00000000000..01aaa2384eb --- /dev/null +++ b/platform/i18n/src/locales/ru/DataRow.json @@ -0,0 +1,10 @@ +{ + "Hide": "Скрыть", + "Show": "Показать", + "Rename": "Переименовать", + "Duplicate": "Дублировать", + "Delete": "Удалить", + "Change Color": "Изменить цвет", + "Lock": "Заблокировать", + "Unlock": "Разблокировать" +} diff --git a/platform/i18n/src/locales/ru/SegmentationTable.json b/platform/i18n/src/locales/ru/SegmentationPanel.json similarity index 69% rename from platform/i18n/src/locales/ru/SegmentationTable.json rename to platform/i18n/src/locales/ru/SegmentationPanel.json index 461aba73e3f..cffb151b884 100644 --- a/platform/i18n/src/locales/ru/SegmentationTable.json +++ b/platform/i18n/src/locales/ru/SegmentationPanel.json @@ -14,5 +14,8 @@ "Outline": "Контур", "Rename": "Переименовать", "Segmentation": "Сегментация", - "Size": "Размер" + "Size": "Размер", + "No segmentations available": "Нет доступных сегментаций", + "Not available on the current viewport": "Недоступно в текущем окне просмотра", + "Add segment to enable this tool": "Добавьте сегмент, чтобы включить этот инструмент" } diff --git a/platform/i18n/src/locales/ru/index.js b/platform/i18n/src/locales/ru/index.js index f53aa5de538..a6ba6e59f8d 100644 --- a/platform/i18n/src/locales/ru/index.js +++ b/platform/i18n/src/locales/ru/index.js @@ -9,8 +9,9 @@ import Header from './Header.json'; import HotkeysValidators from './HotkeysValidators.json'; import InvestigationalUseDialog from './InvestigationalUseDialog.json'; import MeasurementTable from './MeasurementTable.json'; +import DataRow from './DataRow.json'; import Modes from './Modes.json'; -import SegmentationTable from './SegmentationTable.json'; +import SegmentationPanel from './SegmentationPanel.json'; import SidePanel from './SidePanel.json'; import StudyBrowser from './StudyBrowser.json'; import StudyItem from './StudyItem.json'; @@ -36,8 +37,9 @@ export default { HotkeysValidators, InvestigationalUseDialog, MeasurementTable, + DataRow, Modes, - SegmentationTable, + SegmentationPanel, SidePanel, StudyBrowser, StudyItem, diff --git a/platform/i18n/src/locales/test-LNG/Buttons.json b/platform/i18n/src/locales/test-LNG/Buttons.json index 999309aa2b5..c92896576d9 100644 --- a/platform/i18n/src/locales/test-LNG/Buttons.json +++ b/platform/i18n/src/locales/test-LNG/Buttons.json @@ -1,12 +1,15 @@ { "Acquired": "Test Acquired", "Angle": "Test Angle", + "Annotation": "Test Annotation", "Axial": "Test Axial", "Bidirectional": "Test Bidirectional", "Brush": "Test Brush", - "CINE": "Test CINE", + "Cine": "Test Cine", "Cancel": "Test Cancel", + "Capture": "Test Capture", "Circle": "Test Circle", + "Sphere": "Test Sphere", "Clear": "Test Clear", "Coronal": "Test Coronal", "Crosshairs": "Test Crosshairs", @@ -14,8 +17,10 @@ "Ellipse": "Test Ellipse", "Elliptical": "Test Elliptical", "Flip H": "Test Flip H", + "Flip Horizontally": "Test Flip Horizontally", "Flip V": "Test Flip V", "Freehand": "Test Freehand", + "Grid Layout": "Test Grid Layout", "Invert": "Test Invert", "Layout": "Test $t(Common:Layout)", "Length": "Test Length", @@ -24,6 +29,7 @@ "Manual": "Test Manual", "Measurements": "Test Measurements", "More": "Test $t(Common:More)", + "More Tools": "Test More Tools", "Next": "Test $t(Common:Next)", "Pan": "Test Pan", "Play": "Test $t(Common:Play)", @@ -31,24 +37,124 @@ "Probe": "Test Probe", "ROI Window": "Test ROI Window", "Rectangle": "Test Rectangle", + "Reference Lines": "Test Reference Lines", "Reset": "Test $t(Common:Reset)", - "Reset to defaults": "Test $t(Common:Reset) to Defaults", + "Reset to defaults": "Test Reset to defaults", "Rotate Right": "Test Rotate Right", "Sagittal": "Test Sagittal", "Save": "Test Save", "Stack Scroll": "Test Stack Scroll", + "Stack Image Sync": "Test Stack Image Sync", "Stop": "Test $t(Common:Stop)", "Themes": "Test Themes", "Zoom": "Test Zoom", - "Grid Layout": "Test Grid Layout", - "W/L Presets": "Test W/L Presets", - "More Measure Tools": "Test More Measure Tools", - "More Tools": "Test More Tools", - "Capture": "Test Capture", - "Annotation": "Test Annotation", - "Soft Tissue": "Test Soft Tissue", - "Lung": "Test Lung", - "Liver": "Test Liver", + "Adjust window/level presets and customize image contrast settings": "Test Adjust window/level presets and customize image contrast settings", + "Zoom-in": "Test Zoom-in", + "Cobb Angle": "Test Cobb Angle", + "Calibration": "Test Calibration", + "Dicom Tag Browser": "Test Dicom Tag Browser", + "Magnify Probe": "Test Magnify Probe", + "Ultrasound Directional": "Test Ultrasound Directional", + "Window Level Region": "Test Window Level Region", + "Image Overlay": "Test Image Overlay", + "Image Slice Sync": "Test Image Slice Sync", + "Freehand ROI": "Test Freehand ROI", + "Spline ROI": "Test Spline ROI", + "Livewire tool": "Test Livewire tool", + "Livewire Contour": "Test Livewire Contour", + "Segment Label Display": "Test Segment Label Display", + "Data Overlay": "Test Data Overlay", + "Configure data overlay options and manage foreground/background display sets": "Test Configure data overlay options and manage foreground/background display sets", + "Orientation": "Test Orientation", + "Change viewport orientation between axial, sagittal, coronal and reformat planes": "Test Change viewport orientation between axial, sagittal, coronal and reformat planes", + "Status": "Test Status", + "Navigation": "Test Navigation", + "Navigate between segments/measurements and manage their visibility": "Test Navigate between segments/measurements and manage their visibility", + "Tracking Status": "Test Tracking Status", + "View and manage tracking status of measurements and annotations": "Test View and manage tracking status of measurements and annotations", + "Advanced Window Level": "Test Advanced Window Level", + "Advanced window/level settings with manual controls and presets": "Test Advanced window/level settings with manual controls and presets", + "Threshold": "Test Threshold", + "Image threshold settings": "Test Image threshold settings", + "Opacity": "Test Opacity", + "Image opacity settings": "Test Image opacity settings", + "Colorbar": "Test Colorbar", + "Enable position synchronization on stack viewports": "Test Enable position synchronization on stack viewports", + "Show Reference Lines": "Test Show Reference Lines", + "Toggle Image Overlay": "Test Toggle Image Overlay", + "Calibration Line": "Test Calibration Line", + "Ellipse ROI": "Test Ellipse ROI", + "Rectangle ROI": "Test Rectangle ROI", + "Circle Tool": "Test Circle Tool", + "Click to show or hide segment labels when hovering with your mouse.": "Test Click to show or hide segment labels when hovering with your mouse.", + "Arrow Annotate": "Test Arrow Annotate", + "Bidirectional Tool": "Test Bidirectional Tool", "Bone": "Test Bone", - "Cine": "Test Cine" + "Brain": "Test Brain", + "Dismiss Aspect": "Test Dismiss Aspect", + "Ellipse Tool": "Test Ellipse Tool", + "Flip Horizontal": "Test Flip Horizontal", + "Invert Colors": "Test Invert Colors", + "Keep Aspect": "Test Keep Aspect", + "Length Tool": "Test Length Tool", + "Liver": "Test Liver", + "Lung": "Test Lung", + "More Measure Tools": "Test More Measure Tools", + "Reset View": "Test Reset View", + "Rotate +90": "Test Rotate +90", + "Soft tissue": "Test Soft tissue", + "W/L Presets": "Test W/L Presets", + "Window Level": "Test Window Level", + "Point": "Test Point", + "Point Tool": "Test Point Tool", + "Polygon": "Test Polygon", + "Polygon Tool": "Test Polygon Tool", + "Box": "Test Box", + "Box Tool": "Test Box Tool", + "Freehand Polygon": "Test Freehand Polygon", + "Freehand Polygon Tool": "Test Freehand Polygon Tool", + "Freehand Line": "Test Freehand Line", + "Freehand Line Tool": "Test Freehand Line Tool", + "Line": "Test Line", + "Line Tool": "Test Line Tool", + "3D Rotate": "Test 3D Rotate", + "Shape": "Test Shape", + "MPR": "Test MPR", + "Rectangle ROI Threshold": "Test Rectangle ROI Threshold", + "Select the PT Axial to enable this tool": "Test Select the PT Axial to enable this tool", + "Create new segmentation to enable this tool.": "Test Create new segmentation to enable this tool.", + "Freehand Segmentation": "Test Freehand Segmentation", + "Spline Contour Segmentation Tool": "Test Spline Contour Segmentation Tool", + "Sculptor Tool": "Test Sculptor Tool", + "Interpolate Contours": "Test Interpolate Contours", + "Marker Mode": "Test Marker Mode", + "Include": "Test Include", + "Exclude": "Test Exclude", + "Clear Markers": "Test Clear Markers", + "Radius (mm)": "Test Radius (mm)", + "Dynamic": "Test Dynamic", + "Range": "Test Range", + "Spline Type": "Test Spline Type", + "Catmull Rom Spline": "Test Catmull Rom Spline", + "Linear Spline": "Test Linear Spline", + "B-Spline": "Test B-Spline", + "Simplified Spline": "Test Simplified Spline", + "Dynamic Cursor Size": "Test Dynamic Cursor Size", + "No segmentations available": "Test No segmentations available", + "Threshold Tool": "Test Threshold Tool", + "Threshold Tools": "Test Threshold Tools", + "Select a 3D viewport to enable this tool": "Test Select a 3D viewport to enable this tool", + "Not available on the current viewport": "Test Not available on the current viewport", + "Select an MPR viewport to enable this tool": "Test Select an MPR viewport to enable this tool", + "Interpolate Labelmap": "Test Interpolate Labelmap", + "Segment Bidirectional": "Test Segment Bidirectional", + "Tool not available for this modality": "Test Tool not available for this modality", + "One Click Segment": "Test One Click Segment", + "Labelmap Assist": "Test Labelmap Assist", + "Marker Guided Labelmap": "Test Marker Guided Labelmap", + "Eraser": "Test Eraser", + "Shapes": "Test Shapes", + "Create new segmentation to enable shapes tool.": "Test Create new segmentation to enable shapes tool.", + "US Pleura B-line Annotation": "Test US Pleura B-line Annotation", + "Arrow Annotate Tool": "Test Arrow Annotate Tool" } diff --git a/platform/i18n/src/locales/test-LNG/CaptureViewportModal.json b/platform/i18n/src/locales/test-LNG/CaptureViewportModal.json new file mode 100644 index 00000000000..6fe85588af2 --- /dev/null +++ b/platform/i18n/src/locales/test-LNG/CaptureViewportModal.json @@ -0,0 +1,9 @@ +{ + "File name": "Test File name", + "Image size": "Test Image size", + "Image size in pixels": "Test Image size in pixels", + "Include annotations": "Test Include annotations", + "Include warning message": "Test Include warning message", + "Width": "Test Width", + "Height": "Test Height" +} diff --git a/platform/i18n/src/locales/test-LNG/Colormaps.json b/platform/i18n/src/locales/test-LNG/Colormaps.json new file mode 100644 index 00000000000..d4a725b31d0 --- /dev/null +++ b/platform/i18n/src/locales/test-LNG/Colormaps.json @@ -0,0 +1,15 @@ +{ + "Grayscale": "Test Grayscale", + "X Ray": "Test X Ray", + "Isodose": "Test Isodose", + "hsv": "Test HSV", + "hot_iron": "Test Hot Iron", + "red_hot": "Test Red Hot", + "s_pet": "Test PET", + "perfusion": "Test Perfusion", + "rainbow_2": "Test Rainbow 2", + "suv": "Test SUV", + "ge_256": "Test GE 256", + "ge": "Test GE", + "siemens": "Test Siemens" +} diff --git a/platform/i18n/src/locales/test-LNG/Common.json b/platform/i18n/src/locales/test-LNG/Common.json index a809080484e..82ab6c0f1e9 100644 --- a/platform/i18n/src/locales/test-LNG/Common.json +++ b/platform/i18n/src/locales/test-LNG/Common.json @@ -15,5 +15,15 @@ "Show": "Test Show", "Stop": "Test Stop", "StudyDate": "Test Study Date", - "Yes": "Test Yes" + "Yes": "Test Yes", + "Cancel": "Test Cancel", + "Save": "Test Save", + "Back to": "Test Back to {{location}}", + "LOAD": "Test LOAD", + "NoStudyDate": "Test No Study Date", + "localDateFormat": "\\T\\e\\s\\t MM/DD/YYYY", + "Back": "Test Back", + "Foreground": "Test Foreground", + "SELECT A FOREGROUND": "Test SELECT A FOREGROUND", + "SELECT A SEGMENTATION": "Test SELECT A SEGMENTATION" } diff --git a/platform/i18n/src/locales/test-LNG/DataRow.json b/platform/i18n/src/locales/test-LNG/DataRow.json new file mode 100644 index 00000000000..c5903642ebb --- /dev/null +++ b/platform/i18n/src/locales/test-LNG/DataRow.json @@ -0,0 +1,10 @@ +{ + "Hide": "Test Hide", + "Show": "Test Show", + "Rename": "Test Rename", + "Duplicate": "Test Duplicate", + "Delete": "Test Delete", + "Change Color": "Test Change Color", + "Lock": "Test Lock", + "Unlock": "Test Unlock" +} diff --git a/platform/i18n/src/locales/test-LNG/DataSourceConfiguration.json b/platform/i18n/src/locales/test-LNG/DataSourceConfiguration.json new file mode 100644 index 00000000000..6739a048d1e --- /dev/null +++ b/platform/i18n/src/locales/test-LNG/DataSourceConfiguration.json @@ -0,0 +1,24 @@ +{ + "Configure Data Source": "Test Configure Data Source", + "Data set": "Test Data set", + "DICOM store": "Test DICOM store", + "Location": "Test Location", + "Project": "Test Project", + "Error fetching Data set list": "Test Error fetching data sets", + "Error fetching DICOM store list": "Test Error fetching DICOM stores", + "Error fetching Location list": "Test Error fetching locations", + "Error fetching Project list": "Test Error fetching projects", + "No Project available": "Test No projects available", + "No Location available": "Test No locations available", + "No Data set available": "Test No data sets available", + "No DICOM store available": "Test No DICOM stores available", + "Select": "Test Select", + "Search Data set list": "Test Search data sets", + "Search DICOM store list": "Test Search DICOM stores", + "Search Location list": "Test Search locations", + "Search Project list": "Test Search projects", + "Select Data set": "Test Select a data Set", + "Select DICOM store": "Test Select a DICOM store", + "Select Location": "Test Select a location", + "Select Project": "Test Select a project" +} diff --git a/platform/i18n/src/locales/test-LNG/Hps.json b/platform/i18n/src/locales/test-LNG/Hps.json new file mode 100644 index 00000000000..d3fe1b9c5e5 --- /dev/null +++ b/platform/i18n/src/locales/test-LNG/Hps.json @@ -0,0 +1,14 @@ +{ + "MPR": "Test MPR", + "3D four up": "Test 3D four up", + "Frame View": "Test Frame View", + "Frame view for the active series": "Test Frame view for the active series", + "3D main": "Test 3D main", + "mpr": "Test mpr", + "3D only": "Test 3D only", + "3D primary": "Test 3D primary", + "Axial Primary": "Test Axial Primary", + "Compare Two Studies": "Test Compare Two Studies", + "Compare two studies in various layouts": "Test Compare two studies in various layouts", + "Mammography Breast Screening": "Test Mammography Breast Screening" +} diff --git a/platform/i18n/src/locales/test-LNG/Messages.json b/platform/i18n/src/locales/test-LNG/Messages.json index acf153cac98..31aff7ccb2b 100644 --- a/platform/i18n/src/locales/test-LNG/Messages.json +++ b/platform/i18n/src/locales/test-LNG/Messages.json @@ -12,5 +12,7 @@ "10": "Test Display set has frames with inconsistent number of components.", "11": "Test Display set has frames with inconsistent orientations.", "12": "Test Display set has inconsistent position information.", - "13": "Test Unsupported display set." + "13": "Test Unsupported display set.", + "14": "Test SOP Class UID {{ sopClassUid }} is not supported.", + "15": "Test Display Set is missing a SOP Class UID. Please check the file." } diff --git a/platform/i18n/src/locales/test-LNG/Modes.json b/platform/i18n/src/locales/test-LNG/Modes.json index a9f03e23274..a563bd5a111 100644 --- a/platform/i18n/src/locales/test-LNG/Modes.json +++ b/platform/i18n/src/locales/test-LNG/Modes.json @@ -4,5 +4,6 @@ "Basic Viewer": "Test Basic Viewer", "Microscopy": "Test Microscopy", "Segmentation": "Test Segmentation", - "Total Metabolic Tumor Volume": "Test Total Metabolic Tumor Volume" + "Total Metabolic Tumor Volume": "Test Total Metabolic Tumor Volume", + "US Pleura B-line Annotations": "Test US Pleura B-line Annotations" } diff --git a/platform/i18n/src/locales/test-LNG/Onboarding.json b/platform/i18n/src/locales/test-LNG/Onboarding.json new file mode 100644 index 00000000000..c4b696b93f8 --- /dev/null +++ b/platform/i18n/src/locales/test-LNG/Onboarding.json @@ -0,0 +1,27 @@ +{ + "Scrolling Through Images": "Test Scrolling Through Images", + "You can scroll through the images using the mouse wheel or scrollbar.": "Test You can scroll through the images using the mouse wheel or scrollbar.", + "Zooming In and Out": "Test Zooming In and Out", + "You can zoom the images using the right click.": "Test You can zoom the images using the right click.", + "Panning the Image": "Test Panning the Image", + "You can pan the images using the middle click.": "Test You can pan the images using the middle click.", + "Adjusting Window Level": "Test Adjusting Window Level", + "You can modify the window level using the left click.": "Test You can modify the window level using the left click.", + "Using the Measurement Tools": "Test Using the Measurement Tools", + "You can measure the length of a region using the Length tool.": "Test You can measure the length of a region using the Length tool.", + "Drawing Length Annotations": "Test Drawing Length Annotations", + "Use the length tool on the viewport to measure the length of a region.": "Test Use the length tool on the viewport to measure the length of a region.", + "Tracking Measurements in the Panel": "Test Tracking Measurements in the Panel", + "Click yes to track the measurements in the measurement panel.": "Test Click yes to track the measurements in the measurement panel.", + "Opening the Measurements Panel": "Test Opening the Measurements Panel", + "Click the measurements button to open the measurements panel.": "Test Click the measurements button to open the measurements panel.", + "Scrolling Away from a Measurement": "Test Scrolling Away from a Measurement", + "Scroll the images using the mouse wheel away from the measurement.": "Test Scroll the images using the mouse wheel away from the measurement.", + "Jumping to Measurements in the Panel": "Test Jumping to Measurements in the Panel", + "Click the measurement in the measurement panel to jump to it.": "Test Click the measurement in the measurement panel to jump to it.", + "Changing Layout": "Test Changing Layout", + "You can change the layout of the viewer using the layout button.": "Test You can change the layout of the viewer using the layout button.", + "Selecting the MPR Layout": "Test Selecting the MPR Layout", + "Select the MPR layout to view the images in MPR mode.": "Test Select the MPR layout to view the images in MPR mode.", + "Skip all": "Test Skip all" +} diff --git a/platform/i18n/src/locales/test-LNG/PanelSUV.json b/platform/i18n/src/locales/test-LNG/PanelSUV.json new file mode 100644 index 00000000000..90d59e53e61 --- /dev/null +++ b/platform/i18n/src/locales/test-LNG/PanelSUV.json @@ -0,0 +1,10 @@ +{ + "Patient Information": "Test Patient Information", + "Patient Sex": "Test Patient Sex", + "Weight": "Test Weight", + "Total Dose": "Test Total Dose", + "Half Life": "Test Half Life", + "Injection Time": "Test Injection Time", + "Acquisition Time": "Test Acquisition Time", + "Reload Data": "Test Reload Data" +} diff --git a/platform/i18n/src/locales/test-LNG/ROIThresholdConfiguration.json b/platform/i18n/src/locales/test-LNG/ROIThresholdConfiguration.json new file mode 100644 index 00000000000..1e9655d1a73 --- /dev/null +++ b/platform/i18n/src/locales/test-LNG/ROIThresholdConfiguration.json @@ -0,0 +1,11 @@ +{ + "Max": "Test Max", + "Range": "Test Range", + "Start": "Test Start", + "End": "Test End", + "Percentage of Max SUV": "Test Percentage of Max SUV", + "Lower & Upper Ranges": "Test Lower & Upper Ranges", + "Run": "Test Run", + "Threshold Tools": "Test Threshold Tools", + "ThresholdRange": "Test Threshold Range" +} diff --git a/platform/i18n/src/locales/test-LNG/SegmentationPanel.json b/platform/i18n/src/locales/test-LNG/SegmentationPanel.json new file mode 100644 index 00000000000..f4aa60c1db2 --- /dev/null +++ b/platform/i18n/src/locales/test-LNG/SegmentationPanel.json @@ -0,0 +1,66 @@ +{ + "Active": "Test Active", + "Add new segmentation": "Test Add new segmentation", + "Add segment": "Test Add segment", + "Add segmentation": "Test Add segmentation", + "Contour": "Test Contour", + "Contour tools": "Test Contour tools", + "Contour Segmentations": "Contour segmentations", + "Delete": "Test Delete", + "Display inactive segmentations": "Test Display inactive segmentations", + "Add Segment": "Test Add Segment", + "Download DICOM RTSS": "Test Download DICOM RTSS", + "Export DICOM SEG": "Test Export DICOM SEG", + "Download DICOM SEG": "Test Download DICOM SEG", + "Download DICOM RTSTRUCT": "Test Download DICOM RTSTRUCT", + "Fill": "Test Fill", + "Inactive segmentations": "Test Inactive segmentations", + "Labelmap": "Test Label map", + "Labelmap tools": "Test Label map tools", + "Labelmap Segmentations": "Test Label map segmentations", + "Opacity": "Test Opacity", + "Border": "Test Border", + "Show": "Test Show", + "Fill & Outline": "Test Fill & Outline", + "Outline Only": "Test Outline Only", + "Fill Only": "Test Fill Only", + "Outline": "Test Outline", + "Rename": "Test Rename", + "Segmentation": "Test Segmentation", + "Segmentations": "Test Segmentations", + "Segmentation not supported": "Test Segmentation not supported", + "Size": "Test Size", + "Preview edits before creating": "Test Preview edits before creating", + "Use center as segment index": "Test Use center as segment index", + "Hover on segment border to activate": "Test Hover on segment border to activate", + "Show segment name on hover": "Test Show segment name on hover", + "Create New Segmentation": "Test Create New Segmentation", + "Manage Current Segmentation": "Test Manage Current Segmentation", + "Remove from Viewport": "Test Remove from Viewport", + "Download & Export": "Test Download & Export", + "Download": "Test Download", + "Export": "Test Export", + "CSV Report": "Test CSV Report", + "DICOM SEG": "Test DICOM SEG", + "DICOM RTSS": "Test DICOM RTSS", + "Fill contour holes": "Test Fill contour holes", + "Fill Holes": "Test Fill Holes", + "Remove Small Contours": "Test Remove Small Contours", + "Area Threshold": "Test Area Threshold", + "Create New Segment from Holes": "Test Create New Segment from Holes", + "Create New Segment": "Test Create New Segment", + "Smooth all edges": "Test Smooth all edges", + "Smooth Edges": "Test Smooth Edges", + "Remove extra points": "Test Remove extra points", + "Remove Points": "Test Remove Points", + "Merge": "Test Merge", + "Intersect": "Test Intersect", + "Subtract": "Test Subtract", + "Select a segment": "Test Select a segment", + "Select a segmentation": "Test Select a segmentation", + "Create a new segment": "Test Create a new segment", + "New segment name": "Test New segment name", + "No segmentations available": "Test No segmentations available", + "Not available on the current viewport": "Test Not available on the current viewport", + "Add segment to enable this tool": "Test Add segment to enable this tool" +} diff --git a/platform/i18n/src/locales/test-LNG/SegmentationTable.json b/platform/i18n/src/locales/test-LNG/SegmentationTable.json deleted file mode 100644 index 45f6aae720b..00000000000 --- a/platform/i18n/src/locales/test-LNG/SegmentationTable.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "Active": "Test Active", - "Add new segmentation": "Test Add new segmentation", - "Add segment": "Test Add segment", - "Add segmentation": "Test Add segmentation", - "Delete": "Test Delete", - "Display inactive segmentations": "Test Display inactive segmentations", - "Export DICOM SEG": "Test Export DICOM SEG", - "Download DICOM SEG": "Test Download DICOM SEG", - "Download DICOM RTSTRUCT": "Test Download DICOM RTSTRUCT", - "Fill": "Test Fill", - "Inactive segmentations": "Test Inactive segmentations", - "Opacity": "Test Opacity", - "Outline": "Test Outline", - "Rename": "Test Rename", - "Segmentation": "Test Segmentation", - "Size": "Test Size" -} diff --git a/platform/i18n/src/locales/test-LNG/StudyBrowser.json b/platform/i18n/src/locales/test-LNG/StudyBrowser.json index 160a466dfac..220b9f6a007 100644 --- a/platform/i18n/src/locales/test-LNG/StudyBrowser.json +++ b/platform/i18n/src/locales/test-LNG/StudyBrowser.json @@ -1,5 +1,12 @@ { "Primary": "Test Primary", "Recent": "Test Recent", - "All": "Test All" + "All": "Test All", + "Tracked Series": "Test Tracked Series", + "Tag Browser": "Test Tag Browser", + "Add as Layer": "Test Add as Layer", + "Series Number": "Test Series Number", + "Series Date": "Test Series Date", + "Thumbnail Double Click": "Test Thumbnail Double Click", + "The selected display sets could not be added to the viewport.": "Test The selected display sets could not be added to the viewport." } diff --git a/platform/i18n/src/locales/test-LNG/ToolbarLayoutSelector.json b/platform/i18n/src/locales/test-LNG/ToolbarLayoutSelector.json new file mode 100644 index 00000000000..70b5768d4ac --- /dev/null +++ b/platform/i18n/src/locales/test-LNG/ToolbarLayoutSelector.json @@ -0,0 +1,9 @@ +{ + "Change layout": "Test Change layout", + "Common": "Test Common", + "Advanced": "Test Advanced", + "Custom": "Test Custom", + "Hover to select": "Test Hover to select", + "rows and columns": "Test rows and columns", + "Click to apply": "Test Click to apply" +} diff --git a/platform/i18n/src/locales/test-LNG/Tools.json b/platform/i18n/src/locales/test-LNG/Tools.json new file mode 100644 index 00000000000..2ec495ba45c --- /dev/null +++ b/platform/i18n/src/locales/test-LNG/Tools.json @@ -0,0 +1,13 @@ +{ + "Segmentation": " Test Segmentation", + "Segment": " Test Segment", + "Download High Quality Image": " Test Download High Quality Image", + "Edit Segment Label": " Test Edit Segment Label", + "Enter new label": " Test Enter new label", + "Segment Color": " Test Segment Color", + "Edit Measurement Label": " Test Edit Measurement Label", + "Edit Arrow Text": " Test Edit Arrow Text", + "Enter new text": " Test Enter new text", + "Download Image": " Test Download Image", + "Image cannot be downloaded": " Test Image cannot be downloaded" +} diff --git a/platform/i18n/src/locales/test-LNG/USAnnotationPanel.json b/platform/i18n/src/locales/test-LNG/USAnnotationPanel.json new file mode 100644 index 00000000000..49bb7453eef --- /dev/null +++ b/platform/i18n/src/locales/test-LNG/USAnnotationPanel.json @@ -0,0 +1,17 @@ +{ + "Workflow": "Test Workflow", + "Depth guide toggle": "Test Depth guide toggle", + "Show pleura percentage": "Test Show pleura percentage", + "Sector Annotations": "Test Sector Annotations", + "Pleura line": "Test Pleura line", + "B-line": "Test B-line", + "B-line annotation": "Test B-line annotation", + "Pleura annotation": "Test Pleura annotation", + "Show Overlay": "Test Show Overlay", + "Annotated Frames": "Test Annotated Frames", + "Frame": "Test Frame", + "Pleura lines": "Test Pleura lines", + "B-lines": "Test B-lines", + "JSON": "Test JSON", + "Annotations": "Test Annotations" +} diff --git a/platform/i18n/src/locales/test-LNG/UserPreferencesModal.json b/platform/i18n/src/locales/test-LNG/UserPreferencesModal.json index e6c003feccb..ccc262aecaa 100644 --- a/platform/i18n/src/locales/test-LNG/UserPreferencesModal.json +++ b/platform/i18n/src/locales/test-LNG/UserPreferencesModal.json @@ -9,6 +9,79 @@ "Function": "Test function", "Shortcut": "Test shortcut", "Language": "Test language", + "Select language": "Test Select language", "Hotkeys": "Test hotkeys", - "General": "Test general" + "General": "Test general", + "Zoom": "Test Zoom", + "Zoom In": "Test Zoom In", + "Zoom Out": "Test Zoom Out", + "Zoom to Fit": "Test Zoom to Fit", + "Rotate Right": "Test Rotate Right", + "Rotate Left": "Test Rotate Left", + "Flip Horizontally": "Test Flip Horizontally", + "Flip Vertically": "Test Flip Vertically", + "Cine": "Test Cine", + "Invert": "Test Invert", + "Next Image Viewport": "Test Next Image Viewport", + "Previous Image Viewport": "Test Previous Image Viewport", + "Previous Series": "Test Previous Series", + "Next Series": "Test Next Series", + "Next Stage": "Test Next Stage", + "Previous Stage": "Test Previous Stage", + "Next Image": "Test Next Image", + "Previous Image": "Test Previous Image", + "First Image": "Test First Image", + "Last Image": "Test Last Image", + "Reset": "Test Reset", + "Cancel Measurement": "Test Cancel Measurement", + "W/L Preset 1": "Test W/L Preset 1", + "W/L Preset 2": "Test W/L Preset 2", + "W/L Preset 3": "Test W/L Preset 3", + "W/L Preset 4": "Test W/L Preset 4", + "Delete Annotation": "Test Delete Annotation", + "Accept Preview": "Test Accept Preview", + "Reject Preview": "Test Reject Preview", + "Undo": "Test Undo", + "Redo": "Test Redo", + "Interpolate Scroll": "Test Interpolate Scroll", + "Increase Brush Size": "Test Increase Brush Size", + "Decrease Brush Size": "Test Decrease Brush Size", + "Eraser": "Test Eraser", + "Brush": "Test Brush", + "Add New Segment": "Test Add New Segment", + "Press keys": "Test Press keys...", + "LanguageName.en-US": "Test English (US)", + "LanguageName.fr": "Test French", + "HotkeyKeys.ctrl": "Test Ctrl", + "HotkeyKeys.shift": "Test Shift", + "HotkeyKeys.alt": "Test Alt", + "HotkeyKeys.option": "Test Option", + "HotkeyKeys.meta": "Test Cmd", + "HotkeyKeys.enter": "Test Enter", + "HotkeyKeys.esc": "Test Esc", + "HotkeyKeys.space": "Test Space", + "HotkeyKeys.tab": "Test Tab", + "HotkeyKeys.backspace": "Test Backspace", + "HotkeyKeys.delete": "Test Delete", + "HotkeyKeys.insert": "Test Insert", + "HotkeyKeys.home": "Test Home", + "HotkeyKeys.end": "Test End", + "HotkeyKeys.pageup": "Test Page Up", + "HotkeyKeys.pagedown": "Test Page Down", + "HotkeyKeys.up": "Test Up Arrow", + "HotkeyKeys.down": "Test Down Arrow", + "HotkeyKeys.left": "Test Left Arrow", + "HotkeyKeys.right": "Test Right Arrow", + "HotkeyKeys.capslock": "Test Caps Lock", + "HotkeyKeys.plus": "Test Plus", + "HotkeyKeys.minus": "Test Minus", + "HotkeyKeys.comma": "Test Comma", + "HotkeyKeys.period": "Test Period", + "HotkeyKeys.slash": "Test Slash", + "HotkeyKeys.backslash": "Test Backslash", + "HotkeyKeys.semicolon": "Test Semicolon", + "HotkeyKeys.quote": "Test Quote", + "HotkeyKeys.backquote": "Test Backtick", + "HotkeyKeys.bracketleft": "Test Left Bracket", + "HotkeyKeys.bracketright": "Test Right Bracket" } diff --git a/platform/i18n/src/locales/test-LNG/WindowLevelActionMenu.json b/platform/i18n/src/locales/test-LNG/WindowLevelActionMenu.json index 13c1c82281a..d52caf1fc63 100644 --- a/platform/i18n/src/locales/test-LNG/WindowLevelActionMenu.json +++ b/platform/i18n/src/locales/test-LNG/WindowLevelActionMenu.json @@ -1,5 +1,19 @@ { "Back to Display Options": "Test Back to Display Options", - "Modality Presets": "Test {{modality}} Presets", - "Modality Window Presets": "Test {{modality}} Window Presets" + "Modality Presets": "Test Modality Presets", + "Modality Window Presets": "Test Modality Window Presets", + "Display Color bar": "Test Display Color bar", + "Color LUT": "Test Color LUT", + "Preview in viewport": "Test Preview in viewport", + "Grayscale": "Test Grayscale", + "Rendering Options": "Test Rendering Options", + "Rendering Presets": "Test Rendering Presets", + "Search all": "Test Search all", + "Quality": "Test Quality", + "Lighting": "Test Lighting", + "Shade": "Test Shade", + "Ambient": "Test Ambient", + "Diffuse": "Test Diffuse", + "Specular": "Test Specular", + "Shift": "Test Shift" } diff --git a/platform/i18n/src/locales/test-LNG/index.js b/platform/i18n/src/locales/test-LNG/index.js index 16997ce50cc..64059c1bf11 100644 --- a/platform/i18n/src/locales/test-LNG/index.js +++ b/platform/i18n/src/locales/test-LNG/index.js @@ -2,16 +2,22 @@ import AboutModal from './AboutModal.json'; import Buttons from './Buttons.json'; import CineDialog from './CineDialog.json'; import Common from './Common.json'; +import Colormaps from './Colormaps.json'; +import DataSourceConfiguration from './DataSourceConfiguration.json'; import DatePicker from './DatePicker.json'; import ErrorBoundary from './ErrorBoundary.json'; import Header from './Header.json'; import HotkeysValidators from './HotkeysValidators.json'; import MeasurementTable from './MeasurementTable.json'; +import DataRow from './DataRow.json'; import Messages from './Messages.json'; import Modals from './Modals.json'; import Modes from './Modes.json'; +import Onboarding from './Onboarding.json'; +import PanelSUV from './PanelSUV.json'; import PatientInfo from './PatientInfo.json'; -import SegmentationTable from './SegmentationTable.json'; +import ROIThresholdConfiguration from './ROIThresholdConfiguration.json'; +import SegmentationPanel from './SegmentationPanel.json'; import SidePanel from './SidePanel.json'; import StudyBrowser from './StudyBrowser.json'; import StudyItem from './StudyItem.json'; @@ -23,6 +29,11 @@ import TrackedCornerstoneViewport from './TrackedCornerstoneViewport.json'; import UserPreferencesModal from './UserPreferencesModal.json'; import ViewportDownloadForm from './ViewportDownloadForm.json'; import WindowLevelActionMenu from './WindowLevelActionMenu.json'; +import CaptureViewportModal from './CaptureViewportModal.json'; +import Tools from './Tools.json'; +import Hps from './Hps.json'; +import ToolbarLayoutSelector from './ToolbarLayoutSelector.json'; +import USAnnotationPanel from './USAnnotationPanel.json'; export default { 'test-LNG': { @@ -30,16 +41,22 @@ export default { Buttons, CineDialog, Common, + Colormaps, + DataSourceConfiguration, DatePicker, ErrorBoundary, Header, HotkeysValidators, MeasurementTable, + DataRow, Messages, Modals, Modes, + Onboarding, + PanelSUV, PatientInfo, - SegmentationTable, + ROIThresholdConfiguration, + SegmentationPanel, SidePanel, StudyBrowser, StudyItem, @@ -51,5 +68,10 @@ export default { UserPreferencesModal, ViewportDownloadForm, WindowLevelActionMenu, + CaptureViewportModal, + Tools, + Hps, + ToolbarLayoutSelector, + USAnnotationPanel, }, }; diff --git a/platform/i18n/src/locales/tr-TR/DataRow.json b/platform/i18n/src/locales/tr-TR/DataRow.json new file mode 100644 index 00000000000..b1439e097a2 --- /dev/null +++ b/platform/i18n/src/locales/tr-TR/DataRow.json @@ -0,0 +1,10 @@ +{ + "Hide": "Gizle", + "Show": "Göster", + "Rename": "Yeniden Adlandır", + "Duplicate": "Çoğalt", + "Delete": "Sil", + "Change Color": "Rengi Değiştir", + "Lock": "Kilitle", + "Unlock": "Kilidi Aç" +} diff --git a/platform/i18n/src/locales/tr-TR/index.js b/platform/i18n/src/locales/tr-TR/index.js index 174822bd3cd..7594ff0e827 100644 --- a/platform/i18n/src/locales/tr-TR/index.js +++ b/platform/i18n/src/locales/tr-TR/index.js @@ -5,6 +5,7 @@ import Common from './Common.json'; import DatePicker from './DatePicker.json'; import Header from './Header.json'; import MeasurementTable from './MeasurementTable.json'; +import DataRow from './DataRow.json'; import StudyList from './StudyList.json'; import UserPreferencesModal from './UserPreferencesModal.json'; import ViewportDownloadForm from './ViewportDownloadForm.json'; @@ -18,6 +19,7 @@ export default { DatePicker, Header, MeasurementTable, + DataRow, StudyList, UserPreferencesModal, ViewportDownloadForm, diff --git a/platform/i18n/src/locales/zh/Buttons.json b/platform/i18n/src/locales/zh/Buttons.json index fe99f012365..89d903fa9d3 100644 --- a/platform/i18n/src/locales/zh/Buttons.json +++ b/platform/i18n/src/locales/zh/Buttons.json @@ -2,38 +2,37 @@ "Acquired": "已获取", "Angle": "角度", "Annotation": "注释", - "Arrow Annotate": "标注", + "Arrow Annotate": "箭头标注", "Axial": "轴状面", - "Bidirectional Tool": "双向", - "Bidirectional": "双向", + "Bidirectional Tool": "双向测量", + "Bidirectional": "双向测量", "Bone": "骨窗", "Brain": "脑窗", - "Brush": "橡皮擦", + "Brush": "勾画工具", "Cancel": "取消", - "Capture": "下载", - "CINE": "播放动画", - "Cine": "连续播放", + "Capture": "图像捕获", + "Cine": "动态播放", "Circle": "圆", "Clear": "清除", "Coronal": "冠状面", "Crosshairs": "十字线", - "Dismiss Aspect": "解除Aspect", - "Ellipse Tool": "椭圆", + "Dismiss Aspect": "自由缩放", + "Ellipse Tool": "椭圆工具", "Ellipse": "椭圆", - "Elliptical": "椭圆的", - "Flip H": "左右翻转", + "Elliptical": "椭圆形", + "Flip H": "水平翻转", "Flip Horizontal": "水平翻转", - "Flip Horizontally": "左右翻转", + "Flip Horizontally": "水平翻转", "Flip V": "上下翻转", "Freehand": "自由画线", "Grid Layout": "窗口布局", "Invert Colors": "灰度反转", "Invert": "灰度反转", - "Keep Aspect": "保持Aspect", - "Layout": "显示窗口", - "Length Tool": "长度", + "Keep Aspect": "保持宽高比", + "Layout": "$t(Common:Layout)", + "Length Tool": "长度工具", "Length": "长度", - "Levels": "层级", + "Levels": "窗宽窗位", "Liver": "肝窗", "Lung": "肺窗", "Magnify": "放大镜", @@ -41,28 +40,97 @@ "Measurements": "测量", "More Measure Tools": "更多测量工具", "More Tools": "更多工具", - "More": "更多", - "Next": "下一个", + "More": "$t(Common:More)", + "Next": "$t(Common:Next)", "Pan": "移动", - "Play": "播放", - "Previous": "上一个", + "Play": "$t(Common:Play)", + "Previous": "$t(Common:Previous)", "Probe": "探针", "Rectangle": "矩形", "Reference Lines": "参考线", - "Reset to defaults": "返回默认", - "Reset View": "复原", - "Reset": "复原", - "ROI Window": "选择对比度", - "Rotate +90": "顺时针旋转", + "Reset to defaults": "恢复默认设置", + "Reset View": "重置视图", + "Reset": "$t(Common:Reset)", + "ROI Window": "ROI窗口", + "Rotate +90": "顺时针旋转90°", "Rotate Right": "顺时针旋转", "Sagittal": "矢状面", "Save": "保存", "Soft tissue": "软组织窗", - "Stack Image Sync": "影像联动", - "Stack Scroll": "滑动切换图层", - "Stop": "停止", + "Stack Image Sync": "图像切片同步", + "Stack Scroll": "切片滚动", + "Stop": "$t(Common:Stop)", "Themes": "主题", - "W/L Presets": "窗位预设", + "W/L Presets": "窗宽/窗位预设", "Window Level": "窗位", - "Zoom": "放大" + "Zoom": "放大", + "Adjust window/level presets and customize image contrast settings": "调整窗宽/窗位预设并自定义图像对比度", + "Zoom-in": "放大", + "Cobb Angle": "Cobb 角", + "Calibration": "定标", + "Dicom Tag Browser": "DICOM 标签浏览器", + "Magnify Probe": "放大探针", + "Ultrasound Directional": "超声方向标注", + "Window Level Region": "局部窗位", + "Image Overlay": "图像叠加", + "Image Slice Sync": "图像切片同步", + "Freehand ROI": "手绘ROI", + "Spline ROI": "样条ROI", + "Livewire tool": "智能边缘工具", + "Segment Label Display": "分段标签显示", + "Data Overlay": "数据叠加", + "Configure data overlay options and manage foreground/background display sets": "配置数据叠加选项并管理前景/背景显示集", + "Orientation": "方向", + "Change viewport orientation between axial, sagittal, coronal and reformat planes": "在轴状面、矢状面、冠状面和重组平面之间切换视图方向", + "Status": "状态", + "Navigation": "导航", + "Navigate between segments/measurements and manage their visibility": "在分段/测量之间导航并管理其可见性", + "Tracking Status": "跟踪状态", + "View and manage tracking status of measurements and annotations": "查看和管理测量和注释的跟踪状态", + "Advanced Window Level": "高级窗位", + "Advanced window/level settings with manual controls and presets": "高级窗宽/窗位设置,手动控制和预设", + "Threshold": "阈值", + "Image threshold settings": "图像阈值设置", + "Opacity": "不透明度", + "Image opacity settings": "图像不透明度设置", + "Colorbar": "颜色条", + "Enable position synchronization on stack viewports": "启用堆栈视图端口的位置同步", + "Show Reference Lines": "显示参考线", + "Toggle Image Overlay": "切换图像叠加", + "Calibration Line": "定标线", + "Ellipse ROI": "椭圆ROI", + "Rectangle ROI": "矩形ROI", + "Circle Tool": "圆工具", + "Click to show or hide segment labels when hovering with your mouse.": "点击以在鼠标悬停时显示或隐藏分段标签", + "Download": "下载", + "Point": "点", + "Point Tool": "点工具", + "Polygon": "多边形", + "Polygon Tool": "多边形工具", + "Box": "框", + "Box Tool": "框工具", + "Freehand Polygon": "手绘多边形", + "Freehand Polygon Tool": "手绘多边形工具", + "Freehand Line": "手绘线", + "Freehand Line Tool": "手绘线工具", + "Line": "线", + "Line Tool": "线工具", + "3D Rotate": "3D旋转", + "MPR": "MPR", + "Rectangle ROI Threshold": "矩形ROI阈值", + "Select the PT Axial to enable this tool": "选择PT轴向以启用此工具", + "Create new segmentation to enable this tool.": "创建新分割以启用此工具。", + "Threshold Tool": "阈值工具", + "Select a 3D viewport to enable this tool": "选择3D视口以启用此工具", + "Select an MPR viewport to enable this tool": "选择MPR视口以启用此工具", + "Interpolate Labelmap": "插值标签图", + "Segment Bidirectional": "分割双向", + "One Click Segment": "一键分割", + "Labelmap Assist": "标签图辅助", + "Marker Guided Labelmap": "标记引导标签图", + "Eraser": "橡皮擦", + "Shapes": "形状", + "Create new segmentation to enable shapes tool.": "创建新分割以启用形状工具。", + "US Pleura B-line Annotation": "US胸膜B线注释", + "Arrow Annotate Tool": "箭头注释工具" } diff --git a/platform/i18n/src/locales/zh/CaptureViewportModal.json b/platform/i18n/src/locales/zh/CaptureViewportModal.json new file mode 100644 index 00000000000..3ed6b60a7ff --- /dev/null +++ b/platform/i18n/src/locales/zh/CaptureViewportModal.json @@ -0,0 +1,6 @@ +{ + "File name": "文件名", + "Image size": "图片大小", + "Include annotations": "包含注释", + "Include warning message": "包含警告信息" +} diff --git a/platform/i18n/src/locales/zh/Common.json b/platform/i18n/src/locales/zh/Common.json index 10ce931e9d1..4fda7ee5187 100644 --- a/platform/i18n/src/locales/zh/Common.json +++ b/platform/i18n/src/locales/zh/Common.json @@ -11,5 +11,16 @@ "Series": "序列", "Show": "显示", "Stop": "停止", - "StudyDate": "时间" + "StudyDate": "时间", + "localDateFormat": "YYYY-MM-DD", + "Yes": "是", + "No": "否", + "Cancel": "取消", + "Save": "保存", + "Back to": "返回 {{location}}", + "Close": "关闭", + "LOAD": "加载", + "mm": "mm", + "NoStudyDate": "无时间", + "Back": "返回" } diff --git a/platform/i18n/src/locales/zh/DataRow.json b/platform/i18n/src/locales/zh/DataRow.json new file mode 100644 index 00000000000..40a203acbed --- /dev/null +++ b/platform/i18n/src/locales/zh/DataRow.json @@ -0,0 +1,10 @@ +{ + "Hide": "隐藏", + "Show": "显示", + "Rename": "重命名", + "Duplicate": "复制", + "Delete": "删除", + "Change Color": "更改颜色", + "Lock": "锁定", + "Unlock": "解锁" +} diff --git a/platform/i18n/src/locales/zh/Hps.json b/platform/i18n/src/locales/zh/Hps.json new file mode 100644 index 00000000000..ee468395e9f --- /dev/null +++ b/platform/i18n/src/locales/zh/Hps.json @@ -0,0 +1,11 @@ +{ + "MPR": "多平面重建", + "3D four up": "三维四窗", + "Frame View": "单帧视图", + "Frame view for the active series": "当前序列的单帧视图", + "3D main": "三维主视图", + "mpr": "多平面重建", + "3D only": "仅三维视图", + "3D primary": "三维主视图", + "Axial Primary": "轴状面主视图" +} diff --git a/platform/i18n/src/locales/zh/StudyBrowser.json b/platform/i18n/src/locales/zh/StudyBrowser.json index a4a1e78cece..2487307e29d 100644 --- a/platform/i18n/src/locales/zh/StudyBrowser.json +++ b/platform/i18n/src/locales/zh/StudyBrowser.json @@ -2,5 +2,11 @@ "Primary": "当前", "Recent": "最近", "All": "全部", - "Tracked Series": "个跟踪序列" + "Tracked Series": "跟踪序列", + "Tag Browser": "标签浏览", + "Add as Layer": "添加为图层", + "Series Number": "序列编号", + "Series Date": "序列时间", + "Thumbnail Double Click": "缩略图双击", + "The selected display sets could not be added to the viewport.": "所选显示集无法添加到视图端口。" } diff --git a/platform/i18n/src/locales/zh/ToolbarLayoutSelector.json b/platform/i18n/src/locales/zh/ToolbarLayoutSelector.json new file mode 100644 index 00000000000..90d0c66f4b1 --- /dev/null +++ b/platform/i18n/src/locales/zh/ToolbarLayoutSelector.json @@ -0,0 +1,9 @@ +{ + "Change layout": "改变布局", + "Common": "通用", + "Advanced": "高级", + "Custom": "自定义", + "Hover to select": "悬停以选择", + "rows and columns": "行和列", + "Click to apply": "点击应用" +} diff --git a/platform/i18n/src/locales/zh/Tools.json b/platform/i18n/src/locales/zh/Tools.json new file mode 100644 index 00000000000..0312a1ed373 --- /dev/null +++ b/platform/i18n/src/locales/zh/Tools.json @@ -0,0 +1,13 @@ +{ + "Segmentation": "分割", + "Segment": "分割", + "Download High Quality Image": "下载高清图像", + "Edit Segment Label": "编辑分割标签", + "Enter new label": "输入新标签", + "Segment Color": "分割颜色", + "Edit Measurement Label": "编辑测量标签", + "Edit Arrow Text": "编辑箭头文本", + "Enter new text": "输入新文本", + "Download Image": "下载图片", + "Image cannot be downloaded": "图片无法下载" +} diff --git a/platform/i18n/src/locales/zh/WindowLevelActionMenu.json b/platform/i18n/src/locales/zh/WindowLevelActionMenu.json new file mode 100644 index 00000000000..93d26643093 --- /dev/null +++ b/platform/i18n/src/locales/zh/WindowLevelActionMenu.json @@ -0,0 +1,10 @@ +{ + "Display Color bar": "显示颜色条", + "Color LUT": "颜色查找表", + "Modality Window Presets": "模态窗口预设", + "Rendering Options": "渲染选项", + "Preview in viewport": "在视图中预览", + "Grayscale": "灰度", + "Back to Display Options": "返回显示选项", + "Modality Presets": "模态预设" +} diff --git a/platform/i18n/src/locales/zh/index.js b/platform/i18n/src/locales/zh/index.js index 4e2517c1022..43a1f821784 100644 --- a/platform/i18n/src/locales/zh/index.js +++ b/platform/i18n/src/locales/zh/index.js @@ -5,6 +5,7 @@ import Common from './Common.json'; import DatePicker from './DatePicker.json'; import Header from './Header.json'; import MeasurementTable from './MeasurementTable.json'; +import DataRow from './DataRow.json'; import StudyList from './StudyList.json'; import UserPreferencesModal from './UserPreferencesModal.json'; import ViewportDownloadForm from './ViewportDownloadForm.json'; @@ -18,9 +19,14 @@ import Dialog from './Dialog.json'; import Modals from './Modals.json'; import Local from './Local.json'; import ErrorBoundary from './ErrorBoundary.json'; +import WindowLevelActionMenu from './WindowLevelActionMenu.json'; +import CaptureViewportModal from './CaptureViewportModal.json'; +import Hps from './Hps.json'; +import ToolbarLayoutSelector from './ToolbarLayoutSelector.json'; +import Tools from './Tools.json'; export default { - 'zh': { + zh: { AboutModal, Buttons, CineDialog, @@ -28,6 +34,7 @@ export default { DatePicker, Header, MeasurementTable, + DataRow, StudyList, UserPreferencesModal, ViewportDownloadForm, @@ -41,5 +48,10 @@ export default { Modals, Local, ErrorBoundary, + WindowLevelActionMenu, + CaptureViewportModal, + Hps, + ToolbarLayoutSelector, + Tools, }, -}; \ No newline at end of file +}; diff --git a/platform/ui-next/CHANGELOG.md b/platform/ui-next/CHANGELOG.md index a96a249ac82..1c991264ca8 100644 --- a/platform/ui-next/CHANGELOG.md +++ b/platform/ui-next/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/ui-next @@ -11,7 +11,1141 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.123](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.122...v3.12.0-beta.123) (2026-01-14) + + +### Bug Fixes + +* **window-level-action-menu:** Window level menu no longer needs two clicks to open after it is used ([#5711](https://github.com/OHIF/Viewers/issues/5711)) ([eb6b814](https://github.com/OHIF/Viewers/commit/eb6b814fc9c35feb6aa5f4378632b53202b0d8a3)) + + + + + +# [3.12.0-beta.122](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.121...v3.12.0-beta.122) (2026-01-13) + + +### Bug Fixes + +* **segmentation:** List surface representations in the segmentation table for 3D views. ([#5700](https://github.com/OHIF/Viewers/issues/5700)) ([82e6a18](https://github.com/OHIF/Viewers/commit/82e6a18735b2e2278d6b95037d82bd3cb529104d)) + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.105...v3.12.0-beta.106) (2025-12-16) + + +### Bug Fixes + +* preclinical mode not working ([#5631](https://github.com/OHIF/Viewers/issues/5631)) ([2de81ef](https://github.com/OHIF/Viewers/commit/2de81efa7e6be534972cd763271c038b293d86f0)) + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.101...v3.12.0-beta.102) (2025-12-10) + + +### Bug Fixes + +* **routes:** display 404 feedback page for unmatched URLs ([#5627](https://github.com/OHIF/Viewers/issues/5627)) ([eef755e](https://github.com/OHIF/Viewers/commit/eef755ef05e38d5271b1c79e4dd191d723895470)) + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.99...v3.12.0-beta.100) (2025-12-05) + + +### Features + +* **notification:** add custom notification component support ([#5605](https://github.com/OHIF/Viewers/issues/5605)) ([c4e5a46](https://github.com/OHIF/Viewers/commit/c4e5a4616db87b39df5e16b75f1ba3c18407468f)) + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.97...v3.12.0-beta.98) (2025-12-03) + + +### Bug Fixes + +* **clipboard:** Adds tooltip clipboard icon feedback when copy fails ([#5609](https://github.com/OHIF/Viewers/issues/5609)) ([6566354](https://github.com/OHIF/Viewers/commit/6566354986583e5d7db01c8734e282faf7a072b9)) + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25) + + +### Features + +* tooltips for tool settings ([#5586](https://github.com/OHIF/Viewers/issues/5586)) ([411553e](https://github.com/OHIF/Viewers/commit/411553ee916b8a63b2a934a7e1274bc449041016)) + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19) + + +### Bug Fixes + +* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f)) + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21) + + +### Bug Fixes + +* **segmentation:** Fixed the manual scrolling of the segments list and implemented automatic scrolling to the active segment. ([#5510](https://github.com/OHIF/Viewers/issues/5510)) ([1df4f84](https://github.com/OHIF/Viewers/commit/1df4f843fe3c377643938563ff9e2e4f930d8537)) + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18) + + +### Bug Fixes + +* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462)) + + + + + +# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17) + + +### Bug Fixes + +* **ErrorBoundary:** Allow for details to be shown in production. ([#5504](https://github.com/OHIF/Viewers/issues/5504)) ([4620cc3](https://github.com/OHIF/Viewers/commit/4620cc3acf89f218c3feaaf4c153a3dc8b023b23)) + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.46](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.45...v3.12.0-beta.46) (2025-10-06) + + +### Bug Fixes + +* viewport ready event firing logic ([#5462](https://github.com/OHIF/Viewers/issues/5462)) ([d2ae13b](https://github.com/OHIF/Viewers/commit/d2ae13b03f21e3ab9545d50d7403ec52004bfbfc)) + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.43](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.42...v3.12.0-beta.43) (2025-10-06) + + +### Features + +* **MeasurementService:** add rendering of unmapped measurements ([#5416](https://github.com/OHIF/Viewers/issues/5416)) ([851e74d](https://github.com/OHIF/Viewers/commit/851e74d7b867a806befb5d85fd71ff9a75e9f2d2)) + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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 @ohif/ui-next + + + + + +# [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 @ohif/ui-next + + + + + +# [3.12.0-beta.30](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.29...v3.12.0-beta.30) (2025-09-25) + + +### Bug Fixes + +* **i18n:** Add and update i18n Translation for Toolbar Tool Names ([#5392](https://github.com/OHIF/Viewers/issues/5392)) ([7783d0f](https://github.com/OHIF/Viewers/commit/7783d0f558bbb7016c29513b6fe76ed069dbd75c)) + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.23...v3.12.0-beta.24) (2025-09-10) + + +### Bug Fixes + +* **ErrorBoundary:** Fixing console errors and Improving error logging for better developer experience ([#5378](https://github.com/OHIF/Viewers/issues/5378)) ([56c5828](https://github.com/OHIF/Viewers/commit/56c5828b02d53bfd17182bb8d0c126b7cd4f400e)) + + + + + +# [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 @ohif/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [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/ui-next + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + + +### Bug Fixes + +* header icon logic ([#5339](https://github.com/OHIF/Viewers/issues/5339)) ([a2133ef](https://github.com/OHIF/Viewers/commit/a2133ef6cb6e47fad22908fb1338ee57c67eb8f8)) + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/ui-next + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/ui-next diff --git a/platform/ui-next/package.json b/platform/ui-next/package.json index e841ef42c36..125f1996e05 100644 --- a/platform/ui-next/package.json +++ b/platform/ui-next/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/ui-next", - "version": "3.11.1", + "version": "3.12.0", "description": "Next version of OHIF Viewers UI, more customizable using shadcn/ui", "main": "dist/ohif-ui-next.umd.js", "module": "src/index.ts", diff --git a/platform/ui-next/src/components/AllInOneMenu/BackItem.tsx b/platform/ui-next/src/components/AllInOneMenu/BackItem.tsx index 88fc03a7355..1bf4f40de01 100644 --- a/platform/ui-next/src/components/AllInOneMenu/BackItem.tsx +++ b/platform/ui-next/src/components/AllInOneMenu/BackItem.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Icons } from '@ohif/ui-next'; import DividerItem from './DividerItem'; +import { useTranslation } from 'react-i18next'; type BackItemProps = { backLabel?: string; @@ -9,6 +10,8 @@ type BackItemProps = { }; const BackItem = ({ backLabel, onBackClick }: BackItemProps) => { + const { t } = useTranslation('Common'); + return ( <>
{ name="content-prev" className="ml-2 mr-2" /> - {backLabel || 'Back to Display Options'} + {backLabel || t('Back')}
diff --git a/platform/ui-next/src/components/AllInOneMenu/Menu.tsx b/platform/ui-next/src/components/AllInOneMenu/Menu.tsx index 8fb49741406..a2e75caf58b 100644 --- a/platform/ui-next/src/components/AllInOneMenu/Menu.tsx +++ b/platform/ui-next/src/components/AllInOneMenu/Menu.tsx @@ -1,4 +1,4 @@ -import React, { createContext, ReactNode, useCallback, useEffect, useState } from 'react'; +import React, { createContext, ReactNode, useCallback, useEffect, useRef, useState } from 'react'; import DividerItem from './DividerItem'; import PanelSelector from './PanelSelector'; @@ -108,6 +108,20 @@ const Menu = (props: MenuProps) => { ]); const [itemPanelLabels, setItemPanelLabels] = useState>([]); + // Store latest function references in refs so we can use them in effects + // without triggering re-runs when they change + const onVisibilityChangeRef = useRef(onVisibilityChange); + const preventHideMenuRef = useRef(preventHideMenu); + + // Keep refs up to date + useEffect(() => { + onVisibilityChangeRef.current = onVisibilityChange; + }, [onVisibilityChange]); + + useEffect(() => { + preventHideMenuRef.current = preventHideMenu; + }, [preventHideMenu]); + // If the props change for the this top level menu then we have to update the menu path // because the props to be rendered are maintained in the state. useEffect(() => { @@ -118,23 +132,25 @@ const Menu = (props: MenuProps) => { }, [activePanelIndex, props]); const hideMenu = useCallback(() => { - if (preventHideMenu) { + if (preventHideMenuRef.current) { return; } setMenuPath(path => [path[0]]); setItemPanelLabels([]); setIsMenuVisible(false); - onVisibilityChange?.(false); - }, [preventHideMenu, onVisibilityChange]); + onVisibilityChangeRef.current?.(false); + }, []); + // Only run this effect when isVisible changes, not when functions change. + // Note that hideMenu is stable with no dependencies and thus will not trigger the useEffect to run. useEffect(() => { if (isVisible) { setIsMenuVisible(isVisible); - onVisibilityChange?.(isVisible); + onVisibilityChangeRef.current?.(isVisible); } else { hideMenu(); } - }, [hideMenu, isVisible, onVisibilityChange]); + }, [isVisible, hideMenu]); const showSubMenu = useCallback((subMenuProps: MenuProps) => { setMenuPath(path => { diff --git a/platform/ui-next/src/components/Button/Button.tsx b/platform/ui-next/src/components/Button/Button.tsx index e8ae3d01f6c..106ad106fd2 100644 --- a/platform/ui-next/src/components/Button/Button.tsx +++ b/platform/ui-next/src/components/Button/Button.tsx @@ -39,13 +39,13 @@ export interface ButtonProps } const Button = React.forwardRef( - ({ className, variant, size, asChild = false, ...props }, forwardRef) => { + ({ className, variant, size, asChild = false, dataCY, ...props }, forwardRef) => { const Comp = asChild ? Slot : 'button'; - const dataCY = props.dataCY || `${props.name}-btn`; + const testId = dataCY || `${props.name}-btn`; return ( ; +const DATE_FNS_LOCALE_MAP: Record = { + en: enUS, + 'en-US': enUS, + fr: frLocale, + 'fr-FR': frLocale, + ar: arLocale, + ca: caLocale, + de: deLocale, + 'ja-JP': jaLocale, + ja: jaLocale, + nl: nlLocale, + 'pt-BR': ptBRLocale, + pt: ptBRLocale, + ru: ruLocale, + 'tr-TR': trLocale, + tr: trLocale, + vi: viLocale, + zh: zhLocale, + 'zh-CN': zhLocale, + 'zh-cn': zhLocale, + 'test-LNG': enUS, +}; + function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) { + const { i18n, t } = useTranslation('DatePicker'); + + const locale = useMemo(() => { + const lang = i18n.language || 'en'; + return DATE_FNS_LOCALE_MAP[lang] ?? enUS; + }, [i18n.language]); + return ( undefined, labelYearDropdown: () => undefined, }} + locale={locale} + formatters={{ + formatCaption: month => format(month, 'LLLL yyyy', { locale }), + }} classNames={{ months: 'flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0', month: 'space-y-4', @@ -30,7 +82,7 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C nav: 'space-x-1 flex items-center', table: 'w-full border-collapse space-y-1', head_row: 'flex', - head_cell: 'text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]', + head_cell: 'text-muted-foreground rounded-md w-9 font-normal text-[0.8rem] uppercase', row: 'flex w-full mt-2', cell: 'h-9 w-9 text-center text-base p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20', day: cn( diff --git a/platform/ui-next/src/components/Clipboard/Clipboard.tsx b/platform/ui-next/src/components/Clipboard/Clipboard.tsx index 0b515d868ba..464c8cca8ee 100644 --- a/platform/ui-next/src/components/Clipboard/Clipboard.tsx +++ b/platform/ui-next/src/components/Clipboard/Clipboard.tsx @@ -37,17 +37,12 @@ const Clipboard: React.FC = ({ children }) => { e.stopPropagation(); handleCopy(); }} - className="text-white" + className="text-foreground" title="Copy" > {copyState === 'idle' && } - {copyState === 'success' && } - {copyState === 'error' && ( - - )} + {copyState === 'success' && } + {copyState === 'error' && } ); }; diff --git a/platform/ui-next/src/components/DataRow/DataRow.tsx b/platform/ui-next/src/components/DataRow/DataRow.tsx index 1ae1edebaa9..05151ee465e 100644 --- a/platform/ui-next/src/components/DataRow/DataRow.tsx +++ b/platform/ui-next/src/components/DataRow/DataRow.tsx @@ -9,6 +9,7 @@ import { import { Icons } from '../../components/Icons/Icons'; import { Tooltip, TooltipTrigger, TooltipContent } from '../../components/Tooltip/Tooltip'; import { cn } from '../../lib/utils'; +import { useTranslation } from 'react-i18next'; /** * DataRow is a complex UI component that displays a selectable, interactive row with hierarchical data. @@ -18,6 +19,7 @@ import { cn } from '../../lib/utils'; * @component * @example * ```tsx + * // Basic usage without status * {}} * onColor={() => {}} * /> + * + * // With warning status using composite pattern + * + * + * + * + * // With success status using composite pattern + * + * + * + * + * // Multiple status indicators + * + * + * + * + * * ``` */ @@ -55,6 +80,7 @@ import { cn } from '../../lib/utils'; * @property {() => void} onRename - Callback when rename is requested * @property {() => void} onDelete - Callback when delete is requested * @property {() => void} onColor - Callback when color change is requested + * @property {React.ReactNode} children - Optional children, including Status components */ interface DataRowProps { number: number | null; @@ -62,7 +88,10 @@ interface DataRowProps { description: string; details?: { primary: string[]; secondary: string[] }; // + /** Primary selection: selected and in the active segmentation */ isSelected?: boolean; + /** Secondary selection: selected but in an inactive segmentation */ + isSecondarySelected?: boolean; onSelect?: (e) => void; // isVisible: boolean; @@ -78,10 +107,12 @@ interface DataRowProps { // colorHex?: string; onColor: (e) => void; + onCopy?: (e) => void; className?: string; + children?: React.ReactNode; } -const DataRow = React.forwardRef( +const DataRowComponent = React.forwardRef( ( { number, @@ -95,22 +126,37 @@ const DataRow = React.forwardRef( onRename, onDelete, onColor, + onCopy, isSelected = false, + isSecondarySelected = false, isVisible = true, disableEditing = false, className, + children, }, ref ) => { + const { t } = useTranslation('DataRow'); const [isDropdownOpen, setIsDropdownOpen] = useState(false); const isTitleLong = title?.length > 25; + // Extract Status components from children + const statusComponents = React.Children.toArray(children).filter( + child => + React.isValidElement(child) && + child.type && + (child.type as React.ComponentType).displayName?.startsWith('DataRow.Status') + ); + const handleAction = (action: string, e: React.MouseEvent) => { e.stopPropagation(); switch (action) { case 'Rename': onRename(e); break; + case 'Copy': + onCopy?.(e); + break; case 'Lock': onToggleLocked(e); break; @@ -199,7 +245,11 @@ const DataRow = React.forwardRef( onClick={onSelect} data-cy="data-row" > - {/* Hover Overlay */} + {/* Secondary Selection Tint (below hover, always visible when secondary-selected) */} + {isSecondarySelected && ( +
+ )} +
{/* Number Box */} @@ -264,7 +314,8 @@ const DataRow = React.forwardRef( className={`h-6 w-6 transition-opacity ${ isSelected || !isVisible ? 'opacity-100' : 'opacity-0 group-hover:opacity-100' }`} - aria-label={isVisible ? 'Hide' : 'Show'} + aria-label={isVisible ? t('Hide') : t('Show')} + dataCY="data-row-visibility-toggle" onClick={e => { e.stopPropagation(); onToggleVisibility(e); @@ -278,6 +329,9 @@ const DataRow = React.forwardRef( )} + {/* Status Components */} + {statusComponents} + {/* Actions Dropdown Menu */} {disableEditing &&
} {!disableEditing && ( @@ -310,16 +364,27 @@ const DataRow = React.forwardRef( className="pl-2" data-cy="Rename" > - Rename + {t('Rename')} + {onCopy && ( + handleAction('Copy', e)}> + + + {t('Duplicate')} + + + )} handleAction('Delete', e)}> - Delete + {t('Delete')} {onColor && ( @@ -329,7 +394,7 @@ const DataRow = React.forwardRef( className="pl-2" data-cy="Change Color" > - Change Color + {t('Change Color')} )} @@ -339,7 +404,7 @@ const DataRow = React.forwardRef( className="pl-2" data-cy="LockToggle" > - {isLocked ? 'Unlock' : 'Lock'} + {isLocked ? t('Unlock') : t('Lock')} @@ -367,5 +432,96 @@ const DataRow = React.forwardRef( } ); +DataRowComponent.displayName = 'DataRow'; + +interface StatusProps { + children: React.ReactNode; +} + +interface StatusIndicatorProps { + tooltip?: string; + icon: React.ReactNode; + defaultTooltip: string; +} + +const StatusIndicator: React.FC = ({ tooltip, icon, defaultTooltip }) => ( + + +
{icon}
+
+ +
{tooltip || defaultTooltip}
+
+
+); + +const Status: React.FC & { + Warning: React.FC<{ tooltip?: string }>; + Success: React.FC<{ tooltip?: string }>; + Error: React.FC<{ tooltip?: string }>; + Info: React.FC<{ tooltip?: string }>; +} = ({ children }) => { + return <>{children}; +}; + +const StatusWarning: React.FC<{ tooltip?: string }> = ({ tooltip }) => ( + + } + defaultTooltip="Warning" + /> +); + +const StatusSuccess: React.FC<{ tooltip?: string }> = ({ tooltip }) => ( + } + defaultTooltip="Success" + /> +); + +const StatusError: React.FC<{ tooltip?: string }> = ({ tooltip }) => ( + + } + defaultTooltip="Error" + /> +); + +const StatusInfo: React.FC<{ tooltip?: string }> = ({ tooltip }) => ( + } + defaultTooltip="Info" + /> +); + +Status.displayName = 'DataRow.Status'; +StatusWarning.displayName = 'DataRow.Status.Warning'; +StatusSuccess.displayName = 'DataRow.Status.Success'; +StatusError.displayName = 'DataRow.Status.Error'; +StatusInfo.displayName = 'DataRow.Status.Info'; + +Status.Warning = StatusWarning; +Status.Success = StatusSuccess; +Status.Error = StatusError; +Status.Info = StatusInfo; + +const DataRow = DataRowComponent as typeof DataRowComponent & { + Status: typeof Status; +}; + +DataRow.Status = Status; + export default DataRow; export { DataRow }; diff --git a/platform/ui-next/src/components/DateRange/DateRange.tsx b/platform/ui-next/src/components/DateRange/DateRange.tsx index eebc7b9162e..fa552003d64 100644 --- a/platform/ui-next/src/components/DateRange/DateRange.tsx +++ b/platform/ui-next/src/components/DateRange/DateRange.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { format, parse, isValid } from 'date-fns'; import { Calendar as CalendarIcon } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; import { cn } from '../../lib/utils'; import { Calendar } from '../Calendar'; import * as Popover from '../Popover'; @@ -23,6 +24,7 @@ export function DatePickerWithRange({ onChange, ...props }: React.HTMLAttributes & DatePickerWithRangeProps) { + const { t } = useTranslation('DatePicker'); const [start, setStart] = React.useState( startDate ? format(parse(startDate, 'yyyyMMdd', new Date()), 'yyyy-MM-dd') : '' ); @@ -85,7 +87,7 @@ export function DatePickerWithRange({ handleInputChange(e, 'start')} @@ -122,7 +124,7 @@ export function DatePickerWithRange({ handleInputChange(e, 'end')} diff --git a/platform/ui-next/src/components/Errorboundary/ErrorBoundary.tsx b/platform/ui-next/src/components/Errorboundary/ErrorBoundary.tsx index fee616f7736..78b689cadea 100644 --- a/platform/ui-next/src/components/Errorboundary/ErrorBoundary.tsx +++ b/platform/ui-next/src/components/Errorboundary/ErrorBoundary.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { ErrorBoundary as ReactErrorBoundary, FallbackProps } from 'react-error-boundary'; import { useTranslation } from 'react-i18next'; -import { Dialog, DialogContent } from '../Dialog/Dialog'; +import { Dialog, DialogContent, DialogTitle } from '../Dialog/Dialog'; import { ScrollArea } from '../ScrollArea/ScrollArea'; import { Button } from '../Button/Button'; import { useNotification } from '../../contextProviders'; @@ -194,6 +194,7 @@ const DefaultFallback = ({ open={showDetails} onOpenChange={setShowDetails} > + {errorTitle} e.preventDefault()} @@ -276,7 +277,6 @@ const ErrorBoundary = ({ let errorTimeout: NodeJS.Timeout; const handleError = (event: ErrorEvent) => { - event.preventDefault(); clearTimeout(errorTimeout); errorTimeout = setTimeout(() => { setError(event.error); diff --git a/platform/ui-next/src/components/FooterAction/FooterAction.tsx b/platform/ui-next/src/components/FooterAction/FooterAction.tsx index 052dc466f23..11f4175597a 100644 --- a/platform/ui-next/src/components/FooterAction/FooterAction.tsx +++ b/platform/ui-next/src/components/FooterAction/FooterAction.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Button } from '../Button/Button'; +import { Button, ButtonProps } from '../Button/Button'; import { cn } from '../../lib/utils'; interface FooterActionProps { @@ -7,7 +7,7 @@ interface FooterActionProps { className?: string; } -interface ActionProps extends FooterActionProps { +interface ActionProps extends ButtonProps { onClick: () => void; className?: string; } @@ -60,12 +60,18 @@ FooterAction.Right = ({ children }: FooterActionProps) => { FooterAction.Right.displayName = 'FooterAction.Right'; // Primary action: Solid button (default) -FooterAction.Primary = ({ children, onClick, className = 'min-w-[80px]' }: ActionProps) => { +FooterAction.Primary = ({ + children, + onClick, + className = 'min-w-[80px]', + ...rest +}: ActionProps) => { return ( @@ -74,12 +80,18 @@ FooterAction.Primary = ({ children, onClick, className = 'min-w-[80px]' }: Actio FooterAction.Primary.displayName = 'FooterAction.Primary'; // Secondary action: Ghost button -FooterAction.Secondary = ({ children, onClick, className = 'min-w-[80px]' }: ActionProps) => { +FooterAction.Secondary = ({ + children, + onClick, + className = 'min-w-[80px]', + ...rest +}: ActionProps) => { return ( @@ -88,12 +100,13 @@ FooterAction.Secondary = ({ children, onClick, className = 'min-w-[80px]' }: Act FooterAction.Secondary.displayName = 'FooterAction.Secondary'; // Tertiary action: Ghost button with different styling -FooterAction.Auxiliary = ({ children, onClick, className }: ActionProps) => { +FooterAction.Auxiliary = ({ children, onClick, className, ...rest }: ActionProps) => { return ( diff --git a/platform/ui-next/src/components/Header/Header.tsx b/platform/ui-next/src/components/Header/Header.tsx index 480b45b28cc..21a21e6f39c 100644 --- a/platform/ui-next/src/components/Header/Header.tsx +++ b/platform/ui-next/src/components/Header/Header.tsx @@ -109,7 +109,7 @@ function Header({ > {IconComponent && ( - + )} {option.title} diff --git a/platform/ui-next/src/components/Icons/Icons.tsx b/platform/ui-next/src/components/Icons/Icons.tsx index 4d3deb7426a..59d78755570 100644 --- a/platform/ui-next/src/components/Icons/Icons.tsx +++ b/platform/ui-next/src/components/Icons/Icons.tsx @@ -146,6 +146,8 @@ import { ToolExpand, ToolClickSegment, ToolSegmentLabel, + ToolSculptor, + ToolLabelmapEditWithContour, } from './Sources/Tools'; import ActionNewDialog from './Sources/ActionNewDialog'; import NotificationInfo from './Sources/NotificationInfo'; @@ -228,6 +230,8 @@ import ArrowRight from './Sources/ArrowRight'; import ChevronLeft from './Sources/ChevronLeft'; import StatusAlert from './Sources/StatusAlert'; import Undo from './Sources/Undo'; +import TabContours from './Sources/TabContours'; +import IllustrationNotFound from './Sources/IllustrationNotFound'; // // type IconProps = React.HTMLAttributes; @@ -674,6 +678,8 @@ export const Icons = { 'status-tracked': (props: IconProps) => StatusTracking(props), 'status-untracked': (props: IconProps) => StatusUntracked(props), 'status-locked': (props: IconProps) => StatusLocked(props), + 'tab-contours': (props: IconProps) => TabContours(props), + TabContours: (props: IconProps) => TabContours(props), 'tab-segmentation': (props: IconProps) => TabSegmentation(props), 'tab-studies': (props: IconProps) => TabStudies(props), 'tab-linear': (props: IconProps) => TabLinear(props), @@ -720,6 +726,7 @@ export const Icons = { 'tool-referenceLines': (props: IconProps) => ToolReferenceLines(props), 'tool-reset': (props: IconProps) => ToolReset(props), 'tool-rotate-right': (props: IconProps) => ToolRotateRight(props), + 'icon-tool-sculptor': (props: IconProps) => ToolSculptor(props), 'tool-seg-brush': (props: IconProps) => ToolSegBrush(props), 'tool-seg-eraser': (props: IconProps) => ToolSegEraser(props), 'tool-seg-shape': (props: IconProps) => ToolSegShape(props), @@ -775,6 +782,7 @@ export const Icons = { 'old-trash': (props: IconProps) => Trash(props), 'tool-point': (props: IconProps) => ToolCircle(props), 'tool-freehand-line': (props: IconProps) => ToolFreehand(props), + 'tool-labelmap-edit-with-contour': (props: IconProps) => ToolLabelmapEditWithContour(props), 'actions-smooth': (props: IconProps) => ActionsSmooth(props), 'actions-simplify': (props: IconProps) => ActionsSimplify(props), 'actions-combine': (props: IconProps) => ActionsCombine(props), @@ -791,6 +799,7 @@ export const Icons = { Undo, Redo, JumpToSlice, + IllustrationNotFound, /** Adds an icon to the set of icons */ addIcon: (name: string, icon) => { diff --git a/platform/ui-next/src/components/Icons/Sources/IllustrationNotFound.tsx b/platform/ui-next/src/components/Icons/Sources/IllustrationNotFound.tsx new file mode 100644 index 00000000000..f6ff139e3be --- /dev/null +++ b/platform/ui-next/src/components/Icons/Sources/IllustrationNotFound.tsx @@ -0,0 +1,102 @@ +import React from 'react'; +import type { IconProps } from '../types'; + +export const IllustrationNotFound = (props: IconProps) => ( + + + + + + + + + + + + + +); + +export default IllustrationNotFound; diff --git a/platform/ui-next/src/components/Icons/Sources/Patient.tsx b/platform/ui-next/src/components/Icons/Sources/Patient.tsx index bf6be5f0f2a..0105b47382b 100644 --- a/platform/ui-next/src/components/Icons/Sources/Patient.tsx +++ b/platform/ui-next/src/components/Icons/Sources/Patient.tsx @@ -11,7 +11,6 @@ export const Patient = (props: IconProps) => ( xmlnsXlink="http://www.w3.org/1999/xlink" {...props} > - icon-patient ( + + + + + + + + + + +); + +export default TabContours; diff --git a/platform/ui-next/src/components/Icons/Sources/Tools.tsx b/platform/ui-next/src/components/Icons/Sources/Tools.tsx index 2a612dabf02..b06dfb363a8 100644 --- a/platform/ui-next/src/components/Icons/Sources/Tools.tsx +++ b/platform/ui-next/src/components/Icons/Sources/Tools.tsx @@ -10,7 +10,6 @@ export const ToolLayout = (props: IconProps) => ( xmlns="http://www.w3.org/2000/svg" {...props} > - tool-layout ( xmlns="http://www.w3.org/2000/svg" {...props} > - tool-bidirectional ( xmlns="http://www.w3.org/2000/svg" {...props} > - tool- ( ); @@ -3544,3 +3541,57 @@ export const ToolContract = (props: IconProps) => ( /> ); +export const ToolSculptor = (props: IconProps) => ( + + + + + +); +export const ToolLabelmapEditWithContour = (props: IconProps) => ( + + + + + + +); diff --git a/platform/ui-next/src/components/MeasurementTable/MeasurementTable.tsx b/platform/ui-next/src/components/MeasurementTable/MeasurementTable.tsx index 7a8b13f6db2..f645ec63d69 100644 --- a/platform/ui-next/src/components/MeasurementTable/MeasurementTable.tsx +++ b/platform/ui-next/src/components/MeasurementTable/MeasurementTable.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { DataRow, PanelSection } from '../../index'; +import { Icons, PanelSection, Tooltip, TooltipContent, TooltipTrigger } from '../../index'; +import DataRow from '../DataRow/DataRow'; import { createContext } from '../../lib/createContext'; interface MeasurementTableContext { @@ -11,7 +12,7 @@ interface MeasurementTableContext { } const [MeasurementTableProvider, useMeasurementTableContext] = - createContext('MeasurementTable', { data: [] }); + createContext('MeasurementTable', { data: [], isExpanded: true }); interface MeasurementDataProps extends MeasurementTableContext { title: string; @@ -59,7 +60,7 @@ const Body = () => { if (!data || data.length === 0) { return (
- No tracked measurements + {useTranslation('MeasurementTable').t('No tracked measurements')}
); } @@ -91,6 +92,8 @@ interface MeasurementItem { isLocked: boolean; toolName: string; isExpanded: boolean; + isUnmapped?: boolean; + statusTooltip?: string; } interface RowProps { @@ -117,11 +120,15 @@ const Row = ({ item, index }: RowProps) => { onRename={e => onAction(e, 'renameMeasurement', uid)} onToggleVisibility={e => onAction(e, 'toggleVisibilityMeasurement', uid)} onToggleLocked={e => onAction(e, 'toggleLockMeasurement', uid)} + onColor={e => onAction(e, 'changeMeasurementColor', uid)} disableEditing={disableEditing} - isExpanded={isExpanded} isVisible={item.isVisible} isLocked={item.isLocked} - /> + > + {item.isUnmapped && ( + + )} +
); }; diff --git a/platform/ui-next/src/components/OHIFModals/ImageModal.tsx b/platform/ui-next/src/components/OHIFModals/ImageModal.tsx index e248d7a6f5d..e4e56d2326c 100644 --- a/platform/ui-next/src/components/OHIFModals/ImageModal.tsx +++ b/platform/ui-next/src/components/OHIFModals/ImageModal.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useTranslation } from 'react-i18next'; import { Input } from '../Input/Input'; import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../Select/Select'; import { Switch } from '../Switch/Switch'; @@ -143,6 +144,13 @@ interface ImageSizeProps { className?: string; maxWidth?: string; maxHeight?: string; + /** Optional translation namespace. Defaults to 'CaptureViewportModal'. */ + translationNamespace?: string; + /** Optional labels/placeholders to override translations */ + widthLabel?: string; + heightLabel?: string; + widthPlaceholder?: string; + heightPlaceholder?: string; } function ImageSize({ @@ -154,7 +162,19 @@ function ImageSize({ className, maxWidth, maxHeight, + translationNamespace = 'CaptureViewportModal', + widthLabel, + heightLabel, + widthPlaceholder, + heightPlaceholder, }: ImageSizeProps) { + const { t } = useTranslation(translationNamespace); + + // Use translations as defaults, but allow props to override + const finalWidthLabel = widthLabel ?? t('Width', { defaultValue: 'Width' }); + const finalHeightLabel = heightLabel ?? t('Height', { defaultValue: 'Height' }); + const finalWidthPlaceholder = widthPlaceholder ?? t('Width', { defaultValue: 'Width' }); + const finalHeightPlaceholder = heightPlaceholder ?? t('Height', { defaultValue: 'Height' }); return (
@@ -163,23 +183,23 @@ function ImageSize({
{/* Width group */}
- W + {finalWidthLabel} {})} - placeholder="Width" + placeholder={finalWidthPlaceholder} className="w-20" max={maxWidth} />
- {/* Height group */} + {/* Height/Length group */}
- H + {finalHeightLabel} {})} - placeholder="Height" + placeholder={finalHeightPlaceholder} className="w-20" max={maxHeight} /> diff --git a/platform/ui-next/src/components/OHIFModals/UserPreferencesModal.tsx b/platform/ui-next/src/components/OHIFModals/UserPreferencesModal.tsx index eeb95fa7c5a..2ef9a17d765 100644 --- a/platform/ui-next/src/components/OHIFModals/UserPreferencesModal.tsx +++ b/platform/ui-next/src/components/OHIFModals/UserPreferencesModal.tsx @@ -1,8 +1,114 @@ import * as React from 'react'; +import type { TFunction } from 'i18next'; +import { useTranslation } from 'react-i18next'; import { Label } from '../Label'; import { Input } from '../Input'; import { cn } from '../../lib/utils'; +const HOTKEY_TOKEN_TRANSLATIONS: Record< + string, + { + i18nKey: string; + defaultValue: string; + } +> = { + ctrl: { i18nKey: 'HotkeyKeys.ctrl', defaultValue: 'Ctrl' }, + control: { i18nKey: 'HotkeyKeys.ctrl', defaultValue: 'Ctrl' }, + shift: { i18nKey: 'HotkeyKeys.shift', defaultValue: 'Shift' }, + alt: { i18nKey: 'HotkeyKeys.alt', defaultValue: 'Alt' }, + option: { i18nKey: 'HotkeyKeys.option', defaultValue: 'Option' }, + meta: { i18nKey: 'HotkeyKeys.meta', defaultValue: 'Cmd' }, + command: { i18nKey: 'HotkeyKeys.meta', defaultValue: 'Cmd' }, + cmd: { i18nKey: 'HotkeyKeys.meta', defaultValue: 'Cmd' }, + enter: { i18nKey: 'HotkeyKeys.enter', defaultValue: 'Enter' }, + return: { i18nKey: 'HotkeyKeys.enter', defaultValue: 'Enter' }, + esc: { i18nKey: 'HotkeyKeys.esc', defaultValue: 'Esc' }, + escape: { i18nKey: 'HotkeyKeys.esc', defaultValue: 'Esc' }, + space: { i18nKey: 'HotkeyKeys.space', defaultValue: 'Space' }, + spacebar: { i18nKey: 'HotkeyKeys.space', defaultValue: 'Space' }, + tab: { i18nKey: 'HotkeyKeys.tab', defaultValue: 'Tab' }, + backspace: { i18nKey: 'HotkeyKeys.backspace', defaultValue: 'Backspace' }, + delete: { i18nKey: 'HotkeyKeys.delete', defaultValue: 'Delete' }, + del: { i18nKey: 'HotkeyKeys.delete', defaultValue: 'Delete' }, + insert: { i18nKey: 'HotkeyKeys.insert', defaultValue: 'Insert' }, + ins: { i18nKey: 'HotkeyKeys.insert', defaultValue: 'Insert' }, + home: { i18nKey: 'HotkeyKeys.home', defaultValue: 'Home' }, + end: { i18nKey: 'HotkeyKeys.end', defaultValue: 'End' }, + pageup: { i18nKey: 'HotkeyKeys.pageup', defaultValue: 'Page Up' }, + pagedown: { i18nKey: 'HotkeyKeys.pagedown', defaultValue: 'Page Down' }, + up: { i18nKey: 'HotkeyKeys.up', defaultValue: 'Up Arrow' }, + down: { i18nKey: 'HotkeyKeys.down', defaultValue: 'Down Arrow' }, + left: { i18nKey: 'HotkeyKeys.left', defaultValue: 'Left Arrow' }, + right: { i18nKey: 'HotkeyKeys.right', defaultValue: 'Right Arrow' }, + capslock: { i18nKey: 'HotkeyKeys.capslock', defaultValue: 'Caps Lock' }, + plus: { i18nKey: 'HotkeyKeys.plus', defaultValue: 'Plus' }, + minus: { i18nKey: 'HotkeyKeys.minus', defaultValue: 'Minus' }, + comma: { i18nKey: 'HotkeyKeys.comma', defaultValue: 'Comma' }, + period: { i18nKey: 'HotkeyKeys.period', defaultValue: 'Period' }, + slash: { i18nKey: 'HotkeyKeys.slash', defaultValue: 'Slash' }, + backslash: { i18nKey: 'HotkeyKeys.backslash', defaultValue: 'Backslash' }, + semicolon: { i18nKey: 'HotkeyKeys.semicolon', defaultValue: 'Semicolon' }, + quote: { i18nKey: 'HotkeyKeys.quote', defaultValue: 'Quote' }, + apostrophe: { i18nKey: 'HotkeyKeys.quote', defaultValue: 'Quote' }, + backquote: { i18nKey: 'HotkeyKeys.backquote', defaultValue: 'Backtick' }, + tilde: { i18nKey: 'HotkeyKeys.backquote', defaultValue: 'Backtick' }, + bracketleft: { i18nKey: 'HotkeyKeys.bracketleft', defaultValue: 'Left Bracket' }, + bracketright: { i18nKey: 'HotkeyKeys.bracketright', defaultValue: 'Right Bracket' }, +}; + +const formatFallbackToken = (token: string) => { + if (!token) { + return ''; + } + + if (/^f\d{1,2}$/i.test(token)) { + return token.toUpperCase(); + } + + if (token.length === 1) { + return token.toUpperCase(); + } + + return token.charAt(0).toUpperCase() + token.slice(1); +}; + +const normalizeHotkeyValue = (value?: string | string[]) => { + if (!value) { + return ''; + } + + if (Array.isArray(value)) { + return value.join('+'); + } + + return value; +}; + +const translateHotkeyValue = (value: string | string[] | undefined, t: TFunction) => { + const normalizedValue = normalizeHotkeyValue(value); + if (!normalizedValue) { + return ''; + } + + return normalizedValue + .split('+') + .map(rawToken => { + const trimmed = rawToken.trim(); + if (!trimmed) { + return ''; + } + + const lower = trimmed.toLowerCase(); + const config = HOTKEY_TOKEN_TRANSLATIONS[lower]; + if (config) { + return t(config.i18nKey, { defaultValue: config.defaultValue }); + } + + return formatFallbackToken(trimmed); + }) + .join('+'); +}; + interface UserPreferencesModalProps { children: React.ReactNode; className?: string; @@ -72,6 +178,12 @@ interface HotkeyProps { function Hotkey({ label, placeholder, className, value, onChange, hotkeys }: HotkeyProps) { const [isRecording, setIsRecording] = React.useState(false); + const { t } = useTranslation('UserPreferencesModal'); + const translatedValue = React.useMemo(() => translateHotkeyValue(value, t), [value, t]); + const translatedPlaceholder = React.useMemo( + () => translateHotkeyValue(placeholder, t), + [placeholder, t] + ); const onInputKeyDown = (event: React.KeyboardEvent) => { event.preventDefault(); @@ -95,15 +207,15 @@ function Hotkey({ label, placeholder, className, value, onChange, hotkeys }: Hot }; return ( -
- +
+ = ({ step, showLabel = false, label = '', + tooltip, className, }) => { + const renderLabel = () => { + if (!showLabel || !label) { + return null; + } + + const labelNode = {label}; + + if (!tooltip) { + return labelNode; + } + + return ( + + {labelNode} + {tooltip} + + ); + }; + return ( = ({ step={step} className={cn('flex flex-col space-y-2', className)} > - {showLabel && {label}} + {renderLabel()} ); diff --git a/platform/ui-next/src/components/OHIFToolSettings/RowSegmentedControl.tsx b/platform/ui-next/src/components/OHIFToolSettings/RowSegmentedControl.tsx index e41c6e3d9a7..d86baaf72c7 100644 --- a/platform/ui-next/src/components/OHIFToolSettings/RowSegmentedControl.tsx +++ b/platform/ui-next/src/components/OHIFToolSettings/RowSegmentedControl.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Label } from '../Label'; import { Tabs, TabsList, TabsTrigger } from '../Tabs'; import { cn } from '../../lib/utils'; +import { Tooltip, TooltipContent, TooltipTrigger } from '../Tooltip'; interface RadioValue { value: string; @@ -14,6 +15,7 @@ interface RadioOption { value: string; values: RadioValue[]; onChange?: (val: string) => void; + tooltip?: string; } interface RowSegmentedControlProps { @@ -38,7 +40,18 @@ export const RowSegmentedControl: React.FC = ({ className={cn('flex items-center justify-between text-[13px]', className)} key={option.id} > - +
; } return ( @@ -39,6 +48,12 @@ function ToolSettings({ options }) { return renderCustomSetting(option); case SETTING_TYPES.BUTTON: return renderButtonSetting(option); + case SETTING_TYPES.CHECKBOX: + return renderCheckboxSetting(option); + case SETTING_TYPES.SWITCH: + return renderSwitchSetting(option); + case SETTING_TYPES.SELECT: + return renderSelectSetting(option); default: return null; } @@ -47,13 +62,32 @@ function ToolSettings({ options }) { ); } +const renderLabelWithTooltip = (label: React.ReactNode, tooltip?: string) => { + if (!label) { + return null; + } + + if (!tooltip) { + return {label}; + } + + return ( + + + {label} + + {tooltip} + + ); +}; + const renderRangeSetting = option => { return (
-
{option.name}
+
{renderLabelWithTooltip(option.name, option.tooltip)}
{ function renderDoubleRangeSetting(option) { return ( - + > +
+ {renderLabelWithTooltip(option.name, option.tooltip)} +
+
+ +
+
); } @@ -105,15 +149,107 @@ const renderCustomSetting = option => { }; const renderButtonSetting = option => { - return ( + const button = ( ); + + if (option.tooltip) { + return ( + + {button} + {option.tooltip} + + ); + } + + return button; +}; + +const renderCheckboxLabel = option => { + return ( + + ); +}; + +const renderCheckboxSetting = option => { + return ( +
+ { + option.onChange?.(checked); + }} + /> + {renderCheckboxLabel(option)} +
+ ); +}; + +const renderSwitchSetting = option => { + return ( +
+ { + option.onChange?.(checked); + }} + /> + {renderCheckboxLabel(option)} +
+ ); +}; + +const renderSelectSetting = option => { + return ( +
+
+ {renderLabelWithTooltip(option.name, option.tooltip)} +
+
+ +
+
+ ); }; export default ToolSettings; diff --git a/platform/ui-next/src/components/SegmentationTable/AddSegmentRow.tsx b/platform/ui-next/src/components/SegmentationTable/AddSegmentRow.tsx index 5ab8d23d215..e8d2a8da4e9 100644 --- a/platform/ui-next/src/components/SegmentationTable/AddSegmentRow.tsx +++ b/platform/ui-next/src/components/SegmentationTable/AddSegmentRow.tsx @@ -1,8 +1,10 @@ import React from 'react'; import { Button, Icons } from '@ohif/ui-next'; import { useSegmentationTableContext, useSegmentationExpanded } from './contexts'; +import { useTranslation } from 'react-i18next'; export const AddSegmentRow: React.FC<{ children?: React.ReactNode }> = ({ children = null }) => { + const { t } = useTranslation('SegmentationPanel'); const { activeRepresentation, disableEditing, @@ -56,7 +58,7 @@ export const AddSegmentRow: React.FC<{ children?: React.ReactNode }> = ({ childr onClick={() => onSegmentAdd(segmentationId)} > - Add Segment + {t('Add Segment')} ) : null}
diff --git a/platform/ui-next/src/components/SegmentationTable/AddSegmentationRow.tsx b/platform/ui-next/src/components/SegmentationTable/AddSegmentationRow.tsx index f04f2c0ae6d..7614bf494ac 100644 --- a/platform/ui-next/src/components/SegmentationTable/AddSegmentationRow.tsx +++ b/platform/ui-next/src/components/SegmentationTable/AddSegmentationRow.tsx @@ -6,14 +6,23 @@ import { useSegmentationTableContext } from './contexts'; export const AddSegmentationRow: React.FC<{ children?: React.ReactNode }> = ({ children = null, }) => { - const { t } = useTranslation('SegmentationTable'); + const { t } = useTranslation('SegmentationPanel'); - const { onSegmentationAdd, data, disableEditing, mode, disabled } = - useSegmentationTableContext('AddSegmentationRow'); + const { + onSegmentationAdd, + data, + disableEditing, + mode, + disabled, + segmentationRepresentationTypes, + } = useSegmentationTableContext('AddSegmentationRow'); - const isEmpty = data.length === 0; + // Check if we have at least one segmentation of the representation type for the panel this component is contained in. + const hasRepresentationType = + (!segmentationRepresentationTypes && data.length > 0) || + data.some(info => segmentationRepresentationTypes?.includes(info.representation?.type)); - if (!isEmpty && mode === 'collapsed') { + if (hasRepresentationType && mode === 'collapsed') { return null; } @@ -25,7 +34,13 @@ export const AddSegmentationRow: React.FC<{ children?: React.ReactNode }> = ({
!disabled && onSegmentationAdd('')} + onClick={() => + !disabled && + onSegmentationAdd({ + segmentationId: '', + segmentationRepresentationType: segmentationRepresentationTypes?.[0], + }) + } > {children}
@@ -33,7 +48,7 @@ export const AddSegmentationRow: React.FC<{ children?: React.ReactNode }> = ({ {disabled ? : }
- {t(`${disabled ? 'Segmentation Not Supported' : 'Add Segmentation'}`)} + {t(disabled ? 'Segmentation not supported' : 'Add segmentation')}
diff --git a/platform/ui-next/src/components/SegmentationTable/SegmentationCollapsed.tsx b/platform/ui-next/src/components/SegmentationTable/SegmentationCollapsed.tsx index d51d30ab393..91b245bcb8d 100644 --- a/platform/ui-next/src/components/SegmentationTable/SegmentationCollapsed.tsx +++ b/platform/ui-next/src/components/SegmentationTable/SegmentationCollapsed.tsx @@ -1,6 +1,10 @@ import React from 'react'; import { PanelSection } from '../PanelSection'; -import { useSegmentationTableContext, SegmentationExpandedProvider } from './contexts'; +import { + useSegmentationTableContext, + SegmentationExpandedProvider, + useSegmentationExpanded, +} from './contexts'; import { useTranslation } from 'react-i18next'; import { Button, @@ -45,24 +49,31 @@ const SegmentationCollapsedDropdownMenu = ({ children }: { children: React.React // Selector component - for the segmentation selection dropdown const SegmentationCollapsedSelector = () => { - const { t } = useTranslation('SegmentationTable.HeaderCollapsed'); - const { data, activeSegmentationId, onSegmentationClick } = useSegmentationTableContext( - 'SegmentationCollapsedSelector' - ); + const { t } = useTranslation('SegmentationPanel.HeaderCollapsed'); + const { data, onSegmentationClick, segmentationRepresentationTypes } = + useSegmentationTableContext('SegmentationCollapsedSelector'); + const { segmentation } = useSegmentationExpanded('SegmentationCollapsedSelector'); if (!data?.length) { return null; } - const segmentations = data.map(seg => ({ - id: seg.segmentation.segmentationId, - label: seg.segmentation.label, - })); + const segmentations = data + // Only show segmentations of the representation type for this panel. Show all segmentations if no type is specified. + .filter( + seg => + !segmentationRepresentationTypes || + segmentationRepresentationTypes.includes(seg.representation?.type) + ) + .map(seg => ({ + id: seg.segmentation.segmentationId, + label: seg.segmentation.label, + })); return ( - setOutlineWidth({ type: activeRepresentation.type }, Number(e.target.value)) + setOutlineWidth( + { type: segmentationRepresentationTypes?.[0] }, + Number(e.target.value) + ) } className="mx-1 w-10 flex-none text-center" /> @@ -131,14 +147,12 @@ export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> = {renderInactiveSegmentations && (
- setFillAlphaInactive({ type: activeRepresentation.type }, value) - } + onValueChange={([value]) => setFillAlphaInactive({}, value)} max={1} min={0} step={0.1} @@ -146,9 +160,7 @@ export const SegmentationTableConfig: React.FC<{ children?: React.ReactNode }> = - setFillAlphaInactive({ type: activeRepresentation.type }, Number(e.target.value)) - } + onChange={e => setFillAlphaInactive({}, Number(e.target.value))} />
)} diff --git a/platform/ui-next/src/components/SegmentationTable/contexts/SegmentationTableContext.tsx b/platform/ui-next/src/components/SegmentationTable/contexts/SegmentationTableContext.tsx index 41a60eb37b4..e4f015e4bf7 100644 --- a/platform/ui-next/src/components/SegmentationTable/contexts/SegmentationTableContext.tsx +++ b/platform/ui-next/src/components/SegmentationTable/contexts/SegmentationTableContext.tsx @@ -65,20 +65,37 @@ export interface SegmentationTableContextType { showSegmentIndex?: boolean; renderInactiveSegmentations?: boolean; + // The types of segmentations displayed/filtered in this table. If undefined, show all types. + // The first element is the primary type. Additional elements are secondary types. + segmentationRepresentationTypes?: string[]; + + // The (last) selected segmentation ID for the representation types above. + // If the representation types above is undefined, then it will store the last active segmentation ID. + selectedSegmentationIdForType?: string; + // Function handlers setShowConfig?: (show: boolean) => void; setRenderFill?: ({ type }: { type: string }, value: boolean) => void; + setRenderFillInactive?: ({ type }: { type: string }, value: boolean) => void; setRenderOutline?: ({ type }: { type: string }, value: boolean) => void; + setRenderOutlineInactive?: ({ type }: { type: string }, value: boolean) => void; setOutlineWidth?: ({ type }: { type: string }, value: number) => void; setFillAlpha?: ({ type }: { type: string }, value: number) => void; - setFillAlphaInactive?: ({ type }: { type: string }, value: number) => void; + setFillAlphaInactive?: ({ type }: { type?: string }, value: number) => void; toggleRenderInactiveSegmentations?: () => void; - onSegmentationAdd?: (segmentationId: string) => void; + onSegmentationAdd?: ({ + segmentationId, + segmentationRepresentationType, + }: { + segmentationId: string; + segmentationRepresentationType: string; + }) => void; onSegmentationClick?: (segmentationId: string) => void; onSegmentationDelete?: (segmentationId: string) => void; onSegmentAdd?: (segmentationId: string) => void; onSegmentClick?: (segmentationId: string, segmentIndex: number) => void; onSegmentEdit?: (segmentationId: string, segmentIndex: number) => void; + onSegmentCopy?: (segmentationId: string, segmentIndex: number) => void; onSegmentationEdit?: (segmentationId: string) => void; onSegmentColorClick?: (segmentationId: string, segmentIndex: number) => void; onSegmentDelete?: (segmentationId: string, segmentIndex: number) => void; diff --git a/platform/ui-next/src/components/Thumbnail/Thumbnail.tsx b/platform/ui-next/src/components/Thumbnail/Thumbnail.tsx index 192aca7dda7..8a97aba1cd2 100644 --- a/platform/ui-next/src/components/Thumbnail/Thumbnail.tsx +++ b/platform/ui-next/src/components/Thumbnail/Thumbnail.tsx @@ -88,7 +88,12 @@ const Thumbnail = ({ loadingProgress && loadingProgress < 1 && 'bg-primary/25' )} >
-
{modality}
+
+ {modality} +
{/* top right */} @@ -139,7 +144,10 @@ const Thumbnail = ({ {description} -
+
{description}
@@ -180,11 +188,19 @@ const Thumbnail = ({ >
-
{modality}
+
+ {modality} +
{description} -
+
{description}
diff --git a/platform/ui-next/src/contextProviders/NotificationProvider.tsx b/platform/ui-next/src/contextProviders/NotificationProvider.tsx index e824a403665..ed842462c91 100644 --- a/platform/ui-next/src/contextProviders/NotificationProvider.tsx +++ b/platform/ui-next/src/contextProviders/NotificationProvider.tsx @@ -1,4 +1,12 @@ -import React, { createContext, useContext, useCallback, useEffect, ReactNode, useRef } from 'react'; +import React, { + createContext, + useContext, + useCallback, + useEffect, + ReactNode, + useState, + useRef, +} from 'react'; import PropTypes from 'prop-types'; import { Toaster, toast } from '../components'; @@ -27,17 +35,26 @@ const NotificationProvider = ({ title: '', message: '', duration: 5000, - position: 'bottom-right', // Aligning to Sonner's positioning system - type: 'info', // info, success, error + position: 'bottom-right', + type: 'info', + visible: true, }; + const [options, setOptions] = useState([]); // Cache for recent notifications to prevent duplicates // Structure: { [title_message_type]: { timestamp, id } } const recentNotificationsRef = useRef>({}); + const CustomNotification = service?.getCustomComponent(); + // Use the configurable deduplication interval from props const show = useCallback(options => { + const newNotification = { + ...DEFAULT_OPTIONS, + ...options, + }; + const { title, message, @@ -48,10 +65,7 @@ const NotificationProvider = ({ allowDuplicates = false, deduplicationInterval: optionsDeduplicationInterval, action, - } = { - ...DEFAULT_OPTIONS, - ...options, - }; + } = newNotification; // Use the provider's deduplicationInterval by default, but allow it to be overridden per notification const notificationDeduplicationInterval = optionsDeduplicationInterval || deduplicationInterval; @@ -143,10 +157,13 @@ const NotificationProvider = ({ // The entry will be checked against the deduplication interval } + setOptions(prev => [...prev, { ...newNotification, id: id }]); + return id; }, []); const hide = useCallback(id => { + setOptions(state => [...state.filter(item => item.id !== id)]); toast.dismiss(id); // Remove from cache if present @@ -160,6 +177,7 @@ const NotificationProvider = ({ }, []); const hideAll = useCallback(() => { + setOptions([]); toast.dismiss(); // Clear notification cache recentNotificationsRef.current = {}; @@ -198,7 +216,11 @@ const NotificationProvider = ({ return ( - + {CustomNotification ? ( + + ) : ( + + )} {children} ); diff --git a/platform/ui-next/src/contextProviders/ViewportGridProvider.tsx b/platform/ui-next/src/contextProviders/ViewportGridProvider.tsx index 1a9d5fb59f6..e88007c4ef2 100644 --- a/platform/ui-next/src/contextProviders/ViewportGridProvider.tsx +++ b/platform/ui-next/src/contextProviders/ViewportGridProvider.tsx @@ -404,8 +404,17 @@ export function ViewportGridProvider({ children, service }: ViewportGridProvider const getGridViewportsReady = useCallback(() => { const { viewports } = viewportGridState; - const readyViewports = Array.from(viewports.values()).filter(viewport => viewport.isReady); - return readyViewports.length === viewports.size; + // Filter viewports that have display sets (i.e., have content to display) + const viewportsWithContent = Array.from(viewports.values()).filter( + viewport => viewport.displaySetInstanceUIDs?.length > 0 + ); + // If there are no viewports with content, return false + if (viewportsWithContent.length === 0) { + return false; + } + // Check if all viewports with content are ready + const readyViewports = viewportsWithContent.filter(viewport => viewport.isReady); + return readyViewports.length === viewportsWithContent.length; }, [viewportGridState]); const setLayout = useCallback( diff --git a/platform/ui-next/src/index.ts b/platform/ui-next/src/index.ts index 2955a77cd2e..592b99fab79 100644 --- a/platform/ui-next/src/index.ts +++ b/platform/ui-next/src/index.ts @@ -1,7 +1,7 @@ import * as utils from './utils'; -import { cn } from './utils'; +import { cn, formatDICOMDate } from './utils'; export * from './components'; export * from './contextProviders'; export * as Types from './types'; -export { utils, cn }; +export { utils, cn, formatDICOMDate }; export { useSessionStorage, useDynamicMaxHeight } from './hooks'; diff --git a/platform/ui-next/src/tailwind.css b/platform/ui-next/src/tailwind.css index 4ed52cdb9ea..57f1289baaa 100644 --- a/platform/ui-next/src/tailwind.css +++ b/platform/ui-next/src/tailwind.css @@ -256,3 +256,11 @@ input[type='number']::-webkit-outer-spin-button { input[type='number'] { -moz-appearance: textfield; /* For Firefox */ } + +/* disabled class - used for icons and divs based on context */ +.ohif-disabled { + pointer-events: none; + cursor: not-allowed; + user-select: none; + opacity: 0.5; +} diff --git a/platform/ui-next/src/utils/formatDICOMDate.ts b/platform/ui-next/src/utils/formatDICOMDate.ts new file mode 100644 index 00000000000..6a4c053fbbb --- /dev/null +++ b/platform/ui-next/src/utils/formatDICOMDate.ts @@ -0,0 +1,25 @@ +import moment from 'moment'; +import i18n from 'i18next'; + +/** + * Formats DICOM date. + * + * @param {string} date + * @param {string} strFormat + * @returns {string} formatted date. + */ +export function formatDICOMDate(date: string, strFormat?: string): string { + if (!date) { + return ''; + } + + const format = strFormat ?? i18n.t('Common:localDateFormat', 'MMM D, YYYY'); + const locale = i18n.language || 'en'; + const parsed = moment(date, ['YYYYMMDD', 'YYYY.MM.DD'], true); + + if (!parsed.isValid()) { + return moment(date).locale(locale).format(format); + } + + return parsed.locale(locale).format(format); +} diff --git a/platform/ui-next/src/utils/index.ts b/platform/ui-next/src/utils/index.ts index 5ee62bbd915..245c9a39550 100644 --- a/platform/ui-next/src/utils/index.ts +++ b/platform/ui-next/src/utils/index.ts @@ -1,5 +1,6 @@ import { getToggledClassName } from './getToggledClassName'; import roundNumber from './roundNumber'; import { cn } from '../lib/utils'; +import { formatDICOMDate } from './formatDICOMDate'; -export { getToggledClassName, roundNumber, cn }; +export { getToggledClassName, roundNumber, cn, formatDICOMDate }; diff --git a/platform/ui/.gitignore b/platform/ui/.gitignore index 590af21b2de..3c3629e647f 100644 --- a/platform/ui/.gitignore +++ b/platform/ui/.gitignore @@ -1,2 +1 @@ node_modules -storybook-static diff --git a/platform/ui/.storybook/OHIFTheme.js b/platform/ui/.storybook/OHIFTheme.js deleted file mode 100644 index 6be91b64378..00000000000 --- a/platform/ui/.storybook/OHIFTheme.js +++ /dev/null @@ -1,8 +0,0 @@ -import { create } from '@storybook/theming'; - -export default create({ - base: 'light', - brandTitle: 'OHIF', - brandUrl: 'https://ohif.org', - brandImage: 'ohif-logo-light.svg', -}); diff --git a/platform/ui/.storybook/custom.css b/platform/ui/.storybook/custom.css deleted file mode 100644 index ec7b6867d02..00000000000 --- a/platform/ui/.storybook/custom.css +++ /dev/null @@ -1,3 +0,0 @@ -#storybook-explorer-menu svg { - color: #5034ff; -} diff --git a/platform/ui/.storybook/main.ts b/platform/ui/.storybook/main.ts deleted file mode 100644 index 62612396a18..00000000000 --- a/platform/ui/.storybook/main.ts +++ /dev/null @@ -1,105 +0,0 @@ -import path, { dirname, join } from 'path'; -import remarkGfm from 'remark-gfm'; -import type { StorybookConfig } from '@storybook/react-webpack5'; - -const config: StorybookConfig = { - stories: ['../src/**/*.stories.@(mdx)'], - addons: [ - getAbsolutePath('@storybook/addon-links'), - getAbsolutePath('@storybook/addon-essentials'), - // Other addons go here - { - name: '@storybook/addon-docs', - options: { - mdxPluginOptions: { - mdxCompileOptions: { - remarkPlugins: [remarkGfm], - }, - }, - }, - }, - ], - core: {}, - framework: { - name: getAbsolutePath('@storybook/react-webpack5'), - options: {}, - }, - docs: { - autodocs: true, // see below for alternatives - defaultName: 'Docs', // set to change the name of generated docs entries - }, - staticDirs: ['../static'], - webpackFinal: async (config: any, { configType }) => { - // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION' - // You can change the configuration based on that. - // 'PRODUCTION' is used when building the static version of storybook. - - // config.module.rules[0].use[0].options.plugins[1] = [ - // '@babel/plugin-proposal-class-properties', - // { loose: true }, - // ]; - - // config.module.rules[0].use[0].options.plugins[3] = [ - // '@babel/plugin-proposal-private-methods', - // { loose: true }, - // ]; - - // config.module.rules[0].use[0].options.plugins[4] = [ - // '@babel/plugin-proposal-private-property-in-object', - // { loose: true }, - // ]; - - // Make whatever fine-grained changes you need - config.module.rules.push({ - test: /\.m?js/, - resolve: { - fullySpecified: false, - }, - }); - - // Default rule for images /\.(svg|ico|jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/ - const fileLoaderRule = config.module.rules.find(rule => rule.test && rule.test.test('.svg')); - fileLoaderRule.exclude = /\.svg$/; - - config.module.rules.push({ - test: /\.svg$/, - use: [ - { loader: require.resolve('babel-loader') }, - // { loader: 'svg-inline-loader' }, - ], - }); - - config.module.rules.push({ - test: /\.css$/, - use: [ - { - loader: 'postcss-loader', - options: { - // HERE: OPTIONS - postcssOptions: { - plugins: [require('tailwindcss'), require('autoprefixer')], - }, - }, - }, - ], - include: path.resolve(__dirname, '../'), - }); - - // ignore the file @icr/polyseg-wasm during the build as it is a wasm file and - // we don't need that for ui - config.module.rules.push({ - test: /@icr\/polyseg-wasm/, - type: 'javascript/auto', - loader: 'file-loader', - }); - - // Return the altered config - return config; - }, -}; - -export default config; - -function getAbsolutePath(value: string): any { - return dirname(require.resolve(join(value, 'package.json'))); -} diff --git a/platform/ui/.storybook/manager-head.html b/platform/ui/.storybook/manager-head.html deleted file mode 100644 index a17c2234bf4..00000000000 --- a/platform/ui/.storybook/manager-head.html +++ /dev/null @@ -1,26 +0,0 @@ -OHIF UI Component - - - - diff --git a/platform/ui/.storybook/manager.js b/platform/ui/.storybook/manager.js deleted file mode 100644 index 8ca5f106183..00000000000 --- a/platform/ui/.storybook/manager.js +++ /dev/null @@ -1,15 +0,0 @@ -// .storybook/manager.js - -import { addons } from '@storybook/addons'; -import ohifTheme from './OHIFTheme'; - -const link = document.createElement('link'); -link.setAttribute('rel', 'shortcut icon'); -document.head.appendChild(link); - -addons.setConfig({ - theme: ohifTheme, -}); - -window.STORYBOOK_GA_ID = 'G-3S63CTHNP6'; -window.STORYBOOK_REACT_GA_OPTIONS = {}; diff --git a/platform/ui/.storybook/preview.tsx b/platform/ui/.storybook/preview.tsx deleted file mode 100644 index 6a6ad433a53..00000000000 --- a/platform/ui/.storybook/preview.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React from 'react'; -import { DocsPage, DocsContainer } from '@storybook/addon-docs'; -import { - Heading, - SectionName, - Footer, - AnchorListItem, - LinkComponent, -} from '../src/storybook/components'; - -import '../src/tailwind.css'; -import './custom.css'; - -// https://github.com/mondaycom/monday-ui-react-core/tree/master/.storybook - -export const parameters = { - docs: { - inlineStories: true, - container: ({ children, context }) => ( - {children} - ), - page: DocsPage, - components: { - Heading, - Footer, - h2: SectionName, - h3: ({ children }) =>

{children}

, - li: AnchorListItem, - a: LinkComponent, - p: ({ children }) =>

{children}

, - // todo: add pre and code - }, - }, - viewMode: 'docs', - previewTabs: { - 'storybook/docs/panel': { - index: -1, - }, - canvas: { title: 'Sandbox' }, - }, - actions: { argTypesRegex: '^on[A-Z].*' }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, - viewport: { - disable: true, - }, - backgrounds: { - default: 'OHIF-v3', - values: [ - { - name: 'White', - value: '#FFFFFF', - }, - { - name: 'OHIF-v3', - value: '#090C29', - }, - { - name: 'Light', - value: '#F8F8F8', - }, - { - name: 'Dark', - value: '#333333', - }, - ], - }, - options: { - storySort: { - order: ['Welcome', 'Contribute', 'Foundations', 'Modals', '*'], - }, - }, -}; - -export const decorators = []; diff --git a/platform/ui/CHANGELOG.md b/platform/ui/CHANGELOG.md index 80535978163..4c64a965d2a 100644 --- a/platform/ui/CHANGELOG.md +++ b/platform/ui/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. -## [3.11.1](https://github.com/OHIF/Viewers/compare/v3.11.0...v3.11.1) (2025-10-28) +# [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/ui @@ -11,7 +11,1096 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline -# [3.11.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.11.0) (2025-08-05) +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [3.12.0-beta.39](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.38...v3.12.0-beta.39) (2025-10-01) + + +### Features + +* Add inherit from basic/longitudinal modes ([#5435](https://github.com/OHIF/Viewers/issues/5435)) ([9ad0d7f](https://github.com/OHIF/Viewers/commit/9ad0d7fc8cf34867fa2cfc2bf2b2f63451ec03e5)) + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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 @ohif/ui + + + + + +# [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 @ohif/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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 @ohif/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [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/ui + + + + + +# [3.12.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.15...v3.12.0-beta.16) (2025-08-25) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.14...v3.12.0-beta.15) (2025-08-25) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.13...v3.12.0-beta.14) (2025-08-25) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.12...v3.12.0-beta.13) (2025-08-25) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.11...v3.12.0-beta.12) (2025-08-25) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.10...v3.12.0-beta.11) (2025-08-25) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.9...v3.12.0-beta.10) (2025-08-22) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.8...v3.12.0-beta.9) (2025-08-22) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.7...v3.12.0-beta.8) (2025-08-20) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.6...v3.12.0-beta.7) (2025-08-15) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.5...v3.12.0-beta.6) (2025-08-13) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.4...v3.12.0-beta.5) (2025-08-11) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.3...v3.12.0-beta.4) (2025-08-11) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.2...v3.12.0-beta.3) (2025-08-11) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.1...v3.12.0-beta.2) (2025-08-08) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.0...v3.12.0-beta.1) (2025-08-05) + +**Note:** Version bump only for package @ohif/ui + + + + + +# [3.12.0-beta.0](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.116...v3.12.0-beta.0) (2025-08-05) **Note:** Version bump only for package @ohif/ui diff --git a/platform/ui/README.md b/platform/ui/README.md index 2dcfda14779..c744c68a56c 100644 --- a/platform/ui/README.md +++ b/platform/ui/README.md @@ -1,14 +1 @@ # @ohif/ui - -## For Running Storybook - -``` -cd platform/ui - -yarn install - -yarn storybook - -``` - -Stories are available default at `http://localhost:6006/` diff --git a/platform/ui/package.json b/platform/ui/package.json index bddd34f0a7d..5fbf09b8e30 100644 --- a/platform/ui/package.json +++ b/platform/ui/package.json @@ -1,6 +1,6 @@ { "name": "@ohif/ui", - "version": "3.11.1", + "version": "3.12.0", "description": "A set of React components for Medical Imaging Viewers", "author": "OHIF Contributors", "license": "MIT", diff --git a/platform/ui/src/components/Button/Button.tsx b/platform/ui/src/components/Button/Button.tsx index f3ba159de7e..09425e310fa 100644 --- a/platform/ui/src/components/Button/Button.tsx +++ b/platform/ui/src/components/Button/Button.tsx @@ -6,6 +6,7 @@ import Tooltip from '../Tooltip/Tooltip'; const sizeClasses = { [ButtonEnums.size.small]: 'h-[26px] text-[13px]', + [ButtonEnums.size.smallTall]: 'h-[32px] text-[13px]', [ButtonEnums.size.medium]: 'h-[32px] text-[14px]', }; diff --git a/platform/ui/src/components/Button/ButtonEnums.ts b/platform/ui/src/components/Button/ButtonEnums.ts index 1fe82c014c9..bda981255b2 100644 --- a/platform/ui/src/components/Button/ButtonEnums.ts +++ b/platform/ui/src/components/Button/ButtonEnums.ts @@ -4,6 +4,7 @@ enum type { } enum size { medium = 'medium', + smallTall = 'smallTall', small = 'small', } diff --git a/platform/ui/src/components/Button/__stories__/button.stories.mdx b/platform/ui/src/components/Button/__stories__/button.stories.mdx deleted file mode 100644 index a77374199f0..00000000000 --- a/platform/ui/src/components/Button/__stories__/button.stories.mdx +++ /dev/null @@ -1,171 +0,0 @@ -import { Button, ButtonEnums } from '../../../components'; -import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs'; -import { - createComponentTemplate, - createStoryMetaSettings, -} from '../../../storybook/functions/create-component-story'; - -export const argTypes = { - component: Button, - title: 'Components/Button', -}; - - - -export const buttonTemplate = createComponentTemplate(Button); - - - -- [Overview](#overview) -- [Props](#props) -- [Usage](#usage) -- [Contribute](#contribute) - -## Overview - -You can use the button component to create a button. It can be used in different ways, the default -button is a simple button with text. - - - - {buttonTemplate.bind({})} - - - -## Props - - - -## Usage - -### Types - -There can be different types of buttons: `primary`, and `secondary`. - - - -
- - -
-
-
- -### Sizes - -There are different sizes for the button: `small`, and `medium`. The size refers to the button's -height. - - - -
- - -
-
-
- -### Mixing props - -You can mix different props together to create a button. - - - - - - - -### Disabled - -You can disable the button by setting the `disabled` property to true. - - - - - - - -### Start/End Icons - -You can add an icon to the start of the button. It accepts an icon component. - - - - {() => { - // svg icon for github - const Github = () => { - return ( - - - - ); - }; - return ; - }} - - - -End Icon is the same as start icon, but for the end of the button. - - - - {() => { - // svg icon for github - const Github = () => { - return ( - - - - ); - }; - return ( - - ); - }} - - - -## Contribute - -