fix: Improve mobile responsiveness for diff view
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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 && (
|
||||||
|
|||||||
Reference in New Issue
Block a user