authorgravatar for cursoragent@cursor.comCursor Agent <cursoragent@cursor.com> 2026-02-19 23:36:12+00:00
committergravatar for cursoragent@cursor.comCursor Agent <cursoragent@cursor.com> 2026-02-19 23:36:12+00:00
log842e327583f214714abbde82e7e19a6341c3d140
tree805948e35c0526b64bcc6068be77496e1be52b30
parent891eb02d49d4b511cc1b52ed4d675476484f2aa9

Extract stripDiffSearchParams helper to deduplicate diff param stripping logic

Consolidate the repeated pattern of destructuring and removing diff/diffTurnId/diffFilePath search params into a single stripDiffSearchParams() function in diffRouteSearch.ts. Replaces 5 identical destructuring blocks across 3 files with calls to the shared helper. Applied via @cursor push command

4 files changed, 35 insertions(+), 48 deletions(-)

apps/web/src/components/ChatView.tsx+21-19
...@@ -36,7 +36,7 @@ import { projectSearchEntriesQueryOptions } from "~/lib/projectReactQuery";...@@ -36,7 +36,7 @@ import { projectSearchEntriesQueryOptions } from "~/lib/projectReactQuery";
36import { serverConfigQueryOptions, serverQueryKeys } from "~/lib/serverReactQuery";36import { serverConfigQueryOptions, serverQueryKeys } from "~/lib/serverReactQuery";
3737
38import { isElectron } from "../env";38import { isElectron } from "../env";
39import { parseDiffRouteSearch } from "../diffRouteSearch";39import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch";
40import { buildBootstrapInput } from "../historyBootstrap";40import { buildBootstrapInput } from "../historyBootstrap";
41import {41import {
42 type ComposerTriggerKind,42 type ComposerTriggerKind,
...@@ -1967,12 +1967,7 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -1967,12 +1967,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
1967 to: "/$threadId",1967 to: "/$threadId",
1968 params: { threadId },1968 params: { threadId },
1969 search: (previous) => {1969 search: (previous) => {
1970 const {1970 const rest = stripDiffSearchParams(previous);
1971 diff: _diff,
1972 diffTurnId: _diffTurnId,
1973 diffFilePath: _diffFilePath,
1974 ...rest
1975 } = previous;
1976 return diffOpen ? rest : { ...rest, diff: "1" };1971 return diffOpen ? rest : { ...rest, diff: "1" };
1977 },1972 },
1978 });1973 });
...@@ -1983,12 +1978,7 @@ export default function ChatView({ threadId }: ChatViewProps) {...@@ -1983,12 +1978,7 @@ export default function ChatView({ threadId }: ChatViewProps) {
1983 to: "/$threadId",1978 to: "/$threadId",
1984 params: { threadId },1979 params: { threadId },
1985 search: (previous) => {1980 search: (previous) => {
1986 const {1981 const rest = stripDiffSearchParams(previous);
1987 diff: _diff,
1988 diffTurnId: _diffTurnId,
1989 diffFilePath: _diffFilePath,
1990 ...rest
1991 } = previous;
1992 return filePath1982 return filePath
1993 ? { ...rest, diff: "1", diffTurnId: turnId, diffFilePath: filePath }1983 ? { ...rest, diff: "1", diffTurnId: turnId, diffFilePath: filePath }
1994 : { ...rest, diff: "1", diffTurnId: turnId };1984 : { ...rest, diff: "1", diffTurnId: turnId };
...@@ -2828,11 +2818,16 @@ const MessagesTimeline = memo(function MessagesTimeline({...@@ -2828,11 +2818,16 @@ const MessagesTimeline = memo(function MessagesTimeline({
2828 row.message.id,2818 row.message.id,
2829 );2819 );
2830 if (!turnSummary) return null;2820 if (!turnSummary) return null;
2831 const checkpointFilesState = turnCheckpointFilesByTurnId.get(turnSummary.turnId);2821 const checkpointFilesState = turnCheckpointFilesByTurnId.get(
2822 turnSummary.turnId,
2823 );
2832 const checkpointFiles = checkpointFilesState?.files ?? [];2824 const checkpointFiles = checkpointFilesState?.files ?? [];
2833 const summaryStat = checkpointFiles.reduce(2825 const summaryStat = checkpointFiles.reduce(
2834 (acc, file) => {2826 (acc, file) => {
2835 if (typeof file.additions !== "number" || typeof file.deletions !== "number") {2827 if (
2828 typeof file.additions !== "number" ||
2829 typeof file.deletions !== "number"
2830 ) {
2836 return acc;2831 return acc;
2837 }2832 }
2838 return {2833 return {
...@@ -2843,7 +2838,9 @@ const MessagesTimeline = memo(function MessagesTimeline({...@@ -2843,7 +2838,9 @@ const MessagesTimeline = memo(function MessagesTimeline({
2843 { additions: 0, deletions: 0 },2838 { additions: 0, deletions: 0 },
2844 );2839 );
2845 const changedFileCountLabel =2840 const changedFileCountLabel =
2846 checkpointFilesState?.status === "ready" ? String(checkpointFiles.length) : "...";2841 checkpointFilesState?.status === "ready"
2842 ? String(checkpointFiles.length)
2843 : "...";
2847 return (2844 return (
2848 <div className="mt-2 rounded-lg border border-border/80 bg-card/45 p-2.5">2845 <div className="mt-2 rounded-lg border border-border/80 bg-card/45 p-2.5">
2849 <div className="mb-1.5 flex items-center justify-between gap-2">2846 <div className="mb-1.5 flex items-center justify-between gap-2">
...@@ -2854,7 +2851,9 @@ const MessagesTimeline = memo(function MessagesTimeline({...@@ -2854,7 +2851,9 @@ const MessagesTimeline = memo(function MessagesTimeline({
2854 <span className="mx-1">•</span>2851 <span className="mx-1">•</span>
2855 <span className="text-success">+{summaryStat.additions}</span>2852 <span className="text-success">+{summaryStat.additions}</span>
2856 <span className="mx-0.5 text-muted-foreground/70">/</span>2853 <span className="mx-0.5 text-muted-foreground/70">/</span>
2857 <span className="text-destructive">-{summaryStat.deletions}</span>2854 <span className="text-destructive">
2855 -{summaryStat.deletions}
2856 </span>
2858 </>2857 </>
2859 )}2858 )}
2860 </p>2859 </p>
...@@ -2862,12 +2861,15 @@ const MessagesTimeline = memo(function MessagesTimeline({...@@ -2862,12 +2861,15 @@ const MessagesTimeline = memo(function MessagesTimeline({
2862 type="button"2861 type="button"
2863 size="xs"2862 size="xs"
2864 variant="outline"2863 variant="outline"
2865 onClick={() => onOpenTurnDiff(turnSummary.turnId, checkpointFiles[0]?.path)}2864 onClick={() =>
2865 onOpenTurnDiff(turnSummary.turnId, checkpointFiles[0]?.path)
2866 }
2866 >2867 >
2867 View diff2868 View diff
2868 </Button>2869 </Button>
2869 </div>2870 </div>
2870 {!checkpointFilesState || checkpointFilesState.status === "pending" ? (2871 {!checkpointFilesState ||
2872 checkpointFilesState.status === "pending" ? (
2871 <p className="text-[11px] text-muted-foreground/70">2873 <p className="text-[11px] text-muted-foreground/70">
2872 Waiting for checkpoint metadata...2874 Waiting for checkpoint metadata...
2873 </p>2875 </p>
apps/web/src/components/DiffPanel.tsx+5-19
...@@ -1,16 +1,12 @@...@@ -1,16 +1,12 @@
1import { parsePatchFiles } from "@pierre/diffs";1import { parsePatchFiles } from "@pierre/diffs";
2import {2import { FileDiff, type FileDiffMetadata, Virtualizer } from "@pierre/diffs/react";
3 FileDiff,
4 type FileDiffMetadata,
5 Virtualizer,
6} from "@pierre/diffs/react";
7import { useQuery } from "@tanstack/react-query";3import { useQuery } from "@tanstack/react-query";
8import { useNavigate, useParams, useSearch } from "@tanstack/react-router";4import { useNavigate, useParams, useSearch } from "@tanstack/react-router";
9import { Columns2Icon, Rows3Icon } from "lucide-react";5import { Columns2Icon, Rows3Icon } from "lucide-react";
10import { useEffect, useMemo, useRef, useState } from "react";6import { useEffect, useMemo, useRef, useState } from "react";
11import { checkpointDiffQueryOptions } from "~/lib/providerReactQuery";7import { checkpointDiffQueryOptions } from "~/lib/providerReactQuery";
12import { cn } from "~/lib/utils";8import { cn } from "~/lib/utils";
13import { parseDiffRouteSearch } from "../diffRouteSearch";9import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch";
14import { isElectron } from "../env";10import { isElectron } from "../env";
15import { useNativeApi } from "../hooks/useNativeApi";11import { useNativeApi } from "../hooks/useNativeApi";
16import { useTheme } from "../hooks/useTheme";12import { useTheme } from "../hooks/useTheme";
...@@ -101,7 +97,7 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {...@@ -101,7 +97,7 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
101 useTurnDiffSummaries(activeThread);97 useTurnDiffSummaries(activeThread);
10298
103 const selectedTurnId = diffSearch.diffTurnId ?? null;99 const selectedTurnId = diffSearch.diffTurnId ?? null;
104 const selectedFilePath = selectedTurnId !== null ? diffSearch.diffFilePath ?? null : null;100 const selectedFilePath = selectedTurnId !== null ? (diffSearch.diffFilePath ?? null) : null;
105 const selectedTurn =101 const selectedTurn =
106 selectedTurnId === null102 selectedTurnId === null
107 ? undefined103 ? undefined
...@@ -205,12 +201,7 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {...@@ -205,12 +201,7 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
205 to: "/$threadId",201 to: "/$threadId",
206 params: { threadId: activeThread.id },202 params: { threadId: activeThread.id },
207 search: (previous) => {203 search: (previous) => {
208 const {204 const rest = stripDiffSearchParams(previous);
209 diff: _diff,
210 diffTurnId: _diffTurnId,
211 diffFilePath: _diffFilePath,
212 ...rest
213 } = previous;
214 return { ...rest, diff: "1", diffTurnId: turnId };205 return { ...rest, diff: "1", diffTurnId: turnId };
215 },206 },
216 });207 });
...@@ -221,12 +212,7 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {...@@ -221,12 +212,7 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
221 to: "/$threadId",212 to: "/$threadId",
222 params: { threadId: activeThread.id },213 params: { threadId: activeThread.id },
223 search: (previous) => {214 search: (previous) => {
224 const {215 const rest = stripDiffSearchParams(previous);
225 diff: _diff,
226 diffTurnId: _diffTurnId,
227 diffFilePath: _diffFilePath,
228 ...rest
229 } = previous;
230 return { ...rest, diff: "1" };216 return { ...rest, diff: "1" };
231 },217 },
232 });218 });
apps/web/src/diffRouteSearch.ts+7-1
...@@ -12,6 +12,13 @@ function normalizeSearchString(value: unknown): string | undefined {...@@ -12,6 +12,13 @@ function normalizeSearchString(value: unknown): string | undefined {
12 return normalized.length > 0 ? normalized : undefined;12 return normalized.length > 0 ? normalized : undefined;
13}13}
1414
15export function stripDiffSearchParams<T extends Record<string, unknown>>(
16 params: T,
17): Omit<T, "diff" | "diffTurnId" | "diffFilePath"> {
18 const { diff: _diff, diffTurnId: _diffTurnId, diffFilePath: _diffFilePath, ...rest } = params;
19 return rest as Omit<T, "diff" | "diffTurnId" | "diffFilePath">;
20}
21
15export function parseDiffRouteSearch(search: Record<string, unknown>): DiffRouteSearch {22export function parseDiffRouteSearch(search: Record<string, unknown>): DiffRouteSearch {
16 const diff = search.diff === "1" ? "1" : undefined;23 const diff = search.diff === "1" ? "1" : undefined;
17 const diffTurnId = diff ? normalizeSearchString(search.diffTurnId) : undefined;24 const diffTurnId = diff ? normalizeSearchString(search.diffTurnId) : undefined;
...@@ -23,4 +30,3 @@ export function parseDiffRouteSearch(search: Record<string, unknown>): DiffRoute...@@ -23,4 +30,3 @@ export function parseDiffRouteSearch(search: Record<string, unknown>): DiffRoute
23 ...(diffFilePath ? { diffFilePath } : {}),30 ...(diffFilePath ? { diffFilePath } : {}),
24 };31 };
25}32}
26
apps/web/src/routes/_chat.$threadId.tsx+2-9
...@@ -2,7 +2,7 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router";...@@ -2,7 +2,7 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router";
2import { Activity, Suspense, lazy, type ReactNode, useCallback, useEffect } from "react";2import { Activity, Suspense, lazy, type ReactNode, useCallback, useEffect } from "react";
33
4import ChatView from "../components/ChatView";4import ChatView from "../components/ChatView";
5import { parseDiffRouteSearch } from "../diffRouteSearch";5import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch";
6import { useMediaQuery } from "../hooks/useMediaQuery";6import { useMediaQuery } from "../hooks/useMediaQuery";
7import { useStore } from "../store";7import { useStore } from "../store";
8import { Sheet, SheetPopup } from "../components/ui/sheet";8import { Sheet, SheetPopup } from "../components/ui/sheet";
...@@ -58,13 +58,7 @@ function ChatThreadRouteView() {...@@ -58,13 +58,7 @@ function ChatThreadRouteView() {
58 to: "/$threadId",58 to: "/$threadId",
59 params: { threadId },59 params: { threadId },
60 search: (previous) => {60 search: (previous) => {
61 const {61 return stripDiffSearchParams(previous);
62 diff: _diff,
63 diffTurnId: _diffTurnId,
64 diffFilePath: _diffFilePath,
65 ...rest
66 } = previous;
67 return rest;
68 },62 },
69 });63 });
70 }, [navigate, threadId]);64 }, [navigate, threadId]);
...@@ -118,4 +112,3 @@ export const Route = createFileRoute("/_chat/$threadId")({...@@ -118,4 +112,3 @@ export const Route = createFileRoute("/_chat/$threadId")({
118 validateSearch: (search) => parseDiffRouteSearch(search),112 validateSearch: (search) => parseDiffRouteSearch(search),
119 component: ChatThreadRouteView,113 component: ChatThreadRouteView,
120});114});
121