AI Assistant: stop the Form block prompt from covering the form - #51050
AI Assistant: stop the Form block prompt from covering the form#51050taipeicoder wants to merge 18 commits into
Conversation
The AI input is sticky to the bottom of the viewport. On the Form block it renders in normal flow after the block with no reserved space, so on a narrow screen where the form is taller than the viewport the input hovers over the tail of the form — hiding the last fields and the submit button the moment the block is inserted. Skip the auto-open below the editor's mobile breakpoint. The prompt is still reachable from the block toolbar. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖 Follow this PR Review Process:
If you have questions about anything, reach out in #jetpack-developers for guidance! Jetpack plugin: No scheduled milestone found for this plugin. If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack. |
…or it The sticky positioning assumes the block pads its bottom to make room for the input. The Form block opted out of that adjustment but kept the sticky input, so once the form is taller than the viewport the prompt pins itself over the form's last fields and submit button — on insertion, from the toolbar, and while generating. Tie the sticky positioning to the same flag that reserves the space. Blocks that adjust their position are unchanged; the Form block gets the input in normal flow, below the form. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Code Coverage SummaryCoverage changed in 2 files.
Full summary · PHP report · JS report If appropriate, add one of these labels to override the failing coverage check:
Covered by non-unit tests
|
A prompt in normal flow scrolls away with the block, so the auto-scroll that runs on every generated chunk was pushing it — and its Stop button — below the viewport. Point that scroll at the prompt itself when it is not sticky; it sits directly below the block, so the arriving content stays visible above it. Sticky prompts keep targeting the block, whose reserved padding already holds the prompt clear. Also drop the mobile carve-out for the automatic opening. It relied on the block toolbar as the fallback entry point, but that toolbar is replaced by the AI sidebar button on surfaces where that feature is on, which would have left the form prompt unreachable there. The positioning fix already keeps the prompt off the form on its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It stopped meaning "the last paragraph" when the block became a possible target, and it can now be the prompt wrapper too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two consequences of taking the Form prompt out of sticky positioning, both of which a sticky prompt was absorbing on its own. The prompt opens with the block, and showing it focuses it, which scrolls it into view. Below a form taller than the viewport that drags the editor past the form that was just inserted. Skip the focus for an appearance the user did not ask for; every later opening still focuses. The scroll that follows each generated chunk also ran before the dispatched content had rendered, so it measured the prompt's old position and left it below the viewport once the taller content committed. Wait a frame. Sticky prompts re-pin themselves after a layout change and are left alone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Suppressing focus for every automatically opened prompt was broader than the problem: where the prompt is already on screen, as it is for a short form or a desktop window, focusing costs nothing and losing it means an extra click before typing. Gate on what actually causes the trouble — whether focusing would scroll. An automatic opening focuses only when the prompt is already in view, and so does the focus on completion, which otherwise pulls the editor back to a prompt the user had deliberately scrolled away from. Openings the user asked for, and the focus after Stop, still focus unconditionally. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A sticky prompt reaches completion where it has always been, so gating its focus on visibility would change behaviour this branch has no business changing. Only the prompt that now scrolls with its block needs the check. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Completion adds a missing submit button and swaps the prompt over to its completed buttons, both after auto-scroll has stopped. A sticky prompt absorbed that; one in normal flow can be left past the bottom of the viewport, with the visibility check declining to recover it. Have disableAutoScroll report whether the user had taken the scrolling over, which it already tracks, and use that rather than visibility as the test at completion: focus unconditionally when the prompt was still being followed, and leave a user who scrolled away where they are. Also name the prompt's positioning prop after the class it sets, so the two no longer disagree. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The scroll flag answers the question on its own, so completion no longer needs the visibility check: focus unless the user scrolled off, and skip it entirely if they did rather than focusing an input they cannot see. Measure the input, not the whole control, when deciding whether focusing would move the editor — the input is what focus scrolls to, so a clipped disclaimer below it was wrongly suppressing focus for a prompt already in view. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Suppressing the focus that follows the prompt opening was a mistake. That focus scrolls the minimum needed, which lands the prompt at the bottom of the viewport with the end of the form above it — nothing covered, which is the outcome asked for. Withholding it bought nothing and cost the ability to insert a form and start typing. Removes the auto-open tracking and the visibility test with it. Completion still declines to pull back a user who scrolled away mid-request. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The error panel grows the control, which on a tall form can leave a prompt in normal flow past the bottom of the viewport with Try again out of reach. Scroll it back rather than focus it, since a quota error disables the input and focus would do nothing, and leave a user who scrolled off where they are. Drop the positioning prop while here. It carried one class name to a single call site, which can set that class through the className it already passes, keeping the decision beside the other branches on the same flag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The scroll-takeover flag is not set until the first automatic snap has run, so a request that fails before producing any content reports that the user never scrolled, even when they did — and the recovery scroll then pulled them back. Measure the prompt against the viewport instead, just before the error panel renders. That answers the question the flag was standing in for, and does it for every failure rather than only those that got far enough to scroll once. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Aligning to the bottom moved a prompt that was already fully visible, so an error that did not push anything out of view still jumped the editor. The nearest edge scrolls only as far as it has to, and does nothing when the panel fits — and when the control is taller than the viewport it settles on the top, which is the end the error and its Try again sit at. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Skipping the focus when the user had scrolled asked more of the flag than it can answer. It records any scroll at all, so nudging the page while the prompt stayed in view was enough to lose the focus that lets you type a follow-up, and scrolls during the hook's own listener gap go unrecorded either way. Completion goes back to focusing unconditionally, as it always has. That removes the regression, and with it the only reason for the shared hook to report anything, so it returns to what it was. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Completion focusing unconditionally pulls the editor back to a prompt in normal flow that the user had scrolled away from. Guarding it on the scroll-takeover flag was the wrong instrument — that flag trips on any scroll at all, including one that left the prompt in plain view — so ask where the prompt was instead, measured before the closing update moves it. The error path already asked that question inline; both now share it, which also stops a prompt with one pixel showing counting as visible there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… prompt Geometry alone cannot tell a prompt the user left from one the stream outran: a throttled or dropped snap leaves it off screen with nobody having scrolled anywhere. Ask the scroller too — if it was still following when it stopped, the prompt is worth bringing back. Either signal restores it, which also keeps the case each one misses covered: a harmless nudge trips the follow flag but leaves the prompt in view, and a failure before the first snap has no follow state to read. Skip the measurement entirely for sticky blocks, whose paths are unchanged and which should not pay for a layout read they never consult. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The hand-rolled visibility check was reinventing behaviour that focus() and scrollIntoView already have. Focus scrolls an element into view only when it is not already there, and only as far as needed, so completion needs no test and no guard — it goes back to the unconditional focus it always did. The nearest block alignment does the same for the error path. The editor takes the same line: block selection observes once and scrolls only when the block is out of view, without asking whether the user meant to be elsewhere. Inferring that intent was a policy of this branch's own making, and every attempt at it had a hole. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Focus does both at once, which is why deciding whether to move the editor at a terminal state kept turning into a question about focus. Ask it not to scroll, and the two become separate decisions: the cursor always returns to the input, and the editor moves only when the scrolling was still following the content — the same takeover the per-chunk scrolling already honours, now read from the hook rather than inferred. Also stop dropping the newest explicit target while a scroll is in flight. Chunks arrive faster than that window closes, and a dropped one used to leave a prompt in normal flow wherever the content it was following had pushed it, with its Stop button out of reach until the next chunk. Keep the latest and catch up when the window closes, before the scroll listener is restored so the catch-up is not mistaken for the user's own. Targetless callers are untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Proposed changes
Related product discussion/links
Does this pull request change what data or activity we track or use?
No.
Testing instructions
Requires a site with Jetpack AI available.
On a phone-sized viewport (375px wide — a real device, or your browser's device emulation):
On a desktop viewport: