fix: use PowerShell Start-Process for Windows browser launch, prevent URL wrapping (#1870)
Closes #1574
This commit is contained in:
parent
0188b8eaa8
commit
77b220e9e5
4 changed files with 29 additions and 30 deletions
|
|
@ -1,7 +1,7 @@
|
|||
// GSD Login Dialog Component — OAuth login flow UI
|
||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||
import { getOAuthProviders } from "@gsd/pi-ai/oauth";
|
||||
import { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, type TUI } from "@gsd/pi-tui";
|
||||
import { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, truncateToWidth, type TUI } from "@gsd/pi-tui";
|
||||
import { execFile } from "child_process";
|
||||
import { theme } from "../theme/theme.js";
|
||||
import { DynamicBorder } from "./dynamic-border.js";
|
||||
|
|
@ -121,21 +121,25 @@ export class LoginDialogComponent extends Container implements Focusable {
|
|||
showAuth(url: string, instructions?: string): void {
|
||||
this.contentContainer.clear();
|
||||
this.contentContainer.addChild(new Spacer(1));
|
||||
this.contentContainer.addChild(new Text(theme.fg("accent", url), 1, 0));
|
||||
|
||||
// Truncate the visible URL text so it never wraps (which would break
|
||||
// the OSC 8 hyperlink). The full URL is still the link target.
|
||||
const maxUrlWidth = Math.max(20, this.tui.terminal.columns - 4);
|
||||
const displayUrl = truncateToWidth(url, maxUrlWidth);
|
||||
const urlLink = `\x1b]8;;${url}\x07${theme.fg("accent", displayUrl)}\x1b]8;;\x07`;
|
||||
this.contentContainer.addChild(new Text(urlLink, 1, 0));
|
||||
|
||||
const clickHint = process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open";
|
||||
const hyperlink = `\x1b]8;;${url}\x07${clickHint}\x1b]8;;\x07`;
|
||||
this.contentContainer.addChild(new Text(theme.fg("dim", hyperlink), 1, 0));
|
||||
this.contentContainer.addChild(new Text(theme.fg("dim", clickHint), 1, 0));
|
||||
|
||||
if (instructions) {
|
||||
this.contentContainer.addChild(new Spacer(1));
|
||||
this.contentContainer.addChild(new Text(theme.fg("warning", instructions), 1, 0));
|
||||
}
|
||||
|
||||
// Try to open browser — on Windows, `start` needs an empty title arg
|
||||
// so it treats the URL as a target, not a window title
|
||||
// PowerShell's Start-Process handles URLs with '&' safely; cmd /c start does not.
|
||||
if (process.platform === "win32") {
|
||||
execFile("cmd", ["/c", "start", "", url], () => {});
|
||||
execFile("powershell", ["-c", `Start-Process '${url.replace(/'/g, "''")}'`], () => {});
|
||||
} else {
|
||||
const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
|
||||
execFile(openCmd, [url], () => {});
|
||||
|
|
|
|||
|
|
@ -124,7 +124,8 @@ async function loadPico(): Promise<PicoModule> {
|
|||
/** Open a URL in the system browser (best-effort, non-blocking) */
|
||||
function openBrowser(url: string): void {
|
||||
if (process.platform === 'win32') {
|
||||
execFile('cmd', ['/c', 'start', '', url], () => {})
|
||||
// PowerShell's Start-Process handles URLs with '&' safely; cmd /c start does not.
|
||||
execFile('powershell', ['-c', `Start-Process '${url.replace(/'/g, "''")}'`], () => {})
|
||||
} else {
|
||||
const cmd = process.platform === 'darwin' ? 'open' : 'xdg-open'
|
||||
execFile(cmd, [url], () => {})
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
||||
import { writeFileSync, mkdirSync } from "node:fs";
|
||||
import { join, basename } from "node:path";
|
||||
import { exec } from "node:child_process";
|
||||
import { exec, execFile } from "node:child_process";
|
||||
import {
|
||||
getLedger, getProjectTotals, aggregateByPhase, aggregateBySlice,
|
||||
aggregateByModel, formatCost, formatTokenCount, loadLedgerFromDisk,
|
||||
|
|
@ -20,20 +20,13 @@ import { getErrorMessage } from "./error-utils.js";
|
|||
* Non-blocking, non-fatal — failures are silently ignored.
|
||||
*/
|
||||
export function openInBrowser(filePath: string): void {
|
||||
const cmd =
|
||||
process.platform === "darwin" ? "open" :
|
||||
process.platform === "win32" ? "start" :
|
||||
"xdg-open";
|
||||
|
||||
// On Windows, `start` needs an empty title argument when the path has spaces
|
||||
const args = process.platform === "win32"
|
||||
? `"" "${filePath}"`
|
||||
: `"${filePath}"`;
|
||||
|
||||
exec(`${cmd} ${args}`, (err) => {
|
||||
// Non-fatal — if the browser can't be opened, the file path is still shown
|
||||
if (err) void err;
|
||||
});
|
||||
if (process.platform === "win32") {
|
||||
// PowerShell's Start-Process handles paths with '&' and spaces safely.
|
||||
execFile("powershell", ["-c", `Start-Process '${filePath.replace(/'/g, "''")}'`], () => {});
|
||||
} else {
|
||||
const cmd = process.platform === "darwin" ? "open" : "xdg-open";
|
||||
execFile(cmd, [filePath], () => {});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { randomBytes } from 'node:crypto'
|
||||
import { exec, spawn, type ChildProcess, type SpawnOptions } from 'node:child_process'
|
||||
import { exec, execFile, spawn, type ChildProcess, type SpawnOptions } from 'node:child_process'
|
||||
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
|
||||
import { request as httpRequest } from 'node:http'
|
||||
import { createServer } from 'node:net'
|
||||
|
|
@ -12,12 +12,13 @@ const DEFAULT_PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '.
|
|||
|
||||
/** Open a URL in the user's default browser. */
|
||||
function openBrowser(url: string): void {
|
||||
const cmd = process.platform === 'darwin' ? 'open' :
|
||||
process.platform === 'win32' ? 'start' :
|
||||
'xdg-open'
|
||||
exec(`${cmd} "${url}"`, () => {
|
||||
// Ignore errors — user can manually open the URL
|
||||
})
|
||||
if (process.platform === 'win32') {
|
||||
// PowerShell's Start-Process handles URLs with '&' safely; cmd /c start does not.
|
||||
execFile('powershell', ['-c', `Start-Process '${url.replace(/'/g, "''")}'`], () => {})
|
||||
} else {
|
||||
const cmd = process.platform === 'darwin' ? 'open' : 'xdg-open'
|
||||
execFile(cmd, [url], () => {})
|
||||
}
|
||||
}
|
||||
|
||||
type WritableLike = Pick<typeof process.stderr, 'write'>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue