Skip to content

feat(desktop,service,features): draw changed images before and after in Files changed - #3638

Merged
luke-agent[bot] merged 4 commits into
mainfrom
feat/files-changed-image-diffs
Oct 18, 2026
Merged

luke-agent[bot] merged 4 commits into
mainfrom
feat/files-changed-image-diffs

Conversation

@dastratakos

@dastratakos dastratakos commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Files changed extras, E2 of the "Files changed extras" stack, stacked on #3304.

What was asked: show changed images (png, jpg, gif, webp, svg) before and after instead of "binary file", with stage-cli's cheap modes. Read the bytes for the base and head through the service, inside the agent's own repository only, reusing #3206's image proxy and contents-API path. Bound the size, and mind the CSP.

What the window does

  • The image takes the place of lines. A changed image is drawn as the file's lead: the measured, full-width body feat(desktop): list every file in Files changed, with a filter, collapse all, and stage-cli's keys #3304 introduced for files with no lines.
    • Modified and renamed images get a segmented control for 2-up, Swipe and Onion skin. Swipe and onion skin each have a slider.
    • An added image shows its one side, labelled "Added". A deleted image shows "Deleted".
    • Each picture sits on a checkerboard, so transparency shows. In Swipe and Onion skin, both sides fill one canvas as large as the larger, so at full strength the after hides the before whatever their sizes.
  • SVGs start as pictures. When an SVG has lines to draw, a <> button in the header switches to its source, and back.
  • Lazy loading. A side is read only once its file is open and near the view (an IntersectionObserver on the lead). It is then kept for the window's life (@luke/ui/picture-cache), so a collapsed or far-off image costs nothing. A side whose read failed is asked for again on Refresh changes.
  • CSP. Pictures draw from data: URLs the window makes, which img-src allows. The swipe clip and the onion-skin opacity are set through React's style (the CSSOM), never style markup.
  • Threads on a pictured file stand in its lead, as on a binary file.

Where the bytes come from

  • No new fetch path. Each side is read through the existing CODING_AGENTS_GITHUB_IMAGE act, as raw.githubusercontent.com/<owner>/<name>/<sha>/<path>. fix(desktop,service,features): draw bot badges and repository screenshots on the Pull request tab #3206's githubImageOf reads that address as a FILE image.
  • The service then:
    • reads the image through the contents API on the App's token, in the agent's own repository only
    • accepts png/jpg/gif/webp/svg only, checks the bytes against the type's signature, and caps the size at GITHUB_IMAGE_BOUNDS.MAX_BYTES (8 MB)
  • An image the service won't answer reads "This image could not be shown."

Service changes

The changes answer gains two optional keys, headSha and baseSha:

  • headSha comes from one extra GET /pulls/{n}, run in parallel with the files and people reads the endpoint already makes.
  • baseSha is the merge base, not the base branch's tip, so a "before" picture is never one the pull request didn't have. It comes from GET /compare/{base}...{head}?per_page=1 (imageCommits), which runs only when the files include a modified, renamed or deleted image.
    • A pull request without such an image pays nothing extra.
    • If the compare fails, baseSha is left out and the window shows the after side alone with "Previous version unavailable". It never falls back to the base branch's tip.
  • imageTypeOfPath is now exported from @luke/features/github-image, so the service and the window share one extension → type table.

Goldens

LUKE_UPDATE_FIXTURES=1 regenerated codingAgentChangesAnswerSchema and codingAgentChangesAnswerViewSchema. Each gains only the two optional string keys, headSha and baseSha, under pullRequest.

Screenshots

From a throwaway harness (fake app bridge, real bundles and stylesheet), since deleted.

2-up, with an added image above Swipe (dark, split setting)
2-up swipe
Onion skin Merge base unreadable: after alone
onion no base

Narrow (600px, tree hidden):

narrow

Evidence

  • Desktop and service tsc: pass.
  • pnpm lint, pnpm knip, pnpm discarded-effect, ./scripts/repository-checks.sh: pass.
  • pnpm exec vitest run apps/desktop/src/renderer/tasks packages/service/src/agents/coding-agents-app.test.ts packages/service/src/github packages/features/src: 150 files, 1752 tests passed.
  • New tests:
    • image-commits.test.ts:
      • an image changed in place reads the merge base from one per_page=1 compare
      • a deleted image needs the merge base too; an added image or a text file asks for no compare
      • a compare that fails names no merge base, never the base branch's tip
    • agent-changes-tab.test.ts:
      • an image is read at the merge base and the head only once it comes near the view, and draws before/after with Swipe working
      • with no merge base, the after side draws alone with "Previous version unavailable"
      • an SVG draws as a picture until its source is asked for
      • an image from a service that names no commits stays a binary file
      • a side whose read failed is read again on Refresh changes
      • an SVG renamed without changes offers no source
      • the chosen comparison and slider survive Refresh changes
      • after a refresh, an image far from the view reads nothing until it comes near again
  • pnpm check:affected ran out of memory in this sandbox, so the checks above were run one at a time instead.

Invariants

  • ./scripts/check.sh green. (CI)
  • JSON Schema goldens: changed, explained above.
  • Every behavior change has a test that fails without it; no change-detector test added.
  • Gateway envelope goldens unchanged.
  • No new as assertion.
  • No Effect.run* outside the edges.
  • No new raw async primitives.
  • No discarded Effect.
  • Test count: +11, listed above.
  • No hand-rolled file replaced.
  • No CLAUDE.md/AGENTS.md sentence names a changed part.
  • PRIVACY.md unchanged. Images stay inside the list's ph-no-capture.
  • No dependency change.
  • Barrel/door rule untouched.

Notes

@vercel

vercel Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
luke-web Ready Ready Preview Oct 18, 2026 5:23pm UTC

Request Review

@luke-agent
luke-agent Bot changed the base branch from main to feat/files-changed-list October 18, 2026 16:53
@luke-agent
luke-agent Bot force-pushed the feat/files-changed-image-diffs branch from 937c180 to c9a3efb Compare October 18, 2026 16:55
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Base automatically changed from feat/files-changed-list to main October 18, 2026 17:13
dastratakos and others added 4 commits October 18, 2026 17:19
…efore and after, side by side, swiped, or faded

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…verlays on one canvas, and offers source only with lines

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…he changes

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…me near before it reads

Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
@luke-agent
luke-agent Bot force-pushed the feat/files-changed-image-diffs branch from 5ce0431 to 0cae842 Compare October 18, 2026 17:20
@luke-agent
luke-agent Bot enabled auto-merge October 18, 2026 17:20
@luke-agent
luke-agent Bot added this pull request to the merge queue Oct 18, 2026
Merged via the queue into main with commit ca9ecc4 Oct 18, 2026
12 of 18 checks passed
@luke-agent
luke-agent Bot deleted the feat/files-changed-image-diffs branch October 18, 2026 17:39

This branch was successfully deployed

1 active deployment
Preview — 0cae842d Deployed Oct 18, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant