Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added packages/desktop/assets/editor-targets/fork.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions packages/desktop/src/features/editor-targets/registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { listAvailableEditorTargets, openEditorTarget } from "./registry.js";
import type { EditorTargetIcon, EditorTargetRuntime } from "./target.js";
import { cursorTarget } from "./targets/cursor.js";
import { explorerTarget, fileManagerTarget, finderTarget } from "./targets/file-manager.js";
import { forkTarget } from "./targets/fork.js";
import { intellijIdeaTarget } from "./targets/intellij-idea.js";
import { pycharmTarget } from "./targets/pycharm.js";
import { vscodeTarget } from "./targets/vscode.js";
Expand Down Expand Up @@ -255,6 +256,35 @@ describe("editor target registry", () => {
]);
});

it("opens the repository in Fork through the macOS application", async () => {
const runtime = new FakeEditorTargets("darwin");
runtime.installMacApplication("Fork");

expect(await forkTarget.isInstalled(runtime)).toBe(true);
expect(await forkTarget.describe(runtime)).toEqual({
id: "fork",
label: "Fork",
kind: "editor",
icon: { kind: "image", dataUrl: "data:image/png;base64,fork.png" },
});

// Fork is a repository GUI: it always opens the workspace root, never a file.
await forkTarget.launch(
{ workspacePath: "/repo", filePath: "/repo/src/app.ts", line: 12, column: 4 },
runtime,
);

expect(runtime.openedMacApplications).toEqual([{ applicationName: "Fork", paths: ["/repo"] }]);
});

it("does not offer Fork when its application is absent or off macOS", async () => {
const withoutApp = new FakeEditorTargets("darwin");
const onLinux = new FakeEditorTargets("linux");

expect(await forkTarget.isInstalled(withoutApp)).toBe(false);
expect(await forkTarget.isInstalled(onLinux)).toBe(false);
});

it("delegates folder opening and file reveal to the system file manager", async () => {
const runtime = new FakeEditorTargets("win32");

Expand Down
2 changes: 2 additions & 0 deletions packages/desktop/src/features/editor-targets/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { cursorTarget } from "./targets/cursor.js";
import { datagripTarget } from "./targets/datagrip.js";
import { dataspellTarget } from "./targets/dataspell.js";
import { explorerTarget, fileManagerTarget, finderTarget } from "./targets/file-manager.js";
import { forkTarget } from "./targets/fork.js";
import { golandTarget } from "./targets/goland.js";
import { intellijIdeaTarget } from "./targets/intellij-idea.js";
import { kiroTarget } from "./targets/kiro.js";
Expand Down Expand Up @@ -50,6 +51,7 @@ export const EDITOR_TARGETS: readonly EditorTarget[] = [
finderTarget,
explorerTarget,
fileManagerTarget,
forkTarget,
];

export async function listAvailableEditorTargets(
Expand Down
27 changes: 27 additions & 0 deletions packages/desktop/src/features/editor-targets/targets/fork.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { EditorTarget } from "../target.js";

// Fork (https://git-fork.com) is a macOS/Windows Git client. It opens a
// *repository*, not a file, so we always hand it the workspace root and ignore
// any filePath. Launch goes through the macOS application (`open -a Fork <repo>`)
// rather than a CLI: Fork's `fork` command-line helper is opt-in (the user has
// to install it) and is cwd-based, whereas the app bundle is always present once
// Fork is installed and reliably opens the repo as a tab. Windows support can
// follow once its launcher can be verified.
export const forkTarget: EditorTarget = {
id: "fork",
async describe(runtime) {
return {
id: this.id,
label: "Fork",
kind: "editor",
icon: await runtime.loadIcon("fork.png"),
};
},
async isInstalled(runtime) {
return runtime.hasMacApplication("Fork");
},
async launch(input, runtime) {
if (!runtime.hasMacApplication("Fork")) throw new Error("Fork is not installed");
await runtime.openMacApplication({ applicationName: "Fork", paths: [input.workspacePath] });
},
};