-
-
Notifications
You must be signed in to change notification settings - Fork 179
feat: add show in Finder to git file menu #335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,9 +21,11 @@ import X from "lucide-react/dist/esm/icons/x"; | |
| import { useMemo, useState, useCallback, useEffect, useRef } from "react"; | ||
| import { formatRelativeTime } from "../../../utils/time"; | ||
| import { PanelTabs, type PanelTabId } from "../../layout/components/PanelTabs"; | ||
| import { pushErrorToast } from "../../../services/toasts"; | ||
|
|
||
| type GitDiffPanelProps = { | ||
| workspaceId?: string | null; | ||
| workspacePath?: string | null; | ||
| mode: "diff" | "log" | "issues" | "prs"; | ||
| onModeChange: (mode: "diff" | "log" | "issues" | "prs") => void; | ||
| filePanelMode: PanelTabId; | ||
|
|
@@ -144,6 +146,30 @@ function normalizeRootPath(value: string | null | undefined) { | |
| return value.replace(/\\/g, "/").replace(/\/+$/, ""); | ||
| } | ||
|
|
||
| function isAbsolutePath(value: string) { | ||
| return value.startsWith("/") || /^[A-Za-z]:\//.test(value); | ||
| } | ||
|
|
||
| function resolveRootPath(root: string | null | undefined, workspacePath: string | null | undefined) { | ||
| const normalized = normalizeRootPath(root); | ||
| if (!normalized) { | ||
| return ""; | ||
| } | ||
| if (workspacePath && !isAbsolutePath(normalized)) { | ||
| return joinRootAndPath(workspacePath, normalized); | ||
| } | ||
| return normalized; | ||
| } | ||
|
|
||
| function joinRootAndPath(root: string, relativePath: string) { | ||
| const normalizedRoot = normalizeRootPath(root); | ||
| if (!normalizedRoot) { | ||
| return relativePath; | ||
| } | ||
| const normalizedPath = relativePath.replace(/^\/+/, ""); | ||
| return `${normalizedRoot}/${normalizedPath}`; | ||
| } | ||
|
|
||
| function getStatusSymbol(status: string) { | ||
| switch (status) { | ||
| case "A": | ||
|
|
@@ -617,6 +643,7 @@ function GitLogEntryRow({ | |
|
|
||
| export function GitDiffPanel({ | ||
| workspaceId = null, | ||
| workspacePath = null, | ||
| mode, | ||
| onModeChange, | ||
| filePanelMode, | ||
|
|
@@ -968,6 +995,13 @@ export function GitDiffPanel({ | |
| const fileCount = targetPaths.length; | ||
| const plural = fileCount > 1 ? "s" : ""; | ||
| const countSuffix = fileCount > 1 ? ` (${fileCount})` : ""; | ||
| const normalizedRoot = resolveRootPath(gitRoot, workspacePath); | ||
| const inferredRoot = | ||
| !normalizedRoot && gitRootCandidates.length === 1 | ||
| ? resolveRootPath(gitRootCandidates[0], workspacePath) | ||
| : ""; | ||
| const fallbackRoot = normalizeRootPath(workspacePath); | ||
| const resolvedRoot = normalizedRoot || inferredRoot || fallbackRoot; | ||
|
|
||
| // Separate files by their section for stage/unstage operations | ||
| const stagedPaths = targetPaths.filter((p) => | ||
|
|
@@ -1007,6 +1041,44 @@ export function GitDiffPanel({ | |
| ); | ||
| } | ||
|
|
||
| if (targetPaths.length === 1) { | ||
| const rawPath = targetPaths[0]; | ||
| const absolutePath = resolvedRoot | ||
| ? joinRootAndPath(resolvedRoot, rawPath) | ||
| : rawPath; | ||
| items.push( | ||
| await MenuItem.new({ | ||
| text: "Show in Finder", | ||
| action: async () => { | ||
| try { | ||
| if (!resolvedRoot && !absolutePath.startsWith("/")) { | ||
| pushErrorToast({ | ||
|
Comment on lines
+1053
to
+1055
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The guard Useful? React with 👍 / 👎. |
||
| title: "Couldn't show file in Finder", | ||
| message: "Select a git root first.", | ||
| }); | ||
| return; | ||
| } | ||
| const { revealItemInDir } = await import( | ||
| "@tauri-apps/plugin-opener" | ||
| ); | ||
| await revealItemInDir(absolutePath); | ||
| } catch (error) { | ||
| const message = | ||
| error instanceof Error ? error.message : String(error); | ||
| pushErrorToast({ | ||
| title: "Couldn't show file in Finder", | ||
| message, | ||
| }); | ||
| console.warn("Failed to reveal file", { | ||
| message, | ||
| path: absolutePath, | ||
| }); | ||
| } | ||
| }, | ||
| }), | ||
| ); | ||
| } | ||
|
|
||
| // Revert action for all selected files | ||
| if (onRevertFile) { | ||
| items.push( | ||
|
|
@@ -1035,6 +1107,9 @@ export function GitDiffPanel({ | |
| onStageFile, | ||
| onRevertFile, | ||
| discardFiles, | ||
| gitRoot, | ||
| gitRootCandidates, | ||
| workspacePath, | ||
| ], | ||
| ); | ||
| const logCountLabel = logTotal | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a user picks a git root inside the workspace, the app stores a relative path (e.g.,
subdir) in settings, soresolvedRoothere can be relative rather than absolute. In that caseabsolutePathbecomes something likesubdir/file, andrevealItemInDirwill run against a relative path (typically the app’s CWD), which can fail or reveal the wrong file. This shows up whenever the git root is a subfolder of the workspace. Consider resolving relative git roots againstworkspacePathbefore callingrevealItemInDir.Useful? React with 👍 / 👎.