From 4d3d5e794cb99212eba06bf91dbb30a258725efe Mon Sep 17 00:00:00 2001 From: Joe Boccanfuso <109477394+jbocce@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:33:40 -0500 Subject: [PATCH] feat(measurements): Provide for the Load (SR) measurements button to optionally clear existing measurements prior to loading the SR. (#4586) --- .../src/viewports/_getStatusComponent.tsx | 10 +- .../src/viewports/_getStatusComponent.tsx | 10 +- .../src/commandsModule.ts | 29 ++++- .../OHIFCornerstoneSRMeasurementViewport.tsx | 109 +++++++----------- extensions/cornerstone-dicom-sr/src/index.tsx | 28 ++++- .../src/utils/hydrateStructuredReport.ts | 2 +- extensions/default/src/commandsModule.ts | 2 +- .../TrackedMeasurementsContext.tsx | 8 ++ extensions/measurement-tracking/src/index.tsx | 26 +++++ .../services/ToolBarService/ToolbarService.ts | 15 ++- .../ViewportActionButton.tsx | 28 +++++ .../components/ViewportActionButton/index.tsx | 2 + platform/ui/src/components/index.js | 2 + platform/ui/src/index.js | 1 + yarn.lock | 41 +------ 15 files changed, 185 insertions(+), 128 deletions(-) create mode 100644 platform/ui/src/components/ViewportActionButton/ViewportActionButton.tsx create mode 100644 platform/ui/src/components/ViewportActionButton/index.tsx diff --git a/extensions/cornerstone-dicom-rt/src/viewports/_getStatusComponent.tsx b/extensions/cornerstone-dicom-rt/src/viewports/_getStatusComponent.tsx index fcf4de300a2..d270f278d09 100644 --- a/extensions/cornerstone-dicom-rt/src/viewports/_getStatusComponent.tsx +++ b/extensions/cornerstone-dicom-rt/src/viewports/_getStatusComponent.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { Icon, Tooltip } from '@ohif/ui'; +import { Icon, Tooltip, ViewportActionButton } from '@ohif/ui'; import { Icons } from '@ohif/ui-next'; export default function _getStatusComponent({ isHydrated, onStatusClick }) { @@ -35,13 +35,7 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) { RTSTRUCT {!isHydrated && ( -
- {loadStr} -
+ {loadStr} )} ); diff --git a/extensions/cornerstone-dicom-seg/src/viewports/_getStatusComponent.tsx b/extensions/cornerstone-dicom-seg/src/viewports/_getStatusComponent.tsx index 1e2f5d7df6a..234337ededd 100644 --- a/extensions/cornerstone-dicom-seg/src/viewports/_getStatusComponent.tsx +++ b/extensions/cornerstone-dicom-seg/src/viewports/_getStatusComponent.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { Tooltip } from '@ohif/ui'; +import { Tooltip, ViewportActionButton } from '@ohif/ui'; import { Icons } from '@ohif/ui-next'; export default function _getStatusComponent({ isHydrated, onStatusClick }) { @@ -35,13 +35,7 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) { SEG {!isHydrated && ( -
- {loadStr} -
+ {loadStr} )} ); diff --git a/extensions/cornerstone-dicom-sr/src/commandsModule.ts b/extensions/cornerstone-dicom-sr/src/commandsModule.ts index e4cfb53b463..f31f783f03a 100644 --- a/extensions/cornerstone-dicom-sr/src/commandsModule.ts +++ b/extensions/cornerstone-dicom-sr/src/commandsModule.ts @@ -5,6 +5,7 @@ import dcmjs from 'dcmjs'; import { adaptersSR } from '@cornerstonejs/adapters'; import getFilteredCornerstoneToolState from './utils/getFilteredCornerstoneToolState'; +import hydrateStructuredReport from './utils/hydrateStructuredReport'; const { MeasurementReport } = adaptersSR.Cornerstone3D; const { log } = OHIF; @@ -41,8 +42,8 @@ const _generateReport = (measurementData, additionalFindingTypes, options = {}) }; const commandsModule = (props: withAppTypes) => { - const { servicesManager } = props; - const { customizationService } = servicesManager.services; + const { servicesManager, extensionManager, commandsManager } = props; + const { customizationService, displaySetService, viewportGridService } = servicesManager.services; const actions = { /** * @@ -123,6 +124,27 @@ const commandsModule = (props: withAppTypes) => { throw new Error(error.message || 'Error while saving the measurements.'); } }, + + /** + * Loads measurements by hydrating and loading the SR for the given display set instance UID + * and displays it in the active viewport. + */ + loadSRMeasurements: ({ displaySetInstanceUID }) => { + const { SeriesInstanceUIDs } = hydrateStructuredReport( + { servicesManager, extensionManager }, + displaySetInstanceUID + ); + + const displaySets = displaySetService.getDisplaySetsForSeries(SeriesInstanceUIDs[0]); + if (displaySets.length) { + viewportGridService.setDisplaySetsForViewports([ + { + viewportId: viewportGridService.getActiveViewportId(), + displaySetInstanceUIDs: [displaySets[0].displaySetInstanceUID], + }, + ]); + } + }, }; const definitions = { @@ -132,6 +154,9 @@ const commandsModule = (props: withAppTypes) => { storeMeasurements: { commandFn: actions.storeMeasurements, }, + loadSRMeasurements: { + commandFn: actions.loadSRMeasurements, + }, }; return { diff --git a/extensions/cornerstone-dicom-sr/src/components/OHIFCornerstoneSRMeasurementViewport.tsx b/extensions/cornerstone-dicom-sr/src/components/OHIFCornerstoneSRMeasurementViewport.tsx index c4c493d7857..48f8e8652fb 100644 --- a/extensions/cornerstone-dicom-sr/src/components/OHIFCornerstoneSRMeasurementViewport.tsx +++ b/extensions/cornerstone-dicom-sr/src/components/OHIFCornerstoneSRMeasurementViewport.tsx @@ -1,13 +1,11 @@ import PropTypes from 'prop-types'; import React, { useCallback, useContext, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { ExtensionManager } from '@ohif/core'; +import { ExtensionManager, useToolbar } from '@ohif/core'; import { setTrackingUniqueIdentifiersForElement } from '../tools/modules/dicomSRModule'; import { Icon, Tooltip, useViewportGrid, ViewportActionArrows } from '@ohif/ui'; -import hydrateStructuredReport from '../utils/hydrateStructuredReport'; -import { useAppConfig } from '@state'; import createReferencedImageDisplaySet from '../utils/createReferencedImageDisplaySet'; import { usePositionPresentationStore } from '@ohif/extension-cornerstone'; import { Icons } from '@ohif/ui-next'; @@ -19,10 +17,7 @@ function OHIFCornerstoneSRMeasurementViewport(props: withAppTypes) { const { children, dataSource, displaySets, viewportOptions, servicesManager, extensionManager } = props; - const [appConfig] = useAppConfig(); - - const { displaySetService, measurementService, viewportActionCornersService } = - servicesManager.services; + const { displaySetService, viewportActionCornersService } = servicesManager.services; const viewportId = viewportOptions.viewportId; @@ -47,7 +42,6 @@ function OHIFCornerstoneSRMeasurementViewport(props: withAppTypes) { // Optional hook into tracking extension, if present. let trackedMeasurements; - let sendTrackedMeasurementsEvent; const hasMeasurementTrackingExtension = extensionManager.registeredExtensionIds.includes( MEASUREMENT_TRACKING_EXTENSION_ID @@ -60,29 +54,6 @@ function OHIFCornerstoneSRMeasurementViewport(props: withAppTypes) { const tracked = useContext(contextModule.context); trackedMeasurements = tracked?.[0]; - sendTrackedMeasurementsEvent = tracked?.[1]; - } - - if (!sendTrackedMeasurementsEvent) { - // if no panels from measurement-tracking extension is used, this code will run - trackedMeasurements = null; - sendTrackedMeasurementsEvent = (eventName, { displaySetInstanceUID }) => { - measurementService.clearMeasurements(); - const { SeriesInstanceUIDs } = hydrateStructuredReport( - { servicesManager, extensionManager, appConfig }, - displaySetInstanceUID - ); - - const displaySets = displaySetService.getDisplaySetsForSeries(SeriesInstanceUIDs[0]); - if (displaySets.length) { - viewportGridService.setDisplaySetsForViewports([ - { - viewportId: activeViewportId, - displaySetInstanceUIDs: [displaySets[0].displaySetInstanceUID], - }, - ]); - } - }; } /** @@ -296,8 +267,8 @@ function OHIFCornerstoneSRMeasurementViewport(props: withAppTypes) { viewportId, isRehydratable: srDisplaySet.isRehydratable, isLocked, - sendTrackedMeasurementsEvent, t, + servicesManager, }), indexPriority: -100, location: viewportActionCornersService.LOCATIONS.topLeft, @@ -316,15 +287,7 @@ function OHIFCornerstoneSRMeasurementViewport(props: withAppTypes) { location: viewportActionCornersService.LOCATIONS.topRight, }, ]); - }, [ - isLocked, - onMeasurementChange, - sendTrackedMeasurementsEvent, - srDisplaySet, - t, - viewportActionCornersService, - viewportId, - ]); + }, [isLocked, onMeasurementChange, srDisplaySet, t, viewportActionCornersService, viewportId]); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ let childrenWithProps = null; @@ -412,16 +375,9 @@ function _getStatusComponent({ viewportId, isRehydratable, isLocked, - sendTrackedMeasurementsEvent, t, + servicesManager, }) { - const handleMouseUp = () => { - sendTrackedMeasurementsEvent('HYDRATE_SR', { - displaySetInstanceUID: srDisplaySet.displaySetInstanceUID, - viewportId, - }); - }; - const loadStr = t('LOAD'); // 1 - Incompatible @@ -467,23 +423,46 @@ function _getStatusComponent({ ToolTipMessage = () =>
{`Click ${loadStr} to restore measurements.`}
; } - const StatusArea = () => ( -
-
- - SR -
- {state === 3 && ( -
- {loadStr} + const StatusArea = () => { + const { toolbarButtons: loadSRMeasurementsButtons, onInteraction } = useToolbar({ + servicesManager, + buttonSection: 'loadSRMeasurements', + }); + + const commandOptions = { + displaySetInstanceUID: srDisplaySet.displaySetInstanceUID, + viewportId, + }; + + return ( +
+
+ + SR
- )} -
- ); + {state === 3 && ( + <> + {loadSRMeasurementsButtons.map(toolDef => { + if (!toolDef) { + return null; + } + const { id, Component, componentProps } = toolDef; + const tool = ( + onInteraction({ ...args, ...commandOptions })} + {...componentProps} + /> + ); + + return
{tool}
; + })} + + )} +
+ ); + }; return ( <> diff --git a/extensions/cornerstone-dicom-sr/src/index.tsx b/extensions/cornerstone-dicom-sr/src/index.tsx index 02380b11594..ff0c9e3cf9a 100644 --- a/extensions/cornerstone-dicom-sr/src/index.tsx +++ b/extensions/cornerstone-dicom-sr/src/index.tsx @@ -1,7 +1,6 @@ import React from 'react'; import getSopClassHandlerModule from './getSopClassHandlerModule'; import { srProtocol } from './getHangingProtocolModule'; -import onModeEnter from './onModeEnter'; import getCommandsModule from './commandsModule'; import preRegistration from './init'; import { id } from './id.js'; @@ -9,6 +8,8 @@ import toolNames from './tools/toolNames'; import hydrateStructuredReport from './utils/hydrateStructuredReport'; import createReferencedImageDisplaySet from './utils/createReferencedImageDisplaySet'; import Enums from './enums'; +import { ViewportActionButton } from '@ohif/ui'; +import i18n from '@ohif/i18n'; const Component = React.lazy(() => { return import(/* webpackPrefetch: true */ './components/OHIFCornerstoneSRViewport'); @@ -30,7 +31,30 @@ const dicomSRExtension = { * Only required property. Should be a unique value across all extensions. */ id, - onModeEnter, + + onModeEnter({ servicesManager }) { + const { toolbarService } = servicesManager.services; + + toolbarService.addButtons([ + { + // A base/default button for loading measurements. It is added to the toolbar below. + // Customizations to this button can be made in the mode or by another extension. + // For example, the button label can be changed and/or the command to clear + // the measurements can be dropped. + id: 'loadSRMeasurements', + component: props => ( + {i18n.t('Common:LOAD')} + ), + props: { + commands: ['clearMeasurements', 'loadSRMeasurements'], + }, + }, + ]); + + // The toolbar used in the viewport's status bar. Modes and extensions can further customize + // it to optionally add other buttons. + toolbarService.createButtonSection('loadSRMeasurements', ['loadSRMeasurements']); + }, preRegistration, diff --git a/extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.ts b/extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.ts index bc44814f755..43fb346ddf3 100644 --- a/extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.ts +++ b/extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.ts @@ -41,7 +41,7 @@ const convertSites = (codingValues, sites) => { * */ export default function hydrateStructuredReport( - { servicesManager, extensionManager, appConfig }: withAppTypes, + { servicesManager, extensionManager }: withAppTypes, displaySetInstanceUID ) { const annotationManager = CsAnnotation.state.getAnnotationManager(); diff --git a/extensions/default/src/commandsModule.ts b/extensions/default/src/commandsModule.ts index 8916911af3a..bac5b4a2aed 100644 --- a/extensions/default/src/commandsModule.ts +++ b/extensions/default/src/commandsModule.ts @@ -98,7 +98,7 @@ const commandsModule = ({ }); }, clearMeasurements: () => { - measurementService.clear(); + measurementService.clearMeasurements(); }, /** diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx index 5b8c008fad1..cd5b6f22baa 100644 --- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx +++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx @@ -272,6 +272,14 @@ function TrackedMeasurementsContextProvider( viewports, ]); + useEffect(() => { + // The command needs to be bound to the context's sendTrackedMeasurementsEvent + // so the command has to be registered in a React component. + commandsManager.registerCommand('DEFAULT', 'loadTrackedSRMeasurements', { + commandFn: props => sendTrackedMeasurementsEvent('HYDRATE_SR', props), + }); + }, [commandsManager, sendTrackedMeasurementsEvent]); + return ( ( + {i18n.t('Common:LOAD')} + ), + props: { + commands: ['loadTrackedSRMeasurements'], + }, + }, + ], + true // replace the button if it is already defined + ); + }, }; export default measurementTrackingExtension; diff --git a/platform/core/src/services/ToolBarService/ToolbarService.ts b/platform/core/src/services/ToolBarService/ToolbarService.ts index f8cc6addc7d..11d5383f24b 100644 --- a/platform/core/src/services/ToolBarService/ToolbarService.ts +++ b/platform/core/src/services/ToolBarService/ToolbarService.ts @@ -128,10 +128,11 @@ export default class ToolbarService extends PubSubService { /** * Adds buttons to the toolbar. * @param buttons - The buttons to be added. + * @param replace - Flag indicating if any existing button with the same id as one being added should be replaced */ - public addButtons(buttons: Button[]): void { + public addButtons(buttons: Button[], replace: boolean = false): void { buttons.forEach(button => { - if (!this.state.buttons[button.id]) { + if (replace || !this.state.buttons[button.id]) { if (!button.props) { button.props = {}; } @@ -383,12 +384,18 @@ export default class ToolbarService extends PubSubService { /** * Creates a button section with the specified key and buttons. + * Buttons already in the section (i.e. with the same ids) will NOT be added twice. * @param {string} key - The key of the button section. * @param {Array} buttons - The buttons to be added to the section. */ createButtonSection(key, buttons) { if (this.state.buttonSections[key]) { - this.state.buttonSections[key].push(...buttons); + this.state.buttonSections[key].push( + buttons.filter( + button => + !this.state.buttonSections[key].find(sectionButton => sectionButton.id === button.id) + ) + ); } else { this.state.buttonSections[key] = buttons; } @@ -452,7 +459,7 @@ export default class ToolbarService extends PubSubService { const buttonType = buttonTypes[uiType]; - if (!buttonType) { + if (!buttonType && !component) { return; } diff --git a/platform/ui/src/components/ViewportActionButton/ViewportActionButton.tsx b/platform/ui/src/components/ViewportActionButton/ViewportActionButton.tsx new file mode 100644 index 00000000000..d1624205694 --- /dev/null +++ b/platform/ui/src/components/ViewportActionButton/ViewportActionButton.tsx @@ -0,0 +1,28 @@ +import PropTypes from 'prop-types'; +import React from 'react'; + +function ViewportActionButton({ onInteraction, commands, id, children }) { + return ( +
{ + onInteraction({ + itemId: id, + commands, + }); + }} + > + {children} +
+ ); +} + +ViewportActionButton.propTypes = { + id: PropTypes.string, + onInteraction: PropTypes.func.isRequired, + commands: PropTypes.array, + children: PropTypes.node, +}; + +export default ViewportActionButton; diff --git a/platform/ui/src/components/ViewportActionButton/index.tsx b/platform/ui/src/components/ViewportActionButton/index.tsx new file mode 100644 index 00000000000..ede7d99daa9 --- /dev/null +++ b/platform/ui/src/components/ViewportActionButton/index.tsx @@ -0,0 +1,2 @@ +import ViewportActionButton from './ViewportActionButton'; +export default ViewportActionButton; diff --git a/platform/ui/src/components/index.js b/platform/ui/src/components/index.js index 0787c8a5391..cd80a766c2f 100644 --- a/platform/ui/src/components/index.js +++ b/platform/ui/src/components/index.js @@ -86,6 +86,7 @@ import LabellingFlow from './Labelling'; import SwitchButton, { SwitchLabelLocation } from './SwitchButton'; import * as AllInOneMenu from './AllInOneMenu'; import ViewportActionArrows from './ViewportActionArrows'; +import ViewportActionButton from './ViewportActionButton'; import HeaderPatientInfo from './HeaderPatientInfo'; import LegacySplitButton from './LegacySplitButton'; import { ToolSettings } from './AdvancedToolbox'; @@ -180,6 +181,7 @@ export { Viewport, ViewportActionArrows, ViewportActionBar, + ViewportActionButton, ViewportActionCorners, ViewportActionCornersLocations, ViewportDownloadForm, diff --git a/platform/ui/src/index.js b/platform/ui/src/index.js index b6c3de3a292..2efd0594d3a 100644 --- a/platform/ui/src/index.js +++ b/platform/ui/src/index.js @@ -115,6 +115,7 @@ export { Viewport, ViewportActionArrows, ViewportActionBar, + ViewportActionButton, ViewportActionCorners, ViewportActionCornersLocations, ViewportDownloadForm, diff --git a/yarn.lock b/yarn.lock index 010ea44ec19..e17113b8907 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3203,7 +3203,7 @@ "@docusaurus/theme-search-algolia" "3.6.1" "@docusaurus/types" "3.6.1" -"@docusaurus/react-loadable@5.5.2": +"@docusaurus/react-loadable@5.5.2", "react-loadable@npm:@docusaurus/react-loadable@5.5.2": version "5.5.2" resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== @@ -20853,14 +20853,6 @@ react-loadable-ssr-addon-v5-slorber@^1.0.1: dependencies: "@babel/runtime" "^7.10.3" -"react-loadable@npm:@docusaurus/react-loadable@5.5.2": - version "5.5.2" - resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz#81aae0db81ecafbdaee3651f12804580868fa6ce" - integrity sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ== - dependencies: - "@types/react" "*" - prop-types "^15.6.2" - "react-loadable@npm:@docusaurus/react-loadable@6.0.0": version "6.0.0" resolved "https://registry.yarnpkg.com/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz#de6c7f73c96542bd70786b8e522d535d69069dc4" @@ -22773,7 +22765,7 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -22791,15 +22783,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -22910,7 +22893,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -22931,13 +22914,6 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.0, strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -25255,7 +25231,7 @@ worker-loader@3.0.8, worker-loader@^3.0.8: loader-utils "^2.0.0" schema-utils "^3.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -25281,15 +25257,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"