Skip to content

fix(ui): comment editor textarea not focused on open in WebKit hosts#1031

Open
BrandonNoad wants to merge 1 commit into
backnotprop:mainfrom
BrandonNoad:fix/comment-popover-autofocus
Open

fix(ui): comment editor textarea not focused on open in WebKit hosts#1031
BrandonNoad wants to merge 1 commit into
backnotprop:mainfrom
BrandonNoad:fix/comment-popover-autofocus

Conversation

@BrandonNoad

Copy link
Copy Markdown

Problem

In Comment mode, selecting text (or double-clicking a word) opens the comment editor, but the textarea isn't focused — you have to tab several times or reach for the mouse before you can type. This happens consistently in WKWebView hosts (observed with the glimpseui integration on macOS); Chromium-based browsers are unaffected.

Root cause

CommentPopover focused the textarea from a mount effect keyed on [mode] that scheduled a setTimeout(0):

  • In popover mode, the component renders null until position is computed by a later effect, so the textarea only mounts in the next commit.
  • Whether the 0ms timer fires before or after that commit is engine-dependent task ordering. Chromium happens to commit the re-render first, so textareaRef.current is set when the timer fires. WebKit fires the timer first — the ref is still null, the effect never re-runs (mode doesn't change), and the editor opens unfocused.

Traced by instrumenting the page inside a real glimpse (WKWebView) window:

SCHED focus-timer d=0
FIRE focus-timer
DONE focus-timer active=BODY   ← callback ran, ref was null, focus() never called

Manually calling focus() on the same textarea afterwards worked fine, confirming the engine wasn't refusing focus — the app just never asked.

Fix

Focus from a ref callback on the textarea instead of a mount effect. The callback runs exactly when the element mounts, so there's no commit/timer race in either engine:

  • Deferred (setTimeout(0)) focus and caret-to-end behavior are preserved.
  • Popover ⇄ dialog switches still refocus, because the textarea remounts (this is what the old [mode] dep provided).
  • isConnected guards the case where the popover unmounts before the deferred focus runs (also makes the StrictMode double-attach harmless).

Verification

  • WKWebView (glimpse): instrumented driver over glimpse's JSON-lines protocol now logs focus(): TEXTAREA active=true and document.activeElement is the textarea as soon as the popover opens.
  • Chromium: double-click-to-comment, drag-select-to-comment, and Expand (popover → dialog) all focus the textarea and accept typing immediately.
  • tsc --noEmit on packages/ui/tsconfig.json and tsconfig.strict-consumer.json — clean.
  • bun test packages/ui — 352 pass, 0 fail.

The CommentPopover focus effect scheduled a setTimeout(0) keyed on [mode],
but in popover mode the textarea only renders after `position` is measured
by a later effect. Whether the 0ms timer fires before or after that commit
is engine-dependent task ordering: Chromium commits the re-render first, so
focus lands; WebKit fires the timer first, so textareaRef.current is still
null and the comment editor opens unfocused (reproduced in a WKWebView host
— the glimpseui integration — where users had to tab or click into the
field for every annotation).

Focus from a ref callback on the textarea instead: it runs exactly when the
element mounts, so there is nothing to race. Mode switches (popover/dialog)
still refocus because the textarea remounts. The deferred focus and
caret-to-end behavior are preserved; isConnected guards the popover closing
before the timer fires.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant