@ohif/app 3.13.0-beta.42 → 3.13.0-beta.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/{3461.bundle.d418652280863f312da3.js → 3461.bundle.829d07239cde6238db7b.js} +24 -47
  2. package/dist/{4688.bundle.114adcc40c407ed7d18c.js → 4688.bundle.4e3a2e7fe6353e18fc9d.js} +1 -35
  3. package/dist/{4819.bundle.0cd1049e47d4d46ef63a.js → 4819.bundle.cdf38c8daa32fe3750a1.js} +3 -2
  4. package/dist/{8305.bundle.d907ab2a252d536ac865.js → 8305.bundle.ac779a289cb3546a1c6f.js} +50 -47
  5. package/dist/{8963.bundle.bc2e871cbd92e9fdbbf5.js → 8963.bundle.0a71d2361f66b5b7cafb.js} +2 -10
  6. package/dist/{9195.bundle.5c8fc381c7a8b1b81c43.js → 9195.bundle.dba57be4160f9d16add7.js} +131 -33
  7. package/dist/{app.bundle.ced82ce0abbd3feb867e.js → app.bundle.b46025f18465373b69c8.js} +2 -2
  8. package/dist/app.bundle.css +1 -1
  9. package/dist/index.html +1 -1
  10. package/dist/sw.js +1 -1
  11. package/package.json +20 -20
  12. /package/dist/{1459.bundle.725b4e9d2776ac9063c5.js → 1459.bundle.4c73589f64b0a48f4909.js} +0 -0
  13. /package/dist/{1933.bundle.928939cb97edcea28e7b.js → 1933.bundle.562738060267f493cc6e.js} +0 -0
  14. /package/dist/{2018.bundle.4b011332d36e85206cad.js → 2018.bundle.10ef888cc022e5747c50.js} +0 -0
  15. /package/dist/{213.bundle.9ef487e1b9adb9381590.js → 213.bundle.544cda23ae0bd2ba23c0.js} +0 -0
  16. /package/dist/{2424.bundle.9fbd98a379415bcd4ba9.js → 2424.bundle.f17b736977f093c8a9e8.js} +0 -0
  17. /package/dist/{3138.bundle.55d79e6af7d392cef911.js → 3138.bundle.dbfd7894410e6cd46a7d.js} +0 -0
  18. /package/dist/{4507.bundle.f78fab196e3f5ed59403.js → 4507.bundle.03dcbcf3c4f91798eff6.js} +0 -0
  19. /package/dist/{5015.bundle.34a8788caa0bf258d194.js → 5015.bundle.29198e03f89301ce5bd3.js} +0 -0
  20. /package/dist/{5028.bundle.1778b95ef2d721eff71c.js → 5028.bundle.8760e2ac53c879ec1691.js} +0 -0
  21. /package/dist/{5457.bundle.ee27e2890a04e64a44bf.js → 5457.bundle.38d66e2219fe030a96f8.js} +0 -0
  22. /package/dist/{5485.bundle.079cc5bd705f725c8f5d.js → 5485.bundle.f6e2a62b3098278eb7a0.js} +0 -0
  23. /package/dist/{6027.bundle.ce0fd76251e773c5d5e4.js → 6027.bundle.2cac6718b062d2fe3049.js} +0 -0
  24. /package/dist/{7639.bundle.d2d03567252e749428a6.js → 7639.bundle.c3e8537955e4865b9112.js} +0 -0
  25. /package/dist/{8499.bundle.4cbe5e98f5046cd25fc6.js → 8499.bundle.f15599f9f64bea98bfdb.js} +0 -0
  26. /package/dist/{85.bundle.79c5681051a031061e4d.js → 85.bundle.32beb09c95da00fcf5a1.js} +0 -0
  27. /package/dist/{8558.bundle.8124019bdb429709a049.js → 8558.bundle.723cdf968e65ae08468b.js} +0 -0
  28. /package/dist/{8583.bundle.6b74cee293a1a1e950af.js → 8583.bundle.e80cd615ebfc5ee5c18a.js} +0 -0
  29. /package/dist/{9927.bundle.7af41804c41122527790.js → 9927.bundle.6a13238e6d47189e4e89.js} +0 -0
@@ -980,16 +980,25 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
980
980
  MeasuredValueSequence
981
981
  } = item;
982
982
 
983
- // Handle spatial reference ONLY if ContentSequence exists
983
+ // Handle spatial reference ONLY if ContentSequence exists.
984
+ // ContentSequence may be a scalar SCOORD or an array when additional named
985
+ // SCOORDs (e.g. control points) are nested alongside the primary geometry.
986
+ // Pick the primary geometry entry: prefer the SCOORD without a
987
+ // ConceptNameCodeSequence (plain polyline), falling back to the first SCOORD.
984
988
  if (ContentSequence) {
989
+ const scoordItem = Array.isArray(ContentSequence) ? ContentSequence.find(cs => (cs.ValueType === 'SCOORD' || cs.ValueType === 'SCOORD3D') && !cs.ConceptNameCodeSequence) ?? ContentSequence.find(cs => cs.ValueType === 'SCOORD' || cs.ValueType === 'SCOORD3D') : ContentSequence;
990
+ if (!scoordItem) {
991
+ console.warn('ContentSequence array contains no SCOORD or SCOORD3D entry, skipping annotation.');
992
+ return;
993
+ }
985
994
  const {
986
995
  ValueType
987
- } = ContentSequence;
996
+ } = scoordItem;
988
997
  if (ValueType !== 'SCOORD' && ValueType !== 'SCOORD3D') {
989
998
  console.warn(`Graphic ${ValueType} not currently supported, skipping annotation.`);
990
999
  return;
991
1000
  }
992
- const coords = _getCoordsFromSCOORDOrSCOORD3D(ContentSequence);
1001
+ const coords = _getCoordsFromSCOORDOrSCOORD3D(scoordItem);
993
1002
  if (coords) {
994
1003
  measurement.coords.push(coords);
995
1004
  }
@@ -1190,8 +1199,6 @@ function onModeEnter({
1190
1199
  ds.isHydrated = false;
1191
1200
  });
1192
1201
  }
1193
- // EXTERNAL MODULE: ../../../node_modules/dcmjs/build/dcmjs.es.js
1194
- var dcmjs_es = __webpack_require__(5842);
1195
1202
  ;// ../../../extensions/cornerstone-dicom-sr/src/utils/getFilteredCornerstoneToolState.ts
1196
1203
 
1197
1204
 
@@ -1680,10 +1687,6 @@ function choosePoints(points) {
1680
1687
 
1681
1688
 
1682
1689
 
1683
-
1684
- const {
1685
- downloadBlob
1686
- } = src/* utils */.Wp;
1687
1690
  const {
1688
1691
  MeasurementReport: commandsModule_MeasurementReport
1689
1692
  } = esm/* adaptersSR */.QX.Cornerstone3D;
@@ -1745,29 +1748,8 @@ const commandsModule = props => {
1745
1748
  /**
1746
1749
  *
1747
1750
  * @param measurementData An array of measurements from the measurements service
1748
- * @param additionalFindingTypes toolTypes that should be stored with labels as Findings
1749
- * @param options Naturalized DICOM JSON headers to merge into the displaySet.
1750
- * as opposed to Finding Sites.
1751
1751
  * that you wish to serialize.
1752
- */
1753
- downloadReport: ({
1754
- measurementData,
1755
- additionalFindingTypes,
1756
- options = {}
1757
- }) => {
1758
- const srDataset = _generateReport(measurementData, additionalFindingTypes, options);
1759
- const reportBlob = dcmjs_es/* default.data */.Ay.data.datasetToBlob(srDataset);
1760
-
1761
- //Create a URL for the binary.
1762
- downloadBlob(reportBlob, {
1763
- filename: 'dicom-sr.dcm'
1764
- });
1765
- },
1766
- /**
1767
- *
1768
- * @param measurementData An array of measurements from the measurements service
1769
- * that you wish to serialize.
1770
- * @param dataSource The dataSource that you wish to use to persist the data.
1752
+ * @param dataSource The data source name ('download', 'copyToClipboard', or a named data source).
1771
1753
  * @param additionalFindingTypes toolTypes that should be stored with labels as Findings
1772
1754
  * @param options Naturalized DICOM JSON headers to merge into the displaySet.
1773
1755
  * @return The naturalized report
@@ -1778,23 +1760,24 @@ const commandsModule = props => {
1778
1760
  additionalFindingTypes,
1779
1761
  options = {}
1780
1762
  }) => {
1781
- // Use the @cornerstonejs adapter for converting to/from DICOM
1782
- // But it is good enough for now whilst we only have cornerstone as a datasource.
1783
1763
  log.info('[DICOMSR] storeMeasurements');
1784
- if (!dataSource || !dataSource.store || !dataSource.store.dicom) {
1785
- log.error('[DICOMSR] datasource has no dataSource.store.dicom endpoint!');
1764
+ const storeFn = commandsManager.runCommand('createStoreFunction', {
1765
+ dataSource,
1766
+ defaultFileName: 'dicom-sr.dcm'
1767
+ });
1768
+ if (!storeFn) {
1769
+ log.error('[DICOMSR] No valid store for dataSource:', dataSource);
1786
1770
  return Promise.reject({});
1787
1771
  }
1788
1772
  try {
1789
1773
  const naturalizedReport = _generateReport(measurementData, additionalFindingTypes, options);
1790
1774
  const {
1791
- StudyInstanceUID,
1792
1775
  ContentSequence
1793
1776
  } = naturalizedReport;
1794
1777
  // The content sequence has 5 or more elements, of which
1795
1778
  // the `[4]` element contains the annotation data, so this is
1796
1779
  // checking that there is some annotation data present.
1797
- if (!ContentSequence?.[4].ContentSequence?.length) {
1780
+ if (!ContentSequence?.[4]?.ContentSequence?.length) {
1798
1781
  console.log('naturalizedReport missing imaging content', naturalizedReport);
1799
1782
  throw new Error('Invalid report, no content');
1800
1783
  }
@@ -1810,15 +1793,10 @@ const commandsModule = props => {
1810
1793
  naturalizedReport
1811
1794
  });
1812
1795
  }
1813
- await dataSource.store.dicom(naturalizedReport, null, dicomDict);
1814
- if (StudyInstanceUID) {
1815
- dataSource.deleteStudyMetadataPromise(StudyInstanceUID);
1816
- }
1817
-
1818
- // The "Mode" route listens for DicomMetadataStore changes
1819
- // When a new instance is added, it listens and
1820
- // automatically calls makeDisplaySets
1821
- src/* DicomMetadataStore */.H8.addInstances([naturalizedReport], true);
1796
+ await storeFn(naturalizedReport, {
1797
+ measurementData,
1798
+ dicomDict
1799
+ });
1822
1800
  return naturalizedReport;
1823
1801
  } catch (error) {
1824
1802
  console.warn(error);
@@ -1841,7 +1819,6 @@ const commandsModule = props => {
1841
1819
  }
1842
1820
  };
1843
1821
  const definitions = {
1844
- downloadReport: actions.downloadReport,
1845
1822
  storeMeasurements: actions.storeMeasurements,
1846
1823
  hydrateStructuredReport: actions.hydrateStructuredReport
1847
1824
  };
@@ -7442,37 +7442,13 @@ const ExportSegmentationSubMenuItem = ({
7442
7442
  className: "text-foreground"
7443
7443
  }), /*#__PURE__*/react.createElement("span", {
7444
7444
  className: "pl-2"
7445
- }, t('Download & Export'))), /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuPortal */.dce, null, /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuSubContent */.M56, null, /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuLabel */.lpj, {
7446
- className: "flex items-center pl-0"
7447
- }, /*#__PURE__*/react.createElement(ui_next_src/* Icons */.FI1.Download, {
7448
- className: "h-5 w-5"
7449
- }), /*#__PURE__*/react.createElement("span", {
7450
- className: "pl-1"
7451
- }, t('Download'))), segmentationRepresentationType === esm_enums.SegmentationRepresentations.Labelmap && /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuItem */._26, {
7445
+ }, t('Export'))), /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuPortal */.dce, null, /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuSubContent */.M56, null, segmentationRepresentationType === esm_enums.SegmentationRepresentations.Labelmap && /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuItem */._26, {
7452
7446
  onClick: e => {
7453
7447
  e.preventDefault();
7454
7448
  actions.downloadCSVSegmentationReport(segmentationId);
7455
7449
  },
7456
7450
  disabled: !allowExport
7457
7451
  }, t('CSV Report')), segmentationRepresentationType === esm_enums.SegmentationRepresentations.Labelmap && /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuItem */._26, {
7458
- onClick: e => {
7459
- e.preventDefault();
7460
- actions.onSegmentationDownload(segmentationId);
7461
- },
7462
- disabled: !allowExport
7463
- }, t('DICOM SEG')), /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuItem */._26, {
7464
- onClick: e => {
7465
- e.preventDefault();
7466
- actions.onSegmentationDownloadRTSS(segmentationId);
7467
- },
7468
- disabled: !allowExport
7469
- }, t('DICOM RTSS')), /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuSeparator */.mBJ, null), /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuLabel */.lpj, {
7470
- className: "flex items-center pl-0"
7471
- }, /*#__PURE__*/react.createElement(ui_next_src/* Icons */.FI1.Export, {
7472
- className: "h-5 w-5"
7473
- }), /*#__PURE__*/react.createElement("span", {
7474
- className: "pl-1 pt-1"
7475
- }, t('Export'))), segmentationRepresentationType === esm_enums.SegmentationRepresentations.Labelmap && /*#__PURE__*/react.createElement(ui_next_src/* DropdownMenuItem */._26, {
7476
7452
  onClick: e => {
7477
7453
  e.preventDefault();
7478
7454
  actions.storeSegmentation(segmentationId, 'SEG');
@@ -7549,16 +7525,6 @@ const CustomDropdownMenuContent = () => {
7549
7525
  context: 'CORNERSTONE'
7550
7526
  });
7551
7527
  },
7552
- onSegmentationDownloadRTSS: segmentationId => {
7553
- commandsManager.run('downloadRTSS', {
7554
- segmentationId
7555
- });
7556
- },
7557
- onSegmentationDownload: segmentationId => {
7558
- commandsManager.run('downloadSegmentation', {
7559
- segmentationId
7560
- });
7561
- },
7562
7528
  downloadCSVSegmentationReport: segmentationId => {
7563
7529
  commandsManager.run('downloadCSVSegmentationReport', {
7564
7530
  segmentationId
@@ -803,8 +803,9 @@ function TrackedMeasurementsContextProvider({
803
803
  });
804
804
  },
805
805
  showStructuredReportDisplaySetInActiveViewport: (ctx, evt) => {
806
- if (evt.data.createdDisplaySetInstanceUIDs.length > 0) {
807
- const StructuredReportDisplaySetInstanceUID = evt.data.createdDisplaySetInstanceUIDs[0];
806
+ const uids = evt.data?.createdDisplaySetInstanceUIDs;
807
+ if (uids?.length > 0) {
808
+ const StructuredReportDisplaySetInstanceUID = uids[0];
808
809
  viewportGridService.setDisplaySetsForViewport({
809
810
  viewportId: evt.data.viewportId,
810
811
  displaySetInstanceUIDs: [StructuredReportDisplaySetInstanceUID]
@@ -363,7 +363,6 @@ var PROMPT_RESPONSES = __webpack_require__(96357);
363
363
 
364
364
 
365
365
 
366
-
367
366
  const getTargetViewport = ({
368
367
  viewportId,
369
368
  viewportGridService
@@ -390,12 +389,10 @@ const {
390
389
  }
391
390
  }
392
391
  } = adapters_dist_esm/* adaptersRT */.f_;
393
- const {
394
- /* downloadDICOMData */ "vk": downloadDICOMData
395
- } = adapters_dist_esm/* helpers */._$;
396
392
  const commandsModule = ({
397
393
  servicesManager,
398
- extensionManager
394
+ extensionManager,
395
+ commandsManager
399
396
  }) => {
400
397
  const {
401
398
  segmentationService,
@@ -548,7 +545,11 @@ const commandsModule = ({
548
545
  const generatedSegmentation = actions.generateSegmentation({
549
546
  segmentationId
550
547
  });
551
- downloadDICOMData(generatedSegmentation.dataset, `${segmentationInOHIF.label}`);
548
+ const storeFn = commandsManager.runCommand('createStoreFunction', {
549
+ dataSource: 'download',
550
+ defaultFileName: `${segmentationInOHIF.label}.dcm`
551
+ });
552
+ storeFn(generatedSegmentation.dataset);
552
553
  },
553
554
  /**
554
555
  * Stores a segmentation based on the provided segmentationId into a specified data source.
@@ -575,10 +576,9 @@ const commandsModule = ({
575
576
  label,
576
577
  predecessorImageId
577
578
  } = segmentation;
578
- const defaultDataSource = dataSource ?? extensionManager.getActiveDataSource()[0];
579
579
  const {
580
580
  value: reportName,
581
- dataSourceName: selectedDataSource,
581
+ dataSourceName,
582
582
  series,
583
583
  priorSeriesNumber,
584
584
  action
@@ -587,42 +587,47 @@ const commandsModule = ({
587
587
  extensionManager,
588
588
  predecessorImageId,
589
589
  title: 'Store Segmentation',
590
- modality
590
+ modality,
591
+ enableDownload: true
591
592
  });
592
- if (action === PROMPT_RESPONSES/* default */.A.CREATE_REPORT) {
593
- try {
594
- const selectedDataSourceConfig = selectedDataSource ? extensionManager.getDataSources(selectedDataSource)[0] : defaultDataSource;
595
- const args = {
596
- segmentationId,
597
- options: {
598
- SeriesDescription: series ? undefined : reportName || label || 'Contour Series',
599
- SeriesNumber: series ? undefined : 1 + priorSeriesNumber,
600
- predecessorImageId: series
601
- }
602
- };
603
- const generatedDataAsync = modality === 'SEG' && actions.generateSegmentation(args) || modality === 'RTSTRUCT' && actions.generateContour(args);
604
- const generatedData = await generatedDataAsync;
605
- if (!generatedData || !generatedData.dataset) {
606
- throw new Error('Error during segmentation generation');
607
- }
608
- const {
609
- dataset: naturalizedReport
610
- } = generatedData;
611
-
612
- // DCMJS assigns a dummy study id during creation, and this can cause problems, so clearing it out
613
- if (naturalizedReport.StudyID === 'No Study ID') {
614
- naturalizedReport.StudyID = '';
593
+ if (action !== PROMPT_RESPONSES/* default */.A.CREATE_REPORT) {
594
+ return;
595
+ }
596
+ const defaultFileName = modality === 'RTSTRUCT' ? `rtss-${segmentationId}.dcm` : `${label || 'segmentation'}.dcm`;
597
+ const storeFn = commandsManager.runCommand('createStoreFunction', {
598
+ dataSource: dataSourceName,
599
+ defaultFileName
600
+ });
601
+ if (!storeFn) {
602
+ throw new Error(`No valid store for dataSource: ${dataSourceName}`);
603
+ }
604
+ try {
605
+ const args = {
606
+ segmentationId,
607
+ options: {
608
+ SeriesDescription: series ? undefined : reportName || label || 'Contour Series',
609
+ SeriesNumber: series ? undefined : 1 + priorSeriesNumber,
610
+ predecessorImageId: series
615
611
  }
616
- await selectedDataSourceConfig.store.dicom(naturalizedReport);
612
+ };
613
+ const generatedDataAsync = modality === 'SEG' && actions.generateSegmentation(args) || modality === 'RTSTRUCT' && actions.generateContour(args);
614
+ const generatedData = await generatedDataAsync;
615
+ if (!generatedData?.dataset) {
616
+ throw new Error('Error during segmentation generation');
617
+ }
618
+ const {
619
+ dataset: naturalizedReport
620
+ } = generatedData;
617
621
 
618
- // add the information for where we stored it to the instance as well
619
- naturalizedReport.wadoRoot = selectedDataSourceConfig.getConfig().wadoRoot;
620
- src/* DicomMetadataStore */.H8.addInstances([naturalizedReport], true);
621
- return naturalizedReport;
622
- } catch (error) {
623
- console.debug('Error storing segmentation:', error);
624
- throw error;
622
+ // DCMJS assigns a dummy study id during creation, and this can cause problems, so clearing it out
623
+ if (naturalizedReport.StudyID === 'No Study ID') {
624
+ naturalizedReport.StudyID = '';
625
625
  }
626
+ await storeFn(naturalizedReport, {});
627
+ return naturalizedReport;
628
+ } catch (error) {
629
+ console.debug('Error storing segmentation:', error);
630
+ throw error;
626
631
  }
627
632
  },
628
633
  generateContour: async args => {
@@ -658,13 +663,11 @@ const commandsModule = ({
658
663
  InstanceNumber: instanceNumber = 1,
659
664
  SeriesInstanceUID: seriesUID
660
665
  } = dataset;
661
- try {
662
- //Create a URL for the binary.
663
- const filename = `rtss-${seriesUID}-${instanceNumber}.dcm`;
664
- downloadDICOMData(dataset, filename);
665
- } catch (e) {
666
- console.warn(e);
667
- }
666
+ const storeFn = commandsManager.runCommand('createStoreFunction', {
667
+ dataSource: 'download',
668
+ defaultFileName: `rtss-${seriesUID}-${instanceNumber}.dcm`
669
+ });
670
+ await storeFn(dataset);
668
671
  },
669
672
  toggleActiveSegmentationUtility: ({
670
673
  itemId: buttonId
@@ -12,11 +12,10 @@ __webpack_require__.d(__webpack_exports__, {
12
12
  X6: () => (/* reexport */ adaptersPMAP),
13
13
  f_: () => (/* reexport */ adaptersRT),
14
14
  ql: () => (/* reexport */ adaptersSEG),
15
- QX: () => (/* reexport */ adaptersSR),
16
- _$: () => (/* reexport */ helpers_namespaceObject)
15
+ QX: () => (/* reexport */ adaptersSR)
17
16
  });
18
17
 
19
- // UNUSED EXPORTS: utilities
18
+ // UNUSED EXPORTS: helpers, utilities
20
19
 
21
20
  // NAMESPACE OBJECT: ../../../node_modules/@cornerstonejs/adapters/dist/esm/adapters/Cornerstone/Segmentation.js
22
21
  var Segmentation_namespaceObject = {};
@@ -63,13 +62,6 @@ __webpack_require__.d(enums_namespaceObject, {
63
62
  s: () => (Events)
64
63
  });
65
64
 
66
- // NAMESPACE OBJECT: ../../../node_modules/@cornerstonejs/adapters/dist/esm/adapters/helpers/index.js
67
- var helpers_namespaceObject = {};
68
- __webpack_require__.r(helpers_namespaceObject);
69
- __webpack_require__.d(helpers_namespaceObject, {
70
- vk: () => (downloadDICOMData)
71
- });
72
-
73
65
  // EXTERNAL MODULE: ../../../node_modules/dcmjs/build/dcmjs.es.js
74
66
  var dcmjs_es = __webpack_require__(5842);
75
67
  ;// ../../../node_modules/@cornerstonejs/adapters/dist/esm/adapters/helpers/toArray.js
@@ -4663,7 +4663,8 @@ function CreateReportDialogPrompt({
4663
4663
  minSeriesNumber = 0,
4664
4664
  predecessorImageId,
4665
4665
  extensionManager,
4666
- servicesManager
4666
+ servicesManager,
4667
+ enableDownload = false
4667
4668
  }) {
4668
4669
  const {
4669
4670
  uiDialogService,
@@ -4683,6 +4684,7 @@ function CreateReportDialogPrompt({
4683
4684
  predecessorImageId,
4684
4685
  minSeriesNumber,
4685
4686
  modality,
4687
+ enableDownload,
4686
4688
  onSave: async ({
4687
4689
  reportName,
4688
4690
  dataSource: selectedDataSource,
@@ -7516,8 +7518,6 @@ const useToggleOneUpViewportGridStore = (0,zustand_esm/* create */.vt)(set => ({
7516
7518
  })
7517
7519
  }));
7518
7520
  ;// ../../../extensions/default/src/Actions/createReportAsync.tsx
7519
-
7520
-
7521
7521
  /**
7522
7522
  *
7523
7523
  * @param {*} servicesManager
@@ -7525,7 +7525,8 @@ const useToggleOneUpViewportGridStore = (0,zustand_esm/* create */.vt)(set => ({
7525
7525
  async function createReportAsync({
7526
7526
  servicesManager,
7527
7527
  getReport,
7528
- reportType = 'measurement'
7528
+ reportType = 'Measurements',
7529
+ successMessage
7529
7530
  }) {
7530
7531
  const {
7531
7532
  displaySetService,
@@ -7538,15 +7539,13 @@ async function createReportAsync({
7538
7539
  return;
7539
7540
  }
7540
7541
 
7541
- // The "Mode" route listens for DicomMetadataStore changes
7542
- // When a new instance is added, it listens and
7543
- // automatically calls makeDisplaySets
7544
- src/* DicomMetadataStore */.H8.addInstances([naturalizedReport], true);
7542
+ // addInstances is called by the store command (storeMeasurements/storeSegmentation),
7543
+ // so the display set should already exist at this point.
7545
7544
  const displaySet = displaySetService.getMostRecentDisplaySet();
7546
7545
  const displaySetInstanceUID = displaySet.displaySetInstanceUID;
7547
7546
  uiNotificationService.show({
7548
7547
  title: 'Create Report',
7549
- message: `${reportType} saved successfully`,
7548
+ message: successMessage ?? `${reportType} saved successfully`,
7550
7549
  type: 'success'
7551
7550
  });
7552
7551
  return [displaySetInstanceUID];
@@ -7602,34 +7601,32 @@ async function promptSaveReport({
7602
7601
  predecessorImageId,
7603
7602
  minSeriesNumber: 3000,
7604
7603
  extensionManager,
7605
- servicesManager
7604
+ servicesManager,
7605
+ enableDownload: true
7606
7606
  });
7607
7607
  if (promptResult.action === PROMPT_RESPONSES/* default */.A.CREATE_REPORT) {
7608
- const dataSources = extensionManager.getDataSources(promptResult.dataSourceName);
7609
- const dataSource = dataSources[0];
7610
7608
  const {
7611
7609
  series,
7612
7610
  priorSeriesNumber,
7613
- value: reportName
7611
+ value: reportName,
7612
+ dataSourceName
7614
7613
  } = promptResult;
7615
7614
  const SeriesDescription = reportName || defaultSaveTitle;
7616
- const getReport = async () => {
7617
- return commandsManager.runCommand('storeMeasurements', {
7618
- measurementData,
7619
- dataSource,
7620
- additionalFindingTypes: ['ArrowAnnotate'],
7621
- options: {
7622
- SeriesDescription,
7623
- SeriesNumber: 1 + priorSeriesNumber,
7624
- predecessorImageId: series
7625
- }
7626
- }, 'CORNERSTONE_STRUCTURED_REPORT');
7627
- };
7615
+ const getReport = async () => commandsManager.runCommand('storeMeasurements', {
7616
+ measurementData,
7617
+ dataSource: dataSourceName,
7618
+ additionalFindingTypes: ['ArrowAnnotate'],
7619
+ options: {
7620
+ SeriesDescription,
7621
+ SeriesNumber: 1 + priorSeriesNumber,
7622
+ predecessorImageId: series
7623
+ }
7624
+ }, 'CORNERSTONE_STRUCTURED_REPORT');
7628
7625
  displaySetInstanceUIDs = await Actions_createReportAsync({
7629
7626
  servicesManager,
7630
7627
  getReport
7631
7628
  });
7632
- } else if (promptResult.action === RESPONSE.CANCEL) {
7629
+ } else if (promptResult.action === PROMPT_RESPONSES/* default */.A.CANCEL) {
7633
7630
  // Do nothing
7634
7631
  }
7635
7632
  return {
@@ -7661,6 +7658,10 @@ function findPredecessorImageId(annotations) {
7661
7658
  ;// ../../../extensions/default/src/commandsModule.ts
7662
7659
 
7663
7660
 
7661
+ const {
7662
+ downloadBlob
7663
+ } = src/* utils */.Wp;
7664
+
7664
7665
 
7665
7666
 
7666
7667
 
@@ -8381,6 +8382,74 @@ const commandsModule = ({
8381
8382
  viewportsToUpdate: updatedViewports
8382
8383
  });
8383
8384
  setTimeout(() => actions.scrollActiveThumbnailIntoView(), 0);
8385
+ },
8386
+ /**
8387
+ * Creates a store function based on the data source type.
8388
+ * @param dataSource - 'download', 'copyToClipboard', or a named data source
8389
+ * @param defaultFileName - Default filename for download/clipboard
8390
+ * @param defaultContentType - Default content type for clipboard
8391
+ * @returns A store function, or null if no valid store exists
8392
+ */
8393
+ createStoreFunction: ({
8394
+ dataSource,
8395
+ defaultFileName,
8396
+ defaultContentType
8397
+ }) => {
8398
+ if (dataSource === 'download') {
8399
+ return async dicom => {
8400
+ const instances = Array.isArray(dicom) ? dicom : [dicom];
8401
+ src/* DicomMetadataStore */.H8.addInstances(instances, true);
8402
+ if (instances.length !== 1) {
8403
+ throw new Error('Download only supports a single DICOM instance');
8404
+ }
8405
+ const reportBlob = dcmjs_es/* default.data */.Ay.data.datasetToBlob(instances[0]);
8406
+ downloadBlob(reportBlob, {
8407
+ filename: defaultFileName || 'dicom.dcm'
8408
+ });
8409
+ };
8410
+ }
8411
+ if (dataSource === 'copyToClipboard') {
8412
+ return async dicom => {
8413
+ const instances = Array.isArray(dicom) ? dicom : [dicom];
8414
+ src/* DicomMetadataStore */.H8.addInstances(instances, true);
8415
+ if (instances.length !== 1) {
8416
+ throw new Error('Copy to clipboard only supports a single DICOM instance');
8417
+ }
8418
+ const reportBlob = dcmjs_es/* default.data */.Ay.data.datasetToBlob(instances[0]);
8419
+ const type = defaultContentType || 'application/dicom';
8420
+ await navigator.clipboard.write([new ClipboardItem({
8421
+ [type]: new Blob([reportBlob], {
8422
+ type
8423
+ })
8424
+ })]);
8425
+ };
8426
+ }
8427
+
8428
+ // DICOM STOW path — resolve the named data source
8429
+ const dataSources = extensionManager.getDataSources(dataSource);
8430
+ const resolvedDataSource = dataSources?.[0];
8431
+ if (!resolvedDataSource?.store?.dicom) {
8432
+ return null;
8433
+ }
8434
+ return async (dicom, {
8435
+ dicomDict
8436
+ } = {}) => {
8437
+ const instances = Array.isArray(dicom) ? dicom : [dicom];
8438
+ const config = resolvedDataSource.getConfig?.();
8439
+ if (config?.wadoRoot) {
8440
+ instances.forEach(instance => {
8441
+ instance.wadoRoot = config.wadoRoot;
8442
+ });
8443
+ }
8444
+ src/* DicomMetadataStore */.H8.addInstances(instances, true);
8445
+ for (const instance of instances) {
8446
+ await resolvedDataSource.store.dicom(instance, null, dicomDict);
8447
+ }
8448
+ const studyUIDs = new Set(instances.map(i => i.StudyInstanceUID).filter(Boolean));
8449
+ for (const uid of studyUIDs) {
8450
+ resolvedDataSource.deleteStudyMetadataPromise(uid);
8451
+ }
8452
+ };
8384
8453
  }
8385
8454
  };
8386
8455
  const definitions = {
@@ -8412,7 +8481,8 @@ const commandsModule = ({
8412
8481
  updateViewportDisplaySet: actions.updateViewportDisplaySet,
8413
8482
  scrollActiveThumbnailIntoView: actions.scrollActiveThumbnailIntoView,
8414
8483
  addDisplaySetAsLayer: actions.addDisplaySetAsLayer,
8415
- removeDisplaySetLayer: actions.removeDisplaySetLayer
8484
+ removeDisplaySetLayer: actions.removeDisplaySetLayer,
8485
+ createStoreFunction: actions.createStoreFunction
8416
8486
  };
8417
8487
  return {
8418
8488
  actions,
@@ -10402,8 +10472,8 @@ function AboutModalDefault() {
10402
10472
  name
10403
10473
  } = (0,browser_detect_es5/* default */.A)();
10404
10474
  const browser = `${name[0].toUpperCase()}${name.substr(1)} ${version}`;
10405
- const versionNumber = "3.13.0-beta.42";
10406
- const commitHash = "1df671e9ab67d43f98bf09f6838ddc0a5f220277";
10475
+ const versionNumber = "3.13.0-beta.43";
10476
+ const commitHash = "1d4802c2a38dd75dca7afca87913e100453c9135";
10407
10477
  const [main, beta] = versionNumber.split('-');
10408
10478
  return /*#__PURE__*/react.createElement(ui_next_src/* AboutModal */.VTU, {
10409
10479
  className: "w-[400px]"
@@ -10576,6 +10646,7 @@ function UserPreferencesModalDefault({
10576
10646
 
10577
10647
 
10578
10648
 
10649
+
10579
10650
  function ReportDialog({
10580
10651
  dataSources,
10581
10652
  modality = 'SR',
@@ -10583,11 +10654,16 @@ function ReportDialog({
10583
10654
  minSeriesNumber = 3000,
10584
10655
  hide,
10585
10656
  onSave,
10586
- onCancel
10657
+ onCancel,
10658
+ enableDownload = false
10587
10659
  }) {
10660
+ const {
10661
+ t
10662
+ } = (0,es/* useTranslation */.Bd)('Buttons');
10588
10663
  const {
10589
10664
  servicesManager
10590
10665
  } = (0,src/* useSystem */.Jg)();
10666
+ const actionTakenRef = (0,react.useRef)(false);
10591
10667
  const [selectedDataSource, setSelectedDataSource] = (0,react.useState)(dataSources?.[0]?.value ?? null);
10592
10668
  const {
10593
10669
  displaySetService
@@ -10616,6 +10692,7 @@ function ReportDialog({
10616
10692
  setReportName(newReportName);
10617
10693
  }, [selectedSeries, seriesOptions]);
10618
10694
  const handleSave = (0,react.useCallback)(() => {
10695
+ actionTakenRef.current = true;
10619
10696
  onSave({
10620
10697
  reportName,
10621
10698
  dataSource: selectedDataSource,
@@ -10625,10 +10702,31 @@ function ReportDialog({
10625
10702
  hide();
10626
10703
  }, [selectedDataSource, selectedSeries, reportName, hide, onSave]);
10627
10704
  const handleCancel = (0,react.useCallback)(() => {
10705
+ actionTakenRef.current = true;
10628
10706
  onCancel();
10629
10707
  hide();
10630
10708
  }, [onCancel, hide]);
10709
+ const handleDownload = (0,react.useCallback)(() => {
10710
+ actionTakenRef.current = true;
10711
+ onSave({
10712
+ reportName,
10713
+ dataSource: 'download',
10714
+ priorSeriesNumber: Math.max(...seriesOptions.map(it => it.seriesNumber)),
10715
+ series: selectedSeries
10716
+ });
10717
+ hide();
10718
+ }, [selectedDataSource, selectedSeries, reportName, hide, onSave]);
10719
+
10720
+ // Handles the close dialog button/external close as a cancel
10721
+ (0,react.useEffect)(() => {
10722
+ return () => {
10723
+ if (!actionTakenRef.current) {
10724
+ onCancel();
10725
+ }
10726
+ };
10727
+ }, [onCancel]);
10631
10728
  const showDataSourceSelect = dataSources?.length > 1;
10729
+ const showDownloadButton = enableDownload;
10632
10730
  return /*#__PURE__*/react.createElement("div", {
10633
10731
  className: "text-foreground flex min-w-[400px] max-w-md flex-col"
10634
10732
  }, /*#__PURE__*/react.createElement("div", {
@@ -10685,9 +10783,9 @@ function ReportDialog({
10685
10783
  disabled: !!selectedSeries
10686
10784
  })))), /*#__PURE__*/react.createElement("div", {
10687
10785
  className: "flex justify-end gap-2"
10688
- }, /*#__PURE__*/react.createElement(ui_next_src/* InputDialog */.fa0, null, /*#__PURE__*/react.createElement(ui_next_src/* InputDialog */.fa0.Actions, null, /*#__PURE__*/react.createElement(ui_next_src/* InputDialog */.fa0.ActionsSecondary, {
10689
- onClick: handleCancel
10690
- }, "Cancel"), /*#__PURE__*/react.createElement(ui_next_src/* InputDialog */.fa0.ActionsPrimary, {
10786
+ }, /*#__PURE__*/react.createElement(ui_next_src/* InputDialog */.fa0, null, /*#__PURE__*/react.createElement(ui_next_src/* InputDialog */.fa0.Actions, null, showDownloadButton && /*#__PURE__*/react.createElement(ui_next_src/* InputDialog */.fa0.ActionsSecondary, {
10787
+ onClick: handleDownload
10788
+ }, t('Download')), /*#__PURE__*/react.createElement(ui_next_src/* InputDialog */.fa0.ActionsPrimary, {
10691
10789
  onClick: handleSave
10692
10790
  }, "Save"))))));
10693
10791
  }