Skip to content

Fix iOS touch targets and the two-tap send in the composer - #1673

Open
sberan wants to merge 4 commits into
get-bb:mainfrom
sberan:ios-mobile-fixes
Open

Fix iOS touch targets and the two-tap send in the composer#1673
sberan wants to merge 4 commits into
get-bb:mainfrom
sberan:ios-mobile-fixes

Conversation

@sberan

@sberan sberan commented Aug 16, 2026

Copy link
Copy Markdown

Found while using bb daily from an installed iOS PWA on a phone. Three problems in the composer, all touch-only — nothing here changes desktop behaviour.

1. Send needs two taps

handleSubmitPointerDown skipped preventDefault() unless the TipTap editor reported isFocused:

if (!currentEditor || currentEditor.isDestroyed || !currentEditor.isFocused) {
  return;
}
event.preventDefault();

On iOS that check reads false at pointerdown while the keyboard is still up, because WebKit has already begun moving focus by the time the handler runs. That is precisely the case the guard exists to protect against, so it opts out exactly when it is needed: focus leaves the editor, the keyboard dismisses, the composer reflows upward, and the click lands where the button used to be. The first tap appears to do nothing and the message only sends on a second press.

Cancelling the default is safe regardless of focus state — it suppresses the focus transfer for that press only, and the click still fires, which is what owns the commit.

Added a regression test. It fails on main with expected true to be false (the default is not cancelled) and passes with the fix.

2. Touch targets under 44px

  • The composer's submit / stop / voice slot is 32px (size-8). It is the most-tapped control in the app.
  • The inline message editor's cancel is 24px (size-6), and it is the only exit from edit mode.

Both grow on coarse pointers only.

3. Queued-message actions are unreachable on touch

Send-now / Edit / Delete are rendered, but the container is hidden … md:flex and only revealed on group-hover. On a phone neither condition is ever true, so the row falls back to the 28px overflow menu as the only affordance.

They now show outright on coarse pointers. The overflow trigger hides there in the same breath — both are absolutely positioned against the same right edge, so showing both stacks the two sets on top of each other at different offsets.

Notes

  • Uses the existing max-md:pointer-coarse: idiom already used in ProjectList and ThreadRow, so the scoping matches the codebase. Worth noting that a bare pointer-coarse: would also cover tablets at md and above, where 44px is equally right — happy to widen it if you would prefer that.
  • pnpm exec vitest run apps/app/src/components/promptbox → 329 passed, 30 files.
  • pnpm exec tsc --noEmit -p apps/app/tsconfig.json → clean.
  • Verified against a real device rather than only emulation; the two-tap behaviour in particular does not reproduce in headless Chromium, since it depends on WebKit's focus timing.

sberan added 3 commits August 16, 2026 17:24
Found while using bb from an installed iOS PWA on a phone.

**Send needs two taps.** handleSubmitPointerDown skipped preventDefault unless
the TipTap editor reported isFocused. On iOS that reads false at pointerdown
while the keyboard is still up, because WebKit has already begun moving focus by
the time the handler runs — which is exactly the case the guard exists to
protect. The keyboard dismisses, the composer reflows upward, and the click
lands where the button used to be, so the first tap appears to do nothing.
Cancelling the default is safe regardless of focus state: it suppresses the
focus transfer for that press and the click still fires. Covered by a regression
test that fails without the change.

**Touch targets below the 44px minimum.** The composer's submit/stop/voice slot
is 32px (size-8) and the inline editor's cancel — the only exit from edit mode —
is 24px (size-6). Both now grow on coarse pointers only, so desktop density is
unchanged.

**Queued-message actions are unreachable on touch.** Send-now / Edit / Delete
are rendered but gated behind `md:flex` and revealed on hover, so below that
breakpoint a phone only ever gets the 28px overflow menu. They now show outright
on coarse pointers, and the overflow trigger hides there since the two are
anchored to the same edge and would otherwise stack.

Uses the existing max-md:pointer-coarse: idiom throughout.
The expanded background activity list truncates each description to one line and
exposes the full text only through a title attribute. Hover does not exist on
touch, so on a phone there is no way to find out which command is running — the
row reads "Running background command: ..." and expanding it gives another
truncated line.

Wrap to at most two lines on coarse pointers. The collapsed summary row above is
still a one-line summary, so this only affects the list you deliberately expand.
The previous commit only wrapped the expanded list. The line most people
actually read is the collapsed summary — the one that renders as
"Running background command: ..." without expanding anything — and it truncates
too, with the same hover-only title attribute behind it.

Expanding was no help either, so on a phone there was no path to the text at
all. Wrap the summary to two lines on coarse pointers as well.
@sberan

sberan commented Aug 16, 2026

Copy link
Copy Markdown
Author

Added a follow-up: the first background-activity commit only wrapped the expanded list, but the line most people actually read is the collapsed summary — the one rendering as Running background command: ... without expanding anything. It truncates too, behind the same hover-only title, so on a phone there was no path to the text at all. Both now wrap on coarse pointers.

…the width

"Running background command:" is ~27 characters and consumes the entire first
line on a phone, so the description wrapped onto a second line before it had any
room — the card read as a header with the useful text beneath it.

The icon already identifies the task kind, and the row's aria-label still
carries the full "<label>: <description>", so nothing is lost for screen
readers. Hide the visible prefix on coarse pointers only.
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