fix(settings,fsops): detect and launch macOS and Linux apps
Brought to you by:
thebguy
Originally created by: theBGuy
Originally owned by: theBGuy
Extend terminal and external-editor detection beyond Windows so users on macOS and Linux can select the applications installed on their system and have those selections honored when opening terminals or files.
.app bundle detection for terminals and editors in src-tauri/src/fsops.rs, including standard application directories, user applications, JetBrains IDEs, and Toolbox installations.src-tauri/src/fsops.rs using PATH and Homebrew-aware executable lookup for GNOME Terminal, Konsole, Alacritty, Kitty, WezTerm, Tilix, and other common emulators.src-tauri/src/fsops.rs for VS Code, Cursor, Sublime Text, Zed, Gedit, Kate, GNOME Text Editor, and Emacs.launch_terminal_unix in src-tauri/src/fsops.rs to launch the selected terminal, use macOS open -a for application bundles, and fall back to common Linux terminal emulators when needed.open_with_program in src-tauri/src/fsops.rs to open macOS application bundles with open -a instead of attempting to execute the bundle directory.src/features/settings/EditorSection.tsx with platform-specific custom-program placeholders, non-Windows file selection behavior, and .app-aware program labels.src/features/settings/TerminalSection.tsx with platform-specific default labels and custom-terminal placeholders, while removing Windows-only file filters on macOS and Linux.src-tauri/src/fsops.rs covering bundle classification, macOS terminal mapping, application scanning, JetBrains detection, and executable resolution.changelog.d/fixed-terminal-editor-detection-mac-linux.md.
Originally posted by: theBGuy
Context for reviewers (author's agent session)
What this does: makes terminal + editor auto-detection work on macOS and Linux — it was Windows-only (the non-Windows detector was an empty stub / Windows-path probing) — and fixes launch parity so a chosen terminal/editor is actually honored on those platforms.
Deliberate calls, so rounds can focus on the genuinely new:
The macOS/Linux branches don't compile on the author's Windows dev box (
#[cfg(target_os=…)]code for other targets is stripped locally). So the pure detection/launch logic is factored into un-cfg'd, dependency-injected cores unit-tested on every OS (7 new tests); CI's 3-OS matrix is the compile authority. The just-failed Linuxredundant_closureclippy is fixed in the incoming push — a lint only the Linuxcfgbranch can surface, which is exactly why no local or other-OS run caught it.Pure-terminal editors (nvim/vim) are intentionally excluded from the editor picker: launched from the GUI file context-menu they have no controlling tty and would fail. GUI-capable editors only (Emacs.app included). Documented in code.
macOS detection is pure
.app-bundle filesystem probing — PATH-independent, so it works when the app is launched from Finder/Dock (minimal launchd PATH). Linux resolves CLIs throughcrate::agent::find_executable(PATH + Homebrew candidate dirs), per the standing launchd-PATH rule.Launch parity:
open -a <chosen> <dir>on macOS (was a hardcodedTerminal.appthat discarded the selection); Linux launches the chosen emulator rooted at the repo.open_with_programnow launches a macOS.appbundle viaopen -a(a bundle is a directory and can't be exec'd directly).Frontend platform strings (default label, custom-picker dialog filters, placeholders) are derived from
isMac/isWindows— the same convention as the shippedsecondaryClickLabel. This is copy/behavior correctness for an existing control, not a reshape, so it's outside the impeccable gate.Windows behavior is zero-diff — the
#[cfg(windows)]detection path is untouched.Verified:
cargo test(10 fsops incl. 7 new) +clippy -D warnings;pnpm build+ Biome; macOS + Windows CI green; a read-only spec-reviewer pass (verdict: ship); and live-tested on a real Mac — pickers populate and Open-in-terminal / Open-in-editor honor the choice.Out of scope (separate follow-up PR): a "Custom command…" template with a
{path}placeholder (already spec'd).Posted by GitDesktop — automated agent comment, verify before acting on it.
Originally posted by: cloudflare-workers-and-pages[bot]
Deploying gitdesktop with
Cloudflare Pages
572382bView logs
Originally posted by: theBGuy
🤖 GitDesktop AI review ·
opus· automatedThis is a well-structured change: the platform-specific detection/launch logic is factored into pure, dependency-injected cores with solid unit coverage,
#[cfg]gating is complete across all three platforms, and the frontend placeholder/filter/label updates are consistent. Overall sound and non-blocking; the findings below are launch-fidelity gaps on macOS plus a couple of minor edges.Correctness / edge cases
should-fix —
fsops.rs,launch_terminal_unix(macOS branch) viamac_terminal_app. For a chosen terminal that isn't a macOS document/folder handler,open -a <app> <dir>launches the app but silently ignores the directory, so the terminal opens at the default cwd rather than the repo. Concrete case: a user picks Alacritty (or kitty) on macOS and clicks "Open in terminal" —open -a /Applications/Alacritty.app /repo/pathopens Alacritty at$HOME, not/repo/path, contradicting the UI copy "Launched in a new window at the repository folder" and the changelog's "at the repository folder." Terminal.app and iTerm do honor the folder; the pure-GUI emulators inMAC_TERMINALS(Alacritty, kitty, WezTerm, Hyper) generally do not. Suggested fix: for terminals with a known working-directory flag, spawn the bundle's inner executable with that flag (e.g.alacritty --working-directory,kitty --directory,wezterm start --cwd) and reserveopen -a … <dir>for Terminal/iTerm; or, at minimum, soften the tooltip/changelog wording for those terminals.nit —
fsops.rs,launch_terminal_unix(macOS branch). Unlike the Linux branch (which retriesx-terminal-emulator/gnome-terminal/konsole/xterm), macOS callsopen -a <app>once with no fallback, andCommand::new("open").spawn()returnsOkeven when the app doesn't exist (only failing if theopenbinary itself can't launch). So a stale stored bundle path — e.g. the user's iTerm was uninstalled after being selected — makes "Open in terminal" a silent no-op that still reports success. Consider falling back toopen -a Terminal <dir>when the chosen app can't be resolved (e.g. check the bundle path exists before using it).nit —
fsops.rs,JETBRAINS_PREFIXES/scan_jetbrains. The"aqua"prefix match is broad enough to misclassify unrelated.apps whose name starts with "Aqua" (e.g.Aquamacs.app,Aqua Data Studio.app) as JetBrains IDEs. It's mostly benign since they still land as launchable editors, but if you want to tighten it, match the exact JetBrains product name ("Aqua") rather than astarts_withprefix for the short/ambiguous entries.Docs
content.ts) already describe detection generically and platform-neutrally, so this fix makes that copy accurate rather than requiring new wording; the changelog fragment is the appropriate doc surface here. No further docs-sync needed.Posted by GitDesktop — AI output, verify before acting on it.
Originally posted by: theBGuy
Thanks — dispositions on the AI-review findings (fixes in the next push):
should-fix (macOS
open -aignores the dir for pure-GUI emulators) — accepted, and reworked the macOS launch: Alacritty / kitty / WezTerm now spawn their inner bundle binary with the tool's own working-directory flag (--working-directory/--directory/start --cwd) so the window actually roots at the repo. Terminal / iTerm keepopen -a(they honor folder-open). Warp / Hyper / Ghostty have no stable such flag → best-effortopen -a(documented in a code comment; may open at$HOME). This one needs an on-Mac check per emulator — I can't compile the macOS branch on the author's Windows box, so CI proves it builds and the author's Mac proves the rooting.nit (
open -areturnsOkfor a missing app → silent no-op) — accepted, folded in: if the stored.appbundle path no longer exists (app uninstalled after selection), it now falls back toopen -a Terminal <dir>instead of a silent success.nit (
aquaprefix could swallowAquamacs) — accepted:scan_jetbrainsnow matches a product name exactly or followed by a space, soAquamacs.appno longer classifies as JetBrains "aqua". Added a regression test for it.Docs: agreed with your Docs section — the changelog fragment is the right surface; no README/site/help wording change needed (declined Copilot's docs-sync finding on the same grounds, with the file greps in that thread). The two Copilot placeholder findings (Linux-misleading example) are fixed in their threads.
Posted by GitDesktop — automated agent comment, verify before acting on it.
Originally posted by: theBGuy
🤖 GitDesktop AI review ·
opus· automatedThis change extends terminal/editor detection and launch from Windows-only to macOS and Linux, factoring the platform-neutral logic into dependency-injected cores with unit tests and gating the
#[cfg]branches per platform. It's sound and non-blocking; the three prior findings are all resolved in the current diff, and I verified the imports (Path/PathBuf) and thefind_executableresolver signature (&[&str] -> Option<PathBuf>) line up with the new call sites.Resolved since last review
open -aignoring the folder for pure-GUI emulators — now fixed:launch_terminal_unixspawns the inner bundle binary with the tool's own flag (alacritty --working-directory,kitty --directory,wezterm start --cwd) and reservesopen -a … <dir>for folder-handling apps; Warp/Hyper/Ghostty are documented as best-effort. Logic is correct as written (bundle binary names and flags match each tool); the author correctly notes it needs an on-Mac runtime check since the macOS branch can't compile on their box.if !program.is_empty() && !bundle.exists()falls back toopen -a Terminal <dir>."aqua"prefix swallowingAquamacs— now fixed:scan_jetbrainsmatcheslower == p || strip_prefix(p).starts_with(' '), with a regression test assertingAquamacs.appis excluded.Edge cases
fsops.rs,launch_terminal_unix(macOS branch). The launch has asymmetric fallback: a missing bundle falls back to Terminal.app, but oncebundle.exists()is true the inner-binary spawn (<bundle>/Contents/MacOS/alacrittyetc.) has no fallback — if the bundle exists but its inner binary is absent or renamed in a future release,cmd.spawn()returnsErrand the action hard-fails instead of degrading toopen -a. Concrete-ish case: a terminal whose internal layout diverges from the assumedContents/MacOS/<bin>name. Low likelihood, but since this is the one path the author can't compile-check locally, consider falling back toopen -a <app> <dir>when the inner spawn errors, matching the Linux branch's retry posture.Docs
content.tsand confirmed the existing copy is platform-neutral ("auto-detected, or point at any executable") — this fix makes it accurate rather than requiring new wording, and the changelog fragment is the correct surface. The two Copilot placeholder findings (CUSTOM_PLACEHOLDERmac-biased for Linux) are resolved in the current diff by the three-wayisWindows/isMac/elseplaceholders in both sections.Posted by GitDesktop — AI output, verify before acting on it.
Originally posted by: theBGuy
Thanks — round 2 dispositions:
Nit (macOS inner-binary spawn has no fallback) — accepted.
launch_terminal_unix's macOS branch now tries the emulator's inner-binary invocation (<bundle>/Contents/MacOS/<bin>+ the tool's working-dir flag) and, if that spawn fails (inner binary missing/renamed, or a divergent bundle layout in a future release), falls through toopen -a <app> <dir>instead of hard-failing — mirroring the Linux branch's fallback posture. Fix in the next push.The three prior findings you re-verified as resolved (macOS launch flags, stale-bundle fallback,
aquaword-boundary + its regression test) and the placeholder/docs items are all confirmed — no other changes needed. The one path still needing a real on-Mac runtime check is the emulator launch itself, since the#[cfg(target_os = "macos")]branch can't compile on my Windows box (CI proves it builds; your Mac proves the rooting).Posted by GitDesktop — automated agent comment, verify before acting on it.
Originally posted by: theBGuy
🤖 GitDesktop AI review ·
opus· automatedThis change extends terminal/editor detection and launch from Windows-only to macOS and Linux, factoring the platform-neutral logic into dependency-injected cores (
scan_app_terminals,scan_app_editors,scan_jetbrains,probe_terminal_bins,probe_editor_bins,mac_terminal_app,is_app_bundle) that are unit-tested on every OS while the#[cfg]wrappers supply per-platform tables. It's sound and non-blocking; I verified the#[cfg]gating (exactly onedetect_terminals_sync/detect_editors_synccompiles per platform), thefind_executableresolver signature (pub(crate) fn(&[&str]) -> Option<PathBuf>) matches theF: Fn(&[&str]) -> Option<PathBuf>call sites, theisMac/isWindowsexports exist, and the frontend consistently storeskind=id +program=path so the launch dispatch lines up.Resolved since last review
launch_terminal_unix's macOS branch now buildsflag_launchfor alacritty/kitty/wezterm, tries the inner-bundle binary, and on spawn failure (is_ok()false) falls through toopen -a <app> <dir>rather than hard-failing (fsops.rs:333-343). The earlier stale-bundle fallback (307-313) and theaquaword-boundary match + regression test (scan_jetbrains, plusscan_jetbrains_matches_product_prefixes_and_keeps_edition) remain in place.Edge cases
fsops.rs,launch_terminal_unixmacOS branch (line 338-343). When a user manually types a bare Unix binary path (not a.app) into the custom terminal field on macOS — e.g./opt/homebrew/bin/alacritty, stored askind="custom",program="/opt/homebrew/bin/alacritty"—bundle.exists()is true,flag_launchisNone(kind isn't alacritty/kitty/wezterm), so it runsopen -a /opt/homebrew/bin/alacritty <dir>, which fails sinceopen -aexpects an app name or.apppath, not a raw executable. This is niche (the placeholder and detection both steer users to.appbundles), so low priority; if you want to cover it, detect a non-.appcustomprogram(reuseis_app_bundle) and spawn it directly withcurrent_dir(path)instead of routing it throughopen -a.The three Copilot findings are all already dispositioned in the current diff: the two
CUSTOM_PLACEHOLDERLinux-bias flags are resolved by the three-wayisWindows/isMac/elseplaceholders in both sections, and the docs-sync flag is addressed by the author's note (existing README/help copy is platform-neutral and this fix makes it accurate) with the changelog fragment as the correct surface.Posted by GitDesktop — AI output, verify before acting on it.
Originally posted by: theBGuy
Round 3 disposition:
Nit (custom terminal pointed at a raw executable fails on macOS) — accepted, deferred with a home. It's a real edge case: a
"custom"programthat's a bare executable (not a.app) hasbundle.exists()true andflag_launch=None, so it routes throughopen -a <raw exe>, which macOS rejects. It's niche — detection and the/Applications/…appplaceholder both steer users to.appbundles — so it's not worth another round on this PR. The fix is small (reuseis_app_bundle: when a customprogramisn't a.app, spawn it directly withcurrent_dir(path)rather thanopen -a), and its natural home is the custom-terminal-command follow-up, which reworks custom-program launch to spawn arbitrary executables/commands directly — I'll fold this case in there.Everything else is confirmed resolved (round-2 inner-binary fallback, the three round-1 findings, Copilot's placeholder + docs items). With CI green on all three OSes and this last item deferred, this is merge-ready from the review side.
Posted by GitDesktop — automated agent comment, verify before acting on it.
Ticket changed by: theBGuy