Skip to content

feat(lab): add link support to RichTextEditor - #4670

Merged
potatowagon merged 1 commit into
facebook:mainfrom
potatowagon:navi/feat/richtext-links
Aug 4, 2026
Merged

feat(lab): add link support to RichTextEditor#4670
potatowagon merged 1 commit into
facebook:mainfrom
potatowagon:navi/feat/richtext-links

Conversation

@potatowagon

@potatowagon potatowagon commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

What

Adds link support to the experimental RichTextEditor (@astryxdesign/lab). Previously LinkNode/AutoLinkNode + LinkPlugin were registered, so links rendered — but there was no way to create one. This adds creation, auto-linking, and new-tab behavior.

Why this shape — EPS eps-lexical compatibility

The end goal is to back Meta's EPS EPSRichTextArea (nest/libs/eps-lexical) with this editor. So the design deliberately matches EPS's command + node contract, not its UI chrome:

  • Link toggle bottoms out on Lexical's TOGGLE_LINK_COMMAND (same as eps-lexical's LinkPopoverPlugin.handleSubmit)
  • Active state syncs via $isLinkNode(parent) || $isLinkNode(node) (identical to the EPS toolbar)
  • Collapsed-caret fallback inserts a $createLinkNode (matches EPS)
  • Auto-link uses the matcher shape (createLinkMatcherWithRegExp + attributes) and new-tab defaults — EPS can pass its own richer matchers (e.g. D123/T456/S789 intern URLs)
  • New-tab is baked into the link node data (target/rel via the TOGGLE_LINK_COMMAND {url, target, rel} payload) so it serializes and round-trips — no DOM-patching plugin

EPS keeps its own popover UI / hardened auto-link fork; astryx stays OSS-minimal and exposes the extension points (promptForUrl, matchers, linkOpensInNewTab) so EPS drops in without changing the contract.

Changes

  • RichTextEditorToolbar: Link button (on by default; hasLink={false} to hide) + Cmd/Ctrl+K (via Lexical's isExactShortcutMatch, mirroring the playground's ShortcutsPlugin). Prompts for a URL via overridable promptForUrl (default window.prompt), sanitizes it, toggles the link. Themeable icon via the richtext:link registry key.
  • New-tab baked into node data: created links carry target="_blank" + rel="noopener noreferrer" by default; linkOpensInNewTab={false} opts out.
  • RichTextEditorAutoLinkPlugin: auto-linkify typed/pasted URLs + emails (new-tab); matchers overridable.
  • linkUtils: sanitizeUrl / validateUrl (only http/https/mailto/tel; rejects javascript:/data:) + URL_MATCHER / EMAIL_MATCHER.
  • Tests, Storybook stories (WithLinks, WithAutoLink), doc updates.

Testing

  • pnpm -F @astryxdesign/lab typecheck — clean (pre-existing .doc.mjs implicit-any warnings unrelated)
  • RichTextEditor.test.tsx — passing (incl. new link/sanitizer/autolink/new-tab/shortcut tests)
  • check:sync / check:package-boundaries / check:changesets — clean

@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
astryx Ready Ready Preview Aug 3, 2026 6:17am

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Aug 3, 2026
@potatowagon
potatowagon force-pushed the navi/feat/richtext-links branch from 91a0f25 to 4149440 Compare August 3, 2026 05:47
Add link creation/editing to the experimental RichTextEditor, matching
the command + node contract of Meta's EPS eps-lexical editor so its
internals can later be backed by this component.

- RichTextEditorToolbar: Link button + Cmd/Ctrl+K toggle a link on the
  selection via Lexical TOGGLE_LINK_COMMAND; active state synced with
  $isLinkNode(parent) || $isLinkNode(node). URL obtained via an
  overridable promptForUrl (window.prompt default), sanitized to
  http/https/mailto/tel. Collapsed-caret fallback inserts a link node.
- New-tab is baked into the link node data (target=_blank + rel via the
  TOGGLE_LINK_COMMAND {url, target, rel} payload), so it serializes and
  round-trips — no DOM-patching plugin. Toggle with linkOpensInNewTab.
- RichTextEditorAutoLinkPlugin: auto-linkify typed/pasted URLs + emails
  with new-tab attributes; matchers overridable.
- linkUtils: sanitizeUrl/validateUrl + URL/EMAIL matchers.
- Tests, storybook stories, docs.
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

🧪 Sandbox Preview

View Sandbox for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

Modified Components

RichTextEditor (@astryxdesign/lab) · View in Storybook
Metric Before After Delta
Bundle Size (ESM) N/A N/A N/A
Lines of Code N/A 1491 -
Complexity N/A Very High (96) -

Bundle Size Summary

Package Size (ESM) Size (CJS) Gzipped
@astryxdesign/lab N/A 3.5KB 1.5KB

Accessibility Audit

Status: No accessibility violations detected.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

github-actions Bot added a commit that referenced this pull request Aug 3, 2026
@potatowagon
potatowagon merged commit 0d8d5ba into facebook:main Aug 4, 2026
20 of 21 checks passed
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 Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants