Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/blockly/core/hints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import {Msg} from './msg.js';
import {Toast} from './toast.js';
import {getShortActionShortcut} from './utils/shortcut_formatting.js';
import {getShortcutKeysShort} from './utils/shortcut_formatting.js';
import * as userAgent from './utils/useragent.js';
import type {WorkspaceSvg} from './workspace_svg.js';

Expand Down Expand Up @@ -73,7 +73,7 @@ export function clearMoveHints(workspace: WorkspaceSvg) {
* @param workspace The workspace.
*/
export function showHelpHint(workspace: WorkspaceSvg) {
const shortcut = getShortActionShortcut('list_shortcuts');
const shortcut = getShortcutKeysShort('list_shortcuts');
if (!shortcut) return;

const message = Msg['HELP_PROMPT'].replace('%1', shortcut);
Expand Down
137 changes: 122 additions & 15 deletions packages/blockly/core/utils/shortcut_formatting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ import * as userAgent from './useragent.js';
* Find the primary shortcut for this platform and return it as single string
* in a short user facing format.
*
* @internal
* @param action The action name, e.g. "cut".
* @param shortcutName The keyboard shortcut name, e.g. "cut".
* @returns The formatted shortcut.
*/
export function getShortActionShortcut(action: string): string {
const shortcuts = getActionShortcutsAsKeys(action, shortModifierNames);
export function getShortcutKeysShort(shortcutName: string): string {
const shortcuts = getShortcutKeys(shortcutName, shortModifierNames);
if (shortcuts.length) {
const parts = shortcuts[0];
return parts.join(userAgent.APPLE ? ' ' : ' + ');
Expand All @@ -27,15 +26,14 @@ export function getShortActionShortcut(action: string): string {
}

/**
* Find the relevant shortcuts for the given action for the current platform.
* Find the relevant shortcuts for the given shortcut for the current platform.
* Keys are returned in a long user facing format, e.g. "Command ⌘ Option ⌥ C"
*
* @internal
* @param action The action name, e.g. "cut".
* @param shortcutName The keyboard shortcut name, e.g. "cut".
* @returns The formatted shortcuts as individual keys.
*/
export function getLongActionShortcutsAsKeys(action: string): string[][] {
return getActionShortcutsAsKeys(action, longModifierNames);
export function getShortcutKeysLong(shortcutName: string): string[][] {
return getShortcutKeys(shortcutName, longModifierNames);
}

const longModifierNames: Record<string, string> = {
Expand All @@ -51,21 +49,130 @@ const shortModifierNames: Record<string, string> = {
};

/**
* Find the relevant shortcuts for the given action for the current platform.
* Key names for common characters. These should be used with keyup/keydown
* events, since the .keyCode property on those is meant to indicate the
* _physical key_ the user held down on the keyboard. Hence the mapping uses
* only the unshifted version of each key (e.g. no '#', since that's shift+3).
* Keypress events on the other hand generate (mostly) ASCII codes since they
* correspond to *characters* the user typed.
*
* For further reference: http://unixpapa.com/js/key.html
*
* This list is not localized and therefore some of the key codes are not
* correct for non-US keyboard layouts.
*
* Partially copied from goog.events.keynames and modified to use translatable
* strings or symbols for keys.
*/
const keyNames: Record<number, string> = {
8: Msg['BACKSPACE_KEY'],
9: Msg['TAB_KEY'],
13: Msg['ENTER_KEY'],
16: Msg['SHIFT_KEY'],
17: Msg['CTRL_KEY'],
18: Msg['ALT_KEY'],
19: Msg['PAUSE_KEY'],
20: Msg['CAPS_LOCK_KEY'],
27: Msg['ESCAPE_KEY'],
32: Msg['SPACE_KEY'],
33: Msg['PAGE_UP_KEY'],
34: Msg['PAGE_DOWN_KEY'],
35: Msg['END_KEY'],
36: Msg['HOME_KEY'],
37: '←',
38: '↑',
39: '→',
40: '↓',
45: Msg['INSERT_KEY'],
46: Msg['DELETE_KEY'],
48: '0',
49: '1',
50: '2',
51: '3',
52: '4',
53: '5',
54: '6',
55: '7',
56: '8',
57: '9',
59: ';',
61: '=',
93: Msg['CONTEXT_MENU_KEY'],
96: '0',
97: '1',
98: '2',
99: '3',
100: '4',
101: '5',
102: '6',
103: '7',
104: '8',
105: '9',
106: '×',
107: '+',
109: '−',
110: '.',
111: '÷',
112: 'F1',
113: 'F2',
114: 'F3',
115: 'F4',
116: 'F5',
117: 'F6',
118: 'F7',
119: 'F8',
120: 'F9',
121: 'F10',
122: 'F11',
123: 'F12',
186: ';',
187: '=',
189: '-',
188: ',',
190: '.',
191: '/',
192: '`',
219: '[',
220: '\\',
221: ']',
222: "'",
224: '⌘',
};

/**
* Gets a user-facing name for a keycode.
*
* @param keyCode
* @returns key name, or the character for the keycode if it's not in the list of known keys.
*/
function getKeyName(keyCode: number): string {
if (keyCode >= 65 && keyCode <= 90) {
// letters a-z
return String.fromCharCode(keyCode);
}
const keyName = keyNames[keyCode];
if (keyName) return keyName;
console.warn('Unknown key code: ' + keyCode);
return String.fromCharCode(keyCode);
}

/**
* Find the relevant shortcuts for the given shortcut for the current platform.
* Keys are returned in a short user facing format, e.g. "⌘ ⌥ C"
*
* This could be considerably simpler if we only bound shortcuts relevant to the
* current platform or tagged them with a platform.
*
* @param action The action name, e.g. "cut".
* @param shortcutName The keyboard shortcut name, e.g. "cut".
* @param modifierNames The names to use for the Meta/Control/Alt modifiers.
* @returns The formatted shortcuts.
*/
function getActionShortcutsAsKeys(
action: string,
function getShortcutKeys(
shortcutName: string,
modifierNames: Record<string, string>,
): string[][] {
const shortcuts = ShortcutRegistry.registry.getKeyCodesByShortcutName(action);
const shortcuts =
ShortcutRegistry.registry.getKeyCodesByShortcutName(shortcutName);
if (shortcuts.length === 0) {
return [];
}
Expand Down Expand Up @@ -109,7 +216,7 @@ function getActionShortcutsAsKeys(
return shortcut
.map((maybeNumeric) =>
Number.isFinite(+maybeNumeric)
? String.fromCharCode(+maybeNumeric)
? getKeyName(+maybeNumeric)
: maybeNumeric,
)
.map((k) => upperCaseFirst(modifierNames[k] ?? k));
Expand Down
16 changes: 15 additions & 1 deletion packages/blockly/msg/json/en.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"@metadata": {
"author": "Ellen Spertus <ellen.spertus@gmail.com>",
"lastupdated": "2026-04-21 16:21:15.987859",
"lastupdated": "2026-04-21 17:30:08.288719",
"locale": "en",
"messagedocumentation" : "qqq"
},
Expand Down Expand Up @@ -410,6 +410,20 @@
"OPTION_KEY": "⌥ Option",
"ALT_KEY": "Alt",
"ENTER_KEY": "Enter",
"BACKSPACE_KEY": "Backspace",
"DELETE_KEY": "Delete",
"ESCAPE": "Esc",
"TAB_KEY": "Tab",
"SHIFT_KEY": "Shift",
"CAPS_LOCK_KEY": "Caps Lock",
"SPACE_KEY": "Space",
"PAGE_UP_KEY": "Page Up",
"PAGE_DOWN_KEY": "Page Down",
"END_KEY": "End",
"HOME_KEY": "Home",
"INSERT_KEY": "Insert",
"PAUSE_KEY": "Pause",
"CONTEXT_MENU_KEY": "≣ Menu",
"CUT_SHORTCUT": "Cut",
"COPY_SHORTCUT": "Copy",
"PASTE_SHORTCUT": "Paste",
Expand Down
14 changes: 14 additions & 0 deletions packages/blockly/msg/json/qqq.json
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,20 @@
"OPTION_KEY": "Representation of the Mac Option key used in keyboard shortcuts.",
"ALT_KEY": "Representation of the Alt key used in keyboard shortcuts.",
"ENTER_KEY": "Representation of the Enter key used in keyboard shortcuts.",
"BACKSPACE_KEY": "Representation of the Backspace key used in keyboard shortcuts.",
"DELETE_KEY": "Representation of the Delete key used in keyboard shortcuts.",
"ESCAPE": "Representation of the Escape key used in keyboard shortcuts.",
"TAB_KEY": "Representation of the Tab key used in keyboard shortcuts.",
"SHIFT_KEY": "Representation of the Shift key used in keyboard shortcuts.",
"CAPS_LOCK_KEY": "Representation of the Caps Lock key used in keyboard shortcuts.",
"SPACE_KEY": "Representation of the Space key used in keyboard shortcuts.",
"PAGE_UP_KEY": "Representation of the Page Up key used in keyboard shortcuts.",
"PAGE_DOWN_KEY": "Representation of the Page Down key used in keyboard shortcuts.",
"END_KEY": "Representation of the End key used in keyboard shortcuts.",
"HOME_KEY": "Representation of the Home key used in keyboard shortcuts.",
"INSERT_KEY": "Representation of the Insert key used in keyboard shortcuts.",
"PAUSE_KEY": "Representation of the Pause key used in keyboard shortcuts.",
"CONTEXT_MENU_KEY": "Representation of the Context Menu key used in keyboard shortcuts.",
"CUT_SHORTCUT": "menu label - Contextual menu item that cuts the focused item.",
"COPY_SHORTCUT": "menu label - Contextual menu item that copies the focused item.",
"PASTE_SHORTCUT": "menu label - Contextual menu item that pastes the previously copied item.",
Expand Down
42 changes: 42 additions & 0 deletions packages/blockly/msg/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -1661,6 +1661,48 @@ Blockly.Msg.ALT_KEY = 'Alt';
/// Representation of the Enter key used in keyboard shortcuts.
Blockly.Msg.ENTER_KEY = 'Enter';
/** @type {string} */
/// Representation of the Backspace key used in keyboard shortcuts.
Blockly.Msg.BACKSPACE_KEY = 'Backspace';
/** @type {string} */
/// Representation of the Delete key used in keyboard shortcuts.
Blockly.Msg.DELETE_KEY = 'Delete';
/** @type {string} */
/// Representation of the Escape key used in keyboard shortcuts.
Blockly.Msg.ESCAPE = 'Esc';
/** @type {string} */
/// Representation of the Tab key used in keyboard shortcuts.
Blockly.Msg.TAB_KEY = 'Tab';
/** @type {string} */
/// Representation of the Shift key used in keyboard shortcuts.
Blockly.Msg.SHIFT_KEY = 'Shift';
/** @type {string} */
/// Representation of the Caps Lock key used in keyboard shortcuts.
Blockly.Msg.CAPS_LOCK_KEY = 'Caps Lock';
/** @type {string} */
/// Representation of the Space key used in keyboard shortcuts.
Blockly.Msg.SPACE_KEY = 'Space';
/** @type {string} */
/// Representation of the Page Up key used in keyboard shortcuts.
Blockly.Msg.PAGE_UP_KEY = 'Page Up';
/** @type {string} */
/// Representation of the Page Down key used in keyboard shortcuts.
Blockly.Msg.PAGE_DOWN_KEY = 'Page Down';
/** @type {string} */
/// Representation of the End key used in keyboard shortcuts.
Blockly.Msg.END_KEY = 'End';
/** @type {string} */
/// Representation of the Home key used in keyboard shortcuts.
Blockly.Msg.HOME_KEY = 'Home';
/** @type {string} */
/// Representation of the Insert key used in keyboard shortcuts.
Blockly.Msg.INSERT_KEY = 'Insert';
/** @type {string} */
/// Representation of the Pause key used in keyboard shortcuts.
Blockly.Msg.PAUSE_KEY = 'Pause';
/** @type {string} */
/// Representation of the Context Menu key used in keyboard shortcuts.
Blockly.Msg.CONTEXT_MENU_KEY = '≣ Menu';
/** @type {string} */
/// menu label - Contextual menu item that cuts the focused item.
Blockly.Msg.CUT_SHORTCUT = 'Cut';
/** @type {string} */
Expand Down
Loading