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"));