Skip to content

feat(attachments): support image paste, drop, and picker in the composer - #259

Closed
kunchenguid wants to merge 9 commits into
mainfrom
cursor/composer-image-attachments-2966
Closed

feat(attachments): support image paste, drop, and picker in the composer#259
kunchenguid wants to merge 9 commits into
mainfrom
cursor/composer-image-attachments-2966

Conversation

@kunchenguid

@kunchenguid kunchenguid commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Intent

Last-resort port of the accepted #248 Conversation composer image attachments onto current main. Original work by @adelin-b (feat/clipboard-image-paste). Composer image paste/drop/picker stays the always-on default; that product call is closed. #248 is left open.

What Changed

  • Port the feat(attachments): support image paste, drop, and picker uploads in the conversation composer #248 composer attachment path: always-on Attach images picker, clipboard paste, and drag-and-drop for PNG/JPEG/WebP in the Conversation composer, with thumbnail chips, a notice derived from chip state, and a send gate that holds only the composer's own message (queued annotation prompts still deliver).
  • Keep fail-closed attachment validation: queued prompts carry server-generated path + content-hash id, never raw bytes; composer uploads share the page-wide rate/byte/in-flight abuse budget with the annotation-card path; an in-flight or failed chip blocks that composer send until it settles, is retried, or is removed.
  • Keep fix: add semantic names to table annotations #256 table-cell semantic names and createSdkJs attachment-cap options. Accepted image types remain single-sourced as ACCEPTED_IMAGE_MIME and are threaded through chrome session JSON and createSdkJs.

Conflict summary

Git reported a conflict only in README.md (Precise targets vs Image attachments). Auto-merge kept both code contracts: #256's serializeModuleHelpers / table-cell helpers / attachment-cap options in createSdkJs, and #248's acceptedImageMime / planClipboardPaste serialization plus the chrome composer controller. README keeps #256's table-cell Precise targets language and #248's composer attachment copy.

Risk Assessment

Low: this is a port of already-reviewed #248 behavior onto current main, with #256 table-cell contracts left intact and existing tests covering both composer paste/drop/picker and annotation-card attachments.

Testing

  • pnpm run check — 969 pass, 5 skipped
  • Composer paste/drop/picker plus annotation attachments: test/chrome-client-queue.test.js, test/artifact-sdk-attachments.test.js, test/attachment-upload.browser.test.js
  • fix: add semantic names to table annotations #256 table-cell contracts: test/table-cell.test.js, test/artifact-sdk-bundle.test.js (served SDK still queues semantic table-cell targets), chrome pill rendering in test/chrome-client-queue.test.js

Co-authored-by: Adelin adelin-b@users.noreply.github.com

Open in Web Open in Cursor 

adelin-b and others added 9 commits August 18, 2026 17:30
…p guards

A deep review pass over the composer attachment feature surfaced a set of
confirmed defects; this commit fixes the correctness tier:

- The composer chip gate no longer blocks the entire send pipeline. A pending
  or failed chip now holds back only the composer's own message and an explicit
  end, while queued annotation prompts still deliver - previously Send and
  Send & End silently delivered nothing with the only signal in an 11px notice.
- The per-batch cap count no longer charges size-refused chips (which retain no
  file and cannot upload), so an oversized companion in a mixed paste can no
  longer silently swallow a valid image while syncNotice clears the cap notice
  on the same render tick.
- Paste no longer raises unsupported-type chips: Office and macOS pastes expose
  stray non-image file flavors beside their text, and a permanent red chip for
  a perceived text paste blocked sending until manually removed. The picker and
  drop still refuse visibly, and a Files drag with nothing enumerable now gets
  an explicit refused chip instead of being swallowed after preventDefault.
- A document-level Files-drag guard stops a drop that misses the composer from
  navigating the chrome away (losing chips, uploads, and the SSE connection).
  Text drags are left to the browser so dropping text into the textarea works.
- planClipboardPaste (and its chrome mirror keepsClipboardText) now treats a
  pasted file's own name or path in text/plain as placeholder rather than a
  caption, restoring the pre-branch behavior for Finder/Explorer file copies.
- The chip list gets the card's W3 max-height/scroll backstop so unbounded
  chips cannot push Send or their own Remove buttons off-frame.
- CHAT_ATTACHMENT_MIME falls back to PNG/JPEG/WebP when the session JSON list
  is unwired, matching acceptedImageTypes instead of refusing every image.

AGENTS.md records the new easy-to-reintroduce composer rules; README notes the
filename-placeholder paste refinement.

Claude-Session: https://claude.ai/code/session_01CTomK8PgtEyDEpSbg6CmvW
Merge the accepted #248 Conversation composer attachment path onto current
main as always-on default behavior. Keep #256 table-cell semantic names and
createSdkJs attachment-cap options. The only textual conflict was README's
Precise targets vs Image attachments bullets.

Co-authored-by: Adelin <adelin-b@users.noreply.github.com>
@kunchenguid

Copy link
Copy Markdown
Owner Author

Speaking as Kun's firstmate: closing this as superseded. @adelin-b resolved the conflicts on #248, so this last-resort port is not needed. Do not merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants