From eb74dcecb305857f55f578f846c34e59acac6770 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Thu, 30 Apr 2026 00:16:27 -0700 Subject: [PATCH] style(desktop): polish v2 diff pane header, file path, and unmodified-lines strip - Match ChangesHeader chrome on each file header (border-y + bg-muted/30) - Split filename into dir + basename so the basename always stays visible on narrow widths instead of being truncated behind ellipsis - Add a Copy path button next to the filename, drop Copy file contents - Move StatusIndicator next to the +/- diff stats - Blend diff body with the terminal pane surface color - Flatten the "N unmodified lines" expander flush to the pane edges (kills pierre/diffs' wrapper / content / expand-button rounding + inline gaps for both line-info and line-info-basic separators) - Standardize icon-button padding, drop the inline DiffViewModeToggle in favour of a co-located DiffPaneHeaderExtras component --- .../StatusIndicator/StatusIndicator.tsx | 7 +- .../components/DiffPane/DiffPane.tsx | 6 +- .../DiffFileEntry/DiffFileEntry.tsx | 11 ++- .../DiffFileHeader/DiffFileHeader.tsx | 81 +++++++++++-------- .../DiffPaneHeaderExtras.tsx | 59 ++++++++++++++ .../components/DiffPaneHeaderExtras/index.ts | 1 + .../WorkspaceDiff/WorkspaceDiff.tsx | 73 ++++++++--------- .../gitDecorationColors.ts | 5 ++ .../utils/gitDecorationColors/index.ts | 1 + .../hooks/usePaneRegistry/usePaneRegistry.tsx | 61 +------------- 10 files changed, 164 insertions(+), 141 deletions(-) create mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffPaneHeaderExtras/DiffPaneHeaderExtras.tsx create mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffPaneHeaderExtras/index.ts create mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/utils/gitDecorationColors/gitDecorationColors.ts create mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/utils/gitDecorationColors/index.ts diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/StatusIndicator/StatusIndicator.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/StatusIndicator/StatusIndicator.tsx index 00144f5f8f4..70666c3c549 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/StatusIndicator/StatusIndicator.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/StatusIndicator/StatusIndicator.tsx @@ -26,8 +26,7 @@ const STATUS_COLORS: Record = { untracked: "text-green-700 dark:text-green-400", }; -function getStatusIcon(status: FileStatus): ReactNode { - const iconClass = "w-3 h-3"; +function getStatusIcon(status: FileStatus, iconClass: string): ReactNode { switch (status) { case "added": case "untracked": @@ -49,15 +48,17 @@ function getStatusIcon(status: FileStatus): ReactNode { export function StatusIndicator({ status, className, + iconClassName = "w-3 h-3", }: { status: string; className?: string; + iconClassName?: string; }) { return ( - {getStatusIcon(status as FileStatus)} + {getStatusIcon(status as FileStatus, iconClassName)} ); } diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/DiffPane.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/DiffPane.tsx index 2fc56144335..4feef6946d2 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/DiffPane.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/DiffPane.tsx @@ -98,10 +98,10 @@ export function DiffPane({ context, workspaceId, onOpenFile }: DiffPaneProps) { [updateData], ); - if (!isLoading && files.length === 0) { + if (files.length === 0) { return (
- No changes + {isLoading ? "Loading…" : "No changes"}
); } @@ -109,7 +109,7 @@ export function DiffPane({ context, workspaceId, onOpenFile }: DiffPaneProps) { return ( {files.map((file) => ( diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffFileEntry/DiffFileEntry.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffFileEntry/DiffFileEntry.tsx index 26b25700c50..c6151811231 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffFileEntry/DiffFileEntry.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffFileEntry/DiffFileEntry.tsx @@ -1,3 +1,4 @@ +import { Button } from "@superset/ui/button"; import { toast } from "@superset/ui/sonner"; import { memo, useCallback, useRef, useState } from "react"; import type { ChangesetFile } from "../../../../../useChangeset"; @@ -199,7 +200,7 @@ function DeferredDiffPlaceholder({ : `${(file.additions + file.deletions).toLocaleString()} changed lines`; return ( -
+
{subtitle}
)} - +
)} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffFileHeader/DiffFileHeader.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffFileHeader/DiffFileHeader.tsx index dd5cc3c497c..a38ac4cacf3 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffFileHeader/DiffFileHeader.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffFileHeader/DiffFileHeader.tsx @@ -2,11 +2,13 @@ import { Checkbox } from "@superset/ui/checkbox"; import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { ChevronDown, ChevronRight, Eye, EyeOff } from "lucide-react"; import { useId } from "react"; -import { LuCopy, LuUndo2 } from "react-icons/lu"; +import { LuCheck, LuCopy, LuUndo2 } from "react-icons/lu"; +import { useCopyToClipboard } from "renderer/hooks/useCopyToClipboard"; import { StatusIndicator } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/StatusIndicator"; import { CLICK_HINT_TOOLTIP } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/utils/clickModifierLabels"; import { getSidebarClickIntent } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/utils/getSidebarClickIntent"; import { FileIcon } from "renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/utils"; +import { GIT_STAT_TEXT_CLASSES } from "../../utils/gitDecorationColors"; interface DiffFileHeaderProps { path: string; @@ -21,7 +23,6 @@ interface DiffFileHeaderProps { onToggleViewed: () => void; onOpenFile?: (openInNewTab?: boolean) => void; onOpenInExternalEditor?: () => void; - onCopyContents?: () => void; onDiscard?: () => void; } @@ -38,18 +39,25 @@ export function DiffFileHeader({ onToggleViewed, onOpenFile, onOpenInExternalEditor, - onCopyContents, onDiscard, }: DiffFileHeaderProps) { const viewedId = useId(); + const { copyToClipboard, copied } = useCopyToClipboard(); + + // Split into directory + basename so the basename stays visible when the + // header is narrow — the directory truncates with ellipsis instead of + // hiding the filename behind it. + const lastSlash = path.lastIndexOf("/"); + const dir = lastSlash >= 0 ? path.slice(0, lastSlash + 1) : ""; + const name = lastSlash >= 0 ? path.slice(lastSlash + 1) : path; return ( -
+
- - + @@ -84,17 +96,37 @@ export function DiffFileHeader({ {CLICK_HINT_TOOLTIP} + + + + + + {copied ? "Copied" : "Copy path"} + +
+ {(additions > 0 || deletions > 0) && ( - + {additions > 0 && ( - + +{additions} )} {additions > 0 && deletions > 0 && " "} {deletions > 0 && ( - + -{deletions} )} @@ -110,7 +142,7 @@ export function DiffFileHeader({ /> @@ -125,7 +157,7 @@ export function DiffFileHeader({ aria-label={ expandUnchanged ? "Hide unchanged regions" : "Show all lines" } - className="rounded p-0.5 text-muted-foreground/60 transition-colors hover:bg-accent hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-40" + className="rounded p-1 text-muted-foreground/60 transition-colors hover:bg-accent hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-40" > {expandUnchanged ? ( @@ -139,23 +171,6 @@ export function DiffFileHeader({ - - - - - - Copy file contents - - - diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffPaneHeaderExtras/DiffPaneHeaderExtras.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffPaneHeaderExtras/DiffPaneHeaderExtras.tsx new file mode 100644 index 00000000000..55ec0941792 --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffPaneHeaderExtras/DiffPaneHeaderExtras.tsx @@ -0,0 +1,59 @@ +import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; +import { cn } from "@superset/ui/utils"; +import { SquareSplitHorizontal } from "lucide-react"; +import { TbScan } from "react-icons/tb"; +import { useSettings } from "renderer/stores/settings"; + +export function DiffPaneHeaderExtras() { + const diffStyle = useSettings((s) => s.diffStyle); + const updateSetting = useSettings((s) => s.update); + + const buttonClass = (active: boolean) => + cn( + "flex size-5 items-center justify-center transition-colors", + active + ? "bg-secondary text-foreground" + : "text-muted-foreground hover:text-foreground", + ); + + return ( +
+ + + + + + Unified view + + + + + + + + Split view + + + + ); +} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffPaneHeaderExtras/index.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffPaneHeaderExtras/index.ts new file mode 100644 index 00000000000..a382a3358d7 --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/DiffPaneHeaderExtras/index.ts @@ -0,0 +1 @@ +export { DiffPaneHeaderExtras } from "./DiffPaneHeaderExtras"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/WorkspaceDiff/WorkspaceDiff.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/WorkspaceDiff/WorkspaceDiff.tsx index 95be576d762..3a06ab7aae8 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/WorkspaceDiff/WorkspaceDiff.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/components/WorkspaceDiff/WorkspaceDiff.tsx @@ -3,13 +3,12 @@ import { toast } from "@superset/ui/sonner"; import { workspaceTrpc } from "@superset/workspace-client"; import { useQuery } from "@tanstack/react-query"; import { memo, useMemo } from "react"; -import { useCopyToClipboard } from "renderer/hooks/useCopyToClipboard"; import { electronTrpcClient } from "renderer/lib/trpc-client"; import { getDiffsTheme, getDiffViewerStyle, } from "renderer/screens/main/components/WorkspaceView/utils/code-theme"; -import { useResolvedTheme } from "renderer/stores/theme"; +import { useResolvedTheme, useTerminalTheme } from "renderer/stores/theme"; import type { DiffFileSource } from "../../../../../useChangeset"; import { DiffFileHeader } from "../DiffFileHeader"; @@ -49,6 +48,7 @@ export const WorkspaceDiff = memo(function WorkspaceDiff({ onOpenInExternalEditor, }: WorkspaceDiffProps) { const activeTheme = useResolvedTheme(); + const terminalTheme = useTerminalTheme(); const { data: fontSettings } = useQuery({ queryKey: ["electron", "settings", "getFontSettings"], queryFn: () => electronTrpcClient.settings.getFontSettings.query(), @@ -61,31 +61,18 @@ export const WorkspaceDiff = memo(function WorkspaceDiff({ : typeof fontSettings?.editorFontSize === "string" ? Number.parseFloat(fontSettings.editorFontSize) : Number.NaN; - const baseThemeVars = getDiffViewerStyle(activeTheme, { - fontFamily: fontSettings?.editorFontFamily ?? undefined, - fontSize: Number.isFinite(parsedEditorFontSize) - ? parsedEditorFontSize - : undefined, - }); - // Match the file tree's git decoration colors (v2 WorkspaceFilesTreeItem) - // so addition/deletion/modified highlights read the same across the pane. - const gitDecorationColors = - activeTheme.type === "dark" - ? { - addition: "var(--color-green-400)", - deletion: "var(--color-red-500)", - modified: "var(--color-yellow-400)", - } - : { - addition: "var(--color-green-700)", - deletion: "var(--color-red-700)", - modified: "var(--color-yellow-600)", - }; + // Match the terminal pane's surface color so the diff body blends with + // the chrome. The actual override happens in unsafeCSS below — this just + // paints the wrapper before the diff mounts. + const surfaceBg = terminalTheme?.background ?? "var(--background)"; const themeVars = { - ...baseThemeVars, - "--diffs-addition-color-override": gitDecorationColors.addition, - "--diffs-deletion-color-override": gitDecorationColors.deletion, - "--diffs-modified-color-override": gitDecorationColors.modified, + ...getDiffViewerStyle(activeTheme, { + fontFamily: fontSettings?.editorFontFamily ?? undefined, + fontSize: Number.isFinite(parsedEditorFontSize) + ? parsedEditorFontSize + : undefined, + }), + backgroundColor: surfaceBg, }; const diffInput = useMemo(() => { @@ -118,14 +105,6 @@ export const WorkspaceDiff = memo(function WorkspaceDiff({ }); const worktreePath = workspaceQuery.data?.worktreePath; - const { copyToClipboard } = useCopyToClipboard(); - const newContents = diffQuery.data?.newFile.contents; - const handleCopyContents = useMemo( - () => - newContents != null ? () => copyToClipboard(newContents) : undefined, - [newContents, copyToClipboard], - ); - const handleDiscard = useMemo(() => { if (source.kind !== "unstaged" || !worktreePath) return undefined; return () => { @@ -140,7 +119,7 @@ export const WorkspaceDiff = memo(function WorkspaceDiff({ }, [source.kind, worktreePath, path]); return ( -
+
{diffQuery.data ? ( @@ -171,9 +149,26 @@ export const WorkspaceDiff = memo(function WorkspaceDiff({ theme: shikiTheme, themeType: activeTheme.type, unsafeCSS: ` - * { - user-select: text; - -webkit-user-select: text; + * { user-select: text; -webkit-user-select: text; } + /* Pierre sets --diffs-light-bg/--diffs-dark-bg + * inline on
 from the Shiki theme;
+							 * inline beats :host so we override at the pre. */
+							[data-diff] {
+								--diffs-light-bg: ${surfaceBg} !important;
+								--diffs-dark-bg: ${surfaceBg} !important;
+							}
+							/* Flatten the "N unmodified lines" strip flush to
+							 * the pane edges (kills wrapper/content/expand-
+							 * button rounding + inline gap on both
+							 * line-info and line-info-basic). */
+							[data-separator^='line-info'] [data-separator-wrapper],
+							[data-separator^='line-info'] [data-separator-content],
+							[data-separator^='line-info'] [data-expand-up],
+							[data-separator^='line-info'] [data-expand-down],
+							[data-separator^='line-info'] [data-expand-both] {
+								border-radius: 0 !important;
+								margin-inline: 0 !important;
+								padding-inline: 0 !important;
 							}
 						`,
 					}}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/utils/gitDecorationColors/gitDecorationColors.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/utils/gitDecorationColors/gitDecorationColors.ts
new file mode 100644
index 00000000000..e3d6a2b4001
--- /dev/null
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/utils/gitDecorationColors/gitDecorationColors.ts
@@ -0,0 +1,5 @@
+export const GIT_STAT_TEXT_CLASSES = {
+	addition: "text-green-700 dark:text-green-400",
+	deletion: "text-red-700 dark:text-red-500",
+	modified: "text-yellow-600 dark:text-yellow-400",
+} as const;
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/utils/gitDecorationColors/index.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/utils/gitDecorationColors/index.ts
new file mode 100644
index 00000000000..0108a16f8b4
--- /dev/null
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/DiffPane/utils/gitDecorationColors/index.ts
@@ -0,0 +1 @@
+export { GIT_STAT_TEXT_CLASSES } from "./gitDecorationColors";
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/usePaneRegistry.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/usePaneRegistry.tsx
index 63783752be9..8dc3ea26272 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/usePaneRegistry.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/usePaneRegistry.tsx
@@ -5,7 +5,6 @@ import type {
 } from "@superset/panes";
 import { alert } from "@superset/ui/atoms/Alert";
 import { toast } from "@superset/ui/sonner";
-import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
 import { cn } from "@superset/ui/utils";
 import { workspaceTrpc } from "@superset/workspace-client";
 import {
@@ -13,7 +12,6 @@ import {
 	GitCompareArrows,
 	Globe,
 	MessageSquare,
-	SquareSplitHorizontal,
 	TerminalSquare,
 } from "lucide-react";
 import { useMemo } from "react";
@@ -24,13 +22,11 @@ import {
 	LuEraser,
 	LuPower,
 } from "react-icons/lu";
-import { TbScan } from "react-icons/tb";
 import { useHotkeyDisplay } from "renderer/hotkeys";
 import { getBaseName } from "renderer/lib/pathBasename";
 import { consumeTerminalBackgroundIntent } from "renderer/lib/terminal/terminal-background-intents";
 import { terminalRuntimeRegistry } from "renderer/lib/terminal/terminal-runtime-registry";
 import { FileIcon } from "renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/utils";
-import { useSettings } from "renderer/stores/settings";
 import { getV2NotificationSourcesForPane } from "renderer/stores/v2-notifications";
 import { V2NotificationStatusIndicator } from "../../components/V2NotificationStatusIndicator";
 import {
@@ -53,6 +49,7 @@ import { CommentPane } from "./components/CommentPane";
 import { CommentPaneHeaderExtras } from "./components/CommentPane/components/CommentPaneHeaderExtras";
 import { CommentPaneTitle } from "./components/CommentPane/components/CommentPaneTitle";
 import { DiffPane } from "./components/DiffPane";
+import { DiffPaneHeaderExtras } from "./components/DiffPane/components/DiffPaneHeaderExtras";
 import { FilePane } from "./components/FilePane";
 import { FilePaneHeaderExtras } from "./components/FilePane/components/FilePaneHeaderExtras";
 import { TerminalPane } from "./components/TerminalPane";
@@ -102,60 +99,6 @@ const MOD_KEY = navigator.platform.toLowerCase().includes("mac")
 	? "⌘"
 	: "Ctrl+";
 
-function DiffViewModeToggle() {
-	const diffStyle = useSettings((s) => s.diffStyle);
-	const updateSetting = useSettings((s) => s.update);
-
-	const buttonClass = (active: boolean) =>
-		cn(
-			"flex size-5 items-center justify-center transition-colors",
-			active
-				? "bg-secondary text-foreground"
-				: "text-muted-foreground hover:text-foreground",
-		);
-
-	return (
-		
- - - - - - Unified view - - - - - - - - Split view - - - - ); -} - interface UsePaneRegistryOptions { onOpenFile: (path: string, openInNewTab?: boolean) => void; onRevealPath: (path: string) => void; @@ -285,7 +228,7 @@ export function usePaneRegistry( onOpenFile={onOpenFile} /> ), - renderHeaderExtras: () => , + renderHeaderExtras: () => , contextMenuActions: (_ctx, defaults) => defaults.map((d) => d.key === "close-pane" ? { ...d, label: "Close Diff" } : d,