Compare commits

...
Author SHA1 Message Date
ohif-bot 027ef4f93c chore(release): publish [skip ci]
- @ohif/extension-dicom-html@1.2.1
 - @ohif/extension-dicom-rt@0.1.1
 - @ohif/extension-dicom-segmentation@0.2.2
 - @ohif/extension-vtk@1.5.1
 - @ohif/core@2.9.1
 - @ohif/viewer@3.11.2
2020-04-28 10:52:19 +00:00
Khalid Williams f09ebbe2e8 chore: cleanup dcmjs imports
* Update imports in /platform

* Update dcmjs imports in /extentions
2020-04-28 06:49:57 -04:00
David Connaughtonandeolasd 51b4e60161 docs: updated deployment section with info for using JSON (#1676)
* update deploy section with non-dicomweb info

* fix typo and add info re. case sensitive json

* Add CORS info and reformat inline links

Co-authored-by: eolasd <pacsradwp@gmail.com>
2020-04-27 16:11:35 -04:00
ohif-bot 08de987711 chore(release): publish [skip ci]
- @ohif/extension-dicom-segmentation@0.2.1
 - @ohif/viewer@3.11.1
2020-04-27 08:41:40 +00:00
Igor Octaviano 20e366ffbd fix: 🐛 Dropdown not updating when selecting segment (#1675)
Observe selectedSegmentation changes to refresh segment list

✅ Closes: #1669
2020-04-27 10:39:11 +02:00
ohif-bot 561911056a chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.7.0
 - @ohif/extension-dicom-html@1.2.0
 - @ohif/extension-dicom-rt@0.1.0
 - @ohif/extension-dicom-segmentation@0.2.0
 - @ohif/extension-vtk@1.5.0
 - @ohif/core@2.9.0
 - @ohif/viewer@3.11.0
2020-04-24 10:29:19 +00:00
Igor OctavianoandJames Petts 835f64d47a feat: 🎸 Seg jump to slice + show/hide
* Add segment part 2 (jump to frame and visibility toggle)

* Cr updates

* Filter displaysets with images

* feat: 🎸 Seg jump to slice + show/hide

Co-authored-by: James Petts <jamesapetts@gmail.com>
2020-04-24 12:25:05 +02:00
ohif-bot fc40d84092 chore(release): publish [skip ci]
- @ohif/viewer@3.10.2
2020-04-23 16:20:51 +00:00
Danny Brown 709f14708e fix: undefined errorHandler in cornerstoneWadoImageLoader configuration (#1664) 2020-04-23 12:17:49 -04:00
ohif-bot 5f4c4ab5e7 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.6.1
 - @ohif/extension-vtk@1.4.1
 - @ohif/core@2.8.1
 - @ohif/viewer@3.10.1
2020-04-23 10:04:47 +00:00
James Petts 71205618ec fix: 🐛 Multiframe fix (#1661) 2020-04-23 12:02:51 +02:00
ohif-bot 5014ba864b chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.6.0
 - @ohif/extension-dicom-p10-downloader@0.1.0
 - @ohif/extension-vtk@1.4.0
 - @ohif/core@2.8.0
 - @ohif/viewer@3.10.0
2020-04-23 02:06:44 +00:00
James Pettsanddannyrb e96205de35 feat: configuration to hook into XHR Error handling
* Error handling.

* Fix error handling, add redirect example.

* remove debugger

* docs: whitelabeling and httpErrorHandler

* bump dicomweb-client version

* bump cornerstone-wado-image-loader version

* Add more documentation around httpErrorHandler callback

* test: add errorHandler as an expected top level export

Co-authored-by: dannyrb <danny.ri.brown@gmail.com>
2020-04-22 22:01:34 -04:00
ohif-bot 273f31ad56 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.3.14
 - @ohif/core@2.7.1
 - @ohif/viewer@3.9.2
2020-04-22 20:19:12 +00:00
Danny Brown 2093a00365 fix: whiteLabeling should support component creation by passing React to defined fn (#1659) 2020-04-22 16:16:53 -04:00
dependabot[bot] b1dad6b61b chore(deps): bump https-proxy-agent from 2.2.1 to 2.2.4 in /docs/latest (#1641)
Bumps [https-proxy-agent](https://github.com/TooTallNate/node-https-proxy-agent) from 2.2.1 to 2.2.4.
- [Release notes](https://github.com/TooTallNate/node-https-proxy-agent/releases)
- [Commits](https://github.com/TooTallNate/node-https-proxy-agent/compare/2.2.1...2.2.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-04-18 22:05:44 -04:00
ohif-bot 3a587f4e4b chore(release): publish [skip ci]
- @ohif/viewer@3.9.1
2020-04-17 16:28:57 +00:00
Danny Brown d9fc7bbb0e fix: showStudyList config (#1647)
* fix: `showStudyList` config

* showStudyList should default to true if not defined
2020-04-17 12:26:09 -04:00
73 changed files with 1154 additions and 462 deletions

No files matched your search

+100 -3
View File
@@ -127,10 +127,107 @@ appropriate headers. You can find an example of this setup in our
#### What if my archive doesn't support DicomWeb?
> This is possible to do with the OHIF Viewer, but not as straightforward. Look
> out for documentation on this subject in the near future.
It's possible to supply all Study data via JSON format, in the event you do not have a DicomWeb endpoint.
You can host all of the relevant files on any web accessible server (Amazon S3, Azure Blob Storage, Local file server etc.)
This JSON is supplied via the '?url=' query parameter.
It should reference an endpoint that returns **application/json** formatted text.
If you do not have an API, you can simply return a text file containing the JSON from any web server.
You tell the OHIF viewer to use JSON by appending the `'?url='` query to the `/Viewer` route:
eg. `https://my-test-ohif-server/viewer?url=https://my-json-server/study-uid.json`
The returned JSON object must contain a single root object with a 'studies' array.
*Sample JSON format:*
```JSON
{
"studies": [
{
"StudyInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.78",
"StudyDescription": "BRAIN SELLA",
"StudyDate": "20010108",
"StudyTime": "120022",
"PatientName": "MISTER^MR",
"PatientId": "832040",
"series": [
{
"SeriesDescription": "SAG T-1",
"SeriesInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.121",
"SeriesNumber": 2,
"SeriesDate": "20010108",
"SeriesTime": "120318",
"Modality": "MR",
"instances": [
{
"metadata": {
"Columns": 512,
"Rows": 512,
"InstanceNumber": 3,
"AcquisitionNumber": 0,
"PhotometricInterpretation": "MONOCHROME2",
"BitsAllocated": 16,
"BitsStored": 16,
"PixelRepresentation": 1,
"SamplesPerPixel": 1,
"PixelSpacing": [0.390625, 0.390625],
"HighBit": 15,
"ImageOrientationPatient": [0,1,0,0,0,-1],
"ImagePositionPatient": [11.600000,-92.500000, 98.099998],
"FrameOfReferenceUID": "1.2.840.113619.2.5.1762583153.223134.978956938.470",
"ImageType": ["ORIGINAL","PRIMARY","OTHER"],
"Modality": "MR",
"SOPInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.124",
"SeriesInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.121",
"StudyInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.78"
},
"url": "dicomweb://s3.amazonaws.com/lury/MRStudy/1.2.840.113619.2.5.1762583153.215519.978957063.124.dcm"
}
]
}
]
}
]
}
```
More info on this JSON format can be found here [Issue #1500](https://github.com/OHIF/Viewers/issues/1500)
**Implementation Notes:**
1. When hosting the viewer, you will also need to host a /viewer route on the server - or the browser may not be able to find the route.
2. For each instance url (dicom object) in the returned JSON, you must prefix the `url` with `dicomweb:` in order for the cornerstone image loader to retrieve it correctly.
eg. `https://image-server/my-image.dcm` ---> `dicomweb:https://image-server/my-image.dcm`
3. The JSON format above is compatible with >= v3.7.8 of the application. Older versions of the viewer used a different JSON format. As of 20/04/20 the public [https://viewer.ohif.org/] is a pre 3.0 version that does not support this format yet.
4. The JSON format is case-sensitive. Please ensure you have matched casing with the naturalised Dicom format referenced in [Issue #1500](https://github.com/OHIF/Viewers/issues/1500).
*CORS Issues (Cross-Origin Resource Sharing)*
If you host a JSON API or Images on a different domain from the the app itself, you will likely have CORS issues. This will also happen when testing from Localhost and reaching out to remote servers.
Even if the domain is the same, different ports, subdomains or protocols (https vs http) will also cause CORS errors.
You will to need add a configuration on each server hosting these assets to allow your App server origin.
For example:
Lets assume your application is hosted on `https://my-ohif-server.com`.
Your JSON API is hosted on `https://my-json-api.aws.com`
And your images are stored on Amazon S3 at `https://my-s3-bucket.aws.com`
When you first start your application, browsing to `https://my-ohif-server.com/viewer?url=https://my-json-api.aws.com/api/my-json-study-info.json`, you will likely get a CORS error in the browser console as it tries to connect to `https://my-json-api.aws.com`.
Adding a setting on the JSON server to allow the CORS origin = `https://my-ohif-server.com` should solve this.
Next, you will likely get a similar CORS error, as the browser tries to go to `https://my-s3-bucket.aws.com`.
You will need to go to the S3 bucket configuration, and add a CORS setting to allow origin = `https://my-ohif-server.com`.
Essentially, whenever the application connects to a remote resource, you will need to add the applications url to the allowed CORS Origins on that resource. Adding an origin similar to https://localhost:3000 will also allow for local testing.
...
### Securing Your Data
+53 -1
View File
@@ -11,7 +11,59 @@ are:
```js
window.config = {
routerBasename: '/',
whiteLabelling: {},
/**
* "White Labeling" is used to change the branding, look, and feel of the OHIF
* Viewer. These settings, and the color variables that are used by our components,
* are the easiest way to rebrand the application.
*
* More extensive changes are made possible through swapping out the UI library,
* Viewer project, or extensions.
*/
whiteLabeling: {
/* Optional: Should return a React component to be rendered in the "Logo" section of the application's Top Navigation bar */
createLogoComponentFn: function(React) {
return React.createElement('a', {
target: '_self',
rel: 'noopener noreferrer',
className: 'header-brand',
href: '/',
style: {
display: 'block',
textIndent: '-9999px',
background: 'url(/svg-file-hosted-at-domain-root.svg)',
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
width: '200px',
},
});
},
},
/**
* Internally, the OHIF Viewer fetches data primarily with the
* `cornerstoneWADOImageLoader` and the `DICOMWebClient`. If either of these
* receive a non-200 response, this method allows you to handle that error.
*
* Common use cases include:
* - Showing a notification with the UINotificationService
* - Redirecting the user
* - Refreshing an auth token
*
* @param {Object} error - JS new Error()
* @param {XMLHttpRequest} error.request - The XHR request that's onreadystate change triggered this callback
* @param {string} error.response - The XHR's response property
* @param {number} error.status - The XHR's status property
*/
httpErrorHandler: error => {
const { request: xhr, response, status } = err;
const { responseType, statusText } = xhr;
// In local files, status is 0 upon success in Firefox
if (xhr.readyState === XMLHttpRequest.DONE) {
console.log(statusText, response, responseType);
} else {
console.warn('Likely CORS error');
}
},
extensions: [],
showStudyList: true,
filterQueryParam: false,
+4 -4
View File
@@ -66,11 +66,11 @@ open to pull requests and discussion issues.
Current white-labeling options are limited. We expose the ability to replace the
"Logo" section of the application with a custom "Logo" component. You can do
this by adding a `whiteLabelling` key to your
this by adding a `whiteLabeling` key to your
[configuration file](./configuration.md).
```js
function RadicalImagingLogo() {
function RadicalImagingLogo(React) {
return React.createElement(
'a',
{
@@ -83,8 +83,8 @@ function RadicalImagingLogo() {
);
}
props.whiteLabelling = {
logoComponent: RadicalImagingLogo(),
props.whiteLabeling = {
createLogoComponentFn: RadicalImagingLogo,
};
```
+17 -12
View File
@@ -20,10 +20,10 @@ abbrev@~1.0.9:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU=
agent-base@4, agent-base@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==
agent-base@4, agent-base@^4.1.0, agent-base@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
dependencies:
es6-promisify "^5.0.0"
@@ -773,9 +773,9 @@ err-code@^1.0.0:
prr "~1.0.1"
es6-promise@^4.0.3:
version "4.2.6"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f"
integrity sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==
version "4.2.8"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
es6-promisify@^5.0.0:
version "5.0.0"
@@ -1289,11 +1289,11 @@ http-signature@~1.2.0:
sshpk "^1.7.0"
https-proxy-agent@^2.1.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==
version "2.2.4"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
dependencies:
agent-base "^4.1.0"
agent-base "^4.3.0"
debug "^3.1.0"
humanize-ms@^1.2.1:
@@ -1807,11 +1807,16 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@^2.0.0, ms@^2.1.1:
ms@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
mute-stream@~0.0.4:
version "0.0.8"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
+33
View File
@@ -3,6 +3,39 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.7.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.6.1...@ohif/extension-cornerstone@2.7.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## [2.6.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.6.0...@ohif/extension-cornerstone@2.6.1) (2020-04-23)
### Bug Fixes
* 🐛 Multiframe fix ([#1661](https://github.com/OHIF/Viewers/issues/1661)) ([7120561](https://github.com/OHIF/Viewers/commit/71205618ecb8b592247c5acb32284bfe7e18fce5))
# [2.6.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.5.2...@ohif/extension-cornerstone@2.6.0) (2020-04-23)
### Features
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
## [2.5.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.5.1...@ohif/extension-cornerstone@2.5.2) (2020-04-09)
**Note:** Version bump only for package @ohif/extension-cornerstone
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
"version": "2.5.2",
"version": "2.7.0",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
@@ -35,8 +35,8 @@
"cornerstone-core": "^2.2.8",
"cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.12.5",
"cornerstone-wado-image-loader": "^3.0.0",
"dcmjs": "^0.10.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "^0.12.2",
"dicom-parser": "^1.8.3",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
+1 -1
View File
@@ -267,7 +267,7 @@ const commandsModule = ({ servicesManager }) => {
const study = studyMetadataManager.get(StudyInstanceUID);
const displaySet = study.findDisplaySet(ds => {
return ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID)
return ds.images && ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID)
});
displaySet.SOPInstanceUID = SOPInstanceUID;
@@ -2,6 +2,9 @@ import Hammer from 'hammerjs';
import cornerstone from 'cornerstone-core';
import cornerstoneMath from 'cornerstone-math';
import cornerstoneTools from 'cornerstone-tools';
import OHIF from '@ohif/core';
const { log } = OHIF;
export default function(configuration = {}) {
// For debugging
@@ -12,6 +15,13 @@ export default function(configuration = {}) {
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
cornerstoneTools.init(configuration);
cornerstoneTools.loadHandlerManager.setErrorLoadingHandler(
(element, imageId, error) => {
log.error(imageId);
throw error;
}
);
// Set the tool font and font size
// context.font = "[style] [variant] [weight] [size]/[line height] [font family]";
const fontFamily =
+19
View File
@@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.0...@ohif/extension-dicom-html@1.2.1) (2020-04-28)
**Note:** Version bump only for package @ohif/extension-dicom-html
# [1.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.1.2...@ohif/extension-dicom-html@1.2.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## [1.1.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.1.1...@ohif/extension-dicom-html@1.1.2) (2020-04-02)
**Note:** Version bump only for package @ohif/extension-dicom-html
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-html",
"version": "1.1.2",
"version": "1.2.1",
"description": "OHIF extension for rendering structured reports to HTML",
"author": "OHIF",
"license": "MIT",
@@ -29,7 +29,7 @@
},
"peerDependencies": {
"@ohif/core": "^0.50.0",
"dcmjs": "^0.10.1",
"dcmjs": "^0.12.2",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import * as dcmjs from 'dcmjs';
import dcmjs from 'dcmjs';
import TypedArrayProp from './TypedArrayProp';
import './DicomHtmlViewport.css';
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [0.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.0.2...@ohif/extension-dicom-p10-downloader@0.1.0) (2020-04-23)
### Features
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
## 0.0.2 (2020-04-15)
**Note:** Version bump only for package @ohif/extension-dicom-p10-downloader
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-p10-downloader",
"version": "0.0.2",
"version": "0.1.0",
"description": "OHIF extension for downloading DICOM P10 files",
"author": "OHIF",
"license": "MIT",
@@ -33,6 +33,7 @@
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"dicomweb-client": "^0.6.0",
"file-saver": "^2.0.2",
"jszip": "^3.2.2"
}
+19
View File
@@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.1.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.1.0...@ohif/extension-dicom-rt@0.1.1) (2020-04-28)
**Note:** Version bump only for package @ohif/extension-dicom-rt
# [0.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.0.2...@ohif/extension-dicom-rt@0.1.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## 0.0.2 (2020-04-09)
**Note:** Version bump only for package @ohif/extension-dicom-rt
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-rt",
"version": "0.0.2",
"version": "0.1.1",
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
"author": "OHIF",
"license": "MIT",
@@ -31,7 +31,7 @@
"@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "^4.0.9",
"dcmjs": "^0.8.3",
"dcmjs": "^0.12.2",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
@@ -61,14 +61,17 @@
}
.dcmrt-structure-set-item .item-label {
overflow: hidden;
max-width: calc(100% - 15px); /* calc(100% - 50px); 20px = eye icon */
text-overflow: ellipsis;
overflow-wrap: normal;
white-space: nowrap;
display: flex;
justify-content: space-between;
padding-right: 10px;
}
.dcmrt-structure-set-item .item-label span {
overflow-wrap: normal;
white-space: nowrap;
overflow: hidden;
max-width: calc(100% - 40px); /* calc(100% - 50px); 20px = eye icon */
text-overflow: ellipsis;
}
.dcmrt-structure-set-item .item-label .eye-icon {
@@ -41,7 +41,7 @@ const StructureSetItem = ({
>
<div>
<div className="item-label" style={{ marginBottom: 4 }}>
{label}
<span>{label}</span>
<Icon
className={`eye-icon ${isVisible && '--visible'}`}
name={isVisible ? 'eye' : 'eye-closed'}
+5 -6
View File
@@ -27,10 +27,7 @@ export default {
};
return (
<RTPanel
{...props}
onContourItemClick={contourItemClickHandler}
/>
<RTPanel {...props} onContourItemClick={contourItemClickHandler} />
);
};
@@ -51,8 +48,10 @@ export default {
if (study && study.series) {
for (let j = 0; j < study.series.length; j++) {
const series = study.series[j];
console.log(studies);
if (['RTSTRUCT', 'RTPLAN', 'RTDOSE'].includes(series.Modality)) {
if (
/* Could be expanded to contain RTPLAN and RTDOSE information in the future */
['RTSTRUCT'].includes(series.Modality)
) {
return false;
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
import OHIF from '@ohif/core';
import * as dcmjs from 'dcmjs';
import dcmjs from 'dcmjs';
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
import transformPointsToImagePlane from './utils/transformPointsToImagePlane';
@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.1...@ohif/extension-dicom-segmentation@0.2.2) (2020-04-28)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.0...@ohif/extension-dicom-segmentation@0.2.1) (2020-04-27)
### Bug Fixes
* 🐛 Dropdown not updating when selecting segment ([#1675](https://github.com/OHIF/Viewers/issues/1675)) ([20e366f](https://github.com/OHIF/Viewers/commit/20e366ffbd76781f48dc99c9f59e24b255b5e5df)), closes [#1669](https://github.com/OHIF/Viewers/issues/1669)
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.1.2...@ohif/extension-dicom-segmentation@0.2.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## [0.1.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.1.1...@ohif/extension-dicom-segmentation@0.1.2) (2020-04-09)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-segmentation",
"version": "0.1.2",
"version": "0.2.2",
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
"author": "OHIF",
"license": "MIT",
@@ -31,7 +31,7 @@
"@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.12.5",
"dcmjs": "^0.6.1",
"dcmjs": "^0.12.2",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
@@ -12,14 +12,6 @@
height: 20px;
}
.dcmseg-segment-item .segment-label {
overflow: hidden;
max-width: calc(100% - 50px);
text-overflow: ellipsis;
overflow-wrap: normal;
white-space: nowrap;
}
.dcmseg-segment-item .segment-info {
display: inline-block;
margin-top: 9px;
@@ -62,3 +54,30 @@
.dcmseg-segment-item .segment-actions .btnAction i {
margin-right: 4px;
}
.dcmseg-segment-item .segment-label {
display: flex;
justify-content: space-between;
padding-right: 20px;
}
.dcmseg-segment-item .segment-label span {
overflow-wrap: normal;
white-space: nowrap;
overflow: hidden;
max-width: calc(100% - 40px); /* calc(100% - 50px); 20px = eye icon */
text-overflow: ellipsis;
}
.dcmseg-segment-item .segment-label .eye-icon {
cursor: pointer;
color: var(--active-color);
}
.dcmseg-segment-item .segment-label .eye-icon:hover {
color: var(--hover-color);
}
.dcmseg-segment-item .segment-label .eye-icon.--visible {
color: var(--default-color);
}
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { TableListItem, Icon } from '@ohif/ui';
@@ -17,48 +17,63 @@ ColoredCircle.propTypes = {
color: PropTypes.array.isRequired,
};
const SegmentItem = ({ index, label, onClick, itemClass, color }) => (
<div className="dcmseg-segment-item">
<TableListItem
key={index}
itemKey={index}
itemIndex={index}
itemClass={itemClass}
itemMeta={<ColoredCircle color={color} />}
itemMetaClass="segment-color-section"
onItemClick={onClick}
>
<div>
<div className="segment-label" style={{ marginBottom: 4 }}>
{label}
</div>
{false && <div className="segment-info">{'...'}</div>}
{false && (
<div className="segment-actions">
<button
className="btnAction"
onClick={() => console.log('Relabelling...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
const SegmentItem = ({ index, label, onClick, itemClass, color, visible = true, onVisibilityChange }) => {
const [isVisible, setIsVisible] = useState(visible);
return (
<div className="dcmseg-segment-item">
<TableListItem
key={index}
itemKey={index}
itemIndex={index}
itemClass={itemClass}
itemMeta={<ColoredCircle color={color} />}
itemMetaClass="segment-color-section"
onItemClick={onClick}
>
<div>
<div className="segment-label" style={{ marginBottom: 4 }}>
<span>{label}</span>
<Icon
className={`eye-icon ${isVisible && '--visible'}`}
name={isVisible ? 'eye' : 'eye-closed'}
width="20px"
height="20px"
onClick={event => {
event.stopPropagation();
const newVisibility = !isVisible;
setIsVisible(newVisibility);
onVisibilityChange(newVisibility);
}}
/>
</div>
{false && <div className="segment-info">{'...'}</div>}
{false && (
<div className="segment-actions">
<button
className="btnAction"
onClick={() => console.log('Relabelling...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
Relabel
</button>
<button
className="btnAction"
onClick={() => console.log('Editing description...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
<button
className="btnAction"
onClick={() => console.log('Editing description...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
Description
</button>
</div>
)}
</div>
</TableListItem>
</div>
);
</div>
)}
</div>
</TableListItem>
</div>
);
};
SegmentItem.propTypes = {
index: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
import cornerstoneTools from 'cornerstone-tools';
import cornerstone from 'cornerstone-core';
@@ -28,13 +28,20 @@ const refreshViewport = () => {
/**
* SegmentationPanel component
*
* @param {Object} props
* @param {Array} props.studies
* @param {Array} props.viewports - viewportSpecificData
* @param {number} props.activeIndex - activeViewportIndex
* @param {Array} props.studies - Studies data
* @param {Array} props.viewports - Viewports data (viewportSpecificData)
* @param {number} props.activeIndex - Active viewport index
* @param {boolean} props.isOpen - Boolean that indicates if the panel is expanded
* @param {Function} props.onSegItemClick - Segment click handler
* @returns component
*/
const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
const SegmentationPanel = ({
studies,
viewports,
activeIndex,
isOpen,
onSegItemClick,
}) => {
/*
* TODO: wrap get/set interactions with the cornerstoneTools
* store with context to make these kind of things less blurry.
@@ -43,13 +50,14 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
const DEFAULT_BRUSH_RADIUS = configuration.radius || 10;
const [state, setState] = useState({
brushRadius: DEFAULT_BRUSH_RADIUS,
brushColor: 'rgba(221, 85, 85, 1)', /* TODO: We shouldn't hardcode this color, in the future the SEG may set the colorLUT to whatever it wants. */
brushColor:
'rgba(221, 85, 85, 1)' /* TODO: We shouldn't hardcode this color, in the future the SEG may set the colorLUT to whatever it wants. */,
selectedSegment: null,
selectedSegmentation: null,
showSegSettings: false,
brushStackState: null,
labelmapList: [],
segmentList: []
segmentList: [],
});
useEffect(() => {
@@ -57,7 +65,12 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
log.warn('Segmentation Panel: labelmap modified', event);
const module = cornerstoneTools.getModule('segmentation');
const activeViewport = viewports[activeIndex];
const firstImageId = studyMetadata.getFirstImageId(activeViewport.displaySetInstanceUID);
const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
const firstImageId = studyMetadata.getFirstImageId(
activeViewport.displaySetInstanceUID
);
updateState('brushStackState', module.state.series[firstImageId]);
};
@@ -86,20 +99,29 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
useEffect(() => {
const module = cornerstoneTools.getModule('segmentation');
const activeViewport = viewports[activeIndex];
const studyMetadata = studyMetadataManager.get(activeViewport.StudyInstanceUID);
const firstImageId = studyMetadata.getFirstImageId(activeViewport.displaySetInstanceUID);
const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
const firstImageId = studyMetadata.getFirstImageId(
activeViewport.displaySetInstanceUID
);
const brushStackState = module.state.series[firstImageId];
if (brushStackState) {
const labelmap3D = brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];
const labelmapList = getLabelmapList(brushStackState, firstImageId, activeViewport);
const segmentList = getSegmentList(labelmap3D, firstImageId);
const labelmap3D =
brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];
const labelmapList = getLabelmapList(
brushStackState,
firstImageId,
activeViewport
);
const segmentList = getSegmentList(labelmap3D, firstImageId, brushStackState);
setState(state => ({
...state,
brushStackState,
selectedSegmentation: brushStackState.activeLabelmapIndex,
labelmapList,
segmentList
segmentList,
}));
} else {
setState(state => ({
@@ -108,126 +130,201 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
segmentList: [],
}));
}
}, [studies, viewports, activeIndex]);
}, [studies, viewports, activeIndex, getLabelmapList, getSegmentList, state.selectedSegmentation]);
/* Handle open/closed panel behaviour */
useEffect(() => {
updateState('showSegSettings', state.showSegSettings && !isOpen);
setState(state => ({
...state,
showSegSettings: state.showSegSettings && !isOpen,
}));
}, [isOpen]);
const getLabelmapList = (brushStackState, firstImageId, activeViewport) => {
/* Get list of SEG labelmaps specific to active viewport (reference series) */
const referencedSegDisplaysets = _getReferencedSegDisplaysets(
activeViewport.StudyInstanceUID,
activeViewport.SeriesInstanceUID
);
const getLabelmapList = useCallback(
(brushStackState, firstImageId, activeViewport) => {
/* Get list of SEG labelmaps specific to active viewport (reference series) */
const referencedSegDisplaysets = _getReferencedSegDisplaysets(
activeViewport.StudyInstanceUID,
activeViewport.SeriesInstanceUID
);
return referencedSegDisplaysets.map((displaySet, index) => {
const { labelmapIndex, SeriesDate, SeriesTime } = displaySet;
return referencedSegDisplaysets.map((displaySet, index) => {
const { labelmapIndex, SeriesDate, SeriesTime } = displaySet;
/* Map to display representation */
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
const isActiveLabelmap =
labelmapIndex === brushStackState.activeLabelmapIndex;
const displayDate = date.format('ddd, MMM Do YYYY');
const displayTime = date.format('h:mm:ss a');
const displayDescription = displaySet.SeriesDescription;
/* Map to display representation */
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
const isActiveLabelmap =
labelmapIndex === brushStackState.activeLabelmapIndex;
const displayDate = date.format('ddd, MMM Do YYYY');
const displayTime = date.format('h:mm:ss a');
const displayDescription = displaySet.SeriesDescription;
return {
value: labelmapIndex,
title: displayDescription,
description: displayDate,
onClick: async () => {
const activatedLabelmapIndex = await _setActiveLabelmap(
activeViewport,
studies,
displaySet,
firstImageId,
brushStackState.activeLabelmapIndex
);
updateState('selectedSegmentation', activatedLabelmapIndex);
},
};
});
};
return {
value: labelmapIndex,
title: displayDescription,
description: displayDate,
onClick: async () => {
const activatedLabelmapIndex = await _setActiveLabelmap(
activeViewport,
studies,
displaySet,
firstImageId,
brushStackState.activeLabelmapIndex
);
updateState('selectedSegmentation', activatedLabelmapIndex);
},
};
});
},
[studies]
);
const getSegmentList = (labelmap3D, firstImageId) => {
/*
* Newly created segments have no `meta`
* So we instead build a list of all segment indexes in use
* Then find any associated metadata
*/
const uniqueSegmentIndexes = labelmap3D.labelmaps2D
.reduce((acc, labelmap2D) => {
if (labelmap2D) {
const segmentIndexes = labelmap2D.segmentsOnLabelmap;
const getSegmentList = useCallback(
(labelmap3D, firstImageId, brushStackState) => {
/*
* Newly created segments have no `meta`
* So we instead build a list of all segment indexes in use
* Then find any associated metadata
*/
const uniqueSegmentIndexes = labelmap3D.labelmaps2D
.reduce((acc, labelmap2D) => {
if (labelmap2D) {
const segmentIndexes = labelmap2D.segmentsOnLabelmap;
for (let i = 0; i < segmentIndexes.length; i++) {
if (!acc.includes(segmentIndexes[i]) && segmentIndexes[i] !== 0) {
acc.push(segmentIndexes[i]);
for (let i = 0; i < segmentIndexes.length; i++) {
if (!acc.includes(segmentIndexes[i]) && segmentIndexes[i] !== 0) {
acc.push(segmentIndexes[i]);
}
}
}
return acc;
}, [])
.sort((a, b) => a - b);
const module = cornerstoneTools.getModule('segmentation');
const colorLutTable =
module.state.colorLutTables[labelmap3D.colorLUTIndex];
const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data;
const segmentList = [];
for (let i = 0; i < uniqueSegmentIndexes.length; i++) {
const segmentIndex = uniqueSegmentIndexes[i];
const color = colorLutTable[segmentIndex];
let segmentLabel = '(unlabeled)';
let segmentNumber = segmentIndex;
/* Meta */
if (hasLabelmapMeta) {
const segmentMeta = labelmap3D.metadata.data[segmentIndex];
if (segmentMeta) {
segmentNumber = segmentMeta.SegmentNumber;
segmentLabel = segmentMeta.SegmentLabel;
}
}
return acc;
}, [])
.sort((a, b) => a - b);
const sameSegment = state.selectedSegment === segmentNumber;
const setCurrentSelectedSegment = () => {
_setActiveSegment(
firstImageId,
segmentNumber,
labelmap3D.activeSegmentIndex
);
updateState('selectedSegment', sameSegment ? null : segmentNumber);
const module = cornerstoneTools.getModule('segmentation');
const colorLutTable =
module.state.colorLutTables[labelmap3D.colorLUTIndex];
const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data;
const validIndexList = [];
labelmap3D.labelmaps2D.forEach((labelMap2D, index) => {
if (labelMap2D.segmentsOnLabelmap.includes(segmentNumber)) {
validIndexList.push(index);
}
});
const avg = array => array.reduce((a, b) => a + b) / array.length;
const average = avg(validIndexList);
const closest = validIndexList.reduce((prev, curr) => {
return Math.abs(curr - average) < Math.abs(prev - average)
? curr
: prev;
});
const segmentList = [];
for (let i = 0; i < uniqueSegmentIndexes.length; i++) {
const segmentIndex = uniqueSegmentIndexes[i];
const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element;
const toolState = cornerstoneTools.getToolState(element, 'stack');
const color = colorLutTable[segmentIndex];
let segmentLabel = '(unlabeled)';
let segmentNumber = segmentIndex;
if (!toolState) {
return;
}
/* Meta */
if (hasLabelmapMeta) {
const segmentMeta = labelmap3D.metadata.data[segmentIndex];
const imageIds = toolState.data[0].imageIds;
const imageId = imageIds[closest];
const frameIndex = imageIds.indexOf(imageId);
if (segmentMeta) {
segmentNumber = segmentMeta.SegmentNumber;
segmentLabel = segmentMeta.SegmentLabel;
const SOPInstanceUID = cornerstone.metaData.get(
'SOPInstanceUID',
imageId
);
const StudyInstanceUID = cornerstone.metaData.get(
'StudyInstanceUID',
imageId
);
onSegItemClick({
StudyInstanceUID,
SOPInstanceUID,
frameIndex,
activeViewportIndex: activeIndex,
});
};
const enabledElements = cornerstone.getEnabledElements();
const enabledElementViewport = enabledElements[activeIndex];
let isVisible = true;
if (enabledElementViewport) {
const element = enabledElementViewport.element;
const module = cornerstoneTools.getModule('segmentation');
isVisible = module.getters.isSegmentVisible(
element,
segmentNumber,
brushStackState.activeLabelmapIndex
);
}
segmentList.push(
<SegmentItem
key={segmentNumber}
itemClass={`segment-item ${sameSegment && 'selected'}`}
onClick={setCurrentSelectedSegment}
label={segmentLabel}
index={segmentNumber}
color={color}
visible={isVisible}
onVisibilityChange={() => {
const element = enabledElements[activeIndex].element;
module.setters.toggleSegmentVisibility(
element,
segmentNumber,
brushStackState.activeLabelmapIndex
);
refreshViewport();
}}
/>
);
}
const sameSegment = state.selectedSegment === segmentNumber;
const setCurrentSelectedSegment = () => {
_setActiveSegment(
firstImageId,
segmentNumber,
labelmap3D.activeSegmentIndex
);
updateState('selectedSegment', sameSegment ? null : segmentNumber);
};
return segmentList;
segmentList.push(
<SegmentItem
key={segmentNumber}
itemClass={`segment-item ${sameSegment && 'selected'}`}
onClick={setCurrentSelectedSegment}
label={segmentLabel}
index={segmentNumber}
color={color}
/>
);
}
return segmentList;
/*
* Let's iterate over segmentIndexes ^ above
* If meta has a match, use it to show info
* If now, add "no-meta" class
* Show default name
*/
};
/*
* Let's iterate over segmentIndexes ^ above
* If meta has a match, use it to show info
* If now, add "no-meta" class
* Show default name
*/
},
[activeIndex, onSegItemClick, state.selectedSegment]
);
const updateState = (field, value) => {
setState(state => ({ ...state, [field]: value }));
@@ -270,8 +367,7 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
}
const module = cornerstoneTools.getModule('segmentation');
const colorLutTable =
module.state.colorLutTables[labelmap3D.colorLUTIndex];
const colorLutTable = module.state.colorLutTables[labelmap3D.colorLUTIndex];
const color = colorLutTable[labelmap3D.activeSegmentIndex];
return `rgba(${color.join(',')})`;
@@ -328,9 +424,7 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
<h3>Segmentations</h3>
<div className="segmentations">
<SegmentationSelect
value={
state.labelmapList.find(i => i.value === state.selectedSegmentation) || null
}
value={state.labelmapList.find(i => i.value === state.selectedSegmentation)}
formatOptionLabel={SegmentationItem}
options={state.labelmapList}
/>
@@ -17,6 +17,10 @@ const defaultColor = computedstyle.getPropertyValue('--default-color');
const uiGrayDark = computedstyle.getPropertyValue('--ui-gray-dark');
const segmentationSelectStyles = {
singleValue: (base, state) => ({
...base,
width: '100%'
}),
control: (base, state) => ({
...base,
cursor: 'pointer',
+51 -3
View File
@@ -1,7 +1,9 @@
import React from 'react';
import init from './init.js';
import toolbarModule from './toolbarModule.js';
import panelModule from './panelModule.js';
import sopClassHandlerModule from './OHIFDicomSegSopClassHandler.js';
import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js';
export default {
/**
@@ -21,8 +23,54 @@ export default {
getToolbarModule({ servicesManager }) {
return toolbarModule;
},
getPanelModule({ servicesManager }) {
return panelModule;
getPanelModule({ commandsManager }) {
const ExtendedSegmentationPanel = props => {
const segItemClickHandler = segData => {
commandsManager.runCommand('jumpToImage', segData);
};
return (
<SegmentationPanel {...props} onSegItemClick={segItemClickHandler} />
);
};
return {
menuOptions: [
{
icon: 'list',
label: 'Segmentations',
target: 'segmentation-panel',
isDisabled: studies => {
if (!studies) {
return true;
}
for (let i = 0; i < studies.length; i++) {
const study = studies[i];
if (study && study.series) {
for (let j = 0; j < study.series.length; j++) {
const series = study.series[j];
if (series.Modality === 'SEG') {
return false;
}
}
}
}
return true;
},
},
],
components: [
{
id: 'segmentation-panel',
component: ExtendedSegmentationPanel,
},
],
defaultContext: ['VIEWER'],
};
},
getSopClassHandlerModule({ servicesManager }) {
return sopClassHandlerModule;
@@ -1,5 +1,5 @@
import OHIF from '@ohif/core';
import * as dcmjs from 'dcmjs';
import dcmjs from 'dcmjs';
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
@@ -1,39 +0,0 @@
import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js';
export default {
menuOptions: [
{
icon: 'list',
label: 'Segmentations',
target: 'segmentation-panel',
isDisabled: studies => {
if (!studies) {
return true;
}
for (let i = 0; i < studies.length; i++) {
const study = studies[i];
if (study && study.series) {
for (let j = 0; j < study.series.length; j++) {
const series = study.series[j];
if (series.Modality === 'SEG') {
return false;
}
}
}
}
return true;
},
},
],
components: [
{
id: 'segmentation-panel',
component: SegmentationPanel,
},
],
defaultContext: ['VIEWER'],
};
+46
View File
@@ -3,6 +3,52 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.5.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.5.0...@ohif/extension-vtk@1.5.1) (2020-04-28)
**Note:** Version bump only for package @ohif/extension-vtk
# [1.5.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.4.1...@ohif/extension-vtk@1.5.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## [1.4.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.4.0...@ohif/extension-vtk@1.4.1) (2020-04-23)
**Note:** Version bump only for package @ohif/extension-vtk
# [1.4.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.14...@ohif/extension-vtk@1.4.0) (2020-04-23)
### Features
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
## [1.3.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.13...@ohif/extension-vtk@1.3.14) (2020-04-22)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.3.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.12...@ohif/extension-vtk@1.3.13) (2020-04-17)
**Note:** Version bump only for package @ohif/extension-vtk
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-vtk",
"version": "1.3.13",
"version": "1.5.1",
"description": "OHIF extension for VTK.js",
"author": "OHIF",
"license": "MIT",
@@ -34,8 +34,8 @@
"@ohif/ui": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.12.5",
"cornerstone-wado-image-loader": "^3.0.0",
"dcmjs": "^0.10.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "^0.12.2",
"dicom-parser": "^1.8.3",
"i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1",
@@ -53,10 +53,10 @@
"react-vtkjs-viewport": "^0.8.3"
},
"devDependencies": {
"@ohif/core": "^2.7.0",
"@ohif/core": "^2.9.1",
"@ohif/ui": "^1.4.3",
"cornerstone-tools": "4.12.5",
"cornerstone-wado-image-loader": "^3.0.0",
"cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3",
"gh-pages": "^2.0.1",
"i18next": "^17.0.3",
+52
View File
@@ -3,6 +3,58 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.9.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.0...@ohif/core@2.9.1) (2020-04-28)
**Note:** Version bump only for package @ohif/core
# [2.9.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.8.1...@ohif/core@2.9.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## [2.8.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.8.0...@ohif/core@2.8.1) (2020-04-23)
### Bug Fixes
* 🐛 Multiframe fix ([#1661](https://github.com/OHIF/Viewers/issues/1661)) ([7120561](https://github.com/OHIF/Viewers/commit/71205618ecb8b592247c5acb32284bfe7e18fce5))
# [2.8.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.7.1...@ohif/core@2.8.0) (2020-04-23)
### Features
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
## [2.7.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.7.0...@ohif/core@2.7.1) (2020-04-22)
### Bug Fixes
* whiteLabeling should support component creation by passing React to defined fn ([#1659](https://github.com/OHIF/Viewers/issues/1659)) ([2093a00](https://github.com/OHIF/Viewers/commit/2093a0036584b2cc698c8f06fe62b334523b1029))
# [2.7.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.11...@ohif/core@2.7.0) (2020-04-17)
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/core",
"version": "2.7.0",
"version": "2.9.1",
"description": "Generic business logic for web-based medical imaging applications",
"author": "OHIF Core Team",
"license": "MIT",
@@ -33,14 +33,14 @@
"peerDependencies": {
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.12.5",
"cornerstone-wado-image-loader": "^3.0.0",
"cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3"
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"ajv": "^6.10.0",
"dcmjs": "^0.12.0",
"dicomweb-client": "^0.5.2",
"dcmjs": "^0.12.2",
"dicomweb-client": "^0.6.0",
"immer": "6.0.2",
"isomorphic-base64": "^1.0.2",
"lodash.clonedeep": "^4.5.0",
@@ -1,10 +1,11 @@
import * as dcmjs from 'dcmjs';
import dcmjs from 'dcmjs';
import { api } from 'dicomweb-client';
import DICOMWeb from '../DICOMWeb';
import parseDicomStructuredReport from './parseDicomStructuredReport';
import parseMeasurementsData from './parseMeasurementsData';
import getAllDisplaySets from './utils/getAllDisplaySets';
import errorHandler from '../errorHandler';
const VERSION_NAME = 'dcmjs-0.0';
const TRANSFER_SYNTAX_UID = '1.2.840.10008.1.2.1';
@@ -21,6 +22,7 @@ const retrieveMeasurementFromSR = async (series, studies, serverUrl) => {
const config = {
url: serverUrl,
headers: DICOMWeb.getAuthorizationHeader(),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const dicomWeb = new api.DICOMwebClient(config);
@@ -71,6 +73,7 @@ const stowSRFromMeasurements = async (measurements, serverUrl) => {
const config = {
url: serverUrl,
headers: DICOMWeb.getAuthorizationHeader(),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const dicomWeb = new api.DICOMwebClient(config);
@@ -1,4 +1,4 @@
import * as dcmjs from 'dcmjs';
import dcmjs from 'dcmjs';
import findInstanceMetadataBySopInstanceUID from './utils/findInstanceMetadataBySopInstanceUid';
@@ -1,4 +1,4 @@
import * as dcmjs from 'dcmjs';
import dcmjs from 'dcmjs';
import cornerstone from 'cornerstone-core';
import log from '../log';
@@ -1,4 +1,4 @@
import * as dcmjs from 'dcmjs';
import dcmjs from 'dcmjs';
/**
* Checks if dcmjs has support to determined tool
@@ -1,4 +1,4 @@
import * as dcmjs from 'dcmjs';
import dcmjs from 'dcmjs';
import queryString from 'query-string';
import dicomParser from 'dicom-parser';
import getPixelSpacingInformation from '../utils/metadataProvider/getPixelSpacingInformation';
@@ -378,7 +378,7 @@ class MetadataProvider {
}
metadata = {
overlays
overlays,
};
break;
@@ -400,6 +400,7 @@ class MetadataProvider {
case WADO_IMAGE_LOADER_TAGS.GENERAL_IMAGE_MODULE:
metadata = {
sopInstanceUid: instance.SOPInstanceUID,
instanceNumber: instance.InstanceNumber,
lossyImageCompression: instance.LossyImageCompression,
lossyImageCompressionRatio: instance.LossyImageCompressionRatio,
@@ -11,6 +11,7 @@ import { api } from 'dicomweb-client';
import { isImage } from '../../utils/isImage';
import isDisplaySetReconstructable from '../../utils/isDisplaySetReconstructable';
import isLowPriorityModality from '../../utils/isLowPriorityModality';
import errorHandler from '../../errorHandler';
export class StudyMetadata extends Metadata {
constructor(data, uid) {
@@ -102,10 +103,7 @@ export class StudyMetadata extends Metadata {
* @param {SeriesMetadata} series The series metadata object from which the display sets will be created
* @returns {Array} The list of display sets created for the given series object
*/
_createDisplaySetsForSeries(
sopClassHandlerModules,
series,
) {
_createDisplaySetsForSeries(sopClassHandlerModules, series) {
const study = this;
const displaySets = [];
@@ -293,14 +291,13 @@ export class StudyMetadata extends Metadata {
}
// Loop through the series (SeriesMetadata)
this.forEachSeries(
series => {
const displaySetsForSeries = this._createDisplaySetsForSeries(
sopClassHandlerModules,
series,
);
this.forEachSeries(series => {
const displaySetsForSeries = this._createDisplaySetsForSeries(
sopClassHandlerModules,
series
);
displaySets.push(...displaySetsForSeries);
displaySets.push(...displaySetsForSeries);
});
return sortDisplaySetList(displaySets);
@@ -321,7 +318,10 @@ export class StudyMetadata extends Metadata {
return false;
}
const displaySets = this._createDisplaySetsForSeries(sopClassHandlerModules, series)
const displaySets = this._createDisplaySetsForSeries(
sopClassHandlerModules,
series
);
// Note: filtering in place because this._displaySets has writable: false
for (let i = this._displaySets.length - 1; i >= 0; i--) {
@@ -835,9 +835,11 @@ function _getDisplaySetFromSopClassModule(
const plugin = handlersForSopClassUID[0];
const headers = DICOMWeb.getAuthorizationHeader();
const errorInterceptor = errorHandler.getHTTPErrorHandler();
const dicomWebClient = new dwc({
url: study.getData().wadoRoot,
headers,
errorInterceptor,
});
let displaySet = plugin.getDisplaySetFromSeries(
+6
View File
@@ -0,0 +1,6 @@
// These should be overridden by the implementation
const errorHandler = {
getHTTPErrorHandler: () => null,
};
export default errorHandler;
+3
View File
@@ -18,6 +18,7 @@ import string from './string.js';
import studies from './studies/';
import ui from './ui';
import user from './user.js';
import errorHandler from './errorHandler.js';
import utils, { hotkeys } from './utils/';
import {
@@ -46,6 +47,7 @@ const OHIF = {
string,
ui,
user,
errorHandler,
object,
log,
DICOMWeb,
@@ -79,6 +81,7 @@ export {
string,
ui,
user,
errorHandler,
object,
log,
DICOMWeb,
+1
View File
@@ -24,6 +24,7 @@ describe('Top level exports', () => {
'header',
'cornerstone',
'default', //
'errorHandler',
'string',
'ui',
'user',
@@ -1,6 +1,8 @@
import DICOMWeb from '../../../DICOMWeb/';
import { api } from 'dicomweb-client';
import errorHandler from '../../../errorHandler';
/**
* Parses data returned from a QIDO search and transforms it into
* an array of series that are present in the study
@@ -79,6 +81,7 @@ export default function Instances(server, StudyInstanceUID) {
const config = {
url: server.qidoRoot,
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const dicomWeb = new api.DICOMwebClient(config);
const queryParams = getQIDOQueryParams(
@@ -1,6 +1,8 @@
import { api } from 'dicomweb-client';
import DICOMWeb from '../../../DICOMWeb/';
import errorHandler from '../../../errorHandler';
/**
* Creates a QIDO date string for a date range query
* Assumes the year is positive, at most 4 digits long.
@@ -115,6 +117,7 @@ export default function Studies(server, filter) {
const config = {
url: server.qidoRoot,
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const dicomWeb = new api.DICOMwebClient(config);
@@ -9,6 +9,8 @@ import {
addInstancesToStudy,
} from './studyInstanceHelpers';
import errorHandler from '../../../errorHandler';
const { naturalizeDataset } = dcmjs.data.DicomMetaDictionary;
/**
@@ -73,6 +75,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
const client = new api.DICOMwebClient({
url: server.qidoRoot,
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
});
this.client = client;
@@ -117,7 +120,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
return {
seriesInstanceUIDsMap,
seriesData
seriesData,
};
}
@@ -155,11 +158,14 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
SeriesDescription: series.SeriesDescription,
SeriesNumber: series.SeriesNumber,
Modality: series.Modality,
instances: []
instances: [],
};
if (study.series[idx]) {
study.series[idx] = Object.assign(seriesDataFromQIDO, study.series[idx]);
study.series[idx] = Object.assign(
seriesDataFromQIDO,
study.series[idx]
);
} else {
study.series[idx] = seriesDataFromQIDO;
}
@@ -3,6 +3,8 @@ import DICOMWeb from '../../../DICOMWeb/';
import { createStudyFromSOPInstanceList } from './studyInstanceHelpers';
import RetrieveMetadataLoader from './retrieveMetadataLoader';
import errorHandler from '../../../errorHandler';
/**
* Class for sync load of study metadata.
* It inherits from RetrieveMetadataLoader
@@ -58,6 +60,7 @@ export default class RetrieveMetadataLoaderSync extends RetrieveMetadataLoader {
const client = new api.DICOMwebClient({
url: server.wadoRoot,
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
});
this.client = client;
@@ -144,7 +144,7 @@ async function makeSOPInstance(server, study, instance) {
wadorsuri,
wadoRoot: server.wadoRoot,
imageRendering: server.imageRendering,
thumbnailRendering: server.thumbnailRendering
thumbnailRendering: server.thumbnailRendering,
};
series.instances.push(sopInstance);
@@ -160,8 +160,10 @@ async function makeSOPInstance(server, study, instance) {
const wadoRSMetadata = Object.assign(instance);
if (sopInstance.NumberOfFrames) {
for (let i = 0; i < sopInstance.NumberOfFrames; i++) {
const { NumberOfFrames } = sopInstance.metadata;
if (NumberOfFrames) {
for (let i = 0; i < NumberOfFrames; i++) {
const wadorsImageId = getWADORSImageId(sopInstance, i);
cornerstoneWADOImageLoader.wadors.metaDataManager.add(
@@ -190,7 +192,7 @@ async function makeSOPInstance(server, study, instance) {
*/
async function addInstancesToStudy(server, study, sopInstanceList) {
return Promise.all(
sopInstanceList.map(function (sopInstance) {
sopInstanceList.map(function(sopInstance) {
return makeSOPInstance(server, study, sopInstance);
})
);
@@ -3,6 +3,8 @@ import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import { api } from 'dicomweb-client';
import DICOMWeb from '../DICOMWeb';
import errorHandler from '../errorHandler';
const getImageId = imageObj => {
if (!imageObj) {
return;
@@ -57,11 +59,13 @@ const wadorsRetriever = (
studyInstanceUID,
seriesInstanceUID,
sopInstanceUID,
headers = DICOMWeb.getAuthorizationHeader()
headers = DICOMWeb.getAuthorizationHeader(),
errorInterceptor = errorHandler.getHTTPErrorHandler()
) => {
const config = {
url,
headers,
errorInterceptor,
};
const dicomWeb = new api.DICOMwebClient(config);
@@ -3,6 +3,8 @@ import DICOMWeb from '../../DICOMWeb';
import str2ab from '../str2ab';
import unpackOverlay from './unpackOverlay';
import errorHandler from '../../errorHandler';
export default async function fetchOverlayData(instance, server) {
const OverlayDataPromises = [];
const OverlayDataTags = [];
@@ -21,14 +23,20 @@ export default async function fetchOverlayData(instance, server) {
const inlineBinaryData = atob(instance[OverlayDataTag].InlineBinary);
const arraybuffer = str2ab(inlineBinaryData);
instance[OverlayDataTag] = unpackOverlay(arraybuffer)
} else if (instance[OverlayDataTag] && instance[OverlayDataTag].BulkDataURI) {
instance[OverlayDataTag] = unpackOverlay(arraybuffer);
} else if (
instance[OverlayDataTag] &&
instance[OverlayDataTag].BulkDataURI
) {
OverlayDataPromises.push(
_getOverlayData(instance[OverlayDataTag], server)
);
OverlayDataTags.push(OverlayDataTag);
} else if (instance[OverlayDataTag] && instance[OverlayDataTag] instanceof ArrayBuffer) {
instance[OverlayDataTag] = unpackOverlay(instance[OverlayDataTag])
} else if (
instance[OverlayDataTag] &&
instance[OverlayDataTag] instanceof ArrayBuffer
) {
instance[OverlayDataTag] = unpackOverlay(instance[OverlayDataTag]);
}
}
@@ -60,6 +68,7 @@ async function _getOverlayData(tag, server) {
const config = {
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const dicomWeb = new api.DICOMwebClient(config);
const options = {
@@ -71,5 +80,3 @@ async function _getOverlayData(tag, server) {
.then(result => result[0])
.then(unpackOverlay);
}
@@ -2,6 +2,8 @@ import { api } from 'dicomweb-client';
import DICOMWeb from '../../DICOMWeb';
import str2ab from '../str2ab';
import errorHandler from '../../errorHandler';
export default async function fetchPaletteColorLookupTableData(
instance,
server
@@ -146,6 +148,7 @@ function _getPaletteColor(server, paletteColorLookupTableData, lutDescriptor) {
const config = {
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const dicomWeb = new api.DICOMwebClient(config);
const options = {
@@ -164,6 +167,8 @@ function _getPaletteColor(server, paletteColorLookupTableData, lutDescriptor) {
resolve(arrayBufferToPaletteColorLUT(arraybuffer));
});
} else {
return Promise.resolve(arrayBufferToPaletteColorLUT(paletteColorLookupTableData));
return Promise.resolve(
arrayBufferToPaletteColorLUT(paletteColorLookupTableData)
);
}
}
+79
View File
@@ -3,6 +3,85 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.11.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.11.1...@ohif/viewer@3.11.2) (2020-04-28)
**Note:** Version bump only for package @ohif/viewer
## [3.11.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.11.0...@ohif/viewer@3.11.1) (2020-04-27)
**Note:** Version bump only for package @ohif/viewer
# [3.11.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.10.2...@ohif/viewer@3.11.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## [3.10.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.10.1...@ohif/viewer@3.10.2) (2020-04-23)
### Bug Fixes
* undefined `errorHandler` in cornerstoneWadoImageLoader configuration ([#1664](https://github.com/OHIF/Viewers/issues/1664)) ([709f147](https://github.com/OHIF/Viewers/commit/709f14708e2b0f912b5ea509114acd87af3149cb))
## [3.10.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.10.0...@ohif/viewer@3.10.1) (2020-04-23)
**Note:** Version bump only for package @ohif/viewer
# [3.10.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.9.2...@ohif/viewer@3.10.0) (2020-04-23)
### Features
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
## [3.9.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.9.1...@ohif/viewer@3.9.2) (2020-04-22)
### Bug Fixes
* whiteLabeling should support component creation by passing React to defined fn ([#1659](https://github.com/OHIF/Viewers/issues/1659)) ([2093a00](https://github.com/OHIF/Viewers/commit/2093a0036584b2cc698c8f06fe62b334523b1029))
## [3.9.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.9.0...@ohif/viewer@3.9.1) (2020-04-17)
### Bug Fixes
* `showStudyList` config ([#1647](https://github.com/OHIF/Viewers/issues/1647)) ([d9fc7bb](https://github.com/OHIF/Viewers/commit/d9fc7bbb0e6d868f507c515f031aaf88a2353e2f))
# [3.9.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.21...@ohif/viewer@3.9.0) (2020-04-17)
+10 -10
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/viewer",
"version": "3.9.0",
"version": "3.11.2",
"description": "OHIF Viewer",
"author": "OHIF Contributors",
"license": "MIT",
@@ -48,16 +48,16 @@
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"@ohif/core": "^2.7.0",
"@ohif/extension-cornerstone": "^2.5.2",
"@ohif/extension-dicom-html": "^1.1.2",
"@ohif/core": "^2.9.1",
"@ohif/extension-cornerstone": "^2.7.0",
"@ohif/extension-dicom-html": "^1.2.1",
"@ohif/extension-dicom-microscopy": "^0.50.9",
"@ohif/extension-dicom-p10-downloader": "^0.0.2",
"@ohif/extension-dicom-p10-downloader": "^0.1.0",
"@ohif/extension-dicom-pdf": "^1.0.3",
"@ohif/extension-dicom-rt": "^0.0.2",
"@ohif/extension-dicom-segmentation": "^0.1.2",
"@ohif/extension-dicom-rt": "^0.1.1",
"@ohif/extension-dicom-segmentation": "^0.2.2",
"@ohif/extension-lesion-tracker": "^0.2.0",
"@ohif/extension-vtk": "^1.3.13",
"@ohif/extension-vtk": "^1.5.1",
"@ohif/i18n": "^0.52.8",
"@ohif/ui": "^1.4.3",
"@tanem/react-nprogress": "^1.1.25",
@@ -66,8 +66,8 @@
"cornerstone-core": "^2.2.8",
"cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.12.5",
"cornerstone-wado-image-loader": "^3.0.0",
"dcmjs": "^0.12.0",
"cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "^0.12.2",
"dicom-parser": "^1.8.3",
"dicomweb-client": "^0.4.4",
"hammerjs": "^2.0.8",
-1
View File
@@ -1,7 +1,6 @@
window.config = {
// default: '/'
routerBasename: '/',
whiteLabelling: {},
extensions: [],
showStudyList: true,
filterQueryParam: false,
-1
View File
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/',
whiteLabelling: {},
extensions: [],
showStudyList: true,
servers: {
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/',
whiteLabelling: {},
showStudyList: true,
servers: {
dicomWeb: [
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/',
whiteLabelling: {},
showStudyList: true,
servers: {
// This is an array, but we'll only use the first entry for now
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/',
whiteLabelling: {},
showStudyList: true,
servers: {
// This is an array, but we'll only use the first entry for now
-1
View File
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/',
whiteLabelling: {},
enableGoogleCloudAdapter: true,
healthcareApiEndpoint: 'https://healthcare.googleapis.com/v1beta1',
servers: {
+19
View File
@@ -0,0 +1,19 @@
window.config = function(props) {
var servicesManager = props.servicesManager;
return {
routerBasename: '/',
enableGoogleCloudAdapter: true,
enableGoogleCloudAdapterUI: false,
showStudyList: true,
httpErrorHandler: error => {
// This is 429 when rejected from the public idc sandbox too often.
console.warn(error.status);
// Could use services manager here to bring up a dialog/modal if needed.
console.warn('test, navigate to https://ohif.org/');
window.location = 'https://ohif.org/';
},
healthcareApiEndpoint: 'https://idc-sandbox-002.appspot.com/v1beta1',
};
};
@@ -1,7 +1,6 @@
window.config = {
// default: '/'
routerBasename: '/',
whiteLabelling: {},
// default: ''
showStudyList: true,
servers: {
-1
View File
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/pwa',
whiteLabelling: {},
showStudyList: true,
servers: {
dicomWeb: [
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/',
whiteLabelling: {},
showStudyList: true,
servers: {
dicomWeb: [
+15 -10
View File
@@ -31,7 +31,8 @@ import {
import i18n from '@ohif/i18n';
// TODO: This should not be here
import './config';
//import './config';
import { setConfiguration } from './config';
/** Utils */
import {
@@ -50,7 +51,7 @@ import { getActiveContexts } from './store/layout/selectors.js';
import store from './store';
/** Contexts */
import WhiteLabellingContext from './context/WhiteLabellingContext';
import WhiteLabelingContext from './context/WhiteLabelingContext';
import UserManagerContext from './context/UserManagerContext';
import AppContext from './context/AppContext';
@@ -77,7 +78,9 @@ class App extends Component {
PropTypes.shape({
routerBasename: PropTypes.string.isRequired,
oidc: PropTypes.array,
whiteLabelling: PropTypes.object,
whiteLabeling: PropTypes.shape({
createLogoComponentFn: PropTypes.func,
}),
extensions: PropTypes.array,
}),
]).isRequired,
@@ -86,7 +89,7 @@ class App extends Component {
static defaultProps = {
config: {
whiteLabelling: {},
showStudyList: true,
oidc: [],
extensions: [],
},
@@ -102,10 +105,10 @@ class App extends Component {
const { config, defaultExtensions } = props;
const appDefaultConfig = {
showStudyList: true,
cornerstoneExtensionConfig: {},
extensions: [],
routerBasename: '/',
whiteLabelling: {},
};
this._appConfig = {
@@ -121,6 +124,8 @@ class App extends Component {
oidc,
} = this._appConfig;
setConfiguration(this._appConfig);
this.initUserManager(oidc);
_initServices([
UINotificationService,
@@ -144,7 +149,7 @@ class App extends Component {
}
render() {
const { whiteLabelling, routerBasename } = this._appConfig;
const { whiteLabeling, routerBasename } = this._appConfig;
const {
UINotificationService,
UIDialogService,
@@ -160,7 +165,7 @@ class App extends Component {
<OidcProvider store={store} userManager={this._userManager}>
<UserManagerContext.Provider value={this._userManager}>
<Router basename={routerBasename}>
<WhiteLabellingContext.Provider value={whiteLabelling}>
<WhiteLabelingContext.Provider value={whiteLabeling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider
@@ -173,7 +178,7 @@ class App extends Component {
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabellingContext.Provider>
</WhiteLabelingContext.Provider>
</Router>
</UserManagerContext.Provider>
</OidcProvider>
@@ -188,7 +193,7 @@ class App extends Component {
<Provider store={store}>
<I18nextProvider i18n={i18n}>
<Router basename={routerBasename}>
<WhiteLabellingContext.Provider value={whiteLabelling}>
<WhiteLabelingContext.Provider value={whiteLabeling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider modal={OHIFModal} service={UIModalService}>
@@ -196,7 +201,7 @@ class App extends Component {
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabellingContext.Provider>
</WhiteLabelingContext.Provider>
</Router>
</I18nextProvider>
</Provider>
@@ -4,8 +4,16 @@
height: var(--top-bar-height);
}
.dd-item {
width: 100%;
.header-left-box {
display: inline-block;
position: relative;
}
.header-brand {
text-decoration: none;
height: 30px;
float: left;
clear: both;
}
/* Home Page */
@@ -17,35 +25,11 @@
width: 100%;
}
.header-left-box {
display: inline-block;
position: relative;
}
.header-viewerLink {
float: left;
}
.header-brand {
height: 30px;
text-decoration: none;
float: left;
clear: both;
}
.entry-header.header-big .header-brand {
height: auto;
height: 50px;
padding: 0;
}
.header-logo-image {
fill: transparent;
height: 100%;
margin: 0 8px 0 0;
width: 30px;
float: left;
}
.entry-header.header-big .header-logo-image {
margin-right: 20px;
width: 50px;
@@ -57,6 +41,24 @@
height: auto;
}
/** OTHER STYLES **/
.dd-item {
width: 100%;
}
.header-viewerLink {
float: left;
}
.header-logo-image {
fill: transparent;
height: 100%;
margin: 0 8px 0 0;
width: 30px;
float: left;
}
.header-btn {
color: var(--text-secondary-color);
cursor: pointer;
@@ -130,13 +132,6 @@
width: 40%;
}
/* Account for notification bar height */
.entry-header.header-big .header-brand {
}
.dd-menu {
}
/* Toggle Notification Bar */
.notification-bar {
display: block;
+18 -18
View File
@@ -1,30 +1,29 @@
import React, { useState, useEffect } from 'react';
import { Link, withRouter } from 'react-router-dom';
import { withTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Dropdown, AboutContent, withModal } from '@ohif/ui';
//
import { UserPreferences } from './../UserPreferences';
import OHIFLogo from '../OHIFLogo/OHIFLogo.js';
import './Header.css';
// Context
import AppContext from './../../context/AppContext';
function Header(props) {
const {
t,
user,
userManager,
modal: { show },
home,
useLargeLogo,
linkPath,
linkText,
location,
children,
} = props;
const [options, setOptions] = useState([]);
const hasLink = linkText && linkPath;
useEffect(() => {
const optionsValue = [
@@ -61,15 +60,12 @@ function Header(props) {
setOptions(optionsValue);
}, [setOptions, show, t, user, userManager]);
const { appConfig = {} } = AppContext;
const showStudyList =
appConfig.showStudyList !== undefined ? appConfig.showStudyList : true;
// ANTD -- Hamburger, Drawer, Menu
return (
<>
<div className="notification-bar">{t('INVESTIGATIONAL USE ONLY')}</div>
<div className={`entry-header ${home ? 'header-big' : ''}`}>
<div
className={classNames('entry-header', { 'header-big': useLargeLogo })}
>
<div className="header-left-box">
{location && location.studyLink && (
<Link
@@ -82,15 +78,15 @@ function Header(props) {
{children}
{showStudyList && !home && (
{hasLink && (
<Link
className="header-btn header-studyListLinkSection"
to={{
pathname: '/',
pathname: linkPath,
state: { studyLink: location.pathname },
}}
>
{t('Study list')}
{t(linkText)}
</Link>
)}
</div>
@@ -105,7 +101,11 @@ function Header(props) {
}
Header.propTypes = {
home: PropTypes.bool.isRequired,
// Study list, /
linkText: PropTypes.string,
linkPath: PropTypes.string,
useLargeLogo: PropTypes.bool,
//
location: PropTypes.object.isRequired,
children: PropTypes.node,
t: PropTypes.func.isRequired,
@@ -115,7 +115,7 @@ Header.propTypes = {
};
Header.defaultProps = {
home: true,
useLargeLogo: false,
children: OHIFLogo(),
};
+50 -34
View File
@@ -3,43 +3,59 @@ import cornerstone from 'cornerstone-core';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import dicomParser from 'dicom-parser';
import version from './version.js';
import AppContext from './context/AppContext';
let homepage;
const { process } = window;
if (process && process.env && process.env.PUBLIC_URL) {
homepage = process.env.PUBLIC_URL;
}
window.info = {
version,
homepage,
};
// For debugging
//if (process.env.node_env === 'development') {
window.cornerstone = cornerstone;
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
//}
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
OHIF.user.getAccessToken = () => {
// TODO: Get the Redux store from somewhere else
const state = window.store.getState();
if (!state.oidc || !state.oidc.user) {
return;
export function setConfiguration(appConfig) {
let homepage;
const { process } = window;
if (process && process.env && process.env.PUBLIC_URL) {
homepage = process.env.PUBLIC_URL;
}
return state.oidc.user.access_token;
};
window.info = {
version,
homepage,
};
cornerstoneWADOImageLoader.configure({
beforeSend: function(xhr) {
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
// For debugging
//if (process.env.node_env === 'development') {
window.cornerstone = cornerstone;
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
//}
if (headers.Authorization) {
xhr.setRequestHeader('Authorization', headers.Authorization);
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
OHIF.user.getAccessToken = () => {
// TODO: Get the Redux store from somewhere else
const state = window.store.getState();
if (!state.oidc || !state.oidc.user) {
return;
}
},
});
return state.oidc.user.access_token;
};
OHIF.errorHandler.getHTTPErrorHandler = () => {
// const { appConfig = {} } = AppContext;
return appConfig.httpErrorHandler;
};
cornerstoneWADOImageLoader.configure({
beforeSend: function(xhr) {
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
if (headers.Authorization) {
xhr.setRequestHeader('Authorization', headers.Authorization);
}
},
errorInterceptor: error => {
// const { appConfig = {} } = AppContext;
if (typeof appConfig.httpErrorHandler === 'function') {
appConfig.httpErrorHandler(error);
}
},
});
}
@@ -14,8 +14,9 @@ import SidePanel from './../components/SidePanel.js';
import { extensionManager } from './../App.js';
// Contexts
import WhiteLabellingContext from '../context/WhiteLabellingContext.js';
import WhiteLabelingContext from '../context/WhiteLabelingContext.js';
import UserManagerContext from '../context/UserManagerContext';
import AppContext from '../context/AppContext';
import './Viewer.css';
@@ -226,17 +227,33 @@ class Viewer extends Component {
return (
<>
{/* HEADER */}
<WhiteLabellingContext.Consumer>
{whiteLabelling => (
<WhiteLabelingContext.Consumer>
{whiteLabeling => (
<UserManagerContext.Consumer>
{userManager => (
<ConnectedHeader home={false} userManager={userManager}>
{whiteLabelling.logoComponent}
</ConnectedHeader>
<AppContext.Consumer>
{appContext => (
<ConnectedHeader
linkText={
appContext.appConfig.showStudyList
? 'Study List'
: undefined
}
linkPath={
appContext.appConfig.showStudyList ? '/' : undefined
}
userManager={userManager}
>
{whiteLabeling &&
whiteLabeling.createLogoComponentFn &&
whiteLabeling.createLogoComponentFn(React)}
</ConnectedHeader>
)}
</AppContext.Consumer>
)}
</UserManagerContext.Consumer>
)}
</WhiteLabellingContext.Consumer>
</WhiteLabelingContext.Consumer>
{/* TOOLBAR */}
<ConnectedToolbarRow
@@ -290,11 +307,11 @@ class Viewer extends Component {
activeIndex={this.props.activeViewportIndex}
/>
) : (
<ConnectedStudyBrowser
studies={this.state.thumbnails}
studyMetadata={this.props.studies}
/>
)}
<ConnectedStudyBrowser
studies={this.state.thumbnails}
studyMetadata={this.props.studies}
/>
)}
</SidePanel>
{/* MAIN */}
@@ -332,7 +349,7 @@ export default withDialog(Viewer);
* @param {Study[]} studies
* @param {DisplaySet[]} studies[].displaySets
*/
const _mapStudiesToThumbnails = function (studies) {
const _mapStudiesToThumbnails = function(studies) {
return studies.map(study => {
const { StudyInstanceUID } = study;
@@ -0,0 +1,10 @@
import OHIFLogo from '../components/OHIFLogo/OHIFLogo.js';
import React from 'react';
const defaultContextValues = {
createLogoComponentFn: React => OHIFLogo(),
};
const WhiteLabelingContext = React.createContext(defaultContextValues);
export default WhiteLabelingContext;
@@ -1,10 +0,0 @@
import OHIFLogo from '../components/OHIFLogo/OHIFLogo.js';
import React from 'react';
const defaultContextValues = {
logoComponent: OHIFLogo(),
};
const WhiteLabellingContext = React.createContext(defaultContextValues);
export default WhiteLabellingContext;
@@ -1,5 +1,6 @@
import { httpErrorToStr, checkDicomFile } from '../utils/helpers';
import { api } from 'dicomweb-client';
import { errorHandler } from '@ohif/core';
class DicomUploadService {
async smartUpload(files, url, uploadCallback, cancellationToken) {
@@ -22,8 +23,7 @@ class DicomUploadService {
let error = null;
try {
if (chunk.length > 1) throw new Error('Not implemented');
if (chunk.length === 1)
await this.simpleUpload(chunk[0], url);
if (chunk.length === 1) await this.simpleUpload(chunk[0], url);
} catch (err) {
// It looks like a stupid bug of Babel that err is not an actual Exception object
error = httpErrorToStr(err);
@@ -76,11 +76,12 @@ class DicomUploadService {
getClient(url) {
const headers = this.retrieveAuthHeaderFunc();
const errorInterceptor = errorHandler.getHTTPErrorHandler();
// TODO: a bit weird we are creating a new dicomweb client instance for every upload
return new api.DICOMwebClient({
url,
headers
headers,
});
}
}
@@ -1,4 +1,4 @@
import * as dcmjs from 'dcmjs';
import dcmjs from 'dcmjs';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import FileLoader from './fileLoader';
import OHIF from '@ohif/core';
+2 -7
View File
@@ -45,9 +45,7 @@ const ROUTES_DEF = {
path: ['/studylist', '/'],
component: StudyListRouting,
condition: appConfig => {
return appConfig.showStudyList !== undefined
? appConfig.showStudyList
: true;
return appConfig.showStudyList;
},
},
local: {
@@ -72,10 +70,7 @@ const ROUTES_DEF = {
'/projects/:project/locations/:location/datasets/:dataset/dicomStores/:dicomStore',
component: StudyListRouting,
condition: appConfig => {
const showList =
appConfig.showStudyList !== undefined
? appConfig.showStudyList
: true;
const showList = appConfig.showStudyList;
return showList && !!appConfig.enableGoogleCloudAdapter;
},
@@ -19,7 +19,7 @@ import filesToStudies from '../lib/filesToStudies.js';
// Contexts
import UserManagerContext from '../context/UserManagerContext';
import WhiteLabellingContext from '../context/WhiteLabellingContext';
import WhiteLabelingContext from '../context/WhiteLabelingContext';
import AppContext from '../context/AppContext';
const { urlUtil: UrlUtil } = OHIF.utils;
@@ -209,21 +209,23 @@ function StudyListRoute(props) {
/>
) : null}
{healthCareApiWindows}
<WhiteLabellingContext.Consumer>
{whiteLabelling => (
<WhiteLabelingContext.Consumer>
{whiteLabeling => (
<UserManagerContext.Consumer>
{userManager => (
<ConnectedHeader
home={true}
useLargeLogo={true}
user={user}
userManager={userManager}
>
{whiteLabelling.logoComponent}
{whiteLabeling &&
whiteLabeling.createLogoComponentFn &&
whiteLabeling.createLogoComponentFn(React)}
</ConnectedHeader>
)}
</UserManagerContext.Consumer>
)}
</WhiteLabellingContext.Consumer>
</WhiteLabelingContext.Consumer>
<div className="study-list-header">
<div className="header">
<h1 style={{ fontWeight: 300, fontSize: '22px' }}>
+17 -12
View File
@@ -5953,10 +5953,10 @@ cornerstone-tools@4.12.5:
"@babel/runtime" "7.1.2"
cornerstone-math "0.1.7"
cornerstone-wado-image-loader@^3.0.0:
version "3.0.6"
resolved "https://registry.yarnpkg.com/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-3.0.6.tgz#7b2be91f8f658de3a7474e029aa8314a35725132"
integrity sha512-1luWOh1GzkboZ3uxfu4T8vh7K9nlDiiEbQ4dYIY9oobRZVZQbQiLT8wEZPbb4JTUe7UWJISHPCNgtAU8UE9DNw==
cornerstone-wado-image-loader@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-3.1.0.tgz#6007470be896678636da3188f194f5e66ed07858"
integrity sha512-n2yadbwK7rCHqJyh5AUmYVPUY5m7boKpheK60sXV41Z0eSZLin9Ja+P9yeAvkg0VGV+/zaBZGPUGY3YyjdWBgg==
dependencies:
dicom-parser "^1.8.3"
pako "^1.0.10"
@@ -6534,10 +6534,10 @@ dateformat@^3.0.0:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
dcmjs@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.12.0.tgz#8b1634f9b66e452075295f5d1f2c5bf3dabdf03e"
integrity sha512-AZAnFMvzAxUv5+KWoZcxnTYflLKk0rNPeeFt2KrVAgaExFBFfDgJQh1lEgqdNlIP+XQDFrEEUmp726SQhgmVCg==
dcmjs@^0.12.2:
version "0.12.2"
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.12.2.tgz#1c16e19c27ff43202abc7cd25c5ecd6d8bf7672e"
integrity sha512-B7cEVCfDi3mOLKoBYUMYy+6COjLZhtKaesNpq5XNA4IbEH4EJYVweJfiI9gsR5hypv9LAPg72xG/xrbqjy8TgQ==
dependencies:
"@babel/polyfill" "^7.8.3"
"@babel/runtime" "^7.8.4"
@@ -6858,6 +6858,11 @@ dicomweb-client@^0.5.2:
resolved "https://registry.yarnpkg.com/dicomweb-client/-/dicomweb-client-0.5.2.tgz#aa5a4a6a5044b0702bb0a8a2662c86cd16901951"
integrity sha512-e11n2+g7HfBuMyopWq76W11eSZJ707g4U1XiO8URA+23aoe0g1kGAtlPB29NKXl7zX7RznTJ7wfRBxFkW6EJDA==
dicomweb-client@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/dicomweb-client/-/dicomweb-client-0.6.0.tgz#5e35ada52fe0155af1cc1f0e84f9c7f76477f92d"
integrity sha512-VAkBg4W6odIo2XsFxqjN/rptd7bQ8oHpRuKH5d46E9BUIPzRschazE8Dx1xg7/l3N3f1M70jB7yJ339arAXiDQ==
diff-sequences@^24.9.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
@@ -11428,9 +11433,9 @@ jszip@3.2.2:
set-immediate-shim "~1.0.1"
jszip@^3.2.2:
version "3.3.0"
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.3.0.tgz#29d72c21a54990fa885b11fc843db320640d5271"
integrity sha512-EJ9k766htB1ZWnsV5ZMDkKLgA+201r/ouFF8R2OigVjVdcm2rurcBrrdXaeqBJbqnUVMko512PYmlncBKE1Huw==
version "3.4.0"
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.4.0.tgz#1a69421fa5f0bb9bc222a46bca88182fba075350"
integrity sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==
dependencies:
lie "~3.3.0"
pako "~1.0.2"
@@ -16385,7 +16390,7 @@ regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2:
regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4:
version "0.13.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==