diff --git a/components/email/proofreading/issue-popup.tsx b/components/email/proofreading/issue-popup.tsx index 54be4ca1e..875122bb1 100644 --- a/components/email/proofreading/issue-popup.tsx +++ b/components/email/proofreading/issue-popup.tsx @@ -116,9 +116,10 @@ export function IssuePopup({ state, issue, anchor, onApply, onDismiss, onClose } style={{ width: WIDTH, maxWidth: "calc(100vw - 16px)", top: pos?.top ?? -9999, left: pos?.left ?? -9999 }} onKeyDown={onKeyDown} onBlur={(e) => { - // Tabbing out of it closes it; a click inside doesn't. - const next = e.relatedTarget as Node | null; - if (next && !ref.current?.contains(next)) onClose(false); + // Tabbing out of it closes it; a click inside, or going back to the + // text (where Escape closes it), doesn't. + const next = e.relatedTarget as Element | null; + if (next && !ref.current?.contains(next) && !next.closest?.(".tiptap")) onClose(false); }} > diff --git a/components/email/proofreading/use-proofreading.ts b/components/email/proofreading/use-proofreading.ts index 1860685f2..d9b0b0a6d 100644 --- a/components/email/proofreading/use-proofreading.ts +++ b/components/email/proofreading/use-proofreading.ts @@ -6,6 +6,7 @@ import { useCallback, useEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore, type RefObject } from "react"; import type { Editor } from "@tiptap/core"; +import { TextSelection } from "@tiptap/pm/state"; import { ComposerProofreader, type DismissAction, type PlacedIssue, type ProofreaderStatus } from "@/lib/proofreading/composer-proofreader"; import { checkerAccess, type PolicyView } from "@/lib/proofreading/gate"; import { draftProtection, OPTIONS_SURFACE } from "@/lib/proofreading/protection"; @@ -271,7 +272,10 @@ export function useProofreading({ composerId, sessionKey, editor, textareaRef, p if (!ed || plainRef.current) return; const range = drawnIssues(ed.state).find((r) => r.key === issue.key); if (!range) return; - ed.chain().focus().setTextSelection({ from: range.from, to: range.to }).scrollIntoView().run(); + // Focus now, not later as TipTap's focus command does: a late focus + // would pull the keyboard out of the popup again. + ed.view.focus(); + ed.view.dispatch(ed.state.tr.setSelection(TextSelection.create(ed.state.doc, range.from, range.to)).scrollIntoView()); openPopup(issue.key, issueRect(ed, issue.key), "keyboard"); }, [openPopup]);