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 3ccae838bb8..8b61f4718c9 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 @@ -81,10 +81,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"}
); } @@ -92,7 +92,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 86f73da280a..5df29350807 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"; @@ -192,7 +193,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..052282b52b9 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 first, and the + // basename truncates only as a fallback (very narrow pane or no directory). + 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 58ce11876fc..2c1670e105f 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 @@ -1,15 +1,14 @@ import { MultiFileDiff } from "@pierre/diffs/react"; 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 { electronTrpc } from "renderer/lib/electron-trpc"; 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,10 +48,12 @@ export const WorkspaceDiff = memo(function WorkspaceDiff({ onOpenInExternalEditor, }: WorkspaceDiffProps) { const activeTheme = useResolvedTheme(); - const { data: fontSettings } = electronTrpc.settings.getFontSettings.useQuery( - undefined, - { staleTime: 30_000 }, - ); + const terminalTheme = useTerminalTheme(); + const { data: fontSettings } = useQuery({ + queryKey: ["electron", "settings", "getFontSettings"], + queryFn: () => electronTrpcClient.settings.getFontSettings.query(), + staleTime: 30_000, + }); const shikiTheme = getDiffsTheme(activeTheme); const parsedEditorFontSize = typeof fontSettings?.editorFontSize === "number" @@ -60,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(() => { @@ -117,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 utils = workspaceTrpc.useUtils(); const handleDiscard = useMemo(() => { if (source.kind !== "unstaged" || !worktreePath) return undefined; @@ -144,7 +124,7 @@ export const WorkspaceDiff = memo(function WorkspaceDiff({ }, [source.kind, worktreePath, path, workspaceId, utils]); return ( -
+
{diffQuery.data ? ( @@ -175,9 +154,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/components/FilePane/components/FilePaneHeaderExtras/FilePaneHeaderExtras.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/FilePane/components/FilePaneHeaderExtras/FilePaneHeaderExtras.tsx
index d2f98bc10e2..84ef843c29a 100644
--- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/FilePane/components/FilePaneHeaderExtras/FilePaneHeaderExtras.tsx
+++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/FilePane/components/FilePaneHeaderExtras/FilePaneHeaderExtras.tsx
@@ -1,7 +1,9 @@
 import type { RendererContext } from "@superset/panes";
 import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
 import { useCallback } from "react";
+import { LuCheck, LuCopy } from "react-icons/lu";
 import { TbExternalLink } from "react-icons/tb";
+import { useCopyToClipboard } from "renderer/hooks/useCopyToClipboard";
 import { useOpenInExternalEditor } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useOpenInExternalEditor";
 import { isSpreadsheetFile } from "shared/file-types";
 import { useSharedFileDocument } from "../../../../../../state/fileDocumentStore";
@@ -48,6 +50,7 @@ function FilePaneHeaderExtrasInner({
 	data: FilePaneData;
 }) {
 	const openInExternalEditor = useOpenInExternalEditor(workspaceId);
+	const { copyToClipboard, copied } = useCopyToClipboard();
 	const document = useSharedFileDocument({
 		workspaceId,
 		absolutePath: filePath,
@@ -81,6 +84,25 @@ function FilePaneHeaderExtrasInner({
 					onChange={handleChangeView}
 				/>
 			)}
+			
+				
+					
+				
+				
+					{copied ? "Copied" : "Copy path"}
+				
+			
 			
 				
 					
-				
-				
-					Unified view
-				
-			
-			
-				
-					
-				
-				
-					Split view
-				
-			
-			
-	);
-}
-
 interface UsePaneRegistryOptions {
 	onOpenFile: (path: string, openInNewTab?: boolean) => void;
 	onRevealPath: (path: string) => void;
@@ -348,7 +291,7 @@ export function usePaneRegistry(
 						onOpenFile={onOpenFile}
 					/>
 				),
-				renderHeaderExtras: () => ,
+				renderHeaderExtras: () => ,
 				contextMenuActions: (_ctx, defaults) =>
 					defaults.map((d) =>
 						d.key === "close-pane" ? { ...d, label: "Close Diff" } : d,