From 6a298b8f70a39e9f084ef591acf217eda7176d6d Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Tue, 14 Apr 2026 09:36:38 +0700 Subject: [PATCH] fix: always use V2 lazy diff RPCs in new clients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Back-compat is old clients talking to new daemons, not the other way around. New clients should always use the V2 lazy diff path — there's no need to fall back to V1 based on a feature flag. - Remove useLazyDiff feature gate from git-diff-pane - Always use useCheckoutDiffMetadataQuery (V2) - Remove useCheckoutDiffQuery (V1) usage from the component - Remove supportsLazyDiffRpcs utility (no longer needed client-side) - Server still advertises the flag and serves V1 for old clients --- packages/app/src/components/git-diff-pane.tsx | 85 ++++--------------- .../utils/server-info-capabilities.test.ts | 40 --------- .../app/src/utils/server-info-capabilities.ts | 6 -- 3 files changed, 16 insertions(+), 115 deletions(-) diff --git a/packages/app/src/components/git-diff-pane.tsx b/packages/app/src/components/git-diff-pane.tsx index 0b9c45ccb6..a5698c5118 100644 --- a/packages/app/src/components/git-diff-pane.tsx +++ b/packages/app/src/components/git-diff-pane.tsx @@ -32,7 +32,6 @@ import { } from "lucide-react-native"; import { useCheckoutGitActionsStore } from "@/stores/checkout-git-actions-store"; import { - useCheckoutDiffQuery, type ParsedDiffFile, type DiffLine, type HighlightToken, @@ -42,7 +41,6 @@ import { type DiffFileMetadata, } from "@/hooks/use-checkout-diff-metadata-query"; import { useFileHunks } from "@/hooks/use-file-hunks"; -import { useSessionForServer } from "@/hooks/use-session-directory"; import { useCheckoutStatusQuery } from "@/hooks/use-checkout-status-query"; import { useCheckoutPrStatusQuery } from "@/hooks/use-checkout-pr-status-query"; import { useChangesPreferences } from "@/hooks/use-changes-preferences"; @@ -743,7 +741,6 @@ interface GitDiffPaneProps { type DiffFlatItem = | { type: "header"; file: DiffFileHeaderFields; fileIndex: number; isExpanded: boolean } - | { type: "body"; file: ParsedDiffFile; fileIndex: number } | { type: "lazy-body"; file: DiffFileMetadata; fileIndex: number }; export function GitDiffPane({ serverId, workspaceId, cwd, hideHeaderRow }: GitDiffPaneProps) { @@ -761,13 +758,6 @@ export function GitDiffPane({ serverId, workspaceId, cwd, hideHeaderRow }: GitDi const wrapLines = changesPreferences.wrapLines; const effectiveLayout = canUseSplitLayout ? changesPreferences.layout : "unified"; - // Feature detection: use V2 lazy diff when daemon supports it - const useLazyDiff = - useSessionForServer( - serverId, - (session) => session?.serverInfo?.features?.lazyDiffRpcs === true, - ) ?? false; - const handleToggleWrapLines = useCallback(() => { void updateChangesPreferences({ wrapLines: !wrapLines }); }, [updateChangesPreferences, wrapLines]); @@ -804,50 +794,22 @@ export function GitDiffPane({ serverId, workspaceId, cwd, hideHeaderRow }: GitDi const autoDiffMode = hasUncommittedChanges ? "uncommitted" : "base"; const diffMode = diffModeOverride ?? autoDiffMode; - // V1: full diff (hunks included) — used when daemon doesn't support lazy diff const { - files: v1Files, - payloadError: v1DiffPayloadError, - isLoading: isV1DiffLoading, - isFetching: isV1DiffFetching, - isError: isV1DiffError, - error: v1DiffError, - refresh: refreshV1Diff, - } = useCheckoutDiffQuery({ - serverId, - cwd, - mode: diffMode, - baseRef, - ignoreWhitespace: changesPreferences.hideWhitespace, - enabled: isGit && !useLazyDiff, - }); - - // V2: metadata-only (hunks fetched on demand per file) - const { - files: v2Files, - payloadError: v2DiffPayloadError, - isLoading: isV2DiffLoading, - isFetching: isV2DiffFetching, - isError: isV2DiffError, - error: v2DiffError, - refresh: refreshV2Diff, + files, + payloadError: diffPayloadError, + isLoading: isDiffLoading, + isFetching: isDiffFetching, + isError: isDiffError, + error: diffError, + refresh: refreshDiff, } = useCheckoutDiffMetadataQuery({ serverId, cwd, mode: diffMode, baseRef, ignoreWhitespace: changesPreferences.hideWhitespace, - enabled: isGit && useLazyDiff, + enabled: isGit, }); - - // Unified interface for both V1 and V2 - const files: DiffFileHeaderFields[] = useLazyDiff ? v2Files : v1Files; - const diffPayloadError = useLazyDiff ? v2DiffPayloadError : v1DiffPayloadError; - const isDiffLoading = useLazyDiff ? isV2DiffLoading : isV1DiffLoading; - const isDiffFetching = useLazyDiff ? isV2DiffFetching : isV1DiffFetching; - const isDiffError = useLazyDiff ? isV2DiffError : isV1DiffError; - const diffError = useLazyDiff ? v2DiffError : v1DiffError; - const refreshDiff = useLazyDiff ? refreshV2Diff : refreshV1Diff; const { status: prStatus, githubFeaturesEnabled, @@ -941,15 +903,11 @@ export function GitDiffPane({ serverId, workspaceId, cwd, hideHeaderRow }: GitDi stickyIndices.push(items.length - 1); } if (isExpanded) { - if (useLazyDiff) { - items.push({ type: "lazy-body", file: file as DiffFileMetadata, fileIndex: i }); - } else { - items.push({ type: "body", file: file as ParsedDiffFile, fileIndex: i }); - } + items.push({ type: "lazy-body", file: file as DiffFileMetadata, fileIndex: i }); } } return { flatItems: items, stickyHeaderIndices: stickyIndices }; - }, [expandedPaths, files, useLazyDiff]); + }, [expandedPaths, files]); const handleHeaderHeightChange = useCallback((path: string, height: number) => { if (!Number.isFinite(height) || height <= 0) { @@ -1224,25 +1182,14 @@ export function GitDiffPane({ serverId, workspaceId, cwd, hideHeaderRow }: GitDi /> ); } - if (item.type === "lazy-body") { - return ( - - ); - } return ( - { ).toBeNull(); }); }); - -describe("supportsLazyDiffRpcs", () => { - it("returns false when serverInfo is null", () => { - expect(supportsLazyDiffRpcs({ serverInfo: null })).toBe(false); - }); - - it("returns false when serverInfo is undefined", () => { - expect(supportsLazyDiffRpcs({ serverInfo: undefined })).toBe(false); - }); - - it("returns false when features is absent", () => { - const serverInfo = buildServerInfo(); - expect(supportsLazyDiffRpcs({ serverInfo })).toBe(false); - }); - - it("returns false when lazyDiffRpcs is not set", () => { - const serverInfo: DaemonServerInfo = { - ...buildServerInfo(), - features: {}, - }; - expect(supportsLazyDiffRpcs({ serverInfo })).toBe(false); - }); - - it("returns true when lazyDiffRpcs is true", () => { - const serverInfo: DaemonServerInfo = { - ...buildServerInfo(), - features: { lazyDiffRpcs: true }, - }; - expect(supportsLazyDiffRpcs({ serverInfo })).toBe(true); - }); - - it("returns false when lazyDiffRpcs is false", () => { - const serverInfo: DaemonServerInfo = { - ...buildServerInfo(), - features: { lazyDiffRpcs: false }, - }; - expect(supportsLazyDiffRpcs({ serverInfo })).toBe(false); - }); -}); diff --git a/packages/app/src/utils/server-info-capabilities.ts b/packages/app/src/utils/server-info-capabilities.ts index 2600d5d773..fdba665548 100644 --- a/packages/app/src/utils/server-info-capabilities.ts +++ b/packages/app/src/utils/server-info-capabilities.ts @@ -1,12 +1,6 @@ import type { ServerCapabilityState } from "@server/shared/messages"; import type { DaemonServerInfo } from "@/stores/session-store"; -export function supportsLazyDiffRpcs(params: { - serverInfo: DaemonServerInfo | null | undefined; -}): boolean { - return params.serverInfo?.features?.lazyDiffRpcs === true; -} - export type VoiceReadinessMode = "dictation" | "voice"; export function getServerCapabilities(params: {