Markdown editor with Rich Text preview via MDAST
Status
Ready to QA
Pending:
- README
- PR update
Description
Based on #2501 This is the first stage of the transition to a new Lexical-based editor.
Introduces a Lexical plain text editor with a rich text preview, using MDAST to map and transform Lexical nodes to markdown, aiming to provide lossless transformations.
This PR focuses on providing the same editor experience as before, while transitioning to Lexical.
Screenshots
Refreshed reply editor style, same background as comments
Refreshed top-level editor style
Editor workings
Supported nodes
Nodes are a core concept in Lexical. Not only do they form the visual editor view, as part of the EditorState, but they also represent the underlying data model for what is stored in the editor at any given time. [src]
These are the nodes we support at the moment, the scope for stage 1 is to support everything our old MarkdownInput text editor supported.
| node | what does it do |
|---|---|
SNHeadingNode |
enriches Lexical's HeadingNode with anchors/slugs for ToCs |
TableOfContentsNode |
collects headings and auto-generates a collapsible table of contents |
MathNode |
LaTeX math rendering via KaTeX |
(User/Territory/Item)Node |
a set of nodes for user/territory/item mentions |
MediaNode |
stateful node for images and videos, supports media type checks (disabled) and wraps MediaOrLink. support for captions and resize is disabled. |
EmbedNode |
general node for embeds, wraps the Embed component |
Plugins
A plugin, to all intents and purposes, is a React components that take (either param or via context) a LexicalEditor instance and does something with it. The following are plugins that change the editor's behavior or complete nodes' functionalities.
| plugin | what does it do | commands |
|---|---|---|
FormikBridgePlugin |
extracts text from Lexical and places it in Formik values | |
LocalDraftPlugin |
saves and restores drafts to/from local storage | |
MaxLengthPlugin |
enforces character limits, configurable via lengthOptions |
|
MentionsPlugin |
handles @user and ~territory dual autocomplete |
|
FileUploadPlugin |
file upload with paste and drag/drop support | SN_UPLOAD_FILES_COMMAND: triggers browser file upload |
PreviewPlugin |
toggle between write/preview modes and renders a Lexical Reader | TOGGLE_PREVIEW_COMMAND |
ToolbarPlugin |
editor toolbar | |
HistoryPlugin |
undo/redo |
Extensions
Extensions are a new paradigm, introduced just a couple of major versions ago. Many plugins are essentially just useEffects, they don't render nor effectively need to use React in some useful way.
Extensions are lightweight, meant to be used for stuff that doesn't need React.
Let's say we just want to capture the creation of a specific node to do something special, we can just create an extension that registers a MutationListener and that's it. Less boilerplate, less React dependency.
| extensions | what does it do | commands |
|---|---|---|
CodeShikiSN |
custom Shiki registration that re-register itself on theme changes (light/dark mode) | UPDATE_CODE_THEME_COMMAND |
Shortcuts |
barebones metaKey + key shortcuts handler |
|
MDCommands |
barebones link/bold/italic insert on selection | MD_INSERT_LINK_COMMAND MD_INSERT_BOLD_COMMAND MD_INSERT_ITALIC_COMMAND |
Feature parity
parity list
- write/preview: Y
- bold shortcut: Y
- italic shortcut: Y
- link shortcut: Y
- upload shortcut: Y
- tab insert shortcut: NO
- submit shortcut: Y
- paste images: Y
- file DnD: Y
- upload button: Y
- upload fees: Y
- user/territory/item mentions and autocomplete: Y
- local drafts: Y
- formik: Y
- max length: Y
- undo/redo: Y (by Lexical
HistoryPlugin) - markdown help link: NO
- textarea autosize: Y (by Lexical)
- rich preview: Y (
LexicalReader) - footnotes: NO even though MDAST supports it, we need a Lexical plugin to handle this.
new features
- preview toggle shortcut (
meta+P) - undo/redo via Lexical
- Shiki syntax highlighting
- KaTeX math support
- MDAST-based markdown transformations
MDAST
Lexical already supports markdown transformations via @lexical/markdown, the problem with Lexical's native approach is that we're forced to do workarounds for proper markdown bi-directional transformations.
By treating the Lexical tree as an MDAST tree we can be confident in creating lossless bi-directional markdown transformations. More informations can be found in lib/lexical/mdast/README.md
Lines
This is undoubtedly a big PR but it's smaller than the lines being reported, for example let's take a look at the lib/lexical package:
-
mdast: ~1700 lines README has about 400 lines of infos, then we have the logic and the visitors (~800 lines! but they cover every node) -
nodes: ~1700 lines there's a lot of boilerplate in Lexical nodes -
theme: ~700 lines of CSS ...
Probably around 4k lines are true code.
Additional Context
Persisting and handling the tree in the database is still something that's being conceptualized, but like the original PR, we'll persist the lexical JSON state and its HTML variant.
lib/lexical/server/headless.js
Creating a fake DOM for Lexical operations server-side pollutes the global environment
global.window = newWindow
global.document = newDocument
It is safely cleaned at the end of an operation with the finally block, but it's still something we should keep an eye on.
Checklist
Are your changes backward compatible? Please answer below:
Yes, with a distributed migration approach we're going to migrate legacy content while still keeping its original version right in the text field.
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: 5 - in dev with iterative QA
For frontend changes: Tested on mobile, light and dark mode? Please answer below: In QA
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? Lexical: Ask mode to bridge the gaps in the documentation MDAST: Light usage of Agent mode for refactoring and ensuring every transformer is well-represented
[!NOTE] Replaces the Markdown editor with a Lexical-based editor/reader and SSR pipeline, adds
lexicalState/htmlGraphQL fields, and updates forms/rendering to use the new system.
- Editor/Reader (Lexical):
- Add Lexical plain-text editor (
SNEditor) with toolbar, preview, uploads, mentions, max-length, local drafts, shortcuts, and Shiki/Katex support.- Add client
SNReaderand dynamicreaderwith link interception, ToC, galleries, media/math nodes, embeds.- Replace
MarkdownInputwithSNInputacross post/comment/bio/job/link/poll/territory forms.- Update
Textcomponent to render Lexical (Text) with fallback (LegacyText).- New styles for editor/text; refreshed tooltips and containers.
- GraphQL/SSR:
- Add
lexicalStateandhtmlfields toItemandSubtypes and resolvers.- Introduce
lexicalStateLoader(DataLoader) and SSR HTML generation (lexicalHTMLGenerator); wire into API/Apollo SSR context.- Server libs:
- Implement MDAST import/export/transform pipeline and numerous Lexical nodes/extensions.
- Add server headless DOM utilities (LinkeDOM/DOMPurify) and item-context/media handling.
- Rendering/Components:
- Update comments, item pages, bios, notifications, TOC and media components to use new reader/state.
- Minor UX tweaks (action tooltip delays/noWrapper, gallery/media refactor).
- Config/Deps:
- Add
lexical,dompurify,linkedom,dataloader,katex,@lexical/*,shiki, etc.; aliascanvasoff in Next config.Written by Cursor Bugbot for commit 8727372e77c4c582c9bdee5f17b3a3aade5fcfc5. This will update automatically on new commits. Configure here.
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
| Diff | Package | Supply Chain Security |
Vulnerability | Quality | Maintenance | License |
|---|---|---|---|---|---|---|
| @lexical/clipboard@0.39.0 | ||||||
| @lexical/rich-text@0.39.0 | ||||||
| @lexical/selection@0.39.0 | ||||||
| @lexical/utils@0.39.0 | ||||||
| @lexical/code-shiki@0.39.0 | ||||||
| @lexical/history@0.39.0 | ||||||
| @lexical/html@0.39.0 | ||||||
| dataloader@2.2.3 | ||||||
| dompurify@3.3.0 | ||||||
| linkedom@0.18.12 | ||||||
| @lexical/headless@0.39.0 | ||||||
| katex@0.16.11 ⏵ 0.16.25 | ||||||
| lexical@0.39.0 | ||||||
| @lexical/react@0.39.0 |
I just gave it a little spin - didn't encounter any bugs yet. It feels great and looks great. I'm very excited knowing we took the time to build something we can be confident in. Nice job!
One thing I noticed: I think media loading may be a bit inconsistent with prod behavior
http://localhost:3000/items/458188 vs https://stacker.news/items/737272
I'll give it a deeper look tomorrow.
Edit: the media loading could be caused by me not running imgproxy in dev.
to build something we can be confident in
Agree, the new architecture allows us to do pretty much whatever we want to do with Markdown, and that's a really big power.
One thing I noticed: I think media loading may be a bit inconsistent with prod behavior
It is inconsistent towards measurements because the MediaNode was made for rich text resize/caption behavior. But, in terms of loading, we're still working with the same MediaOrLink component as prod, and you're viewing the image in the preview tab... so that's weird.
edit: unless you didn't run the capture container, in that case I think we don't have a fallback.
Yes, I wasn't running capture. My bad. I'll do a better job QAing tonight.
The other difference is that youtube link renders an embed. I don't think I ever documented in anywhere but the behavior prod follows is: if a media/embed link is in a paragraph, render it as a link.
Oh okay! It makes sense to allow media/embeds only if they're in their own paragraph. I explicitly did the opposite!
I spent most of the time I set aside for this QA troubleshooting weird bugs (like someone disabling all their notification types and causing their notifications to throw errors).
I'll do a serious QA tomorrow and begin review-review!
Just getting started but I'm seeing a bit of weird media flickering
https://github.com/user-attachments/assets/240a4e11-435a-4ace-9c20-b3fa6b46f1e5
Yeah that is something that we have in prod too. Edit mode unmounts and re-mounts the Text component, causing media checks to run each time.
We can likely simply hide Text in edit mode instead of conditionally rendering it. 👀
edit: also the HTML placeholder contributes to this, it renders images as links
I suspect that the code block theme flickerings are due to Shiki not respecting the github-dark-theme-default default theme setting. It's next on the cleanup so I'll address it right away!
I'm just going to keep editing this comment for little things I notice
- triple-click behavior in the markdown editor. actual: selects all text for some reason. expected: selects the paragraph.
- adjacent image/video media doesn't tile into a grid anymore (not a huge deal/priority but for anyone who styled things in the past they might be upset)
- show full text appears in preview but is just fixed and doesn't clamp (in this example I copy and pasted the FAQ)
- headings in posts are no longer links/clickable (useful for linking to a section)
- more of question: curious about the reason for keeping LegacyText
- minor nit: preview toggle shortcut does not toggle back to edit mode (it does go to preview successfully though)
btw I'm really impressed by how well the code is organized. it's a lot, as editors are naturally, but everything feels like it's in a good spot.
triple-click behavior in the markdown editor. actual: selects all text for some reason. expected: selects the paragraph.
editor.registerCommand(lexical.KEY_ENTER_COMMAND, event =>
editor.dispatchCommand(lexical.INSERT_LINE_BREAK_COMMAND, false));
I was weirded out too, Lexical Plain Text stores the entire text in a single paragraph node, and intercepts the Enter key to insert a line break rather than a paragraph. We can restore paragraph on enter key though.
btw I'm really impressed by how well the code is organized.
hey thanks but I do honestly reconsider the structure every 2 minutes
I've done a full initial pass through the code and, dude, it's very nice. You should feel very proud. Lexical is kind of like LDK - barebones for lightning protocol stuff, customizable but useless on its own - and you made SN a custom editor with it. It's amazing. It's crazy to think about what we can and will do with it.
I was trying to keep a mental list of all the cool stuff you did, but I especially like the choice to make the write/preview tabs and the attachment button into a toolbar. That wouldn't have occurred to me.
I'll do a harder pass tomorrow. It's been hard to find faults in it so far.
I also think we'll want READMEs for some of the other directories, or maybe just one README for lib/lexical and components/editor that summarizes what the things in the non-mdast directories are for - even if they mostly just link to the appropriate Lexical docs. I haven't wrapped my head around lib/lexical/html/customs yet, nor lib/lexical/nodes, for instance, and everything they do.
more of question: curious about the reason for keeping LegacyText
We don't actually need it! Especially now that lexical states are computed on-request. It was really useful for live migrations, but we don't need it anymore.
I haven't wrapped my head around lib/lexical/html/customs yet, nor lib/lexical/nodes, for instance, and everything they do.
The HTML customizations are the most fragile piece in this PR. Probably not really fragile, but the fact that I don't feel confident about them, makes them fragile.
They're a nice-enough workaround for imgproxy and outlawed state management, and only for the split second HTML fallback. The need for an HTML fallback, honestly, introduced a lot of head-scratches.
Thank you for the nice words, the fact that we already have a full hybrid editor ready shows that we can confidently build pretty much whatever we want. Lexical is a really good choice.
New nit list:
- media ends up with both the old
mediaContainercss classes and the new classes- we should probably do one or the other
- only curious:
useCallbackRefwhat's it being used for?
`{:toc}` renders with details collapsed, then pops open causing layout shift
https://github.com/user-attachments/assets/a175ce64-a48b-4aa5-b6ff-5ff1fda94a06low priority but something affecting heights of the media containers didn't port over well afaict
IIRC this was very hard to get working well initially - lots of trial-and-error was required and then for videos we kind of had to undue some of what prevented layout shiftlinks to headings referencing the same item (self-created) open in a new tab
I had Chat create a markdown torture test for gfm (it has some stuff we don't support). Everything else worked perfectly afaict.<!--
GFM Torture Test Document
Goal: hit as many edge-cases as possible (CommonMark + GFM extensions).
-->
# GFM Torture Test: “Everything Bagel” 🥯
> **Intent:** This document is intentionally dense, weird, and occasionally “ugly” to stress parsers.
>
> If your renderer survives this, it’s doing pretty well.
> (That line above ends with *two spaces* for a hard line break.)
---
## Table of contents
* [Headings & breaks](#headings--breaks)
* [Emphasis, escaping, and punctuation](#emphasis-escaping-and-punctuation)
* [Links, references, autolinks](#links-references-autolinks)
* [Lists (nested, mixed, loose/tight)](#lists-nested-mixed-loosetight)
* [Blockquotes (nested) + lists + code](#blockquotes-nested--lists--code)
* [Tables (alignment, pipes, inline markdown)](#tables-alignment-pipes-inline-markdown)
* [Code (inline, fenced, indented, nested fences)](#code-inline-fenced-indented-nested-fences)
* [HTML blocks & mixed parsing](#html-blocks--mixed-parsing)
* [Footnotes](#footnotes)
* [Oddities & fuzz section](#oddities--fuzz-section)
---
## Headings & breaks
# Setext heading level 1
## Setext heading level 2
### Heading with escapes: # not a heading, *not emphasis*
#### Heading with inline code: `const x = 1;` and emoji :rocket: 🚀
##### Heading with “smart” punctuation… quotes “like this” and dashes — like this
###### Heading 6
---
Thematic breaks variants:
---
---
---
Ambiguous-ish: (should be a list, not a break)
* * * not a break because it’s a list item
* --- also a list item
---
## Emphasis, escaping, and punctuation
Plain: *italic* **bold** ***bold italic*** ~~strikethrough~~.
Nested: **bold *italic ~~strike~~ italic* bold**.
Edge-ish underscore cases:
* a_word_with_underscores (should usually **not** emphasize inside words)
* *leading underscore*
* trailing underscore_
* **double** and ***triple***
* **bold with *italic inside* and `code`**.
Escapes (these should render literally):
* _ ~ ` # [ ] ( ) { } - + . !
Backslash at end of line for a hard break (CommonMark style):
This line should be on a new line.
HTML entities: © & < > ©
---
## Links, references, autolinks
Inline links:
* [Simple link](https://example.com)
* [Link with title](https://example.com "a title (with parens) and \"quotes\"")
* [Angle-bracket destination](https://example.com/a path/with spaces?q=hello world)
* [Paren-heavy URL](https://example.com/a_%28b%29_c?x=1&y=2)
* [Link with nested brackets [like this]](https://example.com/nested "title")
Reference links (case-insensitive labels):
* [Reference A][ref-a]
* [reference a][REF-A]
* [Collapsed reference][]
* [Shortcut reference]
Autolinks:
* [https://example.com/autolink?x=1&y=2](https://example.com/autolink?x=1&y=2)
* [mailto:[email protected]](mailto:[email protected])
Bare URL (some renderers auto-link this, some don’t—good to test):
[https://example.com/bare-url](https://example.com/bare-url)
Images:
* 
* ![Reference image][img-ref]
GitHub-ish autolink patterns (not strictly “GFM spec” but common on GitHub):
* Mention: @octocat
* Issue-ish: #123
* SHA-ish: deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
[ref-a]: https://example.com/ref "ref title"
[collapsed reference]: https://example.com/collapsed
[shortcut reference]: https://example.com/shortcut
[img-ref]: https://via.placeholder.com/80?text=refimg "ref image title"
---
## Lists (nested, mixed, loose/tight)
Tight list:
* one
* two
* three
Loose list (paragraphs inside items):
* one
still item one after a blank line
* two
* nested bullet A
* nested bullet B
1. nested ordered 1
2. nested ordered 2
* deeper bullet with `inline code`
* deeper bullet with a fenced code block:
```js
// code fence inside deeply nested list
const arr = [1, 2, 3].map(x => x * 2);
console.log(arr);
```
* three with a task list:
* [ ] unchecked task
* [x] checked task
* [x] checked task (capital X)
* [ ] task with **formatting**, ~~strike~~, and a [link](https://example.com)
* [ ] task with nested tasks
* [ ] subtask 1
* [x] subtask 2
Ordered list starting at 7:
7. item seven
8. item eight
9. item nine
* mixed nested bullet
* mixed nested bullet
10. item ten with two paragraphs
second paragraph in item ten
Weird numbering that should still be an ordered list:
1. first
2. second (still “2.” logically)
3. third
---
## Blockquotes (nested) + lists + code
> Blockquote level 1
>
> > Blockquote level 2
> >
> > * list item in nested quote
> > * another item
> >
> > ```python
> > def quoted_code():
> > return "code inside blockquote"
> > ```
>
> Back to level 1 with `inline code`.
> A blockquote with a table (some parsers get quirky):
>
> | a | b |
> | - | - |
> | 1 | 2 |
---
## Tables (alignment, pipes, inline markdown)
Alignment + inline markdown:
| left | center | right | tricky |
| :------------------------- | :--------------------: | -----: | :------------------ |
| *it* | **bd** | ~~st~~ | `code \| span` |
| [lnk](https://example.com) | :smile: 😄 | 12345 | pipe | escaped |
| multi<br>line | cell<br>with<br>breaks | 0 | `<span>html</span>` |
Table with leading/trailing pipes:
| col1 | col2 |
| ---- | ---- |
| a | b |
No leading pipe:
| col1 | col2 | col3 |
| ---- | :--: | ---: |
| x | y | z |
---
## Code (inline, fenced, indented, nested fences)
Inline code edge cases:
* ``code with a ` backtick inside``
* `code with **markdown** inside should stay literal`
Indented code block (4 spaces):
line 1 (indented) line 2 (still code) line 3
Fenced code blocks:
```txt
Plain fenced text.
- This is not a list inside a code block.
- removed line
+ added line
@@ hunk header @@
{
"string": "quotes: \"\", backslash: \\",
"arr": [1, 2, 3],
"nested": { "a": true, "b": null }
}
Tilde fence:
echo "hello from ~~~ fence"
printf '%s\n' "pipes | in | output"
Nested fence demonstration (fence containing fences):
Here is a fenced block *that itself contains* a fenced block:
```js
console.log("inner fence");
```
And a tilde fence:
~~~txt
inner tilde fence
~~~
HTML blocks & mixed parsing
Inline HTML in a paragraph: This is bold via HTML and this is emphasis.
HTML block with nested tags.
Some renderers stop parsing markdown inside HTML blocks; others don’t.
Expandable section (details/summary)
Inside details: markdown should usually work on GitHub.
- [ ] task in details
- [x] another task
type Foo = { a: number; b?: string };
const foo: Foo = { a: 1 };
Raw HTML that might be sanitized (good to test your sanitizer rules):
Footnotes
Footnote reference in text: Here’s a claim that needs a source.[^1] Another footnote, same label reused later.[^1]
Multiple footnotes: alpha[^a] beta[^b] gamma[^c]
[^1]: Footnote one with a link: https://example.com and code.
[^a]: Footnote A.
[^b]: Footnote B with emphasis.
[^c]: Footnote C with a list:
- item 1
- item 2
Oddities & fuzz section
Bracket soup:
- text [with [nested] brackets]
- text (with (nested) parens)
- [unclosed link text](https://example.com
- unbalanced bold _italic ~~strike~~ weirdness maybe
Quotes and punctuation adjacency:
- bold...then text
- ~~strike~~,bold,
code,italic;end.
Unicode + bidi-ish content (rendering quirks):
- café naïve coöperate
- emojis: 🧠⚡️🍕
- combining: é (e + ◌́) vs é (single char)
- RTL snippet: مرحبا بالعالم
Long-ish line (wrapping/perf):
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Final sanity checks
-
A list item ending with two spaces should hard-break. This should be the next line in the same list item.
-
A paragraph after a list:
This should not be part of the list above.
EOF
</details>
Defunct:
- ~~for images, `var(--aspect-ratio)` is not defined (this is what prevents layout shift)~~
- edit: this might be a problem in dev because I wasn't using imgproxy's paid version ... need to double check that
- confirmed that it's a paid version thing
- ~~image grids when width and height are set have strange spacing~~
- ~~I think width and height might be getting switched somewhere - the portrait oriented images have a width > height for some reason~~
- the problem was my browser cache
Adding to the nits:
- nested checklists turn into abstract art (lexical vs prod)
- too much spacing
- too much spacing
Addressing nits:
- something affecting heights of the media containers didn't port over well afaict
This is kind of hard to QA, I'm trying to reproduce but the behavior is 1:1 with prod, mhh...
only curious: useCallbackRef what's it being used for?
The useCallbackRef is needed for Lexical because we dynamically load the Reader, which means that its ref isn't available immediately on mount. It's just an helper for a pattern we would have instead reproduced in many places!
a plan to remove or actual removal of LegacyText
We can remove it, but I'd like to do it in a child PR so that we can rollback safely in the ultra-remote case something happens.
Changes:
- media ends up with both the old
mediaContainercss classes and the new classes- now MediaNode handles styling
-
{:toc}renders with details collapsed, then pops open causing layout shift- At first I wanted to make the table of contents open by default... but it can be big, so now they're closed by default
- links to headings referencing the same item (self-created) open in a new tab
- there are now checks for internal/hash links in MDAST
- Lexical doesn't support
next/linkwhich means that theshow fullbutton wasn't being triggered on hash route changes. So now there's aLinksPluginthat captures clicks to links and usesnext/routerto navigate instead of the default browser behavior.
- replacing html transformation step with context aware SSR rendering
- ItemContextExtension now receives
outlawed, rel, imgproxyUrlsvia thelexicalStateLoader- replaces links, media, embeds with plain text
- gets dimensions and sources from
imgproxyUrls- these dimensions are used by MediaNode to set
width, height- NOTE: I couldn't test this in dev as imgproxy only returns the
srcSet
- NOTE: I couldn't test this in dev as imgproxy only returns the
- sources are used by MediaNode to set
srcSet, bestResSrc
- these dimensions are used by MediaNode to set
- ItemContextExtension now receives
Remaining steps:
- READMEs
- removal of dead code
lmk when you feel like this is ready to be merged and I'll give it a final QA/review.
This is kind of hard to QA, I'm trying to reproduce but the behavior is 1:1 with prod, mhh...
Then that regressed at some point. Not on the menu for this PR then!
The useCallbackRef is needed for Lexical because we dynamically load the Reader, which means that its ref isn't available immediately on mount.
That makes sense. I mostly wanted my suspicions confirmed.
We can remove it, but I'd like to do it in a child PR so that we can rollback safely in the ultra-remote case something happens.
Sounds good to me!
[!WARNING] Review the following alerts detected in dependencies.
According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
| Action | Severity | Alert (click "▶" to expand/collapse) |
|---|---|---|
| Warn | Obfuscated code: npm
|
I'm missing some READMEs (components and server), but other than that, this PR feels ready to be reviewed. I’ll finish writing them ASAP.
fyi i'm seeing a regression on triple click
Ah, forgot to do something about it! This will probably involve creating our own PlainTextExtension.
Oh I thought you fixed it in some commit. I must've not tried to recreate it hard enough
I'm seeing what looks like a placeholder in srcset
I'm seeing what looks like a placeholder in srcset
Forgot to destructure format alongside dimensions, video to exclude it from the srcSetObj. Regressed because of a cleanup (was indeed asking myself why format was there unused)
Okay, didn't end up creating our own Mode: we just had to restore the paragraph commands overridden by PlainTextExtension.
Then on the import side, since we can't use Lexical's full markdown normalizer in plain text, I just implemented \n -> linebreak and \n\n -> paragraph
Okay, didn't end up creating our own Mode: we just had to restore the paragraph commands overridden by PlainTextExtension.
Works for me now. (Also there's a debug console.log in utils that you missed.)
Forgot to destructure format alongside dimensions, video
Cool, not getting warnings in console anymore.
I've done another QA/review. My plan is to merge/deploy this early tomorrow (my) morning.
Should I also plan to merge removing LegacyText?
I finally got to review the SSR fallback to understand what cursorbot was saying, and it did seem like Lexical was being recreated on every render. It wasn't a noticeable thing, because the module is cached, but it was an oversight.
My plan is to merge/deploy this early tomorrow (my) morning.
I'll be ready 🫡
Should I also plan to merge removing LegacyText?
It does make up for the extra bundle, so I would also merge the cleanup PR. I'll continue to QA the cleanup to ensure that I didn't break anything while removing libraries.
btw I think the newline/paragraph stuff broke the gallery node thing
and it looks like because media nodes are wrapped in paragraphs, they appear to have more padding because of line-height
at least we know we're at the stage where everything left is some tiny bug fix creating new ones lol