Skip to content

Markdown editor format pasted text#2017

Merged
simo6529 merged 3 commits intomainfrom
markdown-editor-format-pasted-text
Feb 27, 2026
Merged

Markdown editor format pasted text#2017
simo6529 merged 3 commits intomainfrom
markdown-editor-format-pasted-text

Conversation

@simo6529
Copy link
Copy Markdown
Collaborator

@simo6529 simo6529 commented Feb 27, 2026

Summary by CodeRabbit

  • New Features

    • Enhanced text paste functionality to properly handle multiple clipboard formats and preserve formatting with paragraph breaks and tabs when pasting content.
  • Style

    • Improved paragraph spacing in markdown content display for better visual readability.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 27, 2026

📝 Walkthrough

Walkthrough

This PR enhances paste handling in the Lexical editor's PlainTextPastePlugin by adding support for multiple clipboard MIME types (text/plain and text/uri-list) with newline and tab parsing logic. It includes comprehensive test coverage, adjusts markdown paragraph spacing, and adds React Query mocking for testing.

Changes

Cohort / File(s) Summary
PlainTextPastePlugin Implementation
components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx
Enhanced paste handler with multi-MIME type clipboard support (text/plain, text/uri-list), newline/tab parsing for range selections, added getClipboardText helper, selection guard checks, and new insertRangeSelectionText utility function.
PlainTextPastePlugin Tests
__tests__/components/drops/create/lexical/plugins/PlainTextPastePlugin.test.tsx
New comprehensive test suite covering command registration, clipboard edge cases, file rejection, empty clipboard states, multi-line text handling with blank line preservation, URI fallback behavior, and range vs. non-range selection insertion logic.
Markdown Content Rendering
components/drops/view/part/dropPartMarkdown/content.tsx
Adjusted paragraph classNames to add bottom margin (tw-mb-3) with last-child override (last:tw-mb-0), improving spacing between consecutive paragraphs.
Markdown View Tests
__tests__/components/drops/view/part/DropPartMarkdown.test.tsx
Added React Query useQueryClient mock with setQueryData tracking. Introduces test case for blank-line paragraph rendering (note: test case appears duplicated in two locations within the file).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Syntax-highlighted code blocks  #1500: Directly modifies PlainTextPastePlugin.tsx with overlapping paste-handling logic changes including command registration and clipboard event handling.

Suggested reviewers

  • ragnep
  • prxt6529

Poem

🐰 Paste like a bunny, clean and fast,

Newlines and tabs now hold steadfast,

With MIME types caught and tests so tight,

Our markdown spacing shines so bright! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Markdown editor format pasted text' directly and clearly describes the main change: formatting pasted text in the markdown editor, which aligns with the core objective of handling paste behavior in PlainTextPastePlugin and adjusting paragraph spacing in the markdown rendering.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch markdown-editor-format-pasted-text

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 and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Feb 27, 2026

Quality Gate Passed Quality Gate passed

Issues
0 New issues
1 Accepted issue

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarQube Cloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx (1)

70-86: ⚠️ Potential issue | 🟠 Major

Don’t consume the paste event when no selection exists.

At Line 70, preventDefault() runs before the null-check at Line 74. If selection is missing, this handler still returns true and the paste is lost.

💡 Proposed fix
-        event.preventDefault();
-
-        editor.update(() => {
+        let handled = false;
+        editor.update(() => {
           const selection = $getSelection();
           if (!selection) {
             return;
           }
+          handled = true;
 
           if ($isRangeSelection(selection)) {
             insertRangeSelectionText(selection, text);
             return;
           }
 
           selection.insertRawText(text);
         });
-
-        return true;
+        if (!handled) {
+          return false;
+        }
+        event.preventDefault();
+        return true;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx` around
lines 70 - 86, The paste handler currently calls event.preventDefault() before
checking for a selection, causing the browser paste to be swallowed when
$getSelection() is null; move the preventDefault() call so it only executes when
a valid selection exists and you're about to handle the paste (i.e., after
obtaining selection inside editor.update and confirming selection is truthy),
call preventDefault() then perform the existing $isRangeSelection ->
insertRangeSelectionText or selection.insertRawText logic and return true; if
selection is null do not preventDefault and return false (or allow the handler
to fall through) so the native paste proceeds.
🧹 Nitpick comments (1)
__tests__/components/drops/create/lexical/plugins/PlainTextPastePlugin.test.tsx (1)

85-207: Add a null-selection test case to lock in paste fallback behavior.

Since the plugin now guards !selection, add a test that verifies handler return value and preventDefault behavior when $getSelection() returns null.

✅ Suggested test case
+  it("does not consume paste when selection is missing", () => {
+    renderPlugin();
+    ($getSelection as jest.Mock).mockReturnValue(null);
+
+    const { event, preventDefault } = createClipboardEvent({ text: "hello" });
+    const handled = getCommandHandler()(event);
+
+    expect(handled).toBe(false);
+    expect(preventDefault).not.toHaveBeenCalled();
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@__tests__/components/drops/create/lexical/plugins/PlainTextPastePlugin.test.tsx`
around lines 85 - 207, Add a new test that mocks ($getSelection as
jest.Mock).mockReturnValue(null) and then calls the paste command handler from
getCommandHandler() to verify the plugin returns false and does not call
event.preventDefault; specifically, in the new it block call renderPlugin(),
create a clipboard event via createClipboardEvent({...}) (or empty), pass its
event into getCommandHandler()(event), assert handled === false and that
preventDefault was not called to lock in the !selection guard. Reference
$getSelection and getCommandHandler in the test to locate where to change/add
the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx`:
- Around line 70-86: The paste handler currently calls event.preventDefault()
before checking for a selection, causing the browser paste to be swallowed when
$getSelection() is null; move the preventDefault() call so it only executes when
a valid selection exists and you're about to handle the paste (i.e., after
obtaining selection inside editor.update and confirming selection is truthy),
call preventDefault() then perform the existing $isRangeSelection ->
insertRangeSelectionText or selection.insertRawText logic and return true; if
selection is null do not preventDefault and return false (or allow the handler
to fall through) so the native paste proceeds.

---

Nitpick comments:
In
`@__tests__/components/drops/create/lexical/plugins/PlainTextPastePlugin.test.tsx`:
- Around line 85-207: Add a new test that mocks ($getSelection as
jest.Mock).mockReturnValue(null) and then calls the paste command handler from
getCommandHandler() to verify the plugin returns false and does not call
event.preventDefault; specifically, in the new it block call renderPlugin(),
create a clipboard event via createClipboardEvent({...}) (or empty), pass its
event into getCommandHandler()(event), assert handled === false and that
preventDefault was not called to lock in the !selection guard. Reference
$getSelection and getCommandHandler in the test to locate where to change/add
the test.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c36dc4 and d2acc40.

📒 Files selected for processing (4)
  • __tests__/components/drops/create/lexical/plugins/PlainTextPastePlugin.test.tsx
  • __tests__/components/drops/view/part/DropPartMarkdown.test.tsx
  • components/drops/create/lexical/plugins/PlainTextPastePlugin.tsx
  • components/drops/view/part/dropPartMarkdown/content.tsx

@simo6529 simo6529 merged commit f442271 into main Feb 27, 2026
7 checks passed
@simo6529 simo6529 deleted the markdown-editor-format-pasted-text branch February 27, 2026 19:16
@coderabbitai coderabbitai Bot mentioned this pull request Mar 2, 2026
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.

2 participants