Skip to content

Commit a532709

Browse files
fix loading
1 parent d51d8dd commit a532709

3 files changed

Lines changed: 119 additions & 47 deletions

File tree

apps/web/src/components/DiffPanel.tsx

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ import { cn } from "~/lib/utils";
1919
import { readNativeApi } from "../nativeApi";
2020
import { resolvePathLinkTarget } from "../terminal-links";
2121
import { parseDiffRouteSearch, stripDiffSearchParams } from "../diffRouteSearch";
22-
import { isElectron } from "../env";
2322
import { useTheme } from "../hooks/useTheme";
2423
import { buildPatchCacheKey } from "../lib/diffRendering";
2524
import { resolveDiffThemeName } from "../lib/diffRendering";
2625
import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries";
2726
import { useStore } from "../store";
2827
import { useAppSettings } from "../appSettings";
2928
import { formatShortTimestamp } from "../timestampFormat";
29+
import { DiffPanelLoadingState, DiffPanelShell, type DiffPanelMode } from "./DiffPanelShell";
3030
import { ToggleGroup, Toggle } from "./ui/toggle-group";
3131

3232
type DiffRenderMode = "stacked" | "split";
@@ -152,7 +152,7 @@ function buildFileDiffRenderKey(fileDiff: FileDiffMetadata): string {
152152
}
153153

154154
interface DiffPanelProps {
155-
mode?: "inline" | "sheet" | "sidebar";
155+
mode?: DiffPanelMode;
156156
}
157157

158158
export { DiffWorkerPoolProvider } from "./DiffWorkerPoolProvider";
@@ -398,7 +398,6 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
398398
selectedChip?.scrollIntoView({ block: "nearest", inline: "nearest", behavior: "smooth" });
399399
}, [selectedTurn?.turnId, selectedTurnId]);
400400

401-
const shouldUseDragRegion = isElectron && mode !== "sheet";
402401
const headerRow = (
403402
<>
404403
<div className="relative min-w-0 flex-1 [-webkit-app-region:no-drag]">
@@ -512,28 +511,9 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
512511
</ToggleGroup>
513512
</>
514513
);
515-
const headerRowClassName = cn(
516-
"flex items-center justify-between gap-2 px-4",
517-
shouldUseDragRegion ? "drag-region h-[52px] border-b border-border" : "h-12",
518-
);
519514

520515
return (
521-
<div
522-
className={cn(
523-
"flex h-full min-w-0 flex-col bg-background",
524-
mode === "inline"
525-
? "w-[42vw] min-w-[360px] max-w-[560px] shrink-0 border-l border-border"
526-
: "w-full",
527-
)}
528-
>
529-
{shouldUseDragRegion ? (
530-
<div className={headerRowClassName}>{headerRow}</div>
531-
) : (
532-
<div className="border-b border-border">
533-
<div className={headerRowClassName}>{headerRow}</div>
534-
</div>
535-
)}
536-
516+
<DiffPanelShell mode={mode} header={headerRow}>
537517
{!activeThread ? (
538518
<div className="flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70">
539519
Select a thread to inspect turn diffs.
@@ -558,15 +538,17 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
558538
</div>
559539
)}
560540
{!renderablePatch ? (
561-
<div className="flex h-full items-center justify-center px-3 py-2 text-xs text-muted-foreground/70">
562-
<p>
563-
{isLoadingCheckpointDiff
564-
? "Loading checkpoint diff..."
565-
: hasNoNetChanges
541+
isLoadingCheckpointDiff ? (
542+
<DiffPanelLoadingState label="Loading checkpoint diff..." />
543+
) : (
544+
<div className="flex h-full items-center justify-center px-3 py-2 text-xs text-muted-foreground/70">
545+
<p>
546+
{hasNoNetChanges
566547
? "No net changes in this selection."
567548
: "No patch available for this selection."}
568-
</p>
569-
</div>
549+
</p>
550+
</div>
551+
)
570552
) : renderablePatch.kind === "files" ? (
571553
<Virtualizer
572554
className="diff-render-surface h-full min-h-0 overflow-auto px-2 pb-2"
@@ -622,6 +604,6 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) {
622604
</div>
623605
</>
624606
)}
625-
</div>
607+
</DiffPanelShell>
626608
);
627609
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import type { ReactNode } from "react";
2+
3+
import { isElectron } from "~/env";
4+
import { cn } from "~/lib/utils";
5+
6+
import { Skeleton } from "./ui/skeleton";
7+
8+
export type DiffPanelMode = "inline" | "sheet" | "sidebar";
9+
10+
function getDiffPanelHeaderRowClassName(mode: DiffPanelMode) {
11+
const shouldUseDragRegion = isElectron && mode !== "sheet";
12+
return cn(
13+
"flex items-center justify-between gap-2 px-4",
14+
shouldUseDragRegion ? "drag-region h-[52px] border-b border-border" : "h-12",
15+
);
16+
}
17+
18+
export function DiffPanelShell(props: {
19+
mode: DiffPanelMode;
20+
header: ReactNode;
21+
children: ReactNode;
22+
}) {
23+
const shouldUseDragRegion = isElectron && props.mode !== "sheet";
24+
25+
return (
26+
<div
27+
className={cn(
28+
"flex h-full min-w-0 flex-col bg-background",
29+
props.mode === "inline"
30+
? "w-[42vw] min-w-[360px] max-w-[560px] shrink-0 border-l border-border"
31+
: "w-full",
32+
)}
33+
>
34+
{shouldUseDragRegion ? (
35+
<div className={getDiffPanelHeaderRowClassName(props.mode)}>{props.header}</div>
36+
) : (
37+
<div className="border-b border-border">
38+
<div className={getDiffPanelHeaderRowClassName(props.mode)}>{props.header}</div>
39+
</div>
40+
)}
41+
{props.children}
42+
</div>
43+
);
44+
}
45+
46+
export function DiffPanelHeaderSkeleton() {
47+
return (
48+
<>
49+
<div className="relative min-w-0 flex-1">
50+
<Skeleton className="absolute left-0 top-1/2 size-6 -translate-y-1/2 rounded-md border border-border/50" />
51+
<Skeleton className="absolute right-0 top-1/2 size-6 -translate-y-1/2 rounded-md border border-border/50" />
52+
<div className="flex gap-1 overflow-hidden px-8 py-0.5">
53+
<Skeleton className="h-6 w-16 shrink-0 rounded-md" />
54+
<Skeleton className="h-6 w-24 shrink-0 rounded-md" />
55+
<Skeleton className="h-6 w-24 shrink-0 rounded-md max-sm:hidden" />
56+
</div>
57+
</div>
58+
<div className="flex shrink-0 gap-1">
59+
<Skeleton className="size-7 rounded-md" />
60+
<Skeleton className="size-7 rounded-md" />
61+
</div>
62+
</>
63+
);
64+
}
65+
66+
export function DiffPanelLoadingState(props: { label: string }) {
67+
return (
68+
<div className="flex min-h-0 flex-1 flex-col p-2">
69+
<div
70+
className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-md border border-border/60 bg-card/25"
71+
role="status"
72+
aria-live="polite"
73+
aria-label={props.label}
74+
>
75+
<div className="flex items-center gap-2 border-b border-border/50 px-3 py-2">
76+
<Skeleton className="h-4 w-32 rounded-full" />
77+
<Skeleton className="ml-auto h-4 w-20 rounded-full" />
78+
</div>
79+
<div className="flex min-h-0 flex-1 flex-col gap-4 px-3 py-4">
80+
<div className="space-y-2">
81+
<Skeleton className="h-3 w-full rounded-full" />
82+
<Skeleton className="h-3 w-full rounded-full" />
83+
<Skeleton className="h-3 w-10/12 rounded-full" />
84+
<Skeleton className="h-3 w-11/12 rounded-full" />
85+
<Skeleton className="h-3 w-9/12 rounded-full" />
86+
</div>
87+
<span className="sr-only">{props.label}</span>
88+
</div>
89+
</div>
90+
</div>
91+
);
92+
}

apps/web/src/routes/_chat.$threadId.tsx

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ import { Suspense, lazy, type ReactNode, useCallback, useEffect, useState } from
44

55
import ChatView from "../components/ChatView";
66
import { DiffWorkerPoolProvider } from "../components/DiffWorkerPoolProvider";
7+
import {
8+
DiffPanelHeaderSkeleton,
9+
DiffPanelLoadingState,
10+
DiffPanelShell,
11+
type DiffPanelMode,
12+
} from "../components/DiffPanelShell";
713
import { useComposerDraftStore } from "../composerDraftStore";
814
import {
915
type DiffRouteSearch,
@@ -48,26 +54,18 @@ const DiffPanelSheet = (props: {
4854
);
4955
};
5056

51-
const DiffLoadingFallback = (props: { inline: boolean }) => {
52-
if (props.inline) {
53-
return (
54-
<div className="flex h-full min-h-0 items-center justify-center px-4 text-center text-xs text-muted-foreground/70">
55-
Loading diff viewer...
56-
</div>
57-
);
58-
}
59-
57+
const DiffLoadingFallback = (props: { mode: DiffPanelMode }) => {
6058
return (
61-
<aside className="flex h-full w-[560px] shrink-0 items-center justify-center border-l border-border bg-card px-4 text-center text-xs text-muted-foreground/70">
62-
Loading diff viewer...
63-
</aside>
59+
<DiffPanelShell mode={props.mode} header={<DiffPanelHeaderSkeleton />}>
60+
<DiffPanelLoadingState label="Loading diff viewer..." />
61+
</DiffPanelShell>
6462
);
6563
};
6664

67-
const LazyDiffPanel = (props: { inline: boolean; mode: "sheet" | "sidebar" }) => {
65+
const LazyDiffPanel = (props: { mode: DiffPanelMode }) => {
6866
return (
6967
<DiffWorkerPoolProvider>
70-
<Suspense fallback={<DiffLoadingFallback inline={props.inline} />}>
68+
<Suspense fallback={<DiffLoadingFallback mode={props.mode} />}>
7169
<DiffPanel mode={props.mode} />
7270
</Suspense>
7371
</DiffWorkerPoolProvider>
@@ -155,7 +153,7 @@ const DiffPanelInlineSidebar = (props: {
155153
storageKey: DIFF_INLINE_SIDEBAR_WIDTH_STORAGE_KEY,
156154
}}
157155
>
158-
{renderDiffContent ? <LazyDiffPanel inline mode="sidebar" /> : null}
156+
{renderDiffContent ? <LazyDiffPanel mode="sidebar" /> : null}
159157
<SidebarRail />
160158
</Sidebar>
161159
</SidebarProvider>
@@ -242,7 +240,7 @@ function ChatThreadRouteView() {
242240
<ChatView key={threadId} threadId={threadId} />
243241
</SidebarInset>
244242
<DiffPanelSheet diffOpen={diffOpen} onCloseDiff={closeDiff}>
245-
{shouldRenderDiffContent ? <LazyDiffPanel inline={false} mode="sheet" /> : null}
243+
{shouldRenderDiffContent ? <LazyDiffPanel mode="sheet" /> : null}
246244
</DiffPanelSheet>
247245
</>
248246
);

0 commit comments

Comments
 (0)