Skip to content

Preserve composer file chips on paste#3986

Open
jakeleventhal wants to merge 3 commits into
pingdotgg:mainfrom
jakeleventhal:t3code/reuse-file-chip-on-repaste
Open

Preserve composer file chips on paste#3986
jakeleventhal wants to merge 3 commits into
pingdotgg:mainfrom
jakeleventhal:t3code/reuse-file-chip-on-repaste

Conversation

@jakeleventhal

@jakeleventhal jakeleventhal commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

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 ComposerMentionNode JSON serialization is reused, so file chip paths and the normal plain-text clipboard formats stay in sync.

Validation

  • pnpm exec vp check
  • pnpm exec vp run typecheck

Note

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 ComposerClipboardPlugin writes Lexical’s application/x-lexical-editor payload on copy/cut (alongside existing plain-text formats) and, on paste, restores nodes through $insertDataTransferForRichText when the payload passes validation. Validation lives in ComposerPromptEditor.clipboard: it must match the composer namespace and must not include composer-terminal-context nodes, so terminal context chips are not resurrected from clipboard JSON. The editor namespace is centralized as COMPOSER_EDITOR_NAMESPACE, and @lexical/clipboard is 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

  • Adds a ComposerClipboardPlugin that intercepts copy, cut, and paste commands in the composer editor, writing a Lexical-specific payload under the application/x-lexical-editor MIME type on copy/cut.
  • On paste, validates the clipboard payload originated from the same composer namespace (t3tools-composer-editor) before inserting rich text; falls back to default paste handling otherwise.
  • Excludes composer-terminal-context nodes from valid paste payloads to prevent terminal context chips from being duplicated via paste.
  • Behavioral Change: pasting composer content now uses rich text insertion only when the payload passes namespace and node-type validation; all other paste events use browser defaults.

Macroscope summarized bed06ee.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7210ce13-a295-4bce-8d61-8afcee4cc9a1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Jul 15, 2026
@jakeleventhal jakeleventhal marked this pull request as ready for review July 15, 2026 04:32
@macroscopeapp

macroscopeapp Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread apps/web/src/components/ComposerPromptEditor.tsx Outdated
Comment thread apps/web/src/components/ComposerPromptEditor.tsx
@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Jul 15, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ 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)
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit bed06ee. Configure here.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

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

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant