authorgravatar for 70063959+MaansenV@users.noreply.github.comMaansen <70063959+MaansenV@users.noreply.github.com> 2026-03-28 18:37:36+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-03-28 10:37:36-07:00
log48196e67a73f1f8ed8949254306f3cf41e4680bb
tree44d922ea5f6c10edc4422f6d608b2e8f145d31ba
parent80a176fe1fdd55ee32732297096d5199330c05e0
signature Signed by PGP key B5690EEEBB952194

fix(web): linkify Windows C:/ paths in terminal output (#1483)


3 files changed, 34 insertions(+), 2 deletions(-)

apps/web/src/terminal-links.test.ts+32
...@@ -43,6 +43,32 @@ describe("extractTerminalLinks", () => {...@@ -43,6 +43,32 @@ describe("extractTerminalLinks", () => {
43 },43 },
44 ]);44 ]);
45 });45 });
46
47 it("finds Windows absolute paths with forward slashes", () => {
48 const line = "see C:/Users/someone/project/src/file.ts:42 for details";
49 const path = "C:/Users/someone/project/src/file.ts:42";
50 const start = line.indexOf(path);
51 expect(extractTerminalLinks(line)).toEqual([
52 {
53 kind: "path",
54 text: path,
55 start,
56 end: start + path.length,
57 },
58 ]);
59 });
60
61 it("trims trailing punctuation from Windows forward-slash paths", () => {
62 const line = "(C:/tmp/x.ts).";
63 expect(extractTerminalLinks(line)).toEqual([
64 {
65 kind: "path",
66 text: "C:/tmp/x.ts",
67 start: 1,
68 end: 12,
69 },
70 ]);
71 });
46});72});
4773
48describe("resolvePathLinkTarget", () => {74describe("resolvePathLinkTarget", () => {
...@@ -60,6 +86,12 @@ describe("resolvePathLinkTarget", () => {...@@ -60,6 +86,12 @@ describe("resolvePathLinkTarget", () => {
60 resolvePathLinkTarget("/Users/julius/project/src/main.ts:12", "/Users/julius/project"),86 resolvePathLinkTarget("/Users/julius/project/src/main.ts:12", "/Users/julius/project"),
61 ).toBe("/Users/julius/project/src/main.ts:12");87 ).toBe("/Users/julius/project/src/main.ts:12");
62 });88 });
89
90 it("keeps Windows absolute paths with forward slashes unchanged", () => {
91 expect(
92 resolvePathLinkTarget("C:/Users/julius/project/src/main.ts:12", "C:\\Users\\julius\\project"),
93 ).toBe("C:/Users/julius/project/src/main.ts:12");
94 });
63});95});
6496
65describe("isTerminalLinkActivation", () => {97describe("isTerminalLinkActivation", () => {
apps/web/src/terminal-links.ts+1-1
...@@ -11,7 +11,7 @@ export interface TerminalLinkMatch {...@@ -11,7 +11,7 @@ export interface TerminalLinkMatch {
1111
12const URL_PATTERN = /https?:\/\/[^\s"'`<>]+/g;12const URL_PATTERN = /https?:\/\/[^\s"'`<>]+/g;
13const FILE_PATH_PATTERN =13const FILE_PATH_PATTERN =
14 /(?:~\/|\.{1,2}\/|\/|[A-Za-z]:\\|\\\\)[^\s"'`<>]+|[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+(?::\d+){0,2}/g;14 /(?:~\/|\.{1,2}\/|\/|[A-Za-z]:[\\/]|\\\\)[^\s"'`<>]+|[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+(?::\d+){0,2}/g;
15const TRAILING_PUNCTUATION_PATTERN = /[.,;!?]+$/;15const TRAILING_PUNCTUATION_PATTERN = /[.,;!?]+$/;
1616
17function trimClosingDelimiters(value: string): string {17function trimClosingDelimiters(value: string): string {
apps/web/src/wsTransport.test.ts+1-1
...@@ -175,7 +175,7 @@ describe("WsTransport", () => {...@@ -175,7 +175,7 @@ describe("WsTransport", () => {
175 expect(warnSpy).toHaveBeenNthCalledWith(175 expect(warnSpy).toHaveBeenNthCalledWith(
176 1,176 1,
177 "Dropped inbound WebSocket envelope",177 "Dropped inbound WebSocket envelope",
178 "SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3)",178 expect.stringMatching(/^SyntaxError:/),
179 );179 );
180 expect(warnSpy).toHaveBeenNthCalledWith(180 expect(warnSpy).toHaveBeenNthCalledWith(
181 2,181 2,