Files
sim/apps
Waleed e802da1075 feat(rich-markdown-editor): round-trip gate, VSCode/style paste, highlight, block reorder (#5539)
* fix(rich-markdown-editor): tighten read-only gate for uppercase entities 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.

* feat(rich-markdown-editor): VSCode code paste and strip <style>/<script> 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.

* feat(rich-markdown-editor): add highlight (==mark==) support

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.

* feat(rich-markdown-editor): keyboard block reordering (Mod-Shift-Arrow)

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).
2026-07-09 13:31:16 -07:00
..