fix(mobile): restore original MobileAgentButton using onPointerUp instead of onClick (#524)

The original design used onPointerUp to cycle agents, which doesn't trigger
focus transfer and keeps the soft keyboard open. Recent changes switched to
onClick which causes button to gain focus and closes the keyboard.

- Remove onClick handler entirely
- Restore handlePointerDown, handlePointerUp, handlePointerLeave handlers
- Use onPointerUp to trigger agent cycling
- Add onContextMenu to prevent context menu on long-press
- Add extensive comments explaining why pointer events must be used

This reverts to the behavior from commit 337c458f where clicking the agent
button doesn't close the soft keyboard.

Co-authored-by: Jovines <jovines@qq.com>
This commit is contained in:
Jovines
2026-02-26 20:12:29 +02:00
committed by GitHub
co-authored by Jovines
parent 32619e9f90
commit 0e9b231fed
@@ -13,58 +13,63 @@ interface MobileAgentButtonProps {
const LONG_PRESS_MS = 500;
export const MobileAgentButton: React.FC<MobileAgentButtonProps> = ({ onOpenAgentPanel, onCycleAgent, className }) => {
// NOTE: Use pointer events instead of onClick to keep soft keyboard open on mobile
export const MobileAgentButton: React.FC<MobileAgentButtonProps> = ({ onCycleAgent, onOpenAgentPanel, className }) => {
const { currentAgentName, getVisibleAgents } = useConfigStore();
const currentSessionId = useSessionStore((state) => state.currentSessionId);
const sessionAgentName = useSessionStore((state) =>
currentSessionId ? state.getSessionAgentSelection(currentSessionId) : null
);
const longPressTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
const longPressTriggeredRef = React.useRef(false);
const agents = getVisibleAgents();
const uiAgentName = currentSessionId ? (sessionAgentName || currentAgentName) : currentAgentName;
const agentLabel = getAgentDisplayName(agents, uiAgentName);
const agentColor = getAgentColor(uiAgentName);
const clearLongPressTimer = React.useCallback(() => {
const longPressTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
const isLongPressRef = React.useRef(false);
const handlePointerDown = () => {
isLongPressRef.current = false;
longPressTimerRef.current = setTimeout(() => {
isLongPressRef.current = true;
onOpenAgentPanel();
}, LONG_PRESS_MS);
};
// Use onPointerUp (not onClick) to prevent focus transfer that closes mobile keyboard
const handlePointerUp = () => {
if (longPressTimerRef.current) {
clearTimeout(longPressTimerRef.current);
longPressTimerRef.current = null;
}
}, []);
if (!isLongPressRef.current) {
onCycleAgent();
}
};
const startLongPressTimer = React.useCallback(() => {
clearLongPressTimer();
longPressTriggeredRef.current = false;
longPressTimerRef.current = setTimeout(() => {
longPressTriggeredRef.current = true;
onOpenAgentPanel();
}, LONG_PRESS_MS);
}, [clearLongPressTimer, onOpenAgentPanel]);
const handlePointerLeave = () => {
if (longPressTimerRef.current) {
clearTimeout(longPressTimerRef.current);
longPressTimerRef.current = null;
}
};
React.useEffect(() => {
return () => clearLongPressTimer();
}, [clearLongPressTimer]);
return () => {
if (longPressTimerRef.current) {
clearTimeout(longPressTimerRef.current);
}
};
}, []);
return (
<button
type="button"
onPointerDown={(event) => {
if (event.button !== 0) return;
startLongPressTimer();
}}
onPointerUp={clearLongPressTimer}
onPointerLeave={clearLongPressTimer}
onPointerCancel={clearLongPressTimer}
onClick={(event) => {
if (longPressTriggeredRef.current) {
event.preventDefault();
longPressTriggeredRef.current = false;
return;
}
onCycleAgent();
}}
onPointerDown={handlePointerDown}
onPointerUp={handlePointerUp} // Don't use onClick - it closes mobile keyboard
onPointerLeave={handlePointerLeave}
onContextMenu={(e) => e.preventDefault()}
className={cn(
'inline-flex min-w-0 items-center select-none',
'rounded-lg border border-border/50 px-1.5',
@@ -81,9 +86,7 @@ export const MobileAgentButton: React.FC<MobileAgentButtonProps> = ({ onOpenAgen
}}
title={agentLabel}
>
<span className="min-w-0 max-w-full overflow-x-auto whitespace-nowrap scrollbar-hidden">
{agentLabel}
</span>
<span className="truncate">{agentLabel}</span>
</button>
);
};