Preserve composer file chips on paste#3986
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ApprovabilityVerdict: Needs human review This PR introduces new clipboard handling behavior that modifies how copy/paste works in the composer editor. An unresolved review comment identifies a potential bug where rejecting terminal-context nodes could cause state desync. Human review recommended to assess the correctness of the approach. You can customize Macroscope's approvability policy. Learn more. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fa1c12d6ac
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit bed06ee. Configure here.
| "nodes" in parsed && | ||
| Array.isArray(parsed.nodes) && | ||
| !containsTerminalContextNode(parsed.nodes) | ||
| ); |
There was a problem hiding this comment.
Terminal check blocks rich paste
Medium Severity
Copy/cut still writes full Lexical JSON (including composer-terminal-context nodes), but isComposerLexicalClipboardPayload rejects the entire payload when any terminal node appears. Rich paste is skipped while text/plain still contains INLINE_TERMINAL_CONTEXT_PLACEHOLDER characters, so pasted content can desync terminalContextIds from draft contexts and break file chip restoration in mixed selections.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit bed06ee. Configure here.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bed06ee04a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return false; | ||
| } | ||
| event.preventDefault(); | ||
| $insertDataTransferForRichText(clipboardData, selection, editor); |
There was a problem hiding this comment.
Flatten composer blocks before rich paste
When the copied range is the whole composer (for example Cmd/Ctrl+A), the Lexical payload contains the top-level paragraph node, not just inline chip/text nodes. Handling that payload here with the rich-text inserter lets Lexical insert a block paragraph into this otherwise single-paragraph/plain-text composer; pasting it at a caret in an existing prompt splits the editor into multiple paragraphs, so the next getTextContent() includes paragraph separators and the prompt gains extra blank lines around the pasted content. Flatten composer paragraph nodes to their inline children, or normalize through the composer prompt serializer before accepting this rich paste.
Useful? React with 👍 / 👎.
| return false; | ||
| } | ||
| event.preventDefault(); | ||
| $insertDataTransferForRichText(clipboardData, selection, editor); |
There was a problem hiding this comment.
Preserve paste undo boundaries
When this rich clipboard path handles a paste, it returns before the PlainTextPlugin paste handler can run its editor.update(..., { tag: PASTE_TAG }) path. In the common case where a user types and then immediately pastes a file chip, the untagged insertion can merge with the preceding typing in history, so one Undo removes both the paste and earlier text instead of just the paste. Run the insertion in an update tagged as a paste, matching the plain-text path.
Useful? React with 👍 / 👎.


What changed
Preserve composer file chips when a prompt is copied or cut and then pasted back into the composer.
Why
The composer uses Lexical decorator nodes for file chips, but the plain-text clipboard path only restored their serialized text. The pasted prompt therefore kept the file reference while losing the chip presentation.
This adds Lexical's native structured clipboard payload to copy/cut and restores it through Lexical's rich-text paste path. The existing
ComposerMentionNodeJSON serialization is reused, so file chip paths and the normal plain-text clipboard formats stay in sync.Validation
pnpm exec vp checkpnpm exec vp run typecheckNote
Low Risk
Scoped to composer clipboard UX with explicit rejection of terminal-context nodes; no auth, API, or data-persistence changes.
Overview
Copy/cut/paste in the composer prompt editor now keeps file chip decorator nodes instead of only plain serialized file-link text.
A new
ComposerClipboardPluginwrites Lexical’sapplication/x-lexical-editorpayload on copy/cut (alongside existing plain-text formats) and, on paste, restores nodes through$insertDataTransferForRichTextwhen the payload passes validation. Validation lives inComposerPromptEditor.clipboard: it must match the composer namespace and must not includecomposer-terminal-contextnodes, so terminal context chips are not resurrected from clipboard JSON. The editor namespace is centralized asCOMPOSER_EDITOR_NAMESPACE, and@lexical/clipboardis added as a dependency with unit tests for the payload guard.Reviewed by Cursor Bugbot for commit bed06ee. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Preserve composer editor chips when copying and pasting content
ComposerClipboardPluginthat intercepts copy, cut, and paste commands in the composer editor, writing a Lexical-specific payload under theapplication/x-lexical-editorMIME type on copy/cut.t3tools-composer-editor) before inserting rich text; falls back to default paste handling otherwise.composer-terminal-contextnodes from valid paste payloads to prevent terminal context chips from being duplicated via paste.Macroscope summarized bed06ee.