import React from 'react'; import { useUIStore } from '@/stores/useUIStore'; import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory'; import { useGitStore, useGitStatus, useIsGitRepo, useGitFileCount, useGitLoadingStatus } from '@/stores/useGitStore'; import { cn } from '@/lib/utils'; import type { GitStatus } from '@/lib/api/types'; import { DropdownMenu, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { Tooltip, TooltipContent, TooltipTrigger, } from '@/components/ui/tooltip'; import { Button } from '@/components/ui/button'; import { toast } from '@/components/ui'; import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay'; import { getLanguageFromExtension, isImageFile } from '@/lib/toolHelpers'; import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs'; import { DiffViewToggle } from '@/components/chat/message/DiffViewToggle'; import type { DiffViewMode } from '@/components/chat/message/types'; import { ReviewFlowDialog, type ReviewFlowExecution } from '@/components/session/ReviewFlowDialog'; import { PierreDiffViewer } from './PierreDiffViewer'; import { useDeviceInfo } from '@/lib/device'; import { FileTypeIcon } from '@/components/icons/FileTypeIcon'; import { Icon } from "@/components/icon/Icon"; import { getContextFileOpenFailureMessage, validateContextFileOpen } from '@/lib/contextFileOpenGuard'; import { toAbsoluteFilePath } from '@/lib/path-utils'; import { sessionEvents } from '@/lib/sessionEvents'; import { useI18n } from '@/lib/i18n'; import type { I18nKey } from '@/lib/i18n/store'; import { fileDiffFromPatch } from '@/lib/diff/patchFileDiff'; import { isVSCodeRuntime } from '@/lib/desktop'; import { startReviewFlow } from '@/lib/reviewFlow'; import { useSessionUIStore } from '@/sync/session-ui-store'; import type { FileDiffMetadata } from '@pierre/diffs'; // Minimum width for side-by-side diff view (px) const SIDE_BY_SIDE_MIN_WIDTH = 1100; const DIFF_REQUEST_TIMEOUT_MS = 15000; const LARGE_DIFF_CHANGED_LINES = 500; const STACKED_DIFF_MOUNT_MARGIN = 300; const FULL_CONTEXT_DIFF_LINES = 1_000_000; const DEFAULT_CONTEXT_DIFF_LINES = 3; // Perf: limit concurrent expanded diffs in stacked view. // Expanding many diffs mounts many Pierre instances + lots of DOM. const getStackedViewDefaultExpandedCount = (fileCount: number): number => { if (fileCount <= 6) return fileCount; if (fileCount <= 12) return 6; if (fileCount <= 25) return 4; return 2; }; type FileEntry = GitStatus['files'][number] & { insertions: number; deletions: number; isNew: boolean; }; type DiffContextMode = 'patch' | 'full'; type DiffData = { original: string; modified: string; isBinary?: boolean; patch?: string; fileDiff?: FileDiffMetadata; contextMode?: DiffContextMode; }; type DiffScope = 'all' | 'staged' | 'working'; const BinaryDiffPlaceholder = React.memo(() => { const { t } = useI18n(); return (
{loadFullFiles ? t('diffView.actions.disableFullFiles') : t('diffView.actions.loadFullFiles')}