Skip to content

feat(content): add progressive Markdown to Lexical migration - #2777

Merged
Innei merged 1 commit into
masterfrom
agent/admin-markdown-lexical-migration
Jul 29, 2026
Merged

feat(content): add progressive Markdown to Lexical migration#2777
Innei merged 1 commit into
masterfrom
agent/admin-markdown-lexical-migration

Conversation

@Innei

@Innei Innei commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary

  • add a versioned, document-level Markdown analyzer and deterministic Lexical converter
  • expose an interactive Admin migration flow with source-located blocking diagnostics and draft-only staging
  • transition posts, notes, pages, and their valid AI translations to Lexical atomically under a shared format lock
  • raise shared modal headers when a subtitle is present and align the close action with the title row

Why

Existing non-empty Markdown documents could not switch formats. This change permits progressive migration when every syntax region in the active migration unit has a behavior-preserving mapping, while keeping unsupported documents in Markdown with actionable diagnostics.

Impact

Supported documents can be staged as Lexical without publishing immediately. Publishing the staged document commits the source and existing AI translations as one guarded transition; unsupported syntax remains unchanged.

Validation

  • pnpm -C packages/editor exec vitest run test/markdown-conversion.test.ts — 8 tests passed
  • pnpm -C apps/core exec vitest run ...content-migration... note.service.spec.ts post.service.spec.ts — 25 tests passed
  • pnpm -C apps/core exec vitest run test/src/modules/ai/ai-translation.repository.pg.e2e.spec.ts — 3 tests passed
  • Admin, Core, and Editor TypeScript checks passed
  • ESLint and staged diff checks passed
  • Browser verification measured a 65 px subtitle header and a 0 px title/close center-line delta


// Inline code is ignored by custom-token and unsupported-construct scans.
const inlineCodePattern = /(`+)([^\n]*?)\1/g
for (const match of markdown.matchAll(inlineCodePattern)) {
pattern: RegExp,
callback: (match: RegExpMatchArray, span: SourceSpan) => void,
) => {
for (const match of markdown.matchAll(pattern)) {
Comment on lines +992 to +994
/\*\*[^*]+\*\*|__[^_]+__|~~[^~]+~~|==[^=]+==|\+\+[^+]+\+\+|`[^`]+`|\[[^\]]+\]\([^)]+\)|\{(?:GH|TW|TG)@|\$[^$]+\$/.test(
value,
)
@Innei
Innei marked this pull request as ready for review July 29, 2026 16:43
@safedep

safedep Bot commented Jul 29, 2026

Copy link
Copy Markdown

SafeDep Report Summary

Green Malicious Packages Badge Green Vulnerable Packages Badge Green Risky License Badge

Package Details
Package Malware Vulnerability Risky License Report
icon @lexical/markdown @ 0.48.0
pnpm-lock.yaml packages/editor/package.json
ok icon
ok icon
ok icon
🔗
icon @noble/hashes @ 2.2.0
pnpm-lock.yaml packages/editor/package.json
ok icon
ok icon
ok icon
🔗

View complete scan results →

This report is generated by SafeDep Github App

@Innei
Innei merged commit 7e6dd62 into master Jul 29, 2026
11 of 12 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f4b57835b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +269 to +270
await acquireContentFormatTransitionLock(tx, input)
const persisted = await this.lockSource(tx, input)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Coordinate ordinary source writes with the transition lock

When a normal Markdown save has already read the source before this transaction starts, it does not acquire this advisory lock or condition its repository update on the current format. If the migration commits first, the waiting normal PostRepository.update, NoteRepository.update, or PageRepository.update can subsequently write contentFormat: markdown over the Lexical source while the translations remain Lexical, breaking the atomic format invariant. All source writers need to participate in this lock or use a conditional update/revalidation after obtaining the row lock.

Useful? React with 👍 / 👎.


scanPattern(
// eslint-disable-next-line unicorn/better-regex -- Escaped delimiters avoid non-Unicode parsing ambiguities.
/(?:\[([^\]\n]*)\])?\{(GH|TW|TG)@(\w+)\}[ \t]?/g,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor Markdown escapes before replacing mention tokens

For Markdown containing an escaped literal such as \{GH@alice}, this pattern still matches the text beginning at { and replaces it with a mention node, leaving only the preceding backslash as text. The dry run therefore reports the document as convertible even though publishing changes a deliberately escaped literal into a rich mention; the custom-token scans should skip delimiters preceded by an active Markdown escape.

Useful? React with 👍 / 👎.

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