stacker.news icon indicating copy to clipboard operation
stacker.news copied to clipboard

Rich and Markdown Text Editor

Open Soxasora opened this issue 4 months ago • 4 comments

Description

Introduces Lexical as a Markdown/Rich Text editor and renderer This PR is ready for review as-is. While structure and cleanup are an on-going work, the editor has been QA'd extensively and logic has been refined across these two months.

Screenshots

Comment editor view: image

Toplevel editor view: image

Toolbar

Toolbar with shortcuts image

Toolbar with dropdowns image

Floating Toolbar image

Media

Media captions, resize and DnD

https://github.com/user-attachments/assets/6020d937-c756-4f19-bf2e-f3ce136db047

Decorators Table of contents

https://github.com/user-attachments/assets/4505889b-18b7-405e-b628-a7e03e9e078f

Features

core
  • [x] rich text
  • [x] markdown mode
  • [x] formik integration
  • [x] local-storage draft saving
  • [x] max-length support
  • [x] auto-focus support
  • [x] keyboard shortcuts
  • [x] undo/redo history
  • [x] shared history for nested editors
formatting
  • [x] bold, italic, underline, strikethrough
  • [x] subscript, superscript
  • [x] inline code
  • [x] code blocks
  • [x] block quotes
  • [x] headings (H1-H6)
  • [x] paragraphs
  • [x] text alignment (left, center, right, justify)
  • [x] indentation
lists
  • [x] bullet, numbered, check lists
  • [x] parentheses ordered lists (e.g. 1))
content
  • [x] math equations (KaTeX) - inline and block
  • [x] tables with action menu
  • [x] spoilers (inline and container)
  • [x] table of contents generator
media
  • [x] image and video uploads with fees
  • [x] drag and drop upload
  • [x] image captions (nested editor)
  • [x] image resizing
  • [x] YouTube, Twitter, Spotify, PeerTube, Nostr, Wavlake embeds
  • [x] final media type by checking on insert or db upsert
links
  • [x] autolink detection
  • [x] link editor popup
  • [x] SN item mentions (#123456)
  • [x] link fallback for failed media
mentions
  • [x] user mentions (@username) with popover
  • [x] territory mentions (~territory) with popover
  • [x] item mentions
  • [x] typeahead lazy-loaded suggestions
markdown
  • [x] full markdown ↔ Lexical transformation
  • [x] markdown shortcuts in rich mode
  • [x] high-fidelity round-trip conversion
  • [x] highlighted markdown via Shiki
server
  • [x] headless editor for server operations
  • [x] HTML generation from Lexical state
  • [x] HTML sanitization (DOMPurify)
  • [x] media checks
  • [x] legacy content migration worker
UX
  • [x] floating toolbar for selections
  • [x] fixed toolbar for formatting
  • [x] mode switcher (markdown/rich)
  • [x] upload progress indicators
  • [x] copy to clipboard for math nodes
  • [x] link editing popup
  • [x] character count display

New things

  • KaTeX is used in place of MathJax to render equations and LaTeX.
  • Shiki is used in place of React Syntax Highlighter to highlight code blocks - it is undoubtedly faster and lighter than RSH, reaching 4kB of size in the bundle (this has to be re-verified later)
  • Uploads are now concurrently processed, also featuring a percentage, with placeholder per each upload - the user can continue to write while it uploads
  • MediaOrLink preserves scroll on load, supports captions and resize
  • Embeds refactor with a load timeout that spawns an error component (context)
  • HTML from a lexical JSON state is parsed server-side via Linkedom, a performance-focused DOM simulator
  • TBD, a lot has changed from the last update

Additional Context

TBD, a lot has changed from the last update

Progress

TBD, a lot has changed from the last update

Checklist

Are your changes backward compatible? Please answer below:

For example, a change is not backward compatible if you removed a GraphQL field or dropped a database column. Yes! Everything has backwards compatibility. But it also does not depend on existing features that are going to be replaced.

On a scale of 1-10 how well and how have you QA'd this change and any features it might affect? Please answer below: tbd

For frontend changes: Tested on mobile, light and dark mode? Please answer below: UI is tested on both mobile and desktop.

Did you introduce any new environment variables? If so, call them out explicitly here: n/a

Did you use AI for this? If so, how much did it assist you? It's a big PR with Lexical being a novel paradigm.

Ask: better lexical understanding, things that weren't clear in documentation or in code, best practices Agent: massive restructuring, autocomplete, lexical overrides with lint


[!NOTE] Introduce a Lexical-based rich/markdown editor and renderer, add schema/API fields for lexicalState/html, server HTML generation, and a worker-driven migration for legacy content, with broad UI integration and media/mention tooling.

  • Backend:
    • DB: add Item.lexicalState (JSONB) and Item.html; create LexicalMigrationLog/LexicalBatchMigrationLog.
    • GraphQL: add lexicalState/html to Item; accept lexicalState in item/sub/user mutations; new executeConversion mutation.
    • Resolvers: prepare Lexical state, generate sanitized HTML, upload ID extraction from text, SN item link regex.
    • Workers: add partitioned legacy content migration jobs; server utils for headless Lexical and HTML generation (DOMPurify/LinkeDOM).
  • Editor/Renderer:
    • New Lexical editor/reader, contexts, theme, toolbar (floating/fixed), shortcuts, history, drafts, max-length.
    • Plugins: uploads with progress/DnD, link editor, mentions (@, ~, #), math (KaTeX), tables with action menu, spoilers, ToC, code highlighting (Shiki), media embeds.
  • UI Integration:
    • Replace MarkdownInput with LexicalInput across forms (posts, comments, jobs, polls, bounties, bio, sub descriptions).
    • Render comments/items via Lexical when available; dev actions for conversion; truncation and outlawed content handling.
    • Media upload refactor (S3 XHR with progress, filtering, EXIF strip), MediaOrLink scroll-preserving and resizing/captions.
    • Misc: tooltip, copy button tweaks, layout/CSS for editor; route /u/:id redirect.

Written by Cursor Bugbot for commit bc69de5d376d88451fd7beda4a5d8eb1f47605f4. This will update automatically on new commits. Configure here.

Soxasora avatar Sep 09 '25 15:09 Soxasora

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

socket-security[bot] avatar Oct 11 '25 11:10 socket-security[bot]

Woot! The bug hunt begins! I found a couple just now.

Transforms appear to be unreliable:

https://github.com/user-attachments/assets/96162440-7000-469e-9d82-f22bcc377e8b

Sometimes the editor will get into a state where the cursor isn't in a paragraph block and new blocks will be weird (eg code block). I seemed to trigger this behavior after transforming back and forth to markdown mode:

https://github.com/user-attachments/assets/2a21df4a-3280-4c2e-aafc-181c46e1b27c

huumn avatar Nov 17 '25 00:11 huumn

Transforms appear to be unreliable

wow you spotted a very recent bug haha lexical/7974 I'll get to it 🫡

note: transformations need serious QA because covering a lot of scenarios can be really messy.

the cursor isn't in a paragraph block

Ah, might've reduced selection placement calls too much...

Soxasora avatar Nov 17 '25 01:11 Soxasora