Between the two things this already did. Selecting a sentence is
precise and costs a drag; translating the page is one press and
rewrites everything. A reader working through an article in their
second language usually wants neither — they want *this* paragraph, the
one that stopped them, and they want it without losing their place.
Alt by default, with Ctrl, Shift and off in the popup. A held key
rather than hover alone: a paragraph that translates itself because the
mouse passed over it on the way to a link is a page that fights back,
and a modifier is the one input that cannot be produced by accident
while reading. The modifier is read off the mousemove event rather than
tracked separately — the browser already carries it, and two sources of
truth about whether Alt is down is one more than is needed.
The block under the pointer is outlined while the key is held, so the
gesture answers "what will this do" before it does it. An outline
rather than a border or a background: outlines take no part in layout,
so highlighting a paragraph cannot reflow the page under the cursor
that is pointing at it. `elementFromPoint` rather than the event
target, because the target is whatever leaf the pointer is over — a
<strong>, a link — and the unit anyone means is the paragraph around
it.
`translateBlock` shares `attach` and the appearance with a whole-page
run on purpose: a paragraph translated this way has to be
indistinguishable from the same paragraph translated by a run, or undo
and re-translate would have to know which was which. It fails silently,
because a page-wide run reports failures in a badge the reader asked
for and one paragraph that did not come back is not worth marking the
page over — the gesture can simply be repeated.
`pageDeps` moved above the frame guard. The hover mount is on every
frame, like selection, and a block inside an iframe is as worth
translating as one outside it — but `pageDeps` was declared after the
top-frame early return, so hovering inside an iframe would have hit the
temporal dead zone. TypeScript cannot see that; it is a runtime shape.
Verified in a real browser, since jsdom has no hit testing and
`elementFromPoint` is a stub there: Alt held, pointer moved over the
second of four paragraphs, exactly that one translated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>