fix(docs): stop LoggedInView from hijacking keypresses in document view

LoggedInView has a global keydown handler that redirects any printable
character typed outside an input/contenteditable to the send-message
composer. When the document editor's outer wrapper div (tabIndex=-1) has
focus (i.e. before the first click reaches the contentEditable), this
handler fires and dispatches FocusSendMessageComposer, visibly stealing
all keystrokes.

Fix: skip the redirect when the active element is inside .mx_DocumentView.
Same guard added to the paste handler.

Also simplify handleContentClick to always call ref.current.focus() on
any click inside the content area, not just clicks that miss the editor
div — this ensures focus is reliably placed in the contentEditable even
when the click lands on a wrapper div.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Langley 2026-03-04 13:23:41 +00:00
parent a3875fc854
commit 706a151e19
2 changed files with 13 additions and 12 deletions

View File

@ -451,6 +451,9 @@ class LoggedInView extends React.Component<IProps, IState> {
const inputableElement = getInputableElement(element);
if (inputableElement === document.activeElement) return; // nothing to do
// Don't redirect paste when the document view is active.
if (document.activeElement?.closest(".mx_DocumentView")) return;
if (inputableElement?.focus) {
inputableElement.focus();
} else {
@ -670,7 +673,10 @@ class LoggedInView extends React.Component<IProps, IState> {
// If the user is entering a printable character outside of an input field
// redirect it to the composer for them.
if (!isClickShortcut && isPrintable && !getInputableElement(ev.target as HTMLElement)) {
// Exception: if the active element is inside the document view, the editor
// should handle typing directly without being redirected.
const inDocumentView = !!document.activeElement?.closest(".mx_DocumentView");
if (!isClickShortcut && isPrintable && !getInputableElement(ev.target as HTMLElement) && !inDocumentView) {
const inThread = !!document.activeElement?.closest(".mx_ThreadView");
// synchronous dispatch so we focus before key generates input
dis.dispatch(

View File

@ -210,17 +210,12 @@ export const DocumentView = memo(function DocumentView({ room }: DocumentViewPro
scheduleDeltaSend();
}, [scheduleDeltaSend]);
// Forward clicks anywhere in the content area to the contentEditable so
// the user can click anywhere in the document space to start typing.
const handleContentClick = useCallback(
(ev: React.MouseEvent<HTMLDivElement>) => {
// Only forward if the click didn't already land on the editor itself.
if (ev.target !== ref.current && ref.current) {
ref.current.focus();
}
},
[ref],
);
// Forward clicks anywhere in the content area to the contentEditable.
// The click may land on the wrapper divs rather than the contentEditable
// itself, so always ensure the editor has focus after any click in this area.
const handleContentClick = useCallback(() => {
ref.current?.focus();
}, [ref]);
if (!isLoaded) {
return <div className="mx_DocumentView mx_DocumentView_loading" />;