From 451bbfc5ff4ff64650cc7c7f2d229bccf4428917 Mon Sep 17 00:00:00 2001 From: Donald Silveira <9506146+donnes@users.noreply.github.com> Date: Fri, 25 Sep 2026 09:48:13 -0300 Subject: [PATCH 1/2] feat(web): open projects in px0 when installed Co-Authored-By: Claude Opus 5.5 (1M context) --- apps/server/src/process/externalLauncher.test.ts | 6 ++++++ apps/web/src/components/Icons.tsx | 8 ++++++++ apps/web/src/components/chat/OpenInPicker.tsx | 6 ++++++ packages/contracts/src/editor.ts | 3 +++ 4 files changed, 23 insertions(+) diff --git a/apps/server/src/process/externalLauncher.test.ts b/apps/server/src/process/externalLauncher.test.ts index f714a70f783d..a89377ddf3d9 100644 --- a/apps/server/src/process/externalLauncher.test.ts +++ b/apps/server/src/process/externalLauncher.test.ts @@ -989,6 +989,12 @@ for (const { platform, installPath, editor, args } of [ editor: "zed", args: ["/workspace with spaces/file.ts:12:4"], }, + { + platform: "linux", + installPath: ".local/bin/px0", + editor: "px0", + args: ["/workspace with spaces/file.ts:12:4"], + }, ] as const) { it.effect.skipIf(windowsHost && platform !== "win32")( `discovers and launches ${editor} outside PATH on ${platform}`, diff --git a/apps/web/src/components/Icons.tsx b/apps/web/src/components/Icons.tsx index e4d41d53c3a8..2445e78ad3ac 100644 --- a/apps/web/src/components/Icons.tsx +++ b/apps/web/src/components/Icons.tsx @@ -709,6 +709,14 @@ export const AntigravityIcon: Icon = (props) => ( ); +export const Px0Icon: Icon = (props) => ( + + + + + +); + export const OpenCodeIcon: Icon = (props) => ( diff --git a/apps/web/src/components/chat/OpenInPicker.tsx b/apps/web/src/components/chat/OpenInPicker.tsx index 3c0b2cd118ed..17fcdb81e6e3 100644 --- a/apps/web/src/components/chat/OpenInPicker.tsx +++ b/apps/web/src/components/chat/OpenInPicker.tsx @@ -36,6 +36,7 @@ import { FinderIcon, Icon, KiroIcon, + Px0Icon, TraeIcon, VisualStudioCode, VisualStudioCodeInsiders, @@ -112,6 +113,11 @@ export const resolveOpenInOptions = ( value: "antigravity", kind: "brand", }, + { + Icon: Px0Icon, + value: "px0", + kind: "brand", + }, { Icon: IntelliJIdeaIcon, value: "idea", diff --git a/packages/contracts/src/editor.ts b/packages/contracts/src/editor.ts index 88028b796991..058c563bf15c 100644 --- a/packages/contracts/src/editor.ts +++ b/packages/contracts/src/editor.ts @@ -60,6 +60,9 @@ export const EDITORS = [ remoteScheme: "zed", }, { id: "antigravity", label: "Antigravity", commands: ["agy"], launchStyle: "goto" }, + // px0 serves its UI from a local server and opens it in the browser; each + // launch starts its own server on the next free port. It takes `path:line`. + { id: "px0", label: "px0", commands: ["px0"], launchStyle: "direct-path" }, { id: "idea", label: "IntelliJ IDEA", commands: ["idea"], launchStyle: "line-column" }, { id: "aqua", label: "Aqua", commands: ["aqua"], launchStyle: "line-column" }, { id: "clion", label: "CLion", commands: ["clion"], launchStyle: "line-column" }, From c4bf8ebd2ef373371ac296cb6b6c3ff867c60e83 Mon Sep 17 00:00:00 2001 From: Donald Silveira <9506146+donnes@users.noreply.github.com> Date: Fri, 25 Sep 2026 10:50:35 -0300 Subject: [PATCH 2/2] test(server): cover px0 discovery on macOS and Windows Co-Authored-By: Claude Opus 5.5 (1M context) --- .../src/process/externalLauncher.test.ts | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/apps/server/src/process/externalLauncher.test.ts b/apps/server/src/process/externalLauncher.test.ts index a89377ddf3d9..17c1c34b44d8 100644 --- a/apps/server/src/process/externalLauncher.test.ts +++ b/apps/server/src/process/externalLauncher.test.ts @@ -1034,6 +1034,46 @@ for (const { platform, installPath, editor, args } of [ ); } +// px0 has no app bundle or Windows installer location: it relies on the PATH +// that server startup hydrates from the login shell (macOS) or the known CLI +// dirs such as %USERPROFILE%\.local\bin (Windows). +for (const { platform, binary } of [ + { platform: "darwin", binary: "px0" }, + { platform: "win32", binary: "px0.exe" }, +] as const) { + it.effect.skipIf(windowsHost && platform !== "win32")( + `discovers and launches px0 from PATH on ${platform}`, + () => + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const binDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-px0-" }); + yield* fs.writeFileString(path.join(binDir, binary), "#!/bin/sh\n"); + yield* fs.chmod(path.join(binDir, binary), 0o755); + let spawned: ChildProcess.StandardCommand | undefined; + yield* Effect.gen(function* () { + const launcher = yield* ExternalLauncher.ExternalLauncher; + assert.include(yield* launcher.resolveAvailableEditors(), "px0"); + yield* launcher.launchEditor({ editor: "px0", cwd: "/workspace/file.ts:12:4" }); + }).pipe( + Effect.provide( + testLayer({ + platform, + env: { PATH: binDir, PATHEXT: ".COM;.EXE;.BAT;.CMD" }, + onSpawn: (command) => { + spawned = command; + }, + }), + ), + ); + assert.ok(spawned); + assert.equal(spawned.command, "px0"); + assert.deepEqual(spawned.args, ["/workspace/file.ts:12:4"]); + assert.equal(spawned.options.shell, false); + }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), + ); +} + it.effect.skipIf(windowsHost)("ignores unusable app bundles and keeps PATH launchers first", () => Effect.gen(function* () { const fs = yield* FileSystem.FileSystem;