diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx index 9b2b2b4e551..0f3d5435f2e 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx @@ -107,6 +107,7 @@ export function DashboardSidebarWorkspaceItem({ diffStats={diffStats} /> } + isLocalWorkspace={hostType === "local-device"} onCreateSection={handleCreateSection} onMoveToSection={(targetSectionId) => moveWorkspaceToSection(id, projectId, targetSectionId) @@ -172,6 +173,7 @@ export function DashboardSidebarWorkspaceItem({ onMoveToSection={(targetSectionId) => moveWorkspaceToSection(id, projectId, targetSectionId) } + isLocalWorkspace={hostType === "local-device"} onOpenInFinder={handleOpenInFinder} onCopyPath={handleCopyPath} onRemoveFromSidebar={() => removeWorkspaceFromSidebar(id)} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceContextMenu/DashboardSidebarWorkspaceContextMenu.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceContextMenu/DashboardSidebarWorkspaceContextMenu.tsx index 18872835fa0..29e5e6d1d94 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceContextMenu/DashboardSidebarWorkspaceContextMenu.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceContextMenu/DashboardSidebarWorkspaceContextMenu.tsx @@ -32,6 +32,7 @@ interface DashboardSidebarWorkspaceContextMenuProps { hoverCardContent?: React.ReactNode; projectId: string; isInSection?: boolean; + isLocalWorkspace: boolean; onHoverCardOpen?: () => void; onCreateSection: () => void; onMoveToSection: (sectionId: string | null) => void; @@ -46,6 +47,7 @@ interface DashboardSidebarWorkspaceContextMenuProps { export function DashboardSidebarWorkspaceContextMenu({ projectId, isInSection, + isLocalWorkspace, onHoverCardOpen, hoverCardContent, onCreateSection, @@ -81,15 +83,19 @@ export function DashboardSidebarWorkspaceContextMenu({ Rename - - - - Open in Finder - - - - Copy Path - + {isLocalWorkspace && ( + <> + + + + Open in Finder + + + + Copy Path + + + )} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/hooks/useDashboardSidebarWorkspaceItemActions/useDashboardSidebarWorkspaceItemActions.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/hooks/useDashboardSidebarWorkspaceItemActions/useDashboardSidebarWorkspaceItemActions.ts index 629300efa25..8e41962d19b 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/hooks/useDashboardSidebarWorkspaceItemActions/useDashboardSidebarWorkspaceItemActions.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/hooks/useDashboardSidebarWorkspaceItemActions/useDashboardSidebarWorkspaceItemActions.ts @@ -1,12 +1,16 @@ import { toast } from "@superset/ui/sonner"; import { useMatchRoute, useNavigate } from "@tanstack/react-router"; import { useState } from "react"; +import { useCopyToClipboard } from "renderer/hooks/useCopyToClipboard"; import { apiTrpcClient } from "renderer/lib/api-trpc-client"; +import { getHostServiceClientByUrl } from "renderer/lib/host-service-client"; +import { electronTrpcClient } from "renderer/lib/trpc-client"; import { getDeleteFocusTargetWorkspaceId } from "renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/utils/getDeleteFocusTargetWorkspaceId"; import { getFlattenedV2WorkspaceIds } from "renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/utils/getFlattenedV2WorkspaceIds"; import { navigateToV2Workspace } from "renderer/routes/_authenticated/_dashboard/utils/workspace-navigation"; import { useDashboardSidebarState } from "renderer/routes/_authenticated/hooks/useDashboardSidebarState"; import { useCollections } from "renderer/routes/_authenticated/providers/CollectionsProvider"; +import { useLocalHostService } from "renderer/routes/_authenticated/providers/LocalHostServiceProvider"; interface UseDashboardSidebarWorkspaceItemActionsOptions { workspaceId: string; @@ -22,6 +26,8 @@ export function useDashboardSidebarWorkspaceItemActions({ const navigate = useNavigate(); const matchRoute = useMatchRoute(); const collections = useCollections(); + const { activeHostUrl } = useLocalHostService(); + const { copyToClipboard } = useCopyToClipboard(); const { createSection, moveWorkspaceToSection, removeWorkspaceFromSidebar } = useDashboardSidebarState(); @@ -106,12 +112,44 @@ export function useDashboardSidebarWorkspaceItemActions({ moveWorkspaceToSection(workspaceId, projectId, newSectionId); }; - const handleOpenInFinder = () => { - toast.info("Open in Finder is coming soon"); + const resolveWorktreePath = async (): Promise => { + if (!activeHostUrl) { + toast.error("Host service is not available"); + return null; + } + const workspace = await getHostServiceClientByUrl( + activeHostUrl, + ).workspace.get.query({ id: workspaceId }); + if (!workspace?.worktreePath) { + toast.error("Workspace path is not available"); + return null; + } + return workspace.worktreePath; + }; + + const handleOpenInFinder = async () => { + try { + const path = await resolveWorktreePath(); + if (!path) return; + await electronTrpcClient.external.openInFinder.mutate(path); + } catch (error) { + toast.error( + `Failed to open in Finder: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } }; - const handleCopyPath = () => { - toast.info("Copy Path is coming soon"); + const handleCopyPath = async () => { + try { + const path = await resolveWorktreePath(); + if (!path) return; + await copyToClipboard(path); + toast.success("Path copied"); + } catch (error) { + toast.error( + `Failed to copy path: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } }; return { diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/FileContextMenu/FileContextMenu.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/FileContextMenu/FileContextMenu.tsx index c0f46ec2566..7c4ff4168d8 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/FileContextMenu/FileContextMenu.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/FileContextMenu/FileContextMenu.tsx @@ -3,8 +3,7 @@ import { ContextMenuItem, ContextMenuSeparator, } from "@superset/ui/context-menu"; -import { toast } from "@superset/ui/sonner"; -import { electronTrpcClient } from "renderer/lib/trpc-client"; +import { PathActionsMenuItems } from "../PathActionsMenuItems"; interface FileContextMenuProps { absolutePath: string; @@ -23,32 +22,10 @@ export function FileContextMenu({ Open to the Side - - electronTrpcClient.external.openInFinder.mutate(absolutePath) - } - > - Reveal in Finder - - - { - navigator.clipboard.writeText(absolutePath); - toast.success("Path copied"); - }} - > - Copy Path - - {relativePath && ( - { - navigator.clipboard.writeText(relativePath); - toast.success("Relative path copied"); - }} - > - Copy Relative Path - - )} + setTimeout(onRename, 0)}> Rename... diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/FolderContextMenu/FolderContextMenu.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/FolderContextMenu/FolderContextMenu.tsx index de25fe6bb8f..119e2eb84fe 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/FolderContextMenu/FolderContextMenu.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/FolderContextMenu/FolderContextMenu.tsx @@ -3,8 +3,7 @@ import { ContextMenuItem, ContextMenuSeparator, } from "@superset/ui/context-menu"; -import { toast } from "@superset/ui/sonner"; -import { electronTrpcClient } from "renderer/lib/trpc-client"; +import { PathActionsMenuItems } from "../PathActionsMenuItems"; interface FolderContextMenuProps { absolutePath: string; @@ -32,32 +31,10 @@ export function FolderContextMenu({ New Folder... - - electronTrpcClient.external.openInFinder.mutate(absolutePath) - } - > - Reveal in Finder - - - { - navigator.clipboard.writeText(absolutePath); - toast.success("Path copied"); - }} - > - Copy Path - - {relativePath && ( - { - navigator.clipboard.writeText(relativePath); - toast.success("Relative path copied"); - }} - > - Copy Relative Path - - )} + setTimeout(onRename, 0)}> Rename... diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/PathActionsMenuItems/PathActionsMenuItems.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/PathActionsMenuItems/PathActionsMenuItems.tsx new file mode 100644 index 00000000000..c1bf48057ba --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/PathActionsMenuItems/PathActionsMenuItems.tsx @@ -0,0 +1,59 @@ +import { + ContextMenuItem, + ContextMenuSeparator, +} from "@superset/ui/context-menu"; +import { toast } from "@superset/ui/sonner"; +import { useCopyToClipboard } from "renderer/hooks/useCopyToClipboard"; +import { electronTrpcClient } from "renderer/lib/trpc-client"; + +interface PathActionsMenuItemsProps { + absolutePath: string; + relativePath?: string; +} + +export function PathActionsMenuItems({ + absolutePath, + relativePath, +}: PathActionsMenuItemsProps) { + const { copyToClipboard } = useCopyToClipboard(); + + const handleCopy = async (path: string, successMessage: string) => { + try { + await copyToClipboard(path); + toast.success(successMessage); + } catch (error) { + toast.error( + `Failed to copy path: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } + }; + + const handleRevealInFinder = async () => { + try { + await electronTrpcClient.external.openInFinder.mutate(absolutePath); + } catch (error) { + toast.error( + `Failed to reveal in Finder: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } + }; + + return ( + <> + + Reveal in Finder + + + handleCopy(absolutePath, "Path copied")}> + Copy Path + + {relativePath && ( + handleCopy(relativePath, "Relative path copied")} + > + Copy Relative Path + + )} + + ); +} diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/PathActionsMenuItems/index.ts b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/PathActionsMenuItems/index.ts new file mode 100644 index 00000000000..2f4345f1fcd --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceSidebar/components/FilesTab/components/WorkspaceFilesTreeItem/components/PathActionsMenuItems/index.ts @@ -0,0 +1 @@ +export { PathActionsMenuItems } from "./PathActionsMenuItems";