diff --git a/packages/blockly/core/hints.ts b/packages/blockly/core/hints.ts index 25b9661830f..6c88f1ac506 100644 --- a/packages/blockly/core/hints.ts +++ b/packages/blockly/core/hints.ts @@ -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'; @@ -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); diff --git a/packages/blockly/core/utils/shortcut_formatting.ts b/packages/blockly/core/utils/shortcut_formatting.ts index 1009777c0fb..5130d72bf9b 100644 --- a/packages/blockly/core/utils/shortcut_formatting.ts +++ b/packages/blockly/core/utils/shortcut_formatting.ts @@ -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 ? ' ' : ' + '); @@ -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 = { @@ -51,21 +49,130 @@ const shortModifierNames: Record = { }; /** - * 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 = { + 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[][] { - const shortcuts = ShortcutRegistry.registry.getKeyCodesByShortcutName(action); + const shortcuts = + ShortcutRegistry.registry.getKeyCodesByShortcutName(shortcutName); if (shortcuts.length === 0) { return []; } @@ -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)); diff --git a/packages/blockly/msg/json/en.json b/packages/blockly/msg/json/en.json index 2f5709daedc..12fbfe682ab 100644 --- a/packages/blockly/msg/json/en.json +++ b/packages/blockly/msg/json/en.json @@ -1,7 +1,7 @@ { "@metadata": { "author": "Ellen Spertus ", - "lastupdated": "2026-04-21 16:21:15.987859", + "lastupdated": "2026-04-21 17:30:08.288719", "locale": "en", "messagedocumentation" : "qqq" }, @@ -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", diff --git a/packages/blockly/msg/json/qqq.json b/packages/blockly/msg/json/qqq.json index bc96e9f732b..d2fcf86cb33 100644 --- a/packages/blockly/msg/json/qqq.json +++ b/packages/blockly/msg/json/qqq.json @@ -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.", diff --git a/packages/blockly/msg/messages.js b/packages/blockly/msg/messages.js index f1705750dae..d5cd502d7cb 100644 --- a/packages/blockly/msg/messages.js +++ b/packages/blockly/msg/messages.js @@ -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} */