From c0ff1b7157b4b29e62bbc99fa52ca5b3ea3355ab Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 5 May 2026 20:55:46 +0000 Subject: [PATCH 01/12] Initial commit with task details Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: https://github.com/ProverCoderAI/docker-git/issues/240 --- .gitkeep | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 00000000..e022f40c --- /dev/null +++ b/.gitkeep @@ -0,0 +1 @@ +# .gitkeep file auto-generated at 2026-05-05T20:55:46.098Z for PR creation at branch issue-240-b08428ed1942 for issue https://github.com/ProverCoderAI/docker-git/issues/240 \ No newline at end of file From de920a01317bf4c4e5ff09ae99dec4a1a52e7e41 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 5 May 2026 21:03:36 +0000 Subject: [PATCH 02/12] feat(app): detect image paths in terminal output Add a pure module that scans terminal output for absolute image paths (png, jpg, jpeg, gif, webp), strips ANSI escape sequences, and deduplicates results. This is the first step toward rendering inline image previews for paths that appear in the terminal. --- packages/app/src/web/terminal-image-paths.ts | 36 +++++++++ .../docker-git/terminal-image-paths.test.ts | 75 +++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 packages/app/src/web/terminal-image-paths.ts create mode 100644 packages/app/tests/docker-git/terminal-image-paths.test.ts diff --git a/packages/app/src/web/terminal-image-paths.ts b/packages/app/src/web/terminal-image-paths.ts new file mode 100644 index 00000000..c791002f --- /dev/null +++ b/packages/app/src/web/terminal-image-paths.ts @@ -0,0 +1,36 @@ +const supportedExtensions = ["png", "jpg", "jpeg", "gif", "webp"] as const + +const extensionAlternation = supportedExtensions.join("|") + +const imagePathPattern = new RegExp( + String.raw`(?:^|[\s"'(<>\[\]{}|])(/[^\s"'(<>\[\]{}|\\]+\.(?:${extensionAlternation}))(?=$|[\s"')<>\[\]{}|.,;:?!])`, + "giu" +) + +const escapeChar = String.fromCharCode(0x1b) +const bellChar = String.fromCharCode(0x07) + +const buildAnsiPattern = (source: string): RegExp => new RegExp(source, "gu") + +const ansiCsiPattern = buildAnsiPattern(`${escapeChar}\\[[0-?]*[ -/]*[@-~]`) +const ansiOscPattern = buildAnsiPattern(`${escapeChar}\\][\\s\\S]*?(?:${bellChar}|${escapeChar}\\\\)`) +const ansiOtherEscapePattern = buildAnsiPattern(`${escapeChar}.`) + +export const stripTerminalAnsi = (text: string): string => + text.replace(ansiOscPattern, "").replace(ansiCsiPattern, "").replace(ansiOtherEscapePattern, "") + +export const detectTerminalImagePaths = (text: string): ReadonlyArray => { + const matches = new Set() + for (const match of stripTerminalAnsi(text).matchAll(imagePathPattern)) { + const candidate = match[1] + if (candidate !== undefined && candidate.length > 0) { + matches.add(candidate) + } + } + return [...matches] +} + +export const isSupportedTerminalImagePath = (path: string): boolean => { + const lower = path.toLowerCase() + return supportedExtensions.some((extension) => lower.endsWith(`.${extension}`)) +} diff --git a/packages/app/tests/docker-git/terminal-image-paths.test.ts b/packages/app/tests/docker-git/terminal-image-paths.test.ts new file mode 100644 index 00000000..02fb9e50 --- /dev/null +++ b/packages/app/tests/docker-git/terminal-image-paths.test.ts @@ -0,0 +1,75 @@ +import { describe, expect, it } from "@effect/vitest" + +import { + detectTerminalImagePaths, + isSupportedTerminalImagePath, + stripTerminalAnsi +} from "../../src/web/terminal-image-paths.js" + +describe("terminal image path detection", () => { + it("detects a single absolute image path", () => { + expect(detectTerminalImagePaths("see /tmp/issue232-main.png for details")).toEqual([ + "/tmp/issue232-main.png" + ]) + }) + + it("detects multiple distinct image paths", () => { + const text = "saved /tmp/a.png and /var/data/b.jpg, also /home/user/c.webp" + expect(detectTerminalImagePaths(text)).toEqual([ + "/tmp/a.png", + "/var/data/b.jpg", + "/home/user/c.webp" + ]) + }) + + it("deduplicates repeated image paths", () => { + expect(detectTerminalImagePaths("a /tmp/x.png b /tmp/x.png c")).toEqual(["/tmp/x.png"]) + }) + + it("ignores relative paths", () => { + expect(detectTerminalImagePaths("./relative.png and image.jpg here")).toEqual([]) + }) + + it("ignores unsupported extensions", () => { + expect(detectTerminalImagePaths("/tmp/file.txt /tmp/photo.bmp /tmp/doc.pdf")).toEqual([]) + }) + + it("trims trailing punctuation from detected paths", () => { + expect(detectTerminalImagePaths("look at /tmp/foo.png, then /var/bar.gif.")).toEqual([ + "/tmp/foo.png", + "/var/bar.gif" + ]) + }) + + it("recognises uppercase extensions", () => { + expect(detectTerminalImagePaths("/tmp/Photo.PNG /tmp/Cover.JPG")).toEqual([ + "/tmp/Photo.PNG", + "/tmp/Cover.JPG" + ]) + }) + + it("strips ANSI CSI sequences before scanning", () => { + const text = `/tmp/colored.png` + expect(stripTerminalAnsi(text)).toBe("/tmp/colored.png") + expect(detectTerminalImagePaths(text)).toEqual(["/tmp/colored.png"]) + }) + + it("strips ANSI OSC sequences terminated by BEL or ST", () => { + const belTerminated = `]0;title/tmp/bel.png` + const stTerminated = `]0;title\\/tmp/st.png` + expect(stripTerminalAnsi(belTerminated)).toBe("/tmp/bel.png") + expect(stripTerminalAnsi(stTerminated)).toBe("/tmp/st.png") + expect(detectTerminalImagePaths(belTerminated)).toEqual(["/tmp/bel.png"]) + expect(detectTerminalImagePaths(stTerminated)).toEqual(["/tmp/st.png"]) + }) + + it("classifies supported image extensions", () => { + expect(isSupportedTerminalImagePath("/tmp/a.png")).toBe(true) + expect(isSupportedTerminalImagePath("/tmp/a.JPG")).toBe(true) + expect(isSupportedTerminalImagePath("/tmp/a.jpeg")).toBe(true) + expect(isSupportedTerminalImagePath("/tmp/a.gif")).toBe(true) + expect(isSupportedTerminalImagePath("/tmp/a.webp")).toBe(true) + expect(isSupportedTerminalImagePath("/tmp/a.bmp")).toBe(false) + expect(isSupportedTerminalImagePath("/tmp/a.txt")).toBe(false) + }) +}) From 27c7d7f076a31eb0347227d277ed23478f248caf Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 5 May 2026 21:15:27 +0000 Subject: [PATCH 03/12] fix(app): apply lint preferences in terminal image path detection - Use String.fromCodePoint and uppercase hex literals - Use String.raw for regex sources containing backslashes - Switch test paths from /tmp to /var/data to satisfy publicly-writable-directories rule --- packages/app/src/web/terminal-image-paths.ts | 8 +-- .../docker-git/terminal-image-paths.test.ts | 61 ++++++++++--------- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/packages/app/src/web/terminal-image-paths.ts b/packages/app/src/web/terminal-image-paths.ts index c791002f..7dfa246f 100644 --- a/packages/app/src/web/terminal-image-paths.ts +++ b/packages/app/src/web/terminal-image-paths.ts @@ -7,13 +7,13 @@ const imagePathPattern = new RegExp( "giu" ) -const escapeChar = String.fromCharCode(0x1b) -const bellChar = String.fromCharCode(0x07) +const escapeChar = String.fromCodePoint(0x1B) +const bellChar = String.fromCodePoint(0x07) const buildAnsiPattern = (source: string): RegExp => new RegExp(source, "gu") -const ansiCsiPattern = buildAnsiPattern(`${escapeChar}\\[[0-?]*[ -/]*[@-~]`) -const ansiOscPattern = buildAnsiPattern(`${escapeChar}\\][\\s\\S]*?(?:${bellChar}|${escapeChar}\\\\)`) +const ansiCsiPattern = buildAnsiPattern(String.raw`${escapeChar}\[[0-?]*[ -/]*[@-~]`) +const ansiOscPattern = buildAnsiPattern(String.raw`${escapeChar}\][\s\S]*?(?:${bellChar}|${escapeChar}\\)`) const ansiOtherEscapePattern = buildAnsiPattern(`${escapeChar}.`) export const stripTerminalAnsi = (text: string): string => diff --git a/packages/app/tests/docker-git/terminal-image-paths.test.ts b/packages/app/tests/docker-git/terminal-image-paths.test.ts index 02fb9e50..51e90b2a 100644 --- a/packages/app/tests/docker-git/terminal-image-paths.test.ts +++ b/packages/app/tests/docker-git/terminal-image-paths.test.ts @@ -8,22 +8,22 @@ import { describe("terminal image path detection", () => { it("detects a single absolute image path", () => { - expect(detectTerminalImagePaths("see /tmp/issue232-main.png for details")).toEqual([ - "/tmp/issue232-main.png" + expect(detectTerminalImagePaths("see /var/data/issue232-main.png for details")).toEqual([ + "/var/data/issue232-main.png" ]) }) it("detects multiple distinct image paths", () => { - const text = "saved /tmp/a.png and /var/data/b.jpg, also /home/user/c.webp" + const text = "saved /var/data/a.png and /var/data/sub/b.jpg, also /home/user/c.webp" expect(detectTerminalImagePaths(text)).toEqual([ - "/tmp/a.png", - "/var/data/b.jpg", + "/var/data/a.png", + "/var/data/sub/b.jpg", "/home/user/c.webp" ]) }) it("deduplicates repeated image paths", () => { - expect(detectTerminalImagePaths("a /tmp/x.png b /tmp/x.png c")).toEqual(["/tmp/x.png"]) + expect(detectTerminalImagePaths("a /var/data/x.png b /var/data/x.png c")).toEqual(["/var/data/x.png"]) }) it("ignores relative paths", () => { @@ -31,45 +31,48 @@ describe("terminal image path detection", () => { }) it("ignores unsupported extensions", () => { - expect(detectTerminalImagePaths("/tmp/file.txt /tmp/photo.bmp /tmp/doc.pdf")).toEqual([]) + expect(detectTerminalImagePaths("/var/data/file.txt /var/data/photo.bmp /var/data/doc.pdf")).toEqual([]) }) it("trims trailing punctuation from detected paths", () => { - expect(detectTerminalImagePaths("look at /tmp/foo.png, then /var/bar.gif.")).toEqual([ - "/tmp/foo.png", - "/var/bar.gif" + expect(detectTerminalImagePaths("look at /var/data/foo.png, then /var/data/bar.gif.")).toEqual([ + "/var/data/foo.png", + "/var/data/bar.gif" ]) }) it("recognises uppercase extensions", () => { - expect(detectTerminalImagePaths("/tmp/Photo.PNG /tmp/Cover.JPG")).toEqual([ - "/tmp/Photo.PNG", - "/tmp/Cover.JPG" + expect(detectTerminalImagePaths("/var/data/Photo.PNG /var/data/Cover.JPG")).toEqual([ + "/var/data/Photo.PNG", + "/var/data/Cover.JPG" ]) }) it("strips ANSI CSI sequences before scanning", () => { - const text = `/tmp/colored.png` - expect(stripTerminalAnsi(text)).toBe("/tmp/colored.png") - expect(detectTerminalImagePaths(text)).toEqual(["/tmp/colored.png"]) + const escapeChar = String.fromCodePoint(0x1B) + const text = `${escapeChar}[32m/var/data/colored.png${escapeChar}[0m` + expect(stripTerminalAnsi(text)).toBe("/var/data/colored.png") + expect(detectTerminalImagePaths(text)).toEqual(["/var/data/colored.png"]) }) it("strips ANSI OSC sequences terminated by BEL or ST", () => { - const belTerminated = `]0;title/tmp/bel.png` - const stTerminated = `]0;title\\/tmp/st.png` - expect(stripTerminalAnsi(belTerminated)).toBe("/tmp/bel.png") - expect(stripTerminalAnsi(stTerminated)).toBe("/tmp/st.png") - expect(detectTerminalImagePaths(belTerminated)).toEqual(["/tmp/bel.png"]) - expect(detectTerminalImagePaths(stTerminated)).toEqual(["/tmp/st.png"]) + const escapeChar = String.fromCodePoint(0x1B) + const bellChar = String.fromCodePoint(0x07) + const belTerminated = `${escapeChar}]0;title${bellChar}/var/data/bel.png` + const stTerminated = String.raw`${escapeChar}]0;title${escapeChar}\/var/data/st.png` + expect(stripTerminalAnsi(belTerminated)).toBe("/var/data/bel.png") + expect(stripTerminalAnsi(stTerminated)).toBe("/var/data/st.png") + expect(detectTerminalImagePaths(belTerminated)).toEqual(["/var/data/bel.png"]) + expect(detectTerminalImagePaths(stTerminated)).toEqual(["/var/data/st.png"]) }) it("classifies supported image extensions", () => { - expect(isSupportedTerminalImagePath("/tmp/a.png")).toBe(true) - expect(isSupportedTerminalImagePath("/tmp/a.JPG")).toBe(true) - expect(isSupportedTerminalImagePath("/tmp/a.jpeg")).toBe(true) - expect(isSupportedTerminalImagePath("/tmp/a.gif")).toBe(true) - expect(isSupportedTerminalImagePath("/tmp/a.webp")).toBe(true) - expect(isSupportedTerminalImagePath("/tmp/a.bmp")).toBe(false) - expect(isSupportedTerminalImagePath("/tmp/a.txt")).toBe(false) + expect(isSupportedTerminalImagePath("/var/data/a.png")).toBe(true) + expect(isSupportedTerminalImagePath("/var/data/a.JPG")).toBe(true) + expect(isSupportedTerminalImagePath("/var/data/a.jpeg")).toBe(true) + expect(isSupportedTerminalImagePath("/var/data/a.gif")).toBe(true) + expect(isSupportedTerminalImagePath("/var/data/a.webp")).toBe(true) + expect(isSupportedTerminalImagePath("/var/data/a.bmp")).toBe(false) + expect(isSupportedTerminalImagePath("/var/data/a.txt")).toBe(false) }) }) From fa735f7d54eced9ece5bd34c98ed2ec5f3b048d8 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 5 May 2026 21:15:34 +0000 Subject: [PATCH 04/12] feat(api): add image path validation core for terminal fetch endpoint Pure validation module that produces a TerminalImageFetchPlan from a candidate absolute container path. Rejects empty, relative, whitespace, control-character, parent-traversal, and unsupported-extension inputs and returns the matched media type for valid paths. --- .../src/services/terminal-image-fetch-core.ts | 57 ++++++++++++++++ .../tests/terminal-image-fetch-core.test.ts | 65 +++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 packages/api/src/services/terminal-image-fetch-core.ts create mode 100644 packages/api/tests/terminal-image-fetch-core.test.ts diff --git a/packages/api/src/services/terminal-image-fetch-core.ts b/packages/api/src/services/terminal-image-fetch-core.ts new file mode 100644 index 00000000..937ac99d --- /dev/null +++ b/packages/api/src/services/terminal-image-fetch-core.ts @@ -0,0 +1,57 @@ +export type TerminalImageFetchPlan = + | { + readonly _tag: "InvalidTerminalImageFetch" + readonly message: string + } + | { + readonly _tag: "ValidTerminalImageFetch" + readonly containerPath: string + readonly mediaType: string + } + +export const terminalImageFetchMaxBytes = 10 * 1024 * 1024 + +const supportedExtensionMediaTypes = new Map([ + ["gif", "image/gif"], + ["jpeg", "image/jpeg"], + ["jpg", "image/jpeg"], + ["png", "image/png"], + ["webp", "image/webp"] +]) + +const controlCharRange = `${String.fromCodePoint(0)}-${String.fromCodePoint(0x1F)}` +const deleteChar = String.fromCodePoint(0x7F) +const invalidCharacterPattern = new RegExp(String.raw`[\s${controlCharRange}${deleteChar}]`, "u") +const traversalPattern = /(?:^|\/)(?:\.|\.\.)(?=\/|$)/u + +const lowercaseExtension = (path: string): string | null => { + const lastDot = path.lastIndexOf(".") + if (lastDot < 0 || lastDot === path.length - 1) { + return null + } + return path.slice(lastDot + 1).toLowerCase() +} + +export const planTerminalImageFetch = (path: string): TerminalImageFetchPlan => { + if (typeof path !== "string" || path.length === 0) { + return { _tag: "InvalidTerminalImageFetch", message: "Image path is required." } + } + if (!path.startsWith("/")) { + return { _tag: "InvalidTerminalImageFetch", message: "Image path must be absolute." } + } + if (invalidCharacterPattern.test(path)) { + return { _tag: "InvalidTerminalImageFetch", message: "Image path contains invalid characters." } + } + if (traversalPattern.test(path)) { + return { _tag: "InvalidTerminalImageFetch", message: "Image path must not contain '.' or '..' segments." } + } + const extension = lowercaseExtension(path) + if (extension === null) { + return { _tag: "InvalidTerminalImageFetch", message: "Image path must include a file extension." } + } + const mediaType = supportedExtensionMediaTypes.get(extension) + if (mediaType === undefined) { + return { _tag: "InvalidTerminalImageFetch", message: `Unsupported image extension: .${extension}` } + } + return { _tag: "ValidTerminalImageFetch", containerPath: path, mediaType } +} diff --git a/packages/api/tests/terminal-image-fetch-core.test.ts b/packages/api/tests/terminal-image-fetch-core.test.ts new file mode 100644 index 00000000..635871e8 --- /dev/null +++ b/packages/api/tests/terminal-image-fetch-core.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, it } from "@effect/vitest" + +import { planTerminalImageFetch } from "../src/services/terminal-image-fetch-core.js" + +describe("terminal image fetch core", () => { + it("accepts an absolute path with a supported image extension", () => { + expect(planTerminalImageFetch("/tmp/issue232-main.png")).toEqual({ + _tag: "ValidTerminalImageFetch", + containerPath: "/tmp/issue232-main.png", + mediaType: "image/png" + }) + }) + + it("maps each supported extension to its media type", () => { + expect(planTerminalImageFetch("/a.jpg")).toMatchObject({ mediaType: "image/jpeg" }) + expect(planTerminalImageFetch("/a.jpeg")).toMatchObject({ mediaType: "image/jpeg" }) + expect(planTerminalImageFetch("/a.gif")).toMatchObject({ mediaType: "image/gif" }) + expect(planTerminalImageFetch("/a.webp")).toMatchObject({ mediaType: "image/webp" }) + expect(planTerminalImageFetch("/a.PNG")).toMatchObject({ mediaType: "image/png" }) + }) + + it("rejects an empty path", () => { + expect(planTerminalImageFetch("")).toEqual({ + _tag: "InvalidTerminalImageFetch", + message: "Image path is required." + }) + }) + + it("rejects a relative path", () => { + expect(planTerminalImageFetch("tmp/photo.png")).toEqual({ + _tag: "InvalidTerminalImageFetch", + message: "Image path must be absolute." + }) + }) + + it("rejects whitespace and control characters", () => { + expect(planTerminalImageFetch("/tmp/has space.png")).toMatchObject({ + _tag: "InvalidTerminalImageFetch" + }) + expect(planTerminalImageFetch("/tmp/has\nnewline.png")).toMatchObject({ + _tag: "InvalidTerminalImageFetch" + }) + }) + + it("rejects parent-directory and current-directory traversal segments", () => { + expect(planTerminalImageFetch("/tmp/../etc/photo.png")).toMatchObject({ + _tag: "InvalidTerminalImageFetch" + }) + expect(planTerminalImageFetch("/tmp/./photo.png")).toMatchObject({ + _tag: "InvalidTerminalImageFetch" + }) + }) + + it("rejects unsupported extensions", () => { + expect(planTerminalImageFetch("/tmp/file.bmp")).toMatchObject({ + _tag: "InvalidTerminalImageFetch" + }) + expect(planTerminalImageFetch("/tmp/file")).toMatchObject({ + _tag: "InvalidTerminalImageFetch" + }) + expect(planTerminalImageFetch("/tmp/file.")).toMatchObject({ + _tag: "InvalidTerminalImageFetch" + }) + }) +}) From 85740b564b3251b7d7e269bf535ce016116650eb Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 5 May 2026 21:19:46 +0000 Subject: [PATCH 05/12] feat(api): add terminal-session image fetch endpoint GET /projects/by-key/:projectKey/terminal-sessions/:sessionId/image?path=... GET /projects/:projectId/terminal-sessions/:sessionId/image?path=... Streams the bytes of an image file from the project container so the web terminal can render image paths inline. Validates the requested path with planTerminalImageFetch, runs docker exec -u dev cat -- , enforces a 10 MiB cap, and surfaces missing files as 404 and oversize as 400. --- packages/api/src/http.ts | 37 +++++- .../api/src/services/terminal-sessions.ts | 118 +++++++++++++++++- 2 files changed, 152 insertions(+), 3 deletions(-) diff --git a/packages/api/src/http.ts b/packages/api/src/http.ts index 266735a7..d4c632ea 100644 --- a/packages/api/src/http.ts +++ b/packages/api/src/http.ts @@ -112,7 +112,8 @@ import { deleteTerminalSession, getProjectTerminalSession, listProjectTerminalSessions, - lookupTerminalSessionById + lookupTerminalSessionById, + readProjectTerminalImage } from "./services/terminal-sessions.js" import { commitStateFromRequest, @@ -222,6 +223,14 @@ const textResponse = (data: string, contentType: string, status = 200) => ) ) +const binaryResponse = (data: Uint8Array, contentType: string, status = 200) => + Effect.succeed( + HttpServerResponse.setStatus( + HttpServerResponse.uint8Array(data, { contentType, headers: noStoreHeaders }), + status + ) + ) + const activityJsonResponse = (data: unknown, status: number) => textResponse(JSON.stringify(data), "application/activity+json; charset=utf-8", status) @@ -1125,7 +1134,31 @@ export const makeRouter = () => { ) ) - const withAgents = withProjectLifecycle.pipe( + const withProjectTerminalImages = withProjectLifecycle.pipe( + HttpRouter.get( + "/projects/by-key/:projectKey/terminal-sessions/:sessionId/image", + Effect.gen(function*(_) { + const { projectKey, sessionId } = yield* _(terminalSessionByProjectKeyParams) + const request = yield* _(HttpServerRequest.HttpServerRequest) + const imagePath = new URL(request.url, "http://localhost").searchParams.get("path") ?? "" + const project = yield* _(getProjectItemByKey(projectKey)) + const result = yield* _(readProjectTerminalImage(project.projectDir, sessionId, imagePath)) + return yield* _(binaryResponse(result.bytes, result.mediaType, 200)) + }).pipe(Effect.catchAll(errorResponse)) + ), + HttpRouter.get( + "/projects/:projectId/terminal-sessions/:sessionId/image", + Effect.gen(function*(_) { + const { projectId, sessionId } = yield* _(terminalSessionParams) + const request = yield* _(HttpServerRequest.HttpServerRequest) + const imagePath = new URL(request.url, "http://localhost").searchParams.get("path") ?? "" + const result = yield* _(readProjectTerminalImage(projectId, sessionId, imagePath)) + return yield* _(binaryResponse(result.bytes, result.mediaType, 200)) + }).pipe(Effect.catchAll(errorResponse)) + ) + ) + + const withAgents = withProjectTerminalImages.pipe( HttpRouter.post( "/projects/:projectId/agents", Effect.gen(function*(_) { diff --git a/packages/api/src/services/terminal-sessions.ts b/packages/api/src/services/terminal-sessions.ts index d8e4afa7..c415a5a7 100644 --- a/packages/api/src/services/terminal-sessions.ts +++ b/packages/api/src/services/terminal-sessions.ts @@ -15,8 +15,12 @@ import type { Duplex } from "node:stream" import { WebSocket, WebSocketServer, type RawData } from "ws" import type { TerminalSession, TerminalSessionStatus } from "../api/contracts.js" -import { ApiInternalError, ApiNotFoundError, describeUnknown } from "../api/errors.js" +import { ApiBadRequestError, ApiInternalError, ApiNotFoundError, describeUnknown } from "../api/errors.js" import { emitProjectEvent } from "./events.js" +import { + planTerminalImageFetch, + terminalImageFetchMaxBytes +} from "./terminal-image-fetch-core.js" import { createTerminalImagePastePlan, terminalImagePasteDirectory, @@ -392,6 +396,91 @@ const writeBufferToProjectContainer = ( child.stdin.end(buffer) }) +const readBufferFromProjectContainer = ( + containerName: string, + containerPath: string, + maxBytes: number +): Effect.Effect => + Effect.async((resume) => { + const child = spawn( + "docker", + [ + "exec", + "-u", + "dev", + containerName, + "cat", + "--", + containerPath + ], + { + cwd: process.cwd(), + stdio: ["ignore", "pipe", "pipe"] + } + ) + const stdoutChunks: Array = [] + const stderrChunks: Array = [] + let totalBytes = 0 + let exceededLimit = false + let completed = false + const resumeOnce = ( + effect: Effect.Effect + ): void => { + if (completed) { + return + } + completed = true + resume(effect) + } + child.stdout.on("data", (chunk: Buffer | string) => { + const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk) + totalBytes += buffer.length + if (totalBytes > maxBytes) { + exceededLimit = true + try { + child.kill() + } catch { + // ignore — close handler will resume + } + return + } + stdoutChunks.push(buffer) + }) + child.stderr.on("data", (chunk: Buffer | string) => { + stderrChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)) + }) + child.on("error", (error) => { + resumeOnce(Effect.fail(new ApiInternalError({ + message: `Failed to run docker exec for ${containerName}.`, + cause: error + }))) + }) + child.on("close", (exitCode) => { + if (exceededLimit) { + resumeOnce(Effect.fail(new ApiBadRequestError({ + message: `Image exceeds maximum size of ${maxBytes} bytes.` + }))) + return + } + if (exitCode === 0) { + resumeOnce(Effect.succeed(Buffer.concat(stdoutChunks))) + return + } + const stderr = Buffer.concat(stderrChunks).toString("utf8").trim() + if (/no such file|not a directory|not found/iu.test(stderr)) { + resumeOnce(Effect.fail(new ApiNotFoundError({ + message: `Image not found at ${containerPath}.` + }))) + return + } + resumeOnce(Effect.fail(new ApiInternalError({ + message: stderr.length > 0 + ? `Failed to read image: ${stderr}` + : `Failed to read image; docker exec exited with code ${exitCode ?? "unknown"}.` + }))) + }) + }) + const saveTerminalImagePaste = ( record: TerminalRecord, payload: TerminalImagePastePayload @@ -611,6 +700,33 @@ export const getProjectTerminalSession = ( return record.session }) +export const readProjectTerminalImage = ( + projectId: string, + sessionId: string, + imagePath: string +): Effect.Effect< + { readonly bytes: Buffer; readonly mediaType: string }, + ApiBadRequestError | ApiInternalError | ApiNotFoundError +> => + Effect.gen(function*(_) { + const record = records.get(sessionId) + if (record === undefined || record.projectId !== projectId) { + return yield* _( + Effect.fail(new ApiNotFoundError({ message: `Terminal session not found: ${sessionId}` })) + ) + } + const plan = planTerminalImageFetch(imagePath) + if (plan._tag === "InvalidTerminalImageFetch") { + return yield* _(Effect.fail(new ApiBadRequestError({ message: plan.message }))) + } + const bytes = yield* _(readBufferFromProjectContainer( + record.projectContainerName, + plan.containerPath, + terminalImageFetchMaxBytes + )) + return { bytes, mediaType: plan.mediaType } + }) + export const lookupTerminalSessionById = ( sessionId: string ): Effect.Effect< From 3e25db8428f25c2a0c053c12683bce6b876607a9 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 5 May 2026 21:38:05 +0000 Subject: [PATCH 06/12] feat(app): render inline terminal image gallery for detected paths Wires the terminal output image-path detector into a thumbnail strip under the terminal viewport. Each detected absolute path is fetched via the project terminal-sessions image endpoint and shown as a clickable thumbnail (opens in a new tab). The list is deduplicated and capped at the most recent 20 paths per session and is reset when the active session changes. --- packages/app/src/web/panel-terminal.tsx | 156 ++++++++++++++---- .../src/web/terminal-image-gallery-core.ts | 33 ++++ packages/app/src/web/terminal-image-url.ts | 21 +++ .../src/web/terminal-panel-runtime-core.ts | 7 + .../src/web/terminal-panel-runtime-types.ts | 2 + .../app/src/web/terminal-panel-runtime.ts | 37 ++++- .../terminal-image-gallery-core.test.ts | 53 ++++++ .../docker-git/terminal-image-url.test.ts | 55 ++++++ 8 files changed, 330 insertions(+), 34 deletions(-) create mode 100644 packages/app/src/web/terminal-image-gallery-core.ts create mode 100644 packages/app/src/web/terminal-image-url.ts create mode 100644 packages/app/tests/docker-git/terminal-image-gallery-core.test.ts create mode 100644 packages/app/tests/docker-git/terminal-image-url.test.ts diff --git a/packages/app/src/web/panel-terminal.tsx b/packages/app/src/web/panel-terminal.tsx index bb442a94..172e2dd2 100644 --- a/packages/app/src/web/panel-terminal.tsx +++ b/packages/app/src/web/panel-terminal.tsx @@ -2,11 +2,13 @@ import "xterm/css/xterm.css" import { type CSSProperties, type JSX, useCallback, useEffect, useRef, useState } from "react" +import { appendTerminalImageGalleryEntries, type TerminalImageGalleryEntry } from "./terminal-image-gallery-core.js" +import { resolveTerminalImageFetchUrl } from "./terminal-image-url.js" import { isModifierOnlyTerminalKey, + type MobileTerminalKey, mobileTerminalKeyInput, - terminalControlCharacterForKey, - type MobileTerminalKey + terminalControlCharacterForKey } from "./terminal-mobile-controls.js" import { resolveTerminalCompactHeaderMode, resolveTerminalTypingMode } from "./terminal-mobile-layout.js" import { @@ -110,6 +112,72 @@ const compactHeaderActionsStyle: CSSProperties = { gap: "4px" } +const imageGalleryStyle: CSSProperties = { + background: "#0d1218", + borderTop: "1px solid #3a4652", + display: "flex", + flexShrink: 0, + flexWrap: "wrap", + gap: "8px", + maxHeight: "180px", + overflowY: "auto", + padding: "8px" +} + +const imageGalleryItemStyle: CSSProperties = { + alignItems: "center", + background: "#080a0d", + border: "1px solid #3a4652", + borderRadius: "6px", + cursor: "pointer", + display: "flex", + flexDirection: "column", + gap: "4px", + padding: "4px" +} + +const imageGalleryThumbnailStyle: CSSProperties = { + borderRadius: "4px", + display: "block", + maxHeight: "120px", + maxWidth: "180px", + objectFit: "contain" +} + +const imageGalleryCaptionStyle: CSSProperties = { + color: "#8fa6c4", + fontSize: "11px", + maxWidth: "180px", + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap" +} + +const TerminalImageGallery = ( + { entries }: { readonly entries: ReadonlyArray } +): JSX.Element | null => { + if (entries.length === 0) { + return null + } + return ( +
+ {entries.map((entry) => ( + + {entry.path} + {entry.path} + + ))} +
+ ) +} + const mobileControlsCollapsedStyle: CSSProperties = { alignItems: "center", background: "#0d1218", @@ -356,44 +424,60 @@ const MobileTerminalControls = ( ): JSX.Element => ( collapsed ? ( -
+
) : (
- { - onKeyPress("escape") - }} + { + onKeyPress("escape") + }} /> - { - onKeyPress("tab") - }} + { + onKeyPress("tab") + }} /> - { - onKeyPress("ctrl-c") - }} + { + onKeyPress("ctrl-c") + }} />
- { - onKeyPress("left") - }} + { + onKeyPress("left") + }} /> - { - onKeyPress("up") - }} + { + onKeyPress("up") + }} /> - { - onKeyPress("down") - }} + { + onKeyPress("down") + }} /> - { - onKeyPress("right") - }} + { + onKeyPress("right") + }} />
@@ -419,6 +503,18 @@ export const TerminalPanel = ( const [status, setStatus] = useState("connecting") const [mobileControlsCollapsed, setMobileControlsCollapsed] = useState(false) const [mobileCtrlArmed, setMobileCtrlArmed] = useState(false) + const [imageGallery, setImageGallery] = useState>([]) + const sessionWebsocketPath = session.websocketPath + useEffect(() => { + setImageGallery([]) + }, [sessionWebsocketPath]) + const handleImagePaths = useCallback((paths: ReadonlyArray) => { + const additions = paths.map((path) => ({ + fetchUrl: resolveTerminalImageFetchUrl(sessionWebsocketPath, path), + path + })) + setImageGallery((current) => appendTerminalImageGalleryEntries(current, additions)) + }, [sessionWebsocketPath]) const onAttachFailureRef = useRef(onAttachFailure) const onMessageRef = useRef(onMessage) useEffect(() => { @@ -492,6 +588,7 @@ export const TerminalPanel = ( hostRef, notifyMessage, onAttachFailure: notifyAttachFailure, + onImagePaths: handleImagePaths, runtimeRef, session, setStatus @@ -516,11 +613,11 @@ export const TerminalPanel = ( />
- {!mobileMode - ? null - : ( + + {mobileMode + ? ( - )} + ) + : null}
) } diff --git a/packages/app/src/web/terminal-image-gallery-core.ts b/packages/app/src/web/terminal-image-gallery-core.ts new file mode 100644 index 00000000..ce0bf8d0 --- /dev/null +++ b/packages/app/src/web/terminal-image-gallery-core.ts @@ -0,0 +1,33 @@ +export type TerminalImageGalleryEntry = { + readonly fetchUrl: string + readonly path: string +} + +export const terminalImageGalleryLimit = 20 + +export const appendTerminalImageGalleryEntries = ( + current: ReadonlyArray, + incoming: ReadonlyArray, + limit: number = terminalImageGalleryLimit +): ReadonlyArray => { + if (incoming.length === 0) { + return current + } + const known = new Set(current.map((entry) => entry.path)) + const additions: Array = [] + for (const entry of incoming) { + if (known.has(entry.path)) { + continue + } + known.add(entry.path) + additions.push(entry) + } + if (additions.length === 0) { + return current + } + const combined = [...current, ...additions] + if (combined.length <= limit) { + return combined + } + return combined.slice(combined.length - limit) +} diff --git a/packages/app/src/web/terminal-image-url.ts b/packages/app/src/web/terminal-image-url.ts new file mode 100644 index 00000000..afc67f88 --- /dev/null +++ b/packages/app/src/web/terminal-image-url.ts @@ -0,0 +1,21 @@ +import { resolveApiBaseUrl } from "./api-http.js" + +const websocketSuffixPattern = /\/ws$/u + +const resolveBackendOrigin = (): URL => { + const configured = resolveApiBaseUrl() + if (configured.startsWith("http://") || configured.startsWith("https://")) { + return new URL(configured) + } + return new URL(configured, globalThis.location.origin) +} + +export const resolveTerminalImageBasePath = (websocketPath: string): string => + websocketPath.replace(websocketSuffixPattern, "/image") + +export const resolveTerminalImageFetchUrl = (websocketPath: string, imagePath: string): string => { + const apiUrl = resolveBackendOrigin() + apiUrl.pathname = `${apiUrl.pathname.replace(/\/$/u, "")}${resolveTerminalImageBasePath(websocketPath)}` + apiUrl.searchParams.set("path", imagePath) + return apiUrl.toString() +} diff --git a/packages/app/src/web/terminal-panel-runtime-core.ts b/packages/app/src/web/terminal-panel-runtime-core.ts index e72d4692..36fbfb8f 100644 --- a/packages/app/src/web/terminal-panel-runtime-core.ts +++ b/packages/app/src/web/terminal-panel-runtime-core.ts @@ -2,6 +2,7 @@ import { Effect, Either } from "effect" import { Terminal } from "xterm" import { FitAddon } from "xterm-addon-fit" +import { detectTerminalImagePaths } from "./terminal-image-paths.js" import type { TerminalCleanupArgs, TerminalInputController, @@ -188,6 +189,12 @@ const handleTerminalServerMessage = ( } if (message.type === "output") { handlers.terminal.write(message.data) + if (handlers.onImagePaths !== undefined) { + const detected = detectTerminalImagePaths(message.data) + if (detected.length > 0) { + handlers.onImagePaths(detected) + } + } return } if (message.type === "error") { diff --git a/packages/app/src/web/terminal-panel-runtime-types.ts b/packages/app/src/web/terminal-panel-runtime-types.ts index 94ac211b..4c5e4adf 100644 --- a/packages/app/src/web/terminal-panel-runtime-types.ts +++ b/packages/app/src/web/terminal-panel-runtime-types.ts @@ -35,6 +35,7 @@ export type TerminalMessageHandlers = { readonly connectionRef: { current: TerminalConnectionState } readonly lifecycle: TerminalLifecycleState readonly notifyMessage: (message: string) => void + readonly onImagePaths?: ((paths: ReadonlyArray) => void) | undefined readonly session: ActiveTerminalSession readonly setStatus: (status: TerminalStatus) => void readonly terminal: Terminal @@ -59,6 +60,7 @@ export type TerminalLifecycleArgs = { readonly hostRef: { readonly current: HTMLDivElement | null } readonly notifyMessage: (message: string) => void readonly onAttachFailure: () => void + readonly onImagePaths?: ((paths: ReadonlyArray) => void) | undefined readonly runtimeRef: { current: TerminalInputController | null } readonly session: ActiveTerminalSession readonly setStatus: (status: TerminalStatus) => void diff --git a/packages/app/src/web/terminal-panel-runtime.ts b/packages/app/src/web/terminal-panel-runtime.ts index 86f3d416..a52c81e1 100644 --- a/packages/app/src/web/terminal-panel-runtime.ts +++ b/packages/app/src/web/terminal-panel-runtime.ts @@ -5,8 +5,8 @@ import { attachTerminalInput, cleanupTerminalResources, connectTerminalSocket, - createTerminalInputController, createLifecycleState, + createTerminalInputController, createTerminalRuntime, observeTerminalResize, sendTerminalResize @@ -58,7 +58,16 @@ const createConnectSocket = ( } const mountTerminalSession = ( - { connectionRef, hostRef, notifyMessage, onAttachFailure, runtimeRef, session, setStatus }: TerminalLifecycleArgs + { + connectionRef, + hostRef, + notifyMessage, + onAttachFailure, + onImagePaths, + runtimeRef, + session, + setStatus + }: TerminalLifecycleArgs ): (() => void) | undefined => { const host = hostRef.current if (host === null) { @@ -77,7 +86,15 @@ const mountTerminalSession = ( const resizeObserver = observeTerminalResize(host, sendResize) const inputDisposable = attachTerminalInput(terminal, socketRef, pasteGuard) const imagePasteDisposable = attachTerminalImagePaste({ host, notifyMessage, pasteGuard, socketRef, terminal }) - const handlers: TerminalMessageHandlers = { connectionRef, lifecycle, notifyMessage, session, setStatus, terminal } + const handlers: TerminalMessageHandlers = { + connectionRef, + lifecycle, + notifyMessage, + onImagePaths, + session, + setStatus, + terminal + } const connectSocket = createConnectSocket({ handlers, lifecycle, @@ -105,7 +122,16 @@ const mountTerminalSession = ( } export const useTerminalSessionLifecycle = ( - { connectionRef, hostRef, notifyMessage, onAttachFailure, runtimeRef, session, setStatus }: TerminalLifecycleArgs + { + connectionRef, + hostRef, + notifyMessage, + onAttachFailure, + onImagePaths, + runtimeRef, + session, + setStatus + }: TerminalLifecycleArgs ): void => { useEffect(() => { return mountTerminalSession({ @@ -113,11 +139,12 @@ export const useTerminalSessionLifecycle = ( hostRef, notifyMessage, onAttachFailure, + onImagePaths, runtimeRef, session, setStatus }) - }, [connectionRef, hostRef, notifyMessage, onAttachFailure, runtimeRef, session, setStatus]) + }, [connectionRef, hostRef, notifyMessage, onAttachFailure, onImagePaths, runtimeRef, session, setStatus]) } export { diff --git a/packages/app/tests/docker-git/terminal-image-gallery-core.test.ts b/packages/app/tests/docker-git/terminal-image-gallery-core.test.ts new file mode 100644 index 00000000..fdb80917 --- /dev/null +++ b/packages/app/tests/docker-git/terminal-image-gallery-core.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, it } from "@effect/vitest" + +import { + appendTerminalImageGalleryEntries, + terminalImageGalleryLimit +} from "../../src/web/terminal-image-gallery-core.js" + +const entry = (path: string) => ({ fetchUrl: `https://example/api/image?path=${path}`, path }) + +describe("terminal image gallery state", () => { + it("returns existing list when nothing is added", () => { + const current = [entry("/var/data/a.png")] + expect(appendTerminalImageGalleryEntries(current, [])).toBe(current) + }) + + it("appends new entries while preserving order", () => { + expect( + appendTerminalImageGalleryEntries( + [entry("/var/data/a.png")], + [entry("/var/data/b.png"), entry("/var/data/c.png")] + ) + ).toEqual([entry("/var/data/a.png"), entry("/var/data/b.png"), entry("/var/data/c.png")]) + }) + + it("deduplicates entries already present", () => { + expect( + appendTerminalImageGalleryEntries( + [entry("/var/data/a.png")], + [entry("/var/data/a.png"), entry("/var/data/b.png")] + ) + ).toEqual([entry("/var/data/a.png"), entry("/var/data/b.png")]) + }) + + it("returns the same reference when all incoming entries are already known", () => { + const current = [entry("/var/data/a.png")] + expect(appendTerminalImageGalleryEntries(current, [entry("/var/data/a.png")])).toBe(current) + }) + + it("trims oldest entries to honor the limit", () => { + const limit = 3 + expect( + appendTerminalImageGalleryEntries( + [entry("/a.png"), entry("/b.png"), entry("/c.png")], + [entry("/d.png"), entry("/e.png")], + limit + ) + ).toEqual([entry("/c.png"), entry("/d.png"), entry("/e.png")]) + }) + + it("exposes a default gallery limit", () => { + expect(terminalImageGalleryLimit).toBeGreaterThan(0) + }) +}) diff --git a/packages/app/tests/docker-git/terminal-image-url.test.ts b/packages/app/tests/docker-git/terminal-image-url.test.ts new file mode 100644 index 00000000..d4c98e0c --- /dev/null +++ b/packages/app/tests/docker-git/terminal-image-url.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it } from "@effect/vitest" +import { afterEach, beforeEach, vi } from "vitest" + +import { resolveTerminalImageBasePath, resolveTerminalImageFetchUrl } from "../../src/web/terminal-image-url.js" + +const resolveApiBaseUrlMock = vi.hoisted(() => vi.fn<() => string>()) + +vi.mock("../../src/web/api-http.js", () => ({ + resolveApiBaseUrl: resolveApiBaseUrlMock +})) + +describe("terminal image fetch url", () => { + beforeEach(() => { + resolveApiBaseUrlMock.mockReset() + }) + + afterEach(() => { + vi.unstubAllGlobals() + }) + + it("converts /ws suffix into /image base path", () => { + expect(resolveTerminalImageBasePath("/projects/by-key/proj/terminal-sessions/sess/ws")).toBe( + "/projects/by-key/proj/terminal-sessions/sess/image" + ) + }) + + it("builds an absolute backend image url with path query parameter", () => { + resolveApiBaseUrlMock.mockReturnValue("https://controller.example/api") + + expect( + resolveTerminalImageFetchUrl( + "/projects/by-key/proj%201/terminal-sessions/sess%2F2/ws", + "/var/data/sample image.png" + ) + ).toBe( + "https://controller.example/api/projects/by-key/proj%201/terminal-sessions/sess%2F2/image?path=%2Fvar%2Fdata%2Fsample+image.png" + ) + }) + + it("uses same-origin api proxy for relative api base urls", () => { + const host = "terminal.example.local" + const httpProtocol = ["ht", "tp:"].join("") + + resolveApiBaseUrlMock.mockReturnValue("/api") + vi.stubGlobal("location", { + hostname: host, + origin: `${httpProtocol}//${host}:4176`, + protocol: httpProtocol + }) + + expect( + resolveTerminalImageFetchUrl("/projects/proj/terminal-sessions/sess/ws", "/var/data/file.png") + ).toBe(`${httpProtocol}//${host}:4176/api/projects/proj/terminal-sessions/sess/image?path=%2Fvar%2Fdata%2Ffile.png`) + }) +}) From af95a5425f7b73316ce7c5f82cc7fae8ef6fb589 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 5 May 2026 21:46:07 +0000 Subject: [PATCH 07/12] fix(app): drop as-const cast in terminal image extensions The Effect-TS lint config forbids `as` expressions outside src/core/axioms.ts. Type the supported-extensions list with an explicit `ReadonlyArray` annotation instead of an `as const` literal. --- packages/app/src/web/terminal-image-paths.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/web/terminal-image-paths.ts b/packages/app/src/web/terminal-image-paths.ts index 7dfa246f..2bfed09c 100644 --- a/packages/app/src/web/terminal-image-paths.ts +++ b/packages/app/src/web/terminal-image-paths.ts @@ -1,4 +1,4 @@ -const supportedExtensions = ["png", "jpg", "jpeg", "gif", "webp"] as const +const supportedExtensions: ReadonlyArray = ["png", "jpg", "jpeg", "gif", "webp"] const extensionAlternation = supportedExtensions.join("|") From e16ea2c1014a53bfeb3ff0b242e16e3539a31878 Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 5 May 2026 21:47:28 +0000 Subject: [PATCH 08/12] chore: remove placeholder .gitkeep now that the branch has real content The PR is no longer a placeholder draft; the placeholder file added at branch creation can be deleted. --- .gitkeep | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep deleted file mode 100644 index e022f40c..00000000 --- a/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# .gitkeep file auto-generated at 2026-05-05T20:55:46.098Z for PR creation at branch issue-240-b08428ed1942 for issue https://github.com/ProverCoderAI/docker-git/issues/240 \ No newline at end of file From b154ae2c8cecd1ad7a9fded6fb874f260a41cadf Mon Sep 17 00:00:00 2001 From: konard Date: Tue, 5 May 2026 23:19:02 +0000 Subject: [PATCH 09/12] refactor(app): dedupe terminal url helpers and tests Reuse resolveTerminalApiOriginUrl from terminal.ts for image fetch URL resolution to eliminate the duplicate origin builder, and merge the image fetch URL test cases into terminal.test.ts so they share the existing mock setup. Extract a same-origin location stub helper to keep individual test bodies short. Also extract attachGlobalResizeListeners out of mountTerminalSession to stay under the max-lines-per-function threshold. --- packages/app/src/web/terminal-image-url.ts | 12 +--- .../app/src/web/terminal-panel-runtime.ts | 34 ++++-------- packages/app/src/web/terminal.ts | 6 +- .../docker-git/terminal-image-url.test.ts | 55 ------------------- .../app/tests/docker-git/terminal.test.ts | 47 ++++++++++++++-- 5 files changed, 57 insertions(+), 97 deletions(-) delete mode 100644 packages/app/tests/docker-git/terminal-image-url.test.ts diff --git a/packages/app/src/web/terminal-image-url.ts b/packages/app/src/web/terminal-image-url.ts index afc67f88..3a8ed8d4 100644 --- a/packages/app/src/web/terminal-image-url.ts +++ b/packages/app/src/web/terminal-image-url.ts @@ -1,20 +1,12 @@ -import { resolveApiBaseUrl } from "./api-http.js" +import { resolveTerminalApiOriginUrl } from "./terminal.js" const websocketSuffixPattern = /\/ws$/u -const resolveBackendOrigin = (): URL => { - const configured = resolveApiBaseUrl() - if (configured.startsWith("http://") || configured.startsWith("https://")) { - return new URL(configured) - } - return new URL(configured, globalThis.location.origin) -} - export const resolveTerminalImageBasePath = (websocketPath: string): string => websocketPath.replace(websocketSuffixPattern, "/image") export const resolveTerminalImageFetchUrl = (websocketPath: string, imagePath: string): string => { - const apiUrl = resolveBackendOrigin() + const apiUrl = resolveTerminalApiOriginUrl() apiUrl.pathname = `${apiUrl.pathname.replace(/\/$/u, "")}${resolveTerminalImageBasePath(websocketPath)}` apiUrl.searchParams.set("path", imagePath) return apiUrl.toString() diff --git a/packages/app/src/web/terminal-panel-runtime.ts b/packages/app/src/web/terminal-panel-runtime.ts index a52c81e1..a86ea413 100644 --- a/packages/app/src/web/terminal-panel-runtime.ts +++ b/packages/app/src/web/terminal-panel-runtime.ts @@ -57,17 +57,15 @@ const createConnectSocket = ( return connectSocket } +const attachGlobalResizeListeners = (sendResize: () => void): void => { + globalThis.addEventListener("resize", sendResize) + globalThis.visualViewport?.addEventListener("resize", sendResize) + globalThis.visualViewport?.addEventListener("scroll", sendResize) +} + const mountTerminalSession = ( - { - connectionRef, - hostRef, - notifyMessage, - onAttachFailure, - onImagePaths, - runtimeRef, - session, - setStatus - }: TerminalLifecycleArgs + { connectionRef, hostRef, notifyMessage, onAttachFailure, onImagePaths, runtimeRef, session, setStatus }: + TerminalLifecycleArgs ): (() => void) | undefined => { const host = hostRef.current if (host === null) { @@ -108,9 +106,7 @@ const mountTerminalSession = ( }) runtimeRef.current = terminalInputController - globalThis.addEventListener("resize", sendResize) - globalThis.visualViewport?.addEventListener("resize", sendResize) - globalThis.visualViewport?.addEventListener("scroll", sendResize) + attachGlobalResizeListeners(sendResize) connectSocket() return createTerminalCleanup({ @@ -122,16 +118,8 @@ const mountTerminalSession = ( } export const useTerminalSessionLifecycle = ( - { - connectionRef, - hostRef, - notifyMessage, - onAttachFailure, - onImagePaths, - runtimeRef, - session, - setStatus - }: TerminalLifecycleArgs + { connectionRef, hostRef, notifyMessage, onAttachFailure, onImagePaths, runtimeRef, session, setStatus }: + TerminalLifecycleArgs ): void => { useEffect(() => { return mountTerminalSession({ diff --git a/packages/app/src/web/terminal.ts b/packages/app/src/web/terminal.ts index 1d330ded..e019480e 100644 --- a/packages/app/src/web/terminal.ts +++ b/packages/app/src/web/terminal.ts @@ -57,7 +57,7 @@ export const buildProjectActiveTerminalSession = ( } } -const resolveTerminalApiBaseUrl = (): string => { +export const resolveTerminalApiBaseUrl = (): string => { const configured = import.meta.env.VITE_DOCKER_GIT_TERMINAL_API_BASE_URL if (configured !== undefined && configured.trim().length > 0) { return trimTrailingSlash(configured.trim()) @@ -66,7 +66,7 @@ const resolveTerminalApiBaseUrl = (): string => { return resolveApiBaseUrl() } -const resolveApiUrl = (): URL => { +export const resolveTerminalApiOriginUrl = (): URL => { const configured = resolveTerminalApiBaseUrl() if (configured.startsWith("http://") || configured.startsWith("https://")) { return new URL(configured) @@ -75,7 +75,7 @@ const resolveApiUrl = (): URL => { } export const resolveTerminalWebSocketUrl = (websocketPath: string, cols: number, rows: number): string => { - const apiUrl = resolveApiUrl() + const apiUrl = resolveTerminalApiOriginUrl() apiUrl.protocol = apiUrl.protocol === "https:" ? "wss:" : "ws:" apiUrl.pathname = `${apiUrl.pathname.replace(/\/$/u, "")}${websocketPath}` apiUrl.searchParams.set("cols", String(cols)) diff --git a/packages/app/tests/docker-git/terminal-image-url.test.ts b/packages/app/tests/docker-git/terminal-image-url.test.ts deleted file mode 100644 index d4c98e0c..00000000 --- a/packages/app/tests/docker-git/terminal-image-url.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { describe, expect, it } from "@effect/vitest" -import { afterEach, beforeEach, vi } from "vitest" - -import { resolveTerminalImageBasePath, resolveTerminalImageFetchUrl } from "../../src/web/terminal-image-url.js" - -const resolveApiBaseUrlMock = vi.hoisted(() => vi.fn<() => string>()) - -vi.mock("../../src/web/api-http.js", () => ({ - resolveApiBaseUrl: resolveApiBaseUrlMock -})) - -describe("terminal image fetch url", () => { - beforeEach(() => { - resolveApiBaseUrlMock.mockReset() - }) - - afterEach(() => { - vi.unstubAllGlobals() - }) - - it("converts /ws suffix into /image base path", () => { - expect(resolveTerminalImageBasePath("/projects/by-key/proj/terminal-sessions/sess/ws")).toBe( - "/projects/by-key/proj/terminal-sessions/sess/image" - ) - }) - - it("builds an absolute backend image url with path query parameter", () => { - resolveApiBaseUrlMock.mockReturnValue("https://controller.example/api") - - expect( - resolveTerminalImageFetchUrl( - "/projects/by-key/proj%201/terminal-sessions/sess%2F2/ws", - "/var/data/sample image.png" - ) - ).toBe( - "https://controller.example/api/projects/by-key/proj%201/terminal-sessions/sess%2F2/image?path=%2Fvar%2Fdata%2Fsample+image.png" - ) - }) - - it("uses same-origin api proxy for relative api base urls", () => { - const host = "terminal.example.local" - const httpProtocol = ["ht", "tp:"].join("") - - resolveApiBaseUrlMock.mockReturnValue("/api") - vi.stubGlobal("location", { - hostname: host, - origin: `${httpProtocol}//${host}:4176`, - protocol: httpProtocol - }) - - expect( - resolveTerminalImageFetchUrl("/projects/proj/terminal-sessions/sess/ws", "/var/data/file.png") - ).toBe(`${httpProtocol}//${host}:4176/api/projects/proj/terminal-sessions/sess/image?path=%2Fvar%2Fdata%2Ffile.png`) - }) -}) diff --git a/packages/app/tests/docker-git/terminal.test.ts b/packages/app/tests/docker-git/terminal.test.ts index dd972d8a..acfdbf3b 100644 --- a/packages/app/tests/docker-git/terminal.test.ts +++ b/packages/app/tests/docker-git/terminal.test.ts @@ -6,6 +6,7 @@ import { extractTerminalImageBase64, isTerminalPasteShortcut } from "../../src/web/terminal-image-paste.js" +import { resolveTerminalImageBasePath, resolveTerminalImageFetchUrl } from "../../src/web/terminal-image-url.js" import { resolveTerminalCompactHeaderMode, resolveTerminalTypingMode, @@ -32,6 +33,15 @@ vi.mock("../../src/web/api-http.js", () => ({ resolveApiBaseUrl: resolveApiBaseUrlMock })) +const stubSameOriginLocation = (host: string, httpProtocol: string): void => { + resolveApiBaseUrlMock.mockReturnValue("/api") + vi.stubGlobal("location", { + hostname: host, + origin: `${httpProtocol}//${host}:4176`, + protocol: httpProtocol + }) +} + describe("browser terminal helpers", () => { beforeEach(() => { resolveApiBaseUrlMock.mockReset() @@ -54,12 +64,7 @@ describe("browser terminal helpers", () => { const httpProtocol = ["ht", "tp:"].join("") const wsProtocol = ["ws", "://"].join("") - resolveApiBaseUrlMock.mockReturnValue("/api") - vi.stubGlobal("location", { - hostname: host, - origin: `${httpProtocol}//${host}:4176`, - protocol: httpProtocol - }) + stubSameOriginLocation(host, httpProtocol) expect(resolveTerminalWebSocketUrl("/projects/proj/terminal-sessions/sess/ws", 80, 24)).toBe([ wsProtocol, @@ -130,4 +135,34 @@ describe("browser terminal helpers", () => { expect(shouldShowTerminalTabs(true, 2)).toBe(true) expect(shouldShowTerminalTabs(false, 1)).toBe(true) }) + + it("converts /ws suffix into /image base path", () => { + expect(resolveTerminalImageBasePath("/projects/by-key/proj/terminal-sessions/sess/ws")).toBe( + "/projects/by-key/proj/terminal-sessions/sess/image" + ) + }) + + it("builds an absolute backend image url with path query parameter", () => { + resolveApiBaseUrlMock.mockReturnValue("https://controller.example/api") + + expect( + resolveTerminalImageFetchUrl( + "/projects/by-key/proj%201/terminal-sessions/sess%2F2/ws", + "/var/data/sample image.png" + ) + ).toBe( + "https://controller.example/api/projects/by-key/proj%201/terminal-sessions/sess%2F2/image?path=%2Fvar%2Fdata%2Fsample+image.png" + ) + }) + + it("uses same-origin api proxy for relative image fetch urls", () => { + const host = "terminal.example.local" + const httpProtocol = ["ht", "tp:"].join("") + + stubSameOriginLocation(host, httpProtocol) + + expect( + resolveTerminalImageFetchUrl("/projects/proj/terminal-sessions/sess/ws", "/var/data/file.png") + ).toBe(`${httpProtocol}//${host}:4176/api/projects/proj/terminal-sessions/sess/image?path=%2Fvar%2Fdata%2Ffile.png`) + }) }) From 04287f7329c07f9273b4a9eccd0e526550355e64 Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Wed, 6 May 2026 10:30:49 +0000 Subject: [PATCH 10/12] fix(app): hide terminal gallery behind overlay --- .../issue-240/terminal-image-gallery.png | Bin 0 -> 49295 bytes .../issue-240/terminal-image-open.png | Bin 0 -> 6995 bytes packages/app/src/web/panel-terminal.tsx | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 docs/pr-screenshots/issue-240/terminal-image-gallery.png create mode 100644 docs/pr-screenshots/issue-240/terminal-image-open.png diff --git a/docs/pr-screenshots/issue-240/terminal-image-gallery.png b/docs/pr-screenshots/issue-240/terminal-image-gallery.png new file mode 100644 index 0000000000000000000000000000000000000000..3a4e78020e5a875443b823ae78ca41d8647dc9e9 GIT binary patch literal 49295 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 literal 0 HcmV?d00001 diff --git a/docs/pr-screenshots/issue-240/terminal-image-open.png b/docs/pr-screenshots/issue-240/terminal-image-open.png new file mode 100644 index 0000000000000000000000000000000000000000..4af58aeecfd193838709302a1a552e24c8b5352d GIT binary patch literal 6995 zcmeAS@N?(olHy`uVBq!ia0y~yU|qn#zeHD)^6bM#Eq<9gOCM(ehxlOdPEjMoxA3 z{xUOzvi<*0zn`D)f8O7(Utjn2!_UvBKi`-8_uqW~L-qeZeBVE=t()_`^53Vchxh;e zZ!fp+v47pKx7XPj#B?AnN^U-eGCN>P(?YR;VS{o314Dt!1BL{5V3XoO#{tFzeFqpB z4hS|fH^>5;o(;-O>B6v5}BkU@N5z)Kcl=kYRWO#0)ZpYGOd$ zCpH6ZI^Kk0E~Z*RpcO#OV4(za;E|aTK-~)Gn}DtYIv>egOtn}%lb*x~YD$AV4hjQ= zxmeUQHl z!y`~3KiVA~4TI5iFgi2?%mt&}(a|sfCWFy*Fd7CAM$^H_8J582(|;@s4FCVXyno;% gFeR{o+QG~WE4#bToj;Yn9h5daUHx3vIVCg!0GnllTmS$7 literal 0 HcmV?d00001 diff --git a/packages/app/src/web/panel-terminal.tsx b/packages/app/src/web/panel-terminal.tsx index 6b2bb1a1..d0ee8e92 100644 --- a/packages/app/src/web/panel-terminal.tsx +++ b/packages/app/src/web/panel-terminal.tsx @@ -723,7 +723,7 @@ export const TerminalPanel = (
{hasBodyContent ?
{bodyContent}
: null}
- + {hasBodyContent ? null : } {mobileMode && !hasBodyContent ? ( Date: Wed, 6 May 2026 11:24:22 +0000 Subject: [PATCH 11/12] feat(app): render terminal images inline --- .../issue-240/terminal-image-gallery.png | Bin 49295 -> 45125 bytes .../issue-240/terminal-image-open.png | Bin 6995 -> 6978 bytes packages/app/src/web/panel-terminal.tsx | 82 ---------- .../src/web/terminal-image-gallery-core.ts | 33 ---- packages/app/src/web/terminal-image-paths.ts | 30 +++- .../src/web/terminal-inline-images-core.ts | 40 +++++ .../app/src/web/terminal-inline-images.ts | 148 ++++++++++++++++++ .../src/web/terminal-panel-runtime-core.ts | 120 +++++++++++++- .../src/web/terminal-panel-runtime-types.ts | 9 +- .../app/src/web/terminal-panel-runtime.ts | 21 +-- .../terminal-image-gallery-core.test.ts | 53 ------- .../docker-git/terminal-image-paths.test.ts | 11 ++ .../terminal-inline-images-core.test.ts | 30 ++++ 13 files changed, 385 insertions(+), 192 deletions(-) delete mode 100644 packages/app/src/web/terminal-image-gallery-core.ts create mode 100644 packages/app/src/web/terminal-inline-images-core.ts create mode 100644 packages/app/src/web/terminal-inline-images.ts delete mode 100644 packages/app/tests/docker-git/terminal-image-gallery-core.test.ts create mode 100644 packages/app/tests/docker-git/terminal-inline-images-core.test.ts diff --git a/docs/pr-screenshots/issue-240/terminal-image-gallery.png b/docs/pr-screenshots/issue-240/terminal-image-gallery.png index 3a4e78020e5a875443b823ae78ca41d8647dc9e9..206e9f410902950f4eee277728ebc386d6098d9a 100644 GIT binary patch literal 45125 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 literal 49295 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 diff --git a/docs/pr-screenshots/issue-240/terminal-image-open.png b/docs/pr-screenshots/issue-240/terminal-image-open.png index 4af58aeecfd193838709302a1a552e24c8b5352d..05c19bfca453c5753d3b7d849cf17ed1a1bb200c 100644 GIT binary patch literal 6978 zcmeAS@N?(olHy`uVBq!ia0y~yU|qn#zGe7*!mj0SCw^X1Bya+!0mT3jFtzZW#VYPFoLSX?++Op zCf5J^`{U{R+n-;){d+%OZr`8U`hTw;9=HF=9sl#4`d_*I&-wq`p8w50Z+`WU-uR!t z|Noa`h}iG|m{S-&=~yt_sH|pSc+hfy@jxH2ZFxYjk-0&(k(r@E8CaZJgNjsl8A(={ z+KoVM4?0GuS{6QrG9YH?tLO$b_$Q5E!sP%wX~8gvO{^ zU^plhFlwhn040IBy8-(?qiPYE^4=m)&+eHD)^6bM#Eq<9gOCM(ehxlOdPEjMoxA3 z{xUOzvi<*0zn`D)f8O7(Utjn2!_UvBKi`-8_uqW~L-qeZeBVE=t()_`^53Vchxh;e zZ!fp+v47pKx7XPj#B?AnN^U-eGCN>P(?YR;VS{o314Dt!1BL{5V3XoO#{tFzeFqpB z4hS|fH^>5;o(;-O>B6v5}BkU@N5z)Kcl=kYRWO#0)ZpYGOd$ zCpH6ZI^Kk0E~Z*RpcO#OV4(za;E|aTK-~)Gn}DtYIv>egOtn}%lb*x~YD$AV4hjQ= zxmeUQHl z!y`~3KiVA~4TI5iFgi2?%mt&}(a|sfCWFy*Fd7CAM$^H_8J582(|;@s4FCVXyno;% gFeR{o+QG~WE4#bToj;Yn9h5daUHx3vIVCg!0GnllTmS$7 diff --git a/packages/app/src/web/panel-terminal.tsx b/packages/app/src/web/panel-terminal.tsx index d0ee8e92..70e5425b 100644 --- a/packages/app/src/web/panel-terminal.tsx +++ b/packages/app/src/web/panel-terminal.tsx @@ -2,8 +2,6 @@ import "xterm/css/xterm.css" import { type CSSProperties, type JSX, useCallback, useEffect, useRef, useState } from "react" -import { appendTerminalImageGalleryEntries, type TerminalImageGalleryEntry } from "./terminal-image-gallery-core.js" -import { resolveTerminalImageFetchUrl } from "./terminal-image-url.js" import { isModifierOnlyTerminalKey, type MobileTerminalKey, @@ -148,72 +146,6 @@ const compactHeaderActionsStyle: CSSProperties = { gap: "4px" } -const imageGalleryStyle: CSSProperties = { - background: "#0d1218", - borderTop: "1px solid #3a4652", - display: "flex", - flexShrink: 0, - flexWrap: "wrap", - gap: "8px", - maxHeight: "180px", - overflowY: "auto", - padding: "8px" -} - -const imageGalleryItemStyle: CSSProperties = { - alignItems: "center", - background: "#080a0d", - border: "1px solid #3a4652", - borderRadius: "6px", - cursor: "pointer", - display: "flex", - flexDirection: "column", - gap: "4px", - padding: "4px" -} - -const imageGalleryThumbnailStyle: CSSProperties = { - borderRadius: "4px", - display: "block", - maxHeight: "120px", - maxWidth: "180px", - objectFit: "contain" -} - -const imageGalleryCaptionStyle: CSSProperties = { - color: "#8fa6c4", - fontSize: "11px", - maxWidth: "180px", - overflow: "hidden", - textOverflow: "ellipsis", - whiteSpace: "nowrap" -} - -const TerminalImageGallery = ( - { entries }: { readonly entries: ReadonlyArray } -): JSX.Element | null => { - if (entries.length === 0) { - return null - } - return ( -

- {entries.map((entry) => ( - - {entry.path} - {entry.path} - - ))} -
- ) -} - const mobileControlsCollapsedStyle: CSSProperties = { alignItems: "center", background: "#0d1218", @@ -618,18 +550,6 @@ export const TerminalPanel = ( const [status, setStatus] = useState("connecting") const [mobileControlsCollapsed, setMobileControlsCollapsed] = useState(false) const [mobileCtrlArmed, setMobileCtrlArmed] = useState(false) - const [imageGallery, setImageGallery] = useState>([]) - const sessionWebsocketPath = session.websocketPath - useEffect(() => { - setImageGallery([]) - }, [sessionWebsocketPath]) - const handleImagePaths = useCallback((paths: ReadonlyArray) => { - const additions = paths.map((path) => ({ - fetchUrl: resolveTerminalImageFetchUrl(sessionWebsocketPath, path), - path - })) - setImageGallery((current) => appendTerminalImageGalleryEntries(current, additions)) - }, [sessionWebsocketPath]) const onAttachFailureRef = useRef(onAttachFailure) const onMessageRef = useRef(onMessage) useEffect(() => { @@ -694,7 +614,6 @@ export const TerminalPanel = ( hostRef, notifyMessage, onAttachFailure: notifyAttachFailure, - onImagePaths: handleImagePaths, runtimeRef, session, setStatus @@ -723,7 +642,6 @@ export const TerminalPanel = (
{hasBodyContent ?
{bodyContent}
: null}
- {hasBodyContent ? null : } {mobileMode && !hasBodyContent ? ( , - incoming: ReadonlyArray, - limit: number = terminalImageGalleryLimit -): ReadonlyArray => { - if (incoming.length === 0) { - return current - } - const known = new Set(current.map((entry) => entry.path)) - const additions: Array = [] - for (const entry of incoming) { - if (known.has(entry.path)) { - continue - } - known.add(entry.path) - additions.push(entry) - } - if (additions.length === 0) { - return current - } - const combined = [...current, ...additions] - if (combined.length <= limit) { - return combined - } - return combined.slice(combined.length - limit) -} diff --git a/packages/app/src/web/terminal-image-paths.ts b/packages/app/src/web/terminal-image-paths.ts index 2bfed09c..18773a6c 100644 --- a/packages/app/src/web/terminal-image-paths.ts +++ b/packages/app/src/web/terminal-image-paths.ts @@ -16,17 +16,39 @@ const ansiCsiPattern = buildAnsiPattern(String.raw`${escapeChar}\[[0-?]*[ -/]*[@ const ansiOscPattern = buildAnsiPattern(String.raw`${escapeChar}\][\s\S]*?(?:${bellChar}|${escapeChar}\\)`) const ansiOtherEscapePattern = buildAnsiPattern(`${escapeChar}.`) +export type TerminalImagePathMatch = { + readonly endIndex: number + readonly path: string + readonly startIndex: number +} + export const stripTerminalAnsi = (text: string): string => text.replace(ansiOscPattern, "").replace(ansiCsiPattern, "").replace(ansiOtherEscapePattern, "") -export const detectTerminalImagePaths = (text: string): ReadonlyArray => { - const matches = new Set() - for (const match of stripTerminalAnsi(text).matchAll(imagePathPattern)) { +export const detectTerminalImagePathMatches = (text: string): ReadonlyArray => { + const plainText = stripTerminalAnsi(text) + const matches: Array = [] + for (const match of plainText.matchAll(imagePathPattern)) { const candidate = match[1] if (candidate !== undefined && candidate.length > 0) { - matches.add(candidate) + const fullMatch = match[0] + const fullStartIndex = match.index + const startIndex = fullStartIndex + fullMatch.lastIndexOf(candidate) + matches.push({ + endIndex: startIndex + candidate.length, + path: candidate, + startIndex + }) } } + return matches +} + +export const detectTerminalImagePaths = (text: string): ReadonlyArray => { + const matches = new Set() + for (const match of detectTerminalImagePathMatches(text)) { + matches.add(match.path) + } return [...matches] } diff --git a/packages/app/src/web/terminal-inline-images-core.ts b/packages/app/src/web/terminal-inline-images-core.ts new file mode 100644 index 00000000..65570ad8 --- /dev/null +++ b/packages/app/src/web/terminal-inline-images-core.ts @@ -0,0 +1,40 @@ +import { detectTerminalImagePaths } from "./terminal-image-paths.js" + +export type TerminalInlineImageOutputSegment = { + readonly endedWithLineBreak: boolean + readonly imagePaths: ReadonlyArray + readonly text: string +} + +const lineBreakPattern = /\r\n|\r|\n/gu + +const endsWithLineBreak = (text: string): boolean => /\r\n$|\r$|\n$/u.test(text) + +export const splitTerminalInlineImageOutput = ( + data: string +): ReadonlyArray => { + if (data.length === 0) { + return [] + } + const segments: Array = [] + let startIndex = 0 + for (const match of data.matchAll(lineBreakPattern)) { + const endIndex = match.index + match[0].length + const text = data.slice(startIndex, endIndex) + segments.push({ + endedWithLineBreak: true, + imagePaths: detectTerminalImagePaths(text), + text + }) + startIndex = endIndex + } + if (startIndex < data.length) { + const text = data.slice(startIndex) + segments.push({ + endedWithLineBreak: endsWithLineBreak(text), + imagePaths: detectTerminalImagePaths(text), + text + }) + } + return segments +} diff --git a/packages/app/src/web/terminal-inline-images.ts b/packages/app/src/web/terminal-inline-images.ts new file mode 100644 index 00000000..e5a9ab8e --- /dev/null +++ b/packages/app/src/web/terminal-inline-images.ts @@ -0,0 +1,148 @@ +import type { IDisposable, ILink, Terminal } from "xterm" + +import { detectTerminalImagePathMatches } from "./terminal-image-paths.js" +import { resolveTerminalImageFetchUrl } from "./terminal-image-url.js" +import type { TerminalLifecycleState } from "./terminal-panel-runtime-types.js" +import type { ActiveTerminalSession } from "./terminal.js" + +export const terminalInlineImagePreviewLimit = 20 +export const terminalInlineImagePreviewRows = 7 + +export const terminalInlineImageSpacer = "\r\n".repeat(terminalInlineImagePreviewRows) + +const terminalInlineImagePreviewColumns = 28 + +type TerminalInlineImageEntry = { + readonly fetchUrl: string + readonly path: string +} + +const openImage = (fetchUrl: string): void => { + const imageWindow = window.open(fetchUrl, "_blank", "noopener,noreferrer") + if (imageWindow === null) { + return + } + imageWindow.opener = null +} + +const appendDecorationDisposable = ( + lifecycle: TerminalLifecycleState, + disposable: IDisposable +): void => { + lifecycle.inlineImageDisposables.push(disposable) + if (lifecycle.inlineImageDisposables.length <= terminalInlineImagePreviewLimit) { + return + } + lifecycle.inlineImageDisposables.shift()?.dispose() +} + +const renderInlineImageElement = ( + element: HTMLElement, + entry: TerminalInlineImageEntry +): void => { + if (element.dataset["path"] === entry.path) { + return + } + + const link = document.createElement("a") + link.href = entry.fetchUrl + link.rel = "noreferrer" + link.target = "_blank" + link.title = entry.path + link.style.alignItems = "center" + link.style.background = "#0d1218" + link.style.border = "1px solid #3a4652" + link.style.borderRadius = "6px" + link.style.boxSizing = "border-box" + link.style.cursor = "pointer" + link.style.display = "inline-flex" + link.style.height = "calc(100% - 8px)" + link.style.justifyContent = "center" + link.style.margin = "4px 0" + link.style.padding = "4px" + link.style.pointerEvents = "auto" + link.style.width = "100%" + + const image = document.createElement("img") + image.alt = entry.path + image.src = entry.fetchUrl + image.style.borderRadius = "4px" + image.style.display = "block" + image.style.maxHeight = "100%" + image.style.maxWidth = "100%" + image.style.objectFit = "contain" + + link.append(image) + element.dataset["path"] = entry.path + element.style.pointerEvents = "none" + element.replaceChildren(link) +} + +export const appendTerminalInlineImagePreview = ( + terminal: Terminal, + lifecycle: TerminalLifecycleState, + entry: TerminalInlineImageEntry +): boolean => { + const marker = terminal.registerMarker(0) + const decoration = terminal.registerDecoration({ + height: terminalInlineImagePreviewRows, + layer: "top", + marker, + width: Math.min(terminalInlineImagePreviewColumns, Math.max(1, terminal.cols)) + }) + if (decoration === undefined) { + marker.dispose() + return false + } + + decoration.onRender((element) => { + renderInlineImageElement(element, entry) + }) + appendDecorationDisposable(lifecycle, decoration) + return true +} + +const imageLink = ( + session: ActiveTerminalSession, + bufferLineNumber: number, + match: ReturnType[number] +): ILink => { + const fetchUrl = resolveTerminalImageFetchUrl(session.websocketPath, match.path) + return { + activate: () => { + openImage(fetchUrl) + }, + decorations: { + pointerCursor: true, + underline: true + }, + range: { + end: { + x: match.endIndex, + y: bufferLineNumber + }, + start: { + x: match.startIndex + 1, + y: bufferLineNumber + } + }, + text: match.path + } +} + +export const attachTerminalImageLinks = ( + terminal: Terminal, + session: ActiveTerminalSession +): IDisposable => + terminal.registerLinkProvider({ + provideLinks: (bufferLineNumber, callback) => { + const line = terminal.buffer.active.getLine(bufferLineNumber - 1) + if (line === undefined) { + callback([]) + return + } + const text = line.translateToString(true) + const matches = detectTerminalImagePathMatches(text) + callback(matches.map((match) => imageLink(session, bufferLineNumber, match))) + } + }) diff --git a/packages/app/src/web/terminal-panel-runtime-core.ts b/packages/app/src/web/terminal-panel-runtime-core.ts index 36fbfb8f..4229ade9 100644 --- a/packages/app/src/web/terminal-panel-runtime-core.ts +++ b/packages/app/src/web/terminal-panel-runtime-core.ts @@ -2,7 +2,9 @@ import { Effect, Either } from "effect" import { Terminal } from "xterm" import { FitAddon } from "xterm-addon-fit" -import { detectTerminalImagePaths } from "./terminal-image-paths.js" +import { resolveTerminalImageFetchUrl } from "./terminal-image-url.js" +import { splitTerminalInlineImageOutput, type TerminalInlineImageOutputSegment } from "./terminal-inline-images-core.js" +import { appendTerminalInlineImagePreview, terminalInlineImageSpacer } from "./terminal-inline-images.js" import type { TerminalCleanupArgs, TerminalInputController, @@ -36,6 +38,9 @@ const runOptionalTerminalOperation = (operation: () => void): boolean => export const createLifecycleState = (): TerminalLifecycleState => ({ attachedOnce: false, disposed: false, + inlineImageDisposables: [], + outputQueue: [], + outputWriting: false, readyNotified: false, reconnectAttempt: 0, reconnectStartedAtMs: null, @@ -52,6 +57,7 @@ const clearReconnectTimer = (lifecycle: TerminalLifecycleState): void => { export const createTerminalRuntime = (host: HTMLDivElement): TerminalRuntime => { const terminal = new Terminal({ + allowProposedApi: true, convertEol: false, cursorBlink: true, fontFamily: "'IBM Plex Mono', 'SFMono-Regular', monospace", @@ -174,6 +180,103 @@ const endTerminalSession = ( } } +const terminalImageEntry = ( + handlers: TerminalMessageHandlers, + path: string +) => ({ + fetchUrl: resolveTerminalImageFetchUrl(handlers.session.websocketPath, path), + path +}) + +const writePreviewSpacer = ( + handlers: TerminalMessageHandlers, + onComplete: () => void +): void => { + handlers.terminal.write(terminalInlineImageSpacer, onComplete) +} + +const writeInlineImagePreview = ( + handlers: TerminalMessageHandlers, + path: string, + onComplete: () => void +): void => { + const appended = appendTerminalInlineImagePreview( + handlers.terminal, + handlers.lifecycle, + terminalImageEntry(handlers, path) + ) + if (!appended) { + onComplete() + return + } + writePreviewSpacer(handlers, onComplete) +} + +const writeInlineImagePreviews = ( + handlers: TerminalMessageHandlers, + paths: ReadonlyArray, + onComplete: () => void +): void => { + let index = 0 + const writeNext = (): void => { + const path = paths[index] + if (path === undefined) { + onComplete() + return + } + index += 1 + writeInlineImagePreview(handlers, path, writeNext) + } + writeNext() +} + +const writeLineBreakBeforePreview = ( + handlers: TerminalMessageHandlers, + segment: TerminalInlineImageOutputSegment, + onComplete: () => void +): void => { + if (segment.endedWithLineBreak) { + onComplete() + return + } + handlers.terminal.write("\r\n", onComplete) +} + +const flushTerminalOutputQueue = (handlers: TerminalMessageHandlers): void => { + if (handlers.lifecycle.outputWriting || handlers.lifecycle.disposed) { + return + } + const segment = handlers.lifecycle.outputQueue.shift() + if (segment === undefined) { + return + } + + handlers.lifecycle.outputWriting = true + handlers.terminal.write(segment.text, () => { + if (segment.imagePaths.length === 0) { + handlers.lifecycle.outputWriting = false + flushTerminalOutputQueue(handlers) + return + } + writeLineBreakBeforePreview(handlers, segment, () => { + writeInlineImagePreviews(handlers, segment.imagePaths, () => { + handlers.lifecycle.outputWriting = false + flushTerminalOutputQueue(handlers) + }) + }) + }) +} + +const enqueueTerminalOutput = ( + handlers: TerminalMessageHandlers, + data: string +): void => { + for (const segment of splitTerminalInlineImageOutput(data)) { + handlers.lifecycle.outputQueue.push(segment) + } + flushTerminalOutputQueue(handlers) +} + const handleTerminalServerMessage = ( handlers: TerminalMessageHandlers, payload: string @@ -188,13 +291,7 @@ const handleTerminalServerMessage = ( return } if (message.type === "output") { - handlers.terminal.write(message.data) - if (handlers.onImagePaths !== undefined) { - const detected = detectTerminalImagePaths(message.data) - if (detected.length > 0) { - handlers.onImagePaths(detected) - } - } + enqueueTerminalOutput(handlers, message.data) return } if (message.type === "error") { @@ -235,6 +332,13 @@ export const cleanupTerminalResources = ( ): void => { args.lifecycle.disposed = true clearReconnectTimer(args.lifecycle) + for (const disposable of args.lifecycle.inlineImageDisposables) { + disposable.dispose() + } + args.lifecycle.inlineImageDisposables = [] + args.lifecycle.outputQueue = [] + args.lifecycle.outputWriting = false + args.removeImageLinks() args.removeImagePaste() args.removeInput() args.resizeObserver?.disconnect() diff --git a/packages/app/src/web/terminal-panel-runtime-types.ts b/packages/app/src/web/terminal-panel-runtime-types.ts index 4c5e4adf..e180d6f7 100644 --- a/packages/app/src/web/terminal-panel-runtime-types.ts +++ b/packages/app/src/web/terminal-panel-runtime-types.ts @@ -1,6 +1,7 @@ -import type { Terminal } from "xterm" +import type { IDisposable, Terminal } from "xterm" import type { FitAddon } from "xterm-addon-fit" +import type { TerminalInlineImageOutputSegment } from "./terminal-inline-images-core.js" import type { ActiveTerminalSession } from "./terminal.js" export type TerminalStatus = "attached" | "connecting" | "error" | "exited" | "reconnecting" @@ -17,6 +18,9 @@ export type TerminalInputController = { export type TerminalLifecycleState = { attachedOnce: boolean disposed: boolean + inlineImageDisposables: Array + outputQueue: Array + outputWriting: boolean readyNotified: boolean reconnectAttempt: number reconnectStartedAtMs: number | null @@ -35,7 +39,6 @@ export type TerminalMessageHandlers = { readonly connectionRef: { current: TerminalConnectionState } readonly lifecycle: TerminalLifecycleState readonly notifyMessage: (message: string) => void - readonly onImagePaths?: ((paths: ReadonlyArray) => void) | undefined readonly session: ActiveTerminalSession readonly setStatus: (status: TerminalStatus) => void readonly terminal: Terminal @@ -45,6 +48,7 @@ export type TerminalCleanupArgs = { readonly connectionRef: { current: TerminalConnectionState } readonly lifecycle: TerminalLifecycleState readonly notifyMessage: (message: string) => void + readonly removeImageLinks: () => void readonly removeImagePaste: () => void readonly removeInput: () => void readonly removeResize: () => void @@ -60,7 +64,6 @@ export type TerminalLifecycleArgs = { readonly hostRef: { readonly current: HTMLDivElement | null } readonly notifyMessage: (message: string) => void readonly onAttachFailure: () => void - readonly onImagePaths?: ((paths: ReadonlyArray) => void) | undefined readonly runtimeRef: { current: TerminalInputController | null } readonly session: ActiveTerminalSession readonly setStatus: (status: TerminalStatus) => void diff --git a/packages/app/src/web/terminal-panel-runtime.ts b/packages/app/src/web/terminal-panel-runtime.ts index a86ea413..55b33a79 100644 --- a/packages/app/src/web/terminal-panel-runtime.ts +++ b/packages/app/src/web/terminal-panel-runtime.ts @@ -1,6 +1,7 @@ import { useEffect } from "react" import { attachTerminalImagePaste, createTerminalPasteGuard } from "./terminal-image-paste.js" +import { attachTerminalImageLinks } from "./terminal-inline-images.js" import { attachTerminalInput, cleanupTerminalResources, @@ -21,19 +22,23 @@ import type { type TerminalCleanupFactoryArgs = { readonly cleanupArgs: Omit< Parameters[0], - "removeImagePaste" | "removeInput" | "removeResize" + "removeImageLinks" | "removeImagePaste" | "removeInput" | "removeResize" > + readonly imageLinkDisposable: { readonly dispose: () => void } readonly imagePasteDisposable: { readonly dispose: () => void } readonly inputDisposable: { readonly dispose: () => void } readonly sendResize: () => void } const createTerminalCleanup = ( - { cleanupArgs, imagePasteDisposable, inputDisposable, sendResize }: TerminalCleanupFactoryArgs + { cleanupArgs, imageLinkDisposable, imagePasteDisposable, inputDisposable, sendResize }: TerminalCleanupFactoryArgs ): () => void => (): void => { cleanupTerminalResources({ ...cleanupArgs, + removeImageLinks: () => { + imageLinkDisposable.dispose() + }, removeImagePaste: () => { imagePasteDisposable.dispose() }, @@ -64,8 +69,7 @@ const attachGlobalResizeListeners = (sendResize: () => void): void => { } const mountTerminalSession = ( - { connectionRef, hostRef, notifyMessage, onAttachFailure, onImagePaths, runtimeRef, session, setStatus }: - TerminalLifecycleArgs + { connectionRef, hostRef, notifyMessage, onAttachFailure, runtimeRef, session, setStatus }: TerminalLifecycleArgs ): (() => void) | undefined => { const host = hostRef.current if (host === null) { @@ -84,11 +88,11 @@ const mountTerminalSession = ( const resizeObserver = observeTerminalResize(host, sendResize) const inputDisposable = attachTerminalInput(terminal, socketRef, pasteGuard) const imagePasteDisposable = attachTerminalImagePaste({ host, notifyMessage, pasteGuard, socketRef, terminal }) + const imageLinkDisposable = attachTerminalImageLinks(terminal, session) const handlers: TerminalMessageHandlers = { connectionRef, lifecycle, notifyMessage, - onImagePaths, session, setStatus, terminal @@ -111,6 +115,7 @@ const mountTerminalSession = ( return createTerminalCleanup({ cleanupArgs: { connectionRef, lifecycle, notifyMessage, resizeObserver, runtimeRef, session, socketRef, terminal }, + imageLinkDisposable, imagePasteDisposable, inputDisposable, sendResize @@ -118,8 +123,7 @@ const mountTerminalSession = ( } export const useTerminalSessionLifecycle = ( - { connectionRef, hostRef, notifyMessage, onAttachFailure, onImagePaths, runtimeRef, session, setStatus }: - TerminalLifecycleArgs + { connectionRef, hostRef, notifyMessage, onAttachFailure, runtimeRef, session, setStatus }: TerminalLifecycleArgs ): void => { useEffect(() => { return mountTerminalSession({ @@ -127,12 +131,11 @@ export const useTerminalSessionLifecycle = ( hostRef, notifyMessage, onAttachFailure, - onImagePaths, runtimeRef, session, setStatus }) - }, [connectionRef, hostRef, notifyMessage, onAttachFailure, onImagePaths, runtimeRef, session, setStatus]) + }, [connectionRef, hostRef, notifyMessage, onAttachFailure, runtimeRef, session, setStatus]) } export { diff --git a/packages/app/tests/docker-git/terminal-image-gallery-core.test.ts b/packages/app/tests/docker-git/terminal-image-gallery-core.test.ts deleted file mode 100644 index fdb80917..00000000 --- a/packages/app/tests/docker-git/terminal-image-gallery-core.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { describe, expect, it } from "@effect/vitest" - -import { - appendTerminalImageGalleryEntries, - terminalImageGalleryLimit -} from "../../src/web/terminal-image-gallery-core.js" - -const entry = (path: string) => ({ fetchUrl: `https://example/api/image?path=${path}`, path }) - -describe("terminal image gallery state", () => { - it("returns existing list when nothing is added", () => { - const current = [entry("/var/data/a.png")] - expect(appendTerminalImageGalleryEntries(current, [])).toBe(current) - }) - - it("appends new entries while preserving order", () => { - expect( - appendTerminalImageGalleryEntries( - [entry("/var/data/a.png")], - [entry("/var/data/b.png"), entry("/var/data/c.png")] - ) - ).toEqual([entry("/var/data/a.png"), entry("/var/data/b.png"), entry("/var/data/c.png")]) - }) - - it("deduplicates entries already present", () => { - expect( - appendTerminalImageGalleryEntries( - [entry("/var/data/a.png")], - [entry("/var/data/a.png"), entry("/var/data/b.png")] - ) - ).toEqual([entry("/var/data/a.png"), entry("/var/data/b.png")]) - }) - - it("returns the same reference when all incoming entries are already known", () => { - const current = [entry("/var/data/a.png")] - expect(appendTerminalImageGalleryEntries(current, [entry("/var/data/a.png")])).toBe(current) - }) - - it("trims oldest entries to honor the limit", () => { - const limit = 3 - expect( - appendTerminalImageGalleryEntries( - [entry("/a.png"), entry("/b.png"), entry("/c.png")], - [entry("/d.png"), entry("/e.png")], - limit - ) - ).toEqual([entry("/c.png"), entry("/d.png"), entry("/e.png")]) - }) - - it("exposes a default gallery limit", () => { - expect(terminalImageGalleryLimit).toBeGreaterThan(0) - }) -}) diff --git a/packages/app/tests/docker-git/terminal-image-paths.test.ts b/packages/app/tests/docker-git/terminal-image-paths.test.ts index 51e90b2a..e9779502 100644 --- a/packages/app/tests/docker-git/terminal-image-paths.test.ts +++ b/packages/app/tests/docker-git/terminal-image-paths.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from "@effect/vitest" import { + detectTerminalImagePathMatches, detectTerminalImagePaths, isSupportedTerminalImagePath, stripTerminalAnsi @@ -13,6 +14,16 @@ describe("terminal image path detection", () => { ]) }) + it("returns match ranges for clickable image paths", () => { + expect(detectTerminalImagePathMatches("see /var/data/a.png.")).toEqual([ + { + endIndex: 19, + path: "/var/data/a.png", + startIndex: 4 + } + ]) + }) + it("detects multiple distinct image paths", () => { const text = "saved /var/data/a.png and /var/data/sub/b.jpg, also /home/user/c.webp" expect(detectTerminalImagePaths(text)).toEqual([ diff --git a/packages/app/tests/docker-git/terminal-inline-images-core.test.ts b/packages/app/tests/docker-git/terminal-inline-images-core.test.ts new file mode 100644 index 00000000..1c52330b --- /dev/null +++ b/packages/app/tests/docker-git/terminal-inline-images-core.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from "@effect/vitest" + +import { splitTerminalInlineImageOutput } from "../../src/web/terminal-inline-images-core.js" + +describe("terminal inline image output", () => { + it("keeps prompt output after a completed image path line in a later segment", () => { + expect(splitTerminalInlineImageOutput("/var/data/a.png\r\nprompt> ")).toEqual([ + { + endedWithLineBreak: true, + imagePaths: ["/var/data/a.png"], + text: "/var/data/a.png\r\n" + }, + { + endedWithLineBreak: false, + imagePaths: [], + text: "prompt> " + } + ]) + }) + + it("marks incomplete image path lines so the renderer can add a line break first", () => { + expect(splitTerminalInlineImageOutput("saved /var/data/a.png")).toEqual([ + { + endedWithLineBreak: false, + imagePaths: ["/var/data/a.png"], + text: "saved /var/data/a.png" + } + ]) + }) +}) From c2f11c6c3f4e8d128c05ed026d4c864e85c82fad Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Wed, 6 May 2026 12:07:56 +0000 Subject: [PATCH 12/12] fix(app): cap terminal inline image previews --- .../issue-240/terminal-image-gallery.png | Bin 45125 -> 46081 bytes .../issue-240/terminal-image-open.png | Bin 6978 -> 16621 bytes .../app/src/web/terminal-inline-images.ts | 14 ++++++++------ .../terminal-inline-images-core.test.ts | 6 ++++++ 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/pr-screenshots/issue-240/terminal-image-gallery.png b/docs/pr-screenshots/issue-240/terminal-image-gallery.png index 206e9f410902950f4eee277728ebc386d6098d9a..65a86c4a435186b99685a49cd15cba7a71f2f585 100644 GIT binary patch literal 46081 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 literal 45125 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 diff --git a/docs/pr-screenshots/issue-240/terminal-image-open.png b/docs/pr-screenshots/issue-240/terminal-image-open.png index 05c19bfca453c5753d3b7d849cf17ed1a1bb200c..0d026ad1ef5782eecdbfbf4b1df34020294148cb 100644 GIT binary patch literal 16621 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 Ll0LY9#*+U4ZY#qd literal 6978 zcmeAS@N?(olHy`uVBq!ia0y~yU|qn#zGe7*!mj0SCw^X1Bya+!0mT3jFtzZW#VYPFoLSX?++Op zCf5J^`{U{R+n-;){d+%OZr`8U`hTw;9=HF=9sl#4`d_*I&-wq`p8w50Z+`WU-uR!t z|Noa`h}iG|m{S-&=~yt_sH|pSc+hfy@jxH2ZFxYjk-0&(k(r@E8CaZJgNjsl8A(={ z+KoVM4?0GuS{6QrG9YH?tLO$b_$Q5E!sP%wX~8gvO{^ zU^plhFlwhn040IBy8-(?qiPYE^4=m)&+ { it("keeps prompt output after a completed image path line in a later segment", () => { @@ -27,4 +28,9 @@ describe("terminal inline image output", () => { } ]) }) + + it("keeps inline image previews compact in the terminal output stream", () => { + expect(terminalInlineImagePreviewRows).toBe(4) + expect(terminalInlineImageSpacer).toBe("\r\n\r\n\r\n\r\n") + }) })