From d28c4e9da6a4f4da99044f7c611d91689806aaaf Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Thu, 18 Dec 2025 11:20:37 +0200 Subject: [PATCH] feat: enhance mobile experience by adjusting layout and keyboard handling --- CHANGELOG.md | 2 +- .../ui/src/components/chat/ChatContainer.tsx | 20 ++- packages/ui/src/components/chat/ChatInput.tsx | 84 +++++++++++- packages/ui/src/components/layout/Header.tsx | 33 ++++- .../ui/src/components/layout/MainLayout.tsx | 121 +++++++++++++++++- packages/ui/src/index.css | 31 ++++- packages/ui/src/lib/device.ts | 9 ++ 7 files changed, 283 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef192272..001c2956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - MacOS app menu entries for Check for update and for creating bug/request in Help section - +- For Mobile added settings, improved terminal scrolling, fixed app layout positioning ## [1.2.3] - 2025-12-17 diff --git a/packages/ui/src/components/chat/ChatContainer.tsx b/packages/ui/src/components/chat/ChatContainer.tsx index 462370c1..41c983b7 100644 --- a/packages/ui/src/components/chat/ChatContainer.tsx +++ b/packages/ui/src/components/chat/ChatContainer.tsx @@ -145,7 +145,10 @@ export const ChatContainer: React.FC = () => { if (!currentSessionId) { return ( -
+
@@ -157,7 +160,10 @@ export const ChatContainer: React.FC = () => { const hasMessagesEntry = messages.has(currentSessionId); if (!hasMessagesEntry) { return ( -
+
{[1, 2, 3].map((i) => ( @@ -179,7 +185,10 @@ export const ChatContainer: React.FC = () => { if (sessionMessages.length === 0 && !streamingMessageId) { return ( -
+
@@ -191,7 +200,10 @@ export const ChatContainer: React.FC = () => { } return ( -
+
diff --git a/packages/ui/src/components/chat/ChatInput.tsx b/packages/ui/src/components/chat/ChatInput.tsx index 25269ef9..169746a4 100644 --- a/packages/ui/src/components/chat/ChatInput.tsx +++ b/packages/ui/src/components/chat/ChatInput.tsx @@ -78,6 +78,51 @@ export const ChatInput: React.FC = ({ onOpenSettings, scrollToBo const [showAbortStatus, setShowAbortStatus] = React.useState(false); const abortTimeoutRef = React.useRef | null>(null); const prevWasAbortedRef = React.useRef(false); + const sendTriggeredByPointerDownRef = React.useRef(false); + + const handleTextareaPointerDownCapture = React.useCallback((event: React.PointerEvent) => { + if (!isMobile) { + return; + } + + if (event.pointerType !== 'touch') { + return; + } + + const textarea = textareaRef.current; + if (!textarea) { + return; + } + + if (document.activeElement === textarea) { + return; + } + + // Prevent iOS from scrolling the page to reveal the input. + event.preventDefault(); + event.stopPropagation(); + + const scroller = document.scrollingElement; + if (scroller && scroller.scrollTop !== 0) { + scroller.scrollTop = 0; + } + if (window.scrollY !== 0) { + window.scrollTo(0, 0); + } + + try { + textarea.focus({ preventScroll: true }); + } catch { + textarea.focus(); + } + + const len = textarea.value.length; + try { + textarea.setSelectionRange(len, len); + } catch { + // ignored + } + }, [isMobile]); const currentAgent = React.useMemo(() => { if (!currentAgentName) { @@ -201,7 +246,12 @@ export const ChatInput: React.FC = ({ onOpenSettings, scrollToBo setMessage(''); + if (isMobile) { + textareaRef.current?.blur(); + } + await sendMessage(sanitizedText, currentProviderId, currentModelId, currentAgentName, attachmentsToSend, agentMentionName) + .catch((error: unknown) => { const rawMessage = error instanceof Error @@ -235,7 +285,9 @@ export const ChatInput: React.FC = ({ onOpenSettings, scrollToBo toast.error(rawMessage || 'Message failed to send. Attachments restored.'); }); - textareaRef.current?.focus(); + if (!isMobile) { + textareaRef.current?.focus(); + } }; @@ -768,8 +820,35 @@ export const ChatInput: React.FC = ({ onOpenSettings, scrollToBo ) : (