authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-03 19:07:24-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-03 19:07:24-07:00
log617edab6539ee6573055dc9fc45fdeacdb12f673
tree22c7b05c4ff9aa081990619c467f5d23675dc81b
parent3e2c1a66f74f0a45768332c360cd3682f61129e1
signature Signed by PGP key B5690EEEBB952194

fix(server): reveal normalized paths in File Explorer (#9551)


2 files changed, 19 insertions(+), 5 deletions(-)

apps/server/src/process/externalLauncher.test.ts+10-4
...@@ -206,7 +206,9 @@ it.effect("reveals a file in File Explorer through PowerShell on Windows", () =>...@@ -206,7 +206,9 @@ it.effect("reveals a file in File Explorer through PowerShell on Windows", () =>
206 const launcher = yield* ExternalLauncher.ExternalLauncher;206 const launcher = yield* ExternalLauncher.ExternalLauncher;
207 yield* launcher.launchEditor({207 yield* launcher.launchEditor({
208 editor: "file-manager",208 editor: "file-manager",
209 cwd: "C:\\workspace with spaces\\media\\author's clip.mp4",209 // Web file links normalize separators even when the server runs on
210 // Windows. Explorer's `/select` switch requires Windows separators.
211 cwd: "C:/workspace with spaces/media/author's clip.mp4",
210 reveal: true,212 reveal: true,
211 });213 });
212 return yield* launcher.resolveFileManagerRevealKind();214 return yield* launcher.resolveFileManagerRevealKind();
...@@ -261,8 +263,12 @@ it.skipIf(process.platform !== "win32")(...@@ -261,8 +263,12 @@ it.skipIf(process.platform !== "win32")(
261 const outputPath = NodePath.join(tempDir, "argv.txt");263 const outputPath = NodePath.join(tempDir, "argv.txt");
262 NodeFS.writeFileSync(recorderPath, `@echo off\r\n>"${outputPath}" echo(%*\r\n`);264 NodeFS.writeFileSync(recorderPath, `@echo off\r\n>"${outputPath}" echo(%*\r\n`);
263265
264 const target = "C:\\workspace with spaces\\media\\author's clip.mp4";266 const target = "C:/workspace with spaces/media/author's clip.mp4";
265 const source = ExternalLauncher.buildFileExplorerRevealPowerShellSource(recorderPath, target);267 const explorerTarget = target.replaceAll("/", "\\");
268 const source = ExternalLauncher.buildFileExplorerRevealPowerShellSource(
269 recorderPath,
270 explorerTarget,
271 );
266 const powerShellPath = `${process.env.SYSTEMROOT ?? "C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;272 const powerShellPath = `${process.env.SYSTEMROOT ?? "C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
267 NodeChildProcess.execFileSync(273 NodeChildProcess.execFileSync(
268 powerShellPath,274 powerShellPath,
...@@ -290,7 +296,7 @@ it.skipIf(process.platform !== "win32")(...@@ -290,7 +296,7 @@ it.skipIf(process.platform !== "win32")(
290 }296 }
291 await sleep(200);297 await sleep(200);
292 const recorded = NodeFS.readFileSync(outputPath, "utf8").trim();298 const recorded = NodeFS.readFileSync(outputPath, "utf8").trim();
293 assert.equal(recorded, `/select,"${target}"`);299 assert.equal(recorded, `/select,"${explorerTarget}"`);
294 } finally {300 } finally {
295 NodeFS.rmSync(tempDir, { recursive: true, force: true });301 NodeFS.rmSync(tempDir, { recursive: true, force: true });
296 }302 }
apps/server/src/process/externalLauncher.ts+9-1
...@@ -609,6 +609,10 @@ function fileExplorerRevealLaunch(...@@ -609,6 +609,10 @@ function fileExplorerRevealLaunch(
609 };609 };
610}610}
611611
612function normalizeWindowsFileManagerPath(target: string): string {
613 return target.replaceAll("/", "\\");
614}
615
612const resolveFileManagerRevealLaunch = Effect.fn("resolveFileManagerRevealLaunch")(function* (616const resolveFileManagerRevealLaunch = Effect.fn("resolveFileManagerRevealLaunch")(function* (
613 target: string,617 target: string,
614 platform: NodeJS.Platform,618 platform: NodeJS.Platform,
...@@ -626,7 +630,11 @@ const resolveFileManagerRevealLaunch = Effect.fn("resolveFileManagerRevealLaunch...@@ -626,7 +630,11 @@ const resolveFileManagerRevealLaunch = Effect.fn("resolveFileManagerRevealLaunch
626 }630 }
627631
628 if (platform === "win32") {632 if (platform === "win32") {
629 return fileExplorerRevealLaunch(target, target, resolvePowerShellPath(env));633 return fileExplorerRevealLaunch(
634 target,
635 normalizeWindowsFileManagerPath(target),
636 resolvePowerShellPath(env),
637 );
630 }638 }
631639
632 if (640 if (