fix(vcs): run the merge editor's buttons on a mouse click on macOS (#3160)

WebKit doesn't focus a clicked button, so focus moved to the editor area outside the merge editor and cleared the focused merge editor; Apply, Cancel, Accept Yours/Theirs and the toolbar actions then did nothing on a mouse click. The merge editor root is now focusable (tabindex -1) so a click inside it keeps focus there.
This commit is contained in:
l0ng-ai
2026-11-13 20:20:50 +08:00
committed by GitHub
parent 04d5d9bd58
commit 2ec2e75d40
3 changed files with 23 additions and 0 deletions
+17
View File
@@ -122,6 +122,23 @@ test("Apply writes and stages unresolved text only after confirmation", async ({
expect((await writes(page))[0]!.args[2]).toEqual({ changes: [{ from: 0, to: 13, insert: BASE }] });
});
test("footer buttons work when a click doesn't focus them, as in WebKit", async ({ page }) => {
await mountMerge(page);
await result(page).locator(".cm-content").click();
// WebKit focuses the clicked button's nearest focusable ancestor, not the button.
await page
.locator("#merge-host")
.getByRole("button", { name: "Apply", exact: true })
.evaluate((button) => {
let target = button.parentElement;
while (target && !target.hasAttribute("tabindex")) target = target.parentElement;
if (target) target.focus();
else (document.activeElement as HTMLElement | null)?.blur();
(button as HTMLButtonElement).click();
});
await expect(page.getByRole("dialog")).toContainText("Save and mark the file as resolved anyway?");
});
test("binary and unreadable versions show an error with no Apply controls", async ({ page }) => {
await mountMerge(page, ["\0base", "\0ours", "\0theirs"]);
await expect(page.locator("#merge-host")).toContainText("Binary files cannot be resolved in the merge editor.");
+2
View File
@@ -5,6 +5,7 @@
display: flex;
flex-direction: column;
background: var(--bg-app);
outline: none;
}
.toolbar {
flex: none;
@@ -84,6 +85,7 @@
}
.message {
flex: 1;
outline: none;
display: flex;
flex-direction: column;
align-items: center;
+4
View File
@@ -541,6 +541,10 @@ export function MergeEditor({ input }: { input: EditorInput }) {
}, [actionHost]);
const focusProps = {
// WebKit doesn't focus a clicked button: focus then moves to the nearest focusable
// ancestor, which must be this editor for the footer and toolbar buttons to keep it the
// focused merge editor their actions run in.
tabIndex: -1,
onFocusCapture: (e: React.FocusEvent<HTMLDivElement>) => {
const target = e.target as Node;
if (oursRef.current?.contains(target)) paneRef.current = "left";