fix: always use V2 lazy diff RPCs in new clients

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
This commit is contained in:
Mohamed Boudra
2026-04-14 09:36:38 +07:00
parent bf1d5d2ab2
commit 6a298b8f70
3 changed files with 16 additions and 115 deletions
+16 -69
View File
@@ -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 (
<LazyDiffFileBody
file={item.file}
serverId={serverId}
cwd={cwd}
mode={diffMode}
baseRef={baseRef}
ignoreWhitespace={changesPreferences.hideWhitespace}
layout={effectiveLayout}
wrapLines={wrapLines}
onBodyHeightChange={handleBodyHeightChange}
testID={`diff-file-${item.fileIndex}-body`}
/>
);
}
return (
<DiffFileBody
<LazyDiffFileBody
file={item.file}
serverId={serverId}
cwd={cwd}
mode={diffMode}
baseRef={baseRef}
ignoreWhitespace={changesPreferences.hideWhitespace}
layout={effectiveLayout}
wrapLines={wrapLines}
onBodyHeightChange={handleBodyHeightChange}
@@ -5,7 +5,6 @@ import {
getServerCapabilities,
getVoiceReadinessState,
resolveVoiceUnavailableMessage,
supportsLazyDiffRpcs,
} from "./server-info-capabilities";
function buildServerInfo(capabilities?: ServerCapabilities): DaemonServerInfo {
@@ -118,42 +117,3 @@ describe("server-info-capabilities", () => {
).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);
});
});
@@ -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: {