From 42df43fb477349dc265d31749731151eda27eb8d Mon Sep 17 00:00:00 2001 From: Mohamad Yahia Date: Mon, 29 Dec 2025 15:04:13 +0400 Subject: [PATCH 1/2] fix(desktop): fallback to VS Code Insiders when VS Code is not installed Added logic to try opening files in VS Code Insiders as a fallback when VS Code is not installed on the system. --- .../src/lib/trpc/routers/external/helpers.ts | 13 +++++++++++++ apps/desktop/src/lib/trpc/routers/external/index.ts | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/apps/desktop/src/lib/trpc/routers/external/helpers.ts b/apps/desktop/src/lib/trpc/routers/external/helpers.ts index db981d0a185..69396012319 100644 --- a/apps/desktop/src/lib/trpc/routers/external/helpers.ts +++ b/apps/desktop/src/lib/trpc/routers/external/helpers.ts @@ -2,6 +2,9 @@ import { spawn } from "node:child_process"; import nodePath from "node:path"; import { EXTERNAL_APPS, type ExternalApp } from "@superset/local-db"; +/** VS Code Insiders app name for fallback when VS Code is not installed */ +export const VSCODE_INSIDERS_APP_NAME = "Visual Studio Code - Insiders"; + /** Map of app IDs to their macOS application names */ const APP_NAMES: Record = { finder: null, // Handled specially with shell.showItemInFolder @@ -39,6 +42,16 @@ export function getAppCommand( return { command: "open", args: ["-a", appName, targetPath] }; } +/** + * Get the command to open a path in VS Code Insiders. + * Used as fallback when VS Code is not installed. + */ +export function getVscodeInsidersCommand( + targetPath: string, +): { command: string; args: string[] } { + return { command: "open", args: ["-a", VSCODE_INSIDERS_APP_NAME, targetPath] }; +} + /** * Resolve a path by expanding ~ and converting relative paths to absolute. */ diff --git a/apps/desktop/src/lib/trpc/routers/external/index.ts b/apps/desktop/src/lib/trpc/routers/external/index.ts index a0e2f9c659c..e931475e247 100644 --- a/apps/desktop/src/lib/trpc/routers/external/index.ts +++ b/apps/desktop/src/lib/trpc/routers/external/index.ts @@ -7,6 +7,7 @@ import { EXTERNAL_APPS, type ExternalApp, getAppCommand, + getVscodeInsidersCommand, resolvePath, spawnAsync, } from "./helpers"; @@ -24,6 +25,18 @@ async function openPathInApp( const cmd = getAppCommand(app, filePath); if (cmd) { + // For VS Code, try to fallback to VS Code Insiders if VS Code is not installed + if (app === "vscode") { + try { + await spawnAsync(cmd.command, cmd.args); + return; + } catch { + // VS Code not installed, try VS Code Insiders + const insidersCmd = getVscodeInsidersCommand(filePath); + await spawnAsync(insidersCmd.command, insidersCmd.args); + return; + } + } await spawnAsync(cmd.command, cmd.args); return; } From 52d60560107800f978755002b190712e98d17733 Mon Sep 17 00:00:00 2001 From: Mohamad Yahia Date: Wed, 31 Dec 2025 07:46:16 +0400 Subject: [PATCH 2/2] feat(desktop): add vs code insiders to the options --- .../src/lib/trpc/routers/external/helpers.ts | 14 +---- .../src/lib/trpc/routers/external/index.ts | 13 ----- .../assets/app-icons/vscode-insiders.svg | 56 ++++++++++++++++++ .../components/OpenInButton/OpenInButton.tsx | 57 ++++++++++++++++++- .../renderer/components/OpenInButton/index.ts | 1 + .../components/FileHeader/FileHeader.tsx | 2 +- .../WorkspaceActionBarRight.tsx | 32 ++++++++++- packages/local-db/src/schema/zod.ts | 1 + 8 files changed, 145 insertions(+), 31 deletions(-) create mode 100644 apps/desktop/src/renderer/assets/app-icons/vscode-insiders.svg diff --git a/apps/desktop/src/lib/trpc/routers/external/helpers.ts b/apps/desktop/src/lib/trpc/routers/external/helpers.ts index 69396012319..44059e3a198 100644 --- a/apps/desktop/src/lib/trpc/routers/external/helpers.ts +++ b/apps/desktop/src/lib/trpc/routers/external/helpers.ts @@ -2,13 +2,11 @@ import { spawn } from "node:child_process"; import nodePath from "node:path"; import { EXTERNAL_APPS, type ExternalApp } from "@superset/local-db"; -/** VS Code Insiders app name for fallback when VS Code is not installed */ -export const VSCODE_INSIDERS_APP_NAME = "Visual Studio Code - Insiders"; - /** Map of app IDs to their macOS application names */ const APP_NAMES: Record = { finder: null, // Handled specially with shell.showItemInFolder vscode: "Visual Studio Code", + "vscode-insiders": "Visual Studio Code - Insiders", cursor: "Cursor", xcode: "Xcode", iterm: "iTerm", @@ -42,16 +40,6 @@ export function getAppCommand( return { command: "open", args: ["-a", appName, targetPath] }; } -/** - * Get the command to open a path in VS Code Insiders. - * Used as fallback when VS Code is not installed. - */ -export function getVscodeInsidersCommand( - targetPath: string, -): { command: string; args: string[] } { - return { command: "open", args: ["-a", VSCODE_INSIDERS_APP_NAME, targetPath] }; -} - /** * Resolve a path by expanding ~ and converting relative paths to absolute. */ diff --git a/apps/desktop/src/lib/trpc/routers/external/index.ts b/apps/desktop/src/lib/trpc/routers/external/index.ts index e931475e247..a0e2f9c659c 100644 --- a/apps/desktop/src/lib/trpc/routers/external/index.ts +++ b/apps/desktop/src/lib/trpc/routers/external/index.ts @@ -7,7 +7,6 @@ import { EXTERNAL_APPS, type ExternalApp, getAppCommand, - getVscodeInsidersCommand, resolvePath, spawnAsync, } from "./helpers"; @@ -25,18 +24,6 @@ async function openPathInApp( const cmd = getAppCommand(app, filePath); if (cmd) { - // For VS Code, try to fallback to VS Code Insiders if VS Code is not installed - if (app === "vscode") { - try { - await spawnAsync(cmd.command, cmd.args); - return; - } catch { - // VS Code not installed, try VS Code Insiders - const insidersCmd = getVscodeInsidersCommand(filePath); - await spawnAsync(insidersCmd.command, insidersCmd.args); - return; - } - } await spawnAsync(cmd.command, cmd.args); return; } diff --git a/apps/desktop/src/renderer/assets/app-icons/vscode-insiders.svg b/apps/desktop/src/renderer/assets/app-icons/vscode-insiders.svg new file mode 100644 index 00000000000..5067415c6c7 --- /dev/null +++ b/apps/desktop/src/renderer/assets/app-icons/vscode-insiders.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx b/apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx index 655ae320a33..617f107227b 100644 --- a/apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx +++ b/apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx @@ -33,6 +33,7 @@ import rustroverIcon from "renderer/assets/app-icons/rustrover.svg"; import sublimeIcon from "renderer/assets/app-icons/sublime.svg"; import terminalIcon from "renderer/assets/app-icons/terminal.png"; import vscodeIcon from "renderer/assets/app-icons/vscode.svg"; +import vscodeInsidersIcon from "renderer/assets/app-icons/vscode-insiders.svg"; import warpIcon from "renderer/assets/app-icons/warp.png"; import webstormIcon from "renderer/assets/app-icons/webstorm.svg"; import xcodeIcon from "renderer/assets/app-icons/xcode.svg"; @@ -42,12 +43,12 @@ interface AppOption { id: ExternalApp; label: string; icon: string; + displayLabel?: string; } export const APP_OPTIONS: AppOption[] = [ { id: "finder", label: "Finder", icon: finderIcon }, { id: "cursor", label: "Cursor", icon: cursorIcon }, - { id: "vscode", label: "VS Code", icon: vscodeIcon }, { id: "sublime", label: "Sublime Text", icon: sublimeIcon }, { id: "xcode", label: "Xcode", icon: xcodeIcon }, { id: "iterm", label: "iTerm", icon: itermIcon }, @@ -55,6 +56,21 @@ export const APP_OPTIONS: AppOption[] = [ { id: "terminal", label: "Terminal", icon: terminalIcon }, ]; +export const VSCODE_OPTIONS: AppOption[] = [ + { + id: "vscode", + label: "Standard", + icon: vscodeIcon, + displayLabel: "VS Code", + }, + { + id: "vscode-insiders", + label: "Insiders", + icon: vscodeInsidersIcon, + displayLabel: "VS Code Insiders", + }, +]; + export const JETBRAINS_OPTIONS: AppOption[] = [ { id: "intellij", label: "IntelliJ IDEA", icon: intellijIcon }, { id: "webstorm", label: "WebStorm", icon: webstormIcon }, @@ -70,7 +86,11 @@ export const JETBRAINS_OPTIONS: AppOption[] = [ { id: "rustrover", label: "RustRover", icon: rustroverIcon }, ]; -const ALL_APP_OPTIONS = [...APP_OPTIONS, ...JETBRAINS_OPTIONS]; +const ALL_APP_OPTIONS = [ + ...APP_OPTIONS, + ...VSCODE_OPTIONS, + ...JETBRAINS_OPTIONS, +]; export const getAppOption = (id: ExternalApp) => ALL_APP_OPTIONS.find((app) => app.id === id) ?? APP_OPTIONS[1]; @@ -139,7 +159,7 @@ export function OpenInButton({ - {`Open in ${currentApp.label}${showShortcuts ? " (⌘O)" : ""}`} + {`Open in ${currentApp.displayLabel ?? currentApp.label}${showShortcuts ? " (⌘O)" : ""}`} )} @@ -175,6 +195,37 @@ export function OpenInButton({ )} ))} + + + VS Code + VS Code + + + {VSCODE_OPTIONS.map((app) => ( + handleOpenIn(app.id)} + className="flex items-center justify-between" + > +
+ {app.label} + {app.label} +
+ {showShortcuts && app.id === lastUsedApp && ( + ⌘O + )} +
+ ))} +
+
- Open in {currentApp.label} + Open in {currentApp.displayLabel ?? currentApp.label} diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarRight/WorkspaceActionBarRight.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarRight/WorkspaceActionBarRight.tsx index ebc4ae3772d..5a70e22eb1a 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarRight/WorkspaceActionBarRight.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceActionBar/components/WorkspaceActionBarRight/WorkspaceActionBarRight.tsx @@ -14,10 +14,12 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip"; import { HiChevronDown } from "react-icons/hi2"; import { LuArrowUpRight, LuCopy } from "react-icons/lu"; import jetbrainsIcon from "renderer/assets/app-icons/jetbrains.svg"; +import vscodeIcon from "renderer/assets/app-icons/vscode.svg"; import { APP_OPTIONS, getAppOption, JETBRAINS_OPTIONS, + VSCODE_OPTIONS, } from "renderer/components/OpenInButton"; import { shortenHomePath } from "renderer/lib/formatPath"; import { trpc } from "renderer/lib/trpc"; @@ -105,7 +107,7 @@ export function WorkspaceActionBarRight({ - Open in {currentApp.label} + Open in {currentApp.displayLabel ?? currentApp.label} ⌘O @@ -145,6 +147,34 @@ export function WorkspaceActionBarRight({ )} ))} + + + VS Code + VS Code + + + {VSCODE_OPTIONS.map((app) => ( + handleOpenInOtherApp(app.id)} + > + {app.label} + {app.label} + {app.id === lastUsedApp && ( + ⌘O + )} + + ))} + + ; export const EXTERNAL_APPS = [ "finder", "vscode", + "vscode-insiders", "cursor", "sublime", "xcode",