fix(dashboard): keep keyboard up on send tap; hide status bar with keyboard
Two mobile chat regressions addressed together: 1) ChatView send button: preventDefault now fires on pointerdown for touch pointers, before iOS blurs the textarea. The previous onMouseDown.preventDefault was too late on iOS (mousedown is synthesized after touchend, by which point the keyboard has already started dismissing). Click still runs the action so quick taps remain reliable. 2) ExecutorStatusBar: hidden on mobile while keyboard is open, mirroring MobileNavBar. The bar is position:fixed against the layout viewport, which iOS leaves anchored below the keyboard; during a swipe/pan it would slide over the messages list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
17
.changeset/chat-mobile-send-and-status-bar.md
Normal file
17
.changeset/chat-mobile-send-and-status-bar.md
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Two mobile chat fixes:
|
||||
|
||||
1. Tapping the ChatView send button no longer dismisses the soft
|
||||
keyboard. preventDefault now fires on `pointerdown` for touch
|
||||
pointers (before iOS blurs the textarea — the synthesized mousedown
|
||||
it previously relied on fires too late). Click still runs the send
|
||||
action so quick taps remain reliable.
|
||||
|
||||
2. The bottom executor status bar is now hidden on mobile while the
|
||||
keyboard is open, mirroring `MobileNavBar`. The bar is
|
||||
`position: fixed` against the layout viewport, which iOS leaves
|
||||
anchored below the keyboard — during a swipe/pan it would slide
|
||||
over the message list.
|
||||
@@ -1336,6 +1336,7 @@ function AppInner() {
|
||||
lastFetchTimeMs={lastFetchTimeMs}
|
||||
currentProjectPath={currentProject.path}
|
||||
onOpenProjectDirectory={handleOpenProjectDirectory}
|
||||
keyboardOpen={mobileKeyboardOpen}
|
||||
/>
|
||||
)}
|
||||
<MobileNavBar
|
||||
|
||||
@@ -1994,14 +1994,19 @@ export function ChatView({ projectId, addToast }: ChatViewProps) {
|
||||
<button
|
||||
type="button"
|
||||
className="chat-input-send"
|
||||
// Mobile send pattern: previous code intercepted pointerdown
|
||||
// and touchstart to call handleSend directly, which silently
|
||||
// dropped quick taps on iOS (only long press worked). The
|
||||
// canonical iOS pattern is preventDefault on mousedown to
|
||||
// stop focus from leaving the textarea (keyboard stays up,
|
||||
// viewport doesn't reflow), then run the action on click.
|
||||
// This works for quick taps because click fires reliably
|
||||
// from the synthesized touch sequence.
|
||||
// Keep keyboard up when sending. preventDefault fires on
|
||||
// pointerdown for touch pointers (BEFORE iOS blurs the
|
||||
// textarea — the synthesized mousedown is too late on
|
||||
// iOS), and on mousedown for desktop. Crucially we do NOT
|
||||
// call preventDefault on touchstart and we do NOT run the
|
||||
// action here — both of those broke quick taps. Click
|
||||
// still fires from the iOS touch sequence and runs the
|
||||
// action reliably.
|
||||
onPointerDown={(event) => {
|
||||
if (event.pointerType && event.pointerType !== "mouse") {
|
||||
event.preventDefault();
|
||||
}
|
||||
}}
|
||||
onMouseDown={(event) => {
|
||||
event.preventDefault();
|
||||
}}
|
||||
|
||||
@@ -25,6 +25,10 @@ interface ExecutorStatusBarProps {
|
||||
currentProjectPath?: string;
|
||||
/** Opens the workspace-aware file browser to the project workspace. */
|
||||
onOpenProjectDirectory?: () => void;
|
||||
/** When true on mobile, hide the bar so it doesn't slide over messages
|
||||
* during visualViewport pans (position:fixed is anchored to layout
|
||||
* viewport, which iOS leaves below the keyboard). */
|
||||
keyboardOpen?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +78,8 @@ function getStateDisplay(state: ExecutorState): { label: string; color: string;
|
||||
* - Executor state badge (idle/running/paused)
|
||||
* - Last activity timestamp
|
||||
*/
|
||||
export function ExecutorStatusBar({ tasks, projectId, taskStuckTimeoutMs, backgroundSessions, backgroundGenerating, backgroundNeedsInput, onOpenBackgroundSession, onDismissBackgroundSession, lastFetchTimeMs, currentProjectPath, onOpenProjectDirectory }: ExecutorStatusBarProps) {
|
||||
export function ExecutorStatusBar({ tasks, projectId, taskStuckTimeoutMs, backgroundSessions, backgroundGenerating, backgroundNeedsInput, onOpenBackgroundSession, onDismissBackgroundSession, lastFetchTimeMs, currentProjectPath, onOpenProjectDirectory, keyboardOpen }: ExecutorStatusBarProps) {
|
||||
if (keyboardOpen) return null;
|
||||
const { stats, loading, error } = useExecutorStats(tasks, projectId, taskStuckTimeoutMs, lastFetchTimeMs);
|
||||
const [isProjectPathVisible, setIsProjectPathVisible] = useState(false);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user