From 76ce7ac9e9285711bf133b2a33807f40845fec5c Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Sun, 1 Feb 2026 12:44:15 -0800 Subject: [PATCH 01/10] Click works no double click --- .../ChangesView/hooks/usePathActions.ts | 13 +++++ .../RightSidebar/FilesView/FilesView.tsx | 38 ++++++++++++-- .../components/FileTreeNode/FileTreeNode.tsx | 50 +++++++++++++++++-- .../WorkspaceView/RightSidebar/index.tsx | 47 ++++++++++++++--- .../desktop/src/renderer/stores/tabs/store.ts | 39 +++++++++++---- .../desktop/src/renderer/stores/tabs/types.ts | 3 ++ .../desktop/src/renderer/stores/tabs/utils.ts | 33 ++++++++---- 7 files changed, 185 insertions(+), 38 deletions(-) diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/usePathActions.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/usePathActions.ts index b2f3eb09a05..f147a5129e1 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/usePathActions.ts +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/usePathActions.ts @@ -39,11 +39,24 @@ export function usePathActions({ }, [absolutePath, openInFinderMutation]); const openInEditor = useCallback(() => { + console.log("[usePathActions] openInEditor called", { + absolutePath, + cwd, + lastUsedApp, + }); if (!absolutePath) return; if (cwd) { + console.log("[usePathActions] Calling openFileInEditorMutation", { + path: absolutePath, + cwd, + }); openFileInEditorMutation.mutate({ path: absolutePath, cwd }); } else { + console.log("[usePathActions] Calling openInAppMutation", { + path: absolutePath, + app: lastUsedApp, + }); openInAppMutation.mutate({ path: absolutePath, app: lastUsedApp }); } }, [ diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/FilesView.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/FilesView.tsx index 7883c54ff34..0eac65d77f1 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/FilesView.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/FilesView.tsx @@ -166,6 +166,7 @@ export function FilesView() { const isSearching = hasQuery; const addFileViewerPane = useTabsStore((s) => s.addFileViewerPane); + const pendingOpenRef = useRef | null>(null); const [newItemMode, setNewItemMode] = useState(null); const [newItemParentPath, setNewItemParentPath] = useState(""); @@ -178,13 +179,36 @@ export function FilesView() { (node: { data: FileTreeNodeType }) => { if (!workspaceId || !worktreePath || node.data.isDirectory) return; - addFileViewerPane(workspaceId, { - filePath: node.data.relativePath, - }); + if (pendingOpenRef.current) { + clearTimeout(pendingOpenRef.current); + } + + pendingOpenRef.current = setTimeout(() => { + pendingOpenRef.current = null; + addFileViewerPane(workspaceId, { + filePath: node.data.relativePath, + viewMode: "raw", + }); + }, 300); }, [workspaceId, worktreePath, addFileViewerPane], ); + const cancelPendingOpen = useCallback(() => { + if (pendingOpenRef.current) { + clearTimeout(pendingOpenRef.current); + pendingOpenRef.current = null; + } + }, []); + + useEffect(() => { + return () => { + if (pendingOpenRef.current) { + clearTimeout(pendingOpenRef.current); + } + }; + }, []); + const handleSelect = useCallback( (nodes: { data: FileTreeNodeType }[]) => { if (!worktreePath) return; @@ -390,7 +414,13 @@ export function FilesView() { onRename={handleRename} dndManager={dragDropManager} > - {FileTreeNode} + {(nodeProps) => ( + + )} )} diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileTreeNode/FileTreeNode.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileTreeNode/FileTreeNode.tsx index 4916b71a1f1..691a8949d2f 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileTreeNode/FileTreeNode.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/components/FileTreeNode/FileTreeNode.tsx @@ -1,30 +1,67 @@ import { cn } from "@superset/ui/utils"; import type { NodeRendererProps } from "react-arborist"; +import { useRef } from "react"; import { LuChevronDown, LuChevronRight } from "react-icons/lu"; import type { FileTreeNode as FileTreeNodeType } from "shared/file-tree-types"; +import { usePathActions } from "../../../ChangesView/hooks"; import { getFileIcon } from "../../utils"; -type FileTreeNodeProps = NodeRendererProps; +type FileTreeNodeProps = NodeRendererProps & { + worktreePath: string; + onCancelOpen: () => void; +}; -export function FileTreeNode({ node, style, dragHandle }: FileTreeNodeProps) { +export function FileTreeNode({ + node, + style, + dragHandle, + worktreePath, + onCancelOpen, +}: FileTreeNodeProps) { const { data } = node; const { icon: Icon, color } = getFileIcon( data.name, data.isDirectory, node.isOpen, ); + const { openInEditor } = usePathActions({ + absolutePath: data.path ?? null, + relativePath: data.relativePath, + cwd: worktreePath, + }); const handleClick = (e: React.MouseEvent) => { - e.stopPropagation(); - node.select(); + console.log("[FileTreeNode] handleClick", { + name: data.name, + detail: e.detail, + isDirectory: data.isDirectory, + }); + // Ignore second click of double-click sequence + if (e.detail > 1) return; + if (data.isDirectory) { node.toggle(); + } else { + node.activate(); } }; const handleDoubleClick = (e: React.MouseEvent) => { + console.log("[FileTreeNode] handleDoubleClick", { + name: data.name, + isDirectory: data.isDirectory, + absolutePath: data.path, + }); + if (data.isDirectory) { + return; + } + e.stopPropagation(); - node.activate(); + e.preventDefault(); + + console.log("[FileTreeNode] Calling onCancelOpen and openInEditor"); + onCancelOpen(); + openInEditor(); }; const handleKeyDown = (e: React.KeyboardEvent) => { @@ -53,6 +90,9 @@ export function FileTreeNode({ node, style, dragHandle }: FileTreeNodeProps) { node.isFocused && !node.isSelected && "ring-1 ring-ring ring-inset", )} onClick={handleClick} + onDoubleClickCapture={(e) => { + console.log("[FileTreeNode] onDoubleClickCapture fired", data.name); + }} onDoubleClick={handleDoubleClick} onKeyDown={handleKeyDown} > diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/index.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/index.tsx index 81ddf303493..d41a3630d03 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/index.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/index.tsx @@ -1,7 +1,7 @@ import { Button } from "@superset/ui/button"; import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { useParams } from "@tanstack/react-router"; -import { useCallback } from "react"; +import { useCallback, useEffect } from "react"; import { LuExpand, LuFile, @@ -16,6 +16,7 @@ import { SidebarMode, useSidebarStore, } from "renderer/stores/sidebar-state"; +import { useChangesStore } from "renderer/stores/changes"; import { useTabsStore } from "renderer/stores/tabs/store"; import type { ChangeCategory, ChangedFile } from "shared/changes-types"; import { useScrollContext } from "../ChangesContent"; @@ -53,6 +54,20 @@ export function RightSidebar() { { enabled: !!workspaceId }, ); const worktreePath = workspace?.worktreePath; + const { baseBranch } = useChangesStore(); + const { data: branchData } = electronTrpc.changes.getBranches.useQuery( + { worktreePath: worktreePath || "" }, + { enabled: !!worktreePath }, + ); + const effectiveBaseBranch = baseBranch ?? branchData?.defaultBranch ?? "main"; + const { data: status } = electronTrpc.changes.getStatus.useQuery( + { worktreePath: worktreePath || "", defaultBranch: effectiveBaseBranch }, + { + enabled: !!worktreePath, + refetchInterval: 2500, + refetchOnWindowFocus: true, + }, + ); const { currentMode, rightSidebarTab, @@ -61,6 +76,20 @@ export function RightSidebar() { setMode, } = useSidebarStore(); const isExpanded = currentMode === SidebarMode.Changes; + const hasChanges = status + ? (status.againstBase?.length ?? 0) > 0 || + (status.commits?.length ?? 0) > 0 || + (status.staged?.length ?? 0) > 0 || + (status.unstaged?.length ?? 0) > 0 || + (status.untracked?.length ?? 0) > 0 + : true; + const showChangesTab = !!worktreePath && hasChanges; + + useEffect(() => { + if (!showChangesTab && rightSidebarTab === RightSidebarTab.Changes) { + setRightSidebarTab(RightSidebarTab.Files); + } + }, [rightSidebarTab, setRightSidebarTab, showChangesTab]); const handleExpandToggle = () => { setMode(isExpanded ? SidebarMode.Tabs : SidebarMode.Changes); @@ -124,12 +153,14 @@ export function RightSidebar() { return (