From a009678f113b4e631478e85b316b99ed30c5dd70 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Tue, 21 Jul 2026 00:24:04 -0700 Subject: [PATCH 1/5] feat(desktop): reorganize sidebar and workspace header chrome - Presets bar: remove bottom border, taller bar with more chip spacing - Move run button from presets bar into the tab bar trailing slot (drop TopBar portal fallback) - Move open-in button from TopBar into the PR action header; match chevron sizes - Files tab: search button next to New File, drop Explorer title - Dashboard sidebar: New Workspace first, hide recently-viewed button, help menu into org dropdown - Hide workspace diff stats unless active; remove section/project workspace counts - Inverted tab styling in shared panes tab bar --- .../DashboardSidebar/DashboardSidebar.tsx | 70 +++--- .../DashboardSidebarHeader.tsx | 213 +++++++++++------- .../DashboardSidebarHelpMenu.tsx | 130 ----------- .../DashboardSidebarHelpMenu/index.ts | 1 - .../DashboardSidebarProjectSection.tsx | 1 - .../DashboardSidebarProjectRow.tsx | 5 - .../DashboardSidebarSectionHeader.tsx | 11 +- .../DashboardSidebarExpandedWorkspaceRow.tsx | 1 + .../NavigationControls/NavigationControls.tsx | 23 +- .../_dashboard/components/TopBar/TopBar.tsx | 16 +- .../OrganizationDropdown.tsx | 149 ++++++------ .../components/HelpSubMenu/HelpSubMenu.tsx | 91 ++++++++ .../components/HelpSubMenu/index.ts | 1 + .../SubmitPromptDialog/SubmitPromptDialog.tsx | 0 .../components/SubmitPromptDialog/index.ts | 0 .../V2OpenInMenuButton/V2OpenInMenuButton.tsx | 2 +- .../components/V2PresetsBar/V2PresetsBar.tsx | 15 +- .../WorkspaceSidebar/WorkspaceSidebar.tsx | 31 +-- .../components/FilesTab/FilesTab.tsx | 13 +- .../PRActionHeader/PRActionHeader.tsx | 6 +- .../SidebarHeader/SidebarHeader.tsx | 3 +- .../ChangesTabContent/ChangesTabContent.tsx | 54 ++--- .../ChangesToolbar/ChangesToolbar.tsx | 8 +- .../TerminalPaneHeaderExtras.tsx | 4 - .../TerminalSessionDropdown.tsx | 6 +- .../v2-workspace/$workspaceId/page.tsx | 19 +- .../RightSidebar/headerTabStyles.ts | 19 +- .../Workspace/components/TabBar/TabBar.tsx | 6 +- .../TabBar/components/TabItem/TabItem.tsx | 12 +- 29 files changed, 423 insertions(+), 487 deletions(-) delete mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHelpMenu/DashboardSidebarHelpMenu.tsx delete mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHelpMenu/index.ts create mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OrganizationDropdown/components/HelpSubMenu/HelpSubMenu.tsx create mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/OrganizationDropdown/components/HelpSubMenu/index.ts rename apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/{DashboardSidebar/components/DashboardSidebarHelpMenu => TopBar/components/OrganizationDropdown}/components/SubmitPromptDialog/SubmitPromptDialog.tsx (100%) rename apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/{DashboardSidebar/components/DashboardSidebarHelpMenu => TopBar/components/OrganizationDropdown}/components/SubmitPromptDialog/index.ts (100%) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/DashboardSidebar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/DashboardSidebar.tsx index 38938f70224..08e34a9114a 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/DashboardSidebar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/DashboardSidebar.tsx @@ -26,11 +26,11 @@ import { createPortal } from "react-dom"; import { HiOutlineCog6Tooth } from "react-icons/hi2"; import { UpdatesPill } from "renderer/components/UpdatesPill"; import { useHotkeyDisplay } from "renderer/hotkeys"; +import { OrganizationDropdown } from "renderer/routes/_authenticated/_dashboard/components/TopBar/components/OrganizationDropdown"; import { useDashboardSidebarState } from "renderer/routes/_authenticated/hooks/useDashboardSidebarState"; import { useLocalHostService } from "renderer/routes/_authenticated/providers/LocalHostServiceProvider"; import { useInlineWorkspacePortsEnabled } from "renderer/stores/inline-workspace-ports"; import { DashboardSidebarHeader } from "./components/DashboardSidebarHeader"; -import { DashboardSidebarHelpMenu } from "./components/DashboardSidebarHelpMenu"; import { DashboardSidebarHoverCardOverlay } from "./components/DashboardSidebarHoverCardOverlay"; import { DashboardSidebarPortsList } from "./components/DashboardSidebarPortsList"; import { DashboardSidebarProjectSection } from "./components/DashboardSidebarProjectSection"; @@ -253,52 +253,36 @@ export function DashboardSidebar({ )} > {isCollapsed ? ( - - - - - Settings - + ) : ( - +
+ +
)} - + + + + + + {settingsHotkey !== "Unassigned" + ? `Settings (${settingsHotkey})` + : "Settings"} + + diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsx index a52cd430835..890b71b8ce8 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsx @@ -8,6 +8,7 @@ import { toast } from "@superset/ui/sonner"; import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { cn } from "@superset/ui/utils"; import { useMatchRoute, useNavigate } from "@tanstack/react-router"; +import { useRef } from "react"; import { HiMiniPlus, HiOutlineClipboardDocumentList } from "react-icons/hi2"; import { LuClock, @@ -16,7 +17,9 @@ import { LuLayers, LuLayoutTemplate, LuPlus, + LuSearch, } from "react-icons/lu"; +import { useFrameStackStore } from "renderer/commandPalette"; import { GATED_FEATURES, usePaywall } from "renderer/components/Paywall"; import { ZoomStable } from "renderer/components/ZoomStable"; import { useZoomFactor } from "renderer/hooks/useZoomFactor"; @@ -25,7 +28,6 @@ import { electronTrpc } from "renderer/lib/electron-trpc"; import { useFolderFirstImport } from "renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/hooks/useFolderFirstImport"; import { NavigationControls } from "renderer/routes/_authenticated/_dashboard/components/NavigationControls"; import { SidebarToggle } from "renderer/routes/_authenticated/_dashboard/components/SidebarToggle"; -import { OrganizationDropdown } from "renderer/routes/_authenticated/_dashboard/components/TopBar/components/OrganizationDropdown"; import { ResourceConsumption } from "renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption"; import { useFailedAutomations } from "renderer/routes/_authenticated/_dashboard/hooks/useFailedAutomations"; import { @@ -39,6 +41,9 @@ import { } from "renderer/stores/add-repository-modal"; import { useOpenNewWorkspaceModal } from "renderer/stores/new-workspace-modal"; +// Temporarily hidden, not removed — flip to true to restore the Workspaces tab. +const SHOW_WORKSPACES_TAB = false; + interface DashboardSidebarHeaderProps { isCollapsed?: boolean; } @@ -73,6 +78,19 @@ export function DashboardSidebarHeader({ }; const shortcutText = useHotkeyDisplay("NEW_WORKSPACE").text; + const searchShortcutText = useHotkeyDisplay("OPEN_COMMAND_PALETTE").text; + const openCommandPalette = useFrameStackStore((s) => s.setOpen); + // The palette dialog dismisses on outside pointerdown before our click fires, + // so a live-state toggle would always reopen it. Capture the state at + // pointerdown to make clicking the button close an open palette. + const paletteWasOpenRef = useRef(false); + const handleSearchPointerDown = () => { + paletteWasOpenRef.current = useFrameStackStore.getState().open; + }; + const handleSearchClick = () => { + openCommandPalette(!paletteWasOpenRef.current); + paletteWasOpenRef.current = false; + }; const { data: platform } = electronTrpc.window.getPlatform.useQuery(); // Default to Mac while loading so we don't briefly cover the traffic lights. const isMac = platform === undefined || platform === "darwin"; @@ -120,26 +138,59 @@ export function DashboardSidebarHeader({ if (isCollapsed) { return (
- + + + + + + New Workspace ({shortcutText}) + + - Workspaces + + {searchShortcutText !== "Unassigned" + ? `Search (${searchShortcutText})` + : "Search"} + + {SHOW_WORKSPACES_TAB && ( + + + + + Workspaces + + )} + - - - New Workspace ({shortcutText}) - - - @@ -272,62 +308,9 @@ export function DashboardSidebarHeader({ - -
- - - - - - -
+ + + + {SHOW_WORKSPACES_TAB && ( + + )} + + + + ); } diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHelpMenu/DashboardSidebarHelpMenu.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHelpMenu/DashboardSidebarHelpMenu.tsx deleted file mode 100644 index d172cd4327d..00000000000 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHelpMenu/DashboardSidebarHelpMenu.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { COMPANY } from "@superset/shared/constants"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuShortcut, - DropdownMenuSub, - DropdownMenuSubContent, - DropdownMenuSubTrigger, - DropdownMenuTrigger, -} from "@superset/ui/dropdown-menu"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; -import { useNavigate } from "@tanstack/react-router"; -import { useState } from "react"; -import { FaDiscord, FaGithub, FaXTwitter } from "react-icons/fa6"; -import { - HiOutlineBookOpen, - HiOutlineChatBubbleLeftRight, - HiOutlineEnvelope, - HiOutlineQuestionMarkCircle, -} from "react-icons/hi2"; -import { IoBugOutline } from "react-icons/io5"; -import { LuKeyboard, LuMegaphone } from "react-icons/lu"; -import { useHotkeyDisplay } from "renderer/hotkeys"; -import { electronTrpc } from "renderer/lib/electron-trpc"; -import { SubmitPromptDialog } from "./components/SubmitPromptDialog"; - -interface DashboardSidebarHelpMenuProps { - isCollapsed: boolean; -} - -export function DashboardSidebarHelpMenu({ - isCollapsed, -}: DashboardSidebarHelpMenuProps) { - const navigate = useNavigate(); - const shortcutsHotkey = useHotkeyDisplay("SHOW_HOTKEYS").text; - const [submitPromptOpen, setSubmitPromptOpen] = useState(false); - const openUrlMutation = electronTrpc.external.openUrl.useMutation(); - - const openExternal = (url: string) => { - openUrlMutation.mutate(url); - }; - - const triggerButton = ( - - ); - - return ( - <> - - {isCollapsed ? ( - - - {triggerButton} - - Help - - ) : ( - {triggerButton} - )} - - setSubmitPromptOpen(true)}> - - Submit a prompt - - openExternal(COMPANY.DOCS_URL)}> - - Documentation - - navigate({ to: "/settings/keyboard" })} - > - - Keyboard Shortcuts - {shortcutsHotkey !== "Unassigned" && ( - {shortcutsHotkey} - )} - - openExternal(COMPANY.REPORT_ISSUE_URL)} - > - - Report Issue - - - - - Contact Us - - - openExternal(COMPANY.GITHUB_URL)} - > - - GitHub - - openExternal(COMPANY.DISCORD_URL)} - > - - Discord - - openExternal(COMPANY.X_URL)}> - X - - openExternal(COMPANY.MAIL_TO)}> - - Email Support - - - - - - - - ); -} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHelpMenu/index.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHelpMenu/index.ts deleted file mode 100644 index e80392612a1..00000000000 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHelpMenu/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { DashboardSidebarHelpMenu } from "./DashboardSidebarHelpMenu"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/DashboardSidebarProjectSection.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/DashboardSidebarProjectSection.tsx index df2d8c24766..2baee8ed2d3 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/DashboardSidebarProjectSection.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarProjectSection/DashboardSidebarProjectSection.tsx @@ -97,7 +97,6 @@ export function DashboardSidebarProjectSection({ { projectName: string; iconUrl: string | null; - totalWorkspaceCount: number; isCollapsed: boolean; isRenaming: boolean; renameValue: string; @@ -29,7 +28,6 @@ export const DashboardSidebarProjectRow = forwardRef< { projectName, iconUrl, - totalWorkspaceCount, isCollapsed, isRenaming, renameValue, @@ -118,9 +116,6 @@ export const DashboardSidebarProjectRow = forwardRef< New workspace - - {totalWorkspaceCount} - )} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarSection/components/DashboardSidebarSectionHeader/DashboardSidebarSectionHeader.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarSection/components/DashboardSidebarSectionHeader/DashboardSidebarSectionHeader.tsx index e36fe8c8819..f517644b084 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarSection/components/DashboardSidebarSectionHeader/DashboardSidebarSectionHeader.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarSection/components/DashboardSidebarSectionHeader/DashboardSidebarSectionHeader.tsx @@ -93,22 +93,13 @@ export const DashboardSidebarSectionHeader = forwardRef< {actions ? ( // biome-ignore lint/a11y/noStaticElementInteractions: Nested action controls handle their own semantics; this wrapper only isolates events from the header toggle.
event.stopPropagation()} onKeyDown={(event) => event.stopPropagation()} > {actions}
) : null} - - {section.workspaces.length} - )} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx index 1b4f9972fb4..29b19eed80f 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx @@ -260,6 +260,7 @@ export const DashboardSidebarExpandedWorkspaceRow = forwardRef< {creationStatusText} ) : ( + isActive && diffStats && (diffStats.additions > 0 || diffStats.deletions > 0) && ( s.setOpen); const canGoBack = router.history.canGoBack(); const canGoForward = location.state.__TSR_index < router.history.length - 1; @@ -66,22 +64,7 @@ export function NavigationControls() { - - - - - - - - - - + {/* Temporarily hidden: */} ); } diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx index f947550f00a..e03ff17e63d 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx @@ -12,7 +12,6 @@ import { OpenInMenuButton } from "./components/OpenInMenuButton"; import { OrganizationDropdown } from "./components/OrganizationDropdown"; import { ResourceConsumption } from "./components/ResourceConsumption"; import { RightSidebarToggle } from "./components/RightSidebarToggle"; -import { V2WorkspaceOpenInButton } from "./components/V2WorkspaceOpenInButton"; import { V2WorkspaceTitle } from "./components/V2WorkspaceTitle"; import { WindowControls } from "./components/WindowControls"; @@ -52,7 +51,7 @@ export function TopBar() { return (
Offline
)} - {/* Portal target for the v2 run button when the presets bar is - hidden (see v2-workspace page). empty:hidden keeps the flex - gap from doubling while nothing is portaled in. */} - {isV2WorkspaceRoute && ( -
- )} - {isV2WorkspaceRoute ? ( - - ) : workspace?.worktreePath ? ( + {!isV2WorkspaceRoute && workspace?.worktreePath ? ( {displayName} {planBadge} - ) : ( diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/V2PresetsBar/V2PresetsBar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/V2PresetsBar/V2PresetsBar.tsx index 53c68c86b2e..2099b8eee60 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/V2PresetsBar/V2PresetsBar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/V2PresetsBar/V2PresetsBar.tsx @@ -10,13 +10,7 @@ import { import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { useNavigate } from "@tanstack/react-router"; import { Eye, EyeOff, Settings } from "lucide-react"; -import { - type ReactNode, - useCallback, - useEffect, - useMemo, - useState, -} from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { HiMiniCommandLine } from "react-icons/hi2"; import { useIsDarkTheme } from "renderer/assets/app-icons/preset-icons"; import { HotkeyMenuShortcut } from "renderer/components/HotkeyMenuShortcut"; @@ -33,7 +27,6 @@ interface V2PresetsBarProps { executePreset: (preset: V2TerminalPresetRow) => void | Promise; showPresetsBar: boolean; onToggleShowPresetsBar: (enabled: boolean) => void; - trailing?: ReactNode; } // Co-located to keep v2 self-contained. Mirrors the v1 array in @@ -75,7 +68,6 @@ export function V2PresetsBar({ executePreset, showPresetsBar, onToggleShowPresetsBar, - trailing, }: V2PresetsBarProps) { const navigate = useNavigate(); const isDark = useIsDarkTheme(); @@ -202,7 +194,7 @@ export function V2PresetsBar({ return (
@@ -301,9 +293,6 @@ export function V2PresetsBar({ /> ); })} - {trailing ? ( -
{trailing}
- ) : null}
); } 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 e09f60987e0..4c7ecfcdf42 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,8 +1,5 @@ -import { Button } from "@superset/ui/button"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { eq } from "@tanstack/db"; import { useLiveQuery } from "@tanstack/react-db"; -import { Search } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { LuFile, LuGitCompareArrows } from "react-icons/lu"; import { useWorkspaceGitStatus } from "renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/providers/WorkspaceGitStatusProvider"; @@ -53,32 +50,6 @@ interface WorkspaceSidebarProps { workspaceId: string; } -function IconButton({ - icon: Icon, - tooltip, - onClick, -}: { - icon: React.ComponentType<{ className?: string }>; - tooltip: string; - onClick?: () => void; -}) { - return ( - - - - - {tooltip} - - ); -} - export function WorkspaceSidebar({ onSelectFile, onSelectDiffFile, @@ -162,7 +133,6 @@ export function WorkspaceSidebar({ id: "files", label: "Files", icon: LuFile, - actions: , content: ( ), }; diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/FilesTab.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/FilesTab.tsx index d780fd8d513..2a116b958ed 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/FilesTab.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/FilesTab.tsx @@ -19,6 +19,7 @@ import { FoldVertical, Loader2, RefreshCw, + Search, } from "lucide-react"; import { useCallback, useEffect, useRef } from "react"; import { useGitStatusMap } from "renderer/hooks/host-service/useGitStatusMap"; @@ -66,6 +67,7 @@ interface FilesTabProps { } | null; workspaceId: string; gitStatus: GitStatusData | undefined; + onSearch?: () => void; } export function FilesTab({ @@ -74,6 +76,7 @@ export function FilesTab({ pendingReveal, workspaceId, gitStatus, + onSearch, }: FilesTabProps) { // Shares the query cache with V2WorkspacePage's workspace.get query, so // the first render after a workspace switch typically already has cached @@ -292,9 +295,15 @@ export function FilesTab({ className="flex-1 min-h-0" style={TREE_STYLE} header={ -
- Explorer +
+ {onSearch && ( + + )} -
+
+
+ t.id === activeTab)?.actions; return ( -
+
{tabs.map((tab) => { const isActive = activeTab === tab.id; @@ -38,6 +38,7 @@ export function SidebarHeader({ getSidebarHeaderTabButtonClassName({ isActive, compact, + inverted: true, }), "relative flex-1 justify-center", )} 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 e9f18624a26..aa7018c0a5d 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 @@ -111,32 +111,34 @@ export const ChangesTabContent = memo(function ChangesTabContent({ return (
- - +
+ + +
+
)} - {isRefreshing && ( - - - - )}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalPaneHeaderExtras/TerminalPaneHeaderExtras.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalPaneHeaderExtras/TerminalPaneHeaderExtras.tsx index 57f8b6f6dfa..6c730cc1a27 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalPaneHeaderExtras/TerminalPaneHeaderExtras.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalPaneHeaderExtras/TerminalPaneHeaderExtras.tsx @@ -55,10 +55,6 @@ export function TerminalPaneHeaderExtras({ {label} - ); } diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.tsx index 2a79526ab5e..34d4f35ff93 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.tsx @@ -11,7 +11,7 @@ import { toast } from "@superset/ui/sonner"; import { workspaceTrpc } from "@superset/workspace-client"; import { eq } from "@tanstack/db"; import { useLiveQuery } from "@tanstack/react-db"; -import { Check, ChevronDown, LoaderCircle, Plus, Trash2 } from "lucide-react"; +import { Check, LoaderCircle, Plus, Trash2 } from "lucide-react"; import { useCallback, useMemo, useState, useSyncExternalStore } from "react"; import { useRenderStressInstrumentation } from "renderer/lib/performance/stress-instrumentation"; import { markTerminalForBackground } from "renderer/lib/terminal/terminal-background-intents"; @@ -310,10 +310,8 @@ export function TerminalSessionDropdown({ {triggerTitle} - {sessionsQuery.isFetching && isOpen ? ( + {sessionsQuery.isFetching && isOpen && ( - ) : ( - )} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx index f9f3e0c3e5b..acc5e9c3021 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx @@ -241,10 +241,6 @@ function V2WorkspaceContent() { // The sidebar slot lives at the dashboard layout level (next to TopBar) so // the sidebar runs full-height. const sidebarSlotEl = useSlotElement("workspace-right-sidebar-slot"); - // TopBar slot for the run button when the presets bar (its usual home) is - // hidden. The button renders here via portal so it keeps this page's - // context (pane store, workspace providers) while appearing in the TopBar. - const runButtonSlotEl = useSlotElement("workspace-topbar-run-slot"); useWorkspaceHotkeys({ store, @@ -302,7 +298,6 @@ function V2WorkspaceContent() { executePreset={executePreset} showPresetsBar={showPresetsBar} onToggleShowPresetsBar={setShowPresetsBar} - trailing={workspaceRunButton} /> ) : null } @@ -316,10 +311,13 @@ function V2WorkspaceContent() { /> )} renderTabBarTrailing={() => ( - +
+ + {workspaceRunButton} +
)} renderEmptyState={() => (
- {!showPresetsBar && - runButtonSlotEl && - createPortal(workspaceRunButton, runButtonSlotEl)} {sidebarOpen && sidebarSlotEl && createPortal( diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/headerTabStyles.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/headerTabStyles.ts index cf06bacf244..6f0261fc115 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/headerTabStyles.ts +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/headerTabStyles.ts @@ -3,22 +3,35 @@ import { cn } from "@superset/ui/utils"; const SIDEBAR_HEADER_TAB_ACTIVE_CLASS_NAME = "text-foreground bg-border/30"; const SIDEBAR_HEADER_TAB_INACTIVE_CLASS_NAME = "text-muted-foreground/70 hover:text-muted-foreground hover:bg-tertiary/20"; +// Inverted scheme: the shaded block marks the inactive tabs while the active +// tab blends into the panel below it. Every state keeps a 1px border on all +// sides (transparent where hidden) so tabs don't shift when switching. +const SIDEBAR_HEADER_TAB_ACTIVE_INVERTED_CLASS_NAME = + "text-foreground border border-border border-b-transparent"; +const SIDEBAR_HEADER_TAB_INACTIVE_INVERTED_CLASS_NAME = + "bg-border/30 text-muted-foreground/70 hover:text-muted-foreground hover:bg-border/20 border border-transparent border-b-border"; export function getSidebarHeaderTabButtonClassName({ isActive, compact = false, + inverted = false, }: { isActive: boolean; compact?: boolean; + inverted?: boolean; }) { + const activeClassName = inverted + ? SIDEBAR_HEADER_TAB_ACTIVE_INVERTED_CLASS_NAME + : SIDEBAR_HEADER_TAB_ACTIVE_CLASS_NAME; + const inactiveClassName = inverted + ? SIDEBAR_HEADER_TAB_INACTIVE_INVERTED_CLASS_NAME + : SIDEBAR_HEADER_TAB_INACTIVE_CLASS_NAME; return cn( "h-full shrink-0 transition-all", compact ? "flex w-10 items-center justify-center" : "flex items-center gap-1.5 px-3 text-xs", - isActive - ? SIDEBAR_HEADER_TAB_ACTIVE_CLASS_NAME - : SIDEBAR_HEADER_TAB_INACTIVE_CLASS_NAME, + isActive ? activeClassName : inactiveClassName, ); } diff --git a/packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx b/packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx index 4d3c117d869..2087a61529e 100644 --- a/packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx +++ b/packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx @@ -47,7 +47,7 @@ function AddTabButton<_TData>({ }) { const button = ( - - - - - - - - Add repository - - event.preventDefault()} - > - openNewProject()}> - - Clone from URL - - - - Open from folder - - openTemplateGallery()}> - - Start from a template - - - -
+ {shortcutText} + +
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx index 29b19eed80f..977d8fb094f 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx @@ -145,7 +145,7 @@ export const DashboardSidebarExpandedWorkspaceRow = forwardRef< onDoubleClick={onDoubleClick} className={cn( "group relative flex w-full items-center py-2 pr-2", - isInSection ? "pl-7" : "pl-5", + isInSection ? "pl-10" : "pl-5", onClick && "cursor-pointer", )} > diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspacesHeader/DashboardSidebarWorkspacesHeader.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspacesHeader/DashboardSidebarWorkspacesHeader.tsx new file mode 100644 index 00000000000..dcf1a9e5c6e --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspacesHeader/DashboardSidebarWorkspacesHeader.tsx @@ -0,0 +1,107 @@ +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@superset/ui/dropdown-menu"; +import { toast } from "@superset/ui/sonner"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; +import { cn } from "@superset/ui/utils"; +import { useNavigate } from "@tanstack/react-router"; +import { HiChevronRight, HiMiniPlus } from "react-icons/hi2"; +import { LuFolderInput, LuFolderPlus, LuLayoutTemplate } from "react-icons/lu"; +import { useFolderFirstImport } from "renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/hooks/useFolderFirstImport"; +import { + useOpenNewProjectModal, + useOpenTemplateGalleryModal, +} from "renderer/stores/add-repository-modal"; +import { useSidebarWorkspacesCollapseStore } from "renderer/stores/sidebar-workspaces-collapse"; + +export function DashboardSidebarWorkspacesHeader() { + const isCollapsed = useSidebarWorkspacesCollapseStore((s) => s.isCollapsed); + const toggleCollapsed = useSidebarWorkspacesCollapseStore((s) => s.toggle); + const openNewProject = useOpenNewProjectModal(); + const openTemplateGallery = useOpenTemplateGalleryModal(); + const navigate = useNavigate(); + const folderImport = useFolderFirstImport({ + onError: (message) => { + toast.error(`Import failed: ${message}`); + }, + onMultipleProjects: ({ candidates }) => { + toast.error("Import failed", { + description: `Multiple projects use this repository (${candidates.length}). Choose the project in settings to set it up on this device.`, + action: { + label: "Open Projects", + onClick: () => navigate({ to: "/settings/projects" }), + }, + }); + }, + }); + + const handleImportFolder = async () => { + const result = await folderImport.start(); + if (result) { + toast.success("Project ready — open it from the sidebar."); + } + }; + + return ( + // biome-ignore lint/a11y/useSemanticElements: can't be a native + + + Add repository + + event.preventDefault()} + > + openNewProject()}> + + Clone from URL + + + + Open from folder + + openTemplateGallery()}> + + Start from a template + + + +
+ ); +} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspacesHeader/index.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspacesHeader/index.ts new file mode 100644 index 00000000000..900df6baddd --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspacesHeader/index.ts @@ -0,0 +1 @@ +export { DashboardSidebarWorkspacesHeader } from "./DashboardSidebarWorkspacesHeader"; diff --git a/apps/desktop/src/renderer/stores/sidebar-workspaces-collapse.ts b/apps/desktop/src/renderer/stores/sidebar-workspaces-collapse.ts new file mode 100644 index 00000000000..1484d7b1737 --- /dev/null +++ b/apps/desktop/src/renderer/stores/sidebar-workspaces-collapse.ts @@ -0,0 +1,20 @@ +import { create } from "zustand"; +import { devtools, persist } from "zustand/middleware"; + +interface SidebarWorkspacesCollapseState { + isCollapsed: boolean; + toggle: () => void; +} + +export const useSidebarWorkspacesCollapseStore = + create()( + devtools( + persist( + (set) => ({ + isCollapsed: false, + toggle: () => set((state) => ({ isCollapsed: !state.isCollapsed })), + }), + { name: "sidebar-workspaces-collapse" }, + ), + ), + ); diff --git a/packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx b/packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx index 2087a61529e..1b3a19845d7 100644 --- a/packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx +++ b/packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx @@ -169,7 +169,7 @@ export function TabBar({ return (
From 000334fd77daea100003879b4f0fb55e5955cda0 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Tue, 21 Jul 2026 01:28:21 -0700 Subject: [PATCH 3/5] feat(desktop): merge top bar into pane tab bar, responsive PR header, sidebar polish --- .../20260721-sidebar-header-reorg-notes.md | 31 +++++++ .../commandPalette/CommandPaletteHost.tsx | 2 + .../renderer/commandPalette/core/sections.ts | 3 +- .../src/renderer/commandPalette/core/types.ts | 2 +- .../modules/addProject/commands.tsx | 41 ++++++++++ .../renderer/commandPalette/modules/index.ts | 2 + .../FolderImportMount/FolderImportMount.tsx | 39 +++++++++ .../ui/FolderImportMount/index.ts | 1 + .../DashboardSidebar/DashboardSidebar.tsx | 9 +- .../DashboardSidebarHeader.tsx | 2 +- .../DashboardSidebarWorkspaceDetails.tsx | 2 +- .../_dashboard/components/TopBar/TopBar.tsx | 3 - .../V2OpenInMenuButton/V2OpenInMenuButton.tsx | 22 +++-- .../_authenticated/_dashboard/layout.tsx | 9 +- .../components/FilesTab/FilesTab.tsx | 21 +++-- .../FilesTabHeaderButton.tsx | 6 +- .../PRActionHeader/PRActionHeader.tsx | 2 +- .../PRStatusGroup/PRStatusGroup.tsx | 10 ++- .../ViewModeToggle/ViewModeToggle.tsx | 59 +++---------- .../ChangesTabContent/ChangesTabContent.tsx | 2 +- .../ChangesToolbar/ChangesToolbar.tsx | 82 ++++++++++--------- .../v2-workspace/$workspaceId/page.tsx | 9 ++ .../renderer/stores/folder-import-intent.ts | 16 ++++ .../components/Tab/components/Pane/Pane.tsx | 8 +- .../Pane/components/PaneHeader/PaneHeader.tsx | 4 +- .../DefaultHeaderContent.tsx | 9 +- .../Workspace/components/TabBar/TabBar.tsx | 23 ++++-- 27 files changed, 286 insertions(+), 133 deletions(-) create mode 100644 apps/desktop/src/renderer/commandPalette/modules/addProject/commands.tsx create mode 100644 apps/desktop/src/renderer/commandPalette/ui/FolderImportMount/FolderImportMount.tsx create mode 100644 apps/desktop/src/renderer/commandPalette/ui/FolderImportMount/index.ts create mode 100644 apps/desktop/src/renderer/stores/folder-import-intent.ts diff --git a/apps/desktop/plans/20260721-sidebar-header-reorg-notes.md b/apps/desktop/plans/20260721-sidebar-header-reorg-notes.md index 1de4abe5189..36a84825352 100644 --- a/apps/desktop/plans/20260721-sidebar-header-reorg-notes.md +++ b/apps/desktop/plans/20260721-sidebar-header-reorg-notes.md @@ -25,3 +25,34 @@ Revert guide: each item is independent; revert by undoing the listed file(s). - **Project header indented** `pl-3` → `pl-5`. `DashboardSidebarProjectSection/components/DashboardSidebarProjectRow/DashboardSidebarProjectRow.tsx` - **Workspace diff stats (+N −N) only render on the active row.** `DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx` - **Workspace-count badges removed** from custom section headers and project rows (`totalWorkspaceCount` prop dropped from ProjectRow; collapsed-rail popover count kept). `DashboardSidebarSection/components/DashboardSidebarSectionHeader/DashboardSidebarSectionHeader.tsx`, `DashboardSidebarProjectRow.tsx` + +--- + +# Session 2 additions (same branch, parallel session) + +## Dashboard sidebar + +- **User/org dropdown moved header → footer**; footer is now `[user dropdown | UpdatesPill | ⚙ | ?]` → later `?` removed (help lives in the org dropdown). `DashboardSidebarHeader.tsx`, `DashboardSidebar.tsx` +- **Settings is an icon-only gear** in the footer (was a full-width labeled row); tooltip carries the hotkey. `DashboardSidebar.tsx` +- **Org dropdown (expanded trigger): chevron removed** (topbar variant keeps it); **Settings item removed** from the menu (was the v1 TODO item); **"Switch organization" got an icon** (`HiOutlineArrowsRightLeft`). `TopBar/components/OrganizationDropdown/OrganizationDropdown.tsx` +- **`SubmitPromptDialog` moved** to `OrganizationDropdown/components/`; old `DashboardSidebarHelpMenu/` deleted (git-tracked, restorable). +- **Workspaces tab hidden, not deleted** — `SHOW_WORKSPACES_TAB = false` flag; flip to true to restore. `DashboardSidebarHeader.tsx` +- **New Search row/icon** opens the command palette; clicking while open closes it (state captured on pointerdown because the palette dismisses on outside pointerdown before click). `DashboardSidebarHeader.tsx` +- **Looking-glass button deleted** from `NavigationControls.tsx` (`⌘K` hotkey unaffected). +- **ResourceConsumption moved** from right edge (`ml-auto`) to after back/forward in the top row. `DashboardSidebarHeader.tsx` +- **Section-header hover drag-grip removed** — chevron stays visible; drag still works via row listeners. `DashboardSidebarSectionHeader.tsx` +- **Workspaces under a section: indent `pl-7` → `pl-10`.** `DashboardSidebarExpandedWorkspaceRow.tsx` + +## Terminal pane / changes panel + +- **Pane-title chevron removed** (fetch spinner kept). `v2-workspace/.../TerminalPane/components/TerminalSessionDropdown/TerminalSessionDropdown.tsx` +- **Divider after rich-input ✎ button removed.** `TerminalPane/components/TerminalPaneHeaderExtras/TerminalPaneHeaderExtras.tsx` +- **Duplicate refresh spinner removed** (the refresh icon itself spins). `useChangesTab/components/ChangesToolbar/ChangesToolbar.tsx` +- **Changes header block: `py-1.5` wrapper** around header+toolbar; toolbar `border-b` removed. `useChangesTab/components/ChangesTabContent/ChangesTabContent.tsx`, `ChangesToolbar.tsx` + +## Tabs / top bar (shared surfaces) + +- **`headerTabStyles.ts` gained `inverted` option** used only by v2 `SidebarHeader.tsx` (`inverted: true`) — v1 RightSidebar styling unchanged; active tab framed by l/r/t borders, transparent sides keep 1px stability. +- **`packages/panes` TabItem/TabBar**: inverted colors, active-tab l/r/t frame, close-button hover bgs swapped; ALL `border-b` removed (tabbed + empty-state variants) and inactive-tab `border-r` separators removed; `+` button got `ml-1.5`. Affects every `Workspace` consumer. +- **`TopBar.tsx` `border-b` removed** — NOTE: dashboard-wide; every route loses the line under the top bar, not just the workspace view. +- **TopBar merged into the pane tab bar** (v2 workspace route, expanded sidebar only): `layout.tsx` skips `` when `onV2WorkspaceRoute && !versionMismatch && sidebarOutsideColumn`; the tab bar is now the Electron drag region (`drag` on the bar root, `no-drag` on tabs track / add button / trailing — `packages/panes` `TabBar.tsx`); `RightSidebarToggle` + non-Mac `WindowControls` moved into the tab-bar trailing slot (`v2-workspace/$workspaceId/page.tsx`). Collapsed/closed sidebar still shows the TopBar (its inset keeps content clear of macOS traffic lights). Workspace title breadcrumb no longer renders on this route. diff --git a/apps/desktop/src/renderer/commandPalette/CommandPaletteHost.tsx b/apps/desktop/src/renderer/commandPalette/CommandPaletteHost.tsx index 1dd26424cc6..c8ddbcdcd59 100644 --- a/apps/desktop/src/renderer/commandPalette/CommandPaletteHost.tsx +++ b/apps/desktop/src/renderer/commandPalette/CommandPaletteHost.tsx @@ -5,6 +5,7 @@ import { useFrameStackStore } from "./core/frames"; import { registerAllModules } from "./modules"; import { CommandPalette } from "./ui/CommandPalette/CommandPalette"; import { DeleteWorkspaceMount } from "./ui/DeleteWorkspaceMount/DeleteWorkspaceMount"; +import { FolderImportMount } from "./ui/FolderImportMount/FolderImportMount"; import { RemoveFromSidebarMount } from "./ui/RemoveFromSidebarMount/RemoveFromSidebarMount"; import { SetPreferredOpenInAppMount } from "./ui/SetPreferredOpenInAppMount/SetPreferredOpenInAppMount"; @@ -21,6 +22,7 @@ export function CommandPaletteHost({ children }: { children?: ReactNode }) { + {children} ); diff --git a/apps/desktop/src/renderer/commandPalette/core/sections.ts b/apps/desktop/src/renderer/commandPalette/core/sections.ts index 20db0610467..8ed1305e2f0 100644 --- a/apps/desktop/src/renderer/commandPalette/core/sections.ts +++ b/apps/desktop/src/renderer/commandPalette/core/sections.ts @@ -1,11 +1,12 @@ import type { CommandContext, SectionId } from "./types"; -const BASE: SectionId[] = ["actions", "navigation"]; +const BASE: SectionId[] = ["actions", "navigation", "add-project"]; export const SECTION_LABELS: Record = { workspace: "Workspace actions", actions: "Actions", navigation: "Navigation", + "add-project": "Add project", }; export function resolveSectionOrder(context: CommandContext): SectionId[] { diff --git a/apps/desktop/src/renderer/commandPalette/core/types.ts b/apps/desktop/src/renderer/commandPalette/core/types.ts index 7b80ee5620f..607643957e6 100644 --- a/apps/desktop/src/renderer/commandPalette/core/types.ts +++ b/apps/desktop/src/renderer/commandPalette/core/types.ts @@ -3,7 +3,7 @@ import type { ElementType } from "react"; import type { HotkeyId } from "renderer/hotkeys/registry"; import type { HostServiceAvailabilityStatus } from "renderer/lib/host-service-unavailable"; -export type SectionId = "workspace" | "actions" | "navigation"; +export type SectionId = "workspace" | "actions" | "navigation" | "add-project"; export interface CommandContext { route: { diff --git a/apps/desktop/src/renderer/commandPalette/modules/addProject/commands.tsx b/apps/desktop/src/renderer/commandPalette/modules/addProject/commands.tsx new file mode 100644 index 00000000000..0c28bb43964 --- /dev/null +++ b/apps/desktop/src/renderer/commandPalette/modules/addProject/commands.tsx @@ -0,0 +1,41 @@ +import { FolderInputIcon, LayoutTemplateIcon, PlusIcon } from "lucide-react"; +import { useAddRepositoryModalStore } from "renderer/stores/add-repository-modal"; +import { useFolderImportIntent } from "renderer/stores/folder-import-intent"; +import type { Command, CommandProvider } from "../../core/types"; + +export const addProjectProvider: CommandProvider = { + id: "add-project", + provide: () => { + const commands: Command[] = [ + { + id: "addProject.cloneFromUrl", + title: "Clone from URL", + section: "add-project", + icon: PlusIcon, + keywords: ["add project", "repository", "repo", "git", "clone"], + run: () => { + void useAddRepositoryModalStore.getState().openNewProject(); + }, + }, + { + id: "addProject.openFromFolder", + title: "Open from folder", + section: "add-project", + icon: FolderInputIcon, + keywords: ["add project", "import", "local", "directory"], + run: () => useFolderImportIntent.getState().request(), + }, + { + id: "addProject.startFromTemplate", + title: "Start from a template", + section: "add-project", + icon: LayoutTemplateIcon, + keywords: ["add project", "new", "gallery", "starter"], + run: () => { + void useAddRepositoryModalStore.getState().openTemplateGallery(); + }, + }, + ]; + return commands; + }, +}; diff --git a/apps/desktop/src/renderer/commandPalette/modules/index.ts b/apps/desktop/src/renderer/commandPalette/modules/index.ts index 81b11dad15b..b61685b8ec6 100644 --- a/apps/desktop/src/renderer/commandPalette/modules/index.ts +++ b/apps/desktop/src/renderer/commandPalette/modules/index.ts @@ -1,5 +1,6 @@ import { registerProvider } from "../core/registry"; import { actionsProvider } from "./actions/commands"; +import { addProjectProvider } from "./addProject/commands"; import { navigationProvider } from "./navigation/commands"; import { openInProvider } from "./openIn/commands"; import { workspaceProvider } from "./workspace/commands"; @@ -10,6 +11,7 @@ export function registerAllModules(): () => void { registerProvider(actionsProvider), registerProvider(openInProvider), registerProvider(navigationProvider), + registerProvider(addProjectProvider), ]; return () => { for (const u of unregisters) u(); diff --git a/apps/desktop/src/renderer/commandPalette/ui/FolderImportMount/FolderImportMount.tsx b/apps/desktop/src/renderer/commandPalette/ui/FolderImportMount/FolderImportMount.tsx new file mode 100644 index 00000000000..40b625adf63 --- /dev/null +++ b/apps/desktop/src/renderer/commandPalette/ui/FolderImportMount/FolderImportMount.tsx @@ -0,0 +1,39 @@ +import { toast } from "@superset/ui/sonner"; +import { useNavigate } from "@tanstack/react-router"; +import { useEffect, useRef } from "react"; +import { useFolderFirstImport } from "renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/hooks/useFolderFirstImport"; +import { useFolderImportIntent } from "renderer/stores/folder-import-intent"; + +export function FolderImportMount() { + const tick = useFolderImportIntent((s) => s.tick); + const navigate = useNavigate(); + const folderImport = useFolderFirstImport({ + onError: (message) => { + toast.error(`Import failed: ${message}`); + }, + onMultipleProjects: ({ candidates }) => { + toast.error("Import failed", { + description: `Multiple projects use this repository (${candidates.length}). Choose the project in settings to set it up on this device.`, + action: { + label: "Open Projects", + onClick: () => navigate({ to: "/settings/projects" }), + }, + }); + }, + }); + const folderImportRef = useRef(folderImport); + folderImportRef.current = folderImport; + const lastTickRef = useRef(0); + + useEffect(() => { + if (tick === lastTickRef.current) return; + lastTickRef.current = tick; + void folderImportRef.current.start().then((result) => { + if (result) { + toast.success("Project ready — open it from the sidebar."); + } + }); + }, [tick]); + + return null; +} diff --git a/apps/desktop/src/renderer/commandPalette/ui/FolderImportMount/index.ts b/apps/desktop/src/renderer/commandPalette/ui/FolderImportMount/index.ts new file mode 100644 index 00000000000..007f59ca655 --- /dev/null +++ b/apps/desktop/src/renderer/commandPalette/ui/FolderImportMount/index.ts @@ -0,0 +1 @@ +export { FolderImportMount } from "./FolderImportMount"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/DashboardSidebar.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/DashboardSidebar.tsx index 38bb354ce11..4ac2718140b 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/DashboardSidebar.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/DashboardSidebar.tsx @@ -18,6 +18,7 @@ import { verticalListSortingStrategy, } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; +import { OverflowFadeContainer } from "@superset/ui/overflow-fade-container"; import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { cn } from "@superset/ui/utils"; import { useMatchRoute, useNavigate } from "@tanstack/react-router"; @@ -191,7 +192,10 @@ export function DashboardSidebar({ {!isCollapsed && } -
+ {(isCollapsed || !workspacesListCollapsed) && ( )} -
+ {!isCollapsed && !inlineWorkspacePortsEnabled && ( )} @@ -255,7 +259,6 @@ export function DashboardSidebar({ )}
+
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx index 94b3bfcf2ca..27d42ac6521 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx @@ -183,12 +183,19 @@ function DashboardLayout() { isWorkspaceSidebarOpen && !isWorkspaceSidebarCollapsed(); + // On the v2 workspace route with an expanded sidebar the TopBar row is + // merged into the pane tab bar (which provides the drag region and hosts + // the right-sidebar toggle). Collapsed/closed sidebars keep the TopBar: + // its inset is what keeps content clear of the macOS traffic lights. + const hideTopBar = + onV2WorkspaceRoute && !versionMismatch && sidebarOutsideColumn; + return (
{sidebarOutsideColumn && sidebarPanel}
- + {!hideTopBar && }
{!sidebarOutsideColumn && sidebarPanel}
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/FilesTab.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/FilesTab.tsx index 2a116b958ed..dd97dca51a2 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/FilesTab.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/FilesTab.tsx @@ -295,15 +295,18 @@ export function FilesTab({ className="flex-1 min-h-0" style={TREE_STYLE} header={ -
-
- {onSearch && ( - - )} +
+ {onSearch && ( + + )} +
{loading ? ( - + ) : ( - + )} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/PRActionHeader/PRActionHeader.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/PRActionHeader/PRActionHeader.tsx index 98adbc1c485..f21bb7bc114 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/PRActionHeader/PRActionHeader.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/PRActionHeader/PRActionHeader.tsx @@ -32,7 +32,7 @@ export function PRActionHeader({ const action = selectActionButton(state); return ( -
+
- + {/* Icon-only when the nearest @container is narrow (resizable + sidebar); the number and indicators come back with room. */} + #{pr.number} - {showIndicators && } + {showIndicators && ( + + + + )} - onChange("folders")} - /> - onChange("tree")} - /> -
- ); -} - -interface ToggleButtonProps { - icon: React.ComponentType<{ className?: string }>; - label: string; - active: boolean; - onClick: () => void; -} - -function ToggleButton({ - icon: Icon, - label, - active, - onClick, -}: ToggleButtonProps) { + const next: ChangesViewMode = viewMode === "folders" ? "tree" : "folders"; + const label = next === "tree" ? "Tree view" : "Folder view"; + const Icon = next === "tree" ? ListTree : Folder; return ( - + + {label} 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 aa7018c0a5d..5999290327e 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 @@ -111,7 +111,7 @@ export const ChangesTabContent = memo(function ChangesTabContent({ return (
-
+
-
+ <> +
)}
-
- - - - - - Refresh changes - - - - - - {label} - +
+
+ + + + + + Refresh changes + + + + + + {label} + +
-
+ ); } diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx index acc5e9c3021..dde404e0765 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx @@ -6,6 +6,9 @@ import { createPortal } from "react-dom"; import { useQuickOpenStore } from "renderer/commandPalette/ui/QuickOpen/quickOpenStore"; import { useV2UserPreferences } from "renderer/hooks/useV2UserPreferences"; import { useHotkey } from "renderer/hotkeys"; +import { electronTrpc } from "renderer/lib/electron-trpc"; +import { RightSidebarToggle } from "renderer/routes/_authenticated/_dashboard/components/TopBar/components/RightSidebarToggle"; +import { WindowControls } from "renderer/routes/_authenticated/_dashboard/components/TopBar/components/WindowControls"; import { CommandPalette } from "renderer/screens/main/components/CommandPalette"; import { ResizablePanel } from "renderer/screens/main/components/ResizablePanel"; import { getV2NotificationSourcesForTab } from "renderer/stores/v2-notifications"; @@ -256,6 +259,10 @@ function V2WorkspaceContent() { void workspaceRun.toggleWorkspaceRun(); }); + const { data: platform } = electronTrpc.window.getPlatform.useQuery(); + // Default to Mac while loading so window controls don't flash in. + const isMac = platform === undefined || platform === "darwin"; + const workspaceRunButton = ( {workspaceRunButton} + + {!isMac && }
)} renderEmptyState={() => ( diff --git a/apps/desktop/src/renderer/stores/folder-import-intent.ts b/apps/desktop/src/renderer/stores/folder-import-intent.ts new file mode 100644 index 00000000000..835c3be2a27 --- /dev/null +++ b/apps/desktop/src/renderer/stores/folder-import-intent.ts @@ -0,0 +1,16 @@ +import { create } from "zustand"; + +// Palette commands run outside React, but the folder-import flow is a hook +// (dialogs, host service, react-query). This intent store bridges the two: +// commands call request(), FolderImportMount runs the flow. +interface FolderImportIntentState { + tick: number; + request: () => void; +} + +export const useFolderImportIntent = create( + (set, get) => ({ + tick: 0, + request: () => set({ tick: get().tick + 1 }), + }), +); diff --git a/packages/panes/src/react/components/Workspace/components/Tab/components/Pane/Pane.tsx b/packages/panes/src/react/components/Workspace/components/Tab/components/Pane/Pane.tsx index 47c0a407eca..6693da7ae26 100644 --- a/packages/panes/src/react/components/Workspace/components/Tab/components/Pane/Pane.tsx +++ b/packages/panes/src/react/components/Workspace/components/Tab/components/Pane/Pane.tsx @@ -245,7 +245,13 @@ export function Pane({ {/* biome-ignore lint/a11y/noStaticElementInteractions: clicking anywhere in a pane focuses it (standard IDE behavior) */}
-
+ {/* font-semibold on the wrapper so custom titleContent inherits the + active bolding too, not just the default title span. */} +
{titleContent ?? ( <> {icon && {icon}} diff --git a/packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx b/packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx index 1b3a19845d7..06370298d58 100644 --- a/packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx +++ b/packages/panes/src/react/components/Workspace/components/TabBar/TabBar.tsx @@ -169,14 +169,16 @@ export function TabBar({ return (
-
+
{renderTabBarTrailing && ( -
+
{renderTabBarTrailing()}
)} @@ -188,15 +190,20 @@ export function TabBar({
-
+
{tabs.map((tab, i) => (
({
{hasHorizontalOverflow && ( -
+
)} {renderTabBarTrailing && ( -
+
{renderTabBarTrailing()}
)} From 119b5b5310d6968d3fd7e78195d97faf785aa064 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Tue, 21 Jul 2026 01:38:24 -0700 Subject: [PATCH 4/5] feat(desktop): scroll edge fades for workspace sidebar Files and Changes tabs --- .../components/FilesTab/FilesTab.tsx | 6 ++ .../hooks/useFileTreeScrollFade/index.ts | 1 + .../useFileTreeScrollFade.ts | 89 +++++++++++++++++++ .../ChangesFileList/ChangesFileList.tsx | 6 +- .../ChangesTabContent/ChangesTabContent.tsx | 2 +- 5 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/hooks/useFileTreeScrollFade/index.ts create mode 100644 apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/hooks/useFileTreeScrollFade/useFileTreeScrollFade.ts diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/FilesTab.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/FilesTab.tsx index dd97dca51a2..b9ea09e2b95 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/FilesTab.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/FilesTab.tsx @@ -47,6 +47,7 @@ import { import { useFilesTabActions } from "./hooks/useFilesTabActions"; import { useFilesTabBridge } from "./hooks/useFilesTabBridge"; import { useFilesTabDrop } from "./hooks/useFilesTabDrop"; +import { useFileTreeScrollFade } from "./hooks/useFileTreeScrollFade"; import { buildPierreGitStatus } from "./utils/buildPierreGitStatus"; import { stripTrailingSlash, toAbs, toRel } from "./utils/treePath"; @@ -265,6 +266,10 @@ export function FilesTab({ ], ); + const fadeContainerRef = useFileTreeScrollFade( + Boolean(rootPath), + ); + if (!rootPath) { return (
@@ -283,6 +288,7 @@ export function FilesTab({ return ( // biome-ignore lint/a11y/noStaticElementInteractions: Drop zone for external file upload