@ohif/app 3.13.0-beta.66 → 3.13.0-beta.68

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 (28) hide show
  1. package/dist/{2851.bundle.8757dd07775645121b52.js → 2851.bundle.2f84126ab3178c254a4f.js} +89 -7
  2. package/dist/{5485.bundle.adc11bc68e91358ea7bf.js → 5485.bundle.b2de6b5aee00e1366dbf.js} +10 -4
  3. package/dist/{7166.bundle.12ada8e535973df9c8fd.js → 7166.bundle.a446247d3420500ebf08.js} +245 -25
  4. package/dist/{9927.bundle.c2acd65fc6ae5ee07148.js → 9927.bundle.57766e5471d115426d9f.js} +4 -0
  5. package/dist/app.bundle.css +1 -1
  6. package/dist/{app.bundle.7dbea386d6c9c10848ea.js → app.bundle.e17934d4a3852b1a27ec.js} +57 -4
  7. package/dist/index.html +1 -1
  8. package/dist/sw.js +1 -1
  9. package/package.json +20 -20
  10. /package/dist/{1459.bundle.97fb6947a59096bd69ba.js → 1459.bundle.0c057bb5c04111972c6e.js} +0 -0
  11. /package/dist/{1933.bundle.d265a398d717d9a65980.js → 1933.bundle.8581959ebb842668939a.js} +0 -0
  12. /package/dist/{2018.bundle.75d45a73f6d29f4502eb.js → 2018.bundle.b42d4f0cd572dbc49b73.js} +0 -0
  13. /package/dist/{213.bundle.ec5f15baa925dfbfa439.js → 213.bundle.93c244de852eef5b325e.js} +0 -0
  14. /package/dist/{2424.bundle.b79e5c986efb65d01a14.js → 2424.bundle.9547d598346ee5fbcc3f.js} +0 -0
  15. /package/dist/{3138.bundle.45d9fe9f3ec46eabf6e6.js → 3138.bundle.2d1c32b62a3b4db64881.js} +0 -0
  16. /package/dist/{3461.bundle.2524724cb7e8b1a20592.js → 3461.bundle.d8f093cd878b23ca4de5.js} +0 -0
  17. /package/dist/{4507.bundle.8ffd9859aca01712144c.js → 4507.bundle.73b4e47b069392708b4a.js} +0 -0
  18. /package/dist/{4819.bundle.276d17abd4bb0504612a.js → 4819.bundle.5b8250b9781d7b195c8b.js} +0 -0
  19. /package/dist/{5015.bundle.23373b09d96bd5fccf83.js → 5015.bundle.2d52fb8a7607c0f5f2bc.js} +0 -0
  20. /package/dist/{5028.bundle.00c40ff6fea856c6f090.js → 5028.bundle.d6b587cec8eca4994f23.js} +0 -0
  21. /package/dist/{5457.bundle.1bebe4c433dd5945aa2b.js → 5457.bundle.c05cbb69ee35ed35c125.js} +0 -0
  22. /package/dist/{6027.bundle.01b5aca8656c7120845c.js → 6027.bundle.ea9fabbfc1fa5be21bd3.js} +0 -0
  23. /package/dist/{7639.bundle.b3db519e0e73c1efc492.js → 7639.bundle.c78c9a319dde07c862cd.js} +0 -0
  24. /package/dist/{8305.bundle.76beb137050442689584.js → 8305.bundle.15c446a807f52ad6870f.js} +0 -0
  25. /package/dist/{8499.bundle.00368cb353ec9096ba82.js → 8499.bundle.207ce16931b41d14154f.js} +0 -0
  26. /package/dist/{85.bundle.bb782db282876c8b886d.js → 85.bundle.1d2b51aa3d4e6ff61247.js} +0 -0
  27. /package/dist/{8558.bundle.20a44fd8d2aa42371d80.js → 8558.bundle.7509744a9f460f73e6ad.js} +0 -0
  28. /package/dist/{8583.bundle.4d01961aa80e4ca39ea9.js → 8583.bundle.1b1c0ed7620c6cfe2447.js} +0 -0
@@ -10612,8 +10612,8 @@ function AboutModalDefault() {
10612
10612
  name
10613
10613
  } = (0,browser_detect_es5/* default */.A)();
10614
10614
  const browser = `${name[0].toUpperCase()}${name.substr(1)} ${version}`;
10615
- const versionNumber = "3.13.0-beta.66";
10616
- const commitHash = "468e5734a9ab02516430cc0dab5d7f2106dea950";
10615
+ const versionNumber = "3.13.0-beta.68";
10616
+ const commitHash = "979d619f7ccb426beb75d59721b01c98175d5241";
10617
10617
  const [main, beta] = versionNumber.split('-');
10618
10618
  return /*#__PURE__*/react.createElement(ui_next_src/* AboutModal */.VTU, {
10619
10619
  className: "w-[400px]"
@@ -10644,16 +10644,50 @@ const {
10644
10644
  defaultLanguage,
10645
10645
  currentLanguage: currentLanguageFn
10646
10646
  } = i18n_src/* default */.A;
10647
+ const MODIFIER_OPTIONS = [{
10648
+ value: '16',
10649
+ label: 'Shift'
10650
+ }, {
10651
+ value: '17',
10652
+ label: 'Ctrl'
10653
+ }, {
10654
+ value: '18',
10655
+ label: 'Alt'
10656
+ }, {
10657
+ value: '91',
10658
+ label: 'Meta'
10659
+ }];
10660
+ const DEFAULT_TOOL_BINDINGS_STORAGE_KEY = 'user-preferred-tool-bindings';
10661
+ function getToolModifier(toolGroupService, toolGroupId, toolName, mouseButton) {
10662
+ if (!toolGroupService) {
10663
+ return null;
10664
+ }
10665
+ const bindings = toolGroupService.getToolBindings(toolGroupId, toolName);
10666
+ if (!bindings?.length) {
10667
+ return null;
10668
+ }
10669
+ const modifierBinding = bindings.find(binding => binding.mouseButton === mouseButton && binding.modifierKey != null && binding.numTouchPoints == null);
10670
+ return modifierBinding?.modifierKey != null ? String(modifierBinding.modifierKey) : null;
10671
+ }
10672
+ function getModifierFromBindings(bindings, mouseButton) {
10673
+ if (!bindings?.length) {
10674
+ return null;
10675
+ }
10676
+ const modifierBinding = bindings.find(binding => binding.mouseButton === mouseButton && binding.modifierKey != null && binding.numTouchPoints == null);
10677
+ return modifierBinding?.modifierKey != null ? String(modifierBinding.modifierKey) : null;
10678
+ }
10647
10679
  function UserPreferencesModalDefault({
10648
10680
  hide
10649
10681
  }) {
10650
10682
  const {
10651
- hotkeysManager
10683
+ hotkeysManager,
10684
+ servicesManager
10652
10685
  } = (0,src/* useSystem */.Jg)();
10653
10686
  const {
10654
10687
  t,
10655
10688
  i18n: i18nextInstance
10656
10689
  } = (0,es/* useTranslation */.Bd)('UserPreferencesModal');
10690
+ const toolGroupService = servicesManager?.services?.toolGroupService;
10657
10691
  const {
10658
10692
  hotkeyDefinitions = {},
10659
10693
  hotkeyDefaults = {}
@@ -10670,9 +10704,12 @@ function UserPreferencesModalDefault({
10670
10704
  const resolvedHotkeyDefaults = Object.keys(hotkeyDefaults).length ? hotkeyDefaults : fallbackHotkeyDefinitions;
10671
10705
  const initialHotkeyDefinitions = Object.keys(hotkeyDefinitions).length ? hotkeyDefinitions : resolvedHotkeyDefaults;
10672
10706
  const currentLanguage = currentLanguageFn();
10707
+ const initialCrosshairModifier = (0,react.useMemo)(() => getToolModifier(toolGroupService, 'mpr', 'Crosshairs', 1), [toolGroupService]);
10708
+ const defaultCrosshairBindings = (0,react.useMemo)(() => toolGroupService?.getDefaultToolBindings?.('mpr', 'Crosshairs'), [toolGroupService]);
10673
10709
  const [state, setState] = (0,react.useState)({
10674
10710
  hotkeyDefinitions: initialHotkeyDefinitions,
10675
- languageValue: currentLanguage.value
10711
+ languageValue: currentLanguage.value,
10712
+ crosshairModifier: initialCrosshairModifier
10676
10713
  });
10677
10714
  const onLanguageChangeHandler = value => {
10678
10715
  setState(state => ({
@@ -10696,9 +10733,17 @@ function UserPreferencesModalDefault({
10696
10733
  setState(state => ({
10697
10734
  ...state,
10698
10735
  languageValue: defaultLanguage.value,
10699
- hotkeyDefinitions: resolvedHotkeyDefaults
10736
+ hotkeyDefinitions: resolvedHotkeyDefaults,
10737
+ crosshairModifier: getModifierFromBindings(defaultCrosshairBindings, 1)
10700
10738
  }));
10701
10739
  hotkeysManager.restoreDefaultBindings();
10740
+ if (toolGroupService && defaultCrosshairBindings?.length) {
10741
+ toolGroupService.setToolBindings('mpr', 'Crosshairs', defaultCrosshairBindings);
10742
+ toolGroupService.applyToolBindings('mpr', 'Crosshairs', {
10743
+ replaceExisting: true
10744
+ });
10745
+ }
10746
+ toolGroupService?.removePersistedToolBindings('mpr', 'Crosshairs');
10702
10747
  };
10703
10748
  const displayNames = react.useMemo(() => {
10704
10749
  if (typeof Intl === 'undefined' || typeof Intl.DisplayNames !== 'function') {
@@ -10755,7 +10800,32 @@ function UserPreferencesModalDefault({
10755
10800
  onChange: newKeys => onHotkeyChangeHandler(id, newKeys),
10756
10801
  placeholder: definition.keys,
10757
10802
  hotkeys: src/* hotkeys */.ot
10758
- })))), /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu, null, /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu.Left, null, /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu.Auxiliary, {
10803
+ }))), state.crosshairModifier != null && /*#__PURE__*/react.createElement(react.Fragment, null, /*#__PURE__*/react.createElement(ui_next_src/* UserPreferencesModal */.xMy.SubHeading, null, t('ModifierKeys', {
10804
+ defaultValue: 'Modifier Keys'
10805
+ })), /*#__PURE__*/react.createElement(ui_next_src/* UserPreferencesModal */.xMy.HotkeysGrid, null, /*#__PURE__*/react.createElement("div", {
10806
+ className: "flex items-center justify-between gap-2"
10807
+ }, /*#__PURE__*/react.createElement("span", {
10808
+ className: "text-foreground text-base"
10809
+ }, t('CrosshairsModifier', {
10810
+ defaultValue: 'Crosshairs'
10811
+ })), /*#__PURE__*/react.createElement("div", {
10812
+ className: "flex items-center gap-1.5"
10813
+ }, /*#__PURE__*/react.createElement("span", {
10814
+ className: "text-muted-foreground text-sm"
10815
+ }, t('PlusLeftClick', {
10816
+ defaultValue: 'Left Click +'
10817
+ })), /*#__PURE__*/react.createElement(ui_next_src/* Select */.l6P, {
10818
+ value: state.crosshairModifier,
10819
+ onValueChange: val => setState(s => ({
10820
+ ...s,
10821
+ crosshairModifier: val
10822
+ }))
10823
+ }, /*#__PURE__*/react.createElement(ui_next_src/* SelectTrigger */.bqE, {
10824
+ className: "w-16"
10825
+ }, /*#__PURE__*/react.createElement(ui_next_src/* SelectValue */.yvm, null)), /*#__PURE__*/react.createElement(ui_next_src/* SelectContent */.gCo, null, MODIFIER_OPTIONS.map(opt => /*#__PURE__*/react.createElement(ui_next_src/* SelectItem */.ebT, {
10826
+ key: opt.value,
10827
+ value: opt.value
10828
+ }, opt.label))))))))), /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu, null, /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu.Left, null, /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu.Auxiliary, {
10759
10829
  onClick: onResetHandler
10760
10830
  }, t('Reset to defaults'))), /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu.Right, null, /*#__PURE__*/react.createElement(ui_next_src/* FooterAction */.esu.Secondary, {
10761
10831
  onClick: () => {
@@ -10772,6 +10842,17 @@ function UserPreferencesModalDefault({
10772
10842
  return; // Exit early since we're reloading
10773
10843
  }
10774
10844
  hotkeysManager.setHotkeys(state.hotkeyDefinitions);
10845
+ if (toolGroupService && state.crosshairModifier != null) {
10846
+ const bindings = [{
10847
+ mouseButton: 1,
10848
+ modifierKey: Number(state.crosshairModifier)
10849
+ }];
10850
+ toolGroupService.setToolBindings('mpr', 'Crosshairs', bindings);
10851
+ toolGroupService.applyToolBindings('mpr', 'Crosshairs', {
10852
+ replaceExisting: true
10853
+ });
10854
+ toolGroupService.persistToolBindings('mpr', 'Crosshairs', bindings);
10855
+ }
10775
10856
  src/* hotkeys */.ot.stopRecord();
10776
10857
  src/* hotkeys */.ot.unpause();
10777
10858
  hide();
@@ -10779,7 +10860,8 @@ function UserPreferencesModalDefault({
10779
10860
  }, t('Save')))));
10780
10861
  }
10781
10862
  /* harmony default export */ const userPreferencesCustomization = ({
10782
- 'ohif.userPreferencesModal': UserPreferencesModalDefault
10863
+ 'ohif.userPreferencesModal': UserPreferencesModalDefault,
10864
+ 'ohif.userPreferences.toolBindingsStorageKey': DEFAULT_TOOL_BINDINGS_STORAGE_KEY
10783
10865
  });
10784
10866
  ;// ../../../extensions/default/src/customizations/reportDialogCustomization.tsx
10785
10867
 
@@ -295,6 +295,10 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager) {
295
295
  }],
296
296
  disabled: [{
297
297
  toolName: toolNames.Crosshairs,
298
+ bindings: [{
299
+ mouseButton: Enums.MouseBindings.Primary,
300
+ modifierKey: Enums.KeyboardBindings.Shift
301
+ }],
298
302
  configuration: {
299
303
  viewportIndicators: true,
300
304
  viewportIndicatorsConfig: {
@@ -302,7 +306,6 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager) {
302
306
  xOffset: 0.95,
303
307
  yOffset: 0.05
304
308
  },
305
- disableOnPassive: true,
306
309
  autoPan: {
307
310
  enabled: false,
308
311
  panSize: 10
@@ -921,15 +924,18 @@ const toolbarButtons = [
921
924
  type: 'tool',
922
925
  icon: 'tool-crosshair',
923
926
  label: i18next/* default */.A.t('Buttons:Crosshairs'),
927
+ tooltip: i18next/* default */.A.t('Buttons:Click to toggle on or off'),
924
928
  commands: {
925
- commandName: 'setToolActiveToolbar',
929
+ commandName: 'toggleActiveDisabledToolbar',
926
930
  commandOptions: {
927
931
  toolGroupIds: ['mpr']
928
932
  }
929
933
  },
930
934
  evaluate: {
931
- name: 'evaluate.cornerstoneTool',
932
- disabledText: i18next/* default */.A.t('Buttons:Select an MPR viewport to enable this tool')
935
+ name: 'evaluate.cornerstoneTool.toggleWithModifier',
936
+ disabledText: i18next/* default */.A.t('Buttons:Select an MPR viewport to enable this tool'),
937
+ toggledOnIcon: 'tool-crosshair-checked',
938
+ defaultIcon: 'tool-crosshair'
933
939
  }
934
940
  }
935
941
  }, {
@@ -10577,7 +10577,14 @@ function commandsModule({
10577
10577
  }
10578
10578
  function _getActiveViewportToolGroupId() {
10579
10579
  const viewport = _getActiveViewportEnabledElement();
10580
- return toolGroupService.getToolGroupForViewport(viewport.id);
10580
+ const toolGroup = viewport && toolGroupService.getToolGroupForViewport(viewport.id);
10581
+ return toolGroup?.id;
10582
+ }
10583
+ function _usesPrimaryActivation(bindings) {
10584
+ if (!bindings?.length) {
10585
+ return true;
10586
+ }
10587
+ return bindings.some(binding => binding.mouseButton === dist_esm.Enums.MouseBindings.Primary && binding.modifierKey == null && binding.numTouchPoints == null);
10581
10588
  }
10582
10589
  function _getActiveSegmentationInfo() {
10583
10590
  const viewportId = viewportGridService.getActiveViewportId();
@@ -11444,31 +11451,46 @@ function commandsModule({
11444
11451
  toggleActiveDisabledToolbar({
11445
11452
  value,
11446
11453
  itemId,
11447
- toolGroupId
11454
+ toolGroupId,
11455
+ toolGroupIds
11448
11456
  }) {
11449
11457
  const toolName = itemId || value;
11450
- toolGroupId = toolGroupId ?? _getActiveViewportToolGroupId();
11451
- const toolGroup = toolGroupService.getToolGroup(toolGroupId);
11452
- if (!toolGroup || !toolGroup.hasTool(toolName)) {
11453
- return;
11454
- }
11455
- const toolIsActive = [dist_esm.Enums.ToolModes.Active, dist_esm.Enums.ToolModes.Enabled, dist_esm.Enums.ToolModes.Passive].includes(toolGroup.getToolOptions(toolName).mode);
11456
- toolIsActive ? toolGroup.setToolDisabled(toolName) : actions.setToolActive({
11457
- toolName,
11458
- toolGroupId
11459
- });
11460
-
11461
- // we should set the previously active tool to active after we set the
11462
- // current tool disabled
11463
- if (toolIsActive) {
11464
- const prevToolName = toolGroup.getPrevActivePrimaryToolName();
11465
- if (prevToolName !== toolName) {
11458
+ const resolvedToolGroupIds = toolGroupIds?.length ? toolGroupIds : [toolGroupId ?? _getActiveViewportToolGroupId()];
11459
+ resolvedToolGroupIds.forEach(toolGroupId => {
11460
+ const toolGroup = toolGroupService.getToolGroup(toolGroupId);
11461
+ if (!toolGroup || !toolGroup.hasTool(toolName)) {
11462
+ return;
11463
+ }
11464
+ const toolIsActive = [dist_esm.Enums.ToolModes.Active, dist_esm.Enums.ToolModes.Enabled, dist_esm.Enums.ToolModes.Passive].includes(toolGroup.getToolOptions(toolName).mode);
11465
+ if (toolIsActive) {
11466
+ toolGroup.setToolDisabled(toolName);
11467
+ const bindings = toolGroupService.getToolBindings(toolGroupId, toolName);
11468
+ if (_usesPrimaryActivation(bindings)) {
11469
+ // we should set the previously active tool to active after we set the
11470
+ // current tool disabled
11471
+ const prevToolName = toolGroup.getPrevActivePrimaryToolName();
11472
+ if (prevToolName !== toolName) {
11473
+ actions.setToolActive({
11474
+ toolName: prevToolName,
11475
+ toolGroupId
11476
+ });
11477
+ }
11478
+ }
11479
+ return;
11480
+ }
11481
+ const bindings = toolGroupService.getToolBindings(toolGroupId, toolName);
11482
+ if (_usesPrimaryActivation(bindings)) {
11466
11483
  actions.setToolActive({
11467
- toolName: prevToolName,
11468
- toolGroupId
11484
+ toolName,
11485
+ toolGroupId,
11486
+ bindings
11487
+ });
11488
+ } else {
11489
+ toolGroup.setToolActive(toolName, {
11490
+ bindings
11469
11491
  });
11470
11492
  }
11471
- }
11493
+ });
11472
11494
  },
11473
11495
  setToolActiveToolbar: ({
11474
11496
  value,
@@ -18475,6 +18497,36 @@ function getToolbarModule({
18475
18497
  isActive: isPrimaryActive
18476
18498
  };
18477
18499
  }
18500
+ }, {
18501
+ name: 'evaluate.cornerstoneTool.toggleWithModifier',
18502
+ evaluate: ({
18503
+ viewportId,
18504
+ button,
18505
+ disabledText,
18506
+ toggledOnIcon,
18507
+ defaultIcon
18508
+ }) => {
18509
+ const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
18510
+ if (!toolGroup) {
18511
+ return;
18512
+ }
18513
+ const toolName = toolbarService.getToolNameForButton(button);
18514
+ if (!toolGroup.hasTool(toolName)) {
18515
+ return getDisabledState(disabledText);
18516
+ }
18517
+ const {
18518
+ mode
18519
+ } = toolGroup.getToolOptions(toolName) ?? {};
18520
+ const isToggled = mode === dist_esm.Enums.ToolModes.Passive || mode === dist_esm.Enums.ToolModes.Active || mode === dist_esm.Enums.ToolModes.Enabled;
18521
+ const toolBindings = toolGroupService.getToolBindings(toolGroup.id, toolName);
18522
+ const hasModifierKey = toolBindings?.some(binding => binding.modifierKey != null) ?? false;
18523
+ return {
18524
+ disabled: false,
18525
+ isActive: false,
18526
+ isToggled,
18527
+ icon: isToggled && hasModifierKey && toggledOnIcon ? toggledOnIcon : defaultIcon ?? button.props.icon
18528
+ };
18529
+ }
18478
18530
  }, {
18479
18531
  name: 'evaluate.action',
18480
18532
  evaluate: () => {
@@ -18627,7 +18679,10 @@ class ToolGroupService {
18627
18679
  this.cornerstoneViewportService = void 0;
18628
18680
  this.viewportGridService = void 0;
18629
18681
  this.uiNotificationService = void 0;
18682
+ this.customizationService = void 0;
18630
18683
  this.toolGroupIds = new Set();
18684
+ this.toolBindingsMap = new Map();
18685
+ this.defaultToolBindingsMap = new Map();
18631
18686
  /**
18632
18687
  * Service-specific
18633
18688
  */
@@ -18653,11 +18708,13 @@ class ToolGroupService {
18653
18708
  const {
18654
18709
  cornerstoneViewportService,
18655
18710
  viewportGridService,
18656
- uiNotificationService
18711
+ uiNotificationService,
18712
+ customizationService
18657
18713
  } = servicesManager.services;
18658
18714
  this.cornerstoneViewportService = cornerstoneViewportService;
18659
18715
  this.viewportGridService = viewportGridService;
18660
18716
  this.uiNotificationService = uiNotificationService;
18717
+ this.customizationService = customizationService;
18661
18718
  this.listeners = {};
18662
18719
  this.EVENTS = ToolGroupService_EVENTS;
18663
18720
  Object.assign(this, src/* pubSubServiceInterface */.Ml);
@@ -18715,6 +18772,8 @@ class ToolGroupService {
18715
18772
  destroy() {
18716
18773
  dist_esm.ToolGroupManager.destroy();
18717
18774
  this.toolGroupIds = new Set();
18775
+ this.toolBindingsMap.clear();
18776
+ this.defaultToolBindingsMap.clear();
18718
18777
  esm.eventTarget.removeEventListener(dist_esm.Enums.Events.TOOL_ACTIVATED, this._onToolActivated);
18719
18778
  }
18720
18779
  destroyToolGroup(toolGroupId) {
@@ -18769,6 +18828,7 @@ class ToolGroupService {
18769
18828
  // this.changeConfigurationIfNecessary(toolGroup, volumeId);
18770
18829
  this._addTools(toolGroup, tools, configs);
18771
18830
  this._setToolsMode(toolGroup, tools);
18831
+ this._loadPersistedBindings(toolGroupId);
18772
18832
  }
18773
18833
  createToolGroupAndAddTools(toolGroupId, tools) {
18774
18834
  const toolGroup = this.createToolGroup(toolGroupId);
@@ -18808,6 +18868,84 @@ class ToolGroupService {
18808
18868
  getActivePrimaryMouseButtonTool(toolGroupId) {
18809
18869
  return this.getToolGroup(toolGroupId)?.getActivePrimaryMouseButtonTool();
18810
18870
  }
18871
+ getToolBindings(toolGroupId, toolName) {
18872
+ return this.toolBindingsMap.get(toolGroupId)?.get(toolName);
18873
+ }
18874
+ setToolBindings(toolGroupId, toolName, bindings) {
18875
+ if (!this.toolBindingsMap.has(toolGroupId)) {
18876
+ this.toolBindingsMap.set(toolGroupId, new Map());
18877
+ }
18878
+ this.toolBindingsMap.get(toolGroupId).set(toolName, this._cloneToolBindings(bindings));
18879
+ }
18880
+ getDefaultToolBindings(toolGroupId, toolName) {
18881
+ const defaultBindings = this.defaultToolBindingsMap.get(toolGroupId)?.get(toolName);
18882
+ return defaultBindings ? this._cloneToolBindings(defaultBindings) : undefined;
18883
+ }
18884
+ persistToolBindings(toolGroupId, toolName, bindings) {
18885
+ const persistedBindings = this._readPersistedToolBindings();
18886
+ if (!persistedBindings[toolGroupId]) {
18887
+ persistedBindings[toolGroupId] = {};
18888
+ }
18889
+ persistedBindings[toolGroupId][toolName] = bindings;
18890
+ this._writePersistedToolBindings(persistedBindings);
18891
+ }
18892
+ removePersistedToolBindings(toolGroupId, toolName) {
18893
+ const persistedBindings = this._readPersistedToolBindings();
18894
+ if (!persistedBindings[toolGroupId]) {
18895
+ return;
18896
+ }
18897
+ if (toolName) {
18898
+ delete persistedBindings[toolGroupId][toolName];
18899
+ if (!Object.keys(persistedBindings[toolGroupId]).length) {
18900
+ delete persistedBindings[toolGroupId];
18901
+ }
18902
+ } else {
18903
+ delete persistedBindings[toolGroupId];
18904
+ }
18905
+ this._writePersistedToolBindings(persistedBindings);
18906
+ }
18907
+
18908
+ /**
18909
+ * Applies the currently tracked bindings to the runtime tool instance.
18910
+ *
18911
+ * Note: this method may activate tools that are currently Passive or Enabled.
18912
+ * Assigning bindings is treated as making the tool interactable.
18913
+ */
18914
+ applyToolBindings(toolGroupId, toolName, options = {}) {
18915
+ const toolGroup = dist_esm.ToolGroupManager.getToolGroup(toolGroupId);
18916
+ if (!toolGroup || !toolGroup.hasTool(toolName)) {
18917
+ return;
18918
+ }
18919
+ const bindings = this.getToolBindings(toolGroupId, toolName);
18920
+ if (!bindings) {
18921
+ return;
18922
+ }
18923
+ const {
18924
+ mode
18925
+ } = toolGroup.getToolOptions(toolName);
18926
+ if (mode === dist_esm.Enums.ToolModes.Active || mode === dist_esm.Enums.ToolModes.Passive || mode === dist_esm.Enums.ToolModes.Enabled) {
18927
+ if (options.replaceExisting) {
18928
+ // Opt-in behavior for callers that need replacement semantics.
18929
+ toolGroup.setToolDisabled(toolName);
18930
+ }
18931
+ toolGroup.setToolActive(toolName, {
18932
+ bindings
18933
+ });
18934
+ }
18935
+ }
18936
+ getAllToolBindings() {
18937
+ const result = [];
18938
+ for (const [toolGroupId, toolMap] of this.toolBindingsMap) {
18939
+ for (const [toolName, bindings] of toolMap) {
18940
+ result.push({
18941
+ toolGroupId,
18942
+ toolName,
18943
+ bindings
18944
+ });
18945
+ }
18946
+ }
18947
+ return result;
18948
+ }
18811
18949
  _setToolsMode(toolGroup, tools) {
18812
18950
  const {
18813
18951
  active,
@@ -18820,6 +18958,10 @@ class ToolGroupService {
18820
18958
  toolName,
18821
18959
  bindings
18822
18960
  }) => {
18961
+ if (bindings) {
18962
+ this.setToolBindings(toolGroup.id, toolName, bindings);
18963
+ this._setDefaultToolBindingsIfMissing(toolGroup.id, toolName, bindings);
18964
+ }
18823
18965
  toolGroup.setToolActive(toolName, {
18824
18966
  bindings
18825
18967
  });
@@ -18827,26 +18969,50 @@ class ToolGroupService {
18827
18969
  }
18828
18970
  if (passive) {
18829
18971
  passive.forEach(({
18830
- toolName
18972
+ toolName,
18973
+ bindings
18831
18974
  }) => {
18975
+ if (bindings) {
18976
+ this.setToolBindings(toolGroup.id, toolName, bindings);
18977
+ this._setDefaultToolBindingsIfMissing(toolGroup.id, toolName, bindings);
18978
+ }
18832
18979
  toolGroup.setToolPassive(toolName);
18833
18980
  });
18834
18981
  }
18835
18982
  if (enabled) {
18836
18983
  enabled.forEach(({
18837
- toolName
18984
+ toolName,
18985
+ bindings
18838
18986
  }) => {
18987
+ if (bindings) {
18988
+ this.setToolBindings(toolGroup.id, toolName, bindings);
18989
+ this._setDefaultToolBindingsIfMissing(toolGroup.id, toolName, bindings);
18990
+ }
18839
18991
  toolGroup.setToolEnabled(toolName);
18840
18992
  });
18841
18993
  }
18842
18994
  if (disabled) {
18843
18995
  disabled.forEach(({
18844
- toolName
18996
+ toolName,
18997
+ bindings
18845
18998
  }) => {
18999
+ if (bindings) {
19000
+ this.setToolBindings(toolGroup.id, toolName, bindings);
19001
+ this._setDefaultToolBindingsIfMissing(toolGroup.id, toolName, bindings);
19002
+ }
18846
19003
  toolGroup.setToolDisabled(toolName);
18847
19004
  });
18848
19005
  }
18849
19006
  }
19007
+ _setDefaultToolBindingsIfMissing(toolGroupId, toolName, bindings) {
19008
+ if (!this.defaultToolBindingsMap.has(toolGroupId)) {
19009
+ this.defaultToolBindingsMap.set(toolGroupId, new Map());
19010
+ }
19011
+ const toolMap = this.defaultToolBindingsMap.get(toolGroupId);
19012
+ if (!toolMap.has(toolName)) {
19013
+ toolMap.set(toolName, this._cloneToolBindings(bindings));
19014
+ }
19015
+ }
18850
19016
  _addTools(toolGroup, tools) {
18851
19017
  const addTools = tools => {
18852
19018
  tools.forEach(({
@@ -18878,6 +19044,60 @@ class ToolGroupService {
18878
19044
  addTools(tools.disabled);
18879
19045
  }
18880
19046
  }
19047
+ _loadPersistedBindings(toolGroupId) {
19048
+ const toolGroupBindings = this._readPersistedToolBindings()[toolGroupId];
19049
+ if (!toolGroupBindings) {
19050
+ return;
19051
+ }
19052
+ const toolGroup = dist_esm.ToolGroupManager.getToolGroup(toolGroupId);
19053
+ for (const [toolName, bindings] of Object.entries(toolGroupBindings)) {
19054
+ this.setToolBindings(toolGroupId, toolName, bindings);
19055
+ if (!toolGroup || !toolGroup.hasTool(toolName)) {
19056
+ continue;
19057
+ }
19058
+ const {
19059
+ mode
19060
+ } = toolGroup.getToolOptions(toolName);
19061
+ if (mode === dist_esm.Enums.ToolModes.Active) {
19062
+ this.applyToolBindings(toolGroupId, toolName, {
19063
+ replaceExisting: true
19064
+ });
19065
+ }
19066
+ }
19067
+ }
19068
+ _readPersistedToolBindings() {
19069
+ try {
19070
+ const stored = localStorage.getItem(this._getToolBindingsStorageKey());
19071
+ if (!stored) {
19072
+ return {};
19073
+ }
19074
+ const parsed = JSON.parse(stored);
19075
+ if (!parsed || typeof parsed !== 'object') {
19076
+ return {};
19077
+ }
19078
+ return parsed;
19079
+ } catch {
19080
+ // ignore corrupt localStorage
19081
+ return {};
19082
+ }
19083
+ }
19084
+ _writePersistedToolBindings(bindings) {
19085
+ const storageKey = this._getToolBindingsStorageKey();
19086
+ if (!Object.keys(bindings).length) {
19087
+ localStorage.removeItem(storageKey);
19088
+ return;
19089
+ }
19090
+ localStorage.setItem(storageKey, JSON.stringify(bindings));
19091
+ }
19092
+ _cloneToolBindings(bindings) {
19093
+ return bindings.map(binding => ({
19094
+ ...binding
19095
+ }));
19096
+ }
19097
+ _getToolBindingsStorageKey() {
19098
+ const customizationValue = this.customizationService?.getCustomization('ohif.userPreferences.toolBindingsStorageKey');
19099
+ return typeof customizationValue === 'string' && customizationValue.length > 0 ? customizationValue : 'user-preferred-tool-bindings';
19100
+ }
18881
19101
  }
18882
19102
  _ToolGroupService = ToolGroupService;
18883
19103
  ToolGroupService.REGISTRATION = {
@@ -157,6 +157,10 @@ function _initToolGroups(toolNames, Enums, toolGroupService, commandsManager) {
157
157
  enabled: [],
158
158
  disabled: [{
159
159
  toolName: toolNames.Crosshairs,
160
+ bindings: [{
161
+ mouseButton: Enums.MouseBindings.Primary,
162
+ modifierKey: Enums.KeyboardBindings.Shift
163
+ }],
160
164
  configuration: {
161
165
  disableOnPassive: true,
162
166
  autoPan: {