diff --git a/platform/docs/docs/README.md b/platform/docs/docs/README.md index 9b5a6f7d5..f7d7e0a17 100644 --- a/platform/docs/docs/README.md +++ b/platform/docs/docs/README.md @@ -24,10 +24,28 @@ Key features: Tailwind CSS -
-
- Subscribe to our newsletter - Release Notes +
+
+ Subscribe to our newsletter + Release Notes
diff --git a/platform/docs/docs/resources.md b/platform/docs/docs/resources.md index 4c60e18fb..0ae9d1add 100644 --- a/platform/docs/docs/resources.md +++ b/platform/docs/docs/resources.md @@ -11,6 +11,9 @@ and other resources that we have provided to the community in the past: ## 2024 + + + ### IMNO 2024 - March 19-20, 2024 We participated in the Imaging Network Ontario (ImNO) 2024 symposium, presenting three posters. One of our presentations received the best talk award during the session. diff --git a/platform/docs/docusaurus.config.js b/platform/docs/docusaurus.config.js index e52508796..7804034ac 100644 --- a/platform/docs/docusaurus.config.js +++ b/platform/docs/docusaurus.config.js @@ -13,12 +13,17 @@ const versions = fs.readFileSync('../../version.txt', 'utf8').split('\n'); const ArchivedVersionsDropdownItems = [ { - version: '2.0-deprecated', + version: '3.8.5', + href: 'https://v3p8.docs.ohif.org', + isExternal: true, + }, + { + version: '2.0', href: 'https://v2.docs.ohif.org', isExternal: true, }, { - version: '1.0-deprecated', + version: '1.0', href: 'https://v1.docs.ohif.org', isExternal: true, }, @@ -136,10 +141,11 @@ module.exports = { // respectPrefersColorScheme: true, }, announcementBar: { - id: 'healthimaging', + id: 'cornerstone20_ohif_anniversary', content: - '🎉 OHIF 3.8 has landed! Explore 4D and volume rendering, enhanced layout menus, streamlined visualization controls, workflow steps, and more. You can find the release notes by following this Link! 🌟', + '🎉 Celebrating OHIF’s 10-Year Anniversary with Cornerstone 2.0! Explore enhanced segmentation, new video & microscopy viewports, UI/UX upgrades, and blazing fast prefetching. Dive into the release notes here! 🚀', }, + prism: { theme: require('prism-react-renderer').themes.github, darkTheme: require('prism-react-renderer').themes.dracula, diff --git a/platform/docs/package.json b/platform/docs/package.json index f34277209..617c8f08d 100644 --- a/platform/docs/package.json +++ b/platform/docs/package.json @@ -41,16 +41,16 @@ ] }, "dependencies": { - "@docusaurus/core": "3.6.0", - "@docusaurus/faster": "3.6.0", - "@docusaurus/plugin-client-redirects": "3.6.0", - "@docusaurus/plugin-google-gtag": "3.6.0", - "@docusaurus/plugin-ideal-image": "3.6.0", - "@docusaurus/plugin-pwa": "3.6.0", - "@docusaurus/preset-classic": "3.6.0", - "@docusaurus/remark-plugin-npm2yarn": "3.6.0", - "@docusaurus/theme-classic": "3.6.0", - "@docusaurus/theme-live-codeblock": "3.6.0", + "@docusaurus/core": "3.6.1", + "@docusaurus/faster": "3.6.1", + "@docusaurus/plugin-client-redirects": "3.6.1", + "@docusaurus/plugin-google-gtag": "3.6.1", + "@docusaurus/plugin-ideal-image": "3.6.1", + "@docusaurus/plugin-pwa": "3.6.1", + "@docusaurus/preset-classic": "3.6.1", + "@docusaurus/remark-plugin-npm2yarn": "3.6.1", + "@docusaurus/theme-classic": "3.6.1", + "@docusaurus/theme-live-codeblock": "3.6.1", "@mdx-js/react": "3.0.1", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-checkbox": "^1.1.1", diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/customizable-overlay.png b/platform/docs/versioned_docs/version-3.8/assets/img/customizable-overlay.png deleted file mode 100644 index 05ca4d275..000000000 Binary files a/platform/docs/versioned_docs/version-3.8/assets/img/customizable-overlay.png and /dev/null differ diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/demo-microscopy.png b/platform/docs/versioned_docs/version-3.8/assets/img/demo-microscopy.png deleted file mode 100644 index ad5480126..000000000 Binary files a/platform/docs/versioned_docs/version-3.8/assets/img/demo-microscopy.png and /dev/null differ diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/github-readme-branches.png b/platform/docs/versioned_docs/version-3.8/assets/img/github-readme-branches.png deleted file mode 100644 index 271001a47..000000000 Binary files a/platform/docs/versioned_docs/version-3.8/assets/img/github-readme-branches.png and /dev/null differ diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/large-pt-ct.png b/platform/docs/versioned_docs/version-3.8/assets/img/large-pt-ct.png deleted file mode 100644 index 16c16bcba..000000000 Binary files a/platform/docs/versioned_docs/version-3.8/assets/img/large-pt-ct.png and /dev/null differ diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/nginx-image-archive.png b/platform/docs/versioned_docs/version-3.8/assets/img/nginx-image-archive.png deleted file mode 100644 index bd7547965..000000000 Binary files a/platform/docs/versioned_docs/version-3.8/assets/img/nginx-image-archive.png and /dev/null differ diff --git a/platform/docs/versioned_docs/version-3.8/deployment/authorization.md b/platform/docs/versioned_docs/version-3.8/deployment/authorization.md deleted file mode 100644 index b80a7c962..000000000 --- a/platform/docs/versioned_docs/version-3.8/deployment/authorization.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -sidebar_position: 6 -sidebar_label: Authorization ---- - -# Authorization -The OHIF Viewer can be configured to work with authorization servers that support one or more of the OpenID-Connect authorization flows. The Viewer finds it's OpenID-Connect settings on the oidc configuration key. You can set these values in your configuration files. For instance you can take a look at our -`google.js` configuration file. - - -```js -oidc: [ - { - // ~ REQUIRED - // Authorization Server URL - authority: 'https://accounts.google.com', - client_id: - '723928408739-k9k9r3i44j32rhu69vlnibipmmk9i57p.apps.googleusercontent.com', - redirect_uri: '/callback', - response_type: 'id_token token', - scope: - 'email profile openid https://www.googleapis.com/auth/cloudplatformprojects.readonly https://www.googleapis.com/auth/cloud-healthcare', // email profile openid - // ~ OPTIONAL - post_logout_redirect_uri: '/logout-redirect.html', - revoke_uri: 'https://accounts.google.com/o/oauth2/revoke?token=', - automaticSilentRenew: true, - revokeAccessTokenOnSignout: true, - }, - ], -``` - -You need to provide the following information: -- authority: The URL of the authorization server. -- client_id: The client id of your application (provided by the authorization server). -- redirect_uri: The callback URL of your application. -- response_type: The response type of the authorization flow (e.g. id_token token, [learn more about different flows](https://darutk.medium.com/diagrams-of-all-the-openid-connect-flows-6968e3990660)). -- scope: The scopes that your application needs to access -- post_logout_redirect_uri: The URL that the user will be redirected to after logout. -- revoke_uri: The URL that the user will be redirected to after logout. -- automaticSilentRenew: If true, the user will be automatically logged in after the token expires. -- revokeAccessTokenOnSignout: If true, the access token will be revoked on logout. - - - -## How it works -The Viewer uses the `userAuthenticationService` to set the OpenID-Connect settings. The `userAuthenticationService` is a singleton service that is responsible for authentication and authorization. It is initialized by the app and you can grab it -from the `servicesManager` - -```js -const userAuthenticationService = servicesManager.services.userAuthenticationService; -``` - -Then the userAuthenticationService will inject the token as Authorization header in the requests that are sent to the server (both metadata -and pixelData). - - -## Token based authentication -Sometimes (although not recommended), some servers like to send the token -in the query string. In this case, the viewer will automatically grab the token from the query string -and add it to the userAuthenticationService and remove it from the query string (to prevent it from being logged in the console -in future requests). - -and example would be - -```js -http://localhost:3000/viewer?StudyInstanceUIDs=1.2.3.4.5.6.6.7&token=e123125jsdfahsdf -``` diff --git a/platform/docs/versioned_docs/version-3.8/deployment/user-account-control.md b/platform/docs/versioned_docs/version-3.8/deployment/user-account-control.md deleted file mode 100644 index 5447ce282..000000000 --- a/platform/docs/versioned_docs/version-3.8/deployment/user-account-control.md +++ /dev/null @@ -1,292 +0,0 @@ ---- -sidebar_position: 11 ---- -# User Account Control - -> DISCLAIMER! We make no claims or guarantees of this approach's security. If in -> doubt, enlist the help of an expert and conduct proper audits. - -Making a viewer and its medical imaging data accessible on the open web can -provide a lot of benefits, but requires additional security to make sure -sensitive information can only be viewed by authorized individuals. Most image -archives are equipped with basic security measures, but they are not -robust/secure enough for the open web. - -This guide covers one of many potential production setups that secure our -sensitive data. - -## Overview - -This guide builds on top of our -[Nginx + Image Archive guide](/deployment/recipes/nginx--image-archive.md), -wherein we used a [`reverse proxy`](https://en.wikipedia.org/wiki/Reverse_proxy) -to retrieve resources from our image archive (Orthanc). - -To add support for "User Account Control" we introduce -[Keycloak](https://www.keycloak.org/about.html). Keycloak is an open source -Identity and Access Management solution that makes it easy to secure -applications and services with little to no code. We improve upon our -`reverse proxy` setup by integrating Keycloak and Nginx to create an -`authenticating reverse proxy`. - -> An authenticating reverse proxy is a reverse proxy that only retrieves the -> resources on behalf of a client if the client has been authenticated. If a -> client is not authenticated they can be redirected to a login page. - -This setup allows us to create a setup similar to the one pictured below: - -![userControlFlow](../assets/img/user-access-control-request-flow.png) - - - -- All web requests are routed through `nginx` on our `OpenResty` image -- `/pacs` is a reverse proxy for `orthanc`'s `DICOM Web` endpoints - - Requires valid `Authorization: Bearer ` header -- `/pacs-admin` is a reverse proxy for `orthanc`'s Web Admin -- `/auth` is a reverse proxy for `keycloak` -- All static resources for OHIF Viewer are unprotected and accessible. We have - application logic that will redirect unauthenticated users to the appropriate - `keycloak` login screen. - -## Getting Started - -### Requirements - -- Docker - - [Docker for Mac](https://docs.docker.com/docker-for-mac/) - - [Docker for Windows](https://docs.docker.com/docker-for-windows/) - -_Not sure if you have `docker` installed already? Try running `docker --version` -in command prompt or terminal_ - -### Setup - -_Spin Things Up_ - -- Navigate to `platform\app\.recipes\OpenResty-Orthanc-Keycloak` in your shell -- Run `docker-compose up` - -_Create Your First User_ - -- Navigate to: `http://127.0.0.1/auth/admin` -- Sign in with: `admin`/`password` -- From the top left dropdown, select the `Ohif` realm -- From the left sidebar, under `Manage`, select `Users` -- Click `Add User` - - Username: `test` - - Email Verified: `ON` - - Click `Save` -- Click the `Credentials` Tab - - New Password: `test` - - Password Confirmation: `test` - - Temporary: `OFF` - - Click: `Reset Password` -- From the top right dropdown, select `Admin`, then `Sign Out` - -_Sign In_ - -- Navigate to `http://127.0.0.1/` -- Username: `test`, Password: `test` -- Click `Log In` - -_Upload Your First Study_ - -- Navigate to `http://127.0.0.1/pacs-admin` -- If you're not already logged in, use `test`/`test` -- From the top right, select "Upload" -- Click "Select files to upload..." (DICOM) -- Click "Start the upload" -- Navigate back to `http://127.0.0.1/` to view your studies in the Study List - -### Troubleshooting - -_Exit code 137_ - -This means Docker ran out of memory. Open Docker Desktop, go to the `advanced` -tab, and increase the amount of Memory available. - -_Cannot create container for service X_ - -Use this one with caution: `docker system prune` - -_X is already running_ - -Stop running all containers: - -- Win: `docker ps -a -q | ForEach { docker stop $_ }` -- Linux: `docker stop $(docker ps -a -q)` - -### Configuration - -After verifying that everything runs with default configuration values, you will -likely want to update: - -- The domain: `http://127.0.0.1` -- Set secure, non-default passwords -- Regenerate Keycloak Client Secrets - -#### OHIF Viewer - -The OHIF Viewer's configuration is imported from a static `.js` file. The -configuration we use is set to a specific file when we build the viewer, and -determined by the env variable: `APP_CONFIG`. You can see where we set its value -in the `dockerfile` for this solution: - -`ENV APP_CONFIG=config/docker_openresty-orthanc-keycloak.js` - -You can find the configuration we're using here: -`/public/config/docker_openresty-orthanc-keycloak.js` - -To rebuild the `webapp` image created by our `dockerfile` after updating the -Viewer's configuration, you can run: - -- `docker-compose build` OR -- `docker-compose up --build` - -#### Other - -All other files are found in: `/docker/OpenResty-Orthanc-Keycloak/` - -| Service | Configuration | Docs | -| ----------------- | ------------------------------------------------ | ------------------------------------------- | -| OHIF Viewer | [dockerfile][dockerfile] / [config.js][config] | You're reading them now! | -| OpenResty (Nginx) | [`/nginx.conf`][config-nginx] | [lua-resty-openidc][lua-resty-openidc-docs] | -| Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] | -| Keycloak | [`/ohif-keycloak-realm.json`][config-keycloak]\* | | - -\* These are the seed values for Keycloak. They can be manually updated at -`http://127.0.0.1/auth/admin` - -#### Keycloak Themeing - -The `Login` screen for the `ohif-viewer` client is using a Custom Keycloak -theme. You can find the source files for it in -`/docker/OpenResty-Orthanc-Keycloak/volumes/keycloak-themes/`. You can see how -we add it to Keycloak in the `docker-compose` file, and you can read up on how -to leverage custom themes in -[Keycloak's own docs](https://www.keycloak.org/docs/latest/server_development/index.html#_themes). - -| Default Theme | OHIF Theme | -| ---------------------------------------------------------------------- | ---------------------------------------------------------------- | -| ![Keycloak Default Theme](../assets/img/keycloak-default-theme.png) | ![Keycloak OHIF Theme](../assets/img/keycloak-ohif-theme.png) | - -## Next Steps - -### Deploying to Production - -While these configuration and docker-compose files model an environment suitable -for production, they are not easy to deploy "as is". You can either: - -- Manually recreate this environment and deploy built application files **OR** -- Deploy to a cloud kubernetes provider like - [Digital Ocean](https://www.digitalocean.com/products/kubernetes/) **OR** - - [See a full list of cloud providers here](https://landscape.cncf.io/category=cloud&format=card-mode&grouping=category) -- Find and follow your preferred provider's guide on setting up - [swarms and stacks](https://docs.docker.com/get-started/) - -### Adding SSL - -Adding SSL registration and renewal for your domain with Let's Encrypt that -terminates at Nginx is an incredibly important step toward securing your data. -Here are some resources, specific to this setup, that may be helpful: - -- [lua-resty-auto-ssl](https://github.com/GUI/lua-resty-auto-ssl) -- [Let's Encrypt + Nginx](https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/) - -While we terminate SSL at Nginx, it may be worth using self signed certificates -for communication between services. - -- [SSL Termination for TCP Upstream Servers](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-tcp/) - -### Use PostgresSQL w/ Orthanc - -Orthanc can handle a large amount of data and requests, but if you find that -requests start to slow as you add more and more studies, you may want to -configure your Orthanc instance to use PostgresSQL. Instructions on how to do -that can be found in the -[`Orthanc Server Book`](http://book.orthanc-server.com/users/docker.html), under -"PostgreSQL and Orthanc inside Docker" - -### Improving This Guide - -Here are some improvements this guide would benefit from, and that we would be -more than happy to accept Pull Requests for: - -- SSL Support -- Complete configuration with `.env` file (or something similar) -- Keycloak Theme improvements -- Any security issues -- One-click deploy to a cloud provider - -## Resources - -### Misc. Helpful Commands - -_Check if `nginx.conf` is valid:_ - -```bash -docker run --rm -t -a stdout --name my-openresty -v $PWD/config/:/usr/local/openresty/nginx/conf/:ro openresty/openresty:alpine-fat openresty -c /usr/local/openresty/nginx/conf/nginx.conf -t -``` - -_Interact w/ running container:_ - -`docker exec -it CONTAINER_NAME bash` - -_List running containers:_ - -`docker ps` - -_Clear Keycloak DB so you can re-seed values:_ - -- `docker volume prune` OR -- `docker volume ls` and `docker volume rm VOLUME_NAME VOLUME_NAME` - -### Referenced Articles - -The inspiration for our setup was driven largely by these articles: - -- [Securing Nginx with Keycloak](https://edhull.co.uk/blog/2018-06-06/keycloak-nginx) -- [Authenticating Reverse Proxy with Keycloak](https://eclipsesource.com/blogs/2018/01/11/authenticating-reverse-proxy-with-keycloak/) -- [Securing APIs with Kong and Keycloak](https://www.jerney.io/secure-apis-kong-keycloak-1/) - -For more documentation on the software we've chosen to use, you may find the -following resources helpful: - -- [Orthanc for Docker](http://book.orthanc-server.com/users/docker.html) -- [OpenResty Guide](http://www.staticshin.com/programming/definitely-an-open-resty-guide/) -- [Lua Ngx API](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/) -- [Auth0: Picking a Grant Type](https://auth0.com/docs/api-auth/which-oauth-flow-to-use) - -We chose to use a generic OpenID Connect library on the client, but it's worth -noting that Keycloak comes packaged with its own: - -- [oidc-client-js](https://github.com/IdentityModel/oidc-client-js/wiki) -- [Keycloak JavaScript Adapter](https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter) - -If you're not already drowning in links, here are some good security resources -for OAuth: - -- [Diagrams of OpenID Connect Flows](https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660) -- [KeyCloak: OpenID Connect Flows](https://www.keycloak.org/docs/latest/securing_apps/index.html#authorization-code) - -For a different take on this setup, check out the repositories our community -members put together: - -- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker) -- [trypag/ohif-orthanc-postgres-docker](https://github.com/trypag/ohif-orthanc-postgres-docker) - - - - - -[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration -[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc - -[config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/src/config.js -[dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/dockerfile -[config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/nginx.conf -[config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/orthanc.json -[config-keycloak]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/ohif-keycloak-realm.json - diff --git a/platform/docs/versioned_docs/version-3.8/faq.md b/platform/docs/versioned_docs/version-3.8/faq.md deleted file mode 100644 index 5064899db..000000000 --- a/platform/docs/versioned_docs/version-3.8/faq.md +++ /dev/null @@ -1,302 +0,0 @@ ---- -sidebar_position: 8 -sidebar_label: FAQ ---- - - -- [General FAQ](#general-faq) - - [How do I report a bug?](#how-do-i-report-a-bug) - - [How can I request a new feature?](#how-can-i-request-a-new-feature) - - [Who should I contact about Academic Collaborations?](#who-should-i-contact-about-academic-collaborations) - - [Does OHIF offer support?](#does-ohif-offer-support) - - [Does The OHIF Viewer have 510(k) Clearance from the U.S. F.D.A or CE Marking from the European Commission?](#does-the-ohif-viewer-have-510k-clearance-from-the-us-fda-or-ce-marking-from-the-european-commission) - - [Is there a DICOM Conformance Statement for the OHIF Viewer?](#is-there-a-dicom-conformance-statement-for-the-ohif-viewer) - - [Is The OHIF Viewer HIPAA Compliant?](#is-the-ohif-viewer-hipaa-compliant) - - [Could you provide me with a particular study from the OHIF Viewer Demo?](#could-you-provide-me-with-a-particular-study-from-the-ohif-viewer-demo) -- [Technical FAQ](#technical-faq) - - [Why do I keep seeing a Cross Origin Isolation warning](#why-do-i-keep-seeing-a-cross-origin-isolation-warning) - - [What if my setup does not support the Shared Array Buffers API?](#what-if-my-setup-does-not-support-the-shared-array-buffers-api) - - [Viewer opens but does not show any thumbnails](#viewer-opens-but-does-not-show-any-thumbnails) - - [What are the list of required metadata for the OHIF Viewer to work?](#what-are-the-list-of-required-metadata-for-the-ohif-viewer-to-work) - - [Mandatory](#mandatory) - - [Optional](#optional) - - [How do I handle large volumes for MPR and Volume Rendering](#how-do-i-handle-large-volumes-for-mpr-and-volume-rendering) - - [`useNorm16Texture`](#usenorm16texture) - - [`preferSizeOverAccuracy`](#prefersizeoveraccuracy) - - -# General FAQ - - -## How do I report a bug? - -Navigate to our [GitHub Repository][new-issue], and submit a new bug report. -Follow the steps outlined in the [Bug Report Template][bug-report-template]. - -## How can I request a new feature? - -At the moment we are in the process of defining our roadmap and will do our best -to communicate this to the community. If your requested feature is on the -roadmap, then it will most likely be built at some point. If it is not, you are -welcome to build it yourself and [contribute it](development/contributing.md). -If you have resources and would like to fund the development of a feature, -please [contact us](https://ohif.org/get-support). - - -## Who should I contact about Academic Collaborations? - -[Gordon J. Harris](https://www.dfhcc.harvard.edu/insider/member-detail/member/gordon-j-harris-phd/) -at Massachusetts General Hospital is the primary contact for any academic -collaborators. We are always happy to hear about new groups interested in using -the OHIF framework, and may be able to provide development support if the -proposed collaboration has an impact on cancer research. - -## Does OHIF offer support? - -yes, you can contact us for more information [here](https://ohif.org/get-support) - - -## Does The OHIF Viewer have [510(k) Clearance][501k-clearance] from the U.S. F.D.A or [CE Marking][ce-marking] from the European Commission? - -**NO.** The OHIF Viewer is **NOT** F.D.A. cleared or CE Marked. It is the users' -responsibility to ensure compliance with applicable rules and regulations. The -[License](https://github.com/OHIF/Viewers/blob/master/LICENSE) for the OHIF -Platform does not prevent your company or group from seeking F.D.A. clearance -for a product built using the platform. - -If you have gone this route (or are going there), please let us know because we -would be interested to hear about your experience. - -## Is there a DICOM Conformance Statement for the OHIF Viewer? - -Yes, check it here [DICOM Conformance Statement](https://docs.google.com/document/d/1hbDlUApX4svX33gAUGxGfD7fXXZNaBsX0hSePbc-hNA/edit?usp=sharing) - -## Is The OHIF Viewer [HIPAA][hipaa-def] Compliant? - -**NO.** The OHIF Viewer **DOES NOT** fulfill all of the criteria to become HIPAA -Compliant. It is the users' responsibility to ensure compliance with applicable -rules and regulations. - -## Could you provide me with a particular study from the OHIF Viewer Demo? - -You can check out the studies that we have put in this [Dropbox link](https://www.dropbox.com/scl/fo/66xidsx13pn0zf3b9cbfq/ADaCgn7aT29WMlnTdT_WRXM?rlkey=rratvx6g4kfxnswjdbupewjye&dl=0) - -# Technical FAQ - - - -## Why do I keep seeing a Cross Origin Isolation warning -If you encounter a warning while running OHIF indicating that your application is not cross-origin isolated, it implies that volume rendering, such as MPR, will not function properly since they depend on Shared Array Buffers. To resolve this issue, we recommend referring to our comprehensive guide on Cross Origin Isolation available at [our dedicated cors page](./deployment/cors.md). - -## What if my setup does not support the Shared Array Buffers API? -You can simply disable that by adding the `useSharedArrayBuffer: 'FALSE'` (notice the string FALSE), and the volumes will only use a regular -array buffer which is a bit slower but will work on all browsers. - - -## Viewer opens but does not show any thumbnails - -Thumbnails may not appear in your DICOMWeb application for various reasons. This guide focuses on one primary scenario, which is you are using -the `supportsWildcard: true` in your configuration file while your sever does not support it. -One - -For instance for the following filtering in the worklist tab we send this request - -![](assets/img/filtering-worklist.png) - -`https://d33do7qe4w26qo.cloudfront.net/dicomweb/studies?PatientName=*Head*&limit=101&offset=0&fuzzymatching=false&includefield=00081030%2C00080060` - -Which our server can respond properly. If your server does not support this type of filtering, you can disable it by setting `supportsWildcard: false` in your configuration file, -or edit your server code to support it for instance something like - -```js -Pseudocode: -For each filter in filters: - if filter.value contains "*": - Convert "*" to SQL LIKE wildcard ("%") - Add "metadataField LIKE ?" to query - else: - Add "metadataField = ?" to query -``` - - - -## What are the list of required metadata for the OHIF Viewer to work? - - -### Mandatory - -**All Modalities** - -- `StudyInstanceUID`, `SeriesInstanceUID`, `SOPInstanceUID`: Unique identifiers for the study, series, and object. -- `PhotometricInterpretation`: Describes the color space of the image. -- `Rows`, `Columns`: Image dimensions. -- `PixelRepresentation`: Indicates how pixel data should be interpreted. -- `Modality`: Type of modality (e.g., CT, MR, etc.). -- `PixelSpacing`: Spacing between pixels. -- `BitsAllocated`: Number of bits allocated for each pixel sample. -- `SOPClassUID`: Specifies the DICOM service class of the object (though you might be able to render without it for most regular images datasets, but it is pretty normal to have it) - -**Rendering** - -You need to have the following tags for the viewer to render the image properly, otherwise you should -use the windowing tools to adjust the image to your liking: - -- `RescaleIntercept`, `RescaleSlope`: Values used for rescaling pixel values for visualization. -- `WindowCenter`, `WindowWidth`: Windowing parameters for display. - -**Some Datasets** - -- `InstanceNumber`: Useful for sorting instances (without it the instances might be out of order) - -**For MPR (Multi-Planar Reformatting) rendering and tools** - -- `ImagePositionPatient`, `ImageOrientationPatient`: Position and orientation of the image in the patient. - -**SEG (Segmentation)** - -- `FrameOfReferenceUID` for handling segmentation layers. -- sequences - - `ReferencedSeriesSequence` - - `SharedFunctionalGroupsSequence` - - `PerFrameFunctionalGroupsSequence` - -**RTSTRUCT (Radiotherapy Structure)** - -- `FrameOfReferenceUID` for handling segmentation layers. -- sequences - - `ROIContourSequence` - - `StructureSetROISequence` - - `ReferencedFrameOfReferenceSequence` - -**US (Ultrasound)** - -- `NumberOfFrames`: Number of frames in a multi-frame image. -- `SequenceOfUltrasoundRegions`: For measurements. -- `FrameTime`: Time between frames if specified. - -**SR (Structured Reporting)** - -- Various sequences for encoding the report content and template. - - `ConceptNameCodeSequence` - - `ContentSequence` - - `ContentTemplateSequence` - - `CurrentRequestedProcedureEvidenceSequence` - - `ContentTemplateSequence` - - `CodingSchemeIdentificationSequence` - -**PT with SUV Correction (Positron Tomography Standardized Uptake Value)** - -- Sequences and tags related to radiopharmaceuticals, units, corrections, and timing. - - `RadiopharmaceuticalInformationSequence` - - `SeriesDate` - - `SeriesTime` - - `CorrectedImage` - - `Units` - - `DecayCorrection` - - `AcquisitionDate` - - `AcquisitionTime` - - `PatientWeight` - -**PDF** - -- `EncapsulatedDocument`: Contains the PDF document. - -**Video** - -- `NumberOfFrames`: Video frame count . - - -### Optional -There are various other optional tags that will add to the viewer experience, but are not required for basic functionality. These include: -Patient Information, Study Information, Series Information, Instance Information, and Frame Information. - - -## How do I handle large volumes for MPR and Volume Rendering - -Currently there are two ways to handle large volumes for MPR and Volume Rendering if that does not -fit in the memory of the client machine. - -### `useNorm16Texture` - -WebGL officially supports only 8-bit and 32-bit data types. For most images, 8 bits are not enough, and 32 bits are too much. However, we have to use the 32-bit data type for volume rendering and MPR, which results in suboptimal memory consumption for the application. - -Through [EXT_texture_norm16](https://registry.khronos.org/webgl/extensions/EXT_texture_norm16/) , WebGL can support 16 bit data type which is ideal -for most images. You can look into the [webgl report](https://webglreport.com/?v=2) to check if you have that extension enabled. - -![](assets/img/webgl-report-norm16.png) - - -This is a flag that you can set in your [configuration file](./configuration/configurationFiles.md) to force usage of 16 bit data type for the volume rendering and MPR. This will reduce the memory usage by half. - - -For instance for a large pt/ct study - -![](assets/img/large-pt-ct.png) - -Before (without the flag) the app shows 399 MB of memory usage - -![](assets/img/memory-profiling-regular.png) - - -After (with flag, running locally) the app shows 249 MB of memory usage - - -![](assets/img/webgl-int16.png) - -:::note -Using the 16 bit texture (if supported) will not have any effect in the rendering what so ever, and pixelData -would be exactly shown as it is. For datasets that cannot be represented with 16 bit data type, the flag will be ignored -and the 32 bit data type will be used. - - -Read more about these discussions in our PRs -- https://github.com/Kitware/vtk-js/pull/2058 -::: - - -:::warning -Although the support for 16 bit data type is available in WebGL, in some settings (e.g., Intel-based Macos) there seems -to be still some issues with it. You can read and track bugs below. - -- https://bugs.chromium.org/p/chromium/issues/detail?id=1246379 -- https://bugs.chromium.org/p/chromium/issues/detail?id=1408247 -::: - -### `preferSizeOverAccuracy` - -This is another flag that you can set in your [configuration file](./configuration/configurationFiles.md) to force the usage of the `half_float` data type for volume rendering and MPR. The main reason to choose this option over `useNorm16Texture` is its broader support across hardware and browsers. However, it is less accurate than the 16-bit data type and may lead to some rendering artifacts. - -```js -Integers between 0 and 2048 can be exactly represented (and also between −2048 and 0) -Integers between 2048 and 4096 round to a multiple of 2 (even number) -Integers between 4096 and 8192 round to a multiple of 4 -Integers between 8192 and 16384 round to a multiple of 8 -Integers between 16384 and 32768 round to a multiple of 16 -Integers between 32768 and 65519 round to a multiple of 32 -``` - -As you see in the ranges above 2048 there will be inaccuracies in the rendering. - -Memory snapshot after enabling `preferSizeOverAccuracy` for the same study as above - -![](assets/img/preferSizeOverAccuracy.png) - - - - - -[general]: #general -[technical]: #technical -[report-bug]: #how-do-i-report-a-bug -[new-feature]: #how-can-i-request-a-new-feature -[commercial-support]: #does-ohif-offer-commercial-support -[academic]: #who-should-i-contact-about-academic-collaborations -[fda-clearance]: #does-the-ohif-viewer-have-510k-clearance-from-the-us-fda-or-ce-marking-from-the-european-commission -[hipaa]: #is-the-ohif-viewer-hipaa-compliant -[501k-clearance]: https://www.fda.gov/MedicalDevices/DeviceRegulationandGuidance/HowtoMarketYourDevice/PremarketSubmissions/PremarketNotification510k/ -[ce-marking]: https://ec.europa.eu/growth/single-market/ce-marking_en -[hipaa-def]: https://en.wikipedia.org/wiki/Health_Insurance_Portability_and_Accountability_Act -[new-issue]: https://github.com/OHIF/Viewers/issues/new/choose -[bug-report-template]: https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Bug+Report+%3Abug%3A&template=---bug-report.md&title= diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/StateSyncService.md b/platform/docs/versioned_docs/version-3.8/platform/services/data/StateSyncService.md deleted file mode 100644 index ebf056526..000000000 --- a/platform/docs/versioned_docs/version-3.8/platform/services/data/StateSyncService.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -sidebar_position: 8 -sidebar_label: State Sync ---- - -# State Sync - -## Overview - -Applications often need a way to save and restore state, allowing users to continue their work seamlessly across sessions or after restarts. This is essential for keeping user preferences, preserving app context, and providing a consistent experience. For example, hanging protocol layouts, and window level improves user interactions, allowing for smooth navigation between protocols and restoring layouts upon returning. This document explains how to manage and sync state in the application using Zustand stores, providing built-in state stores and tools for extensions to create and manage their own. - - -## Built-in Zustand Stores - -The following stores are available for managing different aspects of application state: - -* **`useLutPresentationStore`**: Manages LUT (window level) presentation state. Key features: - * `setLutPresentation(key, value)`: Sets a LUT presentation for a given `key`. - * `clearLutPresentationStore()`: Clears all stored LUT presentations. - * `getPresentationId(id, options)`: Retrieves the presentation ID based on viewport and display set information. - -* **`usePositionPresentationStore`**: Manages viewport position (camera, initial image) state. Key features: - * `setPositionPresentation(key, value)`: Sets a position presentation for a given `key`. - * `clearPositionPresentationStore()`: Clears all stored position presentations. - * `getPresentationId(id, options)`: Retrieves the presentation ID based on viewport and display set information. - * `getPositionPresentationId(viewport, viewports?, isUpdatingSameViewport?)`: Gets the position presentation ID. - -* **`useSegmentationPresentationStore`**: Manages segmentation presentation state. Key features: - * `setSegmentationPresentation(presentationId, value)`: Sets a segmentation presentation for a given `presentationId`. - * `clearSegmentationPresentationStore()`: Clears all stored segmentation presentations. - * `getPresentationId(id, options)`: Retrieves the presentation ID based on viewport, display set, and services manager information. - * `addSegmentationPresentation(presentationId, segmentationPresentation, { servicesManager })`: Adds a new segmentation presentation. - * `getSegmentationPresentationId({ viewport, servicesManager })`: Retrieves the current segmentation presentation ID. - -* **`useSynchronizersStore`**: Manages viewport synchronization state. Key features: - * `setSynchronizers(viewportId, synchronizers)`: Sets synchronizers for a specific viewport. - * `clearSynchronizersStore()`: Clears the entire synchronizers store. - - -## Creating Custom State Stores with Zustand - -Extensions can create their own Zustand stores to manage custom state. This approach leverages Zustand's simplicity and performance. - -```javascript -import { create } from 'zustand'; -import { devtools } from 'zustand/middleware'; - -// Define your state shape -interface MyCustomState { - count: number; - increment: () => void; - reset: () => void; -} - -// Create your store -const useMyCustomStore = create()( - devtools((set) => ({ - count: 0, - increment: () => set((state) => ({ count: state.count + 1 })), - reset: () => set({ count: 0 }), - }), { name: 'MyCustomStore' }) // Use devtools for debugging -); - -// Use the store in your component -function MyComponent() { - const { count, increment, reset } = useMyCustomStore(); - - return ( -
-

Count: {count}

- - -
- ); -} -``` - -### Best Practices - -* **Naming:** Use a clear and descriptive name for your store, prefixed with `use`, e.g., `useMyCustomStore`. -* **Typing:** Define the state interface using TypeScript for better type safety and code maintainability. -* **Devtools:** Use the `devtools` middleware in development to easily inspect and debug your store's state changes. -* **State Updates:** Use the `set` function to update the state, ensuring immutability by returning a new state object or using the callback form to access the previous state. - - - -## Migration from Legacy State Sync Service - -Existing extensions using the legacy `stateSyncService` should migrate to using individual Zustand stores. This involves: - -1. Creating a new Zustand store for each piece of state previously managed by `stateSyncService`. -2. Updating the extension code to use the new store instead of `stateSyncService`. -3. Removing the legacy `stateSyncService` registration code. diff --git a/platform/docs/versioned_docs/version-3.8/README.md b/platform/docs/versioned_docs/version-3.9/README.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/README.md rename to platform/docs/versioned_docs/version-3.9/README.md diff --git a/platform/docs/versioned_docs/version-3.8/assets/designs/architecture-diagram b/platform/docs/versioned_docs/version-3.9/assets/designs/architecture-diagram similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/designs/architecture-diagram rename to platform/docs/versioned_docs/version-3.9/assets/designs/architecture-diagram diff --git a/platform/docs/versioned_docs/version-3.8/assets/designs/canny-full.fig b/platform/docs/versioned_docs/version-3.9/assets/designs/canny-full.fig similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/designs/canny-full.fig rename to platform/docs/versioned_docs/version-3.9/assets/designs/canny-full.fig diff --git a/platform/docs/versioned_docs/version-3.8/assets/designs/cloud.svg b/platform/docs/versioned_docs/version-3.9/assets/designs/cloud.svg similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/designs/cloud.svg rename to platform/docs/versioned_docs/version-3.9/assets/designs/cloud.svg diff --git a/platform/docs/versioned_docs/version-3.8/assets/designs/embedded-viewer-diagram b/platform/docs/versioned_docs/version-3.9/assets/designs/embedded-viewer-diagram similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/designs/embedded-viewer-diagram rename to platform/docs/versioned_docs/version-3.9/assets/designs/embedded-viewer-diagram diff --git a/platform/docs/versioned_docs/version-3.8/assets/designs/nginx-image-archive.fig b/platform/docs/versioned_docs/version-3.9/assets/designs/nginx-image-archive.fig similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/designs/nginx-image-archive.fig rename to platform/docs/versioned_docs/version-3.9/assets/designs/nginx-image-archive.fig diff --git a/platform/docs/versioned_docs/version-3.8/assets/designs/npm-logo-red.svg b/platform/docs/versioned_docs/version-3.9/assets/designs/npm-logo-red.svg similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/designs/npm-logo-red.svg rename to platform/docs/versioned_docs/version-3.9/assets/designs/npm-logo-red.svg diff --git a/platform/docs/versioned_docs/version-3.8/assets/designs/scope-of-project.fig b/platform/docs/versioned_docs/version-3.9/assets/designs/scope-of-project.fig similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/designs/scope-of-project.fig rename to platform/docs/versioned_docs/version-3.9/assets/designs/scope-of-project.fig diff --git a/platform/docs/versioned_docs/version-3.8/assets/designs/user-access-control-request-flow.fig b/platform/docs/versioned_docs/version-3.9/assets/designs/user-access-control-request-flow.fig similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/designs/user-access-control-request-flow.fig rename to platform/docs/versioned_docs/version-3.9/assets/designs/user-access-control-request-flow.fig diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/OHIF-e2e-test-studies.png b/platform/docs/versioned_docs/version-3.9/assets/img/OHIF-e2e-test-studies.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/OHIF-e2e-test-studies.png rename to platform/docs/versioned_docs/version-3.9/assets/img/OHIF-e2e-test-studies.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/SR-exported.png b/platform/docs/versioned_docs/version-3.9/assets/img/SR-exported.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/SR-exported.png rename to platform/docs/versioned_docs/version-3.9/assets/img/SR-exported.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/WORKFLOW_DEPLOY.png b/platform/docs/versioned_docs/version-3.9/assets/img/WORKFLOW_DEPLOY.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/WORKFLOW_DEPLOY.png rename to platform/docs/versioned_docs/version-3.9/assets/img/WORKFLOW_DEPLOY.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/WORKFLOW_PR_CHECKS.png b/platform/docs/versioned_docs/version-3.9/assets/img/WORKFLOW_PR_CHECKS.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/WORKFLOW_PR_CHECKS.png rename to platform/docs/versioned_docs/version-3.9/assets/img/WORKFLOW_PR_CHECKS.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png b/platform/docs/versioned_docs/version-3.9/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png rename to platform/docs/versioned_docs/version-3.9/assets/img/WORKFLOW_PR_OPTIONAL_DOCKER_PUBLISH.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/WORKFLOW_RELEASE.png b/platform/docs/versioned_docs/version-3.9/assets/img/WORKFLOW_RELEASE.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/WORKFLOW_RELEASE.png rename to platform/docs/versioned_docs/version-3.9/assets/img/WORKFLOW_RELEASE.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/add-extension.png b/platform/docs/versioned_docs/version-3.9/assets/img/add-extension.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/add-extension.png rename to platform/docs/versioned_docs/version-3.9/assets/img/add-extension.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/add-mode.png b/platform/docs/versioned_docs/version-3.9/assets/img/add-mode.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/add-mode.png rename to platform/docs/versioned_docs/version-3.9/assets/img/add-mode.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/browser-console-non-secure-context.png b/platform/docs/versioned_docs/version-3.9/assets/img/browser-console-non-secure-context.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/browser-console-non-secure-context.png rename to platform/docs/versioned_docs/version-3.9/assets/img/browser-console-non-secure-context.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/cli-search-no-verbose.png b/platform/docs/versioned_docs/version-3.9/assets/img/cli-search-no-verbose.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/cli-search-no-verbose.png rename to platform/docs/versioned_docs/version-3.9/assets/img/cli-search-no-verbose.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/cli-search-with-verbose.png b/platform/docs/versioned_docs/version-3.9/assets/img/cli-search-with-verbose.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/cli-search-with-verbose.png rename to platform/docs/versioned_docs/version-3.9/assets/img/cli-search-with-verbose.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/clock-mode.png b/platform/docs/versioned_docs/version-3.9/assets/img/clock-mode.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/clock-mode.png rename to platform/docs/versioned_docs/version-3.9/assets/img/clock-mode.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/clock-mode1.png b/platform/docs/versioned_docs/version-3.9/assets/img/clock-mode1.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/clock-mode1.png rename to platform/docs/versioned_docs/version-3.9/assets/img/clock-mode1.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/cornerstone-tools-link.gif b/platform/docs/versioned_docs/version-3.9/assets/img/cornerstone-tools-link.gif similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/cornerstone-tools-link.gif rename to platform/docs/versioned_docs/version-3.9/assets/img/cornerstone-tools-link.gif diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/cors-browser-console-errors.png b/platform/docs/versioned_docs/version-3.9/assets/img/cors-browser-console-errors.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/cors-browser-console-errors.png rename to platform/docs/versioned_docs/version-3.9/assets/img/cors-browser-console-errors.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/cors-network-panel-errors.png b/platform/docs/versioned_docs/version-3.9/assets/img/cors-network-panel-errors.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/cors-network-panel-errors.png rename to platform/docs/versioned_docs/version-3.9/assets/img/cors-network-panel-errors.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/create-extension.png b/platform/docs/versioned_docs/version-3.9/assets/img/create-extension.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/create-extension.png rename to platform/docs/versioned_docs/version-3.9/assets/img/create-extension.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/create-mode.png b/platform/docs/versioned_docs/version-3.9/assets/img/create-mode.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/create-mode.png rename to platform/docs/versioned_docs/version-3.9/assets/img/create-mode.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/custom-logo.png b/platform/docs/versioned_docs/version-3.9/assets/img/custom-logo.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/custom-logo.png rename to platform/docs/versioned_docs/version-3.9/assets/img/custom-logo.png diff --git a/platform/docs/versioned_docs/version-3.9/assets/img/customizable-overlay.jpeg b/platform/docs/versioned_docs/version-3.9/assets/img/customizable-overlay.jpeg new file mode 100644 index 000000000..e166f2440 Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/assets/img/customizable-overlay.jpeg differ diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/data-source-configuration-ui.png b/platform/docs/versioned_docs/version-3.9/assets/img/data-source-configuration-ui.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/data-source-configuration-ui.png rename to platform/docs/versioned_docs/version-3.9/assets/img/data-source-configuration-ui.png diff --git a/platform/docs/versioned_docs/version-3.9/assets/img/dcm4chee-upload.gif b/platform/docs/versioned_docs/version-3.9/assets/img/dcm4chee-upload.gif new file mode 100644 index 000000000..e0e94f103 Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/assets/img/dcm4chee-upload.gif differ diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/demo-4d.webp b/platform/docs/versioned_docs/version-3.9/assets/img/demo-4d.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/demo-4d.webp rename to platform/docs/versioned_docs/version-3.9/assets/img/demo-4d.webp diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/demo-measurements.webp b/platform/docs/versioned_docs/version-3.9/assets/img/demo-measurements.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/demo-measurements.webp rename to platform/docs/versioned_docs/version-3.9/assets/img/demo-measurements.webp diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/demo-pdf.webp b/platform/docs/versioned_docs/version-3.9/assets/img/demo-pdf.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/demo-pdf.webp rename to platform/docs/versioned_docs/version-3.9/assets/img/demo-pdf.webp diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/demo-ptct.webp b/platform/docs/versioned_docs/version-3.9/assets/img/demo-ptct.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/demo-ptct.webp rename to platform/docs/versioned_docs/version-3.9/assets/img/demo-ptct.webp diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/demo-rtstruct.webp b/platform/docs/versioned_docs/version-3.9/assets/img/demo-rtstruct.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/demo-rtstruct.webp rename to platform/docs/versioned_docs/version-3.9/assets/img/demo-rtstruct.webp diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/demo-segmentation.webp b/platform/docs/versioned_docs/version-3.9/assets/img/demo-segmentation.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/demo-segmentation.webp rename to platform/docs/versioned_docs/version-3.9/assets/img/demo-segmentation.webp diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/demo-video.webp b/platform/docs/versioned_docs/version-3.9/assets/img/demo-video.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/demo-video.webp rename to platform/docs/versioned_docs/version-3.9/assets/img/demo-video.webp diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/demo-volume-rendering.webp b/platform/docs/versioned_docs/version-3.9/assets/img/demo-volume-rendering.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/demo-volume-rendering.webp rename to platform/docs/versioned_docs/version-3.9/assets/img/demo-volume-rendering.webp diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/demo-volumeRendering.png b/platform/docs/versioned_docs/version-3.9/assets/img/demo-volumeRendering.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/demo-volumeRendering.png rename to platform/docs/versioned_docs/version-3.9/assets/img/demo-volumeRendering.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/dicom-json-public.png b/platform/docs/versioned_docs/version-3.9/assets/img/dicom-json-public.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/dicom-json-public.png rename to platform/docs/versioned_docs/version-3.9/assets/img/dicom-json-public.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/dicom-json.png b/platform/docs/versioned_docs/version-3.9/assets/img/dicom-json.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/dicom-json.png rename to platform/docs/versioned_docs/version-3.9/assets/img/dicom-json.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/docker-pacs.png b/platform/docs/versioned_docs/version-3.9/assets/img/docker-pacs.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/docker-pacs.png rename to platform/docs/versioned_docs/version-3.9/assets/img/docker-pacs.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/e2e-cypress-final.png b/platform/docs/versioned_docs/version-3.9/assets/img/e2e-cypress-final.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/e2e-cypress-final.png rename to platform/docs/versioned_docs/version-3.9/assets/img/e2e-cypress-final.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/e2e-cypress.png b/platform/docs/versioned_docs/version-3.9/assets/img/e2e-cypress.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/e2e-cypress.png rename to platform/docs/versioned_docs/version-3.9/assets/img/e2e-cypress.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/embedded-viewer-diagram.png b/platform/docs/versioned_docs/version-3.9/assets/img/embedded-viewer-diagram.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/embedded-viewer-diagram.png rename to platform/docs/versioned_docs/version-3.9/assets/img/embedded-viewer-diagram.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/filtering-worklist.png b/platform/docs/versioned_docs/version-3.9/assets/img/filtering-worklist.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/filtering-worklist.png rename to platform/docs/versioned_docs/version-3.9/assets/img/filtering-worklist.png diff --git a/platform/docs/versioned_docs/version-3.9/assets/img/github-readme-branches-Jun2024.png b/platform/docs/versioned_docs/version-3.9/assets/img/github-readme-branches-Jun2024.png new file mode 100644 index 000000000..4129cc43b Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/assets/img/github-readme-branches-Jun2024.png differ diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/google-create-credentials.png b/platform/docs/versioned_docs/version-3.9/assets/img/google-create-credentials.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/google-create-credentials.png rename to platform/docs/versioned_docs/version-3.9/assets/img/google-create-credentials.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/google-enable-apis.png b/platform/docs/versioned_docs/version-3.9/assets/img/google-enable-apis.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/google-enable-apis.png rename to platform/docs/versioned_docs/version-3.9/assets/img/google-enable-apis.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/google-healthcare-service-agent-warning.png b/platform/docs/versioned_docs/version-3.9/assets/img/google-healthcare-service-agent-warning.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/google-healthcare-service-agent-warning.png rename to platform/docs/versioned_docs/version-3.9/assets/img/google-healthcare-service-agent-warning.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/google-manually-add-scopes.png b/platform/docs/versioned_docs/version-3.9/assets/img/google-manually-add-scopes.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/google-manually-add-scopes.png rename to platform/docs/versioned_docs/version-3.9/assets/img/google-manually-add-scopes.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/google-oauth-consent-steps.png b/platform/docs/versioned_docs/version-3.9/assets/img/google-oauth-consent-steps.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/google-oauth-consent-steps.png rename to platform/docs/versioned_docs/version-3.9/assets/img/google-oauth-consent-steps.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/google-projects-drop-down.png b/platform/docs/versioned_docs/version-3.9/assets/img/google-projects-drop-down.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/google-projects-drop-down.png rename to platform/docs/versioned_docs/version-3.9/assets/img/google-projects-drop-down.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/google-provided-accounts-checkbox.png b/platform/docs/versioned_docs/version-3.9/assets/img/google-provided-accounts-checkbox.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/google-provided-accounts-checkbox.png rename to platform/docs/versioned_docs/version-3.9/assets/img/google-provided-accounts-checkbox.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/hangingProtocolExample.png b/platform/docs/versioned_docs/version-3.9/assets/img/hangingProtocolExample.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/hangingProtocolExample.png rename to platform/docs/versioned_docs/version-3.9/assets/img/hangingProtocolExample.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/iframe-basic.png b/platform/docs/versioned_docs/version-3.9/assets/img/iframe-basic.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/iframe-basic.png rename to platform/docs/versioned_docs/version-3.9/assets/img/iframe-basic.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/iframe-headers.png b/platform/docs/versioned_docs/version-3.9/assets/img/iframe-headers.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/iframe-headers.png rename to platform/docs/versioned_docs/version-3.9/assets/img/iframe-headers.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/jwt-explained.png b/platform/docs/versioned_docs/version-3.9/assets/img/jwt-explained.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/jwt-explained.png rename to platform/docs/versioned_docs/version-3.9/assets/img/jwt-explained.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/keycloak-default-theme.png b/platform/docs/versioned_docs/version-3.9/assets/img/keycloak-default-theme.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/keycloak-default-theme.png rename to platform/docs/versioned_docs/version-3.9/assets/img/keycloak-default-theme.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/keycloak-ohif-theme.png b/platform/docs/versioned_docs/version-3.9/assets/img/keycloak-ohif-theme.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/keycloak-ohif-theme.png rename to platform/docs/versioned_docs/version-3.9/assets/img/keycloak-ohif-theme.png diff --git a/platform/docs/versioned_docs/version-3.9/assets/img/large-pt-ct.jpeg b/platform/docs/versioned_docs/version-3.9/assets/img/large-pt-ct.jpeg new file mode 100644 index 000000000..9999e247e Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/assets/img/large-pt-ct.jpeg differ diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/locizeSponsor.svg b/platform/docs/versioned_docs/version-3.9/assets/img/locizeSponsor.svg similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/locizeSponsor.svg rename to platform/docs/versioned_docs/version-3.9/assets/img/locizeSponsor.svg diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/locked-sr.png b/platform/docs/versioned_docs/version-3.9/assets/img/locked-sr.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/locked-sr.png rename to platform/docs/versioned_docs/version-3.9/assets/img/locked-sr.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/measurement-panel-1.png b/platform/docs/versioned_docs/version-3.9/assets/img/measurement-panel-1.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/measurement-panel-1.png rename to platform/docs/versioned_docs/version-3.9/assets/img/measurement-panel-1.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/measurement-panel-prompt.png b/platform/docs/versioned_docs/version-3.9/assets/img/measurement-panel-prompt.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/measurement-panel-prompt.png rename to platform/docs/versioned_docs/version-3.9/assets/img/measurement-panel-prompt.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/measurement-panel-tracked.png b/platform/docs/versioned_docs/version-3.9/assets/img/measurement-panel-tracked.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/measurement-panel-tracked.png rename to platform/docs/versioned_docs/version-3.9/assets/img/measurement-panel-tracked.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/measurement-temporary.png b/platform/docs/versioned_docs/version-3.9/assets/img/measurement-temporary.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/measurement-temporary.png rename to platform/docs/versioned_docs/version-3.9/assets/img/measurement-temporary.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/measurements-prevNext.png b/platform/docs/versioned_docs/version-3.9/assets/img/measurements-prevNext.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/measurements-prevNext.png rename to platform/docs/versioned_docs/version-3.9/assets/img/measurements-prevNext.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/memory-profiling-regular.png b/platform/docs/versioned_docs/version-3.9/assets/img/memory-profiling-regular.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/memory-profiling-regular.png rename to platform/docs/versioned_docs/version-3.9/assets/img/memory-profiling-regular.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/microscopy.webp b/platform/docs/versioned_docs/version-3.9/assets/img/microscopy.webp similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/microscopy.webp rename to platform/docs/versioned_docs/version-3.9/assets/img/microscopy.webp diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/migration-modes.png b/platform/docs/versioned_docs/version-3.9/assets/img/migration-modes.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/migration-modes.png rename to platform/docs/versioned_docs/version-3.9/assets/img/migration-modes.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/migration-split-button.png b/platform/docs/versioned_docs/version-3.9/assets/img/migration-split-button.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/migration-split-button.png rename to platform/docs/versioned_docs/version-3.9/assets/img/migration-split-button.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/mode-archs.png b/platform/docs/versioned_docs/version-3.9/assets/img/mode-archs.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/mode-archs.png rename to platform/docs/versioned_docs/version-3.9/assets/img/mode-archs.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/mode-clock.png b/platform/docs/versioned_docs/version-3.9/assets/img/mode-clock.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/mode-clock.png rename to platform/docs/versioned_docs/version-3.9/assets/img/mode-clock.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/mode-template.png b/platform/docs/versioned_docs/version-3.9/assets/img/mode-template.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/mode-template.png rename to platform/docs/versioned_docs/version-3.9/assets/img/mode-template.png diff --git a/platform/docs/versioned_docs/version-3.9/assets/img/nginx-image-archive.png b/platform/docs/versioned_docs/version-3.9/assets/img/nginx-image-archive.png new file mode 100644 index 000000000..f1ac06119 Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/assets/img/nginx-image-archive.png differ diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/ohif-cli-list.png b/platform/docs/versioned_docs/version-3.9/assets/img/ohif-cli-list.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/ohif-cli-list.png rename to platform/docs/versioned_docs/version-3.9/assets/img/ohif-cli-list.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/ohif-non-secure-context.png b/platform/docs/versioned_docs/version-3.9/assets/img/ohif-non-secure-context.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/ohif-non-secure-context.png rename to platform/docs/versioned_docs/version-3.9/assets/img/ohif-non-secure-context.png diff --git a/platform/docs/versioned_docs/version-3.9/assets/img/ohif-pacs-keycloak.png b/platform/docs/versioned_docs/version-3.9/assets/img/ohif-pacs-keycloak.png new file mode 100644 index 000000000..e95d2af35 Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/assets/img/ohif-pacs-keycloak.png differ diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/open-graph.png b/platform/docs/versioned_docs/version-3.9/assets/img/open-graph.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/open-graph.png rename to platform/docs/versioned_docs/version-3.9/assets/img/open-graph.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/overview.png b/platform/docs/versioned_docs/version-3.9/assets/img/overview.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/overview.png rename to platform/docs/versioned_docs/version-3.9/assets/img/overview.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/panel-module-left-right.png b/platform/docs/versioned_docs/version-3.9/assets/img/panel-module-left-right.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/panel-module-left-right.png rename to platform/docs/versioned_docs/version-3.9/assets/img/panel-module-left-right.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/panel-module-v3.png b/platform/docs/versioned_docs/version-3.9/assets/img/panel-module-v3.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/panel-module-v3.png rename to platform/docs/versioned_docs/version-3.9/assets/img/panel-module-v3.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/panelmodule-icon.png b/platform/docs/versioned_docs/version-3.9/assets/img/panelmodule-icon.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/panelmodule-icon.png rename to platform/docs/versioned_docs/version-3.9/assets/img/panelmodule-icon.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/preferSizeOverAccuracy.png b/platform/docs/versioned_docs/version-3.9/assets/img/preferSizeOverAccuracy.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/preferSizeOverAccuracy.png rename to platform/docs/versioned_docs/version-3.9/assets/img/preferSizeOverAccuracy.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/progressDropdown.png b/platform/docs/versioned_docs/version-3.9/assets/img/progressDropdown.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/progressDropdown.png rename to platform/docs/versioned_docs/version-3.9/assets/img/progressDropdown.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/reference-lines-from-start.png b/platform/docs/versioned_docs/version-3.9/assets/img/reference-lines-from-start.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/reference-lines-from-start.png rename to platform/docs/versioned_docs/version-3.9/assets/img/reference-lines-from-start.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/restore-exported-sr.png b/platform/docs/versioned_docs/version-3.9/assets/img/restore-exported-sr.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/restore-exported-sr.png rename to platform/docs/versioned_docs/version-3.9/assets/img/restore-exported-sr.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/scope-of-project.png b/platform/docs/versioned_docs/version-3.9/assets/img/scope-of-project.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/scope-of-project.png rename to platform/docs/versioned_docs/version-3.9/assets/img/scope-of-project.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/self-signed-cert-advanced-warning.png b/platform/docs/versioned_docs/version-3.9/assets/img/self-signed-cert-advanced-warning.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/self-signed-cert-advanced-warning.png rename to platform/docs/versioned_docs/version-3.9/assets/img/self-signed-cert-advanced-warning.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/self-signed-cert-warning.png b/platform/docs/versioned_docs/version-3.9/assets/img/self-signed-cert-warning.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/self-signed-cert-warning.png rename to platform/docs/versioned_docs/version-3.9/assets/img/self-signed-cert-warning.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/services-data.png b/platform/docs/versioned_docs/version-3.9/assets/img/services-data.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/services-data.png rename to platform/docs/versioned_docs/version-3.9/assets/img/services-data.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/services-measurements.png b/platform/docs/versioned_docs/version-3.9/assets/img/services-measurements.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/services-measurements.png rename to platform/docs/versioned_docs/version-3.9/assets/img/services-measurements.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/services-ui.png b/platform/docs/versioned_docs/version-3.9/assets/img/services-ui.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/services-ui.png rename to platform/docs/versioned_docs/version-3.9/assets/img/services-ui.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/services.png b/platform/docs/versioned_docs/version-3.9/assets/img/services.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/services.png rename to platform/docs/versioned_docs/version-3.9/assets/img/services.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/surge-deploy.gif b/platform/docs/versioned_docs/version-3.9/assets/img/surge-deploy.gif similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/surge-deploy.gif rename to platform/docs/versioned_docs/version-3.9/assets/img/surge-deploy.gif diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/template-extension-files.png b/platform/docs/versioned_docs/version-3.9/assets/img/template-extension-files.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/template-extension-files.png rename to platform/docs/versioned_docs/version-3.9/assets/img/template-extension-files.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/template-mode-files.png b/platform/docs/versioned_docs/version-3.9/assets/img/template-mode-files.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/template-mode-files.png rename to platform/docs/versioned_docs/version-3.9/assets/img/template-mode-files.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/template-mode-ui.png b/platform/docs/versioned_docs/version-3.9/assets/img/template-mode-ui.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/template-mode-ui.png rename to platform/docs/versioned_docs/version-3.9/assets/img/template-mode-ui.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/toolbar-module.png b/platform/docs/versioned_docs/version-3.9/assets/img/toolbar-module.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/toolbar-module.png rename to platform/docs/versioned_docs/version-3.9/assets/img/toolbar-module.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/toolbarModule-layout.png b/platform/docs/versioned_docs/version-3.9/assets/img/toolbarModule-layout.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/toolbarModule-layout.png rename to platform/docs/versioned_docs/version-3.9/assets/img/toolbarModule-layout.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/toolbarModule-nested-buttons.png b/platform/docs/versioned_docs/version-3.9/assets/img/toolbarModule-nested-buttons.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/toolbarModule-nested-buttons.png rename to platform/docs/versioned_docs/version-3.9/assets/img/toolbarModule-nested-buttons.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/toolbarModule-zoom.png b/platform/docs/versioned_docs/version-3.9/assets/img/toolbarModule-zoom.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/toolbarModule-zoom.png rename to platform/docs/versioned_docs/version-3.9/assets/img/toolbarModule-zoom.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/toolbox-modal.png b/platform/docs/versioned_docs/version-3.9/assets/img/toolbox-modal.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/toolbox-modal.png rename to platform/docs/versioned_docs/version-3.9/assets/img/toolbox-modal.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/tracked-not-tracked.png b/platform/docs/versioned_docs/version-3.9/assets/img/tracked-not-tracked.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/tracked-not-tracked.png rename to platform/docs/versioned_docs/version-3.9/assets/img/tracked-not-tracked.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/tracking-workflow1.png b/platform/docs/versioned_docs/version-3.9/assets/img/tracking-workflow1.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/tracking-workflow1.png rename to platform/docs/versioned_docs/version-3.9/assets/img/tracking-workflow1.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/tracking-workflow2.png b/platform/docs/versioned_docs/version-3.9/assets/img/tracking-workflow2.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/tracking-workflow2.png rename to platform/docs/versioned_docs/version-3.9/assets/img/tracking-workflow2.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/tracking-workflow3.png b/platform/docs/versioned_docs/version-3.9/assets/img/tracking-workflow3.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/tracking-workflow3.png rename to platform/docs/versioned_docs/version-3.9/assets/img/tracking-workflow3.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/ui-modal.gif b/platform/docs/versioned_docs/version-3.9/assets/img/ui-modal.gif similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/ui-modal.gif rename to platform/docs/versioned_docs/version-3.9/assets/img/ui-modal.gif diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/ui-services.png b/platform/docs/versioned_docs/version-3.9/assets/img/ui-services.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/ui-services.png rename to platform/docs/versioned_docs/version-3.9/assets/img/ui-services.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/uploader.gif b/platform/docs/versioned_docs/version-3.9/assets/img/uploader.gif similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/uploader.gif rename to platform/docs/versioned_docs/version-3.9/assets/img/uploader.gif diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-access-control-request-flow.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-access-control-request-flow.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-access-control-request-flow.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-access-control-request-flow.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-hotkeys-default.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-hotkeys-default.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-hotkeys-default.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-hotkeys-default.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-hotkeys.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-hotkeys.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-hotkeys.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-hotkeys.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-measurement-export.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-measurement-export.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-measurement-export.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-measurement-export.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-open-viewer.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-open-viewer.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-open-viewer.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-open-viewer.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-study-filter.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-study-filter.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-study-filter.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-study-filter.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-study-list.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-study-list.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-study-list.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-study-list.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-study-next.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-study-next.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-study-next.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-study-next.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-study-panel.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-study-panel.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-study-panel.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-study-panel.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-study-summary.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-study-summary.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-study-summary.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-study-summary.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-studyist-modespecific.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-studyist-modespecific.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-studyist-modespecific.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-studyist-modespecific.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-toolbar-download-icon.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-toolbar-download-icon.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-toolbar-download-icon.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-toolbar-download-icon.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-toolbar-extra.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-toolbar-extra.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-toolbar-extra.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-toolbar-extra.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-toolbar-preset.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-toolbar-preset.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-toolbar-preset.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-toolbar-preset.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-toolbarDownload.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-toolbarDownload.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-toolbarDownload.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-toolbarDownload.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-viewer-layout.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-viewer-layout.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-viewer-layout.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-viewer-layout.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-viewer-main.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-viewer-main.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-viewer-main.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-viewer-main.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-viewer-toolbar-measurements.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-viewer-toolbar-measurements.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-viewer-toolbar-measurements.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-viewer-toolbar-measurements.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-viewer-toolbar.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-viewer-toolbar.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-viewer-toolbar.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-viewer-toolbar.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/user-viewer.png b/platform/docs/versioned_docs/version-3.9/assets/img/user-viewer.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/user-viewer.png rename to platform/docs/versioned_docs/version-3.9/assets/img/user-viewer.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/viewportModule-layout.png b/platform/docs/versioned_docs/version-3.9/assets/img/viewportModule-layout.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/viewportModule-layout.png rename to platform/docs/versioned_docs/version-3.9/assets/img/viewportModule-layout.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/viewportModule.png b/platform/docs/versioned_docs/version-3.9/assets/img/viewportModule.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/viewportModule.png rename to platform/docs/versioned_docs/version-3.9/assets/img/viewportModule.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/webgl-int16.png b/platform/docs/versioned_docs/version-3.9/assets/img/webgl-int16.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/webgl-int16.png rename to platform/docs/versioned_docs/version-3.9/assets/img/webgl-int16.png diff --git a/platform/docs/versioned_docs/version-3.8/assets/img/webgl-report-norm16.png b/platform/docs/versioned_docs/version-3.9/assets/img/webgl-report-norm16.png similarity index 100% rename from platform/docs/versioned_docs/version-3.8/assets/img/webgl-report-norm16.png rename to platform/docs/versioned_docs/version-3.9/assets/img/webgl-report-norm16.png diff --git a/platform/docs/versioned_docs/version-3.8/configuration/_category_.json b/platform/docs/versioned_docs/version-3.9/configuration/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/configuration/_category_.json rename to platform/docs/versioned_docs/version-3.9/configuration/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/configuration/configurationFiles.md b/platform/docs/versioned_docs/version-3.9/configuration/configurationFiles.md similarity index 91% rename from platform/docs/versioned_docs/version-3.8/configuration/configurationFiles.md rename to platform/docs/versioned_docs/version-3.9/configuration/configurationFiles.md index 1c945f4cb..db24e8da9 100644 --- a/platform/docs/versioned_docs/version-3.8/configuration/configurationFiles.md +++ b/platform/docs/versioned_docs/version-3.9/configuration/configurationFiles.md @@ -124,6 +124,8 @@ Here are a list of some options available: - `acceptHeader` : accept header to request specific dicom transfer syntax ex : [ 'multipart/related; type=image/jls; q=1', 'multipart/related; type=application/octet-stream; q=0.1' ] - `investigationalUseDialog`: This should contain an object with `option` value, it can be either `always` which always shows the dialog once per session, `never` which never shows the dialog, or `configure` which shows the dialog once and won't show it again until a set number of days defined by the user, if it's set to configure, you are required to add an additional property `days` which is the number of days to wait before showing the dialog again. - `groupEnabledModesFirst`: boolean, if set to true, all valid modes for the study get grouped together first, then the rest of the modes. If false, all modes are shown in the order they are defined in the configuration. +- `experimentalStudyBrowserSort`: boolean, if set to true, you will get the experimental StudyBrowserSort component in the UI, which displays a list of sort functions that the displaySets can be sorted by, the sort reflects in all part of the app including the thumbnail/study panel. These sort functions are defined in the customizationModule and can be expanded by users. +- `disableConfirmationPrompts`: boolean, if set to true, it skips confirmation prompts for measurement tracking and hydration. - `showPatientInfo`: string, if set to 'visible', the patient info header will be shown and its initial state is expanded. If set to 'visibleCollapsed', the patient info header will be shown but it's initial state is collapsed. If set to 'disabled', the patient info header will never be shown, and if set to 'visibleReadOnly', the patient info header will be shown and always expanded. - `requestTransferSyntaxUID` : Request a specific Transfer syntax from dicom web server ex: 1.2.840.10008.1.2.4.80 (applied only if acceptHeader is not set) - `omitQuotationForMultipartRequest`: Some servers (e.g., .NET) require the `multipart/related` request to be sent without quotation marks. Defaults to `false`. If your server doesn't require this, then setting this flag to `true` might improve performance (by removing the need for preflight requests). Also note that @@ -224,7 +226,6 @@ Example usage:
supportsFuzzyMatching: false, supportsWildcard: false, singlepart: 'bulkdata,video,pdf', - useBulkDataURI: false, onConfiguration: (dicomWebConfig, options) => { const { params } = options; const { project, location, dataset, dicomStore } = params; @@ -256,6 +257,35 @@ This will result in two panels, one with `dicomSeg.panel` and `tracked.measureme ::: +### Study Prefetcher + +You can enable the study prefetcher so that OHIF loads the next/previous series/display sets +based on the proximity to the current series/display set. This can be useful to improve the user experience + + +```js + studyPrefetcher: { + /* Enable/disable study prefetching service (default: false) */ + enabled: true, + /* Number of displaysets to be prefetched (default: 2)*/ + displaySetCount: 2, + /** + * Max number of concurrent prefetch requests (default: 10) + * High numbers may impact on the time to load a new dropped series because + * the browser will be busy with all prefetching requests. As soon as the + * prefetch requests get fulfilled the new ones from the new dropped series + * are sent to the server. + * + * TODO: abort all prefetch requests when a new series is loaded on a viewport. + * (need to add support for `AbortController` on Cornerstone) + * */ + maxNumPrefetchRequests: 10, + /* Display sets loading order (closest (deafult), downward or upward) */ + order: 'closest', + }, + +``` + ### More on Accept Header Configuration In the previous section we showed that you can modify the `acceptHeader` configuration to request specific dicom transfer syntax. By default @@ -292,6 +322,7 @@ alternative data source (or even specify different default hotkeys). | `APP_CONFIG` | Which [configuration file][config-file] to copy to output as `app-config.js` | `config/default.js` | | `PROXY_TARGET` | When developing, proxy requests that match this pattern to `PROXY_DOMAIN` | `undefined` | | `PROXY_DOMAIN` | When developing, proxy requests from `PROXY_TARGET` to `PROXY_DOMAIN` | `undefined` | +| `OHIF_PORT` | The port to run the webpack server on for PWA builds. | `3000` | You can also create a new config file and specify its path relative to the build output's root by setting the `APP_CONFIG` environment variable. You can set the diff --git a/platform/docs/versioned_docs/version-3.8/configuration/dataSources/_category_.json b/platform/docs/versioned_docs/version-3.9/configuration/dataSources/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/configuration/dataSources/_category_.json rename to platform/docs/versioned_docs/version-3.9/configuration/dataSources/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/configuration/dataSources/configuration-ui.md b/platform/docs/versioned_docs/version-3.9/configuration/dataSources/configuration-ui.md similarity index 94% rename from platform/docs/versioned_docs/version-3.8/configuration/dataSources/configuration-ui.md rename to platform/docs/versioned_docs/version-3.9/configuration/dataSources/configuration-ui.md index 6477113e9..1ff70f409 100644 --- a/platform/docs/versioned_docs/version-3.8/configuration/dataSources/configuration-ui.md +++ b/platform/docs/versioned_docs/version-3.9/configuration/dataSources/configuration-ui.md @@ -71,19 +71,19 @@ Besides the configurable item labels themselves, several other string look ups are used base on EACH of the labels returned by this method. For instance, for the label `{itemLabel}`, the following strings are fetched for translation... -1. `No {itemLabel} available` +1. No `{itemLabel}` available - used to indicate no such items are available - - for example, for Google, `No Project available` would be 'No projects available' -2. `Select {itemLabel}` + - for example, for Google, No Project available would be 'No projects available' +2. Select `{itemLabel}` - used to direct selection of the item - - for example, for Google, `Select Project` would be 'Select a project' -3. `Error fetching {itemLabel} list` + - for example, for Google, Select Project would be 'Select a project' +3. Error fetching `{itemLabel}` list - used to indicate an error occurred fetching the list of items - usually accompanied by the error itself - - for example, for Google, `Error fetching Project list` would be 'Error fetching projects' -4. `Search {itemLabel} list` + - for example, for Google, Error fetching Project list would be 'Error fetching projects' +4. Search `{itemLabel}` list - used as the placeholder text for filtering a list of items - - for example, for Google, `Search Project list` would be 'Search projects' + - for example, for Google, Search Project list would be 'Search projects' #### `initialize` diff --git a/platform/docs/versioned_docs/version-3.8/configuration/dataSources/dicom-json.md b/platform/docs/versioned_docs/version-3.9/configuration/dataSources/dicom-json.md similarity index 96% rename from platform/docs/versioned_docs/version-3.8/configuration/dataSources/dicom-json.md rename to platform/docs/versioned_docs/version-3.9/configuration/dataSources/dicom-json.md index c2b95a874..84e7be228 100644 --- a/platform/docs/versioned_docs/version-3.8/configuration/dataSources/dicom-json.md +++ b/platform/docs/versioned_docs/version-3.9/configuration/dataSources/dicom-json.md @@ -38,7 +38,7 @@ You could run it like this: node .scripts/dicom-json-generator.js '/path/to/study/folder' 'url/to/dicom/server/folder' 'json/output/file.json' ``` -Some modalities require additional metadata to be added to the JSON file. You can read more about the minimum amount of metadata required for the viewer to work [here](../../faq.md#what-are-the-list-of-required-metadata-for-the-ohif-viewer-to-work). We will handle this in the script. For example, the script will add the CodeSequences for SR in order to display the measurements in the viewer. +Some modalities require additional metadata to be added to the JSON file. You can read more about the minimum amount of metadata required for the viewer to work [here](../../faq/technical#what-are-the-list-of-required-metadata-for-the-ohif-viewer-to-work). We will handle this in the script. For example, the script will add the CodeSequences for SR in order to display the measurements in the viewer. ::: diff --git a/platform/docs/versioned_docs/version-3.8/configuration/dataSources/dicom-web-proxy.md b/platform/docs/versioned_docs/version-3.9/configuration/dataSources/dicom-web-proxy.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/configuration/dataSources/dicom-web-proxy.md rename to platform/docs/versioned_docs/version-3.9/configuration/dataSources/dicom-web-proxy.md diff --git a/platform/docs/versioned_docs/version-3.8/configuration/dataSources/dicom-web.md b/platform/docs/versioned_docs/version-3.9/configuration/dataSources/dicom-web.md similarity index 94% rename from platform/docs/versioned_docs/version-3.8/configuration/dataSources/dicom-web.md rename to platform/docs/versioned_docs/version-3.9/configuration/dataSources/dicom-web.md index 5548d3c39..d6fd1ec1c 100644 --- a/platform/docs/versioned_docs/version-3.8/configuration/dataSources/dicom-web.md +++ b/platform/docs/versioned_docs/version-3.9/configuration/dataSources/dicom-web.md @@ -65,11 +65,9 @@ yarn run orthanc:up _Upload your first Study:_ 1. Navigate to - [Orthanc's web interface](http://localhost:8042/app/explorer.html) at - `http://localhost:8042/app/explorer.html` in a web browser. -2. In the top right corner, click "Upload" -3. Click "Select files to upload..." and select one or more DICOM files -4. Click "Start the upload" + [Orthanc's web interface](http://localhost:8042/ui/app/index.html#/) at + `http://localhost:8042/ui/app/index.html#/` in a web browser. +2. In the left you can see the upload button where you can drag and drop your DICOM files #### Orthanc: Learn More @@ -104,13 +102,13 @@ is running the `dev:orthanc` script in our project's `package.json` (inside `platform/app`). That script is: ```js -cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker_nginx-orthanc.js webpack-dev-server --config .webpack/webpack.pwa.js -w +cross-env NODE_ENV=development PROXY_TARGET=/dicom-web PROXY_DOMAIN=http://localhost:8042 APP_CONFIG=config/docker-nginx-orthanc.js webpack-dev-server --config .webpack/webpack.pwa.js -w ``` - `cross-env` sets three environment variables - PROXY_TARGET: `/dicom-web` - PROXY_DOMAIN: `http://localhost:8042` - - APP_CONFIG: `config/docker_nginx-orthanc.js` + - APP_CONFIG: `config/docker-nginx-orthanc.js` - `webpack-dev-server` runs using the `.webpack/webpack.pwa.js` configuration file. It will watch for changes and update as we develop. @@ -169,6 +167,16 @@ then consider switching to using [DICOMweb instead](https://book.orthanc-server. ![toolbarModule-layout](../../assets/img/uploader.gif) +Don't forget to add the customization to the config as well + +```js +customizationService: { + dicomUploadComponent: + '@ohif/extension-cornerstone.customizationModule.cornerstoneDicomUploadComponent', +}, +``` + + #### `singlepart` A comma delimited string specifying which payloads the data source responds with as single part. Those not listed are considered multipart. Values that can be included here are `pdf`, `video`, `bulkdata`, `thumbnail` and `image`. @@ -177,8 +185,6 @@ For DICOM video and PDF it has been found that Orthanc delivers multipart, while To learn more about how you can configure the OHIF Viewer, check out our [Configuration Guide](../index.md). -### DICOM Upload -See the [`dicomUploadEnabled`](#dicomuploadenabled) data source configuration option. ### DICOM PDF See the [`singlepart`](#singlepart) data source configuration option. diff --git a/platform/docs/versioned_docs/version-3.8/configuration/dataSources/introduction.md b/platform/docs/versioned_docs/version-3.9/configuration/dataSources/introduction.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/configuration/dataSources/introduction.md rename to platform/docs/versioned_docs/version-3.9/configuration/dataSources/introduction.md diff --git a/platform/docs/versioned_docs/version-3.8/configuration/dataSources/static-files.md b/platform/docs/versioned_docs/version-3.9/configuration/dataSources/static-files.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/configuration/dataSources/static-files.md rename to platform/docs/versioned_docs/version-3.9/configuration/dataSources/static-files.md diff --git a/platform/docs/versioned_docs/version-3.9/configuration/tour-demo.gif b/platform/docs/versioned_docs/version-3.9/configuration/tour-demo.gif new file mode 100644 index 000000000..f351f759b Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/configuration/tour-demo.gif differ diff --git a/platform/docs/versioned_docs/version-3.9/configuration/tours.md b/platform/docs/versioned_docs/version-3.9/configuration/tours.md new file mode 100644 index 000000000..849c82f1b --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/configuration/tours.md @@ -0,0 +1,156 @@ +--- +sidebar_position: 3 +sidebar_label: Tours +--- + +# Configuring Tours in OHIF with Shepherd.js + +In OHIF, you can configure guided tours for users by leveraging [Shepherd.js](https://shepherdjs.dev/), a JavaScript library for building feature tours. This page explains how you can define and customize these tours within your app configuration file. + +## Overview + +Tours allow you to provide step-by-step guidance to users, explaining different features of your mode/extension or the viewer. Each tour is associated with a route and consists of several steps, each guiding the user through specific interactions in the viewer. + +### Adding a Tour to your Configuration + +Here’s an example of adding a tour to your configuration file: + +```javascript +window.config = { + tours: [ + { + id: 'basicViewerTour', + route: '/viewer', + steps: [ + { + id: 'scroll', + title: 'Scrolling Through Images', + text: 'You can scroll through the images using the mouse wheel or scrollbar.', + attachTo: { + element: '.viewport-element', + on: 'top', + }, + advanceOn: { + selector: '.cornerstone-viewport-element', + event: 'CORNERSTONE_TOOLS_MOUSE_WHEEL', + }, + }, + { + id: 'zoom', + title: 'Zooming In and Out', + text: 'You can zoom the images using the right click.', + attachTo: { + element: '.viewport-element', + on: 'left', + }, + advanceOn: { + selector: '.cornerstone-viewport-element', + event: 'CORNERSTONE_TOOLS_MOUSE_UP', + }, + }, + // Add more steps as needed + ], + tourOptions: { + useModalOverlay: true, + defaultStepOptions: { + buttons: [ + { + text: 'Skip all', + action() { + this.complete(); + }, + secondary: true, + }, + ], + }, + }, + }, + ], +}; +``` + +## Explanation of Parameters + +### `tours` Array + +Each item in the `tours` array defines a specific tour for a particular route. The object contains the following properties: + +- **`id`**: A unique identifier for the tour. This helps in tracking whether the tour has been shown. +- **`route`**: The route in the application where the tour is applicable. When the user navigates to this route, the tour can automatically trigger if it hasn't been shown before. +- **`steps`**: An array of steps that define the individual guide elements in the tour. Each step corresponds to a UI element and guides the user through interactions. +- **`tourOptions`**: An object that allows you to configure the overall behavior of the tour, such as using a modal overlay or defining default step options. + +### `steps` Array + +Each step defines a part of the tour. Here's a breakdown of the properties you can define: + +- **`id`**: A unique identifier for the step within the tour. +- **`title`**: The title of the step, which appears at the top of the tooltip for the step. +- **`text`**: The content or description of the step, explaining what the user needs to do or understand. +- **`attachTo`**: Specifies where the step should be attached in the DOM. It includes: + - `element`: A string selector or a DOM element that the step should attach to. + - `on`: Specifies the position of the tooltip relative to the element (e.g., 'top', 'left', 'bottom', 'right'). +- **`advanceOn`**: Defines an event that will automatically advance the tour to the next step. This is useful for actions like clicking a button or scrolling. + - `selector`: The CSS selector for the element that triggers the advance. + - `event`: The event name that advances the step, this can be a OHIF service event, or a cornerstone event, or any native JS event (e.g., 'click', 'CORNERSTONE_TOOLS_MOUSE_WHEEL'). +- **`beforeShowPromise`**: A function that returns a promise. When the promise resolves, the rest of the show logic for the step will execute. You can use this to ensure that the target element is ready before the step shows. + +### `tourOptions` + +The `tourOptions` object allows you to configure the overall behavior of the tour. Here's a breakdown of the available properties: + +- **`useModalOverlay`**: A boolean that, if set to `true`, places the tour steps above a darkened modal overlay. The overlay creates an opening around the target element so it can remain interactive. +- **`defaultStepOptions`**: Default options that apply to all steps in the tour. You can override these in individual steps. The following are some options available: + - `buttons`: An array of button objects that appear in the footer of each step. Each button can trigger actions like advancing the tour or skipping it. For example: + - **`text`**: The label text on the button. + - **`action`**: A function to execute when the button is clicked. You can advance the tour using `this.next()`, or complete it using `this.complete()`. + - **`secondary`**: A boolean that, when set to `true`, styles the button as secondary (often for actions like skipping). + +### `floatingUIOptions` + +You can define positioning options for the steps using **Floating UI** middleware. This helps control how the steps are positioned, especially near the browser edges. + +For example, you can ensure that the steps maintain a margin of 24px from the viewport edges by configuring `preventOverflow` middleware: + +```javascript +floatingUIOptions: { + middleware: [ + preventOverflow({ padding: 24 }), + flip(), // Allows the step to flip if it is overflowing + ] +} +``` + +### Shepherd.js Lifecycle Events + +Each step and tour can have lifecycle events like `show`, `hide`, `complete`, or `cancel`. These events allow you to hook into the tour’s lifecycle to perform actions when certain events are triggered. + +For example: + +```javascript +when: { + show() { + console.log('Step shown!'); + }, + hide() { + console.log('Step hidden.'); + } +} +``` + +## Customizing Your Tour + +Once you have a basic tour in place, you can extend it with more advanced features like custom scrolling behavior, dynamic elements, and event-based step advancement. For more details, check out the [Shepherd.js documentation](https://shepherdjs.dev/). + +## Licensing +All versions below 14.0 for Shepherd.JS is under the MIT license, if you wish to use any version above 14.0, you can visit the ShepherdJS website to learn about their pricing and plans [Shepherd.js](https://www.shepherdjs.dev/) + +[LICENSE](https://github.com/shipshapecode/shepherd?tab=License-1-ov-file#readme) + +## Demo + +![Tour Demo]() + +## Conclusion + +By leveraging **Shepherd.js**, you can provide users with interactive and informative guided tours of the viewer. This can greatly improve the user experience and help users understand how to use key features. diff --git a/platform/docs/versioned_docs/version-3.8/configuration/url.md b/platform/docs/versioned_docs/version-3.9/configuration/url.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/configuration/url.md rename to platform/docs/versioned_docs/version-3.9/configuration/url.md diff --git a/platform/docs/versioned_docs/version-3.8/conformance.md b/platform/docs/versioned_docs/version-3.9/conformance.md similarity index 92% rename from platform/docs/versioned_docs/version-3.8/conformance.md rename to platform/docs/versioned_docs/version-3.9/conformance.md index 62c9c40f8..d0ca8ba00 100644 --- a/platform/docs/versioned_docs/version-3.8/conformance.md +++ b/platform/docs/versioned_docs/version-3.9/conformance.md @@ -1,5 +1,5 @@ --- -sidebar_position: 10 +sidebar_position: 12 sidebar_label: DICOM Conformance Statement (NEW) title: DICOM Conformance Statement --- diff --git a/platform/docs/versioned_docs/version-3.8/deployment/_category_.json b/platform/docs/versioned_docs/version-3.9/deployment/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/deployment/_category_.json rename to platform/docs/versioned_docs/version-3.9/deployment/_category_.json diff --git a/platform/docs/versioned_docs/version-3.9/deployment/authorization.md b/platform/docs/versioned_docs/version-3.9/deployment/authorization.md new file mode 100644 index 000000000..b3589b1a9 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/deployment/authorization.md @@ -0,0 +1,95 @@ +--- +sidebar_position: 6 +sidebar_label: Auth +--- + +# Authorization and Authentication +The OHIF Viewer can be configured to work with authorization servers that support one or more of the OpenID-Connect authorization flows. The Viewer finds it's OpenID-Connect settings on the oidc configuration key. You can set these values in your configuration files. For instance you can take a look at our +`google.js` configuration file. + + +```js +oidc: [ + { + // ~ REQUIRED + authority: 'https://accounts.google.com', + client_id: '723928408739-k9k9r3i44j32rhu69vlnibipmmk9i57p.apps.googleusercontent.com', + redirect_uri: '/callback', + response_type: 'id_token token', + scope: 'email profile openid https://www.googleapis.com/auth/cloudplatformprojects.readonly https://www.googleapis.com/auth/cloud-healthcare', // email profile openid + // ~ OPTIONAL + post_logout_redirect_uri: '/logout-redirect.html', + revoke_uri: 'https://accounts.google.com/o/oauth2/revoke?token=', + automaticSilentRenew: true, + revokeAccessTokenOnSignout: true, + }, +], +``` + +You need to provide the following information: +- authority: The URL of the authorization server. +- client_id: The client id of your application (provided by the authorization server). +- redirect_uri: The callback URL of your application. +- response_type: The response type of the authorization flow (e.g. id_token token, [learn more about different flows](https://darutk.medium.com/diagrams-of-all-the-openid-connect-flows-6968e3990660)). +- scope: The scopes that your application needs to access +- post_logout_redirect_uri: The URL that the user will be redirected to after logout. +- revoke_uri: The URL that the user will be redirected to after logout. +- automaticSilentRenew: If true, the user will be automatically logged in after the token expires. +- revokeAccessTokenOnSignout: If true, the access token will be revoked on logout. + + + +## How it works +The Viewer uses the `userAuthenticationService` to set the OpenID-Connect settings. The `userAuthenticationService` is a singleton service that is responsible for authentication and authorization. It is initialized by the app and you can grab it +from the `servicesManager` + +```js +const userAuthenticationService = servicesManager.services.userAuthenticationService; +``` + +Then the userAuthenticationService will inject the token as Authorization header in the requests that are sent to the server (both metadata +and pixelData). + +## Token based authentication in URL +Sometimes (although not recommended), some servers like to send the token +in the query string. In this case, the viewer will automatically grab the token from the query string +and add it to the userAuthenticationService and remove it from the query string (to prevent it from being logged in the console +in future requests). + +and example would be + +```js +http://localhost:3000/viewer?StudyInstanceUIDs=1.2.3.4.5.6.6.7&token=e123125jsdfahsdf +``` + + + +## Implicit Flow vs Authorization Code Flow + +The Viewer supports both the Implicit Flow and the Authorization Code Flow. The Implicit Flow is the default currently, as it is easier to set up and use. However, you can opt for better security by using the Authorization Code Flow. To do so, add `useAuthorizationCodeFlow` to the configuration and change the `response_type` from `id_token token` to `code`. + +Read more about Implicit Flow vs Authorization Code Flow [here](https://documentation.openiddict.com/guides/choosing-the-right-flow.html#:~:text=The%20implicit%20flow%20is%20similar,when%20using%20response_mode%3Dform_post%20) and [here](https://medium.com/@alysachan830/the-basics-of-oauth-2-0-authorization-code-implicit-flow-state-and-pkce-ed95d3478e1c) + +```js +oidc: [ + { + authority: 'https://accounts.google.com', + client_id: '723928408739-k9k9r3i44j32rhu69vlnibipmmk9i57p.apps.googleusercontent.com', + redirect_uri: '/callback', + scope: 'email profile openid', + post_logout_redirect_uri: '/logout-redirect.html', + revoke_uri: 'https://accounts.google.com/o/oauth2/revoke?token=', + revokeAccessTokenOnSignout: true, + automaticSilentRenew: true, + // CHANGE THESE ***************************** + response_type: 'code', + useAuthorizationCodeFlow: true, + }, +], +``` + +In fact, since browsers are blocking third-party cookies, the Implicit Flow will cease functioning in the future (not specific to OHIF). Read more [here](https://support.okta.com/help/s/article/FAQ-How-Blocking-Third-Party-Cookies-Can-Potentially-Impact-Your-Okta-Environment?language=en_US). It is recommended to use the Authorization Code Flow and begin migrating to it. + +:::note +For the Authorization Code Flow, when authenticating against Google, you must add the `client_secret` to the configuration as well. Unfortunately, this seems to occur only with Google. +::: diff --git a/platform/docs/versioned_docs/version-3.8/deployment/build-for-production.md b/platform/docs/versioned_docs/version-3.9/deployment/build-for-production.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/deployment/build-for-production.md rename to platform/docs/versioned_docs/version-3.9/deployment/build-for-production.md diff --git a/platform/docs/versioned_docs/version-3.8/deployment/cors.md b/platform/docs/versioned_docs/version-3.9/deployment/cors.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/deployment/cors.md rename to platform/docs/versioned_docs/version-3.9/deployment/cors.md diff --git a/platform/docs/versioned_docs/version-3.8/deployment/custom-url-access.md b/platform/docs/versioned_docs/version-3.9/deployment/custom-url-access.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/deployment/custom-url-access.md rename to platform/docs/versioned_docs/version-3.9/deployment/custom-url-access.md diff --git a/platform/docs/versioned_docs/version-3.8/deployment/docker.md b/platform/docs/versioned_docs/version-3.9/deployment/docker.md similarity index 90% rename from platform/docs/versioned_docs/version-3.8/deployment/docker.md rename to platform/docs/versioned_docs/version-3.9/deployment/docker.md index 5a28b4440..1bc793867 100644 --- a/platform/docs/versioned_docs/version-3.8/deployment/docker.md +++ b/platform/docs/versioned_docs/version-3.9/deployment/docker.md @@ -58,7 +58,7 @@ Simply replace `latest` at the end of the command with any of the tags for a spe ### Configuring the `nginx` Listen Port -The Dockerfile and entry point use the `${PORT}` environment variable as the port that the `nginx` server uses to serve the web server. The default value for `${PORT}` is `80`. One way to set this environment variable is to use the `-e` switch when running the container with `docker run`. The block below gives an example where the listen port is set to `8080` and published on the host as `3000`. +The Dockerfile and entry point use the `{PORT}` environment variable as the port that the `nginx` server uses to serve the web server. The default value for `{PORT}` is `80`. One way to set this environment variable is to use the `-e` switch when running the container with `docker run`. The block below gives an example where the listen port is set to `8080` and published on the host as `3000`. ```sh docker run -d -e PORT=8080 -p 3000:8080/tcp --name ohif-viewer-container ohif-viewer-image @@ -83,13 +83,13 @@ docker run -d -p 3000:80/tcp -v /path/to/config/file.js:/usr/share/nginx/html/ap ::: #### Environment Variable -In certain scenarios, such as deploying the Docker container to Google Cloud, it might be convenient to specify the configuration file (contents) as an environment variable. That environment variable is `${APP_CONFIG}` and it can be set in the `docker run` command using the `-e` switch. +In certain scenarios, such as deploying the Docker container to Google Cloud, it might be convenient to specify the configuration file (contents) as an environment variable. That environment variable is `{APP_CONFIG}` and it can be set in the `docker run` command using the `-e` switch. :::tip It is important to stress here that the environment variable is the contents of the configuration file and NOT the path to the config file as is [typically specified](https://docs.ohif.org/configuration/configurationFiles#configuration-files) for development and build environments or for the [volume mounting method](#volume-mounting). ::: -Below the `cat` command is used to convert the configuration file to a string and its result set as the `${APP_CONFIG}` environment variable. +Below the `cat` command is used to convert the configuration file to a string and its result set as the `{APP_CONFIG}` environment variable. ```sh docker run -d -p 3000:80/tcp -e APP_CONFIG="$(cat /path/to/the/config/file)" --name ohif-viewer-container ohif-viewer-image @@ -100,7 +100,7 @@ To be safe, remove single line comments (i.e. `//`) from the configuration file ::: :::tip -As an alternative to the `cat` command, convert the configuration file to a single line and copy and paste it as the value to the `${APP_CONFIG}` environment variable on the `docker run` line. Editors such as [Visual Studio Code](https://stackoverflow.com/questions/46491061/shortcut-for-joining-two-lines) and [Notepad++](https://superuser.com/questions/518229/how-do-i-remove-linebreaks-in-notepad) have 'Join Lines' commands to facilitate this. +As an alternative to the `cat` command, convert the configuration file to a single line and copy and paste it as the value to the `{APP_CONFIG}` environment variable on the `docker run` line. Editors such as [Visual Studio Code](https://stackoverflow.com/questions/46491061/shortcut-for-joining-two-lines) and [Notepad++](https://superuser.com/questions/518229/how-do-i-remove-linebreaks-in-notepad) have 'Join Lines' commands to facilitate this. ::: :::tip @@ -139,7 +139,7 @@ Consideration must be given as to whether OHIF should be deployed in a secure co ### Specifying the SSL Port, Certificate and Private Key For convenience, the [built Docker image](#building-the-docker-image) can be run over SSL by -- setting the `${SSL_PORT}` environment variable +- setting the `{SSL_PORT}` environment variable - volume mounting the SSL certificate - volume mounting the SSL private key @@ -147,7 +147,7 @@ For convenience, the [built Docker image](#building-the-docker-image) can be run The volume mounted SSL certificate and private key are mapped to the [`ssl_certificate`](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate) and [`ssl_certificate_key`](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_key) `nginx` directives respectively. ::: -Similar to the [`nginx` listen port](#configuring-the-nginx-listen-port), the `${SSL_PORT}` environment variable is the internal port that `nginx` listens on to serve the OHIF web server over SSL and has to be likewise published via the `-p` switch. +Similar to the [`nginx` listen port](#configuring-the-nginx-listen-port), the `{SSL_PORT}` environment variable is the internal port that `nginx` listens on to serve the OHIF web server over SSL and has to be likewise published via the `-p` switch. The following is an example command running the Docker container over SSL. Note that depending on the version of Docker, an absolute path to the certificate and private key files might be required. @@ -164,7 +164,7 @@ The private key is a secure entity and should have restricted access. Keep it sa ::: :::caution -The presence of the `${SSL_PORT}` environment variable is used to trigger to deploy over SSL as opposed to HTTP. If `${SSL_PORT}` is NOT defined, then HTTP is used even if the certificate and private key volumes are mounted. +The presence of the `{SSL_PORT}` environment variable is used to trigger to deploy over SSL as opposed to HTTP. If `{SSL_PORT}` is NOT defined, then HTTP is used even if the certificate and private key volumes are mounted. ::: :::tip diff --git a/platform/docs/versioned_docs/version-3.8/deployment/google-cloud-healthcare.md b/platform/docs/versioned_docs/version-3.9/deployment/google-cloud-healthcare.md similarity index 99% rename from platform/docs/versioned_docs/version-3.8/deployment/google-cloud-healthcare.md rename to platform/docs/versioned_docs/version-3.9/deployment/google-cloud-healthcare.md index 73b378554..54df8bb02 100644 --- a/platform/docs/versioned_docs/version-3.8/deployment/google-cloud-healthcare.md +++ b/platform/docs/versioned_docs/version-3.9/deployment/google-cloud-healthcare.md @@ -1,5 +1,5 @@ --- -sidebar_position: 10 +sidebar_position: 9 --- # Google Cloud Healthcare diff --git a/platform/docs/versioned_docs/version-3.8/deployment/iframe.md b/platform/docs/versioned_docs/version-3.9/deployment/iframe.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/deployment/iframe.md rename to platform/docs/versioned_docs/version-3.9/deployment/iframe.md diff --git a/platform/docs/versioned_docs/version-3.8/deployment/index.md b/platform/docs/versioned_docs/version-3.9/deployment/index.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/deployment/index.md rename to platform/docs/versioned_docs/version-3.9/deployment/index.md diff --git a/platform/docs/versioned_docs/version-3.8/deployment/nginx--image-archive.md b/platform/docs/versioned_docs/version-3.9/deployment/nginx--image-archive.md similarity index 53% rename from platform/docs/versioned_docs/version-3.8/deployment/nginx--image-archive.md rename to platform/docs/versioned_docs/version-3.9/deployment/nginx--image-archive.md index 8d8658798..b1a2fd34b 100644 --- a/platform/docs/versioned_docs/version-3.8/deployment/nginx--image-archive.md +++ b/platform/docs/versioned_docs/version-3.9/deployment/nginx--image-archive.md @@ -1,11 +1,9 @@ --- -sidebar_position: 9 +sidebar_position: 10 --- # Nginx + Image Archive -> DISCLAIMER! We make no claims or guarantees of this approach's security. If in -> doubt, enlist the help of an expert and conduct proper audits. At a certain point, you may want others to have access to your instance of the OHIF Viewer and its medical imaging data. This post covers one of many potential @@ -16,19 +14,14 @@ Do not use this recipe to host sensitive medical data on the open web. Depending on your company's policies, this may be an appropriate setup on an internal network when protected with a server's basic authentication. -## Overview -Our two biggest hurdles when hosting our image archive and web client are: - -- Risks related to exposing our PACS to the network -- Cross-Origin Resource Sharing (CORS) requests ### Handling Web Requests We mitigate our first issue by allowing [Nginx][nginx] to handle incoming web requests. Nginx is open source software for web serving, reverse proxying, caching, and more. It's designed for maximum performance and stability -- -allowing us to more reliably serve content than Orthanc's built-in server can. +allowing us to more reliably serve content. More specifically, we accomplish this by using a [`reverse proxy`](https://en.wikipedia.org/wiki/Reverse_proxy) to retrieve @@ -38,35 +31,16 @@ resources from our image archive (Orthanc), and when accessing its web admin. > of a client from one or more servers. These resources are then returned to the > client, appearing as if they originated from the proxy server itself. -### CORS Issues -Cross-Origin Resource Sharing (CORS) is a mechanism that uses HTTP headers to -tell a browser which web applications have permission to access selected -resources from a server at a different origin (domain, protocol, port). IE. By -default, a Web App located at `http://my-website.com` can't access resources -hosted at `http://not-my-website.com` - -We can solve this one of two ways: - -1. Have our Image Archive located at the same domain as our Web App -2. Add appropriate `Access-Control-Allow-*` HTTP headers - -**This solution uses the first approach.** - -You can read more about CORS in this Medium article: [Understanding -CORS][understanding-cors] - -### Diagram This setup allows us to create a setup similar to the one pictured below: ![nginX](../assets/img/nginx-image-archive.png) - -- All web requests are routed through `nginx` on our `OpenResty` image -- `/pacs` is a reverse proxy for `orthanc`'s `DICOM Web` endpoints -- `/pacs-admin` is a reverse proxy for `orthanc`'s Web Admin +- All web requests are routed through `nginx` image +- `/pacs/dicom-web` is a reverse proxy for `orthanc`'s `DICOM Web` endpoints, which handles DICOM requests +- `/pacs` is a reverse proxy for `orthanc`'s Web Admin, which is the UI for managing studies - All static resources for OHIF Viewer are served up by `nginx` when a matching route for that resource is requested @@ -83,36 +57,40 @@ in command prompt or terminal_ ### Setup -- Navigate to `app` folder inside `platform` -- then: `cd .recipes/OpenResty-Orthanc` +- `cd platform/app/.recipes/Nginx-Orthanc` - run: `docker-compose up --build` -- Navigate to `127.0.0.1` for the viewer -- Navigate to `127.0.0.1/pacs-admin` for uploading studies via the UI, or send studies via DIMSE C-STORE to `ORTHANC@127.0.0.1:4242` (hint: you can use utilites like dcm4che's `storescu` to send studies in bulk via the command line) +- Navigate to `127.0.0.1` for the viewer (at first there is no study) +- Navigate to `127.0.0.1/pacs` for uploading studies via the UI, or send studies via DIMSE C-STORE to `ORTHANC@127.0.0.1:4242` (hint: you can use utilizes like dcm4che's `storescu` to send studies in bulk via the command line) +:::note +For subsequent runs, use `docker-compose up -d` to start the services without rebuilding the images. However, ensure you rebuild the images if you make changes to the Dockerfile. If you modify the configurations in the `nginx.conf` or `orthanc.json` files, you can restart the services by running `docker-compose up`, as these files are mounted as volumes. + +``` +Inside docker compose file you see the following volumes mounted: + +volumes: + # Nginx config + - ./config/nginx.conf:/etc/nginx/nginx.conf + # Logs + - ./logs/nginx:/var/logs/nginx +``` +::: You can see the overview of the mentioned steps: +:::info +The following video demonstrates an outdated capture using the deprecated `OpenResty-Orthanc` recipe. However, it still provides insight into the steps for running the viewer with Orthanc. Use the new `Nginx-Orthanc` recipe for the most up-to-date instructions. +::: + + +
- ### Troubleshooting @@ -154,10 +132,10 @@ configuration we use is set to a specific file when we build the viewer, and determined by the env variable: `APP_CONFIG`. You can see where we set its value in the `dockerfile` for this solution: -`ENV APP_CONFIG=config/docker_openresty-orthanc.js` +`ENV APP_CONFIG=config/docker-nginx-orthanc.js` You can find the configuration we're using here: -`/public/config/docker_openresty-orthanc.js` +`/public/config/docker-nginx-orthanc.js` To rebuild the `webapp` image created by our `dockerfile` after updating the Viewer's configuration, you can run: @@ -167,78 +145,49 @@ Viewer's configuration, you can run: #### Other -All other files are found in: `/docker/OpenResty-Orthanc/` +All other files are found in: `/docker/Nginx-Orthanc/` | Service | Configuration | Docs | | ----------------- | --------------------------------- | ------------------------------------------- | | OHIF Viewer | [dockerfile][dockerfile] | You're reading them now! | -| OpenResty (Nginx) | [`/nginx.conf`][config-nginx] | [lua-resty-openidc][lua-resty-openidc-docs] | +| Nginx | [`/nginx.conf`][config-nginx] | | | Orthanc | [`/orthanc.json`][config-orthanc] | [Here][orthanc-docs] | ## Next Steps +### OHIF + Dcm4chee + +You can follow the similar steps above to run OHIF Viewer with Dcm4chee PACS. + +The recipe for this setup can be found at `platform/app/.recipes/Nginx-Dcm4chee`. + + +The routes are as follows: +- `127.0.0.1` for the OHIF viewer +- `127.0.0.1/pacs` for the Dcm4chee UI + +:::info +For uploading studies, you can see the following gif for the steps: + +![alt text](../assets/img/dcm4chee-upload.gif) + +::: + ### Deploying to Production -While these configuration and docker-compose files model an environment suitable -for production, they are not easy to deploy "as is". You can either: +While you can deploy this solution to production, there is one main caveat: every user can access the app and the patient portal without any authentication. In the next step, we will add authentication with Keycloak to secure the app. -- Manually recreate this environment and deploy built application files **OR** -- Deploy to a cloud kubernetes provider like - [Digital Ocean](https://www.digitalocean.com/products/kubernetes/) **OR** - - [See a full list of cloud providers here](https://landscape.cncf.io/category=cloud&format=card-mode&grouping=category) -- Find and follow your preferred provider's guide on setting up - [swarms and stacks](https://docs.docker.com/get-started/) -### Adding SSL -Adding SSL registration and renewal for your domain with Let's Encrypt that -terminates at Nginx is an incredibly important step toward securing your data. -Here are some resources, specific to this setup, that may be helpful: - -- [lua-resty-auto-ssl](https://github.com/GUI/lua-resty-auto-ssl) -- [Let's Encrypt + Nginx](https://www.nginx.com/blog/using-free-ssltls-certificates-from-lets-encrypt-with-nginx/) - -While we terminate SSL at Nginx, it may be worth using self-signed certificates -for communication between services. - -- [SSL Termination for TCP Upstream Servers](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-tcp/) - -### Use PostgresSQL w/ Orthanc - -Orthanc can handle a large amount of data and requests, but if you find that -requests start to slow as you add more and more studies, you may want to -configure your Orthanc instance to use PostgresSQL. Instructions on how to do -that can be found in the -[`Orthanc Server Book`](http://book.orthanc-server.com/users/docker.html), under -"PostgreSQL and Orthanc inside Docker" ### Improving This Guide Here are some improvements this guide would benefit from, and that we would be more than happy to accept Pull Requests for: -- SSL Support -- Complete configuration with `.env` file (or something similar) -- Any security issues -- One-click deploy to a cloud provider +- Add Docker caching for faster builds -## Resources -### Misc. Helpful Commands - -_Check if `nginx.conf` is valid:_ - -```bash -docker run --rm -t -a stdout --name my-openresty -v $PWD/config/:/usr/local/openresty/nginx/conf/:ro openresty/openresty:alpine-fat openresty -c /usr/local/openresty/nginx/conf/nginx.conf -t -``` - -_Interact w/ running container:_ - -`docker exec -it CONTAINER_NAME bash` - -_List running containers:_ - -`docker ps` ### Referenced Articles @@ -246,8 +195,6 @@ For more documentation on the software we've chosen to use, you may find the following resources helpful: - [Orthanc for Docker](http://book.orthanc-server.com/users/docker.html) -- [OpenResty Guide](http://www.staticshin.com/programming/definitely-an-open-resty-guide/) -- [Lua Ngx API](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/) For a different take on this setup, check out the repositories our community members put together: diff --git a/platform/docs/versioned_docs/version-3.8/deployment/static-assets.md b/platform/docs/versioned_docs/version-3.9/deployment/static-assets.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/deployment/static-assets.md rename to platform/docs/versioned_docs/version-3.9/deployment/static-assets.md diff --git a/platform/docs/versioned_docs/version-3.9/deployment/user-account-control.md b/platform/docs/versioned_docs/version-3.9/deployment/user-account-control.md new file mode 100644 index 000000000..87f0235be --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/deployment/user-account-control.md @@ -0,0 +1,524 @@ +--- +sidebar_position: 11 +--- +# User Account Control + + +:::danger +DISCLAIMER: We make no claims or guarantees regarding the security of this approach. If you have any doubts, please consult an expert and conduct thorough audits. +::: + +Making a viewer and its medical imaging data accessible on the open web can +provide a lot of benefits, but requires additional security to make sure +sensitive information can only be viewed by authorized individuals. Most image +archives are equipped with basic security measures, but they are not +robust/secure enough for the open web. + +This guide covers one of many potential production setups that secure our +sensitive data. + +## Overview + +This guide builds on top of our +[Nginx + Image Archive guide](./nginx--image-archive.md), +wherein we used a [`reverse proxy`](https://en.wikipedia.org/wiki/Reverse_proxy) +to retrieve resources from our image archive (Orthanc). + +To add support for "User Account Control" we introduce +[Keycloak](https://www.keycloak.org/about.html). Keycloak is an open source +Identity and Access Management solution that makes it easy to secure +applications and services with little to no code. We improve upon our +`reverse proxy` setup by integrating Keycloak and Nginx to create an +`authenticating reverse proxy`. + +> An authenticating reverse proxy is a reverse proxy that only retrieves the +> resources on behalf of a client if the client has been authenticated. If a +> client is not authenticated they can be redirected to a login page. + +This setup allows us to create a setup similar to the one pictured below: + +![userControlFlow](../assets/img/ohif-pacs-keycloak.png) + + + +**Nginx:** + +- Acts as a reverse proxy server that handles incoming requests to the domain (mydomain.com:80) and forwards them to the appropriate backend services. +- It also ensures that all requests go through the OAuth2 Proxy for authentication. + + +**OAuth2 Proxy:** + +- Serves as an intermediary that authenticates users via OAuth2. +- Works in conjunction with Keycloak to manage user sessions and authentication tokens. +- Once the user is authenticated, it allows access to specific routes (/ohif-viewer, /pacs, /pacs-admin). + +**Keycloak:** + +- An open-source identity and access management solution. +- Manages user identities, including authentication and authorization. +- Communicates with the OAuth2 Proxy to validate user credentials and provide tokens for authenticated sessions. + +**OHIF Viewer:** + +- Hosted under the route /ohif-viewer, which serves the static assets of the OHIF Viewer. + +**Orthanc/DCM4chee:** + +- PACS (Picture Archiving and Communication System) for managing medical imaging data. +Exposes two routes: +- /pacs: Accesses the DICOM web services. +- /pacs-admin: Provides administrative and explorer interfaces. + + + +## Getting Started - Orthanc + + +### Requirements + +- Docker + - [Docker for Mac](https://docs.docker.com/docker-for-mac/) + - [Docker for Windows](https://docs.docker.com/docker-for-windows/) + +_Not sure if you have `docker` installed already? Try running `docker --version` +in command prompt or terminal_ + +### Setup 1 - Trying Locally + +Navigate to the Orthanc Keycloak configuration directory: + +`cd platform\app\.recipes\Nginx-Orthanc-Keycloak` + +Due to the increased complexity of this setup, we've introduced a magic word `YOUR_DOMAIN`. Replace this word with your project IP address to follow along more easily. + +Since we are running this locally, we will use `127.0.0.1` as our IP address. + +In the `docker-compose.yml` file, replace `YOUR_DOMAIN` with `127.0.0.1`. + +In the Keycloak service: + + +Before: + +``` +KC_HOSTNAME_ADMIN_URL: http://YOUR_DOMAIN/keycloak/ +KC_HOSTNAME_URL: http://YOUR_DOMAIN/keycloak/ +``` + + +After + +``` +KC_HOSTNAME_ADMIN_URL: http://127.0.0.1/keycloak/ +KC_HOSTNAME_URL: http://127.0.0.1/keycloak/ +``` + +In the Keycloak healthcheck, replace `YOUR_DOMAIN` with `localhost`. + +In the Nginx config, change: + +``` +server_name YOUR_DOMAIN; +``` + +to: + +``` +server_name 127.0.0.1; +``` + +Since we're not using SSL, remove the following lines from the Nginx config file and create one server instead of two: + +Before (two servers one for http and one for https): + +``` +server { + listen 80; + server_name YOUR_DOMAIN; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + server_name YOUR_DOMAIN; + + ssl_certificate /etc/letsencrypt/live/ohifviewer.duckdns.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/ohifviewer.duckdns.org/privkey.pem; + + root /var/www/html; +``` + +After (merging both servers into one only http server): + +``` +server { + listen 80; + server_name 127.0.0.1; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + root /var/www/html; +``` + +In OAuth2-proxy configuration at `oauth2-proxy.cfg` + +Before: + +``` +redirect_url="http://YOUR_DOMAIN/oauth2/callback" +oidc_issuer_url="http://YOUR_DOMAIN/keycloak/realms/ohif" +``` + +After: + +``` +redirect_url="http://127.0.0.1/oauth2/callback" +oidc_issuer_url="http://127.0.0.1/keycloak/realms/ohif" +``` + +Finally, in the docker-nginx-orthanc-keycloak config file that lives in `platform/app/public/config/docker-nginx-orthanc-keycloak.js`, replace `YOUR_DOMAIN` with + +Before: + +``` +wadoUriRoot: 'http://YOUR_DOMAIN/pacs', +qidoRoot: 'http://YOUR_DOMAIN/pacs', +wadoRoot: 'http://YOUR_DOMAIN/pacs', +``` + +After: + +``` +wadoUriRoot: 'http://127.0.0.1/pacs', +qidoRoot: 'http://127.0.0.1/pacs', +wadoRoot: 'http://127.0.0.1/pacs', +``` + +:::note +This is the config that is used inside the dockerfile to build the viewer, look at dockerfile + +`ENV APP_CONFIG=config/docker-nginx-orthanc-keycloak.js` +::: + +Run the following command to start the services: + +``` +docker-compose up --build +``` + + +You can watch the following video, which will guide you through the process of setting up Orthanc with keycloak and OHIF locally. + +We have set up two predefined users in Keycloak: + +- `user: admin password: admin` - Has access to keycloak portal for managing users and clients +- `user: viewer password: viewer` - Has access to the OHIF Viewer but not the pacs-admin +- `user: pacsadmin password: pacsadmin` - Has access to both the pacs-admin for uploading and the OHIF Viewer + +You can navigate to: + +- `http://127.0.0.1` - This will redirect you to `http://127.0.0.1/ohif-viewer`, prompting you to log in with Keycloak using either user +- `http://127.0.0.1/pacs-admin` - Only the `pacsadmin` user can access this route, while the `viewer` user cannot +- + +
+ +
+ + +### Step 2 - Trying via a Server + +Now that you have successfully set up Orthanc with Keycloak and OHIF locally, you can deploy it to a server. While you can rent a server from any provider, this tutorial will demonstrate the process using Linode as an example. + +You can watch the following video, which will guide you through the process. + +Some notes: + +- Since this is a remote machine we need to clone the repo +- Typically a Linux machine, you need to download and install Docker on it +- Use the Visual Studio Code Remote SSH extension to connect to the server +- Use docker extension in Visual Studio Code to manage the containers +- The public IP address of the server now becomes the YOUR_DOMAIN and is used in the configuration files. + +Still we have not set up SSL, so we will use HTTP instead of HTTPS. + +We should use the same one server configuration as we did locally for Nginx (but with the new server IP address) + +:::info +Don't forget to change the `docker-ngix-orthanc-keycloak.js` file to use the new server IP address. +::: + +After you run `docker compose up --build` you can navigate to the server IP address and see the viewer will not work... + +We have encountered some strange issues with the Keycloak service not allowing non-HTTPS connections (around 10:00). To resolve this, we need to modify the Keycloak configuration to permit HTTPS. This requires accessing the container and making the necessary changes. + +After accessing the container shell + +``` +cd /opt/keycloak/bin + +./kcadm.sh config credentials --server http://localhost:8080 --realm master --user admin +./kcadm.sh update realms/master -s sslRequired=NONE +``` + +After we need to change some configurations in the Keycloak UI to enable the connection in the server + +Navigate to + +``` +http://IP_ADDRESS/keycloak +``` + +which will redirect you to the Keycloak login page + +0. login with the admin user `admin` and password `admin` +1. From the top left drop down menu, select `ohif` realm +2. Go to `Clients` and select `ohif_viewer` +3. In the `Access Settings` change all instances of `http://127.0.0.1` to `http://IP_ADDRESS` + 1. Root URL: `http://IP_ADDRESS` + 2. Home URL: `http://IP_ADDRESS` + 3. Valid Redirect URIs: `http://IP_ADDRESS/oauth2/callback` + 4. Valid post logout URIs: `*` + 5. Web Origins: `http://IP_ADDRESS` + 6. Admin URL: `http://IP_ADDRESS` + +Now if you navigate to the IP address it should work !! + + +
+ +
+ +### Step 3 - Adding SSL and Deploying to Production + +Now we'll add an SSL certificate to our server to enable HTTPS. We'll use Let's Encrypt to generate the SSL certificate. + +Let's Encrypt requires a domain name, so we'll use a free domain name service like DuckDNS (duckdns.org). Follow these steps: + +1. Visit https://www.duckdns.org/ and create an account. +2. Create a free domain name and point it to your server's IP address. + +You can watch a video guide for this process if needed. + +Replace `YOUR_DOMAIN` with your new domain name in the `docker-compose.yml` file and all other config files, as we did previously. + +Next, we'll add HTTPS support. Add the following lines to the Nginx config file: + +(Note: We'll have both HTTP and HTTPS servers, and the server IP will use HTTPS) +``` +server { + listen 80; + server_name https://IP_ADDRESS; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + server_name https://IP_ADDRESS; + + ssl_certificate /etc/letsencrypt/live/ohifviewer.duckdns.org/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/ohifviewer.duckdns.org/privkey.pem; + + root /var/www/html; +``` + +Don't forget to replace `YOUR_DOMAIN` with the new domain name in the `docker-nginx-orthanc-keycloak.js` file. + +:::info +Remember to include `https://` when adding the domain name to the configurations. +::: + +Now, we need to add a certificate. Let's assume we have the domain name `hospital.duckdns.org` and the email we registered with DuckDNS is `your_email@example.com`. + +``` + docker run -it --rm --name certbot \ + -v ./config/letsencrypt:/etc/letsencrypt \ + -v ./config/certbot:/var/www/certbot \ + -p 80:80 \ + certbot/certbot certonly \ + --standalone \ + --preferred-challenges http \ + --email your_email@example.com \ + --agree-tos \ + --no-eff-email \ + -d hospital.duckdns.org +``` + +:::note +Replace "hospital.duckdns.org" with your domain name and update the email address accordingly. +::: + +:::warning +DuckDNS is suitable for testing and demonstration purposes only. For production environments, use a proper domain name and SSL certificate to ensure security. +::: + +If you follow these steps, you'll encounter the error `invalid parameter: redirect_uri` when attempting to log in to Keycloak. This occurs because the redirect URL isn't set up correctly in the Keycloak client configuration. To resolve this, we need to log in and adjust these settings. + +Navigate to: + +``` +http://IP_ADDRESS/keycloak +``` + +Log in using the admin credentials: +- Username: `admin` +- Password: `admin` + +Replace all IP addresses with the new domain name, using HTTPS. + +
+ +
+ + + + + + +## Getting Started - DCM4CHEE + + + + +You can follow the same steps as above to set up DCM4CHEE. The only difference is that you need to navigate to the correct directory. `platform\app\.recipes\Nginx-Dcm4chee-Keycloak` + +You can watch the following video, which will guide you through the process of setting up DCM4CHEE. + + +
+ +
+ + + +## Troubleshooting + + +_invalid parameter: redirect_uri_ + +This means the redirect URL isn't set up correctly in the Keycloak client configuration. To resolve this, log in to Keycloak and adjust the settings in the correct client (ohif_viewer) and correct realm (ohif). + +_Exit code 137_ + +This means Docker ran out of memory. Open Docker Desktop, go to the `advanced` +tab, and increase the amount of Memory available. + +_Cannot create container for service X_ + +Use this one with caution: `docker system prune` + +_X is already running_ + +Stop running all containers: + +- Win: `docker ps -a -q | ForEach { docker stop $_ }` +- Linux: `docker stop $(docker ps -a -q)` + + +#### OHIF Viewer + +The OHIF Viewer's configuration is imported from a static `.js` file. The +configuration we use is set to a specific file when we build the viewer, and +determined by the env variable: `APP_CONFIG`. You can see where we set its value +in the `dockerfile` for this solution: + +`ENV APP_CONFIG=config/docker-nginx-orthanc-keycloak.js` + +You can find the configuration we're using here: +`/public/config/docker-nginx-orthanc-keycloak.js` + +To rebuild the `webapp` image created by our `dockerfile` after updating the +Viewer's configuration, you can run: + +- `docker-compose build` OR +- `docker-compose up --build` + + + +## Next Steps + +### Keycloak Theming + +The `Login` screen for the `ohif-viewer` client is using a Custom Keycloak +theme. You can find the source files for it in +`platform/app/.recipes/deprecated-recipes/OpenResty-Orthanc-Keycloak/volumes/keycloak-themes`. You can see how +we add it to Keycloak in the `docker-compose` file, and you can read up on how +to leverage custom themes in +[Keycloak's own docs](https://www.keycloak.org/docs/latest/server_development/index.html#_themes). + +| Default Theme | OHIF Theme | +| ---------------------------------------------------------------------- | ---------------------------------------------------------------- | +| ![Keycloak Default Theme](../assets/img/keycloak-default-theme.png) | ![Keycloak OHIF Theme](../assets/img/keycloak-ohif-theme.png) | + + + + + +## Resources + +### Referenced Articles + +The inspiration for our setup was driven largely by these articles: + +- [Securing Nginx with Keycloak](https://edhull.co.uk/blog/2018-06-06/keycloak-nginx) +- [Authenticating Reverse Proxy with Keycloak](https://eclipsesource.com/blogs/2018/01/11/authenticating-reverse-proxy-with-keycloak/) +- [Securing APIs with Kong and Keycloak](https://www.jerney.io/secure-apis-kong-keycloak-1/) + +For more documentation on the software we've chosen to use, you may find the +following resources helpful: + +- [Orthanc for Docker](http://book.orthanc-server.com/users/docker.html) +- [OpenResty Guide](http://www.staticshin.com/programming/definitely-an-open-resty-guide/) +- [Lua Ngx API](https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/) +- [Auth0: Picking a Grant Type](https://auth0.com/docs/api-auth/which-oauth-flow-to-use) + +We chose to use a generic OpenID Connect library on the client, but it's worth +noting that Keycloak comes packaged with its own: + +- [oidc-client-js](https://github.com/IdentityModel/oidc-client-js/wiki) +- [Keycloak JavaScript Adapter](https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter) + +If you're not already drowning in links, here are some good security resources +for OAuth: + +- [Diagrams of OpenID Connect Flows](https://medium.com/@darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660) +- [KeyCloak: OpenID Connect Flows](https://www.keycloak.org/docs/latest/securing_apps/index.html#authorization-code) + +For a different take on this setup, check out the repositories our community +members put together: + +- [mjstealey/ohif-orthanc-dimse-docker](https://github.com/mjstealey/ohif-orthanc-dimse-docker) +- [trypag/ohif-orthanc-postgres-docker](https://github.com/trypag/ohif-orthanc-postgres-docker) + + + + + +[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration +[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc + +[config]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/src/config.js +[dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/dockerfile +[config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/nginx.conf +[config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/orthanc.json +[config-keycloak]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc-Keycloak/config/ohif-keycloak-realm.json + diff --git a/platform/docs/versioned_docs/version-3.8/development/_category_.json b/platform/docs/versioned_docs/version-3.9/development/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/development/_category_.json rename to platform/docs/versioned_docs/version-3.9/development/_category_.json diff --git a/platform/docs/versioned_docs/version-3.9/development/android-ios-debugging.md b/platform/docs/versioned_docs/version-3.9/development/android-ios-debugging.md new file mode 100644 index 000000000..67c42734a --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/development/android-ios-debugging.md @@ -0,0 +1,80 @@ +--- +sidebar_position: 12 +sidebar_label: Android & iOS Debugging +--- + +# Android & iOS Debugging for OHIF using Emulators + +This guide covers how to debug the OHIF viewer on Android and iOS emulators using Chrome DevTools and Safari Web Inspector, respectively. You can use these tools to inspect elements, debug JavaScript, and view console logs for the web content running on the emulators. + +## Android Emulator Setup with Android Studio + +### Prerequisites: +- Install [Android Studio](https://developer.android.com/studio) +- Ensure you have a recent Android SDK and Emulator installed via Android Studio +- Google Chrome installed on your machine + +### Steps to Run Android Emulator: + +1. **Launch Android Studio:** + - Open Android Studio and create a new project if you don't already have one. + - Once your IDE opens up, click on the **Device Manager** icon in the right-side toolbar. + +2. **Create a Virtual Device (if necessary):** + - If you don’t have an existing virtual device, click **Create Virtual Device**. + - Choose a device model (e.g., Pixel series) and click **Next**. + - Select a system image with the required Android API version and click **Next**. + - Finish the setup by clicking **Finish**. + +3. **Start the Android Emulator:** + - Once the device is created, click the **Play** button next to the virtual device to start the emulator. + +4. **Open a Browser on the Emulator:** + - Once the emulator is running, open the **Chrome** app on the virtual device. + - Navigate to the OHIF Viewer URL to view the application. The URL will be 10.0.2.2:3000, you can read more about it [here](https://developer.android.com/studio/run/emulator-networking). + +5. **Debug Using Chrome DevTools:** + - On your development machine, open Google Chrome. + - Type `chrome://inspect` in the Chrome address bar and hit **Enter**. + - You will see your Android device listed under **Remote Target**. + - Click **Inspect** to open DevTools for the browser on the Android emulator. + +6. **Happy Debugging!:** + - You can now use Chrome DevTools to inspect elements, debug JavaScript, and view console logs directly from the emulator’s browser. + +### Video Tutorial + + + +--- + +## iOS Emulator Setup with Xcode + +### Prerequisites: +- Install [Xcode](https://developer.apple.com/xcode/) from the Mac App Store. +- Ensure you have the latest iOS SDK. + +### Steps to Run iOS Emulator: + +1. **Launch Xcode:** + - Open Xcode and navigate to **Xcode > Settings**. + - Go to the **Platform** tab and ensure you have an iOS simulator installed for the version of iOS you need. If not you can do so using the + button. + +2. **Start the iOS Simulator:** + - Open Xcode and navigate to **Xcode > Open Developer Tools > Simulator**. + - Select your device from the list of available simulators and click on it. + +3. **Open a Browser on the Simulator:** + - Run the **Safari** browser + +4. **Connect Safari DevTools to the iOS Simulator:** + - On your development machine, open **Safari** on your Mac. + - Click **Develop** in the menu bar and select your simulator under **Devices**. + - You will see the web pages open on the iOS simulator. Select the page to open the inspector. + +5. **Happy Debugging!:** + - You can now use the Safari Web Inspector to inspect elements, debug JavaScript, and view logs for the OHIF Viewer on the iOS simulator. + +### Video Tutorial + + diff --git a/platform/docs/versioned_docs/version-3.8/development/architecture.md b/platform/docs/versioned_docs/version-3.9/development/architecture.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/development/architecture.md rename to platform/docs/versioned_docs/version-3.9/development/architecture.md diff --git a/platform/docs/versioned_docs/version-3.8/development/continuous-integration.md b/platform/docs/versioned_docs/version-3.9/development/continuous-integration.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/development/continuous-integration.md rename to platform/docs/versioned_docs/version-3.9/development/continuous-integration.md diff --git a/platform/docs/versioned_docs/version-3.8/development/contributing.md b/platform/docs/versioned_docs/version-3.9/development/contributing.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/development/contributing.md rename to platform/docs/versioned_docs/version-3.9/development/contributing.md diff --git a/platform/docs/versioned_docs/version-3.8/development/getting-started.md b/platform/docs/versioned_docs/version-3.9/development/getting-started.md similarity index 98% rename from platform/docs/versioned_docs/version-3.8/development/getting-started.md rename to platform/docs/versioned_docs/version-3.9/development/getting-started.md index d170e83fa..32214b66f 100644 --- a/platform/docs/versioned_docs/version-3.8/development/getting-started.md +++ b/platform/docs/versioned_docs/version-3.9/development/getting-started.md @@ -60,6 +60,7 @@ Each package is tagged with version numbers and published to npm, such as `@ohif Here is a schematic representation of our development workflow: +![alt text](../assets/img/github-readme-branches-Jun2024.png) diff --git a/platform/docs/versioned_docs/version-3.8/development/link.md b/platform/docs/versioned_docs/version-3.9/development/link.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/development/link.md rename to platform/docs/versioned_docs/version-3.9/development/link.md diff --git a/platform/docs/versioned_docs/version-3.8/development/ohif-cli.md b/platform/docs/versioned_docs/version-3.9/development/ohif-cli.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/development/ohif-cli.md rename to platform/docs/versioned_docs/version-3.9/development/ohif-cli.md diff --git a/platform/docs/versioned_docs/version-3.8/development/our-process.md b/platform/docs/versioned_docs/version-3.9/development/our-process.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/development/our-process.md rename to platform/docs/versioned_docs/version-3.9/development/our-process.md diff --git a/platform/docs/versioned_docs/version-3.9/development/playwright-testing.md b/platform/docs/versioned_docs/version-3.9/development/playwright-testing.md new file mode 100644 index 000000000..679872399 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/development/playwright-testing.md @@ -0,0 +1,159 @@ +--- +sidebar_position: 11 +sidebar_label: Playwright Testing +--- + +# Writing PlayWright Tests + +Our Playwright tests are written using the Playwright test framework. We use these tests to test our OHIF Viewer and ensure that it is working as expected. + +In this guide, we will show you how to write Playwright tests for the OHIF Viewer. + +## Using a specific study and mode + +If you would like to use a specific study, you can use the `studyInstanceUID` property to reference the study you would like to visit. for example, if you would like to use the study with StudyInstanceUID `2.16.840.1.114362.1.11972228.22789312658.616067305.306.2` and the mode `Basic Viewer`, you can use the following code snippet: + +```ts +import { test } from '@playwright/test'; +import { visitStudy, checkForScreenshot, screenShotPaths } from './utils/index.js'; + +test.beforeEach(async ({ page }) => { + const studyInstanceUID = '2.16.840.1.114362.1.11972228.22789312658.616067305.306.2'; + const mode = 'Basic Viewer'; + await visitStudy(page, studyInstanceUID, mode); +}); + +test.describe('Some Test', async () => { + test('should do something.', async ({ page }) => { + // Your test code here... + }); +}); + +``` + +## Screenshots + +A good way to check your tests is working as expected is to capture screenshots at different stages of the test. You can use our `checkForScreenshot` function located in `tests/utils/checkForScreenshot.ts` to capture screenshots. You should also plan your screenshots in advance, screenshots need to be defined in the `tests/utils/screenshotPaths.ts` file. For example, if you would to capture a screenshot after a measurement is added, you can define a screenshot path like this: + +```ts +const screenShotPaths = { + your_test_name: { + measurementAdded: 'measurementAdded.png', + measurementRemoved: 'measurementRemoved.png', + }, +}; +``` + +It's okay if the screenshot doesn't exist yet, this will be dealt with in the next step. Once you have defined your screenshot path, you can use the `checkForScreenshot` function in your test to capture the screenshot. For example, if you would like to capture a screenshot of the page after a measurement is added, you can use the following code snippet: + +```ts +import { test } from '@playwright/test'; +import { + visitStudy, + checkForScreenshot, + screenshotPath, +} from './utils/index.js'; + +test.beforeEach(async ({ page }) => { + const studyInstanceUID = '2.16.840.1.114362.1.11972228.22789312658.616067305.306.2'; + const mode = 'Basic Viewer'; + await visitStudy(page, studyInstanceUID, mode); +}); + +test.describe('Some test', async () => { + test('should do something', async ({ page }) => { + // Your test code here to add a measurement + await checkForScreenshot( + page, + page, + screenshotPath.your_test_name.measurementAdded + ); + }); +}); +``` + +The test will automatically fail the first time you run it, it will however generate the screenshot for you, you will notice 3 new entries in the `tests/screenshots` folder, under `chromium/your-test.spec.js/measurementAdded.png`, `firefox/your-test.spec.js/measurementAdded.png` and `webkit/your-test.spec.js/measurementAdded.png` folders. You can now run the test again and it will use those screenshots to compare against the current state of the example. Please verify that the ground truth screenshots are correct before committing them or testing against them. + +## Simulating mouse drags + +If you would like to simulate a mouse drag, you can use the `simulateDrag` function located in `tests/utils/simulateDrag.ts`. You can use this function to simulate a mouse drag on an element. For example, if you would like to simulate a mouse drag on the `cornerstone-canvas` element, you can use the following code snippet: + +```ts +import { + visitStudy, + checkForScreenshot, + screenShotPaths, + simulateDrag, +} from './utils/index.js'; + +test.beforeEach(async ({ page }) => { + const studyInstanceUID = '2.16.840.1.114362.1.11972228.22789312658.616067305.306.2'; + const mode = 'Basic Viewer'; + await visitStudy(page, studyInstanceUID, mode); +}); + +test.describe('Some Test', async () => { + test('should do something..', async ({ + page, + }) => { + const locator = page.locator('.cornerstone-canvas'); + await simulateDrag(page, locator); + }); +}); +``` + +Our simulate drag utility can simulate a drag on any element, and avoid going out of bounds. It will calculuate the bounding box of the element and ensure that the drag stays within the bounds of the element. This should be good enough for most tools, and better than providing custom x, and y coordinates which can be error prone and make the code difficult to maintain. + +## Running the tests + +After you have wrote your tests, you can run them by using the following command: + +```bash +yarn test:e2e:ci +``` + +If you want to use headed mode, you can use the following command: + +```bash +yarn test:e2e:headed +``` + +You will see the test results in your terminal, if you want an indepth report, you can use the following command: + +```bash +yarn playwright show-report tests/playwright-report +``` + +## Serving the viewer manually for development + +By default, when you run the tests, it will call the `yarn start` command to serve the viewer first, then run the tests, if you would like to serve the viewer manually, you can use the same command. The viewer will be available at `http://localhost:3000`. This could speed up your development process since playwright will skip this step and use the existing server on port 3000. + +## Accessing services, managers, configs and cornerstone in your tests + +If you would like to access the cornerstone3D, services, or command managers in your tests, you can use the `page.evaluate` function to access them. For example, if you would like to access the `services` so you can show a UI notifcation using the uiNotifcationService, you can use the following code snippet: + +```ts + await page.evaluate(({ services }: AppTypes.Test) => { + const { uiNotificationService } = services; + uiNotificationService.show({ + title: 'Test', + message: 'This is a test', + type: 'info', + }); + }, await page.evaluateHandle('window')); + ``` + +## Playwright VSCode Extension and Recording Tests + +If you are using VSCode, you can use the Playwright extension to help you write your tests. The extension provides a test runner and many great features such as picking a locator using your mouse, recording a new test, and more. You can install the extension by searching for `Playwright` in the extensions tab in VSCode or by visiting the [Playwright extension page](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright). + +
+ +
+ + +
+ +
diff --git a/platform/docs/versioned_docs/version-3.8/development/testing.md b/platform/docs/versioned_docs/version-3.9/development/testing.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/development/testing.md rename to platform/docs/versioned_docs/version-3.9/development/testing.md diff --git a/platform/docs/versioned_docs/version-3.8/development/types.md b/platform/docs/versioned_docs/version-3.9/development/types.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/development/types.md rename to platform/docs/versioned_docs/version-3.9/development/types.md diff --git a/platform/docs/versioned_docs/version-3.8/development/video-tutorials.md b/platform/docs/versioned_docs/version-3.9/development/video-tutorials.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/development/video-tutorials.md rename to platform/docs/versioned_docs/version-3.9/development/video-tutorials.md diff --git a/platform/docs/versioned_docs/version-3.9/development/webWorkers.md b/platform/docs/versioned_docs/version-3.9/development/webWorkers.md new file mode 100644 index 000000000..7fa3bed1a --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/development/webWorkers.md @@ -0,0 +1,191 @@ +--- +sidebar_position: 13 +sidebar_label: Web Workers +--- +# Web Worker Implementation Guide + +## Overview +Web Workers enable running computationally intensive tasks in background threads without blocking the UI. This guide explains how to implement them step by step. + +## Basic Setup + +### 1. Create Your Worker File +First, create a worker file with your background tasks: + +```javascript +// myWorker.js +import { expose } from 'comlink'; + +const obj = { + // Simple task + basicCalculation({ data }) { + // Your computation here + return result; + }, + + // Task with progress updates + longRunningTask({ data }, progressCallback) { + const total = data.length; + + for (let i = 0; i < total; i++) { + // Your processing logic + + if (progressCallback) { + const progress = Math.round((i / total) * 100); + progressCallback(progress); + } + } + + return result; + } +}; + +expose(obj); +``` + +### 2. Register the Worker + +In the main thread, can be your service, commands module, etc. + +```javascript +import { getWebWorkerManager } from '@cornerstonejs/core'; + +const workerManager = getWebWorkerManager(); + +// Define worker creation function +const workerFn = () => { + return new Worker( + new URL('./myWorker.js', import.meta.url), + { name: 'my-worker' } + ); +}; + +// Registration options +const options = { + maxWorkerInstances: 1, // Number of concurrent workers + autoTerminateOnIdle: { + enabled: true, + idleTimeThreshold: 3000, // Terminate after 3s idle + }, +}; + +// Register the worker +workerManager.registerWorker('my-worker', workerFn, options); +``` + +:::info +It is recommended to register the worker in top of the commands module. So that it +gets registered before any commands that need to use the worker. +::: + +### 3. Execute Tasks + +```javascript +// Basic execution +try { + const result = await workerManager.executeTask( + 'my-worker', + 'basicCalculation', + { data: myData } + ); +} catch (error) { + console.error('Task failed:', error); +} + +// Execution with progress callback +try { + const result = await workerManager.executeTask( + 'my-worker', + 'longRunningTask', + { data: myData }, + { + callbacks: [ + (progress) => { + console.log(`Progress: ${progress}%`); + } + ] + } + ); +} catch (error) { + console.error('Task failed:', error); +} +``` + +## Progress Events (Optional) + +If you want to show progress in your UI as a loading spinner, you can implement a progress event system: + +### 1. Publish Progress Events + +```javascript +// Helper to trigger progress events +const publishProgress = (eventTarget, progress, taskId) => { + triggerEvent(eventTarget, 'WEB_WORKER_PROGRESS', { + progress, // number 0-100 + type: 'YOUR_TASK_TYPE', // can be any string identifier + id: taskId, // unique task identifier + }); +}; + +// Usage in your application +async function runTaskWithProgress(data) { + // Start progress + publishProgress(eventTarget, 0, data.id); + + try { + const result = await workerManager.executeTask( + 'my-worker', + 'longRunningTask', + { data }, + { + callbacks: [ + (progress) => { + publishProgress(eventTarget, progress, data.id); + } + ] + } + ); + + // Complete progress + publishProgress(eventTarget, 100, data.id); + + return result; + } catch (error) { + console.error('Task failed:', error); + throw error; + } +} +``` + +Note: Publishing the `WEB_WORKER_PROGRESS` event on Cornerstone's `eventTarget` will automatically trigger the built-in loading spinner. This gives users visual feedback while your worker runs in the background. + + +## Multiple Methods in One Worker + +You can define multiple related methods in a single worker file: + +```javascript +// complexWorker.js +import { expose } from 'comlink'; + +const obj = { + processingMethod1({ data }, progressCallback) { + // Implementation + }, + + processingMethod2({ data }, progressCallback) { + // Implementation + }, + + processingMethod3({ data }, progressCallback) { + // Implementation + }, + + // Shared helper methods + _internalHelper() { + // Helper logic + } +}; + +expose(obj); +``` diff --git a/platform/docs/versioned_docs/version-3.9/faq/_category_.json b/platform/docs/versioned_docs/version-3.9/faq/_category_.json new file mode 100644 index 000000000..d83af0328 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/faq/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "FAQ", + "position": 13 +} diff --git a/platform/docs/versioned_docs/version-3.9/faq/faq-measure-1.png b/platform/docs/versioned_docs/version-3.9/faq/faq-measure-1.png new file mode 100644 index 000000000..86d92ddd9 Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/faq/faq-measure-1.png differ diff --git a/platform/docs/versioned_docs/version-3.9/faq/faq-measure-2.png b/platform/docs/versioned_docs/version-3.9/faq/faq-measure-2.png new file mode 100644 index 000000000..3feef696a Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/faq/faq-measure-2.png differ diff --git a/platform/docs/versioned_docs/version-3.9/faq/faq-measure-4.png b/platform/docs/versioned_docs/version-3.9/faq/faq-measure-4.png new file mode 100644 index 000000000..4198eda5a Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/faq/faq-measure-4.png differ diff --git a/platform/docs/versioned_docs/version-3.9/faq/faq-measure-5.png b/platform/docs/versioned_docs/version-3.9/faq/faq-measure-5.png new file mode 100644 index 000000000..1dcdc4253 Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/faq/faq-measure-5.png differ diff --git a/platform/docs/versioned_docs/version-3.9/faq/faq-measure3.png b/platform/docs/versioned_docs/version-3.9/faq/faq-measure3.png new file mode 100644 index 000000000..3231771c0 Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/faq/faq-measure3.png differ diff --git a/platform/docs/versioned_docs/version-3.9/faq/general.md b/platform/docs/versioned_docs/version-3.9/faq/general.md new file mode 100644 index 000000000..2c01de7d1 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/faq/general.md @@ -0,0 +1,82 @@ +--- +id: general +--- + + + +# General FAQ + + +## How do I report a bug? + +Navigate to our [GitHub Repository][new-issue], and submit a new bug report. +Follow the steps outlined in the [Bug Report Template][bug-report-template]. + +## How can I request a new feature? + +At the moment we are in the process of defining our roadmap and will do our best +to communicate this to the community. If your requested feature is on the +roadmap, then it will most likely be built at some point. If it is not, you are +welcome to build it yourself and [contribute it](../development/contributing.md). +If you have resources and would like to fund the development of a feature, +please [contact us](https://ohif.org/get-support). + + +## Who should I contact about Academic Collaborations? + +[Gordon J. Harris](https://www.dfhcc.harvard.edu/insider/member-detail/member/gordon-j-harris-phd/) +at Massachusetts General Hospital is the primary contact for any academic +collaborators. We are always happy to hear about new groups interested in using +the OHIF framework, and may be able to provide development support if the +proposed collaboration has an impact on cancer research. + +## Does OHIF offer support? + +yes, you can contact us for more information [here](https://ohif.org/get-support) + + +## Does The OHIF Viewer have [510(k) Clearance][501k-clearance] from the U.S. F.D.A or [CE Marking][ce-marking] from the European Commission? + +**NO.** The OHIF Viewer is **NOT** F.D.A. cleared or CE Marked. It is the users' +responsibility to ensure compliance with applicable rules and regulations. The +[License](https://github.com/OHIF/Viewers/blob/master/LICENSE) for the OHIF +Platform does not prevent your company or group from seeking F.D.A. clearance +for a product built using the platform. + +If you have gone this route (or are going there), please let us know because we +would be interested to hear about your experience. + +## Is there a DICOM Conformance Statement for the OHIF Viewer? + +Yes, check it here [DICOM Conformance Statement](https://docs.google.com/document/d/1hbDlUApX4svX33gAUGxGfD7fXXZNaBsX0hSePbc-hNA/edit?usp=sharing) + +## Is The OHIF Viewer [HIPAA][hipaa-def] Compliant? + +**NO.** The OHIF Viewer **DOES NOT** fulfill all of the criteria to become HIPAA +Compliant. It is the users' responsibility to ensure compliance with applicable +rules and regulations. + +## Could you provide me with a particular study from the OHIF Viewer Demo? + +You can check out the studies that we have put in this [Dropbox link](https://www.dropbox.com/scl/fo/66xidsx13pn0zf3b9cbfq/ADaCgn7aT29WMlnTdT_WRXM?rlkey=rratvx6g4kfxnswjdbupewjye&dl=0) + + + + + + +[general]: general +[technical]: technical +[report-bug]: how-do-i-report-a-bug +[new-feature]: how-can-i-request-a-new-feature +[commercial-support]: does-ohif-offer-commercial-support +[academic]: who-should-i-contact-about-academic-collaborations +[fda-clearance]: does-the-ohif-viewer-have-510k-clearance-from-the-us-fda-or-ce-marking-from-the-european-commission +[hipaa]: is-the-ohif-viewer-hipaa-compliant +[501k-clearance]: https://www.fda.gov/MedicalDevices/DeviceRegulationandGuidance/HowtoMarketYourDevice/PremarketSubmissions/PremarketNotification510k/ +[ce-marking]: https://ec.europa.eu/growth/single-market/ce-marking_en +[hipaa-def]: https://en.wikipedia.org/wiki/Health_Insurance_Portability_and_Accountability_Act +[new-issue]: https://github.com/OHIF/Viewers/issues/new/choose +[bug-report-template]: https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Bug+Report+%3Abug%3A&template=---bug-report.md&title= diff --git a/platform/docs/versioned_docs/version-3.9/faq/index.md b/platform/docs/versioned_docs/version-3.9/faq/index.md new file mode 100644 index 000000000..e44af0f30 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/faq/index.md @@ -0,0 +1,81 @@ +--- +id: index +--- + + +# General FAQ + + +## How do I report a bug? + +Navigate to our [GitHub Repository][new-issue], and submit a new bug report. +Follow the steps outlined in the [Bug Report Template][bug-report-template]. + +## How can I request a new feature? + +At the moment we are in the process of defining our roadmap and will do our best +to communicate this to the community. If your requested feature is on the +roadmap, then it will most likely be built at some point. If it is not, you are +welcome to build it yourself and [contribute it](../development/contributing.md). +If you have resources and would like to fund the development of a feature, +please [contact us](https://ohif.org/get-support). + + +## Who should I contact about Academic Collaborations? + +[Gordon J. Harris](https://www.dfhcc.harvard.edu/insider/member-detail/member/gordon-j-harris-phd/) +at Massachusetts General Hospital is the primary contact for any academic +collaborators. We are always happy to hear about new groups interested in using +the OHIF framework, and may be able to provide development support if the +proposed collaboration has an impact on cancer research. + +## Does OHIF offer support? + +yes, you can contact us for more information [here](https://ohif.org/get-support) + + +## Does The OHIF Viewer have [510(k) Clearance][501k-clearance] from the U.S. F.D.A or [CE Marking][ce-marking] from the European Commission? + +**NO.** The OHIF Viewer is **NOT** F.D.A. cleared or CE Marked. It is the users' +responsibility to ensure compliance with applicable rules and regulations. The +[License](https://github.com/OHIF/Viewers/blob/master/LICENSE) for the OHIF +Platform does not prevent your company or group from seeking F.D.A. clearance +for a product built using the platform. + +If you have gone this route (or are going there), please let us know because we +would be interested to hear about your experience. + +## Is there a DICOM Conformance Statement for the OHIF Viewer? + +Yes, check it here [DICOM Conformance Statement](https://docs.google.com/document/d/1hbDlUApX4svX33gAUGxGfD7fXXZNaBsX0hSePbc-hNA/edit?usp=sharing) + +## Is The OHIF Viewer [HIPAA][hipaa-def] Compliant? + +**NO.** The OHIF Viewer **DOES NOT** fulfill all of the criteria to become HIPAA +Compliant. It is the users' responsibility to ensure compliance with applicable +rules and regulations. + +## Could you provide me with a particular study from the OHIF Viewer Demo? + +You can check out the studies that we have put in this [Dropbox link](https://www.dropbox.com/scl/fo/66xidsx13pn0zf3b9cbfq/ADaCgn7aT29WMlnTdT_WRXM?rlkey=rratvx6g4kfxnswjdbupewjye&dl=0) + + + + + + +[general]: #general +[technical]: #technicalß˚ +[report-bug]: #how-do-i-report-a-bug +[new-feature]: #how-can-i-request-a-new-feature +[commercial-support]: #does-ohif-offer-commercial-support +[academic]: #who-should-i-contact-about-academic-collaborations +[fda-clearance]: #does-the-ohif-viewer-have-510k-clearance-from-the-us-fda-or-ce-marking-from-the-european-commission +[hipaa]: #is-the-ohif-viewer-hipaa-compliant +[501k-clearance]: https://www.fda.gov/MedicalDevices/DeviceRegulationandGuidance/HowtoMarketYourDevice/PremarketSubmissions/PremarketNotification510k/ +[ce-marking]: https://ec.europa.eu/growth/single-market/ce-marking_en +[hipaa-def]: https://en.wikipedia.org/wiki/Health_Insurance_Portability_and_Accountability_Act +[new-issue]: https://github.com/OHIF/Viewers/issues/new/choose +[bug-report-template]: https://github.com/OHIF/Viewers/issues/new?assignees=&labels=Bug+Report+%3Abug%3A&template=---bug-report.md&title= diff --git a/platform/docs/versioned_docs/version-3.9/faq/study-sorting.png b/platform/docs/versioned_docs/version-3.9/faq/study-sorting.png new file mode 100644 index 000000000..46d2be1ec Binary files /dev/null and b/platform/docs/versioned_docs/version-3.9/faq/study-sorting.png differ diff --git a/platform/docs/versioned_docs/version-3.9/faq/technical.md b/platform/docs/versioned_docs/version-3.9/faq/technical.md new file mode 100644 index 000000000..4b446e5ab --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/faq/technical.md @@ -0,0 +1,365 @@ +# Technical FAQ + + + +## Why do I keep seeing a Cross Origin Isolation warning +If you encounter a warning while running OHIF indicating that your application is not cross-origin isolated, it implies that volume rendering, such as MPR, will not function properly since they depend on Shared Array Buffers. To resolve this issue, we recommend referring to our comprehensive guide on Cross Origin Isolation available at [our dedicated cors page](../deployment/cors.md). + +## What if my setup does not support the Shared Array Buffers API? +You can simply disable that by adding the `useSharedArrayBuffer: 'FALSE'` (notice the string FALSE), and the volumes will only use a regular +array buffer which is a bit slower but will work on all browsers. + + +## Viewer opens but does not show any thumbnails + +Thumbnails may not appear in your DICOMWeb application for various reasons. This guide focuses on one primary scenario, which is you are using +the `supportsWildcard: true` in your configuration file while your sever does not support it. +One + +For instance for the following filtering in the worklist tab we send this request + +![](../assets/img/filtering-worklist.png) + +`https://d33do7qe4w26qo.cloudfront.net/dicomweb/studies?PatientName=*Head*&limit=101&offset=0&fuzzymatching=false&includefield=00081030%2C00080060` + +Which our server can respond properly. If your server does not support this type of filtering, you can disable it by setting `supportsWildcard: false` in your configuration file, +or edit your server code to support it for instance something like + +```js +Pseudocode: +For each filter in filters: + if filter.value contains "*": + Convert "*" to SQL LIKE wildcard ("%") + Add "metadataField LIKE ?" to query + else: + Add "metadataField = ?" to query +``` + + + +## What are the list of required metadata for the OHIF Viewer to work? + + +### Mandatory + +**All Modalities** + +- `StudyInstanceUID`, `SeriesInstanceUID`, `SOPInstanceUID`: Unique identifiers for the study, series, and object. +- `PhotometricInterpretation`: Describes the color space of the image. +- `Rows`, `Columns`: Image dimensions. +- `PixelRepresentation`: Indicates how pixel data should be interpreted. +- `Modality`: Type of modality (e.g., CT, MR, etc.). +- `PixelSpacing`: Spacing between pixels. +- `BitsAllocated`: Number of bits allocated for each pixel sample. +- `SOPClassUID`: Specifies the DICOM service class of the object (though you might be able to render without it for most regular images datasets, but it is pretty normal to have it) + +**Rendering** + +You need to have the following tags for the viewer to render the image properly, otherwise you should +use the windowing tools to adjust the image to your liking: + +- `RescaleIntercept`, `RescaleSlope`: Values used for rescaling pixel values for visualization. +- `WindowCenter`, `WindowWidth`: Windowing parameters for display. + +**Some Datasets** + +- `InstanceNumber`: Useful for sorting instances (without it the instances might be out of order) + +**For MPR (Multi-Planar Reformatting) rendering and tools** + +- `ImagePositionPatient`, `ImageOrientationPatient`: Position and orientation of the image in the patient. + +**SEG (Segmentation)** + +- `FrameOfReferenceUID` for handling segmentation layers. +- sequences + - `ReferencedSeriesSequence` + - `SharedFunctionalGroupsSequence` + - `PerFrameFunctionalGroupsSequence` + +**RTSTRUCT (Radiotherapy Structure)** + +- `FrameOfReferenceUID` for handling segmentation layers. +- sequences + - `ROIContourSequence` + - `StructureSetROISequence` + - `ReferencedFrameOfReferenceSequence` + +**US (Ultrasound)** + +- `NumberOfFrames`: Number of frames in a multi-frame image. +- `SequenceOfUltrasoundRegions`: For measurements. +- `FrameTime`: Time between frames if specified. + +**SR (Structured Reporting)** + +- Various sequences for encoding the report content and template. + - `ConceptNameCodeSequence` + - `ContentSequence` + - `ContentTemplateSequence` + - `CurrentRequestedProcedureEvidenceSequence` + - `ContentTemplateSequence` + - `CodingSchemeIdentificationSequence` + +**PT with SUV Correction (Positron Tomography Standardized Uptake Value)** + +- Sequences and tags related to radiopharmaceuticals, units, corrections, and timing. + - `RadiopharmaceuticalInformationSequence` + - `SeriesDate` + - `SeriesTime` + - `CorrectedImage` + - `Units` + - `DecayCorrection` + - `AcquisitionDate` + - `AcquisitionTime` + - `PatientWeight` + +**PDF** + +- `EncapsulatedDocument`: Contains the PDF document. + +**Video** + +- `NumberOfFrames`: Video frame count . + + +### Optional +There are various other optional tags that will add to the viewer experience, but are not required for basic functionality. These include: +Patient Information, Study Information, Series Information, Instance Information, and Frame Information. + + +## How do I handle large volumes for MPR and Volume Rendering + +Currently there are two ways to handle large volumes for MPR and Volume Rendering if that does not +fit in the memory of the client machine. + +### `useNorm16Texture` + +WebGL officially supports only 8-bit and 32-bit data types. For most images, 8 bits are not enough, and 32 bits are too much. However, we have to use the 32-bit data type for volume rendering and MPR, which results in suboptimal memory consumption for the application. + +Through [EXT_texture_norm16](https://registry.khronos.org/webgl/extensions/EXT_texture_norm16/) , WebGL can support 16 bit data type which is ideal +for most images. You can look into the [webgl report](https://webglreport.com/?v=2) to check if you have that extension enabled. + +![](../assets/img/webgl-report-norm16.png) + + +This is a flag that you can set in your [configuration file](../configuration/configurationFiles.md) to force usage of 16 bit data type for the volume rendering and MPR. This will reduce the memory usage by half. + + +For instance for a large pt/ct study + +![](../assets/img/large-pt-ct.jpeg) + +Before (without the flag) the app shows 399 MB of memory usage + +![](../assets/img/memory-profiling-regular.png) + + +After (with flag, running locally) the app shows 249 MB of memory usage + + +![](../assets/img/webgl-int16.png) + +:::note +Using the 16 bit texture (if supported) will not have any effect in the rendering what so ever, and pixelData +would be exactly shown as it is. For datasets that cannot be represented with 16 bit data type, the flag will be ignored +and the 32 bit data type will be used. + + +Read more about these discussions in our PRs +- https://github.com/Kitware/vtk-js/pull/2058 +::: + + +:::warning +Although the support for 16 bit data type is available in WebGL, in some settings (e.g., Intel-based Macos) there seems +to be still some issues with it. You can read and track bugs below. + +- https://bugs.chromium.org/p/chromium/issues/detail?id=1246379 +- https://bugs.chromium.org/p/chromium/issues/detail?id=1408247 +::: + +### `preferSizeOverAccuracy` + +This is another flag that you can set in your [configuration file](../configuration/configurationFiles.md) to force the usage of the `half_float` data type for volume rendering and MPR. The main reason to choose this option over `useNorm16Texture` is its broader support across hardware and browsers. However, it is less accurate than the 16-bit data type and may lead to some rendering artifacts. + +```js +Integers between 0 and 2048 can be exactly represented (and also between −2048 and 0) +Integers between 2048 and 4096 round to a multiple of 2 (even number) +Integers between 4096 and 8192 round to a multiple of 4 +Integers between 8192 and 16384 round to a multiple of 8 +Integers between 16384 and 32768 round to a multiple of 16 +Integers between 32768 and 65519 round to a multiple of 32 +``` + +As you see in the ranges above 2048 there will be inaccuracies in the rendering. + +Memory snapshot after enabling `preferSizeOverAccuracy` for the same study as above + +![](../assets/img/preferSizeOverAccuracy.png) + + +## How to dynamically load a measurement + +You can dynamically load a measurement by using a combination of `MeasurementService` and `CornerstoneTools` Annotation API. Here, we will demonstrate this with an example of loading a `Rectangle` measurement. + +![alt text](faq-measure-1.png) + +So if we look at the terminal and get the measurement service we can see there is one measurement + +![alt text](faq-measure-2.png) + +However, this is the `mapped` cornerstone measurement inside OHIF, and it has additional information such as `geReport` and `source`, which are internal details of OHIF Viewers that you don't need to worry about. + +we can call the `cornerstoneTools` api to grab the raw annotation data with the `uid` + +`cornerstoneTools.annotation.state.getAnnotation("ea45a45c-0731-47d4-9438-d2a53ffea4ff")` + +![alt text](faq-measure3.png) + + + + +:::note +Note: There is a `pointsInShape` attribute inside the data that stores the points within the annotation for some tools like `Rectangle` and `EllipticalRoi`. However, you can remove that attribute as well. +::: + +For the sake of this example, I have extracted those keys and uploaded them to our server for fetching. + +` +https://ohif-assets.s3.us-east-2.amazonaws.com/ohif-faq/rectangle-roi.json +` + +Now, let's discuss how to load this measurement dynamically and programmatically. + +There are numerous places in OHIF where you can add annotations, but we always recommend having your own extensions and modes to maintain full control over your custom API. + +For this example, I will add the logic in the `longitudinal` mode. However, as mentioned, you can create your own extension and mode, and either use `onModeEnter` or other lifecycle hooks to add annotations. Learn more about lifecycle hooks [here](../platform/extensions/lifecycle.md). + + +Of course, you need to load the appropriate measurement for each study. However, for simplicity's sake, I will hardcode the URL in this example. + +```js +import * as cs3dTools from '@cornerstonejs/tools'; + +onModeEnter: function ({ servicesManager, extensionManager, commandsManager }: withAppTypes) { + // rest of logic + + const annotationResponse = await fetch( + 'https://ohif-assets.s3.us-east-2.amazonaws.com/ohif-faq/rectangle-roi.json' + ); + + const annotationData = await annotationResponse.json(); + + cs3dTools.annotation.state.addAnnotation(annotationData); +}, +``` + +As you can see, we use the CornerstoneTools API to add the annotation. Since OHIF has mappers set up for CornerstoneTools (`extensions/cornerstone/src/utils/measurementServiceMappings/measurementServiceMappingsFactory.ts`), it will automatically map the annotation to the OHIF measurement service. + +If you refresh the viewer, you'll see the measurement loaded on the image. + +![alt text](faq-measure-4.png) + +But if you notice it does not appear on the right panel, the reason is that the right panel is the tracking measurement panel. You can switch to a non-tracking measurement by changing + +`rightPanels: [dicomSeg.panel, tracked.measurements],` + +to + +`rightPanels: [dicomSeg.panel, '@ohif/extension-default.panelModule.measure'],` + +which then it will look like + +![alt text](faq-measure-5.png) + + + +## How do I sort the series in the study panel by a specific value + +You need to enable the experimental StudyBrowserSort component by setting the `experimentalStudyBrowserSort` to true in your config file. This will add a dropdown in the study panel to sort the series by a specific value. This component is experimental +since we are re-deigning the study panel and it might change in the future, but the functionality will remain the same. + +```js +{ + experimentalStudyBrowserSort: true, +} +``` +The component will appear in the study panel and will allow you to sort the series by a specific value. It comes with 3 default sorting functions, Series Number, Series Image Count, and Series Date. + +You can sort the series in the study panel by a specific value by adding a custom sorting function in the customizationModule, you can use the existing customizationModule in `extensions/default/src/getCustomizationModule.tsx` or create your own in your extension. + +The value to be used for the entry is `studyBrowser.sortFunctions` and should be under the `default` key. + +### Example + +```js +export default function getCustomizationModule({ servicesManager, extensionManager }) { + return [ + { + name: 'default', + value: [ + + { + id: 'studyBrowser.sortFunctions', + values: [ + { + label: 'Series Number', + sortFunction: (a, b) => { + return a?.SeriesNumber - b?.SeriesNumber; + }, + }, + // Add more sort functions as needed + ], + }, + ], + }, + ]; +} +``` + +### Explanation +This function will be retrieved by the StudyBrowserSort component and will be used to sort all displaySets, it will reflect in all parts of the app since it works at the displaySetService level, which means the thumbnails in the study panel will also be sorted by the desired value. +You can define multiple functions and pick which sort to use via the dropdown in the StudyBrowserSort component that appears in the study panel. + + +## How can i change the sorting of the thumbnail / study panel / study browser +We are currently redesigning the study panel and the study browser. During this process, you can enable our undesigned component via the `experimentalStudyBrowserSort` flag. This will look like: + +![alt text](study-sorting.png) + +You can also add your own sorting functions by utilizing the `customizationService` and adding the `studyBrowser.sortFunctions` key, as shown below: + +``` +customizationService.addModeCustomizations([ + { + id: 'studyBrowser.sortFunctions', + values: [{ + label: 'Series Images', + sortFunction: (a, b) => { + return a?.numImageFrames - b?.numImageFrames; + }, + }], + }, +]); +``` + +:::note +Notice the arrays and objects, the values are arrays +::: + + +## How do I change the cine auto mount behavior + +You can change the cine auto mount behavior by adding the `autoCineModalities` mode customization, the value is an array of modalities that should be mounted with cine. + +By default the viewer will mount with cine enabled for `OT` and `US` modalities. + +```js +customizationService.addModeCustomizations([ + { + id: 'autoCineModalities', + modalities: ['OT', 'US'], + }, +]); +``` diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/0-general.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/0-general.md new file mode 100644 index 000000000..8a96e70e2 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/0-general.md @@ -0,0 +1,288 @@ +--- +id: 0-general +title: General +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Not SharedArrayBuffer anymore! + +We have streamlined the process of loading volumes without sacrificing speed by eliminating the need for shared array buffers. This change resolves issues across various frameworks, where previously, specific security headers were required. Now, you can remove any previously set headers, which lowers the barrier for adopting Cornerstone 3D in frameworks that didn't support those headers. Shared array buffers are no longer necessary, and all related headers can be removed. + +You can remove `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` from your custom headers if you don't need them in other +aspects of your app. + +# React 18 Migration Guide +As we upgrade to React 18, we're making some exciting changes to improve performance and developer experience. This guide will help you navigate the key updates and ensure your custom extensions and modes are compatible with the new version. +What's Changing? + + + + +```md +- React 17 +- Using `defaultProps` +- `babel-inline-svg` for SVG imports +``` + + + + +```md +- React 18 +- Default parameters for props +- `svgr` for SVG imports +``` + + + + + +## Update React version: +In your custom extensions and modes, change the version of react and react-dom to ^18.3.1. + +## Replace defaultProps with default parameters: + + + + +```jsx +const MyComponent = ({ prop1, prop2 }) => { + return
{prop1} {prop2}
+} + +MyComponent.defaultProps = { + prop1: 'default value', + prop2: 'default value' +} +``` + +
+ + +```jsx +const MyComponent = ({ prop1 = 'default value', prop2 = 'default value' }) => { + return
{prop1} {prop2}
+} +``` +
+
+ +## Update SVG imports: + +You might need to update your SVG imports to use the `ReactComponent` syntax, if you want to use the old Icon component. However, we have made a significant change to how we handle Icons, read the UI Migration Guide for more information. + + + + +```javascript +import arrowDown from './../../assets/icons/arrow-down.svg'; +``` + + + + +```javascript +import { ReactComponent as arrowDown } from './../../assets/icons/arrow-down.svg'; +``` + + + + +--- + +## Polyfill.io + +We have removed the Polyfill.io script from the Viewer. If you require polyfills, you can add them to your project manually. This change primarily affects Internet Explorer, which Microsoft has already [ended support for](https://learn.microsoft.com/en-us/lifecycle/faq/internet-explorer-microsoft-edge#is-internet-explorer-11-the-last-version-of-internet-explorer-). + + + +--- + +## Crosshairs + +They now have new colors in their associated viewports in the MPR view. However, you can turn this feature off. + +To disable it, remove the configuration from the `initToolGroups` in your mode. + +``` +{ + configuration: { + viewportIndicators: true, + viewportIndicatorsConfig: { + circleRadius: 5, + xOffset: 0.95, + yOffset: 0.05, + }, + } +} +``` + +--- + + +## useAuthorizationCodeFlow + +`useAuthorizationCodeFlow` config is deprecated + +now internally we detect the authorizationCodeFlow if the response_type is equal to `code` + +you can remove the config from the appConfig + +--- + +## StackScrollMouseWheel -> StackScroll Tool + Mouse bindings + +If you previously used: + +```js +{ toolName: toolNames.StackScrollMouseWheel, bindings: [] } +``` + +in your `initToolGroups`, you should now use: + +```js +{ + toolName: toolNames.StackScroll, + bindings: [{ mouseButton: Enums.MouseBindings.Wheel }], +} +``` + +This change allows for more flexible mouse bindings and keyboard combinations. + +## VolumeRotateMouseWheel -> VolumeRotate Tool + Mouse bindings + +Before: + +```js +{ + toolName: toolNames.VolumeRotateMouseWheel, + configuration: { + rotateIncrementDegrees: 5, + }, +}, +``` + +Now: + +```js +{ + toolName: toolNames.VolumeRotate, + bindings: [{ mouseButton: Enums.MouseBindings.Wheel }], + configuration: { + rotateIncrementDegrees: 5, + }, +}, +``` + +--- + +## SidePanel auto switch if open + +In `basic viewer` mode, if the side panel is open and the segmentation panel is active, adding a measurement will automatically switch to the measurement panel. This switch won't occur if the side panel is closed. To enable or disable this feature, adjust your mode configuration accordingly. + +```js +panelService.addActivatePanelTriggers('your.panel.id', [ +{ + sourcePubSubService: segmentationService, + sourceEvents: [segmentationService.EVENTS.SEGMENTATION_ADDED], +}, +]) + +panelService.addActivatePanelTriggers('your.panel.id', [ + { + sourcePubSubService: measurementService, + sourceEvents: [ + measurementService.EVENTS.MEASUREMENT_ADDED, + measurementService.EVENTS.RAW_MEASUREMENT_ADDED, + ], + }, +]) +``` + +--- + +## DicomUpload + +The DICOM upload functionality in OHIF has been refactored to use the standard customization service pattern. Now you don't need to put + +`customizationService: { dicomUploadComponent: '@ohif/extension-cornerstone.customizationModule.cornerstoneDicomUploadComponent', },` + +in your config, we will automatically add that if you have `dicomUploadEnabled` + +--- + +## Viewport and Modality Support for Toolbar Buttons + +Previously, toolbar buttons had limited support for disabling themselves based on the active viewport type (e.g., `volume3d`, `video`, `sr`) or the modality of the displayed data (e.g., `US`, `SM`). This led to inconsistencies and sometimes enabled tools in contexts where they weren't applicable. + +The new implementation introduces more robust and flexible evaluators to control the enabled/disabled state of toolbar buttons based on viewport types and modalities. + +**Key Changes** + +1. **New Evaluators:** New evaluators have been added to the `getToolbarModule`: + - `evaluate.viewport.supported`: Disables a button if the active viewport's type is listed in the `unsupportedViewportTypes` property. + - `evaluate.modality.supported`: Disables a button based on the modalities of the displayed data. It checks for both `unsupportedModalities` (exclusion) and `supportedModalities` (inclusion). +2. **Removal of Legacy Evaluators:** + - Evaluators such as `evaluate.not.sm`, `evaluate.action.not.video`, `evaluate.not3D`, and `evaluate.isUS` have been removed. Migrate your toolbar button definitions to use the new evaluators mentioned above. + + +**Replace Legacy Evaluators:** + - Replace `evaluate.not.sm` with: + + ```json + { + name: 'evaluate.viewport.supported', + unsupportedViewportTypes: ['sm'], + } + ``` + + - Replace `evaluate.action.not.video` with: + + ```json + { + name: 'evaluate.viewport.supported', + unsupportedViewportTypes: ['video'], + } + ``` + + - Replace `evaluate.not3D` with: + + ```json + { + name: 'evaluate.viewport.supported', + unsupportedViewportTypes: ['volume3d'], + } + ``` + + - Replace `evaluate.isUS` with: + + ```json + { + name: 'evaluate.modality.supported', + supportedModalities: ['US'], + } + ``` + +
+Example Migration + +Before: + +```json +evaluate: ['evaluate.cine', 'evaluate.not3D'], +``` + +After + +```json +evaluate: [ + 'evaluate.cine', + { + name: 'evaluate.viewport.supported', + unsupportedViewportTypes: ['volume3d'], + }, +], +``` +
diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/1-Architecture.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/1-Architecture.md new file mode 100644 index 000000000..c8a92efa0 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/1-Architecture.md @@ -0,0 +1,48 @@ +--- +id: seg-new-arch +title: New Architecture +--- + + +## New Architecture + +* **Viewport-Centric Architecture** + * Previous: Segmentations were tied to toolGroups + * Now: Segmentations are tied directly to viewports + * Impact: More granular control but requires significant code changes + +* **Representation Management** + * Previous: Required managing segmentation representation UIDs + * Now: Uses simpler segmentationId + type combination + * Impact: Simplified but requires API updates + + + +If you are not familiar with the difference between a segmentation and a segmentation representation, below + +
+Read More + +In Cornerstone3DTools, we have decoupled the concept of a Segmentation from a Segmentation Representation. This means that from one Segmentation we can create multiple Segmentation Representations. For instance, a Segmentation Representation of a 3D Labelmap, can be created from a Segmentation data, and a Segmentation Representation of a Contour can be created from the same Segmentation data. This way we have decouple the presentational aspect of a Segmentation from the underlying data. + + +Similar relationship structure has been adapted in popular medical imaging softwares such as 3D Slicer with the addition of polymorph segmentation. + +- https://github.com/PerkLab/PolySeg +- https://www.slicer.org/ + + + +
+ + + + +### Architecture Overview + +The new architecture in Cornerstone3D 2.0 makes a clear distinction between: + +* A segmentation (the data structure containing segments) +* A segmentation representation (how that segmentation is visualized in a specific viewport) + +Let's now review what has changed diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/2-segmentationService-basic.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/2-segmentationService-basic.md new file mode 100644 index 000000000..06601b7cd --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/2-segmentationService-basic.md @@ -0,0 +1,433 @@ +--- +id: seg-api +title: SegmentationService API +--- + + + +Below we will review the changes to the API of the `SegmentationService` + +# SegmentationService API + +## Events + +SEGMENTATION_UPDATED -> SEGMENTATION_MODIFIED + + +Just a rename to match the cornerstone terminology + +## VolumeId vs SegmentationId + +Previously, we used the SegmentationId as the VolumeId for volume-based segmentations, which led to confusion and issues. + +Now, we have two separate IDs: one for the segmentation and one for the volume. + +`segmentationService.getLabelmapVolume(segmentationId)` will return the volume associated with the segmentation. + +If your code uses `cache.getVolume(segmentationId)`, update it to use the new `getLabelmapVolume` method. + + +## getSegmentation(segmentationId) + +remains the same it will return the segmentation object = cornerstone segmentation object with the following properties: + +```js +/** + * Global Segmentation Data which is used for the segmentation + */ +type Segmentation = { + /** segmentation id */ + segmentationId: string; + /** segmentation label */ + label: string; + segments: { + [segmentIndex: number]: Segment; + }; + /** + * Representations of the segmentation. Each segmentation "can" be viewed + * in various representations. For instance, if a DICOM SEG is loaded, the main + * representation is the labelmap. However, for DICOM RT the main representation + * is contours, and other representations can be derived from the contour (currently + * only labelmap representation is supported) + */ + representationData: RepresentationsData; + /** + * Segmentation level stats, Note each segment can have its own stats + * This is used for caching stats for the segmentation level + */ + cachedStats: { [key: string]: unknown }; +}; + +export type Segment = { + /** segment index */ + segmentIndex: number; + /** segment label */ + label: string; + /** is segment locked for editing */ + locked: boolean; + /** cached stats for the segment, e.g., pt suv mean, max etc. */ + cachedStats: { [key: string]: unknown }; + /** is segment active for editing, at the same time only one segment can be active for editing */ + active: boolean; +}; +``` + + +
+Compared to Cornerstone3D 1.x + +Previously this function was returning this + +```js +export type Segmentation = { + segmentationId: string; + type: Enums.SegmentationRepresentations; + label: string; + activeSegmentIndex: number; + segmentsLocked: Set; + cachedStats: { [key: string]: number }; + segmentLabels: { [key: string]: string }; + representationData: SegmentationRepresentationData; +}; + +``` + +As you can see `segmentLabels`, `segmentsLocked`, `activeSegmentIndex`, are all gathered under the new `segments` object. We now have support for per segment cachedStats as well. + +
+ +--- + +## getSegmentations + +It provides all segmentations in the state. Previously, it accepted a `filterNonhydrated` flag, but since we've moved away from hydration and every loaded segmentation is now hydrated by default, it returns all segmentations. + + + + +--- + +## getActiveSegmentation + + +After migrating to viewport-specific segmentations, different viewports can have distinct active segmentations for editing. The panel will always display the active segmentation when the active viewport changes. + +Before (3.8) + +```js +// Returns full segmentation object +public getActiveSegmentation(): Segmentation { + const segmentations = this.getSegmentations(); + return segmentations.find(segmentation => segmentation.isActive); +} +``` + +After (3.9) + +```js +public getActiveSegmentation(viewportId: string): Segmentation | null { + return cstSegmentation.activeSegmentation.getActiveSegmentation(viewportId); +} +``` + +
+Key Changes + +1. **Viewport Specificity** + - Before: Global active segmentation across all tool groups + - After: Active segmentation per viewport +2. **Required Parameters** + - Before: No parameters needed + - After: Requires viewportId parameter +
+ + +
+Migration Examples + +**Before:** + +```js +// Get active segmentation +const activeSegmentation = segmentationService.getActiveSegmentation(); +if (activeSegmentation) { + console.log('Active segmentation:', activeSegmentation.segmentationId); + console.log('Active segment:', activeSegmentation.activeSegmentIndex); +} +``` + +**After:** + +```js +// Get active segmentation for specific viewport +const activeSegmentation = segmentationService.getActiveSegmentation('viewport1'); + +``` + +
+ +--- + +## getToolGroupIdsWithSegmentation + +is now -> `getViewportIdsWithSegmentation` as you guessed + + + +## setActiveSegmentationForToolGroup + +-> setActiveSegmentation + + + +**Before (OHIF 3.8)** + +```js +setActiveSegmentationForToolGroup( + segmentationId: string, + toolGroupId?: string, + suppressEvents?: boolean +): void +``` + +**After (OHIF 3.9)** + +```js +setActiveSegmentation( + viewportId: string, + segmentationId: string +): void +``` + +
+Migration Examples + +1. **Basic Usage Update** + + ```js + // Before - OHIF 3.8 + segmentationService.setActiveSegmentationForToolGroup( + segmentationId, + toolGroupId + ); + // After - OHIF 3.9 + segmentationService.setActiveSegmentation( + viewportId, + segmentationId + ); + ``` + +
+ + + +--- + + +## addSegment + +The `addSegment` method in OHIF 3.9 has been updated to handle segmentation properties in a viewport-centric way, removing tool group dependencies and simplifying the configuration structure. + + +**Before (OHIF 3.8)** + +```js +addSegment( + segmentationId: string, + config: { + segmentIndex?: number; + toolGroupId?: string; + properties?: { + label?: string; + color?: ohifTypes.RGB; + opacity?: number; + visibility?: boolean; + isLocked?: boolean; + active?: boolean; + }; + } +): void +``` + +**After (OHIF 3.9)** + +```js +addSegment( + segmentationId: string, + config: { + segmentIndex?: number; + label?: string; + isLocked?: boolean; + active?: boolean; + color?: csTypes.Color; + visibility?: boolean; + } +): void +``` + +
+Key Changes + +1. **Configuration Structure** + - Removed double nested `properties` object + - Configuration options now at top level + - Removed `toolGroupId` parameter + - Removed `opacity` parameter (now part of color) +2. **Segment Index Generation** + - Changed from length-based to max-value-based indexing + - More reliable for non-sequential segment indices +3. **Color Handling** + - Color now includes alpha channel (opacity) + - Applied to all relevant viewports automatically +
+ + + + +
+Migration Examples + +1. **Basic Segment Creation** + + ```js + // Before - OHIF 3.8 + segmentationService.addSegment(segmentationId, { + properties: { + label: 'Segment 1' + } + }); + // After - OHIF 3.9 + segmentationService.addSegment(segmentationId, { + label: 'Segment 1' + }); + ``` + +2. **Creating Segment with Color** + + ```js + // Before - OHIF 3.8 + segmentationService.addSegment(segmentationId, { + properties: { + color: [255, 0, 0], + opacity: 255 + } + }); + // After - OHIF 3.9 + segmentationService.addSegment(segmentationId, { + color: [255, 0, 0, 255] // RGB + Alpha + }); + ``` + +3. **Setting Visibility and Lock Status** + + ```js + // Before - OHIF 3.8 + segmentationService.addSegment(segmentationId, { + toolGroupId: 'myToolGroup', + properties: { + visibility: true, + isLocked: true + } + }); + // After - OHIF 3.9 + segmentationService.addSegment(segmentationId, { + visibility: true, + isLocked: true + }); + ``` + +4. **Complete Configuration Example** + + ```js + // Before - OHIF 3.8 + segmentationService.addSegment(segmentationId, { + segmentIndex: 1, + toolGroupId: 'myToolGroup', + properties: { + label: 'Tumor', + color: [255, 0, 0], + opacity: 200, + visibility: true, + isLocked: false, + active: true + } + }); + // After - OHIF 3.9 + segmentationService.addSegment(segmentationId, { + segmentIndex: 1, + label: 'Tumor', + color: [255, 0, 0, 200], // RGB + Alpha + visibility: true, + isLocked: false, + active: true + }); + ``` + +
+ + + + +
+Important Changes + +1. **Tool Group Removal** + ```js + // Before - OHIF 3.8 + segmentationService.addSegment(segmentationId, { + toolGroupId: 'myToolGroup' + // ... other properties + }); + // After - OHIF 3.9 + // No tool group needed - automatically applies to all relevant viewports + segmentationService.addSegment(segmentationId, { + // ... properties + }); + ``` + +2. **Segment Index Generation** + ```js + // Before - OHIF 3.8 + // Used array length + segmentIndex = segmentation.segments.length === 0 ? 1 : segmentation.segments.length; + // After - OHIF 3.9 + // Uses highest existing index + 1 + segmentIndex = Math.max(...Object.keys(csSegmentation.segments).map(Number)) + 1; + ``` + +3. **Color and Opacity** + ```js + // Before - OHIF 3.8 + segmentationService.addSegment(segmentationId, { + properties: { + color: [255, 0, 0], + opacity: 200 + } + }); + + // After - OHIF 3.9 + segmentationService.addSegment(segmentationId, { + color: [255, 0, 0, 200] // Combined color and opacity + }); + ``` + +
+ + +--- + +--- + +## getActiveSegment + +now requires viewportId, since we have moved away from global active segmentation to viewport specific one + +**API Changes** + +```js +// Before +getActiveSegment(): Segment + +// After +getActiveSegment(viewportId: string): Segment | null +``` diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/3-segmentationserice-representation.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/3-segmentationserice-representation.md new file mode 100644 index 000000000..522259276 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/3-segmentationserice-representation.md @@ -0,0 +1,189 @@ +--- +id: seg-representation +title: Segmentation Representations +--- + + + + +## Segmentation Representation Management API + +```js +addSegmentationRepresentationToToolGroup +removeSegmentationRepresentationFromToolGroup +getSegmentationRepresentationsForToolGroup +``` + +In Cornerstone3D 2.0, segmentation representation management has shifted from a tool group-centric approach to a viewport-centric approach. This architectural change provides better control over segmentation rendering and simplifies the mental model for managing segmentations. + + +### Adding Segmentation Representations + +**Before (3.8)**: + +```js +// Tool group-based approach +await segmentation.addSegmentationRepresentationToToolGroup( + toolGroupId, + segmentationId, + hydrateSegmentation, + csToolsEnums.SegmentationRepresentations.Labelmap +); +``` + +**After (3.9)**: + +```js +// Viewport-centric approach +await segmentation.addSegmentationRepresentation( + viewportId, + { + segmentationId: segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + } +); +``` + +### Removing Segmentation Representations + +**Before** : + +```js +// Remove specific representations from a tool group +segmentation.removeSegmentationRepresentationFromToolGroup( + toolGroupId, + [segmentationRepresentationUID] +); +// Remove all representations from a tool group +segmentation.removeSegmentationRepresentationFromToolGroup(toolGroupId); +``` + +**After** + +```js +// Remove specific representation from a viewport +segmentation.removeSegmentationRepresentation( + viewportId, + { + segmentationId: segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap + } +); +// Remove all representations from a viewport +segmentation.removeSegmentationRepresentations(viewportId); +``` + +### Getting Segmentation Representations + +**Before**: + +```js +// Get representations for a tool group +const representations = segmentation.getSegmentationRepresentationsForToolGroup(toolGroupId); +``` + +**After** : + +```js +// Get all representations for a viewport +const representations = segmentation.getSegmentationRepresentations(viewportId); + +// Get specific type of representations +const labelmapReps = segmentation.getSegmentationRepresentations(viewportId, { + type: csToolsEnums.SegmentationRepresentations.Labelmap +}); + +// Get representations for specific segmentation +const segmentationReps = segmentation.getSegmentationRepresentations(viewportId, { + segmentationId: segmentationId +}); + +// Get specific representation +const representation = segmentation.getSegmentationRepresentation(viewportId, { + segmentationId: segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap +}); +``` + +### Understanding the Specifier Pattern + +The Cornerstone3D 2.0 (OHIF 3.9) API introduces a "specifier" pattern that provides more flexible and precise control over segmentation representations. A specifier is an object that can include: + +```js +type Specifier = { + segmentationId?: string; // The ID of the segmentation + type?: SegmentationRepresentations; // The type of representation (Labelmap, Contour, etc.) +} +``` + +The specifier pattern allows for: + +1. **Precise Targeting**: You can target specific segmentations and representation types + - Allows direct access to individual segmentations + - Enables filtering by representation type + +2. **Flexible Querying**: You can get all representations of a certain type or for a specific segmentation + - Query by segmentation ID + - Query by representation type + - Combine queries for specific needs + +3. **Granular Control**: You can manage representations at different levels of specificity + - Viewport level control + - Segmentation level control + - Individual representation type control + +### Examples of Specifier Usage + +```js +// Get all labelmap representations in a viewport +const labelmaps = segmentation.getSegmentationRepresentations(viewportId, { + type: csToolsEnums.SegmentationRepresentations.Labelmap +}); + +// Get all representations of a specific segmentation (including contour, labelmap, surface) +const segReps = segmentation.getSegmentationRepresentations(viewportId, { + segmentationId: 'seg123' +}); + +// Get a specific representation +const specificRep = segmentation.getSegmentationRepresentation(viewportId, { + segmentationId: 'seg123', + type: csToolsEnums.SegmentationRepresentations.Labelmap +}); +``` + +
+Benefits of the New Approach + +1. **Direct Viewport Control**: + - Each viewport can have its own unique representation configuration + - No need to create separate tool groups for different viewport representations +2. **Simpler Mental Model**: + - Representations are directly tied to where they're displayed + - No intermediate tool group layer to manage +3. **More Flexible Rendering**: + - Each viewport can render the same segmentation differently + - Better support for multiple views of the same data +4. **Improved Type Safety**: + - Specifier pattern provides better TypeScript support + - More explicit API with clearer intentions +
+ + +
+Migration Tips + +1. **Replace Tool Group References**: + - Search your codebase for `toolGroupId` references in segmentation code + - Replace with appropriate `viewportId` references +2. **Update Event Handlers**: + - Update any code listening for segmentation events + - Events now include viewportId instead of toolGroupId +3. **Review Representation Management**: + - Identify where you manage segmentation representations + - Convert to using the new viewport-centric methods +4. **Consider Viewport Context**: + - Think about segmentation representation in terms of viewport display + - Use specifiers to target specific representations when needed + +
diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-creation.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-creation.md new file mode 100644 index 000000000..c70021e33 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-creation.md @@ -0,0 +1,215 @@ +--- +id: seg-creation +title: Segmentation Creation +--- + +## createEmptySegmentationForViewport + +is now `createLabelmapForViewport` to align with other segmentation creation methods. + +Run it using `commandsManager.runCommand('createLabelmapForViewport', {viewportId})`. + +## createSegmentationForDisplaySet + +is now -> `createLabelmapForDisplaySet` + +Since we are moving towards segmentations be contours as well, this is renamed to clearly state the purpose. +Since OHIF 3.9 introduced Stack Segmentation support, we no longer generate a volume-based labelmap or convert the viewport to a volume viewport by default. Our default creation is now stack-based. + +API Changes +- `createSegmentationForDisplaySet` has been renamed to `createLabelmapForDisplaySet`. +- Pass a `displaySet` object instead of a `displaySetInstanceUID`. This change enhances type safety and flexibility, accommodating future updates to the `displaySetService`. + +**Before (OHIF 3.8)** + +```js +async createSegmentationForDisplaySet( + displaySetInstanceUID: string, + options?: { + segmentationId: string; + FrameOfReferenceUID: string; + label: string; + } +): Promise +``` + +**After (OHIF 3.9)** + +```js +// Method 1: Display Set Based +async createLabelmapForDisplaySet( + displaySet: DisplaySet, + options?: { + segmentationId?: string; + label: string; + segments?: { + [segmentIndex: number]: Partial + }; + } +): Promise +``` + + +
+Migration Examples + + +```js +// Before - OHIF 3.8 +const segmentationId = await segmentationService.createSegmentationForDisplaySet( + displaySetInstanceUID, + { + label: 'My Segmentation' + } +); +``` + +```js +// After - OHIF 3.9 +// Option 1: If you have a display set UID +const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID); + +const segmentationId = await segmentationService.createLabelmapForDisplaySet( + displaySet, + { + label: 'My Segmentation' + } +); +``` + +
+ +--- + +## createSegmentationForRTDisplaySet + + +**Before (OHIF 3.8)** + +```js +async createSegmentationForRTDisplaySet( + rtDisplaySet, + segmentationId?: string, + suppressEvents = false +): Promise +``` + +**After (OHIF 3.9)** + +```js +async createSegmentationForRTDisplaySet( + rtDisplaySet, + options: { + segmentationId?: string; + type: SegmentationRepresentations; // not required, defaults to Contour + } +): Promise +``` + + +
+Migration Examples + +if you were not passing segmentationId, you don't need to change anything + + +```js +// Before - OHIF 3.8 +const segmentationId = await segmentationService.createSegmentationForRTDisplaySet( + rtDisplaySet +); + +// After - OHIF 3.9 +const segmentationId = await segmentationService.createSegmentationForRTDisplaySet( + rtDisplaySet, +); +``` + +if you were passing segmentationId, you need to update the API to pass an options object and set the segmentationId in there. + +```js +// Before - OHIF 3.8 +const segmentationId = await segmentationService.createSegmentationForRTDisplaySet( + rtDisplaySet, + 'custom-id', +); +// After - OHIF 3.9 +const segmentationId = await segmentationService.createSegmentationForRTDisplaySet( + rtDisplaySet, + { + segmentationId: 'custom-id', + type: csToolsEnums.SegmentationRepresentations.Contour + } +); +``` + +
+ +--- + + +## createSegmentationForSEGDisplaySet Changes + +**Before (OHIF 3.8)** + +```js +async createSegmentationForSEGDisplaySet( + segDisplaySet, + segmentationId?: string, + suppressEvents = false +): Promise +``` + +**After (OHIF 3.9)** + +```js +async createSegmentationForSEGDisplaySet( + segDisplaySet, + options: { + segmentationId?: string; + type: SegmentationRepresentations; // not required, defaults to Labelmap + } +): Promise +``` + +
+Migration Examples + +1. **Basic Usage Update** + + ``` + // Before - OHIF 3.8 + const segmentationId = await segmentationService.createSegmentationForSEGDisplaySet( + segDisplaySet + ); + // After - OHIF 3.9 + const segmentationId = await segmentationService.createSegmentationForSEGDisplaySet( + segDisplaySet, + { + type: csToolsEnums.SegmentationRepresentations.Labelmap + } + ); + ``` + +2. **Custom Configuration** + + ``` + // Before - OHIF 3.8 + const segmentationId = await segmentationService.createSegmentationForSEGDisplaySet( + segDisplaySet, + 'custom-id', + false + ); + // After - OHIF 3.9 + const segmentationId = await segmentationService.createSegmentationForSEGDisplaySet( + segDisplaySet, + { + segmentationId: 'custom-id', + type: csToolsEnums.SegmentationRepresentations.Labelmap + } + ); + ``` +
+ + +--- diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-modification.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-modification.md new file mode 100644 index 000000000..ebe16f7c1 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/4-segmentationserice-modification.md @@ -0,0 +1,193 @@ +--- +id: seg-service-mod +title: SegmentationService Modifications +--- + + +--- + + +## Segmentation Representation Management API + +```js +addSegmentationRepresentationToToolGroup +removeSegmentationRepresentationFromToolGroup +getSegmentationRepresentationsForToolGroup +``` + +In Cornerstone3D 2.0, segmentation representation management has shifted from a tool group-centric approach to a viewport-centric approach. This architectural change provides better control over segmentation rendering and simplifies the mental model for managing segmentations. + + +### Adding Segmentation Representations + +**Before (3.8)**: + +```js +// Tool group-based approach +await segmentation.addSegmentationRepresentationToToolGroup( + toolGroupId, + segmentationId, + hydrateSegmentation, + csToolsEnums.SegmentationRepresentations.Labelmap +); +``` + +**After (3.9)**: + +```js +// Viewport-centric approach +await segmentation.addSegmentationRepresentation( + viewportId, + { + segmentationId: segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap, + } +); +``` + +### Removing Segmentation Representations + +**Before** : + +```js +// Remove specific representations from a tool group +segmentation.removeSegmentationRepresentationFromToolGroup( + toolGroupId, + [segmentationRepresentationUID] +); +// Remove all representations from a tool group +segmentation.removeSegmentationRepresentationFromToolGroup(toolGroupId); +``` + +**After** + +```js +// Remove specific representation from a viewport +segmentation.removeSegmentationRepresentation( + viewportId, + { + segmentationId: segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap + } +); +// Remove all representations from a viewport +segmentation.removeSegmentationRepresentations(viewportId); +``` + +### Getting Segmentation Representations + +**Before**: + +```js +// Get representations for a tool group +const representations = segmentation.getSegmentationRepresentationsForToolGroup(toolGroupId); +``` + +**After** : + +```js +// Get all representations for a viewport +const representations = segmentation.getSegmentationRepresentations(viewportId); + +// Get specific type of representations +const labelmapReps = segmentation.getSegmentationRepresentations(viewportId, { + type: csToolsEnums.SegmentationRepresentations.Labelmap +}); + +// Get representations for specific segmentation +const segmentationReps = segmentation.getSegmentationRepresentations(viewportId, { + segmentationId: segmentationId +}); + +// Get specific representation +const representation = segmentation.getSegmentationRepresentation(viewportId, { + segmentationId: segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap +}); +``` + +### Understanding the Specifier Pattern + +The Cornerstone3D 2.0 (OHIF 3.9) API introduces a "specifier" pattern that provides more flexible and precise control over segmentation representations. A specifier is an object that can include: + +```js +type Specifier = { + segmentationId?: string; // The ID of the segmentation + type?: SegmentationRepresentations; // The type of representation (Labelmap, Contour, etc.) +} +``` + +The specifier pattern allows for: + +1. **Precise Targeting**: You can target specific segmentations and representation types + - Allows direct access to individual segmentations + - Enables filtering by representation type + +2. **Flexible Querying**: You can get all representations of a certain type or for a specific segmentation + - Query by segmentation ID + - Query by representation type + - Combine queries for specific needs + +3. **Granular Control**: You can manage representations at different levels of specificity + - Viewport level control + - Segmentation level control + - Individual representation type control + +### Examples of Specifier Usage + +```js +// Get all labelmap representations in a viewport +const labelmaps = segmentation.getSegmentationRepresentations(viewportId, { + type: csToolsEnums.SegmentationRepresentations.Labelmap +}); + +// Get all representations of a specific segmentation (including contour, labelmap, surface) +const segReps = segmentation.getSegmentationRepresentations(viewportId, { + segmentationId: 'seg123' +}); + +// Get a specific representation +const specificRep = segmentation.getSegmentationRepresentation(viewportId, { + segmentationId: 'seg123', + type: csToolsEnums.SegmentationRepresentations.Labelmap +}); +``` + +
+Benefits of the New Approach + +1. **Direct Viewport Control**: + - Each viewport can have its own unique representation configuration + - No need to create separate tool groups for different viewport representations +2. **Simpler Mental Model**: + - Representations are directly tied to where they're displayed + - No intermediate tool group layer to manage +3. **More Flexible Rendering**: + - Each viewport can render the same segmentation differently + - Better support for multiple views of the same data +4. **Improved Type Safety**: + - Specifier pattern provides better TypeScript support + - More explicit API with clearer intentions +
+ + +
+Migration Tips + +1. **Replace Tool Group References**: + - Search your codebase for `toolGroupId` references in segmentation code + - Replace with appropriate `viewportId` references +2. **Update Event Handlers**: + - Update any code listening for segmentation events + - Events now include viewportId instead of toolGroupId +3. **Review Representation Management**: + - Identify where you manage segmentation representations + - Convert to using the new viewport-centric methods +4. **Consider Viewport Context**: + - Think about segmentation representation in terms of viewport display + - Use specifiers to target specific representations when needed + +
+ + +--- diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/5-segmentationserice-style.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/5-segmentationserice-style.md new file mode 100644 index 000000000..266206bf1 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/5-segmentationserice-style.md @@ -0,0 +1,362 @@ +--- +id: seg-style +title: SegmentationService Style +--- + + +## Style + + +### setSegmentVisibility + +since visibility is viewport concern and representation is what is being toggled -> + +**Before (OHIF 3.8)** + +```js +setSegmentVisibility( + segmentationId: string, + segmentIndex: number, + isVisible: boolean, + toolGroupId?: string +): void +``` + +**After (OHIF 3.9)** + +```js +setSegmentVisibility( + viewportId: string, + segmentationId: string, + segmentIndex: number, + isVisible: boolean, + type?: SegmentationRepresentations +): void +``` + +
+Migration Example + +```js +// Before +segmentationService.setSegmentVisibility( + 'segmentation1', + 1, + true, + 'toolGroup1' +); +// After +segmentationService.setSegmentVisibility( + 'viewport1', + 'segmentation1', + 1, + true +); +``` + +**Getting Viewport IDs** + +When you need to update visibility across multiple viewports: + +```js +// Before +const toolGroupIds = ['toolGroup1', 'toolGroup2']; +toolGroupIds.forEach(toolGroupId => { + segmentationService.setSegmentVisibility( + 'segmentation1', + 1, + true, + toolGroupId + ); +}); +// After +const viewportIds = segmentationService.getViewportIdsWithSegmentation('segmentation1'); +viewportIds.forEach(viewportId => { + segmentationService.setSegmentVisibility( + viewportId, + 'segmentation1', + 1, + true + ); +}); +``` + + +
+ + +### get/set Configuration -> get/setStyle + +The segmentation configuration system has been completely redesigned: + +- Moved from global/toolGroup configuration to viewport-specific styles +- Split rendering of inactive segmentations into separate API +- More granular control over styles at different levels (global, segmentation, viewport, segment) + + +**Before (OHIF 3.8)** + +```js +interface SegmentationConfig { + brushSize: number; + brushThresholdGate: number; + fillAlpha: number; + fillAlphaInactive: number; + outlineWidthActive: number; + renderFill: boolean; + renderInactiveSegmentations: boolean; + renderOutline: boolean; + outlineOpacity: number; + outlineOpacityInactive: number; +} +``` + +**After (OHIF 3.9)** + +```js +// Style Types +interface StyleSpecifier { + viewportId?: string; + segmentationId?: string; + type: SegmentationRepresentations; + segmentIndex?: number; +} +interface LabelmapStyle { + renderOutline: boolean; + outlineWidth: number; + renderFill: boolean; + fillAlpha: number; + outlineAlpha: number; + // .... +} +// Functions +getStyle(specifier: StyleSpecifier): LabelmapStyle | ContourStyle | SurfaceStyle; +setStyle(specifier: StyleSpecifier, style: LabelmapStyle | ContourStyle | SurfaceStyle): void; +setRenderInactiveSegmentations(viewportId: string, renderInactive: boolean): void; +getRenderInactiveSegmentations(viewportId: string): boolean; +``` + + +**Before:** + +```js +// Get global configuration +const config = segmentationService.getConfiguration(); +console.log(config.fillAlpha, config.renderOutline); +// Get tool group specific config +const toolGroupConfig = segmentationService.getConfiguration('toolGroup1'); +``` + +**After:** + +```js +// Get global style for labelmap +const labelmapStyle = segmentationService.getStyle({ + type: SegmentationRepresentations.Labelmap +}); +// Get viewport-specific style +const viewportStyle = segmentationService.getStyle({ + viewportId: 'viewport1', + type: SegmentationRepresentations.Labelmap +}); +// Get segmentation-specific style +const segmentationStyle = segmentationService.getStyle({ + segmentationId: 'seg1', + type: SegmentationRepresentations.Labelmap +}); +// Get segment-specific style +const segmentStyle = segmentationService.getStyle({ + segmentationId: 'seg1', + type: SegmentationRepresentations.Labelmap, + segmentIndex: 1 +}); +``` + + + +**Setting Configuration/Style** + +**Before:** + +```js +segmentationService.setConfiguration({ + fillAlpha: 0.5, + outlineWidthActive: 2, + renderOutline: true, + renderFill: true, + renderInactiveSegmentations: true +}); +``` + +**After:** + +```js +// Set global style +segmentationService.setStyle( + { type: SegmentationRepresentations.Labelmap }, + { + fillAlpha: 0.5, + outlineWidth: 2, + renderOutline: true, + renderFill: true + } +); +// Set viewport-specific style +segmentationService.setStyle( + { + viewportId: 'viewport1', + type: SegmentationRepresentations.Labelmap + }, + { + fillAlpha: 0.5, + outlineWidth: 2 + } +); +// Handle inactive segmentations separately +segmentationService.setRenderInactiveSegmentations('viewport1', true); +``` + + +
+Migration Examples + +**Combining Multiple Style Settings** + +**Before:** + +```js +segmentationService.setConfiguration({ + fillAlpha: 0.5, + fillAlphaInactive: 0.2, + outlineWidthActive: 2, + outlineOpacity: 1, + outlineOpacityInactive: 0.5, + renderOutline: true, + renderFill: true, + renderInactiveSegmentations: true +}); +``` + +**After:** + +```js +// Set base style +segmentationService.setStyle( + { type: SegmentationRepresentations.Labelmap }, + { + fillAlpha: 0.5, + outlineWidth: 2, + outlineAlpha: 1, + renderOutline: true, + renderFill: true + } +); +``` + +
+ + + +**Set inactive rendering per viewport** + +```js +segmentationService.setRenderInactiveSegmentations('viewport1', true); +// Set style for inactive segments if needed +segmentationService.setStyle( + { + viewportId: 'viewport1', + type: SegmentationRepresentations.Labelmap, + segmentationId: 'seg1' + }, + { + fillAlpha: 0.2, + outlineAlpha: 0.5 + } +); +``` + +--- + + + +## setSegmentRGBAColor , setSegmentOpacity, setSegmentRGBA +Previously, the SegmentationService had multiple redundant methods for setting colors and opacity (`setSegmentRGBA`, `setSegmentColor`, `setSegmentOpacity`). This led to confusion and potential state inconsistencies between the service and Cornerstone.js Tools. + +The old methods (`setSegmentRGBA`, `setSegmentRGBA`, and `setSegmentOpacity`) are now removed. + + +1. Replace `setSegmentRGBAColor`, `setSegmentRGBA`, and `setSegmentOpacity` calls: Replace all instances of the old methods with the new `setSegmentColor` method. Note that you now need to provide the `viewportId` as the first argument since segment color is managed per viewport and representation in cornerstone3D. + + +**Before** + +```js +// Old API: +segmentationService.setSegmentRGBAColor(segmentationId, segmentIndex, rgbaColor, toolGroupId); +segmentationService.setSegmentRGBA(segmentationId, segmentIndex, rgbaColor, toolGroupId); +segmentationService.setSegmentOpacity(segmentationId, segmentIndex, opacity, toolGroupId); +``` + +**After** + +```js +// New API: +segmentationService.setSegmentColor(viewportId, segmentationId, segmentIndex, color); // color is an array of [red, green, blue, alpha] +``` + +The new `color` argument is an array representing the RGBA color, where the alpha component determines the opacity. Since the Cornerstone Tools library handles segment color per viewport and representation, we require the `viewportId` as an argument now. + + + +2. **Retrieve Segment Color using** `getSegmentColor`: The new `getSegmentColor` provides a way to fetch the color of a segment within a specific viewport. + +```js +const color = segmentationService.getSegmentColor(viewportId, segmentationId, segmentIndex); //returns [r, g, b, a] +``` + + +--- + + + +## ToggleSegmentationVisibility + +In Cornerstone3D v2.x, `toggleSegmentationVisibility` has been replaced with `toggleSegmentationRepresentationVisibility`. This change reflects the fact that +a representation is what is being toggled, not the segmentation. + + +**Before (OHIF 3.8)** + +```js +// Toggle visibility for a segmentation globally +segmentationService.toggleSegmentationVisibility(segmentationId); +``` + +**After (OHIF 3.9)** + +```js +// Toggle visibility for a segmentation representation in a specific viewport +segmentationService.toggleSegmentationRepresentationVisibility(viewportId, { + segmentationId: segmentationId, + type: csToolsEnums.SegmentationRepresentations.Labelmap +}); +``` + +**Migration Steps** + +1. Update all calls to `toggleSegmentationVisibility` to use `toggleSegmentationRepresentationVisibility` +2. Add the required `viewportId` parameter +3. Add a `type` parameter specifying the representation type (e.g., Labelmap, Contour) +4. If you were toggling visibility across all viewports, you'll need to loop through the viewports: + + +
+Additional Notes + + +- Each viewport can now have independent visibility settings for the same segmentation +- The visibility state is specific to the representation type (Labelmap, Contour, etc.) +- To check current visibility, use `getSegmentationRepresentationVisibility(viewportId, { segmentationId, type })` +
+ +--- diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md new file mode 100644 index 000000000..1236787e1 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/6-segmentationserice-other.md @@ -0,0 +1,374 @@ +--- +id: seg-other +title: Other Changes +--- + + + + +## addOrUpdateSegmentation + +This was a public method but there is a good chance you were not using it + + +**Before (OHIF 3.8)** + +```js +// Before +addOrUpdateSegmentation( + segmentation: Segmentation, + suppressEvents = false, + notYetUpdatedAtSource = false +): string +``` + +**After** + +```js +addOrUpdateSegmentation( + segmentationInput: SegmentationPublicInput | Partial +) +``` + +### Data Structure Changes + +The segmentation object that was used previously was a custom segmentation object that was used internally by the SegmentationService. But +we have moved to the cornerstone public segmentation input type. + +**Before:** + +```js +const segmentation = { + id: 'segmentation1', + type: SegmentationRepresentations.Labelmap, + isActive: true, + activeSegmentIndex: 1, + segments: [ + { + segmentIndex: 1, + color: [255, 0, 0], + isVisible: true, + isLocked: false, + opacity: 255 + } + ], + label: 'Segmentation 1', + cachedStats: {}, + representationData: { + LABELMAP: { + volumeId: 'volume1', + referencedVolumeId: 'reference1' + } + } +}; +``` + + +**After:** + +This matches the cornerstone public segmentation input type. + +```js +const segmentationInput = { + segmentationId: 'segmentation1', + representation: { + type: SegmentationRepresentations.Labelmap, + data: { + imageIds: segmentationImageIds, + referencedVolumeId: 'reference1' + } + }, + config: { + label: 'Segmentation 1', + segments: { + 1: { + label: 'Segment 1', + active: true, + locked: false + } + } + } +}; +``` + +
+Migration Examples + + +```js +// Before +const newSegmentation = { + id: 'seg1', + type: SegmentationRepresentations.Labelmap, + segments: [...], + representationData: { + LABELMAP: { + volumeId: 'volume1', + referencedVolumeId: 'reference1' + } + } +}; +segmentationService.addOrUpdateSegmentation(newSegmentation); + +// After +segmentationService.addOrUpdateSegmentation({ + segmentationId: 'seg1', + representation: { + type: SegmentationRepresentations.Labelmap, + data: { + imageIds: segmentationImageIds, + referencedVolumeId: 'reference1' + } + }, + config: { + segments: { + 1: { + label: 'Segment 1', + active: true + } + } + } +}); +``` + + +**Updating Existing Segmentation** + +```js +// Before +const updatedSegmentation = { + ...existingSegmentation, + segments: [...modifiedSegments], + activeSegmentIndex: 2 +}; +segmentationService.addOrUpdateSegmentation(updatedSegmentation); + +// After +segmentationService.addOrUpdateSegmentation({ + segmentationId: 'seg1', + config: { + segments: { + 2: { active: true }, + } + } +}); +``` + +
+ + +## loadSegmentationsForViewport + +same as addOrUpdateSegmentation, you should pass in the new segmentation data structure. + +For instance + +**Before** + +```js +const segmentations = [ + { + id: '1', + label: 'Segmentations', + segments: labels.map((label, index) => ({ + segmentIndex: index + 1, + label + })), + isActive: true, + activeSegmentIndex: 1, + }, +]; + +commandsManager.runCommand('loadSegmentationsForViewport', { + segmentations, +}); +``` + + + +**After** + +```js + +const labels = ['Segment 1', 'Segment 2', 'Segment 3']; + +const segmentations = [ + { + segmentationId: '1', + representation: { + type: Enums.SegmentationRepresentations.Labelmap, + }, + config: { + label: 'Segmentations', + segments: labels.reduce((acc, label, index) => { + acc[index + 1] = { + label, + active: index === 0, // First segment is active + locked: false, + }; + return acc; + }, {}), + }, + }, +]; + +commandsManager.runCommand('loadSegmentationsForViewport', { + segmentations, +}); +``` + + +--- + + + + +## highlightSegment + +**Before (OHIF 3.8)** + +```js +// Before (v1.x) +highlightSegment( + segmentationId: string, + segmentIndex: number, + toolGroupId?: string, + alpha = 0.9, + animationLength = 750, + hideOthers = true, + highlightFunctionType = 'ease-in-out' +) + +``` + +**After (OHIF 3.9)** + +```js +highlightSegment( + segmentationId: string, + segmentIndex: number, + viewportId?: string, // notice viewportId instead of toolGroupId + alpha = 0.9, + animationLength = 750, + hideOthers = true, + highlightFunctionType = 'ease-in-out' +) +``` + +
+Key Changes + +1. Removed `toolGroupId` in favor of `viewportId` +2. If no viewportId is provided, highlights in all relevant viewports + +
+ +
+Migration Examples + +**Basic Usage** + +```js +// Before +segmentationService.highlightSegment( + 'seg1', + 1, + 'toolGroup1', + 0.9, + 750, + true, +); +// After +segmentationService.highlightSegment( + 'seg1', + 1, + 'viewport1', + 0.9, + 750, + true +); +``` + +**Highlighting in Multiple Views** + +```js +// Before +const toolGroupIds = ['toolGroup1', 'toolGroup2']; +toolGroupIds.forEach(toolGroupId => { + segmentationService.highlightSegment( + 'seg1', + 1, + toolGroupId + ); +}); +// After - Method 1: Let service handle multiple viewports +segmentationService.highlightSegment('seg1', 1); +// After - Method 2: Explicitly specify viewports +const viewportIds = ['viewport1', 'viewport2']; +viewportIds.forEach(viewportId => { + segmentationService.highlightSegment( + 'seg1', + 1, + viewportId + ); +}); +``` +
+ +--- + +## jumpToSegmentCenter + +**Before (OHIF 3.8)** + +```js +jumpToSegmentCenter( + segmentationId: string, + segmentIndex: number, + toolGroupId?: string, + highlightAlpha = 0.9, + highlightSegment = true, + animationLength = 750, + highlightHideOthers = false, + highlightFunctionType = 'ease-in-out' +) +``` + +**After (OHIF 3.9)** + +```js +jumpToSegmentCenter( + segmentationId: string, + segmentIndex: number, + viewportId? string, // notice viewportId instead of toolGroupId + highlightAlpha = 0.9, + highlightSegment = true, + animationLength = 750, + highlightHideOthers = false, + highlightFunctionType = 'ease-in-out' +) +``` + +
+Key Changes + +1. Removed `toolGroupId` parameter infavor of viewportId +2. Automatically handles relevant viewports if `viewportId` not provided + + +``` +// Before +segmentationService.jumpToSegmentCenter( + 'seg1', + 1, + 'toolGroup1' +); +// After +segmentationService.jumpToSegmentCenter( + 'seg1', + 1, + 'viewportId1' +); +``` + +
diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/index.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/index.md new file mode 100644 index 000000000..0eea04b41 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/1-segmentation/index.md @@ -0,0 +1,11 @@ +--- +id: segmentation-index +title: Segmentation +sidebar_position: 1 +--- + +:::info +This migration involves significant architectural changes to the segmentation system. While we typically aim for incremental updates, the shift from a tool group-centric to a viewport-centric architecture was necessary to support OHIF 3.9's advanced visualization capabilities, and more flexible segmentation handling. + +Don't worry - we'll guide you through each change step by step! +::: diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/2-Renamings.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/2-Renamings.md new file mode 100644 index 000000000..6c0763f3c --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/2-Renamings.md @@ -0,0 +1,33 @@ +--- +id: 2-renamings +title: Renamings +sidebar_position: 2 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + +## Panel Measurements + +The panel in the default extension is renamed from `measure` to `panelMeasurement` to be more consistent with the rest of the extensions. + +**Action Needed** + +Update any references to the `measure` panel to `panelMeasurement` in your code. + +Find and replace + + + + @ohif/extension-default.panelModule.measure + + + @ohif/extension-cornerstone.panelModule.panelMeasurement + + + +## addIcon from ui + +The addIcon from the ui package has had a version added in the default extension as +`utils.addIcon` which adds to both `ui` and `ui-next`. diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/3-DataSources.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/3-DataSources.md new file mode 100644 index 000000000..2d14adf53 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/3-DataSources.md @@ -0,0 +1,40 @@ +--- +id: 3-data-sources +title: Data Sources +sidebar_position: 3 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## BulkDataURI Configuration + +We've updated the configuration for BulkDataURI to provide more flexibility and control. This guide will help you migrate from the old configuration to the new one. + +### What's Changing? + + + + +```javascript +useBulkDataURI: false, +``` + + + + +```javascript +bulkDataURI: { + enabled: true, + // Additional configuration **options** +}, +``` + + + + + +**Additional Notes:** +- The new configuration allows for more granular control over BulkDataURI behavior. +- You can now add custom URL prefixing logic using the startsWith and prefixWith properties. +- This change enables easier correction of retrieval URLs, especially in scenarios where URLs pass through multiple systems. diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/4-Measurements.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/4-Measurements.md new file mode 100644 index 000000000..d4b89faf3 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/4-Measurements.md @@ -0,0 +1,43 @@ +--- +title: Measurements +--- + + +## Display Text + + +Previously, `displayText` for measurements was often a simple string or an array of strings. This approach made it difficult to distinguish between primary measurement values (e.g., length, area) and secondary information (e.g., series number, instance number). It also limited styling options for differentiating these types of information. + +The new approach introduces a structured object for `displayText`, consisting of `primary` and `secondary` arrays. This separation allows for better organization and presentation of measurement information. The `primary` array is intended for the main measurement values (on the left), while the `secondary` array is for contextual information like series and instance numbers (on the right) + +### Migration Steps + +If you have custom measurement tools or modify existing ones, you need to update the `getDisplayText` functions within the `measurementServiceMappings` to return a structured object in the new format. + +**Update Measurement Mappings:** If your extension defines custom measurement tools or modifies existing ones, update the `getDisplayText` functions within the `measurementServiceMappings` to return a structured object in the new format. + +```js +// Old Implementation (example for Length tool) +function getDisplayText(mappedAnnotations, displaySet, customizationService) { + // ... + return `${roundedLength} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`; +} +// New Implementation +function getDisplayText(mappedAnnotations, displaySet) { + // ... + return { + primary: [`${roundedLength} ${unit}`], // Primary measurement value + secondary: [`S: ${SeriesNumber}${instanceText}${frameText}`], // Secondary information + }; +} +``` + +--- + +### selected property + +`selected` property on measurements is now renamed to `isSelected` to match the rest of `isLocked` , `isVisible` naming convention. + +Migration: you probably don't need to perform any migration + +--- diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/4-ViewportActionCorner.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/4-ViewportActionCorner.md new file mode 100644 index 000000000..7b85dafc0 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/4-ViewportActionCorner.md @@ -0,0 +1,40 @@ +--- +id: viewport-action-corner +title: ViewportActionCorner +--- + + + + +## Key Changes and Rationale + +Previously, the `ViewportActionCornersService` used the `setComponent` or `setComponents` methods to add components to viewport corners. These methods, when used with multiple components, would essentially overwrite existing components at the same location, unless great care was taken with the `indexPriority` property. This made it difficult to reliably position multiple components within the same corner. + +The new approach introduces the methods `addComponent` and `addComponents`, which insert components into the viewport corners based on an optional `indexPriority` property and provide predictable ordering based on the relative `indexPriority` of the components already at the corner. If no `indexPriority` is given, components are added to the end (for the left side) or the beginning (for the right side) by default. + +### Migration Steps + +**Update Component Addition Methods:** Replace calls to `setComponent` and `setComponents` with `addComponent` and `addComponents`, respectively. + +```js +// Old API +viewportActionCornersService.setComponent({ + viewportId, + id: 'myComponent', + component: , + location: viewportActionCornersService.LOCATIONS.topRight +}); +``` + +**New API** + +```js +viewportActionCornersService.addComponent({ + viewportId, + id: 'myComponent', + component: , + location: viewportActionCornersService.LOCATIONS.topRight, + indexPriority: 1, // indexPriority is now optional and determines placement order within the corner +}); + +``` diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/5-StateSyncService.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/5-StateSyncService.md new file mode 100644 index 000000000..8505a342e --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/5-StateSyncService.md @@ -0,0 +1,343 @@ +--- +id: state-sync-service +title: StateSyncService +--- + + +## Migrating from StateSyncService to Zustand Stores + +The `StateSyncService` has been deprecated in favor of more modern and efficient state management using Zustand stores. This migration guide outlines the reasons for the change and provides step-by-step instructions on how to migrate your extension or mode from using `StateSyncService` to Zustand. + +## Why Migrate? + +The `StateSyncService` had limitations: + +- **Limited Reactivity:** Updates weren't always reactive, requiring manual re-renders. +- **Lack of Granularity:** It stored large chunks of state, hindering performance. +- **Complexity:** Managing and syncing state across components was cumbersome. + +Zustand offers several advantages: + +- **Lightweight and Fast:** Zustand is a minimal and performant state management library. +- **Granular Control:** Create individual stores for specific data, improving reactivity and performance. +- **Simplified API:** Easy-to-use hooks for subscribing and updating state. + +## Migration Steps: + +1. **Identify State to Migrate:** Determine which parts of your extension or mode rely on the `StateSyncService`. Typical examples include: + - **Viewport Presentations:** LUT and position information for viewports. + - **Layout State:** Custom grid layouts and one-up toggling. + - **Synchronizers:** State for cross-viewport synchronization. + - **UI State:** UI-specific settings. +2. **Replace StateSyncService Usage:** In your extension or mode: + - **Import Zustand Stores:** Import the new stores you created. + - **Replace** `getState()` and `store()`: Use the Zustand hooks (`useStore`, `set`, `get`) to access and update state in your components. + - **Handle Presentation IDs:** Implement logic for generating and managing presentation IDs within your stores or relevant components. This can involve using unique keys based on viewport options, display sets, and unique indices. See the `presentationUtils.ts` file for example implementations. + - **Rehydrate State:** On mode entry, rehydrate your Zustand stores with any relevant persisted state from localStorage or other storage mechanisms. + - **Clear State on Mode Exit:** Ensure you clear your Zustand stores appropriately on mode exit to prevent memory leaks. + + + +### `LutPresentationStore` + + +**Before (StateSyncService):** + +```js +const stateSyncService = servicesManager.services.stateSyncService; +const lutPresentationStore = stateSyncService.getState().lutPresentationStore; +const lutPresentation = lutPresentationStore[presentationId]; +// ...to update +stateSyncService.store({ + lutPresentationStore: { + ...lutPresentationStore, + [presentationId]: newLutPresentation, + }, +}); +``` + +**After (Zustand):** + +```js +import { useLutPresentationStore } from '../stores/useLutPresentationStore'; +const { lutPresentationStore, setLutPresentation } = useLutPresentationStore(); +const lutPresentation = lutPresentationStore[presentationId]; +// ...to update +setLutPresentation(presentationId, newLutPresentation); +``` + +The `getPresentationId` for `lutPresentationStore` was previously registered in `platform/core`. Now, the Zustand store provides this functionality. + +```js +// Fetch getPresentationId functions from respective Zustand stores +const { getPresentationId: getLutPresentationId } = useLutPresentationStore.getState(); + +// Register presentation id providers +viewportGridService.addPresentationIdProvider('lutPresentationId', getLutPresentationId); +``` + + +--- + +### `PositionPresentationStore` + +**Before (StateSyncService):** + +```js +const stateSyncService = servicesManager.services.stateSyncService; +const positionPresentationStore = stateSyncService.getState().positionPresentationStore; +const positionPresentation = positionPresentationStore[presentationId]; +// ...to update +stateSyncService.store({ + positionPresentationStore: { + ...positionPresentationStore, + [presentationId]: newPositionPresentation, + }, +}); +``` + +**After (Zustand):** + +```js +import { usePositionPresentationStore } from '../stores/usePositionPresentationStore'; +const { positionPresentationStore, setPositionPresentation } = usePositionPresentationStore(); +const positionPresentation = positionPresentationStore[presentationId]; +// ...to update +setPositionPresentation(presentationId, newPositionPresentation); +``` + +Similar to lutPresentationId, the PositionPresentationId is also registered from outside + +```js + + const { getPresentationId: getPositionPresentationId } = usePositionPresentationStore.getState(); + + // register presentation id providers + viewportGridService.addPresentationIdProvider( + 'positionPresentationId', + getPositionPresentationId + ); +``` + +--- + +### `ViewportGridStore` + +**Before (StateSyncService):** + +```js +const stateSyncService = servicesManager.services.stateSyncService; +const viewportGridStore = stateSyncService.getState().viewportGridStore; +const gridState = viewportGridStore[storeId]; +// ...to update +stateSyncService.store({ + viewportGridStore: { + ...viewportGridStore, + [storeId]: newGridState, + }, +}); +``` + +**After (Zustand):** + +```js +import { useViewportGridStore } from '../stores/useViewportGridStore'; +const { viewportGridState, setViewportGridState } = useViewportGridStore(); +const gridState = viewportGridState[storeId]; +// ...to update +setViewportGridState(storeId, newGridState); +``` + +--- + +### `DisplaySetSelectorStore` + + +**Before (StateSyncService):** + +```js +const stateSyncService = servicesManager.services.stateSyncService; +const displaySetSelectorMap = stateSyncService.getState().displaySetSelectorMap; +const displaySetUID = displaySetSelectorMap[selectorKey]; +// ...to update +stateSyncService.store({ + displaySetSelectorMap: { + ...displaySetSelectorMap, + [selectorKey]: newDisplaySetUID, + }, +}); +``` + +**After (Zustand):** + +```js +import { useDisplaySetSelectorStore } from '../stores/useDisplaySetSelectorStore'; +const { displaySetSelectorMap, setDisplaySetSelector } = useDisplaySetSelectorStore(); +const displaySetUID = displaySetSelectorMap[selectorKey]; +// ...to update +setDisplaySetSelector(selectorKey, newDisplaySetUID); +``` + +--- + +### `HangingProtocolStageIndexStore` + + +**Before (StateSyncService):** + +```js +const stateSyncService = servicesManager.services.stateSyncService; +const hangingProtocolStageIndexMap = stateSyncService.getState().hangingProtocolStageIndexMap; +const hpInfo = hangingProtocolStageIndexMap[cacheId]; +// ...to update +stateSyncService.store({ + hangingProtocolStageIndexMap: { + ...hangingProtocolStageIndexMap, + [cacheId]: newHpInfo, + }, +}); +``` + +**After (Zustand):** + +```js +import { useHangingProtocolStageIndexStore } from '../stores/useHangingProtocolStageIndexStore'; +const { hangingProtocolStageIndexMap, setHangingProtocolStageIndex } = useHangingProtocolStageIndexStore(); +const hpInfo = hangingProtocolStageIndexMap[cacheId]; +// ...to update +setHangingProtocolStageIndex(cacheId, newHpInfo); +``` + +--- + +### `ToggleHangingProtocolStore` + + +**Before (StateSyncService):** + +```js +const stateSyncService = servicesManager.services.stateSyncService; +const toggleHangingProtocol = stateSyncService.getState().toggleHangingProtocol; +const previousHpInfo = toggleHangingProtocol[storedHanging]; +// ...to update +stateSyncService.store({ + toggleHangingProtocol: { + ...toggleHangingProtocol, + [storedHanging]: newHpInfo, + }, +}); +``` + +**After (Zustand):** + +```js +import { useToggleHangingProtocolStore } from '../stores/useToggleHangingProtocolStore'; +const { toggleHangingProtocol, setToggleHangingProtocol } = useToggleHangingProtocolStore(); +const previousHpInfo = toggleHangingProtocol[storedHanging]; +// ...to update +setToggleHangingProtocol(storedHanging, newHpInfo); +``` + +--- + +### `ToggleOneUpViewportGridStore` + + +**Before (StateSyncService):** + +```js +const stateSyncService = servicesManager.services.stateSyncService; +const toggleOneUpViewportGridStore = stateSyncService.getState().toggleOneUpViewportGridStore; +const previousGridState = toggleOneUpViewportGridStore.layout; // Assuming layout was a property +// ...to update +stateSyncService.store({ + toggleOneUpViewportGridStore: newGridState, +}); +``` + +**After (Zustand):** + +```js +import { useToggleOneUpViewportGridStore } from '../stores/useToggleOneUpViewportGridStore'; +const { toggleOneUpViewportGridStore, setToggleOneUpViewportGridStore } = useToggleOneUpViewportGridStore(); +const previousGridState = toggleOneUpViewportGridStore; // No nested layout property +// ...to update +setToggleOneUpViewportGridStore(newGridState); +``` + +--- + +### `UIStateStore` + + +**Before (StateSyncService):** + +```js +const stateSyncService = servicesManager.services.stateSyncService; +const uiState = stateSyncService.getState().uiStateStore[someUIKey]; +// ...to update +stateSyncService.store({ + uiStateStore: { + ...stateSyncService.getState().uiStateStore, + [someUIKey]: newUIState, + }, +}); +``` + +**After (Zustand):** + +```js +import { useUIStateStore } from '../stores/useUIStateStore'; +const { uiState, setUIState } = useUIStateStore(); +const currentUIState = uiState[someUIKey]; +// ...to update +setUIState(someUIKey, newUIState); +``` + +--- + +### `ViewportsByPositionStore` + + +**Before (StateSyncService):** + +```js +const stateSyncService = servicesManager.services.stateSyncService; +const viewportsByPosition = stateSyncService.getState().viewportsByPosition; +const cachedViewport = viewportsByPosition[positionId]; +// ...to update +stateSyncService.store({ + viewportsByPosition: { + ...viewportsByPosition, + [positionId]: newViewport, + }, +}); +``` + +**After (Zustand):** + +```js +import { useViewportsByPositionStore } from '../stores/useViewportsByPositionStore'; +const { viewportsByPosition, setViewportsByPosition } = useViewportsByPositionStore(); +const cachedViewport = viewportsByPosition[positionId]; +// ...to update +setViewportsByPosition(positionId, newViewport); +``` + +--- + +### `SegmentationPresentationStore` + +**After (Zustand):** + +```js +import { useSegmentationPresentationStore } from '../stores/useSegmentationPresentationStore'; +const { segmentationPresentationStore, setSegmentationPresentation } = + useSegmentationPresentationStore(); +// ...to update +setSegmentationPresentation(presentationId, newSegmentationPresentation); +// You likely have functions within the store like: +// addSegmentationPresentation +// setSegmentationVisibility +// etc. +``` diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/6-RTSTRUCT.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/6-RTSTRUCT.md new file mode 100644 index 000000000..f13bf9130 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/6-RTSTRUCT.md @@ -0,0 +1,18 @@ +--- +id: 6-rtstruct +title: RTSTRUCT +sidebar_position: 6 +--- + + + +# RTStructure Set has transitioned from VTK actors to SVG. + +We have transitioned from VTK-based rendering to SVG-based rendering for RTStructure Set contours. This change should not require any modifications to your codebase. We anticipate improved stability and speed in our contour rendering. + +As a result of this update, viewports rendering RTStructure Sets will no longer convert to volume viewports. Instead, they will remain as stack viewports. + + +Read more in Pull Requests: +- https://github.com/OHIF/Viewers/pull/4074 +- https://github.com/OHIF/Viewers/pull/4157 diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/7-UI.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/7-UI.md new file mode 100644 index 000000000..71e625d93 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/7-UI.md @@ -0,0 +1,149 @@ +--- +title: UI +--- + + + + +## New Components + +You can explore our new playground at `docs.ohif.org/ui` to see the latest components and their properties. We haven't provided a migration guide yet because the old components are still available. Feel free to update your codebase, including custom extensions and UI, to use the new Button, Dropdown, Icons, and other new components from `@ohif/ui-next`. The old methods (importing from `@ohif/ui`) will continue to work for now. However, the new components have a slightly different API, and we plan to deprecate the old components in a future release, as we see the new ones as the future of OHIF. + + + + +## `UINotificationService` + + +We've switched our custom notification service to the Sonner component from https://sonner.emilkowal.ski/ + +### 1. Toast Positions (Kebab-Case) + +Toast positions are now defined using kebab-case instead of camelCase. For instance, `topRight` becomes `top-right`, `bottomRight` becomes `bottom-right`, etc. Ensure your position strings are updated accordingly. + +**Old API:** + +```js +uiNotificationService.show({ + title: 'My Title', + message: 'My Message', + duration: 3000, + position: 'topRight', + type: 'error', + autoClose: true, +}); +``` + + +**New API:** + +```js +uiNotificationService.show({ + title: 'My Title', + message: 'My Message', + duration: 3000, + position: 'top-right', // Note the change to kebab-case + type: 'error', + autoClose: true, +}); +``` + +### 2. Promise Support + +The `show()` method now supports promises, enabling you to display loading notifications and automatically update them based on the promise's resolution or rejection. This significantly simplifies asynchronous operation feedback. + +**Example:** + +```js +const myPromise = someAsyncOperation(); +const notificationId = uiNotificationService.show({ + title: 'Loading Data', + message: 'Fetching data from server...', + type: 'info', + promise: myPromise, + promiseMessages: { + loading: 'Fetching...', + success: (data) => `Data loaded: ${data.length} items`, // Access promise result + error: (error) => `Failed to load data: ${error.message}`, // Access error details + }, +}); +// Optionally hide notification manually if needed +// myPromise.finally(() => uiNotificationService.hide(notificationId)); +``` + +### 3. `hide()` API Change + +The `hide()` method no longer takes an options object. It only accepts the notification ID as a string argument. + +**Old API:** + +```js +uiNotificationService.hide({ id: notificationId }); +``` + +**New API:** + +```js +uiNotificationService.hide(notificationId); +``` + + +--- + + +## Viewport Pane Tailwindcss class + +Previously, when targeting the viewport pane to add custom CSS, you likely used `group-hover:visible` with the viewportPane having a `group` class. + +The naming was confusing as we added more groups, so we renamed it to `group/pane`. Now you can apply `group-hover/pane` for better clarity. + + +--- + +## Header Component + + +Header Component has been refactored in the @ohif/ui-next package. + + +**Before** + + +```js +function Header({ + children, + menuOptions, + isReturnEnabled, + onClickReturnButton, + isSticky, + WhiteLabeling, + showPatientInfo, + servicesManager, + Secondary, + appConfig, + ...props +}: withAppTypes): ReactNode +``` + +**After** + +```js +function Header({ + children, + menuOptions, + isReturnEnabled, + onClickReturnButton, + isSticky, + WhiteLabeling, + PatientInfo, + Secondary, + ...props +}: HeaderProps): ReactNode +``` + +The `PatientInfo` component is now preferred, and the `showPatientInfo` prop has been removed. The previous method depended on `servicesManager`, which was cumbersome because the UI shouldn't need to interact with `servicesManager`. + +All the DropDown and Icons are now in the @ohif/ui-next package. + + +--- diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/8-Refactorings.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/8-Refactorings.md new file mode 100644 index 000000000..795b3e3c6 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/8-Refactorings.md @@ -0,0 +1,120 @@ +--- +title: Refactoring +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + + +## Panel Segmentation + +is now moved from `@ohif/extension-cornerstone-dicom-seg` to `@ohif/extension-cornerstone`. + + +The cornerstone extension now provides the panelSegmentation feature, which was previously part of the cornerstone-dicom-seg extension. This change is logical as panelSegmentation handles more than just DICOM. It can process various formats, including custom formats from the backend and potentially NIFTI format in the future. + + +Before in your modes you were using + +```js +'@ohif/extension-cornerstone-dicom-seg.panelModule.panelSegmentation', +``` + + +Now you should use it via + + +```js +'@ohif/extension-cornerstone.panelModule.panelSegmentation', +``` + +--- + +## `callInputDialog` and `colorPickerDialog` and `showLabelAnnotationPopup` + +Due to the excessive number of `callInputDialog` instances, we centralized them. You can now import them from `@ohif/extension-default`. + + +```js +import { showLabelAnnotationPopup, callInputDialog, colorPickerDialog } from '@ohif/extension-default'; +``` + + +--- + +## disableEditing + +The configuration has moved from appConfig to allow more precise control over component disabling. To disable editing for segmentation and measurements, add the following settings: + + +**Before: ** + +```js +customizationService.addModeCustomizations([ + { + id: 'segmentation.panel', + disableEditing: true, + }, +]); +``` + +**Now ** + +```js +customizationService.addModeCustomizations([ + // To disable editing in the SegmentationTable + { + id: 'PanelSegmentation.disableEditing', + disableEditing: true, + }, + // To disable editing in the MeasurementTable + { + id: 'PanelMeasurement.disableEditing', + disableEditing: true, + }, +]) +``` + + +--- + +## Customization Ids + +The primary reason for this migration is to improve modularity and maintainability in configuration management, as we plan to focus more on the customization service in the near future. + +**Before** + +```js +customizationService.addModeCustomizations([ + { + id: 'segmentation.panel', + segmentationPanelMode: 'expanded', + addSegment: false, + onSegmentationAdd: () => { + commandsManager.run('createNewLabelmapFromPT'); + }, + }, +]); +``` + + +**Now** + +```js +customizationService.addModeCustomizations([ + { + id: 'PanelSegmentation.tableMode', + mode: 'expanded', + }, + { + id: 'PanelSegmentation.onSegmentationAdd', + onSegmentationAdd: () => { + commandsManager.run('createNewLabelmapFromPT'); + }, + }, +]); + +``` diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/9-other.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/9-other.md new file mode 100644 index 000000000..5bd8b735d --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/9-other.md @@ -0,0 +1,99 @@ +--- +title: Other Changes +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + +## External Libraries +Some libraries are loaded via dynamic import. You can provide a global function +`browserImport` the allows loading of dynamic imports without affecting the +webpack build. This import looks like: + +```html + +``` + +and belongs in the root html file for your application. +You then need to remove `dependencies` on the external import, and add a reference +to the external import in your `pluginConfig.json` file. + +### Example plugin config for `dicom-microscopy-viewer` +The example below imports the `dicom-microscopy-viewer` for use as an external +dependency. The example is part of the default `pluginConfig.json` file. + +```json + "public": [ + { + "directory": "./platform/public" + }, + { + "packageName": "dicom-microscopy-viewer", + "importPath": "/dicom-microscopy-viewer/dicomMicroscopyViewer.min.js", + "globalName": "dicomMicroscopyViewer", + "directory": "./node_modules/dicom-microscopy-viewer/dist/dynamic-import" + } + ] +``` + +This defines two directory modules, whose contents are copied unchanged to the +output build directory. It then defines the `dicom-microscopy-viewer` using +the `packageName` element as being a module which is imported dynamically. +Then, the import path passed into the browserImportFunction above is +specified, and then how to access the import itself, via the `window.dicomMicroscopyViewer` +global name reference. + +### Referencing External Imports +The appConfig either defines or has a default peerImport function which can be +used to load references to the modules defined in the pluginConfig file. See +the example in `init.tsx` for the cornerstone extension for how this is passed +into CS3D for loading the whole slide imaging library. + + + +--- + + + +--- + + +--- + + +## Use of ViewReference for navigation +When navigating to measurements and storing/remembering navigation positions, +the `viewport.getViewReference` is used to get a position, and `viewport.isReferenceViewable` +used to check if a reference can be applied, and finally `viewport.setViewReference` to +navigate to a view. Note that this changes the behaviour of navigation between +MPR and Stack viewports, and also enables navigation of video and microscopy +viewports in CS3D. This can cause some unexpected behaviour depending on how the +frame of reference values are configured to allow for navigation. + +The isReferenceViewable is used to determine when a view or measurement can be +shown on a given view. For stack versus volume viewports, this can cause unexpected +behaviour to be seen depending on how the view reference was fetched. + +### `getViewReference` with `forFrameOfReference` +When a view reference is fetched with the for frame of reference flag set to true, +a reference will be returned which can be displayed on any viewport containing +the same frame of reference and encompassing the given FOR and able to display the required +orientation. Without this flag, a view reference is returned which will be +displayed on a stack with the given image id, or a volume containing said image id +or the specified volume. + +### `isReferenceViewable` with navigation and/or orientation +The is reference viewable will return false unless the given reference is directly +viewable in the viewport as is. However, it can be passed various flags to determine +whether the reference could be displayed if the viewport was modified in various ways, +for example, by changing the position or orientation of the viewport. This allows +checking for degrees of closeness so that the correct viewport can be chosen. + +Note that this may result in displaying a measurement from one viewport on a completely +different viewport, for example, showing a Probe tool from the stack viewport on +an MPR view. diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/index.md b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/index.md new file mode 100644 index 000000000..56e414e87 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/3p8-to-3p9/index.md @@ -0,0 +1,7 @@ +--- +id: 3p8-to-3p9 +title: 3.8 -> 3.9 +sidebar_position: 1 +--- + +Here are the changes you need to make to migrate from 3.8 to 3.9. diff --git a/platform/docs/versioned_docs/version-3.8/migration-guide/_category_.json b/platform/docs/versioned_docs/version-3.9/migration-guide/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/migration-guide/_category_.json rename to platform/docs/versioned_docs/version-3.9/migration-guide/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/migration-guide/from-3p7-to-3p8.md b/platform/docs/versioned_docs/version-3.9/migration-guide/from-3p7-to-3p8.md similarity index 64% rename from platform/docs/versioned_docs/version-3.8/migration-guide/from-3p7-to-3p8.md rename to platform/docs/versioned_docs/version-3.9/migration-guide/from-3p7-to-3p8.md index dde25c341..b07d8ffe0 100644 --- a/platform/docs/versioned_docs/version-3.8/migration-guide/from-3p7-to-3p8.md +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/from-3p7-to-3p8.md @@ -1,5 +1,5 @@ --- -sidebar_position: 1 +sidebar_position: 2 sidebar_label: 3.7 -> 3.8 --- @@ -132,6 +132,55 @@ Additional Resources - For more information on the new toolbar module and its usage, refer to the [Toolbar documentation](../platform/extensions/modules/toolbar.md). - Consult the updated button definitions in `modes/longitudinal/src/toolbarButtons.ts` for examples of the new object-based button definition format and the usage of evaluators. +### Tool listeners + +Some tools can be configured to listen to events to trigger, for example + +```ts +createButton({ + id: 'ReferenceLines', + icon: 'tool-referenceLines', + label: 'Reference Lines', + tooltip: 'Show Reference Lines', + commands: 'toggleEnabledDisabledToolbar', + listeners: { + [ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: + ReferenceLinesListeners, + [ViewportGridService.EVENTS.VIEWPORTS_READY]: + ReferenceLinesListeners, + }, + evaluate: 'evaluate.cornerstoneTool.toggle', + }), +``` + +If you have a custom viewport component, and you are overriding the ```onElementEnabled``` handler, than ensure to call ```viewportGridService.setViewportIsReady(viewportId, true)``` in your own handler so that eventually the ```VIEWPORTS_READY``` event fires as expected, if you are not modifying the handler, then an existing handler that is automatically passed down via the props will call that for you, it is passed down from ```ViewportGrid.tsx``` + +```ts + 1 ? viewportLabel : ''} + viewportId={viewportId} + dataSource={dataSource} + viewportOptions={viewportOptions} + displaySetOptions={displaySetOptions} + needsRerendering={displaySetsNeedsRerendering} + isHangingProtocolLayout={isHangingProtocolLayout} + onElementEnabled={() => { + viewportGridService.setViewportIsReady(viewportId, true); + }} +/> + +``` + +## Toolbar Service + +toolbarService.init is not a function. + +**Action Needed** +remove the call to toolbarService.init() from your codebase. + + + ## leftPanelDefaultClosed and rightPanelDefaultClosed Now they are renamed to `leftPanelClosed` and `rightPanelClosed` respectively. @@ -140,3 +189,24 @@ Now they are renamed to `leftPanelClosed` and `rightPanelClosed` respectively. ## StudyInstanceUID in the URL param Previously there were two params that you could choose: seriesInstanceUID and seriesInstanceUIDs, they have been replaced with seriesInstanceUIDs so even if you would like to filter one series use ``seriesInstanceUIDs` + + +## UI + +### Header +Header in @ohif/ui now needs servicesManager and appConfig as input. + + +### Panels +Left and right panel lists are no longer injected into the LayoutTemplate, and have been moved to a PanelService where you have to fetch them from. + +If you're using the main layout, you're fine. However, if you have a custom layout, you'll need to update it. To get the panels, see the + +`extensions/default/src/ViewerLayout/index.tsx` + + + + +## Refactoring + +- TimingEnum (and I guess all enums exported from OHIF core have now moved from Types to Enums export). diff --git a/platform/docs/versioned_docs/version-3.8/migration-guide/from-v2.md b/platform/docs/versioned_docs/version-3.9/migration-guide/from-v2.md similarity index 99% rename from platform/docs/versioned_docs/version-3.8/migration-guide/from-v2.md rename to platform/docs/versioned_docs/version-3.9/migration-guide/from-v2.md index 20ee1afeb..ce953d993 100644 --- a/platform/docs/versioned_docs/version-3.8/migration-guide/from-v2.md +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/from-v2.md @@ -1,5 +1,5 @@ --- -sidebar_position: 2 +sidebar_position: 3 sidebar_label: 2.x -> 3.5 --- @@ -102,7 +102,7 @@ There are various configurations available to customize the viewer. Each configu OHIF v3 has a new configuration structure. The main difference is that the `servers` is renamed to `dataSources` and the configuration is now asynchronous. Datasources are more abstract and far more capable than servers. Read more about dataSources [here](../platform/extensions/modules/data-source.md). -- `StudyPrefetcher` is not currently supported in OHIF v3. +- `StudyPrefetcher` is only available in OHIF v3.9 beta and will be available in the next stable 3.9 release. - The `servers` object has been replaced with a `dataSources` array containing objects representing different data sources. - The cornerstoneExtensionConfig property has been removed, you should use `customizationService` instead (you can read more [here](../platform/services/ui/customization-service.md)) - The maxConcurrentMetadataRequests property has been removed in favor of `maxNumRequests` @@ -209,8 +209,8 @@ Remember that there is no strict rule for deciding between modes and extensions. In OHIF v2 a study was loaded and mounted on `/viewer/:studyInstanceUID` route. In OHIF v3 we have reworked the route registration to enable more sophisticated routing. Now, Modes are tied to specific routes in the viewer, and multiple modes/routes can be present within a single application, making "routes" configuration the most important part of mode configuration. -- Routes with a dataSourceName: `${mode.id}/${dataSourceName}` -- Routes without a dataSourceName: `${mode.id}` which uses the default dataSourceName +- Routes with a dataSourceName: `{mode.id}/{dataSourceName}` +- Routes without a dataSourceName: `{mode.id}` which uses the default dataSourceName This makes a mode flexible enough to be able to connect to multiple datasources without rebuild of the app for use cases such as reading from one PACS and diff --git a/platform/docs/versioned_docs/version-3.9/migration-guide/index.md b/platform/docs/versioned_docs/version-3.9/migration-guide/index.md new file mode 100644 index 000000000..85d2af849 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/migration-guide/index.md @@ -0,0 +1,13 @@ +--- +id: index +--- + + +import DocCardList from '@theme/DocCardList'; +import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; + +# Migration Guides + +Based on the version you are migrating from, you can find the migration guide for the latest version of the platform. + + diff --git a/platform/docs/versioned_docs/version-3.8/platform/_category_.json b/platform/docs/versioned_docs/version-3.9/platform/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/_category_.json rename to platform/docs/versioned_docs/version-3.9/platform/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/platform/browser-support.md b/platform/docs/versioned_docs/version-3.9/platform/browser-support.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/browser-support.md rename to platform/docs/versioned_docs/version-3.9/platform/browser-support.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/environment-variables.md b/platform/docs/versioned_docs/version-3.9/platform/environment-variables.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/environment-variables.md rename to platform/docs/versioned_docs/version-3.9/platform/environment-variables.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/_category_.json b/platform/docs/versioned_docs/version-3.9/platform/extensions/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/_category_.json rename to platform/docs/versioned_docs/version-3.9/platform/extensions/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/extension.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/extension.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/extension.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/extension.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/index.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/index.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/index.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/index.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/installation.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/installation.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/installation.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/installation.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/lifecycle.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/lifecycle.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/lifecycle.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/lifecycle.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/modules/_category_.json b/platform/docs/versioned_docs/version-3.9/platform/extensions/modules/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/modules/_category_.json rename to platform/docs/versioned_docs/version-3.9/platform/extensions/modules/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/modules/commands.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/modules/commands.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/modules/commands.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/modules/commands.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/modules/contextModule.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/modules/contextModule.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/modules/contextModule.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/modules/contextModule.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/modules/data-source.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/modules/data-source.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/modules/data-source.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/modules/data-source.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/modules/hpModule.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/modules/hpModule.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/modules/hpModule.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/modules/hpModule.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/modules/layout-template.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/modules/layout-template.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/modules/layout-template.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/modules/layout-template.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/modules/panel.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/modules/panel.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/modules/panel.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/modules/panel.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/modules/sop-class-handler.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/modules/sop-class-handler.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/modules/sop-class-handler.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/modules/sop-class-handler.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/modules/toolbar.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/modules/toolbar.md similarity index 99% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/modules/toolbar.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/modules/toolbar.md index 94fdb22fc..b353a0304 100644 --- a/platform/docs/versioned_docs/version-3.8/platform/extensions/modules/toolbar.md +++ b/platform/docs/versioned_docs/version-3.9/platform/extensions/modules/toolbar.md @@ -142,7 +142,13 @@ this pattern, where multiple toolbar buttons are using the same evaluator but wi You can choose to set up multiple evaluators for a single button. This comes in handy when you need to assess the button according to various conditions. For example, we aim to prevent the Cine player from showing up on the 3D viewport, so we have: ```js -evaluate: ['evaluate.cine', 'evaluate.not3D'], +evaluate: [ + 'evaluate.cine', + { + name: 'evaluate.viewport.supported', + unsupportedViewportTypes: ['volume3d'], + }, +], ``` You can even come up with advanced evaluators such as: diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/modules/utility.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/modules/utility.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/modules/utility.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/modules/utility.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/extensions/modules/viewport.md b/platform/docs/versioned_docs/version-3.9/platform/extensions/modules/viewport.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/extensions/modules/viewport.md rename to platform/docs/versioned_docs/version-3.9/platform/extensions/modules/viewport.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/internationalization.md b/platform/docs/versioned_docs/version-3.9/platform/internationalization.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/internationalization.md rename to platform/docs/versioned_docs/version-3.9/platform/internationalization.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/managers/_category_.json b/platform/docs/versioned_docs/version-3.9/platform/managers/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/managers/_category_.json rename to platform/docs/versioned_docs/version-3.9/platform/managers/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/platform/managers/commands.md b/platform/docs/versioned_docs/version-3.9/platform/managers/commands.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/managers/commands.md rename to platform/docs/versioned_docs/version-3.9/platform/managers/commands.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/managers/extension.md b/platform/docs/versioned_docs/version-3.9/platform/managers/extension.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/managers/extension.md rename to platform/docs/versioned_docs/version-3.9/platform/managers/extension.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/managers/hotkeys.md b/platform/docs/versioned_docs/version-3.9/platform/managers/hotkeys.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/managers/hotkeys.md rename to platform/docs/versioned_docs/version-3.9/platform/managers/hotkeys.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/managers/index.md b/platform/docs/versioned_docs/version-3.9/platform/managers/index.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/managers/index.md rename to platform/docs/versioned_docs/version-3.9/platform/managers/index.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/managers/service.md b/platform/docs/versioned_docs/version-3.9/platform/managers/service.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/managers/service.md rename to platform/docs/versioned_docs/version-3.9/platform/managers/service.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/modes/_category_.json b/platform/docs/versioned_docs/version-3.9/platform/modes/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/modes/_category_.json rename to platform/docs/versioned_docs/version-3.9/platform/modes/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/platform/modes/index.md b/platform/docs/versioned_docs/version-3.9/platform/modes/index.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/modes/index.md rename to platform/docs/versioned_docs/version-3.9/platform/modes/index.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/modes/installation.md b/platform/docs/versioned_docs/version-3.9/platform/modes/installation.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/modes/installation.md rename to platform/docs/versioned_docs/version-3.9/platform/modes/installation.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/modes/lifecycle.md b/platform/docs/versioned_docs/version-3.9/platform/modes/lifecycle.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/modes/lifecycle.md rename to platform/docs/versioned_docs/version-3.9/platform/modes/lifecycle.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/modes/routes.md b/platform/docs/versioned_docs/version-3.9/platform/modes/routes.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/modes/routes.md rename to platform/docs/versioned_docs/version-3.9/platform/modes/routes.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/modes/validity.md b/platform/docs/versioned_docs/version-3.9/platform/modes/validity.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/modes/validity.md rename to platform/docs/versioned_docs/version-3.9/platform/modes/validity.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/pwa-vs-packaged.md b/platform/docs/versioned_docs/version-3.9/platform/pwa-vs-packaged.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/pwa-vs-packaged.md rename to platform/docs/versioned_docs/version-3.9/platform/pwa-vs-packaged.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/scope-of-project.md b/platform/docs/versioned_docs/version-3.9/platform/scope-of-project.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/scope-of-project.md rename to platform/docs/versioned_docs/version-3.9/platform/scope-of-project.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/_category_.json b/platform/docs/versioned_docs/version-3.9/platform/services/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/_category_.json rename to platform/docs/versioned_docs/version-3.9/platform/services/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/DicomMetadataStore.md b/platform/docs/versioned_docs/version-3.9/platform/services/data/DicomMetadataStore.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/data/DicomMetadataStore.md rename to platform/docs/versioned_docs/version-3.9/platform/services/data/DicomMetadataStore.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/DisplaySetService.md b/platform/docs/versioned_docs/version-3.9/platform/services/data/DisplaySetService.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/data/DisplaySetService.md rename to platform/docs/versioned_docs/version-3.9/platform/services/data/DisplaySetService.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/HangingProtocolService.md b/platform/docs/versioned_docs/version-3.9/platform/services/data/HangingProtocolService.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/data/HangingProtocolService.md rename to platform/docs/versioned_docs/version-3.9/platform/services/data/HangingProtocolService.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/MeasurementService.md b/platform/docs/versioned_docs/version-3.9/platform/services/data/MeasurementService.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/data/MeasurementService.md rename to platform/docs/versioned_docs/version-3.9/platform/services/data/MeasurementService.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/PanelService.md b/platform/docs/versioned_docs/version-3.9/platform/services/data/PanelService.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/data/PanelService.md rename to platform/docs/versioned_docs/version-3.9/platform/services/data/PanelService.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/SegmentationService.md b/platform/docs/versioned_docs/version-3.9/platform/services/data/SegmentationService.md similarity index 84% rename from platform/docs/versioned_docs/version-3.8/platform/services/data/SegmentationService.md rename to platform/docs/versioned_docs/version-3.9/platform/services/data/SegmentationService.md index bd99e12de..31e4b6726 100644 --- a/platform/docs/versioned_docs/version-3.8/platform/services/data/SegmentationService.md +++ b/platform/docs/versioned_docs/version-3.9/platform/services/data/SegmentationService.md @@ -20,11 +20,10 @@ There are seven events that get publish in `MeasurementService`: | Event | Description | | --------------------- | ------------------------------------------------------ | -| SEGMENTATION_UPDATED | Fires when a segmentation is updated e.g., segment added, removed etc.| +| SEGMENTATION_MODIFIED | Fires when a segmentation is updated e.g., segment added, removed etc.| | SEGMENTATION_DATA_MODIFIED | Fires when the segmentation data changes | | SEGMENTATION_ADDED | Fires when a new segmentation is added to OHIF | | SEGMENTATION_REMOVED | Fires when a segmentation is removed from OHIF | -| SEGMENTATION_CONFIGURATION_CHANGED | Fires when a segmentation configuration is changed | | SEGMENT_LOADING_COMPLETE | Fires when a segment group adds its pixel data to the volume | | SEGMENTATION_LOADING_COMPLETE | Fires when the full segmentation volume is filled with its segments | @@ -33,7 +32,7 @@ There are seven events that get publish in `MeasurementService`: ### Segmentation Creation -- `createSegmentationForDisplaySet`: based on a reference displaySet, create a new segmentation. E.g., create a new segmentation based on a CT series +- `createEmptyLabelmapForDisplaySetUID`: based on a reference displaySet, create a new segmentation. E.g., create a new segmentation based on a CT series - `createSegmentationForSEGDisplaySet`: given a segDisplaySet loaded by a sopClassHandler, create a new segmentation - `addSegmentationRepresentationToToolGroup`: given the toolGroupId, add the given segmentationId to the toolGroup. diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/SyncGroupService.md b/platform/docs/versioned_docs/version-3.9/platform/services/data/SyncGroupService.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/data/SyncGroupService.md rename to platform/docs/versioned_docs/version-3.9/platform/services/data/SyncGroupService.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/ToolGroupService.md b/platform/docs/versioned_docs/version-3.9/platform/services/data/ToolGroupService.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/data/ToolGroupService.md rename to platform/docs/versioned_docs/version-3.9/platform/services/data/ToolGroupService.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/ToolbarService.md b/platform/docs/versioned_docs/version-3.9/platform/services/data/ToolbarService.md similarity index 99% rename from platform/docs/versioned_docs/version-3.8/platform/services/data/ToolbarService.md rename to platform/docs/versioned_docs/version-3.9/platform/services/data/ToolbarService.md index 3eb03ffd2..3c8aff777 100644 --- a/platform/docs/versioned_docs/version-3.8/platform/services/data/ToolbarService.md +++ b/platform/docs/versioned_docs/version-3.9/platform/services/data/ToolbarService.md @@ -27,6 +27,8 @@ The `ToolBarService` is a straightforward service designed to handle the toolbar - `addButtons`: add the button definition to the service. [See below for button definition](#button-definitions). +- `removeButton(key)` : remove a button from the toolbar. + - `setButtons`: sets the buttons defined in the service. It overrides all the previous buttons diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/WorkflowStepService.md b/platform/docs/versioned_docs/version-3.9/platform/services/data/WorkflowStepService.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/data/WorkflowStepService.md rename to platform/docs/versioned_docs/version-3.9/platform/services/data/WorkflowStepService.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/_category_.json b/platform/docs/versioned_docs/version-3.9/platform/services/data/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/data/_category_.json rename to platform/docs/versioned_docs/version-3.9/platform/services/data/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/data/index.md b/platform/docs/versioned_docs/version-3.9/platform/services/data/index.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/data/index.md rename to platform/docs/versioned_docs/version-3.9/platform/services/data/index.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/index.md b/platform/docs/versioned_docs/version-3.9/platform/services/index.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/index.md rename to platform/docs/versioned_docs/version-3.9/platform/services/index.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/pubsub.md b/platform/docs/versioned_docs/version-3.9/platform/services/pubsub.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/pubsub.md rename to platform/docs/versioned_docs/version-3.9/platform/services/pubsub.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/ui/_category_.json b/platform/docs/versioned_docs/version-3.9/platform/services/ui/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/ui/_category_.json rename to platform/docs/versioned_docs/version-3.9/platform/services/ui/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/ui/cine-service.md b/platform/docs/versioned_docs/version-3.9/platform/services/ui/cine-service.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/ui/cine-service.md rename to platform/docs/versioned_docs/version-3.9/platform/services/ui/cine-service.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/ui/customization-service.md b/platform/docs/versioned_docs/version-3.9/platform/services/ui/customization-service.md similarity index 73% rename from platform/docs/versioned_docs/version-3.8/platform/services/ui/customization-service.md rename to platform/docs/versioned_docs/version-3.9/platform/services/ui/customization-service.md index 7fc5a60ee..a6baf90f4 100644 --- a/platform/docs/versioned_docs/version-3.8/platform/services/ui/customization-service.md +++ b/platform/docs/versioned_docs/version-3.9/platform/services/ui/customization-service.md @@ -26,6 +26,61 @@ supports customization. (for example, `CustomizableViewportOverlay` component us `CustomizationService` to implement viewport overlay that is easily customizable from configuration.) +## Global, Default and Mode customizations +There are various customization sets that define the lifetime/setup of the +customization. The global customizations are those used for overriding +customizations defined elsewhere, and allow replacing a customization. + +Mode customizations are only registered for the lifetime of the mode, allowing +the mode definition to update/modify the underlying behaviour. This is related +to default customizations, which provide a fallback if the mode or global customization +isn't defined. Default customizations may only be defined once, otherwise throwing +an exception. + +## Append and Merge Customizations +In addition to the replace a customization, there is the ability to merge or append +a customization. The merge customization simply applies the lodash merge functionality +to the existing customization, with the new one, while the append customization +modifies the customization by appending to the value. + +### Append Behaviour +When a list is found in the destination object, the append source object is +examined to see how to handle the change. If the source is simply a list, +then the list object is appended, and no additional changes are performed. +However, if the source is an object other than a list, then the iterable +attributes of the object are examined to match child objects to the destination list, +according to the following table: + +* Natural or zero number value - match the given index location and merge at the point +* Fractional number value - insert at a new point in the list, starting from the end or beginning +* keyword - match a value having the same id as the keyword, inserting at the end, or at _priority as defined in the keywords above. + +#### Example Append + +```javascript +const destination = [ + 1, + {id: 'two', value: 2}, + {id: 'three', value: 3} +] + +const source = { + two: { value: 'updated2' }, + 1: { extraValue: 2 }, + 1.0001: { id: 'inserted', value: 1.0001 }, + -1: { value: -3 }, +} +``` + +Results in two updates to `destination[1]`, the first using an id match on 'two', while the second one +does a positional match on `1`, resulting in the value `{id: 'two', value: 'updated2', extraValue: 2 }` + +Then, it inserts the id 'inserted' after position 1. + +Finally, position -1 (the end position) is updated from value 3 to value -3. + +The ordering is not specified on any of these insertions, so can happen out of order. Use multiple updates to perform order specific inserts. + ## Registering customizable modules (or defining customization prototypes) Extensions and Modes can register customization templates they support. @@ -37,8 +92,8 @@ Below is the protocol of the `getCustomizationModule()`, if defined in Typescrip getCustomizationModule() : { name: string, value: any }[] ``` -If the name is 'default', it is the Default customization, which is loaded -automatically when the extension or mode is loaded. +If the name is 'default', it is the a default customization, while if it +is 'global', then it is a priority/over-riding customization. In the `value` of each customizations, you will define customization prototype(s). These customization prototype(s) can be considered like "Prototype" in Javascript. @@ -346,148 +401,71 @@ customizationService: [ Below is the full example configuration of the customizable viewport overlay and the screenshot of the result overlay. -```javascript -// this is one of the configuration files in `platform/app/public/config/*.js` -window.config = { - // ... - - customizationService: { - cornerstoneOverlayTopLeft: { - id: 'cornerstoneOverlayTopLeft', - items: [ - { - id: 'WindowLevel', - customizationType: 'ohif.overlayItem.windowLevel', - }, - { - id: 'PatientName', - customizationType: 'ohif.overlayItem', - label: '', - color: 'green', - background: 'white', - condition: ({ instance }) => - instance && instance.PatientName && instance.PatientName.Alphabetic, - contentF: ({ instance, formatters: { formatPN } }) => - formatPN(instance.PatientName.Alphabetic) + - ' ' + - (instance.PatientSex ? '(' + instance.PatientSex + ')' : ''), - }, - { - id: 'Species', - customizationType: 'ohif.overlayItem', - label: 'Species:', - condition: ({ instance }) => - instance && instance.PatientSpeciesDescription, - contentF: ({ instance }) => - instance.PatientSpeciesDescription + - '/' + - instance.PatientBreedDescription, - }, - { - id: 'PID', - customizationType: 'ohif.overlayItem', - label: 'PID:', - title: 'Patient PID', - condition: ({ instance }) => instance && instance.PatientID, - contentF: ({ instance }) => instance.PatientID, - }, - { - id: 'PatientBirthDate', - customizationType: 'ohif.overlayItem', - label: 'DOB:', - title: "Patient's Date of birth", - condition: ({ instance }) => instance && instance.PatientBirthDate, - contentF: ({ instance }) => instance.PatientBirthDate, - }, - { - id: 'OtherPid', - customizationType: 'ohif.overlayItem', - label: 'Other PID:', - title: 'Other Patient IDs', - condition: ({ instance }) => instance && instance.OtherPatientIDs, - contentF: ({ instance, formatters: { formatPN } }) => - formatPN(instance.OtherPatientIDs), - }, - ], - }, - cornerstoneOverlayTopRight: { - id: 'cornerstoneOverlayTopRight', - - items: [ - { - id: 'InstanceNmber', - customizationType: 'ohif.overlayItem.instanceNumber', - }, - { - id: 'StudyDescription', - customizationType: 'ohif.overlayItem', - label: '', - title: ({ instance }) => - instance && - instance.StudyDescription && - `Study Description: ${instance.StudyDescription}`, - condition: ({ instance }) => instance && instance.StudyDescription, - contentF: ({ instance }) => instance.StudyDescription, - }, - { - id: 'StudyDate', - customizationType: 'ohif.overlayItem', - label: '', - title: 'Study date', - condition: ({ instance }) => instance && instance.StudyDate, - contentF: ({ instance, formatters: { formatDate } }) => - formatDate(instance.StudyDate), - }, - { - id: 'StudyTime', - customizationType: 'ohif.overlayItem', - label: '', - title: 'Study time', - condition: ({ instance }) => instance && instance.StudyTime, - contentF: ({ instance, formatters: { formatTime } }) => - formatTime(instance.StudyTime), - }, - ], - }, - cornerstoneOverlayBottomLeft: { - id: 'cornerstoneOverlayBottomLeft', - - items: [ - { - id: 'SeriesNumber', - customizationType: 'ohif.overlayItem', - label: 'Ser:', - title: 'Series Number', - condition: ({ instance }) => instance && instance.SeriesNumber, - contentF: ({ instance }) => instance.SeriesNumber, - }, - { - id: 'SliceLocation', - customizationType: 'ohif.overlayItem', - label: 'Loc:', - title: 'Slice Location', - condition: ({ instance }) => instance && instance.SliceLocation, - contentF: ({ instance, formatters: { formatNumberPrecision } }) => - formatNumberPrecision(instance.SliceLocation, 2) + ' mm', - }, - { - id: 'SliceThickness', - customizationType: 'ohif.overlayItem', - label: 'Thick:', - title: 'Slice Thickness', - condition: ({ instance }) => instance && instance.SliceThickness, - contentF: ({ instance, formatters: { formatNumberPrecision } }) => - formatNumberPrecision(instance.SliceThickness, 2) + ' mm', - }, - ], - }, - }, - - // ... -} +There are working examples that can be run with: +``` +set APP_CONFIG=config/customization.js +yarn dev ``` - +```javascript +// this is part of customization.js, an example customization dataset +window.config = { + + // This shows how to append to the customization data + customizationService: [ + { + id: '@ohif/cornerstoneOverlay', + // Append recursively, rather than replacing + merge: 'Append', + topRightItems: { + id: 'cornerstoneOverlayTopRight', + items: [ + { + id: 'PatientNameOverlay', + // Note below that here we are using the customization prototype of + // `ohif.overlayItem` which was registered to the customization module in + // `ohif/extension-default` extension. + customizationType: 'ohif.overlayItem', + // the following props are passed to the `ohif.overlayItem` prototype + // which is used to render the overlay item based on the label, color, + // conditions, etc. + attribute: 'PatientName', + label: 'PN:', + title: 'Patient Name', + color: 'yellow', + condition: ({ instance }) => instance?.PatientName, + contentF: ({ instance, formatters: { formatPN } }) => + formatPN(instance.PatientName) + + (instance.PatientSex ? ' (' + instance.PatientSex + ')' : ''), + }, + ], + }, + + topLeftItems: { + items: { + // Note the -10000 means -10000 + length of existing list, which is + // much before the start of hte list, so put the new value at the start. + '-10000': + { + id: 'Species', + customizationType: 'ohif.overlayItem', + label: 'Species:', + color: 'red', + background: 'green', + condition: ({ instance }) => + instance?.PatientSpeciesDescription, + contentF: ({ instance }) => + instance.PatientSpeciesDescription + + '/' + + instance.PatientBreedDescription, + }, + }, + }, + }, +... +``` + + ## Context Menus diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/ui/index.md b/platform/docs/versioned_docs/version-3.9/platform/services/ui/index.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/ui/index.md rename to platform/docs/versioned_docs/version-3.9/platform/services/ui/index.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/ui/ui-dialog-service.md b/platform/docs/versioned_docs/version-3.9/platform/services/ui/ui-dialog-service.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/ui/ui-dialog-service.md rename to platform/docs/versioned_docs/version-3.9/platform/services/ui/ui-dialog-service.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/ui/ui-modal-service.md b/platform/docs/versioned_docs/version-3.9/platform/services/ui/ui-modal-service.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/ui/ui-modal-service.md rename to platform/docs/versioned_docs/version-3.9/platform/services/ui/ui-modal-service.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/ui/ui-notification-service.md b/platform/docs/versioned_docs/version-3.9/platform/services/ui/ui-notification-service.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/ui/ui-notification-service.md rename to platform/docs/versioned_docs/version-3.9/platform/services/ui/ui-notification-service.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/ui/ui-viewport-dialog-service.md b/platform/docs/versioned_docs/version-3.9/platform/services/ui/ui-viewport-dialog-service.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/ui/ui-viewport-dialog-service.md rename to platform/docs/versioned_docs/version-3.9/platform/services/ui/ui-viewport-dialog-service.md diff --git a/platform/docs/versioned_docs/version-3.9/platform/services/ui/viewport-action-menu.md b/platform/docs/versioned_docs/version-3.9/platform/services/ui/viewport-action-menu.md new file mode 100644 index 000000000..5a0592535 --- /dev/null +++ b/platform/docs/versioned_docs/version-3.9/platform/services/ui/viewport-action-menu.md @@ -0,0 +1,30 @@ +--- +sidebar_position: 8 +sidebar_label: Viewport Action Corners +--- + +# Viewport Action Corners Service + +The Viewport Action Corners Service is a powerful tool for managing interactive components in the corners of viewports within the OHIF viewer. This service allows developers to dynamically add, remove, and organize various UI elements such as menus, buttons, or custom components in specific locations around the viewport. + +## Overview + +The Viewport Action Corners Service extends the PubSubService and provides methods to: + +- Add single or multiple components to viewport corners +- Clear components from a specific viewport +- Manage the state of viewport corner components + +## Key Features + +- **Flexible Positioning**: Components can be placed in top-left, top-right, bottom-left, or bottom-right corners of the viewport. +- **Priority Ordering**: Components can be assigned priority indices for ordering within a corner. +- **Viewport-Specific**: Actions are associated with specific viewports, allowing for individualized control. +- **Dynamic Updates**: Components can be added or removed at runtime, enabling context-sensitive UI elements. + +## Usage + +To use the Viewport Action Corners Service, you typically interact with it through the `servicesManager`. Here's a basic example of how to add a component: + + +Take a look at how we add window level menu to the top right corner of the viewport in the `OHIFCornerstoneViewport` component. diff --git a/platform/docs/versioned_docs/version-3.8/platform/services/ui/viewport-grid-service.md b/platform/docs/versioned_docs/version-3.9/platform/services/ui/viewport-grid-service.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/services/ui/viewport-grid-service.md rename to platform/docs/versioned_docs/version-3.9/platform/services/ui/viewport-grid-service.md diff --git a/platform/docs/versioned_docs/version-3.8/platform/themeing.md b/platform/docs/versioned_docs/version-3.9/platform/themeing.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/platform/themeing.md rename to platform/docs/versioned_docs/version-3.9/platform/themeing.md diff --git a/platform/docs/versioned_docs/version-3.8/release-notes.md b/platform/docs/versioned_docs/version-3.9/release-notes.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/release-notes.md rename to platform/docs/versioned_docs/version-3.9/release-notes.md diff --git a/platform/docs/versioned_docs/version-3.8/resources.md b/platform/docs/versioned_docs/version-3.9/resources.md similarity index 99% rename from platform/docs/versioned_docs/version-3.8/resources.md rename to platform/docs/versioned_docs/version-3.9/resources.md index 1fad4361f..4c60e18fb 100644 --- a/platform/docs/versioned_docs/version-3.8/resources.md +++ b/platform/docs/versioned_docs/version-3.9/resources.md @@ -1,5 +1,5 @@ --- -sidebar_position: 9 +sidebar_position: 13 sidebar_label: Resources --- @@ -9,7 +9,7 @@ Throughout the development of the OHIF Viewer, we have participated in various conferences and "hackathons". In this page, we will provide the presentations and other resources that we have provided to the community in the past: -## 2023 +## 2024 ### IMNO 2024 - March 19-20, 2024 @@ -20,6 +20,7 @@ We participated in the Imaging Network Ontario (ImNO) 2024 symposium, presenting - Advancing Medical Imaging on the Web: Optimizing the Dicomweb Server Architecture with Static Dicomweb [Poster](https://www.dropbox.com/scl/fi/ep0lxjp90kbxhjoffe4kh/P7-10-Bill-Wallace-Final.pdf?rlkey=xl2u6tdnh9j9hgvkajxv3b02o&dl=0) - (**🏆🏆 BEST PRESENTATION AWARD in the Session 7 Pitches: Devices, HW, SW Development 🏆🏆**) Advancing Medical Imaging on the Web: Integrating High Throughput JPEG 2000 (HTJ2K) in Cornerstone3D for Streamlined Progressive Loading and Visualization [Poster](https://www.dropbox.com/scl/fi/srs2rxgtv2r69ver9ub1j/P7-8-Bill-Wallace-Final.pdf?rlkey=k9mmraw76r9q2s3b9w9s0793w&dl=0) +## 2023 ### ITCR 2023 Conference | September 11-13, 2023 diff --git a/platform/docs/versioned_docs/version-3.8/user-guide/_category_.json b/platform/docs/versioned_docs/version-3.9/user-guide/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/user-guide/_category_.json rename to platform/docs/versioned_docs/version-3.9/user-guide/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/user-guide/index.md b/platform/docs/versioned_docs/version-3.9/user-guide/index.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/user-guide/index.md rename to platform/docs/versioned_docs/version-3.9/user-guide/index.md diff --git a/platform/docs/versioned_docs/version-3.8/user-guide/viewer/Language.md b/platform/docs/versioned_docs/version-3.9/user-guide/viewer/Language.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/user-guide/viewer/Language.md rename to platform/docs/versioned_docs/version-3.9/user-guide/viewer/Language.md diff --git a/platform/docs/versioned_docs/version-3.8/user-guide/viewer/_category_.json b/platform/docs/versioned_docs/version-3.9/user-guide/viewer/_category_.json similarity index 100% rename from platform/docs/versioned_docs/version-3.8/user-guide/viewer/_category_.json rename to platform/docs/versioned_docs/version-3.9/user-guide/viewer/_category_.json diff --git a/platform/docs/versioned_docs/version-3.8/user-guide/viewer/hotkeys.md b/platform/docs/versioned_docs/version-3.9/user-guide/viewer/hotkeys.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/user-guide/viewer/hotkeys.md rename to platform/docs/versioned_docs/version-3.9/user-guide/viewer/hotkeys.md diff --git a/platform/docs/versioned_docs/version-3.8/user-guide/viewer/index.md b/platform/docs/versioned_docs/version-3.9/user-guide/viewer/index.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/user-guide/viewer/index.md rename to platform/docs/versioned_docs/version-3.9/user-guide/viewer/index.md diff --git a/platform/docs/versioned_docs/version-3.8/user-guide/viewer/measurement-panel.md b/platform/docs/versioned_docs/version-3.9/user-guide/viewer/measurement-panel.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/user-guide/viewer/measurement-panel.md rename to platform/docs/versioned_docs/version-3.9/user-guide/viewer/measurement-panel.md diff --git a/platform/docs/versioned_docs/version-3.8/user-guide/viewer/measurement-tracking.md b/platform/docs/versioned_docs/version-3.9/user-guide/viewer/measurement-tracking.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/user-guide/viewer/measurement-tracking.md rename to platform/docs/versioned_docs/version-3.9/user-guide/viewer/measurement-tracking.md diff --git a/platform/docs/versioned_docs/version-3.8/user-guide/viewer/study-panel.md b/platform/docs/versioned_docs/version-3.9/user-guide/viewer/study-panel.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/user-guide/viewer/study-panel.md rename to platform/docs/versioned_docs/version-3.9/user-guide/viewer/study-panel.md diff --git a/platform/docs/versioned_docs/version-3.8/user-guide/viewer/toolbar.md b/platform/docs/versioned_docs/version-3.9/user-guide/viewer/toolbar.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/user-guide/viewer/toolbar.md rename to platform/docs/versioned_docs/version-3.9/user-guide/viewer/toolbar.md diff --git a/platform/docs/versioned_docs/version-3.8/user-guide/viewer/viewport.md b/platform/docs/versioned_docs/version-3.9/user-guide/viewer/viewport.md similarity index 100% rename from platform/docs/versioned_docs/version-3.8/user-guide/viewer/viewport.md rename to platform/docs/versioned_docs/version-3.9/user-guide/viewer/viewport.md diff --git a/platform/docs/versioned_sidebars/version-3.9-sidebars.json b/platform/docs/versioned_sidebars/version-3.9-sidebars.json new file mode 100644 index 000000000..caea0c03b --- /dev/null +++ b/platform/docs/versioned_sidebars/version-3.9-sidebars.json @@ -0,0 +1,8 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ] +} diff --git a/platform/docs/versions.json b/platform/docs/versions.json index 1becfc009..2c682a57e 100644 --- a/platform/docs/versions.json +++ b/platform/docs/versions.json @@ -1 +1 @@ -["3.8"] +["3.9"] diff --git a/platform/docs/versionsArchived.json b/platform/docs/versionsArchived.json deleted file mode 100644 index 07805e0e8..000000000 --- a/platform/docs/versionsArchived.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "Version 2.0": "https://deploy-preview-2791--ohif-platform-docs.netlify.app/1.0/", - "Version 1.0": "https://deploy-preview-2791--ohif-platform-docs.netlify.app/1.0/" -} diff --git a/version.mjs b/version.mjs index aa00aa267..17de99025 100644 --- a/version.mjs +++ b/version.mjs @@ -23,8 +23,8 @@ async function run() { console.log('Branch: release'); await fs.writeFile('./commit.txt', currentCommitHash); const version = await fs.readFile('./version.txt', 'utf-8'); - console.log('Version from version.txt:', version.trim()); - return; + nextVersion = version.trim(); + console.log('Version from version.txt:', nextVersion); } else { console.log('Branch: master'); const prereleaseComponents = semver.prerelease(currentVersion); diff --git a/yarn.lock b/yarn.lock index cfe857ef1..bf1b65507 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2806,10 +2806,10 @@ "@docsearch/css" "3.6.2" algoliasearch "^4.19.1" -"@docusaurus/babel@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/babel/-/babel-3.6.0.tgz#735a003207925bd782dd08ffa5d8b3503c1f8d72" - integrity sha512-7CsoQFiadoq7AHSUIQNkI/lGfg9AQ2ZBzsf9BqfZGXkHwWDy6twuohEaG0PgQv1npSRSAB2dioVxhRSErnqKNA== +"@docusaurus/babel@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/babel/-/babel-3.6.1.tgz#5f48a275934b8164ccac3a6fd1fca3741374c884" + integrity sha512-JcKaunW8Ml2nTnfnvFc55T00Y+aCpNWnf1KY/gG+wWxHYDH0IdXOOz+k6NAlEAerW8+VYLfUqRIqHZ7N/DVXvQ== dependencies: "@babel/core" "^7.25.9" "@babel/generator" "^7.25.9" @@ -2821,23 +2821,23 @@ "@babel/runtime" "^7.25.9" "@babel/runtime-corejs3" "^7.25.9" "@babel/traverse" "^7.25.9" - "@docusaurus/logger" "3.6.0" - "@docusaurus/utils" "3.6.0" + "@docusaurus/logger" "3.6.1" + "@docusaurus/utils" "3.6.1" babel-plugin-dynamic-import-node "^2.3.3" fs-extra "^11.1.1" tslib "^2.6.0" -"@docusaurus/bundler@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/bundler/-/bundler-3.6.0.tgz#bdd060ba4d009211348e4e973a3bf4861cf0996b" - integrity sha512-o5T9HXkPKH0OQAifTxEXaebcO8kaz3tU1+wlIShZ2DKJHlsyWX3N4rToWBHroWnV/ZCT2XN3kLRzXASqrnb9Tw== +"@docusaurus/bundler@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/bundler/-/bundler-3.6.1.tgz#240343d31f39638f987caf54793c09820270ecd5" + integrity sha512-vHSEx8Ku9x/gfIC6k4xb8J2nTxagLia0KvZkPZhxfkD1+n8i+Dj4BZPWTmv+kCA17RbgAvECG0XRZ0/ZEspQBQ== dependencies: "@babel/core" "^7.25.9" - "@docusaurus/babel" "3.6.0" - "@docusaurus/cssnano-preset" "3.6.0" - "@docusaurus/logger" "3.6.0" - "@docusaurus/types" "3.6.0" - "@docusaurus/utils" "3.6.0" + "@docusaurus/babel" "3.6.1" + "@docusaurus/cssnano-preset" "3.6.1" + "@docusaurus/logger" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils" "3.6.1" autoprefixer "^10.4.14" babel-loader "^9.2.1" clean-css "^5.3.2" @@ -2858,18 +2858,18 @@ webpack "^5.95.0" webpackbar "^6.0.1" -"@docusaurus/core@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.6.0.tgz#b23fc7e253a49cc3e5ac9e091354f497cc0b101b" - integrity sha512-lvRgMoKJJSRDt9+HhAqFcICV4kp/mw1cJJrLxIw4Q2XZnFGM1XUuwcbuaqWmGog+NcOLZaPCcCtZbn60EMCtjQ== +"@docusaurus/core@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-3.6.1.tgz#260d78e1eb7129ccb441fa944b5f7e6f492ac6cb" + integrity sha512-cDKxPihiM2z7G+4QtpTczS7uxNfNG6naSqM65OmAJET0CFRHbc9mDlLFtQF0lsVES91SHqfcGaaLZmi2FjdwWA== dependencies: - "@docusaurus/babel" "3.6.0" - "@docusaurus/bundler" "3.6.0" - "@docusaurus/logger" "3.6.0" - "@docusaurus/mdx-loader" "3.6.0" - "@docusaurus/utils" "3.6.0" - "@docusaurus/utils-common" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/babel" "3.6.1" + "@docusaurus/bundler" "3.6.1" + "@docusaurus/logger" "3.6.1" + "@docusaurus/mdx-loader" "3.6.1" + "@docusaurus/utils" "3.6.1" + "@docusaurus/utils-common" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" boxen "^6.2.1" chalk "^4.1.2" chokidar "^3.5.3" @@ -2907,21 +2907,22 @@ webpack-dev-server "^4.15.2" webpack-merge "^6.0.1" -"@docusaurus/cssnano-preset@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.6.0.tgz#02378e53e9568ed5fc8871d4fc158ea96fd7421c" - integrity sha512-h3jlOXqqzNSoU+C4CZLNpFtD+v2xr1UBf4idZpwMgqid9r6lb5GS7tWKnQnauio6OipacbHbDXEX3JyT1PlDkg== +"@docusaurus/cssnano-preset@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/cssnano-preset/-/cssnano-preset-3.6.1.tgz#dc07b15f37d5c7bc1e59255ce0fa8825dde2dfb7" + integrity sha512-ZxYUmNeyQHW2w4/PJ7d07jQDuxzmKr9uPAQ6IVe5dTkeIeV0mDBB3jOLeJkNoI42Ru9JKEqQ9aVDtM9ct6QHnw== dependencies: cssnano-preset-advanced "^6.1.2" postcss "^8.4.38" postcss-sort-media-queries "^5.2.0" tslib "^2.6.0" -"@docusaurus/faster@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/faster/-/faster-3.6.0.tgz#70540e689fd16e26e6105fe689a1347b8ac93834" - integrity sha512-9X06/KHD3f4CKm0SjZQIK3UDE/XvouAMiAlYMPV5LprubFlxVQecZG9QnfOe7VIfUh9IuPiDPuGg17yy0efwww== +"@docusaurus/faster@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/faster/-/faster-3.6.1.tgz#f564abe9904c4132ca32164012f8427270c257c8" + integrity sha512-W3a9m7Q/fEeOpOw9/XktLCHRtp1sV2AdZWMCjH3kP1jY1TDyLFFiHJ0+1uwVpOw4/oPJqZSTRKP+IdW4+65NgQ== dependencies: + "@docusaurus/types" "3.6.1" "@rspack/core" "^1.0.14" "@swc/core" "^1.7.39" "@swc/html" "^1.7.39" @@ -2931,33 +2932,33 @@ tslib "^2.6.0" webpack "^5.95.0" -"@docusaurus/logger@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.6.0.tgz#c7349c2636087f55f573a60a3c7f69b87d59974d" - integrity sha512-BcQhoXilXW0607cH/kO6P5Gt5KxCGfoJ+QDKNf3yO2S09/RsITlW+0QljXPbI3DklTrHrhRDmgGk1yX4nUhWTA== +"@docusaurus/logger@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/logger/-/logger-3.6.1.tgz#724b7f9d8c435c9933d52792458659471ec90919" + integrity sha512-OvetI/nnOMBSqCkUzKAQhnIjhxduECK4qTu3tq/8/h/qqvLsvKURojm04WPE54L+Uy+UXMas0hnbBJd8zDlEOw== dependencies: chalk "^4.1.2" tslib "^2.6.0" -"@docusaurus/lqip-loader@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/lqip-loader/-/lqip-loader-3.6.0.tgz#c40cbe74690455e5c65749d4a9a2bc00806a91a9" - integrity sha512-jEQjnKAjhgyZ/aQf0Tbl3eDpp1j0C/GojE/cv2Bad89o+luV1OAOs9UHDRyKwWQfN+se0D9kFxkfsfwYxMpI8A== +"@docusaurus/lqip-loader@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/lqip-loader/-/lqip-loader-3.6.1.tgz#b5a4956b8906b2087e2f9429c63ddaff184f8f77" + integrity sha512-H/VVvnvFupFhQ81FuTyA/XHxEZPKh99T6Wg6KgN+/yvcn7869RdgrlDhKDnXZ7j2u80eFsVNjAcPfW1cSAtK6A== dependencies: - "@docusaurus/logger" "3.6.0" + "@docusaurus/logger" "3.6.1" file-loader "^6.2.0" lodash "^4.17.21" sharp "^0.32.3" tslib "^2.6.0" -"@docusaurus/mdx-loader@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.6.0.tgz#f8ba7af9d59473a7182f6a9307e0432f8dce905b" - integrity sha512-GhRzL1Af/AdSSrGesSPOU/iP/aXadTGmVKuysCxZDrQR2RtBtubQZ9aw+KvdFVV7R4K/CsbgD6J5oqrXlEPk3Q== +"@docusaurus/mdx-loader@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/mdx-loader/-/mdx-loader-3.6.1.tgz#6482e6f2f32ccab4a74d8b64d7eeec4fdf9be475" + integrity sha512-KPIsYi0S3X3/rNrW3V1fgOu5t6ahYWc31zTHHod8pacFxdmk9Uf6uuw+Jd6Cly1ilgal+41Ku+s0gmMuqKqiqg== dependencies: - "@docusaurus/logger" "3.6.0" - "@docusaurus/utils" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/logger" "3.6.1" + "@docusaurus/utils" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" "@mdx-js/mdx" "^3.0.0" "@slorber/remark-comment" "^1.0.0" escape-html "^1.0.3" @@ -2994,12 +2995,12 @@ react-helmet-async "*" react-loadable "npm:@docusaurus/react-loadable@5.5.2" -"@docusaurus/module-type-aliases@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.6.0.tgz#44083c34a53db1dde06364b4e7f2d144fa2d5394" - integrity sha512-szTrIN/6/fuk0xkf3XbRfdTFJzRQ8d1s3sQj5++58wltrT7v3yn1149oc9ryYjMpRcbsarGloQwMu7ofPe4XPg== +"@docusaurus/module-type-aliases@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/module-type-aliases/-/module-type-aliases-3.6.1.tgz#2780e19411d0c8b16d46a032eae9e60e742ae681" + integrity sha512-J+q1jgm7TnEfVIUZImSFeLA1rghb6nwtoB9siHdcgKpDqFJ9/S7xhQL2aEKE7iZMZYzpu+2F390E9A7GkdEJNA== dependencies: - "@docusaurus/types" "3.6.0" + "@docusaurus/types" "3.6.1" "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" @@ -3007,34 +3008,34 @@ react-helmet-async "*" react-loadable "npm:@docusaurus/react-loadable@6.0.0" -"@docusaurus/plugin-client-redirects@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.6.0.tgz#54155826e9e5da625e296eb93e346c11c1541a78" - integrity sha512-LIqRT6dtgxdENQH8XYwOOyxfKuzFD1ayJmIDCp9Yi/rbdcPE4vvTcESLGXOKvcyWZSfyCu+JA8Tyk4qpp+2J0w== +"@docusaurus/plugin-client-redirects@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.6.1.tgz#a035708ff13234a30dbaa84526ab12a102e00d07" + integrity sha512-gY1LEQw4zSLSPNVxZk9mDj5ct+sJNux0SiY1PbMiI9sK8xXqkTC7QuRyrxRPelSZX9K4nyhK8PjYbVXAN+GUWQ== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/logger" "3.6.0" - "@docusaurus/utils" "3.6.0" - "@docusaurus/utils-common" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/logger" "3.6.1" + "@docusaurus/utils" "3.6.1" + "@docusaurus/utils-common" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" eta "^2.2.0" fs-extra "^11.1.1" lodash "^4.17.21" tslib "^2.6.0" -"@docusaurus/plugin-content-blog@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.6.0.tgz#9128175b4c3ce885d9090183d74c60813844ea8d" - integrity sha512-o4aT1/E0Ldpzs/hQff5uyoSriAhS/yqBhqSn+fvSw465AaqRsva6O7CZSYleuBq6x2bewyE3QJq2PcTiHhAd8g== +"@docusaurus/plugin-content-blog@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.6.1.tgz#1127d35e1a443e87f9674f955acf7864bc62bfed" + integrity sha512-FUmsn3xg/XD/K/4FQd8XHrs92aQdZO5LUtpHnRvO1/6DY87SMz6B6ERAN9IGQQld//M2/LVTHkZy8oVhQZQHIQ== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/logger" "3.6.0" - "@docusaurus/mdx-loader" "3.6.0" - "@docusaurus/theme-common" "3.6.0" - "@docusaurus/types" "3.6.0" - "@docusaurus/utils" "3.6.0" - "@docusaurus/utils-common" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/logger" "3.6.1" + "@docusaurus/mdx-loader" "3.6.1" + "@docusaurus/theme-common" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils" "3.6.1" + "@docusaurus/utils-common" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" cheerio "1.0.0-rc.12" feed "^4.2.2" fs-extra "^11.1.1" @@ -3046,20 +3047,20 @@ utility-types "^3.10.0" webpack "^5.88.1" -"@docusaurus/plugin-content-docs@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.6.0.tgz#15cae4bf81da0b0ddce09d53b10b7209116ea9c2" - integrity sha512-c5gZOxocJKO/Zev2MEZInli+b+VNswDGuKHE6QtFgidhAJonwjh2kwj967RvWFaMMk62HlLJLZ+IGK2XsVy4Aw== +"@docusaurus/plugin-content-docs@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.6.1.tgz#7c461b33ecc41e27fc02830bcde54378f68b2512" + integrity sha512-Uq8kyn5DYCDmkUlB9sWChhWghS4lUFNiQU+RXcAXJ3qCVXsBpPsh6RF+npQG1N+j4wAbjydM1iLLJJzp+x3eMQ== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/logger" "3.6.0" - "@docusaurus/mdx-loader" "3.6.0" - "@docusaurus/module-type-aliases" "3.6.0" - "@docusaurus/theme-common" "3.6.0" - "@docusaurus/types" "3.6.0" - "@docusaurus/utils" "3.6.0" - "@docusaurus/utils-common" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/logger" "3.6.1" + "@docusaurus/mdx-loader" "3.6.1" + "@docusaurus/module-type-aliases" "3.6.1" + "@docusaurus/theme-common" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils" "3.6.1" + "@docusaurus/utils-common" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" "@types/react-router-config" "^5.0.7" combine-promises "^1.1.0" fs-extra "^11.1.1" @@ -3069,95 +3070,95 @@ utility-types "^3.10.0" webpack "^5.88.1" -"@docusaurus/plugin-content-pages@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.6.0.tgz#5dd284bf063baaba1e0305c90b1dd0d5acc7e466" - integrity sha512-RKHhJrfkadHc7+tt1cP48NWifOrhkSRMPdXNYytzhoQrXlP6Ph+3tfQ4/n+nT0S3Y9+wwRxYqRqA380ZLt+QtQ== +"@docusaurus/plugin-content-pages@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.6.1.tgz#7d3dcdcc49e3c31ed13dceab830ee7fd9a1c4658" + integrity sha512-TZtL+2zq20gqGalzoIT2rEF1T4YCZ26jTvlCJXs78+incIajfdHtmdOq7rQW0oV7oqTjpGllbp788nY/vY9jgw== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/mdx-loader" "3.6.0" - "@docusaurus/types" "3.6.0" - "@docusaurus/utils" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/mdx-loader" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" fs-extra "^11.1.1" tslib "^2.6.0" webpack "^5.88.1" -"@docusaurus/plugin-debug@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.6.0.tgz#0a6da9ba31a0acb176ae2762b4d6b96b1906c826" - integrity sha512-o8T1Rl94COLdSlKvjYLQpRJQRU8WWZ8EX1B0yV0dQLNN8reyH7MQW+6z1ig4sQFfH3pnjPWVGHfuEjcib5m7Eg== +"@docusaurus/plugin-debug@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-debug/-/plugin-debug-3.6.1.tgz#e73fca0307b864b000c98b7110009c6b4a3efc2b" + integrity sha512-DeKPZtoVExDSYCbzoz7y5Dhc6+YPqRWfVGwEEUyKopSyQYefp0OV8hvASmbJCn2WyThRgspOUhog3FSEhz+agw== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/types" "3.6.0" - "@docusaurus/utils" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils" "3.6.1" fs-extra "^11.1.1" react-json-view-lite "^1.2.0" tslib "^2.6.0" -"@docusaurus/plugin-google-analytics@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.6.0.tgz#9e8245eef1bee95e44ef2af92ce3e844a8e93e64" - integrity sha512-kgRFbfpi6Hshj75YUztKyEMtI/kw0trPRwoTN4g+W1NK99R/vh8phTvhBTIMnDbetU79795LkwfG0rZ/ce6zWQ== +"@docusaurus/plugin-google-analytics@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.6.1.tgz#f20027f75cd45830eefcd7a172ded8b15de0b516" + integrity sha512-ZEoERiDHxSfhaEeT35ukQ892NzGHWiUvfxUsnPiRuGEhMoQlxMSp60shBuSZ1sUKuZlndoEl5qAXJg09Wls/Sg== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/types" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" tslib "^2.6.0" -"@docusaurus/plugin-google-gtag@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.6.0.tgz#bed8381fe3ab357d56a565f657e38d8ea6272703" - integrity sha512-nqu4IfjaO4UX+dojHL2BxHRS+sKj31CIMWYo49huQ3wTET0Oc3u/WGTaKd3ShTPDhkgiRhTOSTPUwJWrU55nHg== +"@docusaurus/plugin-google-gtag@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.6.1.tgz#db08bfcef319c494e5969c0d96d62bd8f43469db" + integrity sha512-u/E9vXUsZxYaV6Brvfee8NiH/iR0cMml9P/ifz4EpH/Jfxdbw8rbCT0Nm/h7EFgEY48Uqkl5huSbIvFB9n8aTQ== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/types" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" "@types/gtag.js" "^0.0.12" tslib "^2.6.0" -"@docusaurus/plugin-google-tag-manager@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.6.0.tgz#326382de05888ea4317837be736eabd635adbc71" - integrity sha512-OU6c5xI0nOVbEc9eImGvvsgNWe4vGm97t/W3aLHjWsHyNk3uwFNBQMHRvBUwAi9k/K3kyC5E7DWnc67REhdLOw== +"@docusaurus/plugin-google-tag-manager@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.6.1.tgz#f3aa3cd0e7e6be793e5af3fe048a6ad12c3f0211" + integrity sha512-By+NKkGYV8tSo8/RyS1OXikOtqsko5jJZ/uioJfBjsBGgSbiMJ+Y/HogFBke0mgSvf7NPGKZTbYm5+FJ8YUtPQ== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/types" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" tslib "^2.6.0" -"@docusaurus/plugin-ideal-image@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-ideal-image/-/plugin-ideal-image-3.6.0.tgz#41ffed294bd18ef7f9d4bf0d2d248a4bb890d62a" - integrity sha512-6BaZU7mX+RH+lRFPm5nj3GGfAsjsJ1JHjp0Xjb4ilfte7L2IZkTl6MPpvAzyPC0qIUxfXRcJs4qd/HUE0xcTlw== +"@docusaurus/plugin-ideal-image@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-ideal-image/-/plugin-ideal-image-3.6.1.tgz#3c0bb8e398ef0b7ea1ee313ee1e774de93f2b941" + integrity sha512-hiGRPPlsM02aEOPlQc9rVnrckbVR6HswG7yDpZOtBEhw+ysXFsl/8gzAxFBL4ogKjN28WrlMCn/6IIkxY/EyOQ== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/lqip-loader" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/lqip-loader" "3.6.1" "@docusaurus/responsive-loader" "^1.7.0" - "@docusaurus/theme-translations" "3.6.0" - "@docusaurus/types" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/theme-translations" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" "@slorber/react-ideal-image" "^0.0.12" react-waypoint "^10.3.0" sharp "^0.32.3" tslib "^2.6.0" webpack "^5.88.1" -"@docusaurus/plugin-pwa@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-pwa/-/plugin-pwa-3.6.0.tgz#9851d1dd52bf8bf75f7d97e9f42cb6e29cacb68d" - integrity sha512-IVUJIBeGwbIkrI8HgVkw3eonw23wq5EjPOioXzht4Dql1Bb13xF/SmO6Bj8NE2KNmlubOKRKwLy8gYpB3i8cmQ== +"@docusaurus/plugin-pwa@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-pwa/-/plugin-pwa-3.6.1.tgz#541501c814d1b5d9681f8a55224e24b121ab7c75" + integrity sha512-GsbpsgpAwdBpDpeVzk3UPNUQAc0oGU+0GL5s1LmFgqfpql5C7ZnkwD9FmW6lvieabQjC+Ga0d0zDqu20coqIRQ== dependencies: "@babel/core" "^7.25.9" "@babel/preset-env" "^7.25.9" - "@docusaurus/bundler" "3.6.0" - "@docusaurus/core" "3.6.0" - "@docusaurus/logger" "3.6.0" - "@docusaurus/theme-common" "3.6.0" - "@docusaurus/theme-translations" "3.6.0" - "@docusaurus/types" "3.6.0" - "@docusaurus/utils" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/bundler" "3.6.1" + "@docusaurus/core" "3.6.1" + "@docusaurus/logger" "3.6.1" + "@docusaurus/theme-common" "3.6.1" + "@docusaurus/theme-translations" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" babel-loader "^9.2.1" clsx "^2.0.0" core-js "^3.31.1" @@ -3168,39 +3169,39 @@ workbox-precaching "^7.0.0" workbox-window "^7.0.0" -"@docusaurus/plugin-sitemap@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.6.0.tgz#c7c93f75f03391ca9071da48563fc4faa84966bc" - integrity sha512-YB5XMdf9FjLhgbHY/cDbYhVxsgcpPIjxY9769HUgFOB7GVzItTLOR71W035R1BiR2CA5QAn3XOSg36WLRxlhQQ== +"@docusaurus/plugin-sitemap@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.6.1.tgz#c842019a64d8dd12b64145115e60771d482db997" + integrity sha512-i8R/GTKew4Cufb+7YQTwfPcNOhKTJzZ1VZ5OqQwI9c3pZK2TltQyhqKDVN94KCTbSSKvOYYytYfRAB2uPnH1/A== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/logger" "3.6.0" - "@docusaurus/types" "3.6.0" - "@docusaurus/utils" "3.6.0" - "@docusaurus/utils-common" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/logger" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils" "3.6.1" + "@docusaurus/utils-common" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" fs-extra "^11.1.1" sitemap "^7.1.1" tslib "^2.6.0" -"@docusaurus/preset-classic@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.6.0.tgz#71561f366a266be571022764eb8b9e5618f573eb" - integrity sha512-kpGNdQzr/Dpm7o3b1iaQrz4DMDx3WIeBbl4V4P4maa2zAQkTdlaP4CMgA5oKrRrpqPLnQFsUM/b+qf2glhl2Tw== +"@docusaurus/preset-classic@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-3.6.1.tgz#196540ca8495075d24eb724af3bf4c75d2412754" + integrity sha512-b90Y1XRH9e+oa/E3NmiFEFOwgYUd+knFcZUy81nM3FJs038WbEA0T55NQsuPW0s7nOsCShQ7dVFyKxV+Wp31Nw== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/plugin-content-blog" "3.6.0" - "@docusaurus/plugin-content-docs" "3.6.0" - "@docusaurus/plugin-content-pages" "3.6.0" - "@docusaurus/plugin-debug" "3.6.0" - "@docusaurus/plugin-google-analytics" "3.6.0" - "@docusaurus/plugin-google-gtag" "3.6.0" - "@docusaurus/plugin-google-tag-manager" "3.6.0" - "@docusaurus/plugin-sitemap" "3.6.0" - "@docusaurus/theme-classic" "3.6.0" - "@docusaurus/theme-common" "3.6.0" - "@docusaurus/theme-search-algolia" "3.6.0" - "@docusaurus/types" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/plugin-content-blog" "3.6.1" + "@docusaurus/plugin-content-docs" "3.6.1" + "@docusaurus/plugin-content-pages" "3.6.1" + "@docusaurus/plugin-debug" "3.6.1" + "@docusaurus/plugin-google-analytics" "3.6.1" + "@docusaurus/plugin-google-gtag" "3.6.1" + "@docusaurus/plugin-google-tag-manager" "3.6.1" + "@docusaurus/plugin-sitemap" "3.6.1" + "@docusaurus/theme-classic" "3.6.1" + "@docusaurus/theme-common" "3.6.1" + "@docusaurus/theme-search-algolia" "3.6.1" + "@docusaurus/types" "3.6.1" "@docusaurus/react-loadable@5.5.2": version "5.5.2" @@ -3210,10 +3211,10 @@ "@types/react" "*" prop-types "^15.6.2" -"@docusaurus/remark-plugin-npm2yarn@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/remark-plugin-npm2yarn/-/remark-plugin-npm2yarn-3.6.0.tgz#b0e287c88ded88de576f1e2eb74d466062d73c35" - integrity sha512-Imh0v68hFWSWlcE7qLlnZD9r4bQOzM6rcWTh6mPpfx2zHAai+so+SJmrMwZ1AJGfC08Y7Ch2Q/G4AThalXRVqQ== +"@docusaurus/remark-plugin-npm2yarn@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/remark-plugin-npm2yarn/-/remark-plugin-npm2yarn-3.6.1.tgz#9098b41101fe99214ac837954ef6365c046f909f" + integrity sha512-ve8xXfyB4oQwtYMgZoFxWWyp/aa3mobt4jLRjNHwRLQ6Jy7bMmFQ7uX99ExDttyGme2ABX4/vDNYfXDRdKm91Q== dependencies: mdast-util-mdx "^3.0.0" npm-to-yarn "^3.0.0" @@ -3228,24 +3229,24 @@ dependencies: loader-utils "^2.0.0" -"@docusaurus/theme-classic@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.6.0.tgz#8f34b65c85f5082deb3633a893974d2eee309121" - integrity sha512-sAXNfwPL6uRD+BuHuKXZfAXud7SS7IK/JdrPuzyQxdO1gJKzI5GFfe1ED1QoJDNWJWJ01JHE5rSnwYLEADc2rQ== +"@docusaurus/theme-classic@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-classic/-/theme-classic-3.6.1.tgz#dff4c7732b590e231dfb764e1d9a8acb5cf28954" + integrity sha512-5lVUmIXk7zp+n9Ki2lYWrmhbd6mssOlKCnnDJvY4QDi3EgjRisIu5g4yKXoWTIbiqE7m7q/dS9cbeShEtfkKng== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/logger" "3.6.0" - "@docusaurus/mdx-loader" "3.6.0" - "@docusaurus/module-type-aliases" "3.6.0" - "@docusaurus/plugin-content-blog" "3.6.0" - "@docusaurus/plugin-content-docs" "3.6.0" - "@docusaurus/plugin-content-pages" "3.6.0" - "@docusaurus/theme-common" "3.6.0" - "@docusaurus/theme-translations" "3.6.0" - "@docusaurus/types" "3.6.0" - "@docusaurus/utils" "3.6.0" - "@docusaurus/utils-common" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/logger" "3.6.1" + "@docusaurus/mdx-loader" "3.6.1" + "@docusaurus/module-type-aliases" "3.6.1" + "@docusaurus/plugin-content-blog" "3.6.1" + "@docusaurus/plugin-content-docs" "3.6.1" + "@docusaurus/plugin-content-pages" "3.6.1" + "@docusaurus/theme-common" "3.6.1" + "@docusaurus/theme-translations" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils" "3.6.1" + "@docusaurus/utils-common" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" "@mdx-js/react" "^3.0.0" clsx "^2.0.0" copy-text-to-clipboard "^3.2.0" @@ -3260,15 +3261,15 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-common@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.6.0.tgz#9a061d278df76da0f70a9465cd0b7299c14d03d3" - integrity sha512-frjlYE5sRs+GuPs4XXlp9aMLI2O4H5FPpznDAXBrCm+8EpWRiIb443ePMxM3IyMCQ5bwFlki0PI9C+r4apstnw== +"@docusaurus/theme-common@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-common/-/theme-common-3.6.1.tgz#d160516db9482ab19f7921d8a75093885d04d3de" + integrity sha512-18iEYNpMvarGfq9gVRpGowSZD24vZ39Iz4acqaj64180i54V9el8tVnhNr/wRvrUm1FY30A1NHLqnMnDz4rYEQ== dependencies: - "@docusaurus/mdx-loader" "3.6.0" - "@docusaurus/module-type-aliases" "3.6.0" - "@docusaurus/utils" "3.6.0" - "@docusaurus/utils-common" "3.6.0" + "@docusaurus/mdx-loader" "3.6.1" + "@docusaurus/module-type-aliases" "3.6.1" + "@docusaurus/utils" "3.6.1" + "@docusaurus/utils-common" "3.6.1" "@types/history" "^4.7.11" "@types/react" "*" "@types/react-router-config" "*" @@ -3278,34 +3279,34 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-live-codeblock@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-live-codeblock/-/theme-live-codeblock-3.6.0.tgz#de670b60c9acfdf03feb79e32a3be0d90fe11647" - integrity sha512-EphCTxJZgnEvgA1kdbr+R/cBu3fdawA2ooQji8EKPKg9i4Uex0xfFGhWja/0SaMKGqmfHRz9SY8I7j75O3cluw== +"@docusaurus/theme-live-codeblock@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-live-codeblock/-/theme-live-codeblock-3.6.1.tgz#748f2864df9ad2cdbb4f370b816ad1dc2d2bc175" + integrity sha512-8GdnkwaNxb0dmRHGDlO+5QXdz9mrLni/wqai06LSGOTRv+GY3JC2Zov/KKkHZxx5MNFagqq01sGdn1TBBrS9jg== dependencies: - "@docusaurus/core" "3.6.0" - "@docusaurus/theme-common" "3.6.0" - "@docusaurus/theme-translations" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/theme-common" "3.6.1" + "@docusaurus/theme-translations" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" "@philpl/buble" "^0.19.7" clsx "^2.0.0" fs-extra "^11.1.1" react-live "^4.1.6" tslib "^2.6.0" -"@docusaurus/theme-search-algolia@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.6.0.tgz#47dcfca68f50163abce411dd9b181855a9ec9c83" - integrity sha512-4IwRUkxjrisR8LXBHeE4d2btraWdMficbgiVL3UHvJURmyvgzMBZQP8KrK8rjdXeu8SuRxSmeV6NSVomRvdbEg== +"@docusaurus/theme-search-algolia@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.6.1.tgz#a9cc9c9517a22459354703cf33d469e7263d3854" + integrity sha512-BjmuiFRpQP1WEm8Mzu1Bb0Wdas6G65VHXDDNr7XTKgbstxalE6vuxt0ioXTDFS2YVep5748aVhKvnxR9gm2Liw== dependencies: "@docsearch/react" "^3.5.2" - "@docusaurus/core" "3.6.0" - "@docusaurus/logger" "3.6.0" - "@docusaurus/plugin-content-docs" "3.6.0" - "@docusaurus/theme-common" "3.6.0" - "@docusaurus/theme-translations" "3.6.0" - "@docusaurus/utils" "3.6.0" - "@docusaurus/utils-validation" "3.6.0" + "@docusaurus/core" "3.6.1" + "@docusaurus/logger" "3.6.1" + "@docusaurus/plugin-content-docs" "3.6.1" + "@docusaurus/theme-common" "3.6.1" + "@docusaurus/theme-translations" "3.6.1" + "@docusaurus/utils" "3.6.1" + "@docusaurus/utils-validation" "3.6.1" algoliasearch "^4.18.0" algoliasearch-helper "^3.13.3" clsx "^2.0.0" @@ -3315,10 +3316,10 @@ tslib "^2.6.0" utility-types "^3.10.0" -"@docusaurus/theme-translations@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.6.0.tgz#93994e931f340c1712c81ac80dbab5750c24634f" - integrity sha512-L555X8lWE3fv8VaF0Bc1VnAgi10UvRKFcvADHiYR7Gj37ItaWP5i7xLHsSw7fi/SHTXe5wfIeCFNqUYHyCOHAQ== +"@docusaurus/theme-translations@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/theme-translations/-/theme-translations-3.6.1.tgz#d6bbb20141ca70f352201f2f412b6b8d988d86b4" + integrity sha512-bNm5G6sueUezvyhsBegA1wwM38yW0BnqpZTE9KHO2yKnkERNMaV5x/yPJ/DNCOHjJtCcJ5Uz55g2AS75Go31xA== dependencies: fs-extra "^11.1.1" tslib "^2.6.0" @@ -3342,10 +3343,10 @@ webpack "^5.88.1" webpack-merge "^5.9.0" -"@docusaurus/types@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.6.0.tgz#8fa82332a7c7b8093b5c55e1115f5854ce484978" - integrity sha512-jADLgoZGWhAzThr+mRiyuFD4OUzt6jHnb7NRArRKorgxckqUBaPyFOau9hhbcSTHtU6ceyeWjN7FDt7uG2Hplw== +"@docusaurus/types@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-3.6.1.tgz#0e55a0a51a3e55658b0845af83d5fe17c495978e" + integrity sha512-hCB1hj9DYutVYBisnPNobz9SzEmCcf1EetJv09O49Cov3BqOkm+vnnjB3d957YJMtpLGQoKBeN/FF1DZ830JwQ== dependencies: "@mdx-js/mdx" "^3.0.0" "@types/history" "^4.7.11" @@ -3357,34 +3358,36 @@ webpack "^5.95.0" webpack-merge "^5.9.0" -"@docusaurus/utils-common@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.6.0.tgz#11855ea503132bbcaba6ca4d351293ff10a75d34" - integrity sha512-diUDNfbw33GaZMmKwdTckT2IBfVouXLXRD+zphH9ywswuaEIKqixvuf5g41H7MBBrlMsxhna3uTMoB4B/OPDcA== +"@docusaurus/utils-common@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-common/-/utils-common-3.6.1.tgz#994160470e6bd2c0eb771f2132883d21b3b6830f" + integrity sha512-LX1qiTiC0aS8c92uZ+Wj2iNCNJyYZJIKY8/nZDKNMBfo759VYVS3RX3fKP3DznB+16sYp7++MyCz/T6fOGaRfw== dependencies: + "@docusaurus/types" "3.6.1" tslib "^2.6.0" -"@docusaurus/utils-validation@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.6.0.tgz#5557ca14fa64ac29e6f70e61006be721395ecde5" - integrity sha512-CRHiKKJEKA0GFlfOf71JWHl7PtwOyX0+Zg9ep9NFEZv6Lcx3RJ9nhl7p8HRjPL6deyYceavM//BsfW4pCI4BtA== +"@docusaurus/utils-validation@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/utils-validation/-/utils-validation-3.6.1.tgz#8e4b5bd8b71f55228543e3fda1301e9fb83df1c6" + integrity sha512-+iMd6zRl5cJQm7nUP+7pSO/oAXsN79eHO34ME7l2YJt4GEAr70l5kkD58u2jEPpp+wSXT70c7x2A2lzJI1E8jw== dependencies: - "@docusaurus/logger" "3.6.0" - "@docusaurus/utils" "3.6.0" - "@docusaurus/utils-common" "3.6.0" + "@docusaurus/logger" "3.6.1" + "@docusaurus/utils" "3.6.1" + "@docusaurus/utils-common" "3.6.1" fs-extra "^11.2.0" joi "^17.9.2" js-yaml "^4.1.0" lodash "^4.17.21" tslib "^2.6.0" -"@docusaurus/utils@3.6.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.6.0.tgz#192785da6fd62dfd83d6f1879c3aa45547f5df23" - integrity sha512-VKczAutI4mptiAw/WcYEu5WeVhQ6Q1zdIUl64SGw9K++9lziH+Kt10Ee8l2dMpRkiUk6zzK20kMNlX2WCUwXYQ== +"@docusaurus/utils@3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@docusaurus/utils/-/utils-3.6.1.tgz#4e4f80be294671bfb83910352d3625878134bf48" + integrity sha512-nS3WCvepwrnBEgSG5vQu40XG95lC9Jeh/odV5u5IhU1eQFEGDst9xBi6IK5yZdsGvbuaXBZLZtOqWYtuuFa/rQ== dependencies: - "@docusaurus/logger" "3.6.0" - "@docusaurus/utils-common" "3.6.0" + "@docusaurus/logger" "3.6.1" + "@docusaurus/types" "3.6.1" + "@docusaurus/utils-common" "3.6.1" "@svgr/webpack" "^8.1.0" escape-string-regexp "^4.0.0" file-loader "^6.2.0"