@theia/core 1.77.0-next.56 → 1.77.0-next.57

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 (102) hide show
  1. package/lib/browser/catalog.json +66 -0
  2. package/lib/browser/common-frontend-contribution.d.ts +6 -1
  3. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  4. package/lib/browser/common-frontend-contribution.js +18 -1
  5. package/lib/browser/common-frontend-contribution.js.map +1 -1
  6. package/lib/browser/keybinding.d.ts +95 -17
  7. package/lib/browser/keybinding.d.ts.map +1 -1
  8. package/lib/browser/keybinding.js +405 -75
  9. package/lib/browser/keybinding.js.map +1 -1
  10. package/lib/browser/keybinding.spec.js +786 -30
  11. package/lib/browser/keybinding.spec.js.map +1 -1
  12. package/lib/browser/keyboard/browser-keyboard-frontend-contribution.js +1 -1
  13. package/lib/browser/keyboard/browser-keyboard-frontend-contribution.js.map +1 -1
  14. package/lib/browser/keyboard/browser-keyboard-layout-provider.d.ts +3 -4
  15. package/lib/browser/keyboard/browser-keyboard-layout-provider.d.ts.map +1 -1
  16. package/lib/browser/keyboard/browser-keyboard-layout-provider.js +3 -3
  17. package/lib/browser/keyboard/browser-keyboard-layout-provider.js.map +1 -1
  18. package/lib/browser/keyboard/browser-keyboard-layout-provider.spec.js +39 -0
  19. package/lib/browser/keyboard/browser-keyboard-layout-provider.spec.js.map +1 -1
  20. package/lib/browser/keyboard/keyboard-layout-service.d.ts +67 -15
  21. package/lib/browser/keyboard/keyboard-layout-service.d.ts.map +1 -1
  22. package/lib/browser/keyboard/keyboard-layout-service.js +309 -98
  23. package/lib/browser/keyboard/keyboard-layout-service.js.map +1 -1
  24. package/lib/browser/keyboard/keyboard-layout-service.spec.js +538 -12
  25. package/lib/browser/keyboard/keyboard-layout-service.spec.js.map +1 -1
  26. package/lib/browser/keyboard/keys.spec.js +183 -0
  27. package/lib/browser/keyboard/keys.spec.js.map +1 -1
  28. package/lib/browser/menu/action-menu-node.d.ts +5 -3
  29. package/lib/browser/menu/action-menu-node.d.ts.map +1 -1
  30. package/lib/browser/menu/action-menu-node.js +2 -11
  31. package/lib/browser/menu/action-menu-node.js.map +1 -1
  32. package/lib/browser/menu/action-menu-node.spec.d.ts +2 -0
  33. package/lib/browser/menu/action-menu-node.spec.d.ts.map +1 -0
  34. package/lib/browser/menu/action-menu-node.spec.js +54 -0
  35. package/lib/browser/menu/action-menu-node.spec.js.map +1 -0
  36. package/lib/browser/menu/browser-menu-plugin.js +1 -1
  37. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  38. package/lib/browser/test/jsdom.d.ts.map +1 -1
  39. package/lib/browser/test/jsdom.js +11 -0
  40. package/lib/browser/test/jsdom.js.map +1 -1
  41. package/lib/common/keyboard/keyboard-layout-provider.d.ts +7 -0
  42. package/lib/common/keyboard/keyboard-layout-provider.d.ts.map +1 -1
  43. package/lib/common/keyboard/keyboard-layout-provider.js.map +1 -1
  44. package/lib/common/keys.d.ts +86 -3
  45. package/lib/common/keys.d.ts.map +1 -1
  46. package/lib/common/keys.js +262 -23
  47. package/lib/common/keys.js.map +1 -1
  48. package/lib/common/ternary-search-tree.js +2 -2
  49. package/lib/common/ternary-search-tree.js.map +1 -1
  50. package/lib/common/ternary-search-tree.spec.js +12 -0
  51. package/lib/common/ternary-search-tree.spec.js.map +1 -1
  52. package/lib/electron-browser/keyboard/electron-keyboard-module.d.ts +2 -0
  53. package/lib/electron-browser/keyboard/electron-keyboard-module.d.ts.map +1 -1
  54. package/lib/electron-browser/keyboard/electron-keyboard-module.js +8 -1
  55. package/lib/electron-browser/keyboard/electron-keyboard-module.js.map +1 -1
  56. package/lib/electron-browser/keyboard/electron-keyboard-module.spec.d.ts +2 -0
  57. package/lib/electron-browser/keyboard/electron-keyboard-module.spec.d.ts.map +1 -0
  58. package/lib/electron-browser/keyboard/electron-keyboard-module.spec.js +28 -0
  59. package/lib/electron-browser/keyboard/electron-keyboard-module.spec.js.map +1 -0
  60. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
  61. package/lib/electron-browser/menu/electron-main-menu-factory.js +3 -2
  62. package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
  63. package/lib/electron-browser/menu/electron-menu-contribution.d.ts.map +1 -1
  64. package/lib/electron-browser/menu/electron-menu-contribution.js +3 -0
  65. package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
  66. package/lib/electron-common/electron-api.d.ts +5 -0
  67. package/lib/electron-common/electron-api.d.ts.map +1 -1
  68. package/lib/electron-common/electron-api.js +21 -0
  69. package/lib/electron-common/electron-api.js.map +1 -1
  70. package/lib/electron-common/electron-api.spec.d.ts +2 -0
  71. package/lib/electron-common/electron-api.spec.d.ts.map +1 -0
  72. package/lib/electron-common/electron-api.spec.js +40 -0
  73. package/lib/electron-common/electron-api.spec.js.map +1 -0
  74. package/lib/electron-main/electron-api-main.d.ts +0 -1
  75. package/lib/electron-main/electron-api-main.d.ts.map +1 -1
  76. package/lib/electron-main/electron-api-main.js +1 -12
  77. package/lib/electron-main/electron-api-main.js.map +1 -1
  78. package/package.json +4 -4
  79. package/src/browser/common-frontend-contribution.ts +16 -0
  80. package/src/browser/keybinding.spec.ts +878 -34
  81. package/src/browser/keybinding.ts +470 -85
  82. package/src/browser/keyboard/browser-keyboard-frontend-contribution.ts +1 -1
  83. package/src/browser/keyboard/browser-keyboard-layout-provider.spec.ts +52 -1
  84. package/src/browser/keyboard/browser-keyboard-layout-provider.ts +5 -5
  85. package/src/browser/keyboard/keyboard-layout-service.spec.ts +640 -15
  86. package/src/browser/keyboard/keyboard-layout-service.ts +358 -106
  87. package/src/browser/keyboard/keys.spec.ts +206 -1
  88. package/src/browser/menu/action-menu-node.spec.ts +60 -0
  89. package/src/browser/menu/action-menu-node.ts +8 -14
  90. package/src/browser/menu/browser-menu-plugin.ts +1 -1
  91. package/src/browser/test/jsdom.ts +11 -0
  92. package/src/common/keyboard/keyboard-layout-provider.ts +8 -0
  93. package/src/common/keys.ts +311 -26
  94. package/src/common/ternary-search-tree.spec.ts +14 -0
  95. package/src/common/ternary-search-tree.ts +2 -2
  96. package/src/electron-browser/keyboard/electron-keyboard-module.spec.ts +29 -0
  97. package/src/electron-browser/keyboard/electron-keyboard-module.ts +9 -2
  98. package/src/electron-browser/menu/electron-main-menu-factory.ts +3 -3
  99. package/src/electron-browser/menu/electron-menu-contribution.ts +4 -0
  100. package/src/electron-common/electron-api.spec.ts +42 -0
  101. package/src/electron-common/electron-api.ts +23 -0
  102. package/src/electron-main/electron-api-main.ts +2 -13
@@ -17,7 +17,7 @@
17
17
  import { enableJSDOM } from '../../browser/test/jsdom';
18
18
  let disableJSDOM = enableJSDOM();
19
19
 
20
- import { KeyCode, Key, KeyModifier, KeySequence } from './keys';
20
+ import { KeyCode, Key, KeyModifier, KeySequence, NormalizedKeyboardInput, normalizeKeyboardInput, readAltGraph } from './keys';
21
21
  import * as os from '../../common/os';
22
22
  import * as chai from 'chai';
23
23
  import * as sinon from 'sinon';
@@ -117,6 +117,139 @@ describe('keys api', () => {
117
117
  expect(equalKeyCode(parsedKeyCode, keyCode)).to.be.true;
118
118
  });
119
119
 
120
+ it('should keep dispatch strings equivalent to serialized strings', () => {
121
+ const keyCodes = [
122
+ ...Array.from({ length: 16 }, (_, modifiers) => new KeyCode({
123
+ key: Key.KEY_A,
124
+ ctrl: !!(modifiers & 1),
125
+ shift: !!(modifiers & 2),
126
+ alt: !!(modifiers & 4),
127
+ meta: !!(modifiers & 8)
128
+ })),
129
+ new KeyCode({}),
130
+ KeyCode.parse('ctrl+['),
131
+ KeyCode.parse('shift+/'),
132
+ ...KeySequence.parse('ctrlcmd+k ctrlcmd+c')
133
+ ];
134
+ for (const keyCode of keyCodes) {
135
+ expect(keyCode.dispatchString()).to.equal(keyCode.toString());
136
+ }
137
+ });
138
+
139
+ it('should distinguish layout modifiers in dispatch identity without changing serialization', () => {
140
+ const command = new KeyCode({ key: Key.DIGIT8, ctrl: true, alt: true });
141
+ const layout = new KeyCode({ key: Key.DIGIT8, ctrl: true, layoutModifiers: 'altGraph', character: '[' });
142
+ const shiftedLayout = new KeyCode({ key: Key.SLASH, layoutModifiers: 'shift', character: '/' });
143
+
144
+ expect(command.toString()).to.equal('alt+ctrl+8');
145
+ expect(layout.toString()).to.equal('ctrl+8');
146
+ expect(layout.dispatchString()).to.equal('ctrl+8@altgraph');
147
+ expect(shiftedLayout.toString()).to.equal('/');
148
+ expect(shiftedLayout.dispatchString()).to.equal('/@shift');
149
+ expect(command.equals(layout)).to.be.false;
150
+ });
151
+
152
+ it('should keep equality and dispatch identity equivalent for layout modifiers', () => {
153
+ const layers = ['none', 'shift', 'altGraph', 'shiftAltGraph'] as const;
154
+ const keyCodes = Array.from({ length: 64 }, (_, modifiers) => new KeyCode({
155
+ key: Key.KEY_A,
156
+ ctrl: !!(modifiers & 1),
157
+ shift: !!(modifiers & 2),
158
+ alt: !!(modifiers & 4),
159
+ meta: !!(modifiers & 8),
160
+ layoutModifiers: layers[modifiers >> 4]
161
+ }));
162
+ for (const left of keyCodes) {
163
+ for (const right of keyCodes) {
164
+ expect(left.equals(right)).to.equal(left.dispatchString() === right.dispatchString());
165
+ }
166
+ }
167
+ });
168
+
169
+ it('should keep interpretation provenance outside dispatch identity', () => {
170
+ const command = new KeyCode({ key: Key.DIGIT8, ctrl: true, interpretation: 'commandModifiers' });
171
+ const layout = new KeyCode({ key: Key.DIGIT8, ctrl: true, interpretation: 'layoutModifiers' });
172
+
173
+ expect(command.dispatchString()).to.equal(layout.dispatchString());
174
+ expect(command.equals(layout)).to.be.true;
175
+ expect(command.interpretation).to.equal('commandModifiers');
176
+ expect(layout.interpretation).to.equal('layoutModifiers');
177
+ });
178
+
179
+ it('should normalize explicit and native AltGraph state', () => {
180
+ expect(readAltGraph({ altGraph: false, getModifierState: () => true })).to.be.false;
181
+ expect(readAltGraph({ getModifierState: modifier => modifier === 'AltGraph' })).to.be.true;
182
+ expect(readAltGraph({ code: 'Digit8' })).to.be.false;
183
+
184
+ expect(normalizeKeyboardInput({
185
+ key: '[',
186
+ code: 'Digit8',
187
+ keyCode: 56,
188
+ ctrlKey: true,
189
+ altGraph: true,
190
+ location: 0
191
+ })).to.deep.equal({
192
+ key: '[',
193
+ code: 'Digit8',
194
+ keyCode: 56,
195
+ charCode: undefined,
196
+ ctrlKey: true,
197
+ shiftKey: undefined,
198
+ altKey: undefined,
199
+ metaKey: undefined,
200
+ altGraph: true,
201
+ repeat: undefined,
202
+ isComposing: undefined,
203
+ location: 0,
204
+ timeStamp: undefined,
205
+ keyIdentifier: undefined,
206
+ getModifierState: undefined
207
+ });
208
+ });
209
+
210
+ it('should preserve every normalized keyboard input field', () => {
211
+ const getModifierState = (modifier: string) => modifier === 'AltGraph';
212
+ const input: Required<NormalizedKeyboardInput> = {
213
+ key: '[',
214
+ code: 'Digit8',
215
+ keyCode: 56,
216
+ charCode: 91,
217
+ ctrlKey: true,
218
+ shiftKey: true,
219
+ altKey: false,
220
+ metaKey: true,
221
+ altGraph: true,
222
+ repeat: true,
223
+ isComposing: false,
224
+ location: 2,
225
+ timeStamp: 42,
226
+ keyIdentifier: 'U+005B',
227
+ getModifierState
228
+ };
229
+
230
+ const normalized = normalizeKeyboardInput(input);
231
+
232
+ expect({ ...normalized, getModifierState: undefined }).to.deep.equal({ ...input, getModifierState: undefined });
233
+ expect(normalized.getModifierState?.('AltGraph')).to.be.true;
234
+ });
235
+
236
+ it('should create key codes from normalized plain data', () => {
237
+ const keyCode = KeyCode.createKeyCode({
238
+ key: '[',
239
+ code: 'Digit8',
240
+ keyCode: 56,
241
+ ctrlKey: true
242
+ });
243
+ expect(keyCode.key).to.equal(Key.DIGIT8);
244
+ expect(keyCode.ctrl).to.be.true;
245
+ expect(keyCode.character).to.equal('[');
246
+ });
247
+
248
+ it('should preserve legacy key identifier fallback in normalized data', () => {
249
+ const input = normalizeKeyboardInput({ keyIdentifier: Key.F1.code });
250
+ expect(KeyCode.createKeyCode(input).key).to.equal(Key.F1);
251
+ });
252
+
120
253
  it('the order of the modifiers should not matter when parsing the key code', () => {
121
254
  const left = KeySequence.parse('shift+alt+a');
122
255
  const right = KeySequence.parse('alt+shift+a');
@@ -231,6 +364,78 @@ describe('keys api', () => {
231
364
  expect(second.key).is.equal(Key.KEY_B);
232
365
  });
233
366
 
367
+ it('should parse physical scan codes and preserve them when serializing', () => {
368
+ const keycode = KeyCode.parse('ctrl+[BracketLeft]');
369
+ expect(keycode.ctrl).to.be.true;
370
+ expect(keycode.key).to.equal(Key.BRACKET_LEFT);
371
+ expect(keycode.physical).to.be.true;
372
+ expect(keycode.toString()).to.equal('ctrl+[BracketLeft]');
373
+ expect(() => KeyCode.parse('[UnknownPhysicalKey]')).to.throw(/Unknown scan code/);
374
+ });
375
+
376
+ it('should parse bare and escaped logical characters', () => {
377
+ for (const character of ['^', '§', '²', 'ä']) {
378
+ const keycode = KeyCode.parse(`ctrl+${character}`);
379
+ expect(keycode.character).to.equal(character);
380
+ expect(keycode.toString()).to.equal(`ctrl+${character}`);
381
+ }
382
+ expect(KeyCode.parse('ctrl+[char:0x2B]').character).to.equal('+');
383
+ expect(KeyCode.parse('ctrl+[char:0x20]').character).to.equal(' ');
384
+ expect(KeyCode.parse('ctrl+[char:0x5D]').character).to.equal(']');
385
+ expect(KeyCode.parse('ctrl+[char:§]').character).to.equal('§');
386
+ expect(KeyCode.parse('ctrl+[char:0x2B]').toString()).to.equal('ctrl+[char:0x2B]');
387
+ });
388
+
389
+ it('should keep bracketed separators and whitespace within one key token', () => {
390
+ expect(KeySequence.parse('ctrl+[char:0x20] ctrl+[BracketLeft]')).to.have.length(2);
391
+ expect(KeySequence.parse('ctrl+[ ctrl+]')).to.have.length(2);
392
+ expect(KeySequence.parse('ctrl+] ctrl+[')).to.have.length(2);
393
+ expect(KeyCode.parse('ctrl+[char:0x2B]').character).to.equal('+');
394
+ expect(KeyCode.parse('ctrl+[char:0x2D]').character).to.equal('-');
395
+ });
396
+
397
+ it('should reject invalid and multi-code-point logical characters', () => {
398
+ expect(() => KeyCode.parse('[char:0x110000]')).to.throw(/Invalid Unicode/);
399
+ expect(() => KeyCode.parse('[char:0xD800]')).to.throw(/Invalid Unicode/);
400
+ expect(() => KeyCode.parse('[char:ab]')).to.throw(/Invalid Unicode/);
401
+ expect(() => KeyCode.parse('👨‍👩‍👧‍👦')).to.throw(/Unrecognized key/);
402
+ });
403
+
404
+ it('should serialize recorder strokes as logical characters, named keys, or physical scan codes', () => {
405
+ expect(new KeyCode({ key: Key.DIGIT8, ctrl: true, character: '[' }).toAuthoredKeybindingString()).to.equal('ctrl+[');
406
+ expect(new KeyCode({ key: Key.EQUAL, ctrl: true, character: '+' }).toAuthoredKeybindingString()).to.equal('ctrl+[char:0x2B]');
407
+
408
+ const namedKeys: Array<[Key, string]> = [
409
+ [Key.F1, 'f1'],
410
+ [Key.DELETE, 'delete'],
411
+ [Key.BACKSPACE, 'backspace'],
412
+ [Key.ENTER, 'enter'],
413
+ [Key.ARROW_LEFT, 'left'],
414
+ [Key.SPACE, 'space'],
415
+ [Key.ESCAPE, 'escape']
416
+ ];
417
+ for (const [key, name] of namedKeys) {
418
+ const serialized = new KeyCode({ key, ctrl: true }).toAuthoredKeybindingString();
419
+ expect(serialized).to.equal(`ctrl+${name}`);
420
+ expect(KeyCode.parse(serialized).key).to.equal(key);
421
+ }
422
+
423
+ const physicalPrintable = new KeyCode({ key: Key.DIGIT8, ctrl: true }).toAuthoredKeybindingString();
424
+ expect(physicalPrintable).to.equal('ctrl+[Digit8]');
425
+ expect(KeyCode.parse(physicalPrintable).key).to.equal(Key.DIGIT8);
426
+
427
+ const shiftedLetter = new KeyCode({ key: Key.KEY_P, ctrl: true, shift: true, character: 'P' });
428
+ expect(shiftedLetter.toAuthoredKeybindingString()).to.equal('shift+ctrl+p');
429
+ expect(KeyCode.parse(shiftedLetter.toAuthoredKeybindingString()).dispatchString()).to.equal('shift+ctrl+p');
430
+ });
431
+
432
+ it('should serialize recorder strokes for Space and numpad keys by name', () => {
433
+ expect(new KeyCode({ key: Key.SPACE, meta: true, shift: true, character: ' ' }).toAuthoredKeybindingString()).to.equal('meta+shift+space');
434
+ expect(new KeyCode({ key: Key.ADD, ctrl: true, character: '+' }).toAuthoredKeybindingString()).to.equal('ctrl+add');
435
+ expect(new KeyCode({ key: Key.DECIMAL, ctrl: true, character: '.' }).toAuthoredKeybindingString()).to.equal('ctrl+decimal');
436
+ expect(new KeyCode({ key: Key.DIGIT1, ctrl: true, character: '1' }).toAuthoredKeybindingString()).to.equal('ctrl+1');
437
+ });
438
+
234
439
  it('should parse minus as key', () => {
235
440
  const keycode = KeyCode.parse('ctrl+-');
236
441
  expect(keycode.ctrl).to.be.true;
@@ -0,0 +1,60 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 EclipseSource and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { expect } from 'chai';
18
+ import { Key, KeyCode } from '../keyboard/keys';
19
+ import { KeybindingRegistry } from '../keybinding';
20
+ import { ActionMenuNode } from './action-menu-node';
21
+
22
+ describe('ActionMenuNode accelerators', () => {
23
+ it('selects logical and physical accelerator forms explicitly', () => {
24
+ const binding = { command: 'test.command', keybinding: 'ctrl+[' };
25
+ const state = { inactive: false };
26
+ const keybindingRegistry = Object.create(KeybindingRegistry.prototype) as KeybindingRegistry;
27
+ keybindingRegistry.getKeybindingsForCommand = () => [{ ...binding, scope: 0 }];
28
+ keybindingRegistry.isEnabledInScope = () => true;
29
+ keybindingRegistry.isKeybindingInactive = () => state.inactive;
30
+ keybindingRegistry.resolveKeybinding = () => [new KeyCode({
31
+ key: Key.DIGIT8,
32
+ ctrl: true,
33
+ character: '[',
34
+ layoutModifiers: 'altGraph'
35
+ })];
36
+ Object.defineProperty(keybindingRegistry, 'keyboardLayoutService', {
37
+ value: { getKeyboardCharacter: (key: Key) => key.easyString }
38
+ });
39
+ const commands = {
40
+ getAllHandlers: () => [],
41
+ getCommand: () => ({ id: 'test.command' })
42
+ };
43
+ const contextKeyService = {
44
+ parseKeys: () => undefined
45
+ };
46
+ const node = new ActionMenuNode(
47
+ { commandId: 'test.command' },
48
+ commands as never,
49
+ keybindingRegistry,
50
+ contextKeyService as never
51
+ );
52
+
53
+ expect(node.getAccelerator(undefined, 'logical')).to.deep.equal(['Ctrl+[']);
54
+ expect(node.getAccelerator(undefined, 'physical')).to.deep.equal(['Ctrl+AltGr+8']);
55
+
56
+ state.inactive = true;
57
+ expect(node.getAccelerator(undefined, 'logical')).to.deep.equal(['Ctrl+[']);
58
+ expect(node.getAccelerator(undefined, 'physical')).to.deep.equal([]);
59
+ });
60
+ });
@@ -14,13 +14,16 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { KeybindingRegistry } from '../keybinding';
17
+ import { AcceleratorForm, KeybindingRegistry } from '../keybinding';
18
18
  import { ContextKeyService } from '../context-key-service';
19
- import { DisposableCollection, isObject, CommandRegistry, Emitter, environment } from '../../common';
19
+ import { DisposableCollection, isObject, CommandRegistry, Emitter } from '../../common';
20
20
  import { CommandMenu, ContextExpressionMatcher, MenuAction, MenuPath } from '../../common/menu/menu-types';
21
21
 
22
+ export { AcceleratorForm } from '../keybinding';
23
+
22
24
  export interface AcceleratorSource {
23
- getAccelerator(context: HTMLElement | undefined): string[];
25
+ /** Return the accelerator rendered in the explicitly requested logical or physical form. */
26
+ getAccelerator(context: HTMLElement | undefined, form: AcceleratorForm): string[];
24
27
  }
25
28
 
26
29
  export namespace AcceleratorSource {
@@ -80,17 +83,8 @@ export class ActionMenuNode implements CommandMenu {
80
83
  return true;
81
84
  }
82
85
 
83
- getAccelerator(context: HTMLElement | undefined): string[] {
84
- const bindings = this.keybindingRegistry.getKeybindingsForCommand(this.action.commandId);
85
- // Only consider the first active keybinding.
86
- if (bindings.length) {
87
- const binding = bindings.find(b => this.keybindingRegistry.isEnabledInScope(b, context));
88
- if (binding) {
89
- const asciiOnly = environment.electron.is();
90
- return this.keybindingRegistry.acceleratorFor(binding, '+', asciiOnly);
91
- }
92
- }
93
- return [];
86
+ getAccelerator(context: HTMLElement | undefined, form: AcceleratorForm): string[] {
87
+ return this.keybindingRegistry.acceleratorForCommand(this.action.commandId, context, form);
94
88
  }
95
89
 
96
90
  isEnabled(effeciveMenuPath: MenuPath, ...args: unknown[]): boolean {
@@ -368,7 +368,7 @@ export class DynamicMenuWidget extends MenuWidget {
368
368
  iconClass: node.icon,
369
369
  });
370
370
 
371
- const accelerator = (AcceleratorSource.is(node) ? node.getAccelerator(this.options.context) : []);
371
+ const accelerator = (AcceleratorSource.is(node) ? node.getAccelerator(this.options.context, 'logical') : []);
372
372
  if (accelerator.length > 0) {
373
373
  phCommandRegistry.addKeyBinding({
374
374
  command: id,
@@ -58,6 +58,17 @@ export function enableJSDOM(): () => void {
58
58
  }
59
59
  });
60
60
  (dom.window.document as any)['queryCommandSupported'] = function (): void { };
61
+ dom.window.matchMedia = () => ({
62
+ matches: false,
63
+ media: '',
64
+ // eslint-disable-next-line no-null/no-null
65
+ onchange: null,
66
+ addListener: () => undefined,
67
+ removeListener: () => undefined,
68
+ addEventListener: () => undefined,
69
+ removeEventListener: () => undefined,
70
+ dispatchEvent: () => false
71
+ });
61
72
 
62
73
  const disableJSDOM = (global as any)['_disableJSDOM'] = () => {
63
74
  let property: string | undefined;
@@ -21,8 +21,11 @@ export const keyboardPath = '/services/keyboard';
21
21
 
22
22
  export const KeyboardLayoutProvider = Symbol('KeyboardLayoutProvider');
23
23
 
24
+ export type KeyboardLayoutSource = 'navigator.keyboard' | 'user-choice' | 'pressed-keys' | 'native-keymap';
25
+
24
26
  export interface KeyboardLayoutProvider {
25
27
  getNativeLayout(): Promise<NativeKeyboardLayout>;
28
+ readonly layoutSource?: KeyboardLayoutSource;
26
29
  }
27
30
 
28
31
  export const KeyboardLayoutChangeNotifier = Symbol('KeyboardLayoutChangeNotifier');
@@ -37,6 +40,11 @@ export interface KeyValidationInput {
37
40
  shiftKey?: boolean;
38
41
  ctrlKey?: boolean;
39
42
  altKey?: boolean;
43
+ /**
44
+ * Whether the AltGr layer is active for the platform (Linux: AltGraph, macOS: Option, Windows: AltGraph or Ctrl+Alt),
45
+ * not the raw `AltGraph` modifier state.
46
+ */
47
+ altGraph?: boolean;
40
48
  }
41
49
 
42
50
  export const KeyValidator = Symbol('KeyValidator');