Skip to content

Upgrade Lexical to 0.48 and modernize editor internals#950

Merged
petyosi merged 9 commits into
mainfrom
petyosi/lexical-048-adoption
Jul 19, 2026
Merged

Upgrade Lexical to 0.48 and modernize editor internals#950
petyosi merged 9 commits into
mainfrom
petyosi/lexical-048-adoption

Conversation

@petyosi

@petyosi petyosi commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • upgrade the complete Lexical dependency graph from 0.35 to 0.48 and add lockstep, package-consumer, cross-version Markdown, and three-browser compatibility gates
  • make selection Markdown reuse MDXEditor's configured MDAST visitor pipeline, including nested and consumer-defined nodes
  • replace DOM-authoritative Find/Replace with active-editor Lexical positions, atomic Replace All history, current DOM projections, and normalization-safe offsets
  • construct root, nested, and table editors through Lexical extensions while preserving the public Gurx plugin, visitor, React-child, and imperative-ref contracts
  • give Realm/editor sessions explicit Strict Mode-safe ownership and cleanup, then harden initialization, disposal, early method replay, interrupt cleanup, and their regression coverage

Why

The editor had remained on Lexical 0.35 while upstream changed package exports, node contracts, selection behavior, history plumbing, and introduced the extension framework. This updates the dependency boundary without asking consumers to adopt Lexical extensions or replacing MDXEditor's public Markdown visitor architecture.

The work also integrates two useful capabilities made practical by the upgrade: accurate active-selection Markdown export and state-backed search replacement. @lexical/mdast remains intentionally out of scope because its experimental extension-only model is not a self-contained replacement for MDXEditor's consumer-extensible MDAST pipeline.

Compatibility notes

  • all Lexical packages are asserted at exactly 0.48.0
  • React 18 and React 19 packed consumers retain declarations, styles, runtime behavior, public-ref timing, and custom Realm plugin support
  • Markdown authored on 0.48 is replayed through the published 4.0.4/Lexical 0.35 consumer fixture
  • the legacy React horizontal-rule implementation is retained consistently; migration to HorizontalRuleExtension is tracked as separate follow-up work
  • the public Gurx cells/signals, realmPlugin lifecycle, Markdown visitors/options, composer children, and MDXEditor imperative methods remain source-compatible

Validation

  • npm run lint
  • npm run typecheck
  • npm run test:once — 78 passed, 1 skipped, 1 todo after post-review hardening
  • npm run build
  • full Playwright compatibility matrix — 57/57 across Chromium, Firefox, and WebKit before the final error-path-only hardening
  • latest affected search matrix — 12/12 CX-6 scenarios across Chromium, Firefox, and WebKit
  • extension compatibility — 9/9 CX-7 scenarios across all three engines
  • packed React 18/19 consumers, Lexical lockstep assertion, 0.48-to-0.35 Markdown replay, and API documentation build

The implementation roadmap and verification records are included under plans/.

petyosi added 9 commits July 18, 2026 22:48
Move the complete Lexical graph in lockstep while preserving the package's React, Markdown, and browser contracts. Add real-browser, packed-consumer, and cross-version gates so later feature and architecture changes remain attributable and reversible.
Clip the active editor selection through Lexical's registered node graph, then reuse MDXEditor's existing MDAST visitors and options. This keeps custom constructs and nested editors selection-scoped without mutating full-document state.
Use active-editor Lexical positions as the mutation authority while retaining DOM ranges as public highlight projections. Keep replacement coherent across formatting and editor scopes, with isolated table history and deterministic one-step undo.
Move root, recursive nested, and table editors onto one disposable extension factory while preserving Gurx plugins, visitors, React composer children, and command-based history behavior. Keep Realm setup commit-owned and bridge the initial public ref with ordered, stale-safe method replay.
Capture the changelog assessment, PR #949 analysis, bounded child PRPs, spikes, execution notes, and independently verified evidence. Preserve the decision to keep the public MDAST visitor pipeline authoritative and leave lexical/mdast for a separate initiative.
Make AGENTS.md the canonical tool-neutral guide and retain Claude Code discovery through a relative symlink. Document that native Playwright browsers need elevated launch permission under Codex on macOS so launch-time OS crashes are avoided.
@petyosi
petyosi merged commit 2827d44 into main Jul 19, 2026
4 checks passed
@petyosi
petyosi deleted the petyosi/lexical-048-adoption branch July 19, 2026 13:02
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 4.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@doxman

doxman commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Hi @petyosi , testing the markdown selection as you requested in #949 .

I tested with the "Get Selection Markdown" example, here are my comments.

  • ✔️ The edgecase I pointed out in Raise lexical version and use it to improve text selection #949 (partially selected link) seems to be fixed here!
  • 👌 When my selection includes the <Grid> JSX block, the output puts import { Grid } from './selection-components' at the top
    • This is consistent with how it behaves for whole-editor output (the "Read primary Markdown" button).
    • Seems OK to me
  • 👌 If I paste a list into a table cell, or otherwise force in multiple lines, then select the whole table, then only the first line of each cell is in the output
    • If the selection is within one cell, then it's possible to get output from the other lines
    • I think this is acceptable, table cells are intended to be single-line in markdown
  • ❌ The "Read source selection" and "Read diff selection" buttons always produce empty output
    • No error logs anywhere
    • Looks like selected text in source/diff mode doesn't feed into getSelectionMarkdown()

I also tested within my organization's project, and did not find any additional bugs there. And I don't need source/diff for my use-case, so this is fine by me 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants