diff --git a/src/workbench/EditorArea.tsx b/src/workbench/EditorArea.tsx
index 6d55ce12f..2582c5138 100644
--- a/src/workbench/EditorArea.tsx
+++ b/src/workbench/EditorArea.tsx
@@ -43,7 +43,8 @@ export function EmptyEditor() {
{h.label}
)}
- {keys &&
{keys}}
+ {/* Rendered even when unbound: the grid auto-places cells, so a missing one shifts every later row. */}
+ {h.action && {keys}}
);
})}
diff --git a/src/workbench/emptyEditorHints.test.tsx b/src/workbench/emptyEditorHints.test.tsx
new file mode 100644
index 000000000..de8957a7c
--- /dev/null
+++ b/src/workbench/emptyEditorHints.test.tsx
@@ -0,0 +1,53 @@
+import { afterEach, describe, expect, it } from "vitest";
+import { render } from "@testing-library/react";
+import { PRESETS, useKeymap, type Keymap } from "@/platform/keymap";
+import { usePlatform } from "@/platform/platformInfo";
+import { useProject } from "@/platform/project";
+import { EmptyEditor } from "./EditorArea";
+
+const rows = (container: HTMLElement) =>
+ [...container.querySelectorAll("dl > div")].map((row) => ({
+ cells: [...row.children].map((c) => c.tagName),
+ label: row.querySelector("dt")?.textContent,
+ keys: row.querySelector("dd")?.textContent,
+ }));
+
+// The hint grid has two auto-placed columns; a row missing its key cell pulls the next label into it.
+describe("empty editor hints", () => {
+ const { os } = usePlatform.getState();
+ const { preset: initialPreset } = useKeymap.getState();
+ afterEach(() => {
+ useKeymap.setState({ preset: initialPreset, overrides: {} });
+ useProject.setState({ lightEdit: false });
+ usePlatform.setState({ os });
+ });
+
+ const cases: Array<{ name: string; preset: string; overrides?: Keymap }> = [
+ ...Object.keys(PRESETS).map((preset) => ({ name: preset, preset })),
+ { name: "a removed Go to File binding", preset: "intellij-macos", overrides: { "navigate.gotoFile": [] } },
+ {
+ name: "a mouse-only Recent Files binding",
+ preset: "intellij-windows",
+ overrides: { "navigate.recentFiles": ["Button4"] },
+ },
+ ];
+
+ for (const { name, preset, overrides = {} } of cases)
+ for (const lightEdit of [false, true])
+ it(`keeps a label and a key cell on every row with ${name}${lightEdit ? " in LightEdit" : ""}`, () => {
+ useKeymap.setState({ preset, overrides });
+ useProject.setState({ lightEdit });
+ const all = rows(render().container);
+ const last = all.pop();
+ for (const row of all) expect(row.cells, row.label ?? "").toEqual(["DT", "DD"]);
+ expect(last).toMatchObject({ cells: ["DT"], label: "Drop files here to open them" });
+ });
+
+ it("leaves Recent Files' key cell empty and Navigation Bar's shortcut beside it in VS Code (macOS)", () => {
+ usePlatform.setState({ os: "macos" });
+ useKeymap.setState({ preset: "vscode-macos", overrides: {} });
+ const byLabel = Object.fromEntries(rows(render().container).map((r) => [r.label, r.keys]));
+ expect(byLabel["Recent Files"]).toBe("");
+ expect(byLabel["Navigation Bar"]).toBe("⇧⌘.");
+ });
+});