diff --git a/components/plugins/__tests__/plugin-dialog-focus.test.tsx b/components/plugins/__tests__/plugin-dialog-focus.test.tsx
index 53385b48d..2d83d761d 100644
--- a/components/plugins/__tests__/plugin-dialog-focus.test.tsx
+++ b/components/plugins/__tests__/plugin-dialog-focus.test.tsx
@@ -39,6 +39,38 @@ describe('plugin dialog focus', () => {
await waitFor(() => expect(opener).toHaveFocus());
});
+ it('leaves Enter to the focused button, so Cancel on a destructive confirm never answers yes', async () => {
+ render();
+ let answer: Promise;
+ act(() => { answer = awaitDialog({ pluginId: 'p', kind: 'confirm', title: 'Delete it?', message: 'Sure?', danger: true }); });
+ const cancel = screen.getByRole('button', { name: 'cancel' });
+ expect(cancel).toHaveFocus();
+
+ // Not prevented: the browser turns Enter on a button into a click on it.
+ let notPrevented = false;
+ await act(async () => { notPrevented = fireEvent.keyDown(cancel, { key: 'Enter' }); });
+ expect(notPrevented).toBe(true);
+ expect(head()).not.toBeNull();
+
+ await act(async () => { fireEvent.click(cancel); });
+ await expect(answer!).resolves.toBe(false);
+ });
+
+ it('does not take Enter from the message of a destructive confirm as a yes', async () => {
+ render();
+ act(() => { void awaitDialog({ pluginId: 'p', kind: 'confirm', title: 'Delete it?', message: 'Sure?', danger: true }); });
+ await act(async () => { fireEvent.keyDown(screen.getByText('Sure?'), { key: 'Enter' }); });
+ expect(head()).not.toBeNull();
+ });
+
+ it('still accepts a harmless confirm on Enter from its message', async () => {
+ render();
+ let answer: Promise;
+ act(() => { answer = awaitDialog({ pluginId: 'p', kind: 'confirm', title: 'Send?', message: 'Sure?' }); });
+ await act(async () => { fireEvent.keyDown(screen.getByText('Sure?'), { key: 'Enter' }); });
+ await expect(answer!).resolves.toBe(true);
+ });
+
it('starts in the first field of a prompt and marks required fields', async () => {
render();
let answer: Promise | null>;
diff --git a/components/plugins/plugin-dialog-host.tsx b/components/plugins/plugin-dialog-host.tsx
index a4fa12b38..24ed5d314 100644
--- a/components/plugins/plugin-dialog-host.tsx
+++ b/components/plugins/plugin-dialog-host.tsx
@@ -169,6 +169,9 @@ function PluginDialogView({ current, layer }: { current: DialogRequest; layer: '
// validation and works from within an input); confirm/alert accept.
// Custom dialogs resolve only via their own onResult callback or the
// host's X/Escape - an Enter press has no defined meaning for them.
+ // On a button Enter is that button's: Cancel must not answer yes. And
+ // a destructive confirm only says yes from its own button.
+ if ((e.target as Element | null)?.closest?.('button') || current.danger) return;
e.preventDefault();
resolveThis(true);
}