Uh oh!
There was an error while loading. Please reload this page.
feat(rich-markdown-editor): round-trip gate, VSCode/style paste, highlight, block reorder - #5539
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Paste now turns VSCode copies with The read-only round-trip gate ( Tests cover block moves, highlight parse/serialize, paste paths, mark stacking idempotency, and the new gate cases. Reviewed by Cursor Bugbot for commit f2b07b1. Configure here. |
Greptile SummaryThis PR expands the rich markdown editor with safer round-tripping and new editing tools. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (5): Last reviewed commit: "feat(rich-markdown-editor): keyboard blo..." | Re-trigger Greptile |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
c8c4580 to
deff589Comparedeff589 to
4241b65Comparewaleedlatif1
commented
Jul 9, 2026
@greptile review |
waleedlatif1
commented
Jul 9, 2026
@cursor review |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
4241b65 to
17530cdComparewaleedlatif1
commented
Jul 9, 2026
@greptile review |
waleedlatif1
commented
Jul 9, 2026
@cursor review |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
17530cd to
eff75c3Comparewaleedlatif1
commented
Jul 9, 2026
@greptile review |
waleedlatif1
commented
Jul 9, 2026
@cursor review |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…ies and orphan ref-defs Two silent-corruption cases the idempotency probe can't see: - The HTML-entity safe-list used a case-insensitive regex, so `&`/`<`/`>` were treated as the round-trippable canonical entities and let through as editable, but the serializer only round-trips the lowercase forms and mangles the uppercase ones. Make the safe-list case-sensitive. - An unused link/image reference definition (`[x]: url` with no `[x]` reference) is dropped entirely on serialize, a deletion the idempotency probe misses. Detect orphan definitions and open read-only; used definitions still inline losslessly and stay editable. The use check tolerates bracket-internal padding (`[ x ]`), and GFM footnote definitions (`[^id]: …`) are excluded since they round-trip verbatim.
…pt> from pasted HTML - Code copied from VSCode carries a `vscode-editor-data` payload with the source language, but its text/html is per-token colored spans that ProseMirror flattens into plain paragraphs. Read the payload and paste a real fenced code block with the mapped language. markdown/plaintext modes resolve to no language and fall through, so markdown copied from VSCode still parses as rich content rather than a fenced block. - Google Sheets and Word prepend a `<style>` block of CSS that PM's DOM parser walks into the document as literal text. Strip <style>/<script> in transformPastedHTML before parsing. - Add a `==…==` inline-mark hint (allowing a lone interior `=`) so a plain-text paste of `==highlight==` routes through the markdown parser and becomes a highlight mark.
Adds a highlight mark rendered as <mark> and serialized to/from ==text== (Pandoc/Obsidian syntax). A custom inline tokenizer parses ==text== (inner text parsed as inline markdown so nested marks like ==**bold**== survive; the body allows a lone `=` so ==a=b== round-trips). `==` cannot be encoded in the delimiter, so an appendTransaction guard strips the mark from any text that ends up containing `==` (e.g. a toolbar highlight over a==b), keeping the text and never emitting the corrupting ==a==b==. Comparison operators (x == y) stay literal. Wired with an input rule, paste rule, Mod-Shift-H, a bubble-menu button, and themed <mark> styling. Also locks in mark-stacking round-trips across contexts.
Mod-Shift-ArrowUp/ArrowDown swaps the current top-level block with its neighbour, carrying the caret at its original offset (newBefore + offset, no off-by-one), and no-ops at the document edges. Exposed as moveBlockUp/moveBlockDown commands (the keyboard shortcuts call them). Pure UI interaction, no schema change. Covered by tests (order, caret offset, edge no-op, list stays intact).
eff75c3 to
f2b07b1Comparewaleedlatif1
commented
Jul 9, 2026
@greptile review |
waleedlatif1
commented
Jul 9, 2026
@cursor review |
There was a problem hiding this comment.
✅ 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 f2b07b1. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
Summary
A correctness + capability pass on the rich-markdown (
.md) editor. Everything here is empirically verified in a real-browser ProseMirror harness and covered by CI tests.Correctness — read-only gate (
round-trip-safety.ts)Two silent-corruption cases the idempotency probe can't see:
&/</>) slipped past a case-insensitive safe-list but aren't round-tripped by the serializer → now case-sensitive, opens read-only.[x]: urlwith no[x]use) were dropped entirely on first edit → detected and opened read-only; used definitions still inline losslessly.Paste (
markdown-paste.ts)vscode-editor-datapayload and pastes a real fenced code block with the source language, instead of flattening the colored-span HTML into plain paragraphs.<style>/<script>strip —transformPastedHTMLremoves them so pasting from Google Sheets/Word doesn't leak raw CSS into the doc.Capabilities
==mark==— a<mark>mark serialized to/from==text==(custom marked tokenizer so nested marks like==**bold**==survive; comparison operatorsx == ystay literal). Input rule, paste rule,⌘⇧H, bubble-menu button, themed styling.⌘⇧↑/⌘⇧↓moves the current top-level block, carrying the caret.Validation
Deliberately not done
~x~—~x~and~~x~~both render<del>x</del>per the GFM spec (one-or-two tildes = strikethrough), so our single→double normalization is lossless, not a meaning change. Left as-is.+/grip drag-handle — the keyboard reorder above is the robust core; the hover UI needs a drag-handle dep + visual iteration, best done with live visual QA. Recommended as a focused follow-up.Testing
Real-browser harness (60+ cases) + committed Vitest in CI: round-trip idempotency, mark stacking, highlight parse/serialize/nesting/safety, block reorder, entity/ref-def gate. No regressions.