| ... | ... | @@ -1,5 +1,10 @@ |
| 1 | 1 | import { parsePatchFiles } from "@pierre/diffs"; |
| 2 | | import { FileDiff, type FileDiffMetadata, WorkerPoolContextProvider } from "@pierre/diffs/react"; |
| 2 | import { |
| 3 | FileDiff, |
| 4 | type FileDiffMetadata, |
| 5 | Virtualizer, |
| 6 | WorkerPoolContextProvider, |
| 7 | } from "@pierre/diffs/react"; |
| 3 | 8 | import { useQuery, useQueryClient } from "@tanstack/react-query"; |
| 4 | 9 | import { Columns2Icon, Rows3Icon } from "lucide-react"; |
| 5 | 10 | import { type ReactNode, useEffect, useMemo, useRef, useState } from "react"; |
| ... | ... | @@ -15,6 +20,7 @@ import { useStore } from "../store"; |
| 15 | 20 | import { ToggleGroup, Toggle } from "./ui/toggle-group"; |
| 16 | 21 | |
| 17 | 22 | type DiffRenderMode = "stacked" | "split"; |
| 23 | type DiffThemeType = "light" | "dark" | "system"; |
| 18 | 24 | |
| 19 | 25 | type RenderablePatch = |
| 20 | 26 | | { |
| ... | ... | @@ -61,6 +67,10 @@ function resolveFileDiffPath(fileDiff: FileDiffMetadata): string { |
| 61 | 67 | return raw; |
| 62 | 68 | } |
| 63 | 69 | |
| 70 | function buildFileDiffRenderKey(fileDiff: FileDiffMetadata): string { |
| 71 | return fileDiff.cacheKey ?? `${fileDiff.prevName ?? "none"}:${fileDiff.name}`; |
| 72 | } |
| 73 | |
| 64 | 74 | interface DiffPanelProps { |
| 65 | 75 | mode?: "inline" | "sheet"; |
| 66 | 76 | } |
| ... | ... | @@ -396,46 +406,60 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) { |
| 396 | 406 | </div> |
| 397 | 407 | ) : ( |
| 398 | 408 | <> |
| 399 | | <div ref={patchViewportRef} className="min-w-0 flex-1 overflow-auto px-3 py-2"> |
| 409 | <div ref={patchViewportRef} className="min-h-0 min-w-0 flex-1 overflow-hidden"> |
| 400 | 410 | {!canApplyStoredTarget && state.diffThreadId && ( |
| 401 | | <p className="mb-2 text-[11px] text-muted-foreground/65"> |
| 402 | | Showing diffs for the active thread. |
| 403 | | </p> |
| 411 | <div className="px-3 pt-2"> |
| 412 | <p className="mb-2 text-[11px] text-muted-foreground/65"> |
| 413 | Showing diffs for the active thread. |
| 414 | </p> |
| 415 | </div> |
| 404 | 416 | )} |
| 405 | 417 | {checkpointDiffError && !renderablePatch && ( |
| 406 | | <p className="mb-2 text-[11px] text-red-500/80">{checkpointDiffError}</p> |
| 418 | <div className="px-3"> |
| 419 | <p className="mb-2 text-[11px] text-red-500/80">{checkpointDiffError}</p> |
| 420 | </div> |
| 407 | 421 | )} |
| 408 | 422 | {!renderablePatch ? ( |
| 409 | | <div className="flex h-full items-center justify-center text-xs text-muted-foreground/70"> |
| 410 | | {isLoadingCheckpointDiff |
| 411 | | ? "Loading checkpoint diff..." |
| 412 | | : "No patch available for this selection."} |
| 423 | <div className="flex h-full items-center justify-center px-3 py-2 text-xs text-muted-foreground/70"> |
| 424 | <p> |
| 425 | {isLoadingCheckpointDiff |
| 426 | ? "Loading checkpoint diff..." |
| 427 | : "No patch available for this selection."} |
| 428 | </p> |
| 413 | 429 | </div> |
| 414 | 430 | ) : renderablePatch.kind === "files" ? ( |
| 415 | | <div className="space-y-3"> |
| 416 | | {renderableFiles.map((fileDiff) => ( |
| 417 | | <div |
| 418 | | key={fileDiff.cacheKey ?? `${fileDiff.prevName ?? "none"}:${fileDiff.name}`} |
| 419 | | data-diff-file-path={resolveFileDiffPath(fileDiff)} |
| 420 | | className="rounded-md" |
| 421 | | > |
| 422 | | <FileDiff |
| 423 | | fileDiff={fileDiff} |
| 424 | | options={{ |
| 425 | | diffStyle: diffRenderMode === "split" ? "split" : "unified", |
| 426 | | lineDiffType: "none", |
| 427 | | themeType: resolvedTheme, |
| 428 | | }} |
| 429 | | /> |
| 430 | | </div> |
| 431 | | ))} |
| 432 | | </div> |
| 431 | <Virtualizer |
| 432 | className="h-full min-h-0 overflow-auto px-3 py-2" |
| 433 | config={{ |
| 434 | overscrollSize: 600, |
| 435 | intersectionObserverMargin: 1200, |
| 436 | }} |
| 437 | > |
| 438 | {renderableFiles.map((fileDiff) => { |
| 439 | const filePath = resolveFileDiffPath(fileDiff); |
| 440 | const fileKey = buildFileDiffRenderKey(fileDiff); |
| 441 | return ( |
| 442 | <div key={fileKey} data-diff-file-path={filePath} className="rounded-md"> |
| 443 | <FileDiff |
| 444 | fileDiff={fileDiff} |
| 445 | options={{ |
| 446 | diffStyle: diffRenderMode === "split" ? "split" : "unified", |
| 447 | lineDiffType: "none", |
| 448 | themeType: resolvedTheme as DiffThemeType, |
| 449 | }} |
| 450 | /> |
| 451 | </div> |
| 452 | ); |
| 453 | })} |
| 454 | </Virtualizer> |
| 433 | 455 | ) : ( |
| 434 | | <div className="space-y-2"> |
| 435 | | <p className="text-[11px] text-muted-foreground/75">{renderablePatch.reason}</p> |
| 436 | | <pre className="max-h-[72vh] overflow-auto rounded-md border border-border/70 bg-background/70 p-3 font-mono text-[11px] leading-relaxed text-muted-foreground/90"> |
| 437 | | {renderablePatch.text} |
| 438 | | </pre> |
| 456 | <div className="h-full overflow-auto px-3 py-2"> |
| 457 | <div className="space-y-2"> |
| 458 | <p className="text-[11px] text-muted-foreground/75">{renderablePatch.reason}</p> |
| 459 | <pre className="max-h-[72vh] overflow-auto rounded-md border border-border/70 bg-background/70 p-3 font-mono text-[11px] leading-relaxed text-muted-foreground/90"> |
| 460 | {renderablePatch.text} |
| 461 | </pre> |
| 462 | </div> |
| 439 | 463 | </div> |
| 440 | 464 | )} |
| 441 | 465 | </div> |