Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion apps/web/src/components/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5643,7 +5643,14 @@ function ChatViewContent(props: ChatViewProps) {
/>
);
const panelLayoutControls = (
<div className="workspace-titlebar-controls z-50 mr-px gap-1 [-webkit-app-region:no-drag]">
<div
className={cn(
"workspace-titlebar-controls z-50 gap-1 [-webkit-app-region:no-drag]",
rightPanelOpen && !shouldUsePlanSidebarSheet
? "right-2 wco:right-[var(--workspace-controls-right)]"
: "mr-px",
)}
Comment thread
cursor[bot] marked this conversation as resolved.
>
{rightPanelOpen && !shouldUsePlanSidebarSheet ? (
<RightPanelMaximizeControl
maximized={rightPanelMaximized}
Expand Down
121 changes: 88 additions & 33 deletions apps/web/src/components/DiffPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ const DIFF_PANEL_UNSAFE_CSS = `
[data-virtualizer-buffer] {
--diffs-header-font-family: var(--font-sans) !important;
--diffs-font-family: var(--font-mono) !important;
--diffs-bg: color-mix(in srgb, var(--card) 90%, var(--background)) !important;
--diffs-light-bg: color-mix(in srgb, var(--card) 90%, var(--background)) !important;
--diffs-dark-bg: color-mix(in srgb, var(--card) 90%, var(--background)) !important;
--diffs-bg: var(--background) !important;
--diffs-light-bg: var(--background) !important;
--diffs-dark-bg: var(--background) !important;
--diffs-token-light-bg: transparent;
--diffs-token-dark-bg: transparent;

Expand All @@ -120,23 +120,72 @@ const DIFF_PANEL_UNSAFE_CSS = `
}

[data-file-info] {
background-color: color-mix(in srgb, var(--card) 94%, var(--foreground)) !important;
border-block-color: var(--border) !important;
background-color: var(--background) !important;
border-block-color: transparent !important;
color: var(--foreground) !important;
}

[data-diffs-header] {
position: sticky !important;
top: 0;
z-index: 4;
background-color: color-mix(in srgb, var(--card) 94%, var(--foreground)) !important;
border-bottom: 1px solid var(--border) !important;
background-color: var(--background) !important;
border-bottom-color: transparent !important;
align-items: center !important;
font-family: var(--font-sans) !important;
font-size: 12px !important;
line-height: 1 !important;
min-height: 32px !important;
padding-block: 6px !important;
padding-inline: 8px 12px !important;
}

[data-diffs-header]:hover {
background-color: color-mix(in srgb, var(--background) 97%, var(--foreground)) !important;
}

:is([data-separator="line-info"], [data-separator="line-info-basic"]) {
height: 24px !important;
margin-block: 0 !important;
background-color: var(--background) !important;
}

:is([data-separator="line-info"], [data-separator="line-info-basic"])
[data-separator-wrapper] {
padding-inline: 8px 12px !important;
background-color: transparent !important;
}

:is([data-separator="line-info"], [data-separator="line-info-basic"])
[data-separator-content] {
gap: 8px;
padding-inline: 0 !important;
background-color: transparent !important;
color: color-mix(in srgb, var(--foreground) 52%, var(--background)) !important;
font-family: var(--font-sans) !important;
font-size: 11px !important;
}

:is([data-separator="line-info"], [data-separator="line-info-basic"])
[data-separator-content]::before,
:is([data-separator="line-info"], [data-separator="line-info-basic"])
[data-separator-content]::after {
width: auto;
height: 1px;
flex: 1 1 auto;
content: "";
background-color: color-mix(in srgb, var(--background) 92%, var(--foreground));
}

:is([data-separator="line-info"], [data-separator="line-info-basic"])
[data-unmodified-lines] {
flex: 0 0 auto;
}

:is([data-separator="line-info"], [data-separator="line-info-basic"])
[data-expand-button] {
background-color: transparent !important;
border-color: color-mix(in srgb, var(--background) 92%, var(--foreground)) !important;
}

[data-diffs-header] [data-header-content] {
Expand Down Expand Up @@ -298,6 +347,7 @@ export default function DiffPanel({
const collapseScopeKey = routeThreadRef
? `${routeThreadRef.environmentId}:${routeThreadRef.threadId}:${reviewSectionId}`
: null;
const codeViewMountKey = collapseScopeKey ?? reviewSectionId;
const collapsedDiffFileKeys =
collapsedDiffFiles.scopeKey === collapseScopeKey
? collapsedDiffFiles.fileKeys
Expand Down Expand Up @@ -455,13 +505,14 @@ export default function DiffPanel({
const diffFileKeys = useMemo(() => codeViewFiles.map((file) => file.fileKey), [codeViewFiles]);
const allDiffFilesCollapsed = areAllDiffFilesCollapsed(diffFileKeys, collapsedDiffFileKeys);
const diffLineStat = useMemo(() => getDiffLineStat(renderableFiles), [renderableFiles]);
const selectedDiffFileKey = selectedFilePath
? (codeViewFiles.find((candidate) => candidate.filePath === selectedFilePath)?.fileKey ?? null)
: null;

useEffect(() => {
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
if (!selectedFilePath) return;
const file = codeViewFiles.find((candidate) => candidate.filePath === selectedFilePath);
if (!file) return;
codeViewRef.current?.scrollTo({ type: "item", id: file.fileKey, align: "start" });
}, [codeViewFiles, selectedFilePath, selectedFileRevealRequestId]);
if (!selectedDiffFileKey) return;
codeViewRef.current?.scrollTo({ type: "item", id: selectedDiffFileKey, align: "start" });
}, [codeViewMountKey, selectedDiffFileKey, selectedFileRevealRequestId]);

const openDiffFile = useCallback(
(filePath: string) => {
Expand Down Expand Up @@ -730,17 +781,17 @@ export default function DiffPanel({
render={
<Button
type="button"
size="icon-xs"
variant="outline"
size="icon-sm"
variant="ghost"
aria-label={allDiffFilesCollapsed ? "Expand all files" : "Collapse all files"}
onClick={toggleDiffFileCollapse}
/>
}
>
{allDiffFilesCollapsed ? (
<ChevronsUpDownIcon className="size-3" />
<ChevronsUpDownIcon className="size-3.5" />
) : (
<ChevronsDownUpIcon className="size-3" />
<ChevronsDownUpIcon className="size-3.5" />
)}
</TooltipTrigger>
<TooltipPopup side="top">
Expand All @@ -749,9 +800,8 @@ export default function DiffPanel({
</Tooltip>
)}
<ToggleGroup
className="shrink-0"
variant="outline"
size="xs"
className="shrink-0 gap-1"
size="sm"
value={[diffRenderMode]}
onValueChange={(value) => {
const next = value[0];
Expand All @@ -760,28 +810,28 @@ export default function DiffPanel({
}
}}
>
<Toggle aria-label="Stacked diff view" value="stacked">
<Rows3Icon className="size-3" />
<Toggle aria-label="Stacked diff view" value="stacked" variant="ghost">
<Rows3Icon className="size-3.5" />
</Toggle>
<Toggle aria-label="Split diff view" value="split">
<Columns2Icon className="size-3" />
<Toggle aria-label="Split diff view" value="split" variant="ghost">
<Columns2Icon className="size-3.5" />
</Toggle>
</ToggleGroup>
<Tooltip>
<TooltipTrigger
render={
<Toggle
aria-label={wordWrap ? "Disable diff line wrapping" : "Enable diff line wrapping"}
variant="outline"
size="xs"
variant="ghost"
size="sm"
pressed={wordWrap}
onPressedChange={(pressed) => {
setWordWrap(Boolean(pressed));
}}
/>
}
>
<TextWrapIcon className="size-3" />
<TextWrapIcon className="size-3.5" />
</TooltipTrigger>
<TooltipPopup side="top">
{wordWrap ? "Disable line wrapping" : "Enable line wrapping"}
Expand All @@ -794,16 +844,16 @@ export default function DiffPanel({
aria-label={
diffIgnoreWhitespace ? "Show whitespace changes" : "Hide whitespace changes"
}
variant="outline"
size="xs"
variant="ghost"
size="sm"
pressed={diffIgnoreWhitespace}
onPressedChange={(pressed) => {
setDiffIgnoreWhitespace(Boolean(pressed));
}}
/>
}
>
<PilcrowIcon className="size-3" />
<PilcrowIcon className="size-3.5" />
</TooltipTrigger>
<TooltipPopup side="top">
{diffIgnoreWhitespace ? "Show whitespace changes" : "Hide whitespace changes"}
Expand Down Expand Up @@ -876,7 +926,7 @@ export default function DiffPanel({
>
<AnnotatableCodeView
viewerRef={codeViewRef}
key={collapseScopeKey ?? reviewSectionId}
key={codeViewMountKey}
className="diff-render-surface h-full min-h-0 overflow-auto"
files={codeViewFiles}
sectionId={reviewSectionId}
Expand All @@ -891,7 +941,7 @@ export default function DiffPanel({
<button
type="button"
className={cn(
"inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-sm border-0 bg-transparent p-0 transition-colors hover:bg-foreground/10 focus-visible:outline-hidden",
"-ms-0.5 inline-flex size-5 shrink-0 cursor-pointer items-center justify-center rounded-sm border-0 bg-transparent p-0 transition-colors hover:bg-foreground/10 focus-visible:outline-hidden",
getDiffCollapseIconClassName(fileDiff),
)}
aria-label={collapsed ? `Expand ${filePath}` : `Collapse ${filePath}`}
Expand Down Expand Up @@ -923,8 +973,13 @@ export default function DiffPanel({
themeType: resolvedTheme as DiffThemeType,
unsafeCSS: DIFF_PANEL_UNSAFE_CSS,
stickyHeaders: true,
itemMetrics: { diffHeaderHeight: 33 },
layout: { paddingTop: 0, paddingBottom: 8, gap: 8 },
itemMetrics: {
diffHeaderHeight: 32,
hunkSeparatorHeight: 24,
paddingTop: 0,
paddingBottom: 0,
},
layout: { paddingTop: 0, paddingBottom: 0, gap: 0 },
}}
/>
</div>
Expand Down
68 changes: 46 additions & 22 deletions apps/web/src/components/DiffPanelShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export type DiffPanelMode = "inline" | "sheet" | "sidebar" | "embedded";
function getDiffPanelHeaderRowClassName(mode: DiffPanelMode) {
const shouldUseDragRegion = isElectron && mode !== "sheet" && mode !== "embedded";
return cn(
"flex items-center justify-between gap-2 px-4",
"flex items-center justify-between gap-2",
mode === "embedded" ? "px-2" : "px-4",
shouldUseDragRegion
? "drag-region h-[52px] border-b border-border wco:h-[env(titlebar-area-height)] wco:pr-[calc(100vw-env(titlebar-area-width)-env(titlebar-area-x)+1em)]"
: "surface-subheader",
Expand Down Expand Up @@ -59,30 +60,53 @@ export function DiffPanelHeaderSkeleton() {
);
}

function DiffFileHeaderSkeleton({ titleClassName }: { titleClassName: string }) {
return (
<div className="flex h-8 items-center gap-2 px-2 pr-3">
<div className="flex size-5 shrink-0 items-center justify-center">
<Skeleton className="size-2.5 rounded-[2px]" />
</div>
<Skeleton className="size-5 shrink-0 rounded-md" />
<Skeleton className={cn("h-3 rounded-full", titleClassName)} />
<div className="ml-auto flex shrink-0 items-center gap-2">
<Skeleton className="h-3 w-5 rounded-full" />
<Skeleton className="h-3 w-5 rounded-full" />
</div>
</div>
);
}

function DiffCodeLineSkeleton({ contentClassName }: { contentClassName: string }) {
return (
<div className="flex items-center gap-3">
<Skeleton className="h-2.5 w-5 shrink-0 rounded-full" />
<Skeleton className={cn("h-2.5 rounded-full", contentClassName)} />
</div>
);
}

export function DiffPanelLoadingState(props: { label: string }) {
return (
<div className="flex min-h-0 flex-1 flex-col p-2">
<div
className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-md border border-border/60 bg-card/25"
role="status"
aria-live="polite"
aria-label={props.label}
>
<div className="flex items-center gap-2 border-b border-border/50 px-3 py-2">
<Skeleton className="h-4 w-32 rounded-full" />
<Skeleton className="ml-auto h-4 w-20 rounded-full" />
</div>
<div className="flex min-h-0 flex-1 flex-col gap-4 px-3 py-4">
<div className="space-y-2">
<Skeleton className="h-3 w-full rounded-full" />
<Skeleton className="h-3 w-full rounded-full" />
<Skeleton className="h-3 w-10/12 rounded-full" />
<Skeleton className="h-3 w-11/12 rounded-full" />
<Skeleton className="h-3 w-9/12 rounded-full" />
</div>
<span className="sr-only">{props.label}</span>
</div>
<div
className="min-h-0 flex-1 overflow-hidden bg-background"
role="status"
aria-live="polite"
aria-label={props.label}
>
<DiffFileHeaderSkeleton titleClassName="w-1/2 max-w-64" />
<div className="flex h-6 items-center gap-2 px-2 pr-3">
<div className="h-px flex-1 bg-border/40" />
<Skeleton className="h-2.5 w-24 rounded-full" />
<div className="h-px flex-1 bg-border/40" />
</div>
<div className="space-y-2 px-3 py-2">
<DiffCodeLineSkeleton contentClassName="w-2/3" />
<DiffCodeLineSkeleton contentClassName="w-4/5" />
<DiffCodeLineSkeleton contentClassName="w-3/5" />
</div>
<DiffFileHeaderSkeleton titleClassName="w-2/5 max-w-52" />
<DiffFileHeaderSkeleton titleClassName="w-3/5 max-w-72" />
<span className="sr-only">{props.label}</span>
</div>
);
}
Loading
Loading