feat: improve mobile draft session layout

Keeps the welcome prompt centered on mobile
Moves the draft composer back to the bottom on mobile
Preserves the desktop draft layout
This commit is contained in:
Bohdan Triapitsyn
2026-05-27 23:36:26 +03:00
parent 8d7b75106e
commit 5331c4d962
2 changed files with 32 additions and 3 deletions
@@ -21,7 +21,8 @@ import { OverlayScrollbar } from '@/components/ui/OverlayScrollbar';
import { Icon } from "@/components/icon/Icon"; import { Icon } from "@/components/icon/Icon";
import type { PermissionRequest } from '@/types/permission'; import type { PermissionRequest } from '@/types/permission';
import type { QuestionRequest } from '@/types/question'; import type { QuestionRequest } from '@/types/question';
import { cn } from '@/lib/utils'; import { cn, formatDirectoryName } from '@/lib/utils';
import { useProjectsStore } from '@/stores/useProjectsStore';
import { import {
collectVisibleSessionIdsForBlockingRequests, collectVisibleSessionIdsForBlockingRequests,
flattenBlockingRequests, flattenBlockingRequests,
@@ -329,6 +330,11 @@ const ReadOnlyPromptBanner: React.FC = () => {
); );
}; };
const getProjectDisplayLabel = (project: { label?: string; path: string }): string => {
const label = project.label?.trim();
return label || formatDirectoryName(project.path);
};
type ChatContainerProps = { type ChatContainerProps = {
autoOpenDraft?: boolean; autoOpenDraft?: boolean;
readOnly?: boolean; readOnly?: boolean;
@@ -341,6 +347,8 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ autoOpenDraft = tr
const openNewSessionDraft = useSessionUIStore((s) => s.openNewSessionDraft); const openNewSessionDraft = useSessionUIStore((s) => s.openNewSessionDraft);
const setCurrentSession = useSessionUIStore((s) => s.setCurrentSession); const setCurrentSession = useSessionUIStore((s) => s.setCurrentSession);
const newSessionDraft = useSessionUIStore((s) => s.newSessionDraft); const newSessionDraft = useSessionUIStore((s) => s.newSessionDraft);
const projects = useProjectsStore((s) => s.projects);
const activeProjectId = useProjectsStore((s) => s.activeProjectId);
// Sync actions // Sync actions
const sync = useSync(); const sync = useSync();
@@ -522,6 +530,16 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ autoOpenDraft = tr
const draftOpen = Boolean(newSessionDraft?.open); const draftOpen = Boolean(newSessionDraft?.open);
const isDesktopExpandedInput = isExpandedInput && !isMobile; const isDesktopExpandedInput = isExpandedInput && !isMobile;
const messageListRef = React.useRef<MessageListHandle | null>(null); const messageListRef = React.useRef<MessageListHandle | null>(null);
const draftProjectLabel = React.useMemo(() => {
const selectedProject = newSessionDraft?.selectedProjectId
? projects.find((project) => project.id === newSessionDraft.selectedProjectId) ?? null
: null;
const activeProject = activeProjectId
? projects.find((project) => project.id === activeProjectId) ?? null
: null;
const project = selectedProject ?? activeProject ?? projects[0] ?? null;
return project ? getProjectDisplayLabel(project) : null;
}, [activeProjectId, newSessionDraft?.selectedProjectId, projects]);
const parentSession = React.useMemo(() => { const parentSession = React.useMemo(() => {
if (!currentSessionId) return null; if (!currentSessionId) return null;
@@ -769,12 +787,23 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ autoOpenDraft = tr
if (!currentSessionId && draftOpen) { if (!currentSessionId && draftOpen) {
return ( return (
<div className="relative flex h-full flex-col bg-background transform-gpu"> <div className="relative flex h-full flex-col bg-background transform-gpu">
{isMobile && !isDesktopExpandedInput ? (
<div className="flex min-h-0 flex-1 items-center justify-center px-6 text-center">
<h1 className="text-balance text-3xl font-medium tracking-tight text-foreground">
{draftProjectLabel
? t('chat.emptyState.draftTitleWithProject', { project: draftProjectLabel })
: t('chat.emptyState.draftTitle')}
</h1>
</div>
) : null}
<div <div
className={cn( className={cn(
'relative z-10 flex min-h-0', 'relative z-10 flex min-h-0',
isDesktopExpandedInput isDesktopExpandedInput
? 'flex-1 bg-background' ? 'flex-1 bg-background'
: 'flex-1 items-center justify-center bg-background px-0 pb-[6vh]' : isMobile
? 'bg-background px-0'
: 'flex-1 items-center justify-center bg-background px-0 pb-[6vh]'
)} )}
> >
{promptReadOnly ? <ReadOnlyPromptBanner /> : <ChatInput scrollToBottom={resumeToLatestInstant} />} {promptReadOnly ? <ReadOnlyPromptBanner /> : <ChatInput scrollToBottom={resumeToLatestInstant} />}
@@ -3787,7 +3787,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
)} )}
style={isMobile && inputBarOffset > 0 ? { marginBottom: `${inputBarOffset}px` } : undefined} style={isMobile && inputBarOffset > 0 ? { marginBottom: `${inputBarOffset}px` } : undefined}
> >
{newSessionDraftOpen && !isDesktopExpanded ? ( {newSessionDraftOpen && !isDesktopExpanded && !isMobile ? (
<div className="chat-input-column mb-7 text-center"> <div className="chat-input-column mb-7 text-center">
<h1 className="text-balance text-2xl font-medium tracking-tight text-foreground md:text-3xl"> <h1 className="text-balance text-2xl font-medium tracking-tight text-foreground md:text-3xl">
{draftProjectLabel {draftProjectLabel