diff --git a/src/features/vcs/__tests__/diffDialog.test.tsx b/src/features/vcs/__tests__/diffDialog.test.tsx
index 8b8b65eaa..3cc108aee 100644
--- a/src/features/vcs/__tests__/diffDialog.test.tsx
+++ b/src/features/vcs/__tests__/diffDialog.test.tsx
@@ -23,7 +23,7 @@ import { ChangesTree, nodeMenu } from "../CommitView";
import type { ChangeNode } from "../changes";
import { VcsDialogHost } from "../dialogs";
import { useVcs } from "../store";
-import { openDiffDialog } from "../diffDialog";
+import { loadDiffDialogSize, openDiffDialog } from "../diffDialog";
const modified = (path: string) => ({
path,
@@ -116,6 +116,28 @@ describe("Commit dialog diff window (#1863)", () => {
await screen.findByRole("dialog", { name: "b.ts (HEAD ↔ Your version)" });
});
+ it("remembers the resized size for the next opening (#2328)", async () => {
+ render();
+ let close: () => void = () => {};
+ act(() => {
+ close = openDiffDialog({ path: "/p/a.ts", left: { type: "rev", rev: "HEAD" }, right: { type: "worktree" } });
+ });
+ await diffDialogs();
+ // Drag the corner grip; jsdom reports a zero rect so the dialog clamps to its
+ // minimums — what matters is that the resize end is persisted.
+ fireEvent.pointerDown(screen.getByRole("separator", { name: "Resize" }), { button: 0, clientX: 0, clientY: 0 });
+ fireEvent.pointerMove(window, { buttons: 1, clientX: 100, clientY: 80 });
+ fireEvent.pointerUp(window);
+ expect(loadDiffDialogSize()).toEqual({ w: 480, h: 280 });
+ act(() => close());
+ // A fresh open starts at the remembered size rather than the default.
+ act(() => {
+ close = openDiffDialog({ path: "/p/a.ts", left: { type: "rev", rev: "HEAD" }, right: { type: "worktree" } });
+ });
+ const reopened = await diffDialogs();
+ expect([reopened.style.width, reopened.style.height]).toEqual(["480px", "280px"]);
+ });
+
it("closes on Jump to Source so the editor it opens is not hidden behind it", async () => {
render(
<>
diff --git a/src/features/vcs/diffDialog.tsx b/src/features/vcs/diffDialog.tsx
index e7aeed4a4..d7944d28b 100644
--- a/src/features/vcs/diffDialog.tsx
+++ b/src/features/vcs/diffDialog.tsx
@@ -15,6 +15,26 @@ import { diffInput, diffTitle, type DiffData } from "./diffInput";
import { openDialog } from "./dialogs";
import { GitMergeDiff } from "./GitMergeDiff";
+const SIZE_KEY = "openidea.vcs.diffDialogSize";
+
+/** The diff window's last size (#2328); null on first run, clamped to the window by Dialog. */
+export function loadDiffDialogSize(): { w: number; h: number } | null {
+ try {
+ const v = JSON.parse(localStorage.getItem(SIZE_KEY) ?? "null") as { w?: unknown; h?: unknown } | null;
+ return typeof v?.w === "number" && typeof v.h === "number" ? { w: v.w, h: v.h } : null;
+ } catch {
+ return null;
+ }
+}
+
+export function saveDiffDialogSize(size: { w: number; h: number }) {
+ try {
+ localStorage.setItem(SIZE_KEY, JSON.stringify(size));
+ } catch {
+ // Private mode etc. — the size just isn't remembered.
+ }
+}
+
/** The open diff dialog's close function (one diff window at a time, as in IDEA). */
let shown: (() => void) | null = null;
@@ -52,6 +72,14 @@ export function openMergeDiffDialog(input: EditorInput): () => void {
function FrontWindow({ title, onClose, children }: { title: string; onClose: () => void; children: ReactNode }) {
const body = useRef(null);
+ // The size the user last resized the diff window to (#2328), else the usual default.
+ const [initialSize] = useState(
+ () =>
+ loadDiffDialogSize() ?? {
+ w: Math.min(1100, globalThis.innerWidth - 64),
+ h: Math.min(720, globalThis.innerHeight - 64),
+ },
+ );
useEffect(
() =>
registerKeyClaim((e, stroke) => {
@@ -74,7 +102,8 @@ function FrontWindow({ title, onClose, children }: { title: string; onClose: ()
resizable="both"
minWidth={480}
minHeight={280}
- initialSize={{ w: Math.min(1100, globalThis.innerWidth - 64), h: Math.min(720, globalThis.innerHeight - 64) }}
+ initialSize={initialSize}
+ onResized={saveDiffDialogSize}
buttons={[{ label: "Close", role: "cancel" }]}
>