Files
paseo/packages/app/src/workspace/open-target-planner.ts
T
Mohamed BoudraandGitHub f4509fe044 Open files in more installed editors (#2119)
* feat(desktop): open files in more installed editors

Detect bundled app commands as well as PATH launchers and preserve file positions through editor-specific launches.

* fix(desktop): preserve editor target compatibility

Keep existing file-manager preference ids, retain project context for file launches, and recognize Windows 64-bit IDE commands.
2026-07-16 14:39:55 +02:00

139 lines
4.0 KiB
TypeScript

import { buildGitHubBlobUrl, buildGitHubBranchTreeUrl } from "@/git/github-url";
import type { DesktopOpenTarget, OpenDesktopTargetInput } from "@/workspace/desktop-open-targets";
import {
type ResolvedWorkspaceFilePaths,
resolveWorkspaceFilePaths,
type WorkspaceFileLocation,
} from "@/workspace/file-open";
interface CheckoutStatusForOpenTarget {
isGit: boolean;
remoteUrl?: string | null;
currentBranch?: string | null;
}
export interface PlannedDesktopOpenTarget {
source: "desktop";
id: string;
label: string;
editorId: string;
icon: DesktopOpenTarget["icon"];
openInput: OpenDesktopTargetInput;
}
export interface PlannedGitHubOpenTarget {
source: "github";
id: "github";
label: "GitHub";
url: string;
}
export type PlannedWorkspaceOpenTarget = PlannedDesktopOpenTarget | PlannedGitHubOpenTarget;
export interface PlanWorkspaceOpenTargetsInput {
workspaceDirectory: string;
activeFile?: WorkspaceFileLocation | null;
resolvedActiveFile?: ResolvedWorkspaceFilePaths | null;
desktopTargets: readonly DesktopOpenTarget[];
canUseDesktopBridge: boolean;
isLocalExecution: boolean;
checkoutStatus?: CheckoutStatusForOpenTarget | null;
}
function resolveActiveFileForOpenTargets(
input: Pick<
PlanWorkspaceOpenTargetsInput,
"activeFile" | "resolvedActiveFile" | "workspaceDirectory"
>,
): ResolvedWorkspaceFilePaths | null {
if (input.resolvedActiveFile !== undefined) {
return input.resolvedActiveFile;
}
return input.activeFile
? resolveWorkspaceFilePaths({
path: input.activeFile.path,
workspaceRoot: input.workspaceDirectory,
})
: null;
}
function planDesktopOpenTargets(input: {
workspaceDirectory: string;
activeFile?: WorkspaceFileLocation | null;
resolvedFile: ResolvedWorkspaceFilePaths | null;
desktopTargets: readonly DesktopOpenTarget[];
canUseDesktopBridge: boolean;
isLocalExecution: boolean;
}): PlannedDesktopOpenTarget[] {
if (!input.canUseDesktopBridge || !input.isLocalExecution) {
return [];
}
return input.desktopTargets.map((target) => {
if (!input.resolvedFile) {
return {
source: "desktop",
id: target.id,
label: target.label,
editorId: target.id,
icon: target.icon,
openInput: { editorId: target.id, workspacePath: input.workspaceDirectory },
};
}
return {
source: "desktop",
id: target.id,
label: target.label,
editorId: target.id,
icon: target.icon,
openInput: {
editorId: target.id,
workspacePath: input.workspaceDirectory,
filePath: input.resolvedFile.absolutePath,
...(input.activeFile?.lineStart ? { line: input.activeFile.lineStart } : {}),
},
};
});
}
function planGitHubOpenTarget(input: {
activeFile?: WorkspaceFileLocation | null;
resolvedFile: ResolvedWorkspaceFilePaths | null;
checkoutStatus?: CheckoutStatusForOpenTarget | null;
}): PlannedGitHubOpenTarget | null {
if (!input.checkoutStatus?.isGit) {
return null;
}
const url = input.resolvedFile?.relativePath
? buildGitHubBlobUrl({
remoteUrl: input.checkoutStatus.remoteUrl,
branch: input.checkoutStatus.currentBranch,
path: input.resolvedFile.relativePath,
lineStart: input.activeFile?.lineStart,
lineEnd: input.activeFile?.lineEnd,
})
: buildGitHubBranchTreeUrl({
remoteUrl: input.checkoutStatus.remoteUrl,
branch: input.checkoutStatus.currentBranch,
});
if (!url) {
return null;
}
return {
source: "github",
id: "github",
label: "GitHub",
url,
};
}
export function planWorkspaceOpenTargets(
input: PlanWorkspaceOpenTargetsInput,
): PlannedWorkspaceOpenTarget[] {
const resolvedFile = resolveActiveFileForOpenTargets(input);
const desktopTargets = planDesktopOpenTargets({ ...input, resolvedFile });
const githubTarget = planGitHubOpenTarget({ ...input, resolvedFile });
return githubTarget ? [...desktopTargets, githubTarget] : desktopTargets;
}