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
Preview Images Before Sending Show borderless 70px image thumbnails in the composer, generated as dimension- and byte-bounded previews with a compact remove affordance.
Smooth Preview Loading Reserve the final 70px thumbnail frame with a light gray placeholder and accessible spinner while each bounded preview is generated, and recover stale lazy modules with a clean reload after a server or deployment update.
Render Sent Images Inline Persist a hidden bounded preview beside the original upload and use only that preview in live, reloaded, and shared conversation bubbles, while keeping the original downloadable and falling back to its file chip.
Arrange Image Pairs Present exactly two sent images in a compact side-by-side grid while leaving single-image sizing unchanged.
Cover Image Lifecycles Test staged and loading previews, passive sent images, hidden WebP and Safari PNG preview persistence, original–preview share mapping, fallback behavior, transcript hydration, stale-module recovery, shared views, and accessible image labels.
Screenshots
Composer Preview A staged image appears as a borderless 70px thumbnail above the prompt.
Sent Prompt The sent prompt renders the image directly without file metadata or controls.
Paired Images Exactly two images render as compact square crops in a two-column grid.
Shared Conversation Read-only shared transcripts use the same passive image presentation.
Risk assessment
Stylistic frontend only: No The production diff changes attachment preview generation, safe image-source selection, persistence, and stale-module recovery at runtime.
Logic changes: Present Supported local images receive a 512px, 1 MB internal WebP or Safari PNG preview that is uploaded and persisted separately; preview decoding is serialized, loading frames reserve layout space, stale lazy imports reload the current app, and invalid or oversized images fall back to chips.
Security risk: Low Full attachment base64 and original artifacts are never used as image sources, source bytes, aggregate decoding, decoded pixels, and preview output are bounded, previews are revalidated server-side, hidden previews require the original viewer authorization plus an explicit preview path, and authentication, dependencies, and secrets are unchanged.
Risk level: Medium · Safe to auto-merge: No The UI behavior is narrow and reversible, but the fix adds optional preview metadata to attachment persistence and sharing paths; it has focused core and Web UI coverage and requires no migration or operational coordination.
These images are the attachment content, not decorative UI, but alt="" removes them from the accessibility tree. Because the shared view also removes the filename/chip, screen-reader users get no indication that an image was sent; use a non-empty generic label such as Attached image without exposing the filename visually.
alt="" marks the uploaded image as decorative, so screen readers get no indication that this attachment exists or what it represents. Keep the UI free of filenames, but provide a generic accessible name (for example, alt="Attached image") or another non-visual description.
Shared uploaded images are hidden from screen readers
plugins/web-ui/src/shared-session.ts:62
The shared view repeats the accessibility regression by hiding every user-uploaded image from the accessibility tree with an empty alt. Use a generic accessible name such as alt="Attached image"; this does not add a filename, link, or control to the visual presentation.
The shared renderer has no load-error fallback for this new passive <img>. An allowlisted MIME does not guarantee decodable bytes, and a copied share blob can also become unavailable, so the shared transcript can show a broken image indefinitely instead of the promised file-chip fallback. Track failed sources and rerender this attachment as a chip, matching the live renderer.
browserRenderableImage includes image/avif, but imageDimensions() has no AVIF parser. Consequently every AVIF passes the initial renderability check but gets undefined dimensions, so boundedImagePreview() always falls back to a file chip and sent AVIFs are never rendered inline. Add a bounded AVIF dimension parser or remove AVIF from the shared allowlist consistently.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes
Screenshots
Composer Preview A staged image appears as a borderless 70px thumbnail above the prompt.
Sent Prompt The sent prompt renders the image directly without file metadata or controls.
Paired Images Exactly two images render as compact square crops in a two-column grid.
Shared Conversation Read-only shared transcripts use the same passive image presentation.
Risk assessment