feat(mobile): chat input keybinds + safe-area + tap targets + overflow safety

- ChatInput: e.nativeEvent.isComposing early-return added (CJK IME
  safety — first Enter of a composition no longer submits). Bare-Enter
  send path gated by !isMobile so mobile inserts a newline; send is
  button-only. Cmd/Ctrl+Enter and Shift+Cmd/Ctrl+Enter retained as
  desktop secondary bindings. Placeholder is now viewport-aware. Outer
  wrapper gets paddingBottom: env(safe-area-inset-bottom) so iOS home
  indicator doesn't overlap.
- MessageBubble: ActionRow buttons (Copy / Regenerate / Fork / Trash)
  bumped to max-md min-h/min-w 44px; opacity-100 on mobile so actions
  don't hide behind a hover-to-reveal pattern. User bubble and
  assistant content wrapper gain break-words + min-w-0 so long
  unbreakable strings (URLs / paths) wrap rather than blowing out
  the column on narrow viewports.
- ChatPane: queued-message dropdown + close X + Stop-generating button
  hit max-md 44px sizing.
- ChatTabBar: per-tab X, +/History/Close-pane action buttons hit
  max-md 44px. Tab close X is force-visible on mobile (no
  hover-to-reveal).
- M8: CodeBlock / Markdown tables / ToolCallCard already wrap
  overflow-x-auto pre-existing — no source change needed there; the
  break-words + min-w-0 additions above are the new defensive layer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-16 05:55:34 +00:00
parent cd897d6893
commit 273eeac68c
4 changed files with 30 additions and 17 deletions

View File

@@ -120,7 +120,7 @@ export function ChatPane({ sessionId, chatId, projectId, sessionChats }: Props)
<DropdownMenuTrigger asChild>
<button
type="button"
className="p-0.5 hover:bg-muted rounded shrink-0"
className="inline-flex items-center justify-center p-0.5 hover:bg-muted rounded shrink-0 max-md:min-h-[44px] max-md:min-w-[44px]"
aria-label="Queued message options"
>
<ChevronDown size={12} />
@@ -138,7 +138,7 @@ export function ChatPane({ sessionId, chatId, projectId, sessionChats }: Props)
<button
type="button"
onClick={() => removeQueued(i)}
className="p-0.5 hover:bg-muted rounded shrink-0"
className="inline-flex items-center justify-center p-0.5 hover:bg-muted rounded shrink-0 max-md:min-h-[44px] max-md:min-w-[44px]"
aria-label="Cancel queued message"
>
<X size={12} />
@@ -156,7 +156,7 @@ export function ChatPane({ sessionId, chatId, projectId, sessionChats }: Props)
<button
type="button"
onClick={() => void handleStop()}
className="flex items-center gap-1.5 text-xs px-3 py-1 rounded-full border hover:bg-muted text-muted-foreground hover:text-foreground"
className="flex items-center gap-1.5 text-xs px-3 py-1 rounded-full border hover:bg-muted text-muted-foreground hover:text-foreground max-md:min-h-[44px] max-md:px-5"
>
<Square size={10} className="fill-current" />
Stop generating