From 7e219d1b2e40080dcc32038a770694fab3986ec0 Mon Sep 17 00:00:00 2001 From: AviPeltz Date: Wed, 15 Apr 2026 06:14:21 -0700 Subject: [PATCH 1/9] fix(desktop): remove viewed checkboxes from v2 diff sidebar The sidebar file list no longer shows checkboxes for marking files as viewed. Removes the Checkbox component, viewed/onSetViewed props, and the partitionByViewed utility from the changes sidebar pipeline. --- .../ChangesFileList/ChangesFileList.tsx | 16 +---- .../components/FileRow/FileRow.tsx | 72 +++++++------------ .../utils/partitionByViewed/index.ts | 1 - .../partitionByViewed/partitionByViewed.ts | 15 ---- .../ChangesTabContent/ChangesTabContent.tsx | 6 -- .../hooks/useChangesTab/useChangesTab.tsx | 5 -- 6 files changed, 28 insertions(+), 87 deletions(-) delete mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/utils/partitionByViewed/index.ts delete mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/utils/partitionByViewed/partitionByViewed.ts diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/ChangesFileList.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/ChangesFileList.tsx index a5c3bb18c06..27eddd2062e 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/ChangesFileList.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/ChangesFileList.tsx @@ -1,28 +1,18 @@ -import { memo, useMemo } from "react"; +import { memo } from "react"; import type { ChangesetFile } from "../../../../../../hooks/useChangeset"; import { FileRow } from "./components/FileRow"; -import { partitionByViewed } from "./utils/partitionByViewed"; interface ChangesFileListProps { files: ChangesetFile[]; isLoading?: boolean; onSelectFile?: (path: string) => void; - viewedSet: Set; - onSetViewed: (path: string, next: boolean) => void; } export const ChangesFileList = memo(function ChangesFileList({ files, isLoading, onSelectFile, - viewedSet, - onSetViewed, }: ChangesFileListProps) { - const sortedFiles = useMemo( - () => partitionByViewed(files, viewedSet), - [files, viewedSet], - ); - if (isLoading) { return (
@@ -41,13 +31,11 @@ export const ChangesFileList = memo(function ChangesFileList({ return (
- {sortedFiles.map((file) => ( + {files.map((file) => ( ))}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/components/FileRow/FileRow.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/components/FileRow/FileRow.tsx index 39adbeb74be..67edb291ff5 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/components/FileRow/FileRow.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/components/FileRow/FileRow.tsx @@ -1,4 +1,3 @@ -import { Checkbox } from "@superset/ui/checkbox"; import { memo } from "react"; import { StatusIndicator } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/StatusIndicator"; import { FileIcon } from "renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/utils"; @@ -16,57 +15,38 @@ function splitPath(path: string): { dir: string; basename: string } { interface FileRowProps { file: ChangesetFile; onSelect?: (path: string) => void; - viewed: boolean; - onSetViewed: (path: string, next: boolean) => void; } -export const FileRow = memo(function FileRow({ - file, - onSelect, - viewed, - onSetViewed, -}: FileRowProps) { +export const FileRow = memo(function FileRow({ file, onSelect }: FileRowProps) { const { dir, basename } = splitPath(file.path); return ( -
onSelect?.(file.path)} > - onSetViewed(file.path, checked === true)} - className="size-3.5 shrink-0 border-muted-foreground/50" - aria-label={viewed ? "Mark as not viewed" : "Mark as viewed"} - /> - -
+ + + {(file.additions > 0 || file.deletions > 0) && ( + + {file.additions > 0 && ( + +{file.additions} + )} + {file.additions > 0 && file.deletions > 0 && " "} + {file.deletions > 0 && ( + -{file.deletions} + )} + + )} + + + ); }); diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/utils/partitionByViewed/index.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/utils/partitionByViewed/index.ts deleted file mode 100644 index afe616cabed..00000000000 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/utils/partitionByViewed/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { partitionByViewed } from "./partitionByViewed"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/utils/partitionByViewed/partitionByViewed.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/utils/partitionByViewed/partitionByViewed.ts deleted file mode 100644 index d7f66863733..00000000000 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesFileList/utils/partitionByViewed/partitionByViewed.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { ChangesetFile } from "../../../../../../../../hooks/useChangeset"; - -export function partitionByViewed( - files: ChangesetFile[], - viewedSet: Set, -): ChangesetFile[] { - if (viewedSet.size === 0) return files; - const unviewed: ChangesetFile[] = []; - const viewed: ChangesetFile[] = []; - for (const file of files) { - if (viewedSet.has(file.path)) viewed.push(file); - else unviewed.push(file); - } - return [...unviewed, ...viewed]; -} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesTabContent/ChangesTabContent.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesTabContent/ChangesTabContent.tsx index 8e48ffa4078..51169fa24ca 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesTabContent/ChangesTabContent.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/components/ChangesTabContent/ChangesTabContent.tsx @@ -26,8 +26,6 @@ interface ChangesTabContentProps { onBaseBranchChange: (branchName: string) => void; onRenameBranch: (newName: string) => void; canRenameBranch: boolean; - viewedSet: Set; - onSetViewed: (path: string, next: boolean) => void; } export const ChangesTabContent = memo(function ChangesTabContent({ @@ -45,8 +43,6 @@ export const ChangesTabContent = memo(function ChangesTabContent({ onBaseBranchChange, onRenameBranch, canRenameBranch, - viewedSet, - onSetViewed, }: ChangesTabContentProps) { if (status.isLoading) { return ( @@ -89,8 +85,6 @@ export const ChangesTabContent = memo(function ChangesTabContent({ files={files} isLoading={isLoading} onSelectFile={onSelectFile} - viewedSet={viewedSet} - onSetViewed={onSetViewed} />
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/useChangesTab.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/useChangesTab.tsx index a7518491114..0370625c04b 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/useChangesTab.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/hooks/useChangesTab/useChangesTab.tsx @@ -6,7 +6,6 @@ import { useCollections } from "renderer/routes/_authenticated/providers/Collect import type { ChangesFilter } from "renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema"; import { useChangeset } from "../../../../hooks/useChangeset"; import { useSidebarDiffRef } from "../../../../hooks/useSidebarDiffRef"; -import { useViewedFiles } from "../../../../hooks/useViewedFiles"; import type { SidebarTabDefinition } from "../../types"; import { ChangesTabContent } from "./components/ChangesTabContent"; @@ -31,8 +30,6 @@ export function useChangesTab({ const baseBranch: string | null = localState?.sidebarState?.baseBranch ?? null; - const { viewedSet, setViewed } = useViewedFiles(workspaceId); - const ref = useSidebarDiffRef(workspaceId); const { files, isLoading } = useChangeset({ workspaceId, ref }); @@ -111,8 +108,6 @@ export function useChangesTab({ onBaseBranchChange={setBaseBranch} onRenameBranch={handleRenameBranch} canRenameBranch={canRenameBranch} - viewedSet={viewedSet} - onSetViewed={setViewed} /> ); From e710c115cbd984de1e1bac83b56e1f2706b13c64 Mon Sep 17 00:00:00 2001 From: AviPeltz Date: Wed, 15 Apr 2026 06:19:29 -0700 Subject: [PATCH 2/9] feat(desktop): add icons to v2 sidebar tabs and nest Review under Changes Match the v1 sidebar layout: top-level tabs are Changes (GitCompareArrows icon) and Files (FileText icon). Review is now a subtab within Changes alongside Diffs, using the same Tabs/TabsTrigger pattern as v1. --- .../WorkspaceSidebar/WorkspaceSidebar.tsx | 78 ++++++++++++++++++- .../SidebarHeader/SidebarHeader.tsx | 1 + .../components/WorkspaceSidebar/types.ts | 3 +- 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx index 84167cdfd0f..daad2a1d376 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx @@ -1,6 +1,8 @@ import { Button } from "@superset/ui/button"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@superset/ui/tabs"; import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; -import { Search } from "lucide-react"; +import { cn } from "@superset/ui/utils"; +import { FileText, GitCompareArrows, Search } from "lucide-react"; import { useMemo, useState } from "react"; import { useGitStatus } from "renderer/hooks/host-service/useGitStatus"; import type { CommentPaneData } from "../../types"; @@ -10,6 +12,12 @@ import { useChangesTab } from "./hooks/useChangesTab"; import { useReviewTab } from "./hooks/useReviewTab"; import type { SidebarTabDefinition } from "./types"; +const subtabTriggerClassName = cn( + "flex h-full flex-none shrink-0 items-center gap-2 rounded-none border-0 bg-transparent px-3 text-sm font-normal shadow-none transition-all outline-none", + "data-[state=active]:bg-border/30 data-[state=active]:text-foreground data-[state=active]:shadow-none", + "data-[state=inactive]:text-muted-foreground/70 data-[state=inactive]:hover:bg-tertiary/20 data-[state=inactive]:hover:text-muted-foreground", +); + interface WorkspaceSidebarProps { onSelectFile: (absolutePath: string, openInNewTab?: boolean) => void; onSelectDiffFile?: (path: string) => void; @@ -55,7 +63,10 @@ export function WorkspaceSidebar({ workspaceId, workspaceName, }: WorkspaceSidebarProps) { - const [activeTab, setActiveTab] = useState("files"); + const [activeTab, setActiveTab] = useState("changes"); + const [changesSubtab, setChangesSubtab] = useState<"diffs" | "review">( + "diffs", + ); const gitStatus = useGitStatus(workspaceId); @@ -70,7 +81,8 @@ export function WorkspaceSidebar({ const filesTab: SidebarTabDefinition = useMemo( () => ({ id: "files", - label: "All files", + label: "Files", + icon: FileText, actions: , content: ( ({ + id: "changes", + label: "Changes", + icon: GitCompareArrows, + badge: changesTab.badge, + actions: + changesSubtab === "diffs" ? changesTab.actions : reviewTab.actions, + content: ( + setChangesSubtab(v as "diffs" | "review")} + className="flex min-h-0 flex-1 flex-col gap-0" + > +
+ + + Diffs + {changesTab.badge != null && ( + + {changesTab.badge} + + )} + + + Review + {reviewTab.badge != null && reviewTab.badge > 0 && ( + + {reviewTab.badge} + + )} + + +
+ + {changesTab.content} + + + {reviewTab.content} + +
+ ), + }), + [changesTab, reviewTab, changesSubtab], + ); + + const tabs = [combinedChangesTab, filesTab]; const activeTabDef = tabs.find((t) => t.id === activeTab); return ( diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx index 0ca94f1a1ac..4d3ee252dc1 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx @@ -29,6 +29,7 @@ export function SidebarHeader({ : "text-muted-foreground/70 hover:text-muted-foreground hover:bg-tertiary/20", )} > + {tab.icon && } {tab.label} {tab.badge != null && ( {tab.badge} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/types.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/types.ts index d771451be48..01f1ed5d73b 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/types.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/types.ts @@ -1,8 +1,9 @@ -import type { ReactNode } from "react"; +import type { ComponentType, ReactNode } from "react"; export interface SidebarTabDefinition { id: string; label: string; + icon?: ComponentType<{ className?: string }>; badge?: number; actions?: ReactNode; content: ReactNode; From 0b6c76240c5fddc936f799d457faed96e3668ded Mon Sep 17 00:00:00 2001 From: AviPeltz Date: Wed, 15 Apr 2026 06:21:34 -0700 Subject: [PATCH 3/9] fix(desktop): use v1 icons and styles for v2 sidebar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use LuGitCompareArrows and LuFile from react-icons/lu (same as v1), getSidebarHeaderTabButtonClassName for the top-level tab buttons, and sidebarHeaderTabTriggerClassName for the Diffs/Review subtabs — matching the v1 sidebar layout and styling exactly. --- .../WorkspaceSidebar/WorkspaceSidebar.tsx | 24 ++++++++++--------- .../SidebarHeader/SidebarHeader.tsx | 19 ++++++--------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx index daad2a1d376..1d01dc702b0 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx @@ -2,9 +2,11 @@ import { Button } from "@superset/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@superset/ui/tabs"; import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { cn } from "@superset/ui/utils"; -import { FileText, GitCompareArrows, Search } from "lucide-react"; +import { Search } from "lucide-react"; import { useMemo, useState } from "react"; +import { LuFile, LuGitCompareArrows } from "react-icons/lu"; import { useGitStatus } from "renderer/hooks/host-service/useGitStatus"; +import { sidebarHeaderTabTriggerClassName } from "renderer/screens/main/components/WorkspaceView/RightSidebar/headerTabStyles"; import type { CommentPaneData } from "../../types"; import { FilesTab } from "./components/FilesTab"; import { SidebarHeader } from "./components/SidebarHeader"; @@ -12,12 +14,6 @@ import { useChangesTab } from "./hooks/useChangesTab"; import { useReviewTab } from "./hooks/useReviewTab"; import type { SidebarTabDefinition } from "./types"; -const subtabTriggerClassName = cn( - "flex h-full flex-none shrink-0 items-center gap-2 rounded-none border-0 bg-transparent px-3 text-sm font-normal shadow-none transition-all outline-none", - "data-[state=active]:bg-border/30 data-[state=active]:text-foreground data-[state=active]:shadow-none", - "data-[state=inactive]:text-muted-foreground/70 data-[state=inactive]:hover:bg-tertiary/20 data-[state=inactive]:hover:text-muted-foreground", -); - interface WorkspaceSidebarProps { onSelectFile: (absolutePath: string, openInNewTab?: boolean) => void; onSelectDiffFile?: (path: string) => void; @@ -82,7 +78,7 @@ export function WorkspaceSidebar({ () => ({ id: "files", label: "Files", - icon: FileText, + icon: LuFile, actions: , content: ( ({ id: "changes", label: "Changes", - icon: GitCompareArrows, + icon: LuGitCompareArrows, badge: changesTab.badge, actions: changesSubtab === "diffs" ? changesTab.actions : reviewTab.actions, @@ -122,7 +118,10 @@ export function WorkspaceSidebar({ Diffs {changesTab.badge != null && ( @@ -133,7 +132,10 @@ export function WorkspaceSidebar({ Review {reviewTab.badge != null && reviewTab.badge > 0 && ( diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx index 4d3ee252dc1..7e7ac9367e4 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx @@ -1,4 +1,4 @@ -import { cn } from "@superset/ui/utils"; +import { getSidebarHeaderTabButtonClassName } from "renderer/screens/main/components/WorkspaceView/RightSidebar/headerTabStyles"; import type { SidebarTabDefinition } from "../../types"; interface SidebarHeaderProps { @@ -16,29 +16,24 @@ export function SidebarHeader({ return (
-
+
{tabs.map((tab) => ( ))}
+
{actions && ( -
{actions}
+
{actions}
)}
); From 966451df998dd9d77f32e573f4a939854b5d3ee7 Mon Sep 17 00:00:00 2001 From: AviPeltz Date: Wed, 15 Apr 2026 06:22:55 -0700 Subject: [PATCH 4/9] fix(desktop): center empty review tab content vertically Make the tab content wrapper a flex column so TabsContent can fill the available height, allowing the "Open a pull request" message to center vertically instead of sticking to the top. --- .../components/WorkspaceSidebar/WorkspaceSidebar.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx index 1d01dc702b0..cd9a2bfd9d7 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx @@ -174,7 +174,9 @@ export function WorkspaceSidebar({ activeTab={activeTab} onTabChange={setActiveTab} /> -
{activeTabDef?.content}
+
+ {activeTabDef?.content} +
); } From 933fc197f9f89983f2be03e309cf8b07d0a7ecd1 Mon Sep 17 00:00:00 2001 From: AviPeltz Date: Wed, 15 Apr 2026 06:24:19 -0700 Subject: [PATCH 5/9] feat(desktop): compact icon-only tabs when v2 sidebar is narrow When the sidebar width drops below 250px, the Changes and Files tabs collapse to icon-only buttons with tooltips, matching v1 behavior. Uses the same getSidebarHeaderTabButtonClassName compact mode. --- .../WorkspaceSidebar/WorkspaceSidebar.tsx | 20 +++++++- .../SidebarHeader/SidebarHeader.tsx | 46 +++++++++++++------ 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx index cd9a2bfd9d7..031e100b43d 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx @@ -3,7 +3,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@superset/ui/tabs"; import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { cn } from "@superset/ui/utils"; import { Search } from "lucide-react"; -import { useMemo, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { LuFile, LuGitCompareArrows } from "react-icons/lu"; import { useGitStatus } from "renderer/hooks/host-service/useGitStatus"; import { sidebarHeaderTabTriggerClassName } from "renderer/screens/main/components/WorkspaceView/RightSidebar/headerTabStyles"; @@ -64,6 +64,18 @@ export function WorkspaceSidebar({ "diffs", ); + const containerRef = useRef(null); + const [compact, setCompact] = useState(false); + useEffect(() => { + const el = containerRef.current; + if (!el) return; + const ro = new ResizeObserver(([entry]) => { + if (entry) setCompact(entry.contentRect.width < 250); + }); + ro.observe(el); + return () => ro.disconnect(); + }, []); + const gitStatus = useGitStatus(workspaceId); const changesTab = useChangesTab({ @@ -168,11 +180,15 @@ export function WorkspaceSidebar({ const activeTabDef = tabs.find((t) => t.id === activeTab); return ( -
+
{activeTabDef?.content} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx index 7e7ac9367e4..862593f8ac1 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx @@ -1,3 +1,4 @@ +import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { getSidebarHeaderTabButtonClassName } from "renderer/screens/main/components/WorkspaceView/RightSidebar/headerTabStyles"; import type { SidebarTabDefinition } from "../../types"; @@ -5,31 +6,50 @@ interface SidebarHeaderProps { tabs: SidebarTabDefinition[]; activeTab: string; onTabChange: (id: string) => void; + compact?: boolean; } export function SidebarHeader({ tabs, activeTab, onTabChange, + compact, }: SidebarHeaderProps) { const actions = tabs.find((t) => t.id === activeTab)?.actions; return (
- {tabs.map((tab) => ( - - ))} + {tabs.map((tab) => { + const isActive = activeTab === tab.id; + const btn = ( + + ); + + if (compact) { + return ( + + {btn} + + {tab.label} + + + ); + } + + return btn; + })}
{actions && ( From f689c2712fdd441d32b0198e8c829bda3239af3a Mon Sep 17 00:00:00 2001 From: AviPeltz Date: Wed, 15 Apr 2026 06:25:11 -0700 Subject: [PATCH 6/9] fix(desktop): lower compact sidebar breakpoint to 200px --- .../components/WorkspaceSidebar/WorkspaceSidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx index 031e100b43d..08b11abc59f 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx @@ -70,7 +70,7 @@ export function WorkspaceSidebar({ const el = containerRef.current; if (!el) return; const ro = new ResizeObserver(([entry]) => { - if (entry) setCompact(entry.contentRect.width < 250); + if (entry) setCompact(entry.contentRect.width < 200); }); ro.observe(el); return () => ro.disconnect(); From 84f97bbce3973ee06968e2e48c768a21275f0ca5 Mon Sep 17 00:00:00 2001 From: AviPeltz Date: Wed, 15 Apr 2026 06:27:16 -0700 Subject: [PATCH 7/9] feat(desktop): persist v2 sidebar active tab and subtab across sessions Store activeTab (Changes/Files) and changesSubtab (Diffs/Review) in the workspace local state collection so the sidebar reopens to the same tab the user last had open. --- .../WorkspaceSidebar/WorkspaceSidebar.tsx | 31 ++++++++++++++++--- .../dashboardSidebarLocal/schema.ts | 2 ++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx index 08b11abc59f..6f7c2149711 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx @@ -3,9 +3,10 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@superset/ui/tabs"; import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { cn } from "@superset/ui/utils"; import { Search } from "lucide-react"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { LuFile, LuGitCompareArrows } from "react-icons/lu"; import { useGitStatus } from "renderer/hooks/host-service/useGitStatus"; +import { useCollections } from "renderer/routes/_authenticated/providers/CollectionsProvider"; import { sidebarHeaderTabTriggerClassName } from "renderer/screens/main/components/WorkspaceView/RightSidebar/headerTabStyles"; import type { CommentPaneData } from "../../types"; import { FilesTab } from "./components/FilesTab"; @@ -59,9 +60,29 @@ export function WorkspaceSidebar({ workspaceId, workspaceName, }: WorkspaceSidebarProps) { - const [activeTab, setActiveTab] = useState("changes"); - const [changesSubtab, setChangesSubtab] = useState<"diffs" | "review">( - "diffs", + const collections = useCollections(); + const localState = collections.v2WorkspaceLocalState.get(workspaceId); + const activeTab = localState?.sidebarState?.activeTab ?? "changes"; + const changesSubtab = localState?.sidebarState?.changesSubtab ?? "diffs"; + + const setActiveTab = useCallback( + (tab: string) => { + if (!collections.v2WorkspaceLocalState.get(workspaceId)) return; + collections.v2WorkspaceLocalState.update(workspaceId, (draft) => { + draft.sidebarState.activeTab = tab; + }); + }, + [collections, workspaceId], + ); + + const setChangesSubtab = useCallback( + (subtab: "diffs" | "review") => { + if (!collections.v2WorkspaceLocalState.get(workspaceId)) return; + collections.v2WorkspaceLocalState.update(workspaceId, (draft) => { + draft.sidebarState.changesSubtab = subtab; + }); + }, + [collections, workspaceId], ); const containerRef = useRef(null); @@ -173,7 +194,7 @@ export function WorkspaceSidebar({ ), }), - [changesTab, reviewTab, changesSubtab], + [changesTab, reviewTab, changesSubtab, setChangesSubtab], ); const tabs = [combinedChangesTab, filesTab]; diff --git a/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.ts b/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.ts index d715b8b2cc0..cbbc76e16bc 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.ts @@ -37,6 +37,8 @@ export const workspaceLocalStateSchema = z.object({ sectionId: z.string().uuid().nullable().default(null), changesFilter: changesFilterSchema.default({ kind: "all" }), baseBranch: z.string().nullable().default(null), + activeTab: z.string().default("changes"), + changesSubtab: z.enum(["diffs", "review"]).default("diffs"), }), paneLayout: paneWorkspaceStateSchema, rightSidebarOpen: z.boolean().default(false), From 735e7293e05adb4d8b69d36137b8a741ab8effee Mon Sep 17 00:00:00 2001 From: AviPeltz Date: Wed, 15 Apr 2026 09:08:43 -0700 Subject: [PATCH 8/9] fix(desktop): code review fixes for v2 sidebar - Remove duplicate React key on compact tab button (SidebarHeader) - Stabilize setActiveTab/setChangesSubtab callbacks via refs so they don't bust the combinedChangesTab useMemo on every collection update - Narrow activeTab schema from z.string() to z.enum(["changes","files"]) with a runtime guard in the setter --- .../WorkspaceSidebar/WorkspaceSidebar.tsx | 41 +++++++++++-------- .../SidebarHeader/SidebarHeader.tsx | 13 +++++- .../dashboardSidebarLocal/schema.ts | 2 +- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx index 6f7c2149711..29620ce3d82 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx @@ -65,25 +65,30 @@ export function WorkspaceSidebar({ const activeTab = localState?.sidebarState?.activeTab ?? "changes"; const changesSubtab = localState?.sidebarState?.changesSubtab ?? "diffs"; - const setActiveTab = useCallback( - (tab: string) => { - if (!collections.v2WorkspaceLocalState.get(workspaceId)) return; - collections.v2WorkspaceLocalState.update(workspaceId, (draft) => { - draft.sidebarState.activeTab = tab; - }); - }, - [collections, workspaceId], - ); + // Stable refs so the callbacks never bust downstream memos. + const collectionsRef = useRef(collections); + collectionsRef.current = collections; + const workspaceIdRef = useRef(workspaceId); + workspaceIdRef.current = workspaceId; - const setChangesSubtab = useCallback( - (subtab: "diffs" | "review") => { - if (!collections.v2WorkspaceLocalState.get(workspaceId)) return; - collections.v2WorkspaceLocalState.update(workspaceId, (draft) => { - draft.sidebarState.changesSubtab = subtab; - }); - }, - [collections, workspaceId], - ); + const setActiveTab = useCallback((tab: string) => { + if (tab !== "changes" && tab !== "files") return; + const c = collectionsRef.current; + const id = workspaceIdRef.current; + if (!c.v2WorkspaceLocalState.get(id)) return; + c.v2WorkspaceLocalState.update(id, (draft) => { + draft.sidebarState.activeTab = tab; + }); + }, []); + + const setChangesSubtab = useCallback((subtab: "diffs" | "review") => { + const c = collectionsRef.current; + const id = workspaceIdRef.current; + if (!c.v2WorkspaceLocalState.get(id)) return; + c.v2WorkspaceLocalState.update(id, (draft) => { + draft.sidebarState.changesSubtab = subtab; + }); + }, []); const containerRef = useRef(null); const [compact, setCompact] = useState(false); diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx index 862593f8ac1..a30d54783a6 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/SidebarHeader/SidebarHeader.tsx @@ -24,7 +24,6 @@ export function SidebarHeader({ const isActive = activeTab === tab.id; const btn = ( + ); })}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.ts b/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.ts index cbbc76e16bc..20be475acf2 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/dashboardSidebarLocal/schema.ts @@ -37,7 +37,7 @@ export const workspaceLocalStateSchema = z.object({ sectionId: z.string().uuid().nullable().default(null), changesFilter: changesFilterSchema.default({ kind: "all" }), baseBranch: z.string().nullable().default(null), - activeTab: z.string().default("changes"), + activeTab: z.enum(["changes", "files"]).default("changes"), changesSubtab: z.enum(["diffs", "review"]).default("diffs"), }), paneLayout: paneWorkspaceStateSchema, From 962c42644bba119f0df572f27163b80cb10159cc Mon Sep 17 00:00:00 2001 From: AviPeltz Date: Wed, 15 Apr 2026 18:51:50 -0700 Subject: [PATCH 9/9] refactor(desktop): remove unnecessary useMemo/useCallback/refs from WorkspaceSidebar No memo boundaries downstream benefit from stable references, so plain functions and plain objects are simpler and equivalent. --- .../WorkspaceSidebar/WorkspaceSidebar.tsx | 188 ++++++++---------- 1 file changed, 82 insertions(+), 106 deletions(-) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx index 29620ce3d82..dd6f1d61907 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/WorkspaceSidebar.tsx @@ -3,7 +3,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@superset/ui/tabs"; import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { cn } from "@superset/ui/utils"; import { Search } from "lucide-react"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { LuFile, LuGitCompareArrows } from "react-icons/lu"; import { useGitStatus } from "renderer/hooks/host-service/useGitStatus"; import { useCollections } from "renderer/routes/_authenticated/providers/CollectionsProvider"; @@ -65,30 +65,20 @@ export function WorkspaceSidebar({ const activeTab = localState?.sidebarState?.activeTab ?? "changes"; const changesSubtab = localState?.sidebarState?.changesSubtab ?? "diffs"; - // Stable refs so the callbacks never bust downstream memos. - const collectionsRef = useRef(collections); - collectionsRef.current = collections; - const workspaceIdRef = useRef(workspaceId); - workspaceIdRef.current = workspaceId; - - const setActiveTab = useCallback((tab: string) => { + function setActiveTab(tab: string) { if (tab !== "changes" && tab !== "files") return; - const c = collectionsRef.current; - const id = workspaceIdRef.current; - if (!c.v2WorkspaceLocalState.get(id)) return; - c.v2WorkspaceLocalState.update(id, (draft) => { + if (!collections.v2WorkspaceLocalState.get(workspaceId)) return; + collections.v2WorkspaceLocalState.update(workspaceId, (draft) => { draft.sidebarState.activeTab = tab; }); - }, []); + } - const setChangesSubtab = useCallback((subtab: "diffs" | "review") => { - const c = collectionsRef.current; - const id = workspaceIdRef.current; - if (!c.v2WorkspaceLocalState.get(id)) return; - c.v2WorkspaceLocalState.update(id, (draft) => { + function setChangesSubtab(subtab: "diffs" | "review") { + if (!collections.v2WorkspaceLocalState.get(workspaceId)) return; + collections.v2WorkspaceLocalState.update(workspaceId, (draft) => { draft.sidebarState.changesSubtab = subtab; }); - }, []); + } const containerRef = useRef(null); const [compact, setCompact] = useState(false); @@ -112,95 +102,81 @@ export function WorkspaceSidebar({ const reviewTab = useReviewTab({ workspaceId, onOpenComment }); - const filesTab: SidebarTabDefinition = useMemo( - () => ({ - id: "files", - label: "Files", - icon: LuFile, - actions: , - content: ( - - ), - }), - [ - gitStatus.data, - onSearch, - onSelectFile, - selectedFilePath, - workspaceId, - workspaceName, - ], - ); + const filesTab: SidebarTabDefinition = { + id: "files", + label: "Files", + icon: LuFile, + actions: , + content: ( + + ), + }; - const combinedChangesTab: SidebarTabDefinition = useMemo( - () => ({ - id: "changes", - label: "Changes", - icon: LuGitCompareArrows, - badge: changesTab.badge, - actions: - changesSubtab === "diffs" ? changesTab.actions : reviewTab.actions, - content: ( - setChangesSubtab(v as "diffs" | "review")} - className="flex min-h-0 flex-1 flex-col gap-0" + const combinedChangesTab: SidebarTabDefinition = { + id: "changes", + label: "Changes", + icon: LuGitCompareArrows, + badge: changesTab.badge, + actions: changesSubtab === "diffs" ? changesTab.actions : reviewTab.actions, + content: ( + setChangesSubtab(v as "diffs" | "review")} + className="flex min-h-0 flex-1 flex-col gap-0" + > +
+ + + Diffs + {changesTab.badge != null && ( + + {changesTab.badge} + + )} + + + Review + {reviewTab.badge != null && reviewTab.badge > 0 && ( + + {reviewTab.badge} + + )} + + +
+ -
- - - Diffs - {changesTab.badge != null && ( - - {changesTab.badge} - - )} - - - Review - {reviewTab.badge != null && reviewTab.badge > 0 && ( - - {reviewTab.badge} - - )} - - -
- - {changesTab.content} - - - {reviewTab.content} - -
- ), - }), - [changesTab, reviewTab, changesSubtab, setChangesSubtab], - ); + {changesTab.content} + + + {reviewTab.content} + +
+ ), + }; const tabs = [combinedChangesTab, filesTab]; const activeTabDef = tabs.find((t) => t.id === activeTab);