diff --git a/packages/common/src/utils.ts b/packages/common/src/utils.ts index 5d29370249..dab6f700f0 100644 --- a/packages/common/src/utils.ts +++ b/packages/common/src/utils.ts @@ -16,7 +16,6 @@ import { ENV, FONT_FAMILY, getFontFamilyFallbacks, - isDarwin, isAndroid, isIOS, WINDOWS_EMOJI_FALLBACK_FONT, @@ -422,19 +421,6 @@ export const allowFullScreen = () => export const exitFullScreen = () => document.exitFullscreen(); -export const getShortcutKey = (shortcut: string): string => { - shortcut = shortcut - .replace(/\bAlt\b/i, "Alt") - .replace(/\bShift\b/i, "Shift") - .replace(/\b(Enter|Return)\b/i, "Enter"); - if (isDarwin) { - return shortcut - .replace(/\bCtrlOrCmd\b/gi, "Cmd") - .replace(/\bAlt\b/i, "Option"); - } - return shortcut.replace(/\bCtrlOrCmd\b/gi, "Ctrl"); -}; - export const viewportCoordsToSceneCoords = ( { clientX, clientY }: { clientX: number; clientY: number }, { diff --git a/packages/excalidraw/actions/actionAlign.tsx b/packages/excalidraw/actions/actionAlign.tsx index 63a887635b..2bbe4fab97 100644 --- a/packages/excalidraw/actions/actionAlign.tsx +++ b/packages/excalidraw/actions/actionAlign.tsx @@ -4,7 +4,7 @@ import { isFrameLikeElement } from "@excalidraw/element"; import { updateFrameMembershipOfSelectedElements } from "@excalidraw/element"; -import { KEYS, arrayToMap, getShortcutKey } from "@excalidraw/common"; +import { KEYS, arrayToMap } from "@excalidraw/common"; import { alignElements } from "@excalidraw/element"; @@ -30,6 +30,8 @@ import { t } from "../i18n"; import { isSomeElementSelected } from "../scene"; +import { getShortcutKey } from "../shortcut"; + import { register } from "./register"; import type { AppClassProperties, AppState, UIAppState } from "../types"; diff --git a/packages/excalidraw/actions/actionCanvas.tsx b/packages/excalidraw/actions/actionCanvas.tsx index df0b7511da..66fa2fd8a5 100644 --- a/packages/excalidraw/actions/actionCanvas.tsx +++ b/packages/excalidraw/actions/actionCanvas.tsx @@ -7,7 +7,6 @@ import { MIN_ZOOM, THEME, ZOOM_STEP, - getShortcutKey, updateActiveTool, CODES, KEYS, @@ -46,6 +45,7 @@ import { t } from "../i18n"; import { getNormalizedZoom } from "../scene"; import { centerScrollOn } from "../scene/scroll"; import { getStateForZoom } from "../scene/zoom"; +import { getShortcutKey } from "../shortcut"; import { register } from "./register"; diff --git a/packages/excalidraw/actions/actionDistribute.tsx b/packages/excalidraw/actions/actionDistribute.tsx index f02906741c..88e085f1de 100644 --- a/packages/excalidraw/actions/actionDistribute.tsx +++ b/packages/excalidraw/actions/actionDistribute.tsx @@ -2,7 +2,7 @@ import { getNonDeletedElements } from "@excalidraw/element"; import { isFrameLikeElement } from "@excalidraw/element"; -import { CODES, KEYS, arrayToMap, getShortcutKey } from "@excalidraw/common"; +import { CODES, KEYS, arrayToMap } from "@excalidraw/common"; import { updateFrameMembershipOfSelectedElements } from "@excalidraw/element"; @@ -26,6 +26,8 @@ import { t } from "../i18n"; import { isSomeElementSelected } from "../scene"; +import { getShortcutKey } from "../shortcut"; + import { register } from "./register"; import type { AppClassProperties, AppState } from "../types"; diff --git a/packages/excalidraw/actions/actionDuplicateSelection.tsx b/packages/excalidraw/actions/actionDuplicateSelection.tsx index daf1dbb3c6..69508a0228 100644 --- a/packages/excalidraw/actions/actionDuplicateSelection.tsx +++ b/packages/excalidraw/actions/actionDuplicateSelection.tsx @@ -3,7 +3,6 @@ import { KEYS, MOBILE_ACTION_BUTTON_BG, arrayToMap, - getShortcutKey, } from "@excalidraw/common"; import { getNonDeletedElements } from "@excalidraw/element"; @@ -26,6 +25,7 @@ import { DuplicateIcon } from "../components/icons"; import { t } from "../i18n"; import { isSomeElementSelected } from "../scene"; +import { getShortcutKey } from "../shortcut"; import { register } from "./register"; diff --git a/packages/excalidraw/actions/actionGroup.tsx b/packages/excalidraw/actions/actionGroup.tsx index dc0c22efdb..c72216b761 100644 --- a/packages/excalidraw/actions/actionGroup.tsx +++ b/packages/excalidraw/actions/actionGroup.tsx @@ -14,7 +14,7 @@ import { replaceAllElementsInFrame, } from "@excalidraw/element"; -import { KEYS, randomId, arrayToMap, getShortcutKey } from "@excalidraw/common"; +import { KEYS, randomId, arrayToMap } from "@excalidraw/common"; import { getSelectedGroupIds, @@ -43,6 +43,8 @@ import { t } from "../i18n"; import { isSomeElementSelected } from "../scene"; +import { getShortcutKey } from "../shortcut"; + import { register } from "./register"; import type { AppClassProperties, AppState } from "../types"; diff --git a/packages/excalidraw/actions/actionLink.tsx b/packages/excalidraw/actions/actionLink.tsx index abb78f7f51..34b3326b57 100644 --- a/packages/excalidraw/actions/actionLink.tsx +++ b/packages/excalidraw/actions/actionLink.tsx @@ -1,6 +1,6 @@ import { isEmbeddableElement } from "@excalidraw/element"; -import { KEYS, getShortcutKey } from "@excalidraw/common"; +import { KEYS } from "@excalidraw/common"; import { CaptureUpdateAction } from "@excalidraw/element"; @@ -8,8 +8,8 @@ import { ToolButton } from "../components/ToolButton"; import { getContextMenuLabel } from "../components/hyperlink/Hyperlink"; import { LinkIcon } from "../components/icons"; import { t } from "../i18n"; - import { getSelectedElements } from "../scene"; +import { getShortcutKey } from "../shortcut"; import { register } from "./register"; diff --git a/packages/excalidraw/actions/actionProperties.tsx b/packages/excalidraw/actions/actionProperties.tsx index 59dc2d4882..35f6f271f8 100644 --- a/packages/excalidraw/actions/actionProperties.tsx +++ b/packages/excalidraw/actions/actionProperties.tsx @@ -18,7 +18,6 @@ import { randomInteger, arrayToMap, getFontFamilyString, - getShortcutKey, getLineHeight, isTransparent, reduceToCommonValue, @@ -144,6 +143,8 @@ import { restoreCaretPosition, } from "../hooks/useTextEditorFocus"; +import { getShortcutKey } from "../shortcut"; + import { register } from "./register"; import type { AppClassProperties, AppState, Primitive } from "../types"; diff --git a/packages/excalidraw/actions/actionZindex.tsx b/packages/excalidraw/actions/actionZindex.tsx index 62a6aa411f..1269ed23f6 100644 --- a/packages/excalidraw/actions/actionZindex.tsx +++ b/packages/excalidraw/actions/actionZindex.tsx @@ -1,4 +1,4 @@ -import { KEYS, CODES, getShortcutKey, isDarwin } from "@excalidraw/common"; +import { KEYS, CODES, isDarwin } from "@excalidraw/common"; import { moveOneLeft, @@ -16,6 +16,7 @@ import { SendToBackIcon, } from "../components/icons"; import { t } from "../i18n"; +import { getShortcutKey } from "../shortcut"; import { register } from "./register"; diff --git a/packages/excalidraw/actions/shortcuts.ts b/packages/excalidraw/actions/shortcuts.ts index 1a13f1703c..ca593c3402 100644 --- a/packages/excalidraw/actions/shortcuts.ts +++ b/packages/excalidraw/actions/shortcuts.ts @@ -1,8 +1,9 @@ -import { isDarwin, getShortcutKey } from "@excalidraw/common"; +import { isDarwin } from "@excalidraw/common"; import type { SubtypeOf } from "@excalidraw/common/utility-types"; import { t } from "../i18n"; +import { getShortcutKey } from "../shortcut"; import type { ActionName } from "./types"; diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index ac62e42be7..ab68c51005 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -80,7 +80,6 @@ import { wrapEvent, updateObject, updateActiveTool, - getShortcutKey, isTransparent, easeToValuesRAF, muteFSAbortError, @@ -411,6 +410,8 @@ import { LassoTrail } from "../lasso"; import { EraserTrail } from "../eraser"; +import { getShortcutKey } from "../shortcut"; + import ConvertElementTypePopup, { getConversionTypeFromElements, convertElementTypePopupAtom, diff --git a/packages/excalidraw/components/ColorPicker/ColorInput.tsx b/packages/excalidraw/components/ColorPicker/ColorInput.tsx index e5e6f3a771..557f9c1c00 100644 --- a/packages/excalidraw/components/ColorPicker/ColorInput.tsx +++ b/packages/excalidraw/components/ColorPicker/ColorInput.tsx @@ -1,8 +1,9 @@ import clsx from "clsx"; import { useCallback, useEffect, useRef, useState } from "react"; -import { KEYS, getShortcutKey } from "@excalidraw/common"; +import { KEYS } from "@excalidraw/common"; +import { getShortcutKey } from "../..//shortcut"; import { useAtom } from "../../editor-jotai"; import { t } from "../../i18n"; import { useDevice } from "../App"; diff --git a/packages/excalidraw/components/CommandPalette/CommandPalette.tsx b/packages/excalidraw/components/CommandPalette/CommandPalette.tsx index d72a1001ab..c6a8c19609 100644 --- a/packages/excalidraw/components/CommandPalette/CommandPalette.tsx +++ b/packages/excalidraw/components/CommandPalette/CommandPalette.tsx @@ -7,12 +7,13 @@ import { EVENT, KEYS, capitalizeString, - getShortcutKey, isWritableElement, } from "@excalidraw/common"; import { actionToggleShapeSwitch } from "@excalidraw/excalidraw/actions/actionToggleShapeSwitch"; +import { getShortcutKey } from "@excalidraw/excalidraw/shortcut"; + import type { MarkRequired } from "@excalidraw/common/utility-types"; import { diff --git a/packages/excalidraw/components/HelpDialog.tsx b/packages/excalidraw/components/HelpDialog.tsx index 2cc8de0b7c..8fea159bec 100644 --- a/packages/excalidraw/components/HelpDialog.tsx +++ b/packages/excalidraw/components/HelpDialog.tsx @@ -2,11 +2,12 @@ import React from "react"; import { isDarwin, isFirefox, isWindows } from "@excalidraw/common"; -import { KEYS, getShortcutKey } from "@excalidraw/common"; +import { KEYS } from "@excalidraw/common"; import { getShortcutFromShortcutName } from "../actions/shortcuts"; import { probablySupportsClipboardBlob } from "../clipboard"; import { t } from "../i18n"; +import { getShortcutKey } from "../shortcut"; import { Dialog } from "./Dialog"; import { ExternalLinkIcon, GithubIcon, youtubeIcon } from "./icons"; diff --git a/packages/excalidraw/components/HintViewer.scss b/packages/excalidraw/components/HintViewer.scss index 7ed7e62362..bb18bf5a54 100644 --- a/packages/excalidraw/components/HintViewer.scss +++ b/packages/excalidraw/components/HintViewer.scss @@ -28,11 +28,24 @@ $wide-viewport-width: 1000px; > span { padding: 0.25rem; } + + kbd { + display: inline-block; + margin: 0 1px; + font-family: monospace; + border: 1px solid var(--color-gray-40); + border-radius: 4px; + padding: 1px 3px; + font-size: 10px; + } } &.theme--dark { .HintViewer { color: var(--color-gray-60); + kbd { + border-color: var(--color-gray-60); + } } } } diff --git a/packages/excalidraw/components/HintViewer.tsx b/packages/excalidraw/components/HintViewer.tsx index 93cc167bf4..7eec7cbd58 100644 --- a/packages/excalidraw/components/HintViewer.tsx +++ b/packages/excalidraw/components/HintViewer.tsx @@ -10,11 +10,10 @@ import { isTextElement, } from "@excalidraw/element"; -import { getShortcutKey } from "@excalidraw/common"; - import { isNodeInFlowchart } from "@excalidraw/element"; import { t } from "../i18n"; +import { getShortcutKey } from "../shortcut"; import { isEraserActive } from "../appState"; import { isGridModeEnabled } from "../snapping"; @@ -29,6 +28,11 @@ interface HintViewerProps { app: AppClassProperties; } +const getTaggedShortcutKey = (key: string | string[]) => + Array.isArray(key) + ? `${key.map(getShortcutKey).join(" + ")}` + : `${getShortcutKey(key)}`; + const getHints = ({ appState, isMobile, @@ -50,7 +54,9 @@ const getHints = ({ appState.openSidebar.tab === CANVAS_SEARCH_TAB && appState.searchMatches?.matches.length ) { - return t("hints.dismissSearch"); + return t("hints.dismissSearch", { + shortcut: getTaggedShortcutKey("Escape"), + }); } if (appState.openSidebar && !device.editor.canFitSidebar) { @@ -58,14 +64,21 @@ const getHints = ({ } if (isEraserActive(appState)) { - return t("hints.eraserRevert"); + return t("hints.eraserRevert", { + shortcut: getTaggedShortcutKey("Alt"), + }); } if (activeTool.type === "arrow" || activeTool.type === "line") { if (multiMode) { - return t("hints.linearElementMulti"); + return t("hints.linearElementMulti", { + shortcut_1: getTaggedShortcutKey("Escape"), + shortcut_2: getTaggedShortcutKey("Enter"), + }); } if (activeTool.type === "arrow") { - return t("hints.arrowTool", { arrowShortcut: getShortcutKey("A") }); + return t("hints.arrowTool", { + shortcut: getTaggedShortcutKey("A"), + }); } return t("hints.linearElement"); } @@ -91,31 +104,51 @@ const getHints = ({ ) { const targetElement = selectedElements[0]; if (isLinearElement(targetElement) && targetElement.points.length === 2) { - return t("hints.lockAngle"); + return t("hints.lockAngle", { + shortcut: getTaggedShortcutKey("Shift"), + }); } return isImageElement(targetElement) - ? t("hints.resizeImage") - : t("hints.resize"); + ? t("hints.resizeImage", { + shortcut_1: getTaggedShortcutKey("Shift"), + shortcut_2: getTaggedShortcutKey("Alt"), + }) + : t("hints.resize", { + shortcut_1: getTaggedShortcutKey("Shift"), + shortcut_2: getTaggedShortcutKey("Alt"), + }); } if (isRotating && lastPointerDownWith === "mouse") { - return t("hints.rotate"); + return t("hints.rotate", { + shortcut: getTaggedShortcutKey("Shift"), + }); } if (selectedElements.length === 1 && isTextElement(selectedElements[0])) { - return t("hints.text_selected"); + return t("hints.text_selected", { + shortcut: getTaggedShortcutKey("Enter"), + }); } if (appState.editingTextElement) { - return t("hints.text_editing"); + return t("hints.text_editing", { + shortcut_1: getTaggedShortcutKey("Escape"), + shortcut_2: getTaggedShortcutKey(["CtrlOrCmd", "Enter"]), + }); } if (appState.croppingElementId) { - return t("hints.leaveCropEditor"); + return t("hints.leaveCropEditor", { + shortcut_1: getTaggedShortcutKey("Enter"), + shortcut_2: getTaggedShortcutKey("Escape"), + }); } if (selectedElements.length === 1 && isImageElement(selectedElements[0])) { - return t("hints.enterCropEditor"); + return t("hints.enterCropEditor", { + shortcut: getTaggedShortcutKey("Enter"), + }); } if (activeTool.type === "selection") { @@ -125,33 +158,60 @@ const getHints = ({ !appState.editingTextElement && !appState.selectedLinearElement?.isEditing ) { - return [t("hints.deepBoxSelect")]; + return [ + t("hints.deepBoxSelect", { + shortcut: getTaggedShortcutKey("CtrlOrCmd"), + }), + ]; } if (isGridModeEnabled(app) && appState.selectedElementsAreBeingDragged) { - return t("hints.disableSnapping"); + return t("hints.disableSnapping", { + shortcut: getTaggedShortcutKey("CtrlOrCmd"), + }); } if (!selectedElements.length && !isMobile) { - return [t("hints.canvasPanning")]; + return [ + t("hints.canvasPanning", { + shortcut: getTaggedShortcutKey("Space"), + }), + ]; } if (selectedElements.length === 1) { if (isLinearElement(selectedElements[0])) { if (appState.selectedLinearElement?.isEditing) { return appState.selectedLinearElement.selectedPointsIndices - ? t("hints.lineEditor_pointSelected") - : t("hints.lineEditor_nothingSelected"); + ? t("hints.lineEditor_pointSelected", { + shortcut_1: getTaggedShortcutKey("Delete"), + shortcut_2: getTaggedShortcutKey(["CtrlOrCmd", "D"]), + }) + : t("hints.lineEditor_nothingSelected", { + shortcut_1: getTaggedShortcutKey("Shift"), + shortcut_2: getTaggedShortcutKey("Alt"), + }); } return isLineElement(selectedElements[0]) - ? t("hints.lineEditor_line_info") - : t("hints.lineEditor_info"); + ? t("hints.lineEditor_line_info", { + shortcut: getTaggedShortcutKey("Enter"), + }) + : t("hints.lineEditor_info", { + shortcut_1: getTaggedShortcutKey("CtrlOrCmd"), + shortcut_2: getTaggedShortcutKey(["CtrlOrCmd", "Enter"]), + }); } if ( !appState.newElement && !appState.selectedElementsAreBeingDragged && isTextBindableContainer(selectedElements[0]) ) { + const bindTextToElement = t("hints.bindTextToElement", { + shortcut: getTaggedShortcutKey("Enter"), + }); + const createFlowchart = t("hints.createFlowchart", { + shortcut: getTaggedShortcutKey("CtrlOrCmd"), + }); if (isFlowchartNodeElement(selectedElements[0])) { if ( isNodeInFlowchart( @@ -159,13 +219,13 @@ const getHints = ({ app.scene.getNonDeletedElementsMap(), ) ) { - return [t("hints.bindTextToElement"), t("hints.createFlowchart")]; + return [bindTextToElement, createFlowchart]; } - return [t("hints.bindTextToElement"), t("hints.createFlowchart")]; + return [bindTextToElement, createFlowchart]; } - return t("hints.bindTextToElement"); + return bindTextToElement; } } } @@ -191,16 +251,21 @@ export const HintViewer = ({ } const hint = Array.isArray(hints) - ? hints - .map((hint) => { - return getShortcutKey(hint).replace(/\. ?$/, ""); - }) - .join(". ") - : getShortcutKey(hints); + ? hints.map((hint) => hint.replace(/\. ?$/, "")).join(". ") + : hints; + + const hintJSX = hint.split(/([^<]+<\/kbd>)/g).map((part, index) => { + if (index % 2 === 1) { + const shortcutMatch = + part[0] === "<" && part.match(/^([^<]+)<\/kbd>$/); + return {shortcutMatch ? shortcutMatch[1] : part}; + } + return part; + }); return (
- {hint} + {hintJSX}
); }; diff --git a/packages/excalidraw/components/TTDDialog/TTDDialogSubmitShortcut.tsx b/packages/excalidraw/components/TTDDialog/TTDDialogSubmitShortcut.tsx index 05cad640b8..21a6f16948 100644 --- a/packages/excalidraw/components/TTDDialog/TTDDialogSubmitShortcut.tsx +++ b/packages/excalidraw/components/TTDDialog/TTDDialogSubmitShortcut.tsx @@ -1,4 +1,4 @@ -import { getShortcutKey } from "@excalidraw/common"; +import { getShortcutKey } from "@excalidraw/excalidraw/shortcut"; export const TTDDialogSubmitShortcut = () => { return ( diff --git a/packages/excalidraw/locales/en.json b/packages/excalidraw/locales/en.json index b0058575d2..87895d4fdc 100644 --- a/packages/excalidraw/locales/en.json +++ b/packages/excalidraw/locales/en.json @@ -337,34 +337,34 @@ "shapes": "Shapes" }, "hints": { - "dismissSearch": "Escape to dismiss search", - "canvasPanning": "To move canvas, hold mouse wheel or spacebar while dragging, or use the hand tool", + "dismissSearch": "{{shortcut}} to dismiss search", + "canvasPanning": "To move canvas, hold mouse wheel or {{shortcut}} while dragging, or use the hand tool", "linearElement": "Click to start multiple points, drag for single line", "arrowBindModifiers": "Hold Alt to bind inside, or CtrlOrCmd to disable binding", "arrowTool": "Click to start multiple points, drag for single line. Press {{arrowShortcut}} again to change arrow type.", "freeDraw": "Click and drag, release when you're finished", "text": "Tip: you can also add text by double-clicking anywhere with the selection tool", "embeddable": "Click-drag to create a website embed", - "text_selected": "Double-click or press ENTER to edit text", - "text_editing": "Press Escape or CtrlOrCmd+ENTER to finish editing", - "linearElementMulti": "Click on last point or press Escape or Enter to finish", - "lockAngle": "You can constrain angle by holding SHIFT", - "resize": "You can constrain proportions by holding SHIFT while resizing,\nhold ALT to resize from the center", - "resizeImage": "You can resize freely by holding SHIFT,\nhold ALT to resize from the center", - "rotate": "You can constrain angles by holding SHIFT while rotating", - "lineEditor_info": "Hold CtrlOrCmd and Double-click or press CtrlOrCmd + Enter to edit points", - "lineEditor_line_info": "Double-click or press Enter to edit points", - "lineEditor_pointSelected": "Press Delete to remove point(s),\nCtrlOrCmd+D to duplicate, or drag to move", - "lineEditor_nothingSelected": "Select a point to edit (hold SHIFT to select multiple),\nor hold Alt and click to add new points", + "text_selected": "Double-click or press {{shortcut}} to edit text", + "text_editing": "Press {{shortcut_1}} or {{shortcut_2}} to finish editing", + "linearElementMulti": "Click on last point or press {{shortcut_1}} or {{shortcut_2}} to finish", + "lockAngle": "You can constrain angle by holding {{shortcut}}", + "resize": "You can constrain proportions by holding {{shortcut_1}} while resizing,\nhold {{shortcut_2}} to resize from the center", + "resizeImage": "You can resize freely by holding {{shortcut_1}},\nhold {{shortcut_2}} to resize from the center", + "rotate": "You can constrain angles by holding {{shortcut}} while rotating", + "lineEditor_info": "Hold {{shortcut_1}} and Double-click or press {{shortcut_2}} to edit points", + "lineEditor_line_info": "Double-click or press {{shortcut}} to edit points", + "lineEditor_pointSelected": "Press {{shortcut_1}} to remove point(s),\n{{shortcut_2}} to duplicate, or drag to move", + "lineEditor_nothingSelected": "Select a point to edit (hold {{shortcut_1}} to select multiple),\nor hold {{shortcut_2}} and click to add new points", "publishLibrary": "Publish your own library", - "bindTextToElement": "Press enter to add text", - "createFlowchart": "Hold CtrlOrCmd and Arrow key to create a flowchart", - "deepBoxSelect": "Hold CtrlOrCmd to deep select, and to prevent dragging", - "eraserRevert": "Hold Alt to revert the elements marked for deletion", + "bindTextToElement": "Press {{shortcut}} to add text", + "createFlowchart": "Hold {{shortcut}} and Arrow key to create a flowchart", + "deepBoxSelect": "Hold {{shortcut}} to deep select, and to prevent dragging", + "eraserRevert": "Hold {{shortcut}} to revert the elements marked for deletion", "firefox_clipboard_write": "This feature can likely be enabled by setting the \"dom.events.asyncClipboard.clipboardItem\" flag to \"true\". To change the browser flags in Firefox, visit the \"about:config\" page.", - "disableSnapping": "Hold CtrlOrCmd to disable snapping", - "enterCropEditor": "Double click the image or press ENTER to crop the image", - "leaveCropEditor": "Click outside the image or press ENTER or ESCAPE to finish cropping" + "disableSnapping": "Hold {{shortcut}} to disable snapping", + "enterCropEditor": "Double click the image or press {{shortcut}} to crop the image", + "leaveCropEditor": "Click outside the image or press {{shortcut_1}} or {{shortcut_2}} to finish cropping" }, "canvasError": { "cannotShowPreview": "Cannot show preview", @@ -649,5 +649,16 @@ }, "itemNotAvailable": "Command is not available...", "shortcutHint": "For Command palette, use {{shortcut}}" + }, + "keys": { + "ctrl": "Ctrl", + "option": "Option", + "cmd": "Cmd", + "alt": "Alt", + "escape": "Esc", + "enter": "Enter", + "shift": "Shift", + "spacebar": "Space", + "delete": "Delete" } } diff --git a/packages/excalidraw/shortcut.ts b/packages/excalidraw/shortcut.ts new file mode 100644 index 0000000000..c3cb1f8bb2 --- /dev/null +++ b/packages/excalidraw/shortcut.ts @@ -0,0 +1,19 @@ +import { isDarwin } from "@excalidraw/common"; + +import { t } from "./i18n"; + +export const getShortcutKey = (shortcut: string): string => + shortcut + .replace( + /\b(Opt(?:ion)?|Alt)\b/i, + isDarwin ? t("keys.option") : t("keys.alt"), + ) + .replace(/\bShift\b/i, t("keys.shift")) + .replace(/\b(Enter|Return)\b/i, t("keys.enter")) + .replace( + /\b(Ctrl|Cmd|Command|CtrlOrCmd)\b/gi, + isDarwin ? t("keys.cmd") : t("keys.ctrl"), + ) + .replace(/\b(Esc(?:ape)?)\b/i, t("keys.escape")) + .replace(/\b(Space(?:bar)?)\b/i, t("keys.spacebar")) + .replace(/\b(Del(?:ete)?)\b/i, t("keys.delete"));