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