/** * Markdown → the ranges a live-preview surface decorates, as pure * tree-in / descriptors-out. * * This is the half of the CodeMirror editor that decides what a * document *looks* like, and it is deliberately the half with no * `@codemirror/view` import in it. Descriptors are plain * `{ from, to, type }` objects; turning them into `Decoration`s is * `livePreview.ts`'s job. The split buys two things: * * - **These tests run in Node.** `packages/ui`'s Vitest project is * `.ts`-only and has no DOM, on purpose. A test here parses real * markdown with the real GFM parser and asserts real offsets — * no jsdom, no mount, no story. * - **The runtime stays incremental.** Tests hand in * `markdownLanguage.parser.parse(text)`; the view hands in * `syntaxTree(state)`, which CodeMirror has already reparsed * incrementally. Same function, same offsets, and the expensive * path is never the one under test. * * ### What separates this from `markdownSpans.ts` * * The sibling `MarkdownEditor` paints *behind* a `