feat: Add customization URL parameter (#5992)

* Add customization URL parameter

* fix: Preserve should be customizeable

* Update customizations docs

* fix: Overlay items on patient name

* Add customization test

* Fix resolve to absolute path

* fix: Warn on no data in load

* Remove unused customization stuff

* fix: PR comments

* Update stored parameters to only use an array for mulitples

* Remove requires ohif.* special call out

* Remove strict mode

* PR comments

* Document segmentation examples

* Add three examples as requested

* PR comments

* lock

* Remove old customizatoin export

* fix: Ordering issues on customization loads

* fix: Use correct default for dev builds app config

* Fixes for conflicts

* chore: restore pnpm-lock.yaml to match master

The lockfile diff was incidental peer-descriptor churn and carried no
functional dependency change. It tripped the CircleCI security-audit gate
(which only runs when pnpm-lock.yaml is in the PR diff), surfacing a
pre-existing critical `decompress` transitive vuln that also exists on
master. Restoring master's lockfile removes the audit trigger.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(ci): restore json5 lockfile entry; ignore unfixable decompress GHSA

The previous commit restored pnpm-lock.yaml from master, which dropped the
json5@2.2.3 entry that platform/core legitimately depends on (JSONC parsing
for the customization feature). That broke `--frozen-lockfile` install
(ERR_PNPM_OUTDATED_LOCKFILE). This restores the correct lockfile.

Because the lockfile must change (json5), the CircleCI security-audit gate
runs and previously failed on a critical `decompress` <=4.2.1 zip-slip
advisory. This is a pre-existing transitive vuln (present on master too) with
no published patch — decompress's latest release is 4.2.1, so no version
bump/override can resolve it. It reaches the tree only via @itk-wasm/dam, a
build/data-asset extraction tool under @cornerstonejs/labelmap-interpolation.

Add GHSA-mp2f-45pm-3cg9 to the existing pnpm-workspace.yaml auditConfig
ignoreGhsas accepted-risk list, matching how the repo already exempts other
build-tooling advisories. `pnpm audit --audit-level high` now passes locally
(1 critical ignored, 0 high).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(e2e): fix visitStudy URL encoding that broke mpr2 study load

The visitStudy rewrite (added for the ?customization= option) built the URL
with new URLSearchParams({ StudyInstanceUIDs: studyInstanceUID }), which
percent-encodes the value. mpr2.spec.ts embeds an extra param in the UID
string ('<uid>&hangingprotocolid=mpr'), so the & and = were encoded and the
whole thing collapsed into one invalid StudyInstanceUIDs value -> the study
could not be found ('studies are not available'), the viewer never rendered,
and the side-panel-header-right click timed out.

Restore master's raw concatenation for StudyInstanceUIDs (so embedded params
survive as separate query params) while still appending the customization
option separately. Only mpr2 embeds & in the UID, matching the single failure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* PR comments

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bill WallaceandClaude Opus 4.8 authored and GitHub committed 2026-07-07 15:30:27 -04:00
1 parent 3d9a17bc0c
commit 3dd5c70cb2
58 files changed
+3411 -586

No files matched your search

+22 -76
View File
@@ -1,11 +1,9 @@
import update from 'immutability-helper';
import { ToolbarService, utils } from '@ohif/core';
import { utils } from '@ohif/core';
import initToolGroups from './initToolGroups';
import toolbarButtons from './toolbarButtons';
import { id } from './id';
const { TOOLBAR_SECTIONS } = ToolbarService;
const { structuredCloneWithFunctions } = utils;
/**
@@ -145,9 +143,22 @@ export function onModeEnter({
// Init Default and SR ToolGroups
initToolGroups(extensionManager, toolGroupService, commandsManager);
toolbarService.register(this.toolbarButtons);
// Toolbar buttons and layout may be supplied either as a customization name
// (a string, resolved through the customization service so `?customization=`
// modules can extend the cornerstone-registered defaults) or as a literal
// value (the button array / sections object) for modes that define them inline.
const resolveToolbarCustomization = (value: unknown) =>
typeof value === 'string' ? customizationService.getCustomization(value) : value;
for (const [key, section] of Object.entries(this.toolbarSections)) {
const toolbarButtons = resolveToolbarCustomization(this.toolbarButtons) as any;
const toolbarSections = (resolveToolbarCustomization(this.toolbarSections) ?? {}) as Record<
string,
string[]
>;
toolbarService.register(toolbarButtons);
for (const [key, section] of Object.entries(toolbarSections)) {
toolbarService.updateSection(key, section);
}
@@ -212,74 +223,6 @@ export function onModeExit({ servicesManager }: withAppTypes) {
cornerstoneViewportService.destroy();
}
export const toolbarSections = {
[TOOLBAR_SECTIONS.primary]: [
'MeasurementTools',
'Zoom',
'Pan',
'TrackballRotate',
'WindowLevel',
'Capture',
'Layout',
'Crosshairs',
'MoreTools',
],
[TOOLBAR_SECTIONS.viewportActionMenu.topLeft]: ['orientationMenu', 'dataOverlayMenu'],
[TOOLBAR_SECTIONS.viewportActionMenu.bottomMiddle]: ['AdvancedRenderingControls'],
AdvancedRenderingControls: [
'windowLevelMenuEmbedded',
'voiManualControlMenu',
'Colorbar',
'opacityMenu',
'thresholdMenu',
],
[TOOLBAR_SECTIONS.viewportActionMenu.topRight]: [
'modalityLoadBadge',
'trackingStatus',
'navigationComponent',
],
[TOOLBAR_SECTIONS.viewportActionMenu.bottomLeft]: ['windowLevelMenu'],
MeasurementTools: [
'Length',
'Bidirectional',
'ArrowAnnotate',
'EllipticalROI',
'RectangleROI',
'CircleROI',
'PlanarFreehandROI',
'SplineROI',
'LivewireContour',
],
MoreTools: [
'Reset',
'rotate-right',
'flipHorizontal',
'ImageSliceSync',
'ReferenceLines',
'ImageOverlayViewer',
'StackScroll',
'invert',
'Probe',
'Cine',
'Angle',
'CobbAngle',
'Magnify',
'CalibrationLine',
'TagBrowser',
'AdvancedMagnify',
'UltrasoundDirectionalTool',
'WindowLevelRegion',
'SegmentLabelTool',
],
};
export const basicLayout = {
id: ohif.layout,
props: {
@@ -342,7 +285,10 @@ export const modeInstance = {
hide: false,
displayName: 'Non-Longitudinal Basic',
_activatePanelTriggersSubscriptions: [],
toolbarSections,
// Toolbar buttons and layout are referenced by customization name; the
// cornerstone extension registers the defaults and `?customization=` modules
// can extend them. onModeEnter resolves these names via the customization service.
toolbarSections: 'cornerstone.toolbarSections',
/**
* Lifecycle hooks
@@ -364,7 +310,7 @@ export const modeInstance = {
// general handler needs to come last. For this case, the dicomvideo must
// come first to remove video transfer syntax before ohif uses images
sopClassHandlers,
toolbarButtons,
toolbarButtons: 'cornerstone.toolbarButtons',
enableSegmentationEdit: false,
nonModeModalities: NON_IMAGE_MODALITIES,
};
@@ -389,4 +335,4 @@ export const mode = {
};
export default mode;
export { initToolGroups, toolbarButtons };
export { initToolGroups };
+1
View File
@@ -69,6 +69,7 @@ function initDefaultToolGroup(extensionManager, toolGroupService, commandsManage
{ toolName: toolNames.Angle },
{ toolName: toolNames.CobbAngle },
{ toolName: toolNames.Magnify },
{ toolName: toolNames.PlanarRotate },
{ toolName: toolNames.CalibrationLine },
{
toolName: toolNames.PlanarFreehandContourSegmentation,
-711
View File
@@ -1,711 +0,0 @@
import type { Button } from '@ohif/core/types';
import { EVENTS } from '@cornerstonejs/core';
import { ViewportGridService } from '@ohif/core';
import i18n from 'i18next';
const callbacks = (toolName: string) => [
{
commandName: 'setViewportForToolConfiguration',
commandOptions: {
toolName,
},
},
];
export const setToolActiveToolbar = {
commandName: 'setToolActiveToolbar',
commandOptions: {
toolGroupIds: ['default', 'mpr', 'SRToolGroup', 'volume3d'],
},
};
const toolbarButtons: Button[] = [
// sections
{
id: 'MeasurementTools',
uiType: 'ohif.toolButtonList',
props: {
buttonSection: true,
},
},
{
id: 'MoreTools',
uiType: 'ohif.toolButtonList',
props: {
buttonSection: true,
},
},
{
id: 'AdvancedRenderingControls',
uiType: 'ohif.advancedRenderingControls',
props: {
buttonSection: true,
},
},
// tool defs
{
id: 'modalityLoadBadge',
uiType: 'ohif.modalityLoadBadge',
props: {
icon: 'Status',
label: i18n.t('Buttons:Status'),
tooltip: i18n.t('Buttons:Status'),
evaluate: {
name: 'evaluate.modalityLoadBadge',
hideWhenDisabled: true,
},
},
},
{
id: 'navigationComponent',
uiType: 'ohif.navigationComponent',
props: {
icon: 'Navigation',
label: i18n.t('Buttons:Navigation'),
tooltip: i18n.t('Buttons:Navigate between segments/measurements and manage their visibility'),
evaluate: {
name: 'evaluate.navigationComponent',
hideWhenDisabled: true,
},
},
},
{
id: 'trackingStatus',
uiType: 'ohif.trackingStatus',
props: {
icon: 'TrackingStatus',
label: i18n.t('Buttons:Tracking Status'),
tooltip: i18n.t('Buttons:View and manage tracking status of measurements and annotations'),
evaluate: {
name: 'evaluate.trackingStatus',
hideWhenDisabled: true,
},
},
},
{
id: 'dataOverlayMenu',
uiType: 'ohif.dataOverlayMenu',
props: {
icon: 'ViewportViews',
label: i18n.t('Buttons:Data Overlay'),
tooltip: i18n.t(
'Buttons:Configure data overlay options and manage foreground/background display sets'
),
evaluate: 'evaluate.dataOverlayMenu',
},
},
{
id: 'orientationMenu',
uiType: 'ohif.orientationMenu',
props: {
icon: 'OrientationSwitch',
label: i18n.t('Buttons:Orientation'),
tooltip: i18n.t(
'Buttons:Change viewport orientation between axial, sagittal, coronal and reformat planes'
),
evaluate: {
name: 'evaluate.orientationMenu',
// hideWhenDisabled: true,
},
},
},
{
id: 'windowLevelMenuEmbedded',
uiType: 'ohif.windowLevelMenuEmbedded',
props: {
icon: 'WindowLevel',
label: i18n.t('Buttons:Window Level'),
tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'),
evaluate: {
name: 'evaluate.windowLevelMenuEmbedded',
hideWhenDisabled: true,
},
},
},
{
id: 'windowLevelMenu',
uiType: 'ohif.windowLevelMenu',
props: {
icon: 'WindowLevel',
label: i18n.t('Buttons:Window Level'),
tooltip: i18n.t('Buttons:Adjust window/level presets and customize image contrast settings'),
evaluate: {
name: 'evaluate.windowLevelMenu',
},
},
},
{
id: 'voiManualControlMenu',
uiType: 'ohif.voiManualControlMenu',
props: {
icon: 'WindowLevelAdvanced',
label: i18n.t('Buttons:Advanced Window Level'),
tooltip: i18n.t('Buttons:Advanced window/level settings with manual controls and presets'),
evaluate: 'evaluate.voiManualControlMenu',
},
},
{
id: 'thresholdMenu',
uiType: 'ohif.thresholdMenu',
props: {
icon: 'Threshold',
label: i18n.t('Buttons:Threshold'),
tooltip: i18n.t('Buttons:Image threshold settings'),
evaluate: {
name: 'evaluate.thresholdMenu',
hideWhenDisabled: true,
},
},
},
{
id: 'opacityMenu',
uiType: 'ohif.opacityMenu',
props: {
icon: 'Opacity',
label: i18n.t('Buttons:Opacity'),
tooltip: i18n.t('Buttons:Image opacity settings'),
evaluate: {
name: 'evaluate.opacityMenu',
hideWhenDisabled: true,
},
},
},
{
id: 'Colorbar',
uiType: 'ohif.colorbar',
props: {
type: 'tool',
label: i18n.t('Buttons:Colorbar'),
},
},
{
id: 'Reset',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-reset',
label: i18n.t('Buttons:Reset View'),
tooltip: i18n.t('Buttons:Reset View'),
commands: 'resetViewport',
evaluate: 'evaluate.action',
},
},
{
id: 'rotate-right',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-rotate-right',
label: i18n.t('Buttons:Rotate Right'),
tooltip: i18n.t('Buttons:Rotate +90'),
commands: 'rotateViewportCW',
evaluate: [
'evaluate.action',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['video'],
},
],
},
},
{
id: 'flipHorizontal',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-flip-horizontal',
label: i18n.t('Buttons:Flip Horizontal'),
tooltip: i18n.t('Buttons:Flip Horizontally'),
commands: 'flipViewportHorizontal',
evaluate: [
'evaluate.viewportProperties.toggle',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['video', 'volume3d'],
},
],
},
},
{
id: 'ImageSliceSync',
uiType: 'ohif.toolButton',
props: {
icon: 'link',
label: i18n.t('Buttons:Image Slice Sync'),
tooltip: i18n.t('Buttons:Enable position synchronization on stack viewports'),
commands: {
commandName: 'toggleSynchronizer',
commandOptions: {
type: 'imageSlice',
},
},
listeners: {
[EVENTS.VIEWPORT_NEW_IMAGE_SET]: {
commandName: 'toggleImageSliceSync',
commandOptions: { toggledState: true },
},
},
evaluate: [
'evaluate.cornerstone.synchronizer',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['video', 'volume3d'],
},
],
},
},
{
id: 'ReferenceLines',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-referenceLines',
label: i18n.t('Buttons:Reference Lines'),
tooltip: i18n.t('Buttons:Show Reference Lines'),
commands: 'toggleEnabledDisabledToolbar',
listeners: {
[ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: callbacks('ReferenceLines'),
[ViewportGridService.EVENTS.VIEWPORTS_READY]: callbacks('ReferenceLines'),
},
evaluate: [
'evaluate.cornerstoneTool.toggle',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['video'],
},
],
},
},
{
id: 'ImageOverlayViewer',
uiType: 'ohif.toolButton',
props: {
icon: 'toggle-dicom-overlay',
label: i18n.t('Buttons:Image Overlay'),
tooltip: i18n.t('Buttons:Toggle Image Overlay'),
commands: 'toggleEnabledDisabledToolbar',
evaluate: [
'evaluate.cornerstoneTool.toggle',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['video'],
},
],
},
},
{
id: 'StackScroll',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-stack-scroll',
label: i18n.t('Buttons:Stack Scroll'),
tooltip: i18n.t('Buttons:Stack Scroll'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'invert',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-invert',
label: i18n.t('Buttons:Invert'),
tooltip: i18n.t('Buttons:Invert Colors'),
commands: 'invertViewport',
evaluate: [
'evaluate.viewportProperties.toggle',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['video'],
},
],
},
},
{
id: 'Probe',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-probe',
label: i18n.t('Buttons:Probe'),
tooltip: i18n.t('Buttons:Probe'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'Cine',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-cine',
label: i18n.t('Buttons:Cine'),
tooltip: i18n.t('Buttons:Cine'),
commands: 'toggleCine',
evaluate: [
'evaluate.cine',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['volume3d'],
},
],
},
},
{
id: 'Angle',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-angle',
label: i18n.t('Buttons:Angle'),
tooltip: i18n.t('Buttons:Angle'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'CobbAngle',
uiType: 'ohif.toolButton',
props: {
icon: 'icon-tool-cobb-angle',
label: i18n.t('Buttons:Cobb Angle'),
tooltip: i18n.t('Buttons:Cobb Angle'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'Magnify',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-magnify',
label: i18n.t('Buttons:Zoom-in'),
tooltip: i18n.t('Buttons:Zoom-in'),
commands: setToolActiveToolbar,
evaluate: [
'evaluate.cornerstoneTool',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['video'],
},
],
},
},
{
id: 'CalibrationLine',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-calibration',
label: i18n.t('Buttons:Calibration'),
tooltip: i18n.t('Buttons:Calibration Line'),
commands: setToolActiveToolbar,
evaluate: [
'evaluate.cornerstoneTool',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['video'],
},
],
},
},
{
id: 'TagBrowser',
uiType: 'ohif.toolButton',
props: {
icon: 'dicom-tag-browser',
label: i18n.t('Buttons:Dicom Tag Browser'),
tooltip: i18n.t('Buttons:Dicom Tag Browser'),
commands: 'openDICOMTagViewer',
},
},
{
id: 'AdvancedMagnify',
uiType: 'ohif.toolButton',
props: {
icon: 'icon-tool-loupe',
label: i18n.t('Buttons:Magnify Probe'),
tooltip: i18n.t('Buttons:Magnify Probe'),
commands: 'toggleActiveDisabledToolbar',
evaluate: [
'evaluate.cornerstoneTool.toggle.ifStrictlyDisabled',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['video'],
},
],
},
},
{
id: 'UltrasoundDirectionalTool',
uiType: 'ohif.toolButton',
props: {
icon: 'icon-tool-ultrasound-bidirectional',
label: i18n.t('Buttons:Ultrasound Directional'),
tooltip: i18n.t('Buttons:Ultrasound Directional'),
commands: setToolActiveToolbar,
evaluate: [
'evaluate.cornerstoneTool',
{
name: 'evaluate.modality.supported',
supportedModalities: ['US'],
},
],
},
},
{
id: 'WindowLevelRegion',
uiType: 'ohif.toolButton',
props: {
icon: 'icon-tool-window-region',
label: i18n.t('Buttons:Window Level Region'),
tooltip: i18n.t('Buttons:Window Level Region'),
commands: setToolActiveToolbar,
evaluate: [
'evaluate.cornerstoneTool',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['video'],
},
],
},
},
{
id: 'Length',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-length',
label: i18n.t('Buttons:Length'),
tooltip: i18n.t('Buttons:Length Tool'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'Bidirectional',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-bidirectional',
label: i18n.t('Buttons:Bidirectional'),
tooltip: i18n.t('Buttons:Bidirectional Tool'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'ArrowAnnotate',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-annotate',
label: i18n.t('Buttons:Annotation'),
tooltip: i18n.t('Buttons:Arrow Annotate'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'EllipticalROI',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-ellipse',
label: i18n.t('Buttons:Ellipse'),
tooltip: i18n.t('Buttons:Ellipse ROI'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'RectangleROI',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-rectangle',
label: i18n.t('Buttons:Rectangle'),
tooltip: i18n.t('Buttons:Rectangle ROI'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'CircleROI',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-circle',
label: i18n.t('Buttons:Circle'),
tooltip: i18n.t('Buttons:Circle Tool'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'PlanarFreehandROI',
uiType: 'ohif.toolButton',
props: {
icon: 'icon-tool-freehand-roi',
label: i18n.t('Buttons:Freehand ROI'),
tooltip: i18n.t('Buttons:Freehand ROI'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'SplineROI',
uiType: 'ohif.toolButton',
props: {
icon: 'icon-tool-spline-roi',
label: i18n.t('Buttons:Spline ROI'),
tooltip: i18n.t('Buttons:Spline ROI'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'LivewireContour',
uiType: 'ohif.toolButton',
props: {
icon: 'icon-tool-livewire',
label: i18n.t('Buttons:Livewire tool'),
tooltip: i18n.t('Buttons:Livewire tool'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
// Window Level
{
id: 'WindowLevel',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-window-level',
label: i18n.t('Buttons:Window Level'),
commands: setToolActiveToolbar,
evaluate: [
'evaluate.cornerstoneTool',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['wholeSlide'],
},
],
},
},
{
id: 'Pan',
uiType: 'ohif.toolButton',
props: {
type: 'tool',
icon: 'tool-move',
label: i18n.t('Buttons:Pan'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'Zoom',
uiType: 'ohif.toolButton',
props: {
type: 'tool',
icon: 'tool-zoom',
label: i18n.t('Buttons:Zoom'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'TrackballRotate',
uiType: 'ohif.toolButton',
props: {
type: 'tool',
icon: 'tool-3d-rotate',
label: i18n.t('Buttons:3D Rotate'),
commands: setToolActiveToolbar,
evaluate: {
name: 'evaluate.cornerstoneTool',
disabledText: i18n.t('Buttons:Select a 3D viewport to enable this tool'),
},
},
},
{
id: 'Capture',
uiType: 'ohif.toolButton',
props: {
icon: 'tool-capture',
label: i18n.t('Buttons:Capture'),
commands: 'showDownloadViewportModal',
evaluate: [
'evaluate.action',
{
name: 'evaluate.viewport.supported',
unsupportedViewportTypes: ['video', 'wholeSlide'],
},
],
},
},
{
id: 'Layout',
uiType: 'ohif.layoutSelector',
props: {
rows: 3,
columns: 4,
evaluate: 'evaluate.action',
},
},
{
id: 'Crosshairs',
uiType: 'ohif.toolButton',
props: {
type: 'tool',
icon: 'tool-crosshair',
label: i18n.t('Buttons:Crosshairs'),
tooltip: i18n.t('Buttons:Click to toggle on or off'),
commands: {
commandName: 'toggleActiveDisabledToolbar',
commandOptions: {
toolGroupIds: ['mpr'],
},
},
evaluate: {
name: 'evaluate.cornerstoneTool.toggleWithModifier',
disabledText: i18n.t('Buttons:Select an MPR viewport to enable this tool'),
toggledOnIcon: 'tool-crosshair-checked',
defaultIcon: 'tool-crosshair',
},
},
},
{
id: 'SegmentLabelTool',
uiType: 'ohif.toolBoxButton',
props: {
icon: 'tool-segment-label',
label: i18n.t('Buttons:Segment Label Display'),
tooltip: i18n.t(
'Buttons:Click to show or hide segment labels when hovering with your mouse.'
),
commands: { commandName: 'toggleSegmentLabel' },
evaluate: [
'evaluate.cornerstoneTool.toggle',
{
name: 'evaluate.cornerstone.hasSegmentation',
},
],
},
},
// {
// id: 'Undo',
// uiType: 'ohif.toolButton',
// props: {
// type: 'tool',
// icon: 'prev-arrow',
// label: 'Undo',
// commands: {
// commandName: 'undo',
// },
// evaluate: 'evaluate.action',
// },
// },
// {
// id: 'Redo',
// uiType: 'ohif.toolButton',
// props: {
// type: 'tool',
// icon: 'next-arrow',
// label: 'Redo',
// commands: {
// commandName: 'redo',
// },
// evaluate: 'evaluate.action',
// },
// },
];
export default toolbarButtons;
+2 -2
View File
@@ -1,6 +1,6 @@
import i18n from 'i18next';
import { id } from './id';
import { initToolGroups, toolbarButtons, cornerstone,
import { initToolGroups, cornerstone,
ohif,
dicomsr,
dicomvideo,
@@ -73,4 +73,4 @@ const mode = {
};
export default mode;
export { initToolGroups, toolbarButtons };
export { initToolGroups };