Skip to content

feat(app,api): inline terminal image gallery for detected paths#241

Merged
skulidropek merged 16 commits intoProverCoderAI:mainfrom
konard:issue-240-b08428ed1942
May 6, 2026
Merged

feat(app,api): inline terminal image gallery for detected paths#241
skulidropek merged 16 commits intoProverCoderAI:mainfrom
konard:issue-240-b08428ed1942

Conversation

@konard
Copy link
Copy Markdown
Contributor

@konard konard commented May 5, 2026

Summary

Closes #240.

When the terminal stream prints an absolute path that looks like an image file (e.g. /var/data/screenshot.png), the path is detected and a thumbnail strip below the terminal renders the image inline. Each thumbnail is clickable and opens the full-resolution image in a new tab.

The image bytes are served by a new authenticated terminal-sessions image endpoint that runs docker exec cat <path> inside the same project container the terminal is attached to, with strict path validation and a 10 MB cap.

Changes

Backend (packages/api)

  • services/terminal-image-fetch-core.ts — pure path validator (planTerminalImageFetch): rejects relative paths, traversal, NUL bytes, and non-image extensions; enforces terminalImageFetchMaxBytes = 10 * 1024 * 1024.

  • services/terminal-sessions.ts — adds readProjectTerminalImage which streams the file via docker exec for the project's container.

  • http.ts — adds withProjectTerminalImages router exposing:

    • GET /projects/by-key/:projectKey/terminal-sessions/:sessionId/image?path=<abs>
    • GET /projects/:projectId/terminal-sessions/:sessionId/image?path=<abs>

    Responses use HttpServerResponse.uint8Array with the detected MIME type.

Frontend (packages/app)

  • web/terminal-image-paths.ts — detects absolute image paths in terminal output (strips ANSI CSI/OSC sequences first, matches .png|.jpg|.jpeg|.gif|.webp).
  • web/terminal-image-url.ts — derives the image fetch URL from session.websocketPath by replacing the /ws suffix with /image and appending the path query, honoring resolveApiBaseUrl() for both absolute and same-origin proxy bases.
  • web/terminal-image-gallery-core.ts — pure list merge: dedupes by path, caps at the 20 most recent entries, and returns the same reference when nothing new arrives (so React can skip re-renders).
  • web/terminal-panel-runtime-{types,core,runtime}.ts — threads an optional onImagePaths callback through the lifecycle. When the server sends an output message, the runtime extracts any new image paths and forwards them to the panel.
  • web/panel-terminal.tsx — owns the gallery state, resets on session change, builds entries via resolveTerminalImageFetchUrl, and renders the thumbnail strip beneath the terminal body.

Tests

New unit tests, all running under vitest:

  • tests/docker-git/terminal-image-paths.test.ts — strips ANSI before matching; finds paths in mixed output.
  • tests/docker-git/terminal-image-url.test.ts/ws/image suffix swap; absolute URL composition with encoded path query; same-origin proxy mode.
  • tests/docker-git/terminal-image-gallery-core.test.ts — append, dedupe, identity preservation when no additions, FIFO trim, and the terminalImageGalleryLimit constant.
  • tests/docker-git/terminal-image-fetch-core.test.ts (api) — accept/reject matrix for planTerminalImageFetch.

CI parity with main

Lint, Lint Effect-TS, and Test are red on this PR and on main (run 25343558501) with the same error sets:

  • Lint Effect-TS: 8 errors at exactly the same lines on both branches (Casting is only allowed in src/core/axioms.ts at 8:65, 9:17, 22:27, 56:27, 282:79 — all in pre-existing files).
  • Test: 48 errors (8 TypeScript, 40 ESLint) on both branches; the failing files (tests/docker-git/terminal.test.ts, tests/docker-git/app-ready-create.test.ts) are unchanged by this PR.
  • Lint: 14 distinct error patterns, identical between main and this branch; the Total: 39 vs 38 headline difference is line-number drift inside pre-existing nested-ternary warnings in panel-terminal.tsx, not a new violation.

E2E (Login context) was cancelled at the 40-minute job timeout while still running apt-get install containerd (Azure mirror slowness). My code never ran in that job; the other five E2E checks (Local package CLI, OpenCode, Clone cache, Clone auto-open SSH, Runtime volumes + SSH) all pass.

Test plan

  • bunx tsc --noEmit (clean)
  • bunx vitest run in packages/app (47 files / 236 tests pass, including the 19 new terminal-image tests)
  • bunx vitest run in packages/api (image-fetch core suite passes)
  • CI: every passing check on main is also green here; failing checks have identical pre-existing errors
  • Manual visual verification: emit an absolute image path from a terminal session and confirm the thumbnail appears and opens at full resolution (requires a real Docker environment and is out of scope for the sandbox CI)

konard added 6 commits May 5, 2026 20:55
Adding .gitkeep for PR creation (default mode).
This file will be removed when the task is complete.

Issue: ProverCoderAI#240
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.
- 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
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.
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 -- <path>, enforces a
10 MiB cap, and surfaces missing files as 404 and oversize as 400.
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.
@konard konard changed the title [WIP] Я думаю стоит сделать что если есть полный путь до изображения то изображение отрисовывается в терминале feat(app,api): inline terminal image gallery for detected paths May 5, 2026
konard added 2 commits May 5, 2026 21:46
The Effect-TS lint config forbids `as` expressions outside
src/core/axioms.ts. Type the supported-extensions list with an explicit
`ReadonlyArray<string>` annotation instead of an `as const` literal.
The PR is no longer a placeholder draft; the placeholder file added at
branch creation can be deleted.
@konard konard marked this pull request as ready for review May 5, 2026 22:32
@konard
Copy link
Copy Markdown
Contributor Author

konard commented May 5, 2026

Working session summary

PR #241 is now marked ready for review with an updated description that documents CI parity with main and explains the cancelled E2E (Login context) job as Azure-mirror infra flakiness unrelated to this change.

Final state:

  • PR: feat(app,api): inline terminal image gallery for detected paths #241 (ready)
  • Branch: issue-240-b08428ed1942 at commit e16ea2c
  • Passing checks: Build, Types, Snapshot, Dist deps prune (×2), 5/6 E2E
  • Failing checks: Lint, Lint Effect-TS, Test — verified parity with main run 25343558501 (identical 14 / 8 / 48 errors at identical lines in pre-existing files)
  • Cancelled: E2E (Login context) — 40-min job timeout while installing containerd from a slow Azure mirror; my code never ran

This summary was automatically extracted from the AI working session output.

@konard
Copy link
Copy Markdown
Contributor Author

konard commented May 5, 2026

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost: $26.513267

📊 Context and tokens usage:

Claude Opus 4.7: (6 sub-sessions)

  1. 116.6K / 1M (12%) input tokens, 10.6K / 128K (8%) output tokens
  2. 116.8K / 1M (12%) input tokens, 26.7K / 128K (21%) output tokens
  3. 119.5K / 1M (12%) input tokens, 25.9K / 128K (20%) output tokens
  4. 116.0K / 1M (12%) input tokens, 19.4K / 128K (15%) output tokens
  5. 117.0K / 1M (12%) input tokens, 36.4K / 128K (28%) output tokens
  6. 75.5K / 1M (8%) input tokens, 12.4K / 128K (10%) output tokens

Total: (33.0K new + 592.5K cache writes + 37.0M cache reads) input tokens, 166.3K output tokens, $26.513267 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.7 (claude-opus-4-7)

📎 Log file uploaded as Gist (11305KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
Copy link
Copy Markdown
Contributor Author

konard commented May 5, 2026

🔄 Auto-restart 1/5

Detected uncommitted changes from previous run. Starting new session to review and commit or discard them.

Uncommitted files:

?? ci-logs/

Auto-restart will stop after changes are committed or discarded, or after 4 more iterations. Please wait until working session will end and give your feedback.

@konard
Copy link
Copy Markdown
Contributor Author

konard commented May 5, 2026

🔄 Auto-restart 1/5 Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost: $0.328605

📊 Context and tokens usage:

  • 37.5K / 1M (4%) input tokens, 2.0K / 128K (2%) output tokens

Total: (15 new + 31.1K cache writes + 167.9K cache reads) input tokens, 2.0K output tokens, $0.328605 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.7 (claude-opus-4-7)

📎 Log file uploaded as Gist (11632KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
Copy link
Copy Markdown
Contributor Author

konard commented May 5, 2026

Working session summary

All three PR comments are from the prior session (status updates and the auto-restart notice), no new human review comments. The 3 failing checks (Lint, Lint Effect-TS, Test) on the latest run are documented as pre-existing parity with main.

The uncommitted ci-logs directory has been cleaned up — it contained empty debug log files from the prior session that aren't part of the solution. The working tree is now clean and the PR remains in its prior ready state.

Cleaned up the leftover empty ci-logs/ debug directory; tree is clean. PR #241 is unchanged and remains ready for review at #241 — the 3 failing checks (Lint, Lint Effect-TS, Test) match the documented pre-existing parity with main, and there are no new human review comments since the last session.


This summary was automatically extracted from the AI working session output.

@konard
Copy link
Copy Markdown
Contributor Author

konard commented May 5, 2026

🔄 Auto-restart triggered (iteration 1)

Reason: CI failures detected; Merge conflicts

Starting new session to address the issues.


Auto-restart-until-mergeable mode is active. This run will stop after 5 restart iterations.

konard added 2 commits May 5, 2026 22:37
# Conflicts:
#	packages/app/src/web/panel-terminal.tsx
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.
@konard
Copy link
Copy Markdown
Contributor Author

konard commented May 5, 2026

🔄 Auto-restart-until-mergeable Log (iteration 1)

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost: $6.353594

📊 Context and tokens usage:

Claude Opus 4.7: (2 sub-sessions)

  1. 117.3K / 1M (12%) input tokens, 20.2K / 128K (16%) output tokens
  2. 66.4K / 1M (7%) input tokens, 11.2K / 128K (9%) output tokens

Total: (1.9K new + 162.1K cache writes + 8.9M cache reads) input tokens, 36.0K output tokens, $6.353594 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.7 (claude-opus-4-7)

📎 Log file uploaded as Gist (14658KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
Copy link
Copy Markdown
Contributor Author

konard commented May 5, 2026

Working session summary

I'll wait on the running background watchers instead. They'll notify when checks finish.


This summary was automatically extracted from the AI working session output.

@konard
Copy link
Copy Markdown
Contributor Author

konard commented May 5, 2026

🔄 Auto-restart triggered (iteration 2)

Reason: Uncommitted changes detected

Starting new session to address the issues.


Auto-restart-until-mergeable mode is active. This run will stop after 5 restart iterations.

@konard
Copy link
Copy Markdown
Contributor Author

konard commented May 6, 2026

🔄 Auto-restart-until-mergeable Log (iteration 2)

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost: $1.619063

📊 Context and tokens usage:

  • 73.2K / 1M (7%) input tokens, 7.1K / 128K (6%) output tokens

Total: (55 new + 71.5K cache writes + 2.0M cache reads) input tokens, 7.1K output tokens, $1.619063 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.7 (claude-opus-4-7)

📎 Log file uploaded as Gist (15634KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
Copy link
Copy Markdown
Contributor Author

konard commented May 6, 2026

Working session summary

PR is MERGEABLE / CLEAN, branch is clean, all 14 checks pass.

Summary

PR #241 is now fully green and ready to merge:

  • Merged latest upstream/main (5dd8ad1) into the branch to pick up the .changeset/config.json fix from PR fix: configure changeset github changelog #245, resolving the previous UNSTABLE merge state.
  • All 14 CI checks pass on the new merge commit f362171, including the previously-failing E2E (Clone auto-open SSH) (its prior failure was an apt-get timeout against an Azure mirror, unrelated to the code in this PR).
  • Cleaned up the leftover ci-logs/ debug directory; working tree is clean.
  • No new human review comments since the prior session.

PR: #241MERGEABLE / CLEAN.


This summary was automatically extracted from the AI working session output.

@konard
Copy link
Copy Markdown
Contributor Author

konard commented May 6, 2026

✅ Ready to merge

This pull request is now ready to be merged:

  • All CI checks have passed
  • No merge conflicts
  • No pending changes

Monitored by hive-mind with --auto-restart-until-mergeable flag

@skulidropek
Copy link
Copy Markdown
Member

skulidropek commented May 6, 2026

AI Session Backup

Commit: d817e32
Status: success
Files: 1 (132 B)
Links: README | Manifest

git status

On branch issue-240-b08428ed1942
nothing to commit, working tree clean

@skulidropek
Copy link
Copy Markdown
Member

Screenshots for verification (commit af2c01f):

Terminal image gallery after printing an absolute PNG path:

terminal image gallery

Fetched image opened from the gallery link:

terminal image open

@skulidropek
Copy link
Copy Markdown
Member

Inline terminal image preview verification (commit 21f42cc):

The terminal now renders an image preview directly under the printed image path, and the path itself opens the full image.

inline terminal image preview

Full image opened from the clickable terminal path:

terminal image open

@skulidropek
Copy link
Copy Markdown
Member

Compact inline terminal image preview verification (commit c2f11c6):

The terminal printed a real 960x540 PNG path, but the inline preview is capped to a small thumbnail box (verified at 96x56 container, image rendered at 86x46). The full image remains available from the image URL/path.

compact inline terminal image preview

Full image view:

terminal image full view

@skulidropek skulidropek merged commit e43449d into ProverCoderAI:main May 6, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants