---
title: Attach a file
---

# Attach a file

## What it is

You can attach images and documents to the next message you send by clicking the **paperclip** icon next to the composer textarea, dragging files anywhere onto the conversation window, or pasting from the clipboard (Ctrl+V — including screenshots). Attached files appear above the textarea as small preview chips (image thumbnails or a `FileText` icon for documents) before you send. When you hit Send, Omniscio packages the files into the message in the way Claude can actually consume — you don't have to think about which file type goes where.

Three file kinds are supported:

- **Images** (PNG, JPG, WebP, GIF, etc.) — Claude sees them via vision.
- **PDFs** — Claude sees them via vision _and_ the file lands in the project folder so the agent can also `Read` it as a regular file.
- **Text documents** (`.md`, `.txt`, `.csv`, `.json`, `.html`, `.xml`, `.tsv`, `.log`, `.markdown`) — saved into the project folder; the file's path is mentioned in the prompt so Claude can `Read` and edit it just like any project file.

**Office formats** — the modern `.docx` / `.xlsx` / `.pptx`, their **macro-enabled twins `.xlsm` / `.docm` / `.pptm`**, plus legacy binary `.doc` / `.xls` / `.ppt`, OpenDocument `.odt` / `.ods` / `.odp`, `.epub`, and `.rtf` — are **all** accepted. Omniscio extracts them to Markdown in the main process (`extractOfficeText`, which routes through the on-device **anydoc** reader for clean tables, with `.xlsx` on its own dedicated extractor) and injects the extracted `.md` path into the prompt so the agent can `Read` it. The Anthropic API has no native Office content block, so Omniscio does the extraction itself — nothing is blocked at attach time anymore.

The macro-enabled trio share the OOXML container with their non-macro twins, so they use the same extractors — a `.xlsm` reads through the `.xlsx` pipeline. **Not** supported: `.xlsb` (Excel's binary workbook — its sheets are `.bin`, not XML, so there is nothing to extract) and Apple `.numbers`. Those are refused with an "unsupported file type" toast rather than being attached in a form the model can't read; save as `.xlsx` and re-attach.

**A file Omniscio can't take always says so.** Every intake path — drop, paste, paperclip — names the files it turned away in a single batched toast. If you drop something and no chip appears, you'll get a warning telling you which file and why; silence never means "still working on it".

The deeper "what does Claude actually see for each attachment type" picture lives in [chat-attachments.md](chat-attachments.md). This page is for the user-action shape — getting files into the composer.

**Also in the "Start session" dialog.** The **Start session** dialog you open from a **drip** or **alert** inbox item now takes attachments too — click the paperclip, drag files in, or paste (Ctrl+V, including screenshots) into its prompt box, and the files ride along on the session's very first message. Same file kinds, preview chips, and size limits as below; it reuses the exact composer machinery (the shared `usePromptAttachments` hook), so nothing behaves differently — you just don't have to open the session first. Documents (PDF / .docx / .txt …) work here as well as images.

## Where to find it

### How to use it

1. **Click the paperclip** in the small tinted tool zone on the left side of the composer (look for the `Paperclip` icon, tooltip "Attach files"). The OS file picker opens. Pick one or more files. The picker's `accept=` filter is best-effort — the OS may still let you select anything, and Omniscio re-validates after you confirm.
   - **On a phone**, the composer's action buttons show **inline** in the tinted zone to the left of the text box — the same as on desktop, just the ones you've turned on (no **+** menu to tap through). Choose which buttons appear — and hide ones you never use to free up room for typing — at **Settings → Sessions → Composer buttons**; switch them all off and the zone disappears entirely. As the text box grows tall, those icons stack into a vertical column so they stay tidy beside it instead of floating in the middle of the empty space.
2. **Or drag and drop.** Drag any file (or multiple) anywhere over the conversation window — the whole panel highlights with a dashed accent border to confirm a valid drop target. Release. Same validation rules.
3. **Or paste from clipboard.** Ctrl+V (or Cmd+V) inside the textarea works for both copied files (right-click a file → Copy → paste) and clipboard images (e.g. a screenshot you just took with PrtScn / Snipping Tool / macOS Cmd+Shift+5). Pasted images get an auto-name like `pasted-image.png`.
4. **(Or use the keyboard shortcut.)** Default is **Ctrl+U** — bound to the `attachFiles` action. It opens the same OS file picker as the paperclip button.
5. **Watch the chips appear.** Above the textarea, each attached file shows as a 64×64 chip. Images are thumbnails — click one to open a full-size lightbox, then close it with the large **X** button in the top-right corner, by tapping anywhere outside the image, or with **Esc** (arrow keys cycle through if you have several). The close button is a big, high-contrast circle sized for touch, so on a phone you don't have to reach for the device Back button. PDFs and text docs show a `FileText` icon plus the filename. A small `X` on the chip itself removes the attachment; you can also **middle-click any chip — image or document — to remove it**. However you remove one, it is **undoable**: a toast offers **Undo**, and **Ctrl+Z** brings the chip back in its original position (Ctrl+Y removes it again). Removing a chip is never a one-way door.
6. **Type your message and Send.** All attached files travel with that one Send. The chips clear after a successful send. If you hit Send with no text but at least one image (or document) attached, Omniscio inserts a placeholder prompt — the `DEFAULT_IMAGE_MESSAGE` constant, "Please analyze the user's image and infer their desire using the project docs, context, and your best abilities, to deliver what they want." — so Claude has something to act on; your typed prompt always overrides this.

Caps and limits:

- **No count cap** — attach as many images, PDFs, text docs, and pasted-text chips as you want. The IPC schema bounds extreme cases as defense-in-depth (20 images, 50 pasted-text chips per send) but you'll never hit those in normal use. The renderer-side `MAX_IMAGE_ATTACHMENTS = 4` / `MAX_DOC_ATTACHMENTS = 4` caps were removed in 2026-04 alongside paste-as-attachment.
- **30 MB per image, 32 MB per document, 2 MB per pasted-text chip** — Anthropic's vision limit is 32 MB; Omniscio enforces 30 for images out of an abundance of caution. Oversized files are rejected with a toast.
- **One toast per send** batches every problem (Office blocked, oversized) into a single friendly warning so you don't get spammed.
- **Right-click a chip** (image only) for extra actions: Save As, Copy, Open in default viewer.

What happens to the files after Send:

- **Images** become inline `image` content blocks Claude reads with its vision capability — they are NOT saved to your project folder. Nothing has left Omniscio until you Send, so removing a chip (by `X`, middle-click, or Ctrl+Z) before sending is always reversible.
- **PDFs** are sent inline as a `document` block (vision-rendered) AND saved to `<project-folder>/.claude/amc-attachments/<sessionId>/<filename>`. So Claude can both _see_ the PDF as pixels and _open it as a file_ with the `Read` tool.
- **Text documents** are saved to `<project-folder>/.claude/amc-attachments/<sessionId>/<filename>` and the absolute path is prepended to your prompt as `Please examine the following file(s): <path>`. Claude can `Read` and even edit the file in place — it's a real file in your project tree from that point on.

If your project folder isn't writable (read-only mount, permissions error), Omniscio falls back to saving documents under `${userData}/attachments/.claude/amc-attachments/<sessionId>/` and the prompt prefix uses that path instead. The file is still reachable; it just lives outside the project tree. (The `.claude/amc-attachments` segment is the same subdirectory the writer uses inside a project folder — only the root changes.)

#### A note about models that can't read the attachment

Some models are text-only or can't take every file kind — for example an image on DeepSeek V4 Flash (non-vision) or V4 Pro, or a PDF on Gemini. If you attach something the session's selected model can't use, an amber note appears above the attachment strip — "{model} can't use attached images — they won't be sent to the model." It's advisory only: sending always still works. The supported-set is data-driven in [model-media-support.ts](../../src/shared/model-media-support.ts), which resolves per **model**, not per provider — DeepSeek V4.1 Flash and V4 Flash Vision read images fine even though the DeepSeek provider base is text-only.

## How it behaves

### How it works

The paperclip and the hidden file input live in [SessionComposerToolbar.tsx](../../src/renderer/src/features/sessions/SessionPanel/SessionComposerToolbar.tsx); on desktop the paperclip opens the **main-process** native dialog (`DIALOG_PICK_ATTACHMENTS` in [dialog-handlers.ts](../../src/main/ipc/dialog-handlers.ts)), whose "All supported" filter is the authoritative extension list, and the hidden `<input accept=…>` is the web/mobile fallback. The drag-and-drop handlers are mounted on the panel root `<div data-ui-anchor="session-panel">`, so the entire conversation window — header, message list, and composer — is one big drop target. The dashed accent overlay rendered above all children (`pointer-events-none absolute inset-0 z-30`) confirms a valid drop. The three input paths (`handleFileSelect` and `handleDrop` in [useSessionAttachments.ts](../../src/renderer/src/features/sessions/useSessionAttachments.ts), `handlePaste` in [useSessionPanel.ts](../../src/renderer/src/features/sessions/useSessionPanel.ts)) converge on the same validation pipeline: `isBlockedOffice()` first, then `isAllowedMedia()` from [session-attachment-validation.ts](../../src/renderer/src/features/sessions/session-attachment-validation.ts), then `validateAndReadFiles()` which size-checks and base64-reads. Accepted files become `pendingImages` state — the chip strip in `SessionPanel.tsx` renders from that array.

`isAllowedMedia` **fails closed**: a file it doesn't recognise is rejected, never guessed at. There is deliberately no "came from the picker, so trust the `accept=` filter" shortcut — every OS dialog offers an "All files" escape hatch, so the filter was never authoritative, and defaulting an unknown file to `image` produced a broken `<img>` preview chip and shipped the bytes to the model stamped `image/png`. Every allow-list that decides this — the renderer sets, the `ATTACHMENT_MIME_TYPES` IPC enum, the send-time partition, the MIME→ext map, and the native dialog's filters — is generated from the single source in [attachment-mime.ts](../../src/shared/attachment-mime.ts) so they cannot drift apart.

On Send, the pending attachments travel through `SESSION_SEND_RESPONSE` to [session-service.ts](../../src/main/services/session/session-service.ts) `sendResponse()`, which calls `partitionAttachments()` from [attachment-partition.ts](../../src/main/services/attachment-partition.ts) to split images / PDFs / text docs / dropped. PDFs and text docs go through `saveDocToWorkdir()` in [workdir-attachment-store.ts](../../src/main/services/workdir-attachment-store.ts) — atomic write with `.tmp → rename`, sanitized filename, symlink-escape defense via `fs.realpath`, and `(2)`/`(3)` suffix on collisions. Operator-attached doc paths and any auto-injected `.claude/docs/` paths are deduped through a Set and prepended via `buildPromptWithAttachments()` in [image-store.ts](../../src/main/services/image-store.ts). The IPC schema also caps file size and MIME type at the boundary as defense-in-depth — see [ipc-schemas.ts](../../src/shared/ipc-schemas.ts) `ATTACHMENT_MIME_TYPES`. Full pipeline detail in [chat-attachments.md](chat-attachments.md).

## Related

- [chat-attachments.md](chat-attachments.md) — deeper internals: per-bucket handling, Anthropic API content blocks, save paths, retention sweep
- [send-a-message.md](send-a-message.md) — the Send pipeline that ships the attachments
- [start-a-new-session.md](start-a-new-session.md) — needs an open session before you can attach
- [project-docs-auto-injection.md](project-docs-auto-injection.md) — files in `.claude/docs/` auto-attach to every session's first message via the same prompt-prefix mechanism
