Switch for diff split vs inline view (#653)
This commit is contained in:
@@ -24,6 +24,7 @@ import type { TaskAttempt } from 'shared/types';
|
||||
import { useReview, type ReviewDraft } from '@/contexts/ReviewProvider';
|
||||
import { CommentWidgetLine } from '@/components/diff/CommentWidgetLine';
|
||||
import { ReviewCommentRenderer } from '@/components/diff/ReviewCommentRenderer';
|
||||
import { useDiffViewMode } from '@/stores/useDiffViewStore';
|
||||
|
||||
type Props = {
|
||||
diff: Diff;
|
||||
@@ -53,6 +54,7 @@ export default function DiffCard({
|
||||
const { config } = useUserSystem();
|
||||
const theme = getActualTheme(config?.theme);
|
||||
const { comments, drafts, setDraft } = useReview();
|
||||
const globalMode = useDiffViewMode();
|
||||
|
||||
const oldName = diff.oldPath || undefined;
|
||||
const newName = diff.newPath || oldName || 'unknown';
|
||||
@@ -247,7 +249,9 @@ export default function DiffCard({
|
||||
diffViewWrap={false}
|
||||
diffViewTheme={theme}
|
||||
diffViewHighlight
|
||||
diffViewMode={DiffModeEnum.Unified}
|
||||
diffViewMode={
|
||||
globalMode === 'split' ? DiffModeEnum.Split : DiffModeEnum.Unified
|
||||
}
|
||||
diffViewFontSize={12}
|
||||
diffViewAddWidget
|
||||
onAddWidgetClick={handleAddWidgetClick}
|
||||
|
||||
@@ -11,6 +11,8 @@ import { getHighLightLanguageFromPath } from '@/utils/extToLanguage';
|
||||
import { getActualTheme } from '@/utils/theme';
|
||||
import '@/styles/diff-style-overrides.css';
|
||||
import '@/styles/edit-diff-overrides.css';
|
||||
import { useDiffViewMode } from '@/stores/useDiffViewStore';
|
||||
import DiffViewSwitch from '@/components/diff-view-switch';
|
||||
|
||||
type Props = {
|
||||
path: string;
|
||||
@@ -67,6 +69,7 @@ function EditDiffRenderer({
|
||||
const [expanded, setExpanded] = useExpandable(expansionKey, false);
|
||||
|
||||
const theme = getActualTheme(config?.theme);
|
||||
const globalMode = useDiffViewMode();
|
||||
|
||||
const { hunks, hideLineNumbers, additions, deletions, isValidDiff } = useMemo(
|
||||
() => processUnifiedDiff(unifiedDiff, hasLineNumbers),
|
||||
@@ -104,13 +107,20 @@ function EditDiffRenderer({
|
||||
|
||||
{expanded && (
|
||||
<div className={'mt-2 border ' + hideLineNumbersClass}>
|
||||
<div className="flex items-center justify-end border-b px-2 py-1">
|
||||
<DiffViewSwitch />
|
||||
</div>
|
||||
{isValidDiff ? (
|
||||
<DiffView
|
||||
data={diffData}
|
||||
diffViewWrap={false}
|
||||
diffViewTheme={theme}
|
||||
diffViewHighlight
|
||||
diffViewMode={DiffModeEnum.Unified}
|
||||
diffViewMode={
|
||||
globalMode === 'split'
|
||||
? DiffModeEnum.Split
|
||||
: DiffModeEnum.Unified
|
||||
}
|
||||
diffViewFontSize={12}
|
||||
/>
|
||||
) : (
|
||||
|
||||
66
frontend/src/components/diff-view-switch.tsx
Normal file
66
frontend/src/components/diff-view-switch.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { Columns, FileText } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useDiffViewMode, useDiffViewStore } from '@/stores/useDiffViewStore';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
size?: 'xs' | 'sm';
|
||||
};
|
||||
|
||||
/**
|
||||
* Segmented switch for Inline vs Split diff modes.
|
||||
* - Left segment: Inline (Unified)
|
||||
* - Right segment: Split
|
||||
* Uses global Zustand store so changing here updates all diffs.
|
||||
*/
|
||||
export default function DiffViewSwitch({ className, size = 'xs' }: Props) {
|
||||
const mode = useDiffViewMode();
|
||||
const setMode = useDiffViewStore((s) => s.setMode);
|
||||
|
||||
const isUnified = mode === 'unified';
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'inline-flex rounded-md border border-input overflow-hidden',
|
||||
className
|
||||
)}
|
||||
role="group"
|
||||
aria-label="Diff view mode"
|
||||
>
|
||||
<Button
|
||||
variant={isUnified ? 'default' : 'outline'}
|
||||
size={size}
|
||||
className={cn(
|
||||
'rounded-none rounded-l-md h-6',
|
||||
!isUnified && 'bg-background',
|
||||
'gap-1',
|
||||
// Highlight the inner divider when right side is active
|
||||
!isUnified && 'border-r-foreground'
|
||||
)}
|
||||
aria-pressed={isUnified}
|
||||
onClick={() => setMode('unified')}
|
||||
>
|
||||
<FileText className="h-3 w-3" />
|
||||
<span className="text-[11px]">Inline</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={!isUnified ? 'default' : 'outline'}
|
||||
size={size}
|
||||
className={cn(
|
||||
'rounded-none rounded-r-md -ml-px h-6',
|
||||
isUnified && 'bg-background',
|
||||
'gap-1',
|
||||
// Ensure inner divider reflects active left side
|
||||
isUnified && 'border-l-foreground'
|
||||
)}
|
||||
aria-pressed={!isUnified}
|
||||
onClick={() => setMode('split')}
|
||||
>
|
||||
<Columns className="h-3 w-3" />
|
||||
<span className="text-[11px]">Split</span>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { useDiffEntries } from '@/hooks/useDiffEntries';
|
||||
import { useMemo, useCallback, useState, useEffect } from 'react';
|
||||
import { Loader } from '@/components/ui/loader';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import DiffViewSwitch from '@/components/diff-view-switch';
|
||||
import DiffCard from '@/components/DiffCard';
|
||||
import { useDiffSummary } from '@/hooks/useDiffSummary';
|
||||
import type { TaskAttempt } from 'shared/types';
|
||||
@@ -131,14 +132,17 @@ function DiffTabContent({
|
||||
-{deleted}
|
||||
</span>
|
||||
</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="xs"
|
||||
onClick={handleCollapseAll}
|
||||
className="shrink-0"
|
||||
>
|
||||
{allCollapsed ? 'Expand All' : 'Collapse All'}
|
||||
</Button>
|
||||
<div className="flex items-center gap-2">
|
||||
<DiffViewSwitch />
|
||||
<Button
|
||||
variant="outline"
|
||||
size="xs"
|
||||
onClick={handleCollapseAll}
|
||||
className="shrink-0"
|
||||
>
|
||||
{allCollapsed ? 'Expand All' : 'Collapse All'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
19
frontend/src/stores/useDiffViewStore.ts
Normal file
19
frontend/src/stores/useDiffViewStore.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export type DiffViewMode = 'unified' | 'split';
|
||||
|
||||
type State = {
|
||||
mode: DiffViewMode;
|
||||
setMode: (mode: DiffViewMode) => void;
|
||||
toggle: () => void;
|
||||
};
|
||||
|
||||
export const useDiffViewStore = create<State>((set) => ({
|
||||
mode: 'unified',
|
||||
setMode: (mode) => set({ mode }),
|
||||
toggle: () =>
|
||||
set((s) => ({ mode: s.mode === 'unified' ? 'split' : 'unified' })),
|
||||
}));
|
||||
|
||||
export const useDiffViewMode = () => useDiffViewStore((s) => s.mode);
|
||||
export const useToggleDiffViewMode = () => useDiffViewStore((s) => s.toggle);
|
||||
Reference in New Issue
Block a user