fix: lay the screenshot preview out to the image, not to the request

The preview rendered, but with dead space around it. `MdImage.qml` sizes its
container to the request rather than to the image, so with no hint it is the
full pane width and 200px tall and the image is aspect-fitted inside that. A
wide capture sat in a letterbox: measured against the real captures in the
support directory, a 4:1 crop wasted about 106px of the 200px container and a
6.7:1 crop about 140px, which is what made the transcript look gappy and put
the caption far below the image.

- `readAttachment` now reads the PNG `IHDR` chunk, so `Attachment` carries the
  real dimensions and they are persisted with the turn.
- `previewBox` computes an exact box from those dimensions, scaled to fit
  400x200 with the scale capped at 1 so a small capture is not upscaled, and
  `imageSource` emits it as the `raycast-width`/`raycast-height` hints
  `parseImageSize` looks for. The path is resolved from the URL before the
  query, so the hints never reach the file name.
- The alt text is now "Screenshot". It is drawn centred under the image, so the
  sentence "Screenshot attached" read as a stray line of prose floating in the
  middle of the pane.
- A pending note no longer stacks on top of the empty-transcript placeholder.
  Both said the same thing, one line under the other, once a snip was attached.

A capture with no readable header simply falls back to the old sizing.

Verified: 293 tests, tsc, eslint, prettier, and vici build.
This commit is contained in:
2026-10-04 15:41:45 +08:00
parent c1212192c5
commit 2c0653c23c
9 changed files with 227 additions and 26 deletions
+5 -5
View File
@@ -32,13 +32,13 @@ export const STREAMING_CURSOR = "▍";
* than something readable, which is why the action panel also offers Open
* Screenshot for the full image.
*
* The alt text is not decoration: it is what remains if the file has since been
* deleted, so it says what the picture was rather than leaving a blank space.
* The alt text is short on purpose. Whatever is here is drawn centred under the
* image, so a sentence reads as a stray line of prose floating in the middle of
* the pane. It is also the only thing left if the file has since been deleted,
* which is why it is not empty.
*/
export function renderAttachments(attachments: Attachment[]): string {
return attachments
.map((attachment) => `![Screenshot attached](${imageSource(attachment.path)})`)
.join("\n\n");
return attachments.map((attachment) => `![Screenshot](${imageSource(attachment)})`).join("\n\n");
}
/** Which optional sections the transcript includes. Driven by preferences. */