Skip to content

[lexical-rich-text] Feature: Add EscapeFormatAtBoundaryExtension for opt-in format escape at text node boundaries#8383

Merged
etrepum merged 18 commits intomainfrom
fix/escape-inline-code-format
May 2, 2026
Merged

[lexical-rich-text] Feature: Add EscapeFormatAtBoundaryExtension for opt-in format escape at text node boundaries#8383
etrepum merged 18 commits intomainfrom
fix/escape-inline-code-format

Conversation

@vishisht31
Copy link
Copy Markdown
Contributor

@vishisht31 vishisht31 commented Apr 22, 2026

Description

  • Adds a new EscapeFormatAtBoundaryExtension in @lexical/extension that clears inline code formatting when the cursor is at the boundary of a code-formatted text node with no adjacent sibling
  • Opt-in extension with configurable triggers ('enter', 'click', 'arrow')
  • Exposes registerEscapeFormatAtBoundary for non-extension usage
  • Removes the hardcoded code-format escape logic from registerRichText to preserve backwards compatibility
  • Adds the extension to the playground with all triggers enabled as a reference implementation

Closes #4936

Test plan

Before

Screen.Recording.2026-04-22.at.11.28.07.PM.mov

After

Screen.Recording.2026-04-22.at.8.57.42.PM.mov

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lexical Ready Ready Preview, Comment May 2, 2026 0:02am
lexical-playground Ready Ready Preview, Comment May 2, 2026 0:02am

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Apr 22, 2026
@vishisht31 vishisht31 marked this pull request as ready for review April 22, 2026 18:19
Copy link
Copy Markdown
Collaborator

@etrepum etrepum left a comment

Choose a reason for hiding this comment

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

I don't think that this makes sense as a default given that it breaks backwards compatibility but it could be an option to RichTextExtension or its own extension

@levensta
Copy link
Copy Markdown
Contributor

I support the idea of ​​adding this option to the extension. Besides, why not do this for any other formats?

A long time ago, I created a workaround like this for my plugin

editor.registerCommand(
  INSERT_PARAGRAPH_COMMAND,
  () => {
    const selection = $getSelection();
    if ($isRangeSelection(selection)) {
      selection.format = 0;
    }
    return false;
  },
  COMMAND_PRIORITY_HIGH
)

@levensta
Copy link
Copy Markdown
Contributor

Also I'm not sure that it need to handle clicking or right/left keys to exit the format. Are there any editors with a similar UX?

@vishisht31
Copy link
Copy Markdown
Contributor Author

vishisht31 commented Apr 23, 2026

Thanks for the feedback! I want to understand the backwards compatibility concern better as this is a bug fix for the case (#4936) where inline code formatting "sticks" when the cursor moves past a code node boundary.
cc- @etrepum @levensta

@etrepum
Copy link
Copy Markdown
Collaborator

etrepum commented Apr 23, 2026

These aren’t code nodes, they’re just text nodes with code formatting. Not really any different than bold or italic. Trying to make it behave differently is not backwards compatible. Offering an extension or configuration of the RichTextExtension to alter this behavior makes sense, but just changing how it works for everyone with no opt-in doesn’t really make sense.

@vishisht31 vishisht31 changed the title [lexical-rich-text] Bug Fix: Escape inline code format at text node b… [lexical-rich-text] Bug Fix: Add EscapeFormatAtBoundaryExtension for opt-in format escape at text node boundaries Apr 23, 2026
…opt-in format escape at text node boundaries
Copy link
Copy Markdown
Collaborator

@etrepum etrepum left a comment

Choose a reason for hiding this comment

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

After looking a bit more closely at this I think the configuration should probably be more flexible since you may want to have different triggers for different keys. For example the current @lexical/rich-text has a $resetCapitalization which is basically this but for ['lowercase', 'uppercase', 'capitalize'] and ['enter', 'space', 'tab']. We could embed this into the existing RichTextExtension since that functionality is already mostly there.

Possibly a way to configure this would be something like:

export interface TriggerConfig {
  [K in EscapeFormatTrigger]:? boolean;
}
export interface EscapeFormatTriggerConfig {
  [K in TextFormatType]?: null | TriggerConfig;
}
escapeFormatTriggers: {
  lowercase: {enter: true, space: true, tab: true},
  uppercase: {enter: true, space: true, tab: true},
  capitalize: {enter: true, space: true, tab: true},
  code: {enter: true},
},

This structure would also be suitable for a straightforward mergeConfig implementation where overrides can turn anything on or off.

Comment thread packages/lexical-extension/src/EscapeFormatAtBoundaryExtension.ts Outdated
@vishisht31
Copy link
Copy Markdown
Contributor Author

vishisht31 commented Apr 25, 2026

Thanks for the feedback! Just want to make sure I'm on the same page before I start working on this:

  1. Restructure the config Instead of having formats and triggers as separate fields, combine them into a single escapeFormatTriggers map where each format gets its own triggers.
  2. Add a mergeConfig So that partial overrides don't blow away the whole config. Like if someone only passes {escapeFormatTriggers: {code: {arrow: true}}}, it should merge into the defaults instead of replacing everything.
  3. Document the defaults in JSDoc.

One thing I'd prefer keeping this as a standalone extension rather than putting it inside RichTextExtension. Feels like it makes more sense as something you opt into. But if you'd rather have it there, I'm open to that too.

Let me know if I'm understanding this right!
CC : @etrepum

@etrepum
Copy link
Copy Markdown
Collaborator

etrepum commented Apr 25, 2026

It would be fine to put it in RichTextExtension because it overlaps with the $resetCapitalization code, the opt-in would simply be configuring it differently.

If you want to keep it separate for now, that’s fine too, but it will probably end up in RichTextExtension eventually one way or another.

@vishisht31 vishisht31 self-assigned this Apr 26, 2026
Comment thread packages/lexical-rich-text/src/index.ts Outdated
@etrepum etrepum added this pull request to the merge queue May 2, 2026
Merged via the queue into main with commit 251a703 May 2, 2026
41 checks passed
etrepum added a commit to mayrang/lexical that referenced this pull request May 2, 2026
…opt-in format escape at text node boundaries (facebook#8383)

Co-authored-by: Sergey Gorbachev <grbchv.s@gmail.com>
Co-authored-by: Sherry <potatowagon@meta.com>
Co-authored-by: Bob Ippolito <bob@redivi.com>
Co-authored-by: Agyei Holy <agyeiholy978@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. extended-tests Run extended e2e tests on a PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Cannot exit code block without use of toolbar

5 participants