Skip to content

feat(editor): CodeMirror live-cursor description editor (ADR 0011, 2/2)#74

Merged
Musiker15 merged 1 commit into
mainfrom
feat/live-cursors-editor
May 28, 2026
Merged

feat(editor): CodeMirror live-cursor description editor (ADR 0011, 2/2)#74
Musiker15 merged 1 commit into
mainfrom
feat/live-cursors-editor

Conversation

@Musiker15
Copy link
Copy Markdown
Member

Summary

Part 2 of 2 — completes live cursors (ADR 0011). With NEXT_PUBLIC_FEATURE_LIVE_CURSORS="true", the card description field becomes a CRDT-aware CodeMirror 6 editor rendering remote collaborators' carets + selections, consuming the encrypted cursor channel from #73.

⚠️ Crypto-relevant (it instantiates CardCursorProvider, which encrypts cursor awareness under the BoardKey). The transport itself was reviewed in #73.

How it fits the existing model

The editor binds CodeMirror to the same card Y.Text the drawer already mirrors into its description state. So:

  • typing → Y.Text mutates → the drawer's existing observer updates descriptionSave / PATCH / export / encryption are all unchanged;
  • yCollab(yText, cursorAwareness) renders remote carets in each peer's presence colour.

Markdown-source model preserved — no rich-text migration, exactly as the ADR decided.

Changes

  • description-editor.tsx (new): CodeMirror 6 + yCollab + markdown() + line-wrapping + an MSK dark theme + a11y content attributes (role=textbox, aria-multiline, aria-label). Creates a CardCursorProvider for the card and destroys it on unmount (the shared Y.Doc / doc-sync provider are left alone — they're cached).
  • card-drawer.tsx: renders <DescriptionEditor> when the flag is on, else the existing textarea. Dynamically imported (ssr:false) so the CodeMirror bundle never lands in the initial page load.
  • Deps: @codemirror/{state,view,commands,lang-markdown} + y-codemirror.next.

Validation

  • pnpm build ✅ (CodeMirror + y-codemirror.next bundle; dynamic import resolves)
  • pnpm typecheck + pnpm lint clean; full unit suite 171 green
  • The unit-testable logic (cursor awareness PII guard) was covered in feat(realtime): live-cursor transport (ADR 0011, part 1/2) #73. This PR is editor wiring, validated by the production build + the manual plan below — a CodeMirror+Yjs render test would be integration-level (jsdom + heavy mocks) for thin value.

Test plan

  • CI green
  • Merge → redeploy mskanban-ws.service (for the part-1 awareness relay) + set NEXT_PUBLIC_FEATURE_LIVE_CURSORS="true" + rebuild
  • Two browsers, two users, same card open: typing in one shows the other's coloured caret + live text; selections render; no PII in the awareness frames (DevTools → WS shows only ciphertext)
  • Flag off (default): the plain textarea renders, doc sync unaffected
  • a11y: editor reachable + editable by keyboard; remote carets are decorative

🤖 Generated with Claude Code

Completes live cursors. With NEXT_PUBLIC_FEATURE_LIVE_CURSORS="true"
the description field becomes a CRDT-aware CodeMirror 6 editor bound to
the same card Y.Text, rendering remote collaborators' carets +
selections via y-codemirror.next over the encrypted cursor channel
from part 1.

- description-editor.tsx: CodeMirror 6 + yCollab(yText, cursorAwareness)
  + markdown() + line wrapping + an MSK dark theme + a11y content
  attributes (role=textbox, aria-multiline, aria-label). Spins up a
  CardCursorProvider for the card and tears it down on unmount.
- card-drawer.tsx: renders <DescriptionEditor> when the flag is on,
  else the existing textarea. Dynamically imported (ssr:false) so the
  CodeMirror bundle never lands in the initial page load.
- Markdown-source model unchanged: the editor writes the same Y.Text
  the drawer mirrors into `description`, so Save/PATCH/export/encryption
  are all untouched.
- Deps: @codemirror/{state,view,commands,lang-markdown} +
  y-codemirror.next.

Default off; falls back to the textarea. Requires a WS relay redeploy
from the part-1 version. Validated with `pnpm build` (bundles + dynamic
import OK); the testable transport logic was unit-tested in part 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Signed-off-by: Moritz Kohm <moritz.kohm@gmail.com>
Signed-off-by: Musiker15 <info@musiker15.de>
@Musiker15 Musiker15 merged commit 37bb838 into main May 28, 2026
8 checks passed
@Musiker15 Musiker15 deleted the feat/live-cursors-editor branch May 28, 2026 16:53
@Musiker15 Musiker15 mentioned this pull request May 28, 2026
2 tasks
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