---
title: Message image carousel (a run of images becomes one swipeable viewer)
---

# Message image carousel (3+ images in an AI reply become a swipeable carousel)

## What it is

### What it is

When an AI reply includes **several images in a row** — for example an agent that made "5 versions" of a page and shows a full-page screenshot of each — Omniscio no longer stacks them into one tall column you scroll through. Instead, **3 or more consecutive images collapse into a single carousel**: one image at a time, with a small **count badge** (like `3 / 12`) in the corner.

- **On a phone:** swipe left/right through the images. The swipe stays inside the carousel — it will **never** flip you to the next/previous session the way a normal side-swipe does.
- **On desktop:** use the **‹ / ›** arrow buttons (the first/last arrow is greyed out at the ends).
- **Either way:** tap/click the current image to open it **full-screen** in the existing image viewer, exactly as before.

A run of **1 or 2** images is left exactly as it was (shown inline). Images that are broken up by text, or that sit inside a bulleted list, are also left alone — only a clean run of 3+ back-to-back images becomes a carousel.

## Where to find it

In the chat, on an AI reply that shows a run of images. There is no setting — the carousel appears whenever a reply carries three or more images in a row.

## How it behaves

### Why it works this way

- The images sit in a **fixed-height frame** and are scaled to fit (centered, letterboxed if needed), so moving between a tall screenshot and a short one doesn't make the box jump around.
- The grouping is **display-only** — it changes how the message is drawn, not what's stored. Copying or exporting the message still gives you the original text and image links.
- There's **no autoplay**; the carousel only moves when you move it, and it respects your "reduce motion" setting.

### Where the images come from

This works on **both** ways an AI reply can show images:

- Images the agent **embeds in its message text** (markdown images) — the large, roughly full-width ones.
- Images the agent **attaches to the message** — e.g. a tool render or a "here are the mockups" reply that comes back with a batch of screenshots. This is the common case, and it's what most "a dozen stacked images" replies actually are. **3 or more image attachments now collapse into the same carousel.** (1–2 attached images stay as the small thumbnails, and any non-image attachment — a PDF or doc — stays a separate file chip you can open.)

Team Chat messages are unaffected (they use a separate renderer).

## For agents

### Under the hood (for agents with repo access)

- **Grouping**: a rehype plugin, [rehype-group-images.ts](../../src/renderer/src/lib/rehype-group-images.ts), runs during markdown render. It finds a run of 3+ (`CAROUSEL_MIN_IMAGES`) consecutive **root-level** paragraphs that each hold one **renderable** image and replaces them with a single `<amc-image-carousel>` element. It's wired into the module-stable rehype arrays in [AgentMarkdownImpl.tsx](../../src/renderer/src/components/ui/AgentMarkdownImpl.tsx); the `amc-image-carousel` element is mapped in [agent-markdown-components.tsx](../../src/renderer/src/components/ui/agent-markdown-components.tsx).
- **Rendering**: [MessageImageCarousel.tsx](../../src/renderer/src/components/ui/MessageImageCarousel.tsx) — a native horizontal `overflow-x` scroll-snap track (each slide a `MarkdownImage`, so click-to-zoom is preserved), a count badge, and prev/next `IconButton`s. It **stops touch-event propagation** so the mobile session-swipe (`useSwipeNavigation`) never fires from inside it.
- **Attachment path**: [MessageAttachments.tsx](../../src/renderer/src/components/ui/MessageBubble/MessageAttachments.tsx) routes a message's `image/*` attachments through the SAME `MessageImageCarousel` at the same `CAROUSEL_MIN_IMAGES` threshold (each slide is the existing open-lightbox thumbnail button); non-image attachments stay `FileText` chips.
- **Threshold**: 3 is deliberate (owner decision), shared by both surfaces via one `CAROUSEL_MIN_IMAGES` constant. 1–2 images stay inline.
- Full invariants + the guarding tests: `.claude/memory/contracts/message-image-carousel-contract.md`.

Always on; no setting.

## Related

- [agent-message-display.md](agent-message-display.md) — how the rest of a reply renders around the images.
- [agent-markdown-formatting.md](agent-markdown-formatting.md) — the other message shapes an agent can use, including showing a single image at a chosen width.
- [mobile-swipe-dismiss.md](mobile-swipe-dismiss.md) — the phone gestures nearby, and why a swipe inside the carousel never flips your session.

