You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Paste, drop, a thumbnail chip you can take back off, and the refusal that has to happen
before anything is lost.
THE CAP IS 2000px ON THE LONG EDGE, AND IT IS MEASURED, NOT CHOSEN. The plan argued 1568
from first principles. Claude Code's own transcripts are on this machine, so I read them
instead: 24 images, and every single one it re-encodes is exactly 2000 on the long edge.
That is the threshold the vision docs name for staying clear of the stricter per-image
dimension limit above 20 images per request — the largest size that is never unsafe. It
also sits above both model tiers' caps, so the server does the final downscale and we
never throw away fidelity it would have kept. Changed 1568 -> 2000.
The same transcripts confirmed the rule I had derived rather than observed: images UNDER
the cap are passed through untouched IN THEIR ORIGINAL FORMAT (their PNGs stay PNG, their
JPEGs stay JPEG), and only oversize ones are resized and re-encoded to WebP. Re-encoding
something that did not need resizing only stacks artifacts, and that is worst on
screenshots of code, which is most of what gets pasted.
Verified in a real browser against real images, not just in unit tests:
4K screenshot 3840x2160 png 764KB -> 2000x1125 webp 115KB resized
retina window 3024x1964 png 654KB -> 2000x1299 webp 137KB resized
small PNG 1160x480 png 121KB -> 1160x480 png 121KB PASS-THROUGH
under-cap JPEG 1600x900 jpg 208KB -> 1600x900 jpg 208KB PASS-THROUGH
wide panorama 2400x600 png 178KB -> 2000x500 webp 51KB resized
TIFF -> refused, with what to do instead
Aspect ratios come out exact (16:9 stays 16:9, 4:1 stays 4:1).
THE REFUSAL RUNS AT ATTACH TIME, NOT SEND TIME. The composer clears on send, so refusing
host-side would throw away what someone had written. canSeeImages travels with the model
config instead — from BOTH config paths, gateway and BYOK, which a test pins, because one
of them silently allowing is exactly the kind of half-gate that looks fine in review.
Other decisions worth naming:
- An image with no words is a valid message. "Look at this" is implied by attaching it.
- The chip carries its own thumbnail. An attachment you cannot see is one you cannot
check, and a screenshot is the case where the wrong one looks exactly like the right
one in a filename.
- Drop is bound to the whole panel, not the composer: someone dragging a screenshot
aims at the conversation, which is the far bigger target.
- A paste with no image falls through untouched, and the bail comes BEFORE
preventDefault — reversing those two silently breaks ordinary text pasting.
- withImages() copies. agentMessages persists across runs and is what recordTurn writes
to the session log; materializing in place would put megabytes of base64 in both.
9 tests in imageAttach, 37 suites green.
0 commit comments