From aa06b11e1ea60b9e876b9c5285485fd083022f56 Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Sun, 10 May 2026 12:13:37 -0700 Subject: [PATCH 1/2] fix(desktop): restore recently-viewed dropdown for v2 + tighten topbar chrome MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recently-viewed dropdown was permanently disabled in v2: the path matcher only knew about /workspace/$id (v1) and the slug capture greedily consumed search params and nested subroutes, so /tasks/SUP-123?tab=… and /v2-workspace/$id never resolved to a known entity. Add a v2-workspace resource type, an automation resource type, strip search/hash before matching, anchor to the first path segment, and dedupe by canonical path. Filter cross-version workspace entries so clicking a v1 entry while in v2 mode no longer drops you on the cross-version mismatch screen. Re-show the dropdown trigger in both topbar and v2 sidebar header (was hardcoded to false in #4314 because it was always rendering as the disabled icon). Also: collapse the resource monitor trigger to an icon-only ghost button and float it to the far right of its row in both surfaces. --- .../DashboardSidebarHeader.tsx | 6 +- .../NavigationControls/NavigationControls.tsx | 10 +- .../HistoryDropdown/HistoryDropdown.tsx | 178 ++++++++++++++++-- .../useRecentlyViewed/useRecentlyViewed.ts | 41 +++- .../_dashboard/components/TopBar/TopBar.tsx | 6 +- .../ResourceConsumption.tsx | 53 +++--- 6 files changed, 231 insertions(+), 63 deletions(-) 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 4cb7fde926f..20b75b264d9 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 @@ -214,12 +214,12 @@ export function DashboardSidebarHeader({ horizontal inset — keeps traffic-light alignment matching the TopBar's 80px pad regardless of parent padding changes. */}
- - + +
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/NavigationControls.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/NavigationControls.tsx index ad6b1bff0ed..43e44d79886 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/NavigationControls.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/NavigationControls.tsx @@ -5,13 +5,7 @@ import { LuArrowLeft, LuArrowRight } from "react-icons/lu"; import { HotkeyLabel, useHotkey } from "renderer/hotkeys"; import { HistoryDropdown } from "./components/HistoryDropdown"; -interface NavigationControlsProps { - showHistoryDropdown?: boolean; -} - -export function NavigationControls({ - showHistoryDropdown = true, -}: NavigationControlsProps = {}) { +export function NavigationControls() { const router = useRouter(); const location = useLocation(); @@ -70,7 +64,7 @@ export function NavigationControls({ - {showHistoryDropdown && } + ); } diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/HistoryDropdown.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/HistoryDropdown.tsx index 7bf1c177ae9..de0d2358184 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/HistoryDropdown.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/HistoryDropdown.tsx @@ -11,7 +11,9 @@ import { cn } from "@superset/ui/utils"; import { eq } from "@tanstack/db"; import { useLiveQuery } from "@tanstack/react-db"; import { useLocation, useNavigate } from "@tanstack/react-router"; -import { LuHistory } from "react-icons/lu"; +import { LuCpu, LuGitBranch, LuHistory } from "react-icons/lu"; +import { usePresetIcon } from "renderer/assets/app-icons/preset-icons"; +import { useIsV2CloudEnabled } from "renderer/hooks/useIsV2CloudEnabled"; import { electronTrpc } from "renderer/lib/electron-trpc"; import { StatusIcon, @@ -75,6 +77,94 @@ function WorkspaceRow({ ); } +function V2WorkspaceRow({ + entry, + isCurrent, + v2WorkspaceData, + onSelect, +}: { + entry: RecentlyViewedEntry; + isCurrent: boolean; + v2WorkspaceData: { + id: string; + projectName: string; + branch: string; + }[]; + onSelect: () => void; +}) { + const ws = v2WorkspaceData.find((w) => w.id === entry.entityId); + + return ( + + + {ws ? ws.projectName : "Workspace"} + + + + + + {ws ? ws.branch : "Unknown"} + + + ); +} + +function AutomationRow({ + entry, + isCurrent, + automationData, + onSelect, +}: { + entry: RecentlyViewedEntry; + isCurrent: boolean; + automationData: { + id: string; + name: string; + agentId: string; + }[]; + onSelect: () => void; +}) { + const automation = automationData.find((a) => a.id === entry.entityId); + const presetIcon = usePresetIcon(automation?.agentId ?? ""); + + return ( + + + Automation + + + {presetIcon ? ( + + ) : ( + + )} + + + {automation ? automation.name : "Unknown"} + + + ); +} + function TaskRow({ entry, isCurrent, @@ -138,6 +228,7 @@ export function HistoryDropdown() { const recentEntries = useRecentlyViewed(20); const currentPath = useLocation({ select: (loc) => loc.pathname }); const collections = useCollections(); + const isV2CloudEnabled = useIsV2CloudEnabled(); const { data: groups } = electronTrpc.workspaces.getAllGrouped.useQuery(); const workspaceData = (groups ?? []).flatMap((group) => @@ -149,6 +240,34 @@ export function HistoryDropdown() { })), ); + const { data: v2WorkspaceData } = useLiveQuery( + (q) => + q + .from({ workspaces: collections.v2Workspaces }) + .innerJoin( + { projects: collections.v2Projects }, + ({ workspaces, projects }) => eq(workspaces.projectId, projects.id), + ) + .select(({ workspaces, projects }) => ({ + id: workspaces.id, + projectName: projects.name, + branch: workspaces.branch, + })), + [collections], + ); + + const { data: automationData } = useLiveQuery( + (q) => + q + .from({ automations: collections.automations }) + .select(({ automations }) => ({ + id: automations.id, + name: automations.name, + agentId: automations.agentConfig.id, + })), + [collections], + ); + const { data: taskData } = useLiveQuery( (q) => q @@ -169,8 +288,16 @@ export function HistoryDropdown() { const filteredEntries = recentEntries.filter((entry) => { if (entry.type === "workspace") { + if (isV2CloudEnabled) return false; return workspaceData.some((w) => w.id === entry.entityId); } + if (entry.type === "v2-workspace") { + if (!isV2CloudEnabled) return false; + return (v2WorkspaceData ?? []).some((w) => w.id === entry.entityId); + } + if (entry.type === "automation") { + return (automationData ?? []).some((a) => a.id === entry.entityId); + } return (taskData ?? []).some( (t) => t.id === entry.entityId || t.slug === entry.entityId, ); @@ -211,16 +338,41 @@ export function HistoryDropdown() { Recently Viewed - {filteredEntries.map((entry) => - entry.type === "task" ? ( - navigate({ to: entry.path })} - /> - ) : ( + {filteredEntries.map((entry) => { + if (entry.type === "task") { + return ( + navigate({ to: entry.path })} + /> + ); + } + if (entry.type === "v2-workspace") { + return ( + navigate({ to: entry.path })} + /> + ); + } + if (entry.type === "automation") { + return ( + navigate({ to: entry.path })} + /> + ); + } + return ( navigate({ to: entry.path })} /> - ), - )} + ); + })} ); diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/hooks/useRecentlyViewed/useRecentlyViewed.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/hooks/useRecentlyViewed/useRecentlyViewed.ts index 562d09656d5..6d7e7b278e7 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/hooks/useRecentlyViewed/useRecentlyViewed.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/hooks/useRecentlyViewed/useRecentlyViewed.ts @@ -4,31 +4,56 @@ import { persistentHistory } from "renderer/lib/persistent-hash-history"; export interface RecentlyViewedEntry { path: string; - type: "workspace" | "task"; + type: "workspace" | "v2-workspace" | "task" | "automation"; entityId: string; timestamp: number; } +function pathnameOf(href: string): string { + const queryIndex = href.indexOf("?"); + const hashIndex = href.indexOf("#"); + const cutoffs = [queryIndex, hashIndex].filter((i) => i >= 0); + return cutoffs.length === 0 ? href : href.substring(0, Math.min(...cutoffs)); +} + function parseResourceEntry(entry: { path: string; timestamp: number; }): RecentlyViewedEntry | null { - const wsMatch = entry.path.match(/^\/workspace\/(.+)$/); + const pathname = pathnameOf(entry.path); + + const v2WsMatch = pathname.match(/^\/v2-workspace\/([^/]+)/); + if (v2WsMatch?.[1]) + return { + path: `/v2-workspace/${v2WsMatch[1]}`, + type: "v2-workspace", + entityId: v2WsMatch[1], + timestamp: entry.timestamp, + }; + const wsMatch = pathname.match(/^\/workspace\/([^/]+)/); if (wsMatch?.[1]) return { - path: entry.path, + path: `/workspace/${wsMatch[1]}`, type: "workspace", entityId: wsMatch[1], timestamp: entry.timestamp, }; - const taskMatch = entry.path.match(/^\/tasks\/(.+)$/); + const taskMatch = pathname.match(/^\/tasks\/([^/]+)/); if (taskMatch?.[1]) return { - path: entry.path, + path: `/tasks/${taskMatch[1]}`, type: "task", entityId: taskMatch[1], timestamp: entry.timestamp, }; + const automationMatch = pathname.match(/^\/automations\/([^/]+)/); + if (automationMatch?.[1]) + return { + path: `/automations/${automationMatch[1]}`, + type: "automation", + entityId: automationMatch[1], + timestamp: entry.timestamp, + }; return null; } @@ -41,10 +66,10 @@ export function useRecentlyViewed(limit = 20): RecentlyViewedEntry[] { for (let i = allEntries.length - 1; i >= 0; i--) { const entry = allEntries[i]; - if (!entry || seen.has(entry.path)) continue; + if (!entry) continue; const resource = parseResourceEntry(entry); - if (resource) { - seen.set(entry.path, resource); + if (resource && !seen.has(resource.path)) { + seen.set(resource.path, resource); } } 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 85e87916d6d..f5bf4cfe613 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 @@ -54,8 +54,7 @@ export function TopBar() { {!sidebarHostsChrome && ( <> - - + )} @@ -85,6 +84,9 @@ export function TopBar() { )}
+ {!sidebarHostsChrome && ( + + )} {!isOnline && (
diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/ResourceConsumption.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/ResourceConsumption.tsx index cfb35c95c0b..b8a2d61d1f9 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/ResourceConsumption.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/components/ResourceConsumption/ResourceConsumption.tsx @@ -1,4 +1,5 @@ import type { WorkspaceState } from "@superset/panes"; +import { Button } from "@superset/ui/button"; import { DropdownMenu, DropdownMenuContent, @@ -90,10 +91,12 @@ function getTerminalTitleOverrides( interface ResourceConsumptionProps { surface?: "v1" | "v2"; + className?: string; } export function ResourceConsumption({ surface = "v1", + className, }: ResourceConsumptionProps) { const [open, setOpen] = useState(false); const [sortOption, setSortOption] = useState("memory"); @@ -344,40 +347,32 @@ export function ResourceConsumption({ - + - {normalizedSnapshot && ( - - {formatMemory(normalizedSnapshot.totalMemory)} - - )} + + {normalizedSnapshot + ? `Resources · ${formatMemory(normalizedSnapshot.totalMemory)}` + : "Resources"} + From 301bd5a513ba7f34338b05ea350bd8d68f2c25ec Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Sun, 10 May 2026 12:19:40 -0700 Subject: [PATCH 2/2] fix(desktop): hide automation entries in recently-viewed when v1 is active --- .../components/HistoryDropdown/HistoryDropdown.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/HistoryDropdown.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/HistoryDropdown.tsx index de0d2358184..78e3f52ec04 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/HistoryDropdown.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/NavigationControls/components/HistoryDropdown/HistoryDropdown.tsx @@ -296,6 +296,7 @@ export function HistoryDropdown() { return (v2WorkspaceData ?? []).some((w) => w.id === entry.entityId); } if (entry.type === "automation") { + if (!isV2CloudEnabled) return false; return (automationData ?? []).some((a) => a.id === entry.entityId); } return (taskData ?? []).some(