feat(muya): add cursorCoords + active formats to selection-change by Jocs · Pull Request #4387 · marktext/marktext · GitHub
Skip to content

feat(muya): add cursorCoords + active formats to selection-change - #4387

Merged
Jocs merged 2 commits into
developfrom
feat/muya-selection-formats
Jun 8, 2026
Merged

Jocs merged 2 commits into
developfrom
feat/muya-selection-formats

Conversation

@Jocs

@Jocs Jocs commented Jun 8, 2026

Copy link
Copy Markdown
Member

Background

Part of the muyajs → @muyajs/core engine migration. The desktop editor
relies on two extras the legacy engine put on its selectionChange event
payload, which @muyajs/core's selection-change did not carry:

  1. cursorCoords — the caret's screen rect. The desktop typewriter mode
    scrolls using changes.cursorCoords.y.
  2. formats — the active inline formats at the cursor, used to light up
    the desktop format toolbar (legacy emitted these via a selectionFormats
    event).

What this adds

In Selection.setSelection, the emitted selection-change payload now also
includes:

  • cursorCoords: Selection.getCursorCoords() (a DOMRect | null).
  • formats: the active inline-format tokens at the cursor when the selection
    is within a single formattable block, else [].

formats is computed by duck-typing the anchor block's getFormatsInRange
(a value import of Format into selection/index.ts would create a
selection → format circular dependency — verified via check-circular).

heading-copy-link and format-click/preview-image (other legacy
interaction events) are tracked separately and will land in a follow-up.

Tests

New happy-dom spec src/selection/__tests__/selectionChange.spec.ts asserting
the payload carries cursorCoords and a formats array.

Verification

  • pnpm -C packages/muya lint → 0 errors
  • pnpm -C packages/muya lint:types ✓
  • pnpm -C packages/muya check-circular ✓ (no selection → format cycle)
  • pnpm -C packages/muya test → 404 passed
  • pnpm -C packages/muya test:spec → 1347 passed (conformance unchanged)

🤖 Generated with Claude Code

The desktop editor relies on two extras the legacy muyajs put on its
selectionChange payload: cursorCoords (the caret's screen rect, used for
typewriter-mode scrolling via changes.cursorCoords.y) and the active inline
formats at the cursor (to light up the format toolbar). Add both to the
selection-change event emitted by Selection.setSelection. Formats are
duck-typed off the anchor block's getFormatsInRange to avoid a
selection -> format circular import. Adds happy-dom coverage.

Part of the muyajs -> @muyajs/core engine migration (stage A8).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 8, 2026 04:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends Muya’s selection-change event payload to better match MarkText’s legacy selectionChange surface during the muyajs → @muyajs/core migration, enabling desktop features that depend on cursor coordinates and active inline formats.

Changes:

  • Emit cursorCoords (from Selection.getCursorCoords()) on selection-change.
  • Emit formats (from the active block’s getFormatsInRange().formats when selection is within a single formattable block; otherwise []).
  • Add a happy-dom Vitest spec to assert the new payload keys are present.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
packages/muya/src/selection/index.ts Adds cursorCoords + formats to the selection-change payload.
packages/muya/src/selection/tests/selectionChange.spec.ts Adds a regression test for the expanded selection-change payload.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +308 to +312
// Backport of marktext's `selectionChange` payload extras the desktop
// relies on: `cursorCoords` for typewriter-mode scrolling and the
// active inline formats at the cursor for lighting up the toolbar.
const cursorCoords = Selection.getCursorCoords();
// Duck-type the Format block — a value import of Format here would

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bf9b578. getCursorCoords now takes a preferEnd flag (defaulted false, so existing callers like getCursorYOffset are unchanged), and setSelection passes direction === 'forward'. For a forward range selection it now returns the last client rect (the caret/focus end) so typewriter scrolling tracks the cursor instead of the selection start; collapsed and backward selections keep using the first rect.

Comment on lines +42 to +66

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — strengthened in bf9b578. Added a case that places the selection inside **bold** and asserts formats contains a strong token, so the test now fails if active-format detection regresses to always-empty.

- cursorCoords now follows the caret/focus end for forward range selections
  (getCursorCoords gains a `preferEnd` param, defaulted false so existing
  callers are unchanged; setSelection passes `direction === 'forward'`).
  getClientRects returns the first rect by default, which is the selection
  start — typewriter scrolling should track the caret, not the start.
- Strengthen the spec with a non-empty case: cursor inside `**bold**` now
  asserts a `strong` token is emitted in `formats`, not just that it is an
  array.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Jocs
Jocs merged commit 3a3c092 into develop Jun 8, 2026
11 checks passed
@Jocs
Jocs deleted the feat/muya-selection-formats branch June 8, 2026 04:54
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.

2 participants