feat: refine VS Code session UI

This commit is contained in:
Bohdan Triapitsyn
2026-05-28 01:13:03 +03:00
parent 4c7426304f
commit 32ab46c567
4 changed files with 36 additions and 31 deletions
@@ -45,6 +45,7 @@ import { getSessionMaterializationStatus } from '@/sync/materialization';
import { usePlanDetection } from '@/hooks/usePlanDetection';
import { getAllSyncSessions } from '@/sync/sync-refs';
import { useI18n } from '@/lib/i18n';
import { isVSCodeRuntime } from '@/lib/desktop';
const EMPTY_MESSAGES: Array<{ info: Message; parts: Part[] }> = [];
const EMPTY_PERMISSIONS: PermissionRequest[] = [];
@@ -527,8 +528,10 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ autoOpenDraft = tr
}, [currentSessionId, sessionMessages.length, sessionPrefetchInfo, sync]);
const { isMobile } = useDeviceInfo();
const isVSCode = isVSCodeRuntime();
const draftOpen = Boolean(newSessionDraft?.open);
const isDesktopExpandedInput = isExpandedInput && !isMobile;
const useCompactDraftLayout = isMobile || isVSCode;
const messageListRef = React.useRef<MessageListHandle | null>(null);
const draftProjectLabel = React.useMemo(() => {
const selectedProject = newSessionDraft?.selectedProjectId
@@ -787,7 +790,7 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ autoOpenDraft = tr
if (!currentSessionId && draftOpen) {
return (
<div className="relative flex h-full flex-col bg-background transform-gpu">
{isMobile && !isDesktopExpandedInput ? (
{useCompactDraftLayout && !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
@@ -801,7 +804,7 @@ export const ChatContainer: React.FC<ChatContainerProps> = ({ autoOpenDraft = tr
'relative z-10 flex min-h-0',
isDesktopExpandedInput
? 'flex-1 bg-background'
: isMobile
: useCompactDraftLayout
? 'bg-background px-0'
: 'flex-1 items-center justify-center bg-background px-0 pb-[6vh]'
)}
@@ -3787,7 +3787,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
)}
style={isMobile && inputBarOffset > 0 ? { marginBottom: `${inputBarOffset}px` } : undefined}
>
{newSessionDraftOpen && !isDesktopExpanded && !isMobile ? (
{newSessionDraftOpen && !isDesktopExpanded && !isMobile && !isVSCode ? (
<div className="chat-input-column mb-7 text-center">
<h1 className="text-balance text-2xl font-medium tracking-tight text-foreground md:text-3xl">
{draftProjectLabel
@@ -1010,7 +1010,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
});
const sectionsForSidebarRender = React.useMemo(() => {
if (!isVSCode || hasSessionSearchQuery || recentSessionIds.size === 0) {
if (isVSCode || hasSessionSearchQuery || recentSessionIds.size === 0) {
return sectionsForRender;
}
@@ -1355,7 +1355,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
],
);
const topContent = showRecentSection && !hasSessionSearchQuery ? (
const topContent = showRecentSection && !isVSCode && !hasSessionSearchQuery ? (
<SidebarActivitySections
sections={activitySections}
renderSessionNode={renderSessionNode}
@@ -1552,7 +1552,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
>
<SidebarHeader
hideDirectoryControls={hideDirectoryControls}
mobileVariant={mobileVariant}
showRecentControls={!isVSCode}
handleOpenDirectoryDialog={handleOpenDirectoryDialog}
openNewSessionDraft={handleOpenNewSessionDraftFromHeader}
canOpenMultiRun={projects.length > 0}
@@ -15,7 +15,7 @@ import { useI18n } from '@/lib/i18n';
type Props = {
hideDirectoryControls: boolean;
mobileVariant: boolean;
showRecentControls: boolean;
handleOpenDirectoryDialog: () => void;
openNewSessionDraft: () => void;
canOpenMultiRun: boolean;
@@ -40,7 +40,7 @@ export function SidebarHeader(props: Props): React.ReactNode {
const { t } = useI18n();
const {
hideDirectoryControls,
mobileVariant,
showRecentControls,
handleOpenDirectoryDialog,
openNewSessionDraft,
canOpenMultiRun,
@@ -89,21 +89,19 @@ export function SidebarHeader(props: Props): React.ReactNode {
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.actions.addProject')}</p></TooltipContent>
</Tooltip>
{mobileVariant ? (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={openNewSessionDraft}
className={headerActionButtonClass}
aria-label={t('sessions.sidebar.header.actions.newSession')}
>
<Icon name="chat-new" className={headerActionIconClass} />
</button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.actions.newSession')}</p></TooltipContent>
</Tooltip>
) : null}
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={openNewSessionDraft}
className={headerActionButtonClass}
aria-label={t('sessions.sidebar.header.actions.newSession')}
>
<Icon name="chat-new" className={headerActionIconClass} />
</button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.actions.newSession')}</p></TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
@@ -202,14 +200,18 @@ export function SidebarHeader(props: Props): React.ReactNode {
<span>{t('sessions.sidebar.header.displayMode.minimal')}</span>
{displayMode === 'minimal' ? <Icon name="check" className="h-4 w-4 text-primary" /> : null}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={toggleRecentSection}
className="flex items-center justify-between"
>
<span>{t('sessions.sidebar.header.displayMode.showRecent')}</span>
{showRecentSection ? <Icon name="check" className="h-4 w-4 text-primary" /> : null}
</DropdownMenuItem>
{showRecentControls ? (
<>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={toggleRecentSection}
className="flex items-center justify-between"
>
<span>{t('sessions.sidebar.header.displayMode.showRecent')}</span>
{showRecentSection ? <Icon name="check" className="h-4 w-4 text-primary" /> : null}
</DropdownMenuItem>
</>
) : null}
<DropdownMenuSeparator />
<DropdownMenuItem onClick={collapseAllProjects} className="flex items-center gap-2">
<Icon name="contract-up-down" className="h-4 w-4" />