Skip to content

fix(rich-markdown-editor): strict, provenance-aware markdown paste#5463

Merged
waleedlatif1 merged 3 commits into
stagingfrom
fix/md-paste-strict
Jul 7, 2026
Merged

fix(rich-markdown-editor): strict, provenance-aware markdown paste#5463
waleedlatif1 merged 3 commits into
stagingfrom
fix/md-paste-strict

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

Pasting code-like text such as 5 * width * height was being italicized ("width" became emphasis) by StarterKit's lenient mark paste rules. This makes markdown paste strict and correct — matching Obsidian/Linear.

  • Disable StarterKit's mark paste rules (enablePasteRules: false on both editor variants). Emphasis is owned by MarkdownPaste (strict CommonMark via marked) on the plain-text path and by real HTML tags on the DOM path. Input rules (typing *x* → italic) are unaffected — they're a separate mechanism.
  • Provenance-aware paste gate. Structural markdown (headings, lists, tables, blockquotes, fenced code, links, **bold**) always parses from the plain-text sibling — our parser is more faithful for GFM alignment/escaping. Inline-only marks (`*_ ~~ ``) parse for a plain-text-only paste, but defer to a rich HTML sibling (its presence signals a rich source that may carry structure the plain text can't — so a copied table isn't flattened).

Behavior (verified)

  • Renders: *italic*, _italic_, **bold**, __bold__, ~~strike~~, `code`, and all block constructs.
  • Stays literal (strict CommonMark flanking): 5 * width * height, def foo(*args, **kwargs), snake_case, file_path_here, URLs with underscores.
  • A copied table whose plain-text sibling contains an inline mark is preserved (not flattened).
  • Typing, read-only, and code-block paste all behave correctly.

Type of Change

  • Bug fix

Testing

Committed unit tests cover the full gate matrix (constructs render, inline-only defers to HTML, code-like text isn't emphasized, structural parses with an HTML sibling, read-only/code-block/empty). Behavior additionally verified end-to-end in a real-browser paste harness (25 cases + typing/input-rule audit).

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Pasting code-like text such as `5 * width * height` was italicized by StarterKit's lenient mark
paste rules. Own emphasis with the strict CommonMark parser instead:

- Disable StarterKit's mark paste rules (`enablePasteRules: false`); markdown paste is handled by
  MarkdownPaste (marked) and rich paste by real HTML tags. Input rules (typing) are unaffected.
- Split the paste gate by provenance: structural markdown always parses (faithful GFM tables and
  escaping), while inline-only marks parse for a plain-text paste but defer to a rich HTML sibling so
  a copied table isn't flattened.

Emphasis (`*_ ** __ ~~ ``) renders; `5 * width`, `*args`, and `snake_case` stay literal — matching
Obsidian/Linear.
@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 7, 2026 2:49am

Request Review

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
User-facing paste behavior changes across both editor surfaces, but scope is limited to the rich markdown editor and is covered by an expanded unit test matrix.

Overview
Fixes pasted code-like text (e.g. 5 * width * height) being wrongly emphasized by turning off StarterKit mark paste rules (enablePasteRules: false on the file editor and modal field). Inline formatting on paste now goes through strict CommonMark parsing or real HTML, not StarterKit’s lenient rules; typing input rules are unchanged.

MarkdownPaste now splits clipboard detection into structural hints (headings, lists, tables, links, **bold**, etc.)—always parsed from text/plain, even when text/html is present—and inline-only hints (*italic*, ~~strike~~, `code`), which parse only for plain-text-only pastes and defer to the HTML path when a rich sibling exists so tables and other structure aren’t flattened. Pastes inside inline code are skipped like code blocks.

Tests cover the full gate matrix: inline marks render on plain paste, defer with HTML, code-like strings stay literal under strict flanking, and inline-code paste stays literal.

Reviewed by Cursor Bugbot for commit aefb444. Configure here.

@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces StarterKit's lenient mark paste rules with a provenance-aware two-tier markdown paste gate, fixing the bug where code-like expressions such as 5 * width * height were incorrectly italicized on paste.

  • STRUCTURAL_MARKDOWN_HINTS (headings, **bold**, links, lists, blockquotes, fenced code, GFM tables) always force the strict marked-based parser, even when an HTML sibling is present — preserving GFM table alignment and custom escaping.
  • INLINE_MARK_HINTS (*italic*, _italic_, ~~strike~~, `code`) parse only when no HTML sibling exists; if an HTML sibling is present the DOM path is used so rich structure is not flattened.
  • enablePasteRules: false is added to both editor variants to disable StarterKit's lenient mark paste rules; a new guard on editor.isActive('code') prevents the handler from intercepting pastes inside inline-code spans.

Confidence Score: 5/5

The change is safe to merge — it narrows a paste interception bug with a well-structured two-tier gate backed by a comprehensive test suite covering every branch of the new logic.

The two-tier gate (structural vs. inline-only), the HTML-sibling deferral, and the inline-code guard all correctly handle the edge cases described. The marked strict-CommonMark parser preserves non-markdown text byte-for-byte, so the over-claiming in INLINE_MARK_HINTS is provably safe. Tests cover the full gate matrix including the new paths. No data-loss or auth risk is introduced.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-paste.ts Core logic rewritten: splits hints into STRUCTURAL (always parse) and INLINE (defer to HTML sibling if present), adds inline-code guard. Logic is sound and well-commented.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/markdown-paste.test.ts Test suite expanded with the full gate matrix: inline-code guard, deferral to HTML sibling, byte-for-byte literal pass-through for space-flanked asterisks/kwargs/snake_case, and underscore bold/italic variants.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.tsx Single-line change: adds enablePasteRules: false to the useEditor options to disable StarterKit's lenient mark paste rules.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-field.tsx Single-line change: adds enablePasteRules: false to the useEditor options, mirroring the editor variant for consistency.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Paste event] --> B{editor.isEditable?}
    B -- No --> Z[return false\ndefault handler]
    B -- Yes --> C{isActive codeBlock\nor inline code?}
    C -- Yes --> Z
    C -- No --> D{text/plain present?}
    D -- No --> Z
    D -- Yes --> E{STRUCTURAL_MARKDOWN_HINTS\nmatch?}
    E -- Yes --> H[parseMarkdownToDoc\nmarked / strict CommonMark]
    E -- No --> F{INLINE_MARK_HINTS\nmatch?}
    F -- No --> Z
    F -- Yes --> G{text/html sibling\npresent?}
    G -- Yes --> Z2[return false\nDOM path handles HTML sibling]
    G -- No --> H
    H --> I{doc has content?}
    I -- No --> Z
    I -- Yes --> J[insertContent\nreturn true]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Paste event] --> B{editor.isEditable?}
    B -- No --> Z[return false\ndefault handler]
    B -- Yes --> C{isActive codeBlock\nor inline code?}
    C -- Yes --> Z
    C -- No --> D{text/plain present?}
    D -- No --> Z
    D -- Yes --> E{STRUCTURAL_MARKDOWN_HINTS\nmatch?}
    E -- Yes --> H[parseMarkdownToDoc\nmarked / strict CommonMark]
    E -- No --> F{INLINE_MARK_HINTS\nmatch?}
    F -- No --> Z
    F -- Yes --> G{text/html sibling\npresent?}
    G -- Yes --> Z2[return false\nDOM path handles HTML sibling]
    G -- No --> H
    H --> I{doc has content?}
    I -- No --> Z
    I -- Yes --> J[insertContent\nreturn true]
Loading

Reviews (3): Last reviewed commit: "fix(rich-markdown-editor): keep paste li..." | Re-trigger Greptile

…reserved byte-for-byte

The gate is intentionally lenient — a precise CommonMark-emphasis matcher would risk missing real
emphasis. Over-claiming is safe because the strict parser (marked) preserves non-markdown exactly;
assert the handler claims the paste and returns the input unchanged, not just that no mark is added.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

The paste handler skipped a fenced code block but not the inline code mark. Now that inline marks
gate the parse, pasting `*italic*` inside inline code would render rich instead of staying literal —
extend the code-context guard to editor.isActive('code').
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit aefb444. Configure here.

@waleedlatif1 waleedlatif1 merged commit b3175ae into staging Jul 7, 2026
18 checks passed
@waleedlatif1 waleedlatif1 deleted the fix/md-paste-strict branch July 7, 2026 03:06
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.

1 participant