diff --git a/frontend/src/components/DiffCard.tsx b/frontend/src/components/DiffCard.tsx
index 1116f507..a85e5f85 100644
--- a/frontend/src/components/DiffCard.tsx
+++ b/frontend/src/components/DiffCard.tsx
@@ -25,7 +25,10 @@ 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';
+import {
+ useDiffViewMode,
+ useIgnoreWhitespaceDiff,
+} from '@/stores/useDiffViewStore';
import { useProject } from '@/contexts/project-context';
type Props = {
@@ -76,6 +79,7 @@ export default function DiffCard({
const theme = getActualTheme(config?.theme);
const { comments, drafts, setDraft } = useReview();
const globalMode = useDiffViewMode();
+ const ignoreWhitespace = useIgnoreWhitespaceDiff();
const { projectId } = useProject();
const oldName = diff.oldPath || undefined;
@@ -92,6 +96,11 @@ export default function DiffCard({
const newContentSafe = diff.newContent || '';
const isContentEqual = oldContentSafe === newContentSafe;
+ const diffOptions = useMemo(
+ () => (ignoreWhitespace ? { ignoreWhitespace: true as const } : undefined),
+ [ignoreWhitespace]
+ );
+
const diffFile = useMemo(() => {
if (isContentEqual || isOmitted) return null;
try {
@@ -103,7 +112,8 @@ export default function DiffCard({
newFileName,
newContentSafe,
oldLang,
- newLang
+ newLang,
+ diffOptions
);
file.initRaw();
return file;
@@ -120,6 +130,7 @@ export default function DiffCard({
newLang,
oldContentSafe,
newContentSafe,
+ diffOptions,
]);
const add = isOmitted
diff --git a/frontend/src/components/diff-view-switch.tsx b/frontend/src/components/diff-view-switch.tsx
index e1dd3f3e..10d86cb2 100644
--- a/frontend/src/components/diff-view-switch.tsx
+++ b/frontend/src/components/diff-view-switch.tsx
@@ -1,7 +1,11 @@
-import { Columns, FileText } from 'lucide-react';
+import { Columns, FileText, Pilcrow } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { cn } from '@/lib/utils';
-import { useDiffViewMode, useDiffViewStore } from '@/stores/useDiffViewStore';
+import {
+ useDiffViewMode,
+ useDiffViewStore,
+ useIgnoreWhitespaceDiff,
+} from '@/stores/useDiffViewStore';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import {
Tooltip,
@@ -18,46 +22,77 @@ export default function DiffViewSwitch({ className }: Props) {
const { t } = useTranslation('tasks');
const mode = useDiffViewMode();
const setMode = useDiffViewStore((s) => s.setMode);
+ const ignoreWhitespace = useIgnoreWhitespaceDiff();
+ const setIgnoreWhitespace = useDiffViewStore((s) => s.setIgnoreWhitespace);
+
+ const whitespaceValue = ignoreWhitespace ? ['ignoreWhitespace'] : [];
return (
- v && setMode(v as 'unified' | 'split')}
- className={cn('inline-flex gap-4', className)}
- aria-label="Diff view mode"
- >
-
-
-
-
-
-
-
- {t('diff.viewModes.inline')}
-
-
+
+
v && setMode(v as 'unified' | 'split')}
+ className="inline-flex gap-4"
+ aria-label="Diff view mode"
+ >
+
+
+
+
+
+
+
+ {t('diff.viewModes.inline')}
+
+
-
-
-
-
-
-
-
- {t('diff.viewModes.split')}
-
-
-
+
+
+
+
+
+
+
+ {t('diff.viewModes.split')}
+
+
+
+
+
+ setIgnoreWhitespace(values.includes('ignoreWhitespace'))
+ }
+ className="inline-flex gap-4"
+ aria-label={t('diff.ignoreWhitespace')}
+ >
+
+
+
+
+
+
+
+ {t('diff.ignoreWhitespace')}
+
+
+
+
);
}
diff --git a/frontend/src/i18n/locales/en/tasks.json b/frontend/src/i18n/locales/en/tasks.json
index a9ba2762..34d42105 100644
--- a/frontend/src/i18n/locales/en/tasks.json
+++ b/frontend/src/i18n/locales/en/tasks.json
@@ -91,6 +91,7 @@
"inline": "Inline view",
"split": "Split view"
},
+ "ignoreWhitespace": "Ignore whitespace changes",
"errorLoadingDiff": "Failed to load diff: {{error}}",
"expandAll": "Expand all diffs",
"collapseAll": "Collapse all diffs",
diff --git a/frontend/src/i18n/locales/es/tasks.json b/frontend/src/i18n/locales/es/tasks.json
index 5855f922..e6be9469 100644
--- a/frontend/src/i18n/locales/es/tasks.json
+++ b/frontend/src/i18n/locales/es/tasks.json
@@ -75,6 +75,7 @@
"collapseAll": "Collapse all diffs",
"errorLoadingDiff": "Failed to load diff: {{error}}",
"expandAll": "Expand all diffs",
+ "ignoreWhitespace": "Ignorar cambios de espacios en blanco",
"filesChanged_one": "{{count}} file changed",
"filesChanged_other": "{{count}} files changed",
"noChanges": "No changes have been made yet",
diff --git a/frontend/src/i18n/locales/ja/tasks.json b/frontend/src/i18n/locales/ja/tasks.json
index ea9237ab..7b00641d 100644
--- a/frontend/src/i18n/locales/ja/tasks.json
+++ b/frontend/src/i18n/locales/ja/tasks.json
@@ -75,6 +75,7 @@
"collapseAll": "Collapse all diffs",
"errorLoadingDiff": "Failed to load diff: {{error}}",
"expandAll": "Expand all diffs",
+ "ignoreWhitespace": "空白の変更を無視",
"filesChanged_one": "{{count}} file changed",
"filesChanged_other": "{{count}} files changed",
"noChanges": "No changes have been made yet",
diff --git a/frontend/src/i18n/locales/ko/tasks.json b/frontend/src/i18n/locales/ko/tasks.json
index c4bf07ee..aeef5c58 100644
--- a/frontend/src/i18n/locales/ko/tasks.json
+++ b/frontend/src/i18n/locales/ko/tasks.json
@@ -75,6 +75,7 @@
"collapseAll": "Collapse all diffs",
"errorLoadingDiff": "Failed to load diff: {{error}}",
"expandAll": "Expand all diffs",
+ "ignoreWhitespace": "공백 변경 무시",
"filesChanged_one": "{{count}} file changed",
"filesChanged_other": "{{count}} files changed",
"noChanges": "No changes have been made yet",
diff --git a/frontend/src/stores/useDiffViewStore.ts b/frontend/src/stores/useDiffViewStore.ts
index f4572a21..ade630e6 100644
--- a/frontend/src/stores/useDiffViewStore.ts
+++ b/frontend/src/stores/useDiffViewStore.ts
@@ -6,6 +6,8 @@ type State = {
mode: DiffViewMode;
setMode: (mode: DiffViewMode) => void;
toggle: () => void;
+ ignoreWhitespace: boolean;
+ setIgnoreWhitespace: (value: boolean) => void;
};
export const useDiffViewStore = create((set) => ({
@@ -13,6 +15,10 @@ export const useDiffViewStore = create((set) => ({
setMode: (mode) => set({ mode }),
toggle: () =>
set((s) => ({ mode: s.mode === 'unified' ? 'split' : 'unified' })),
+ ignoreWhitespace: true,
+ setIgnoreWhitespace: (value) => set({ ignoreWhitespace: value }),
}));
export const useDiffViewMode = () => useDiffViewStore((s) => s.mode);
+export const useIgnoreWhitespaceDiff = () =>
+ useDiffViewStore((s) => s.ignoreWhitespace);