fix: Improve mobile responsiveness for diff view

This commit is contained in:
Bohdan Triapitsyn
2025-12-15 13:43:32 +02:00
parent 3f31e24cb1
commit f9b3646f17
2 changed files with 40 additions and 18 deletions
+9 -4
View File
@@ -5,7 +5,7 @@ import {
TooltipTrigger, TooltipTrigger,
} from '@/components/ui/tooltip'; } from '@/components/ui/tooltip';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { RiArrowDownSLine, RiArrowUpSLine, RiChat4Line, RiCodeLine, RiGitBranchLine, RiLayoutLeftLine, RiTerminalBoxLine, type RemixiconComponentType } from '@remixicon/react'; import { RiArrowDownSLine, RiArrowUpSLine, RiChat4Line, RiCodeLine, RiGitBranchLine, RiLayoutLeftLine, RiPlayListAddLine, RiTerminalBoxLine, type RemixiconComponentType } from '@remixicon/react';
import { useUIStore, type MainTab } from '@/stores/useUIStore'; import { useUIStore, type MainTab } from '@/stores/useUIStore';
import { useConfigStore } from '@/stores/useConfigStore'; import { useConfigStore } from '@/stores/useConfigStore';
import { useSessionStore } from '@/stores/useSessionStore'; import { useSessionStore } from '@/stores/useSessionStore';
@@ -234,10 +234,15 @@ export const Header: React.FC = () => {
const tabs: TabConfig[] = React.useMemo(() => [ const tabs: TabConfig[] = React.useMemo(() => [
{ id: 'chat', label: 'Chat', icon: RiChat4Line }, { id: 'chat', label: 'Chat', icon: RiChat4Line },
{ id: 'diff', label: 'Diff', icon: RiCodeLine, badge: diffFileCount > 0 ? diffFileCount : undefined }, {
id: 'diff',
label: 'Diff',
icon: RiCodeLine,
badge: !isMobile && diffFileCount > 0 ? diffFileCount : undefined,
},
{ id: 'terminal', label: 'Terminal', icon: RiTerminalBoxLine }, { id: 'terminal', label: 'Terminal', icon: RiTerminalBoxLine },
{ id: 'git', label: 'Git', icon: RiGitBranchLine }, { id: 'git', label: 'Git', icon: RiGitBranchLine },
], [diffFileCount]); ], [diffFileCount, isMobile]);
useEffect(() => { useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
@@ -371,7 +376,7 @@ export const Header: React.FC = () => {
className="app-region-no-drag h-9 w-9 p-2 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary" className="app-region-no-drag h-9 w-9 p-2 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
aria-label="Open sessions" aria-label="Open sessions"
> >
<RiLayoutLeftLine className="h-5 w-5" /> <RiPlayListAddLine className="h-5 w-5" />
</button> </button>
{contextUsage && contextUsage.totalTokens > 0 && activeMainTab === 'chat' && ( {contextUsage && contextUsage.totalTokens > 0 && activeMainTab === 'chat' && (
<ContextUsageDisplay <ContextUsageDisplay
+31 -14
View File
@@ -54,6 +54,7 @@ interface FileSelectorProps {
selectedFile: string | null; selectedFile: string | null;
selectedFileEntry: FileEntry | null; selectedFileEntry: FileEntry | null;
onSelectFile: (path: string) => void; onSelectFile: (path: string) => void;
isMobile: boolean;
} }
const FileSelector = React.memo<FileSelectorProps>(({ const FileSelector = React.memo<FileSelectorProps>(({
@@ -61,16 +62,25 @@ const FileSelector = React.memo<FileSelectorProps>(({
selectedFile, selectedFile,
selectedFileEntry, selectedFileEntry,
onSelectFile, onSelectFile,
isMobile,
}) => { }) => {
if (changedFiles.length === 0) return null; if (changedFiles.length === 0) return null;
const getLabel = React.useCallback((path: string) => {
if (!isMobile) return path;
const lastSlash = path.lastIndexOf('/');
return lastSlash >= 0 ? path.slice(lastSlash + 1) : path;
}, [isMobile]);
return ( return (
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<button className="flex h-8 items-center gap-2 rounded-lg border border-input bg-transparent px-2 typography-ui-label text-foreground outline-none hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring"> <button className="flex h-8 items-center gap-2 rounded-lg border border-input bg-transparent px-2 typography-ui-label text-foreground outline-none hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring">
{selectedFileEntry ? ( {selectedFileEntry ? (
<div className="flex items-center gap-3"> <div className="flex min-w-0 items-center gap-3">
<span className="truncate typography-meta">{selectedFileEntry.path}</span> <span className="min-w-0 flex-1 truncate typography-meta">
{getLabel(selectedFileEntry.path)}
</span>
{formatDiffTotals(selectedFileEntry.insertions, selectedFileEntry.deletions)} {formatDiffTotals(selectedFileEntry.insertions, selectedFileEntry.deletions)}
</div> </div>
) : ( ) : (
@@ -83,9 +93,13 @@ const FileSelector = React.memo<FileSelectorProps>(({
<DropdownMenuRadioGroup value={selectedFile ?? ''} onValueChange={onSelectFile}> <DropdownMenuRadioGroup value={selectedFile ?? ''} onValueChange={onSelectFile}>
{changedFiles.map((file) => ( {changedFiles.map((file) => (
<DropdownMenuRadioItem key={file.path} value={file.path}> <DropdownMenuRadioItem key={file.path} value={file.path}>
<div className="flex w-full items-center justify-between gap-3"> <div className="flex w-full min-w-0 items-center gap-3">
<span className="truncate typography-meta">{file.path}</span> <span className="min-w-0 flex-1 truncate typography-meta">
{formatDiffTotals(file.insertions, file.deletions)} {getLabel(file.path)}
</span>
<span className="ml-auto">
{formatDiffTotals(file.insertions, file.deletions)}
</span>
</div> </div>
</DropdownMenuRadioItem> </DropdownMenuRadioItem>
))} ))}
@@ -161,7 +175,7 @@ const useEffectiveDirectory = () => {
export const DiffView: React.FC = () => { export const DiffView: React.FC = () => {
const { git } = useRuntimeAPIs(); const { git } = useRuntimeAPIs();
const effectiveDirectory = useEffectiveDirectory(); const effectiveDirectory = useEffectiveDirectory();
const { screenWidth } = useDeviceInfo(); const { screenWidth, isMobile } = useDeviceInfo();
const isGitRepo = useIsGitRepo(effectiveDirectory ?? null); const isGitRepo = useIsGitRepo(effectiveDirectory ?? null);
const status = useGitStatus(effectiveDirectory ?? null); const status = useGitStatus(effectiveDirectory ?? null);
@@ -459,19 +473,22 @@ export const DiffView: React.FC = () => {
return ( return (
<div className="flex h-full flex-col overflow-hidden bg-background"> <div className="flex h-full flex-col overflow-hidden bg-background">
<div className="flex items-center gap-3 px-3 py-2 bg-background"> <div className="flex items-center gap-3 px-3 py-2 bg-background">
<div className="flex items-center gap-1 rounded-md px-2 py-1 text-muted-foreground shrink-0"> {!isMobile && (
<RiGitCommitLine size={16} /> <div className="flex items-center gap-1 rounded-md px-2 py-1 text-muted-foreground shrink-0">
<span className="typography-ui-label font-semibold text-foreground"> <RiGitCommitLine size={16} />
{isLoadingStatus && !status <span className="typography-ui-label font-semibold text-foreground">
? 'Loading changes…' {isLoadingStatus && !status
: `${changedFiles.length} ${changedFiles.length === 1 ? 'file' : 'files'} changed`} ? 'Loading changes…'
</span> : `${changedFiles.length} ${changedFiles.length === 1 ? 'file' : 'files'} changed`}
</div> </span>
</div>
)}
<FileSelector <FileSelector
changedFiles={changedFiles} changedFiles={changedFiles}
selectedFile={selectedFile} selectedFile={selectedFile}
selectedFileEntry={selectedFileEntry} selectedFileEntry={selectedFileEntry}
onSelectFile={handleSelectFile} onSelectFile={handleSelectFile}
isMobile={isMobile || screenWidth <= 768}
/> />
<div className="flex-1" /> <div className="flex-1" />
{selectedFileEntry && ( {selectedFileEntry && (