Skip to content

perf(ui): reduce CodeMirror first-edit measurement cost #1401

Description

@backnotprop

Measured problem

Workspaces editor entry on exact main ecc9b368 uses @plannotator/ui 0.32.0, @plannotator/markdown-editor 0.4.0, and @plannotator/atomic-editor 0.8.0.

Built local SPA, deterministic 282,692-byte and 475-byte Markdown fixtures, seven fresh browser contexts at 1x and CDP 4x CPU:

Document CPU Click to editable Click to second paint Style time
475 bytes 1x 392.5 ms 471.1 ms 160.6 ms
475 bytes 4x 777.2 ms 1,126.5 ms 771.2 ms
282,692 bytes 1x 437.1 ms 533.1 ms 191.9 ms
282,692 bytes 4x 1,019.5 ms 1,448.6 ms 984.4 ms

The large read article is unmounted before CodeMirror becomes editable. A host experiment that removed it immediately made heavy 4x second paint 18.6% slower, so it was reverted.

Source attribution

  • packages/ui/components/MarkdownEditor.tsx:93-100 is only the theme/card bridge.
  • @plannotator/markdown-editor@0.4.0/dist/MarkdownEditor.js:18-19 is a thin MarkdownSurface wrapper.
  • @plannotator/atomic-editor/src/AtomicCodeMirrorEditor.tsx:243-321 constructs the full EditorView and all built-in plus consumer extensions in one mount.
  • The decoration-heavy built-ins are at AtomicCodeMirrorEditor.tsx:300-307; consumer extensions append at line 318.

At 4x, the biggest renderer tasks are about 301 ms and 429 ms, followed by a roughly 409 ms CodeMirror animation-frame task. The hot profile paths are ViewState.measure, range getClientRects, selection equality, getBoundingClientRect, and DOM attributes. Markdown parser functions are small by comparison.

The plannotator/atomic-editor repository has Issues disabled, so this package item is tracked here.

Requested bounded experiment

In the package, measure one way to reduce work in initial EditorView construction or its first measure. The leading candidate is to separate essential editing behavior from non-essential first-paint decorations and attach the deferred set after the first editable paint. Do not assume this is a win: a CodeMirror reconfigure may cause another measure.

Acceptance:

  1. Same-machine seven-run before/after at 1x and 4x for both fixture sizes.
  2. Improve heavy 4x click-to-second-paint without regressing the small control.
  3. Preserve byte fidelity, editing commands, consumer extension order, and collaboration.
  4. Preserve the supported @plannotator/ui seam. Workspaces will not fork editor behavior locally.
  5. Normal and reduced-motion visual checks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions