Remove legacy text input/markdown handling
Description
Based on #2657 Removes unused text input code and legacy markdown-related code.
Removed
-
LegacyText(ReactMarkdown) -> Text (SNReader)-
Mention-> UserMentionNode -
Sub-> TerritoryMentionNode -
Item-> ItemMentionNode -
Footnote-> Reference, Definition, Section, Backref nodes -
MediaLink-> MediaNode -
Table-> TableNode (handled by lexical) -
CodeSkeleton -
Code-> CodeNode (handled by lexical with Shiki) -
P
-
-
remarkToc-> handled by MDAST and Lexical -
remarkUnicode-> not needed under KaTeX -
remarkMath-> extracted mdast-util-math -
rehypeSN-> feature parity achieved via MDAST visitors and transforms -
rehypeMathjax-> replaced with KaTeX -
rehypeSNStyled-> superseded by MDAST -
remarkGfm-> not needed - React Syntax Highlighter -> replaced with Shiki
Bundle analysis
tmi: Lexical post-removals is 0.03 MB heavier than master on first load (what matters for the client). I was expecting a much higher first-load and overall bundle from Lexical, considering all the things it handles and can handle, but it's instead marginal.
Master
_app
+ First Load JS shared by all 1.01 MB
├ chunks/framework-c6ee5607585ef091.js 44.9 kB
├ chunks/main-ea7afcb22284b775.js 37.3 kB
├ chunks/pages/_app-99d52cc32cadfb73.js 883 kB
├ css/554058231c942e24.css 46.5 kB
└ other shared chunks (total) 2.13 kB
Lexical pre-removals
_app
+ First Load JS shared by all 1.09 MB
├ chunks/framework-c6ee5607585ef091.js 44.9 kB
├ chunks/main-ea7afcb22284b775.js 37.3 kB
├ chunks/pages/_app-e6dec95ecd8fe254.js 948 kB
├ css/198ebdadd5386d4b.css 55.4 kB
└ other shared chunks (total) 6.2 kB
Lexical post-removals
_app
+ First Load JS shared by all 1.04 MB
├ chunks/framework-c6ee5607585ef091.js 44.9 kB
├ chunks/main-ea7afcb22284b775.js 37.3 kB
├ chunks/pages/_app-db96a9243c75fba9.js 897 kB
├ css/087336682c6d686c.css 55.9 kB
└ other shared chunks (total) 6.14 kB
Additional Context
The following removals have been postponed:
-
useDualAutocomplete- provides user and territory mentions to inputs, should be replaced with Lexical but this raises questions about avoidable overhead on simple text inputs
-
remove-markdown- removes markdown syntax from given text, its performance has to be evaluated to justify migration to our own MDAST system
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.
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:
For frontend changes: Tested on mobile, light and dark mode? Please answer below:
Did you introduce any new environment variables? If so, call them out explicitly here:
Did you use AI for this? If so, how much did it assist you?
[!NOTE] Replaces legacy markdown and ReactMarkdown with a Lexical-based editor/reader, adds server-side HTML generation, and exposes
lexicalState/htmlonItem/Subvia GraphQL while updating UI to useSNInput.
- Frontend:
- Editor/Reader: Add
SNEditor/SNReader, toolbar, uploads, mentions, links, preview, code (Shiki), math (KaTeX), media, gallery, TOC, footnotes, and supporting nodes/plugins.- Components: Replace
MarkdownInputwithSNInput; switchTextrendering to Lexical; update comment/item/bio forms, TOC, media handling, and styles.- UX: Overflow handling, tooltip tweaks, dark-mode code theming.
- Backend/API:
- GraphQL: Add
lexicalStateandhtmlfields toItem/Subwith resolvers using alexicalStateLoader(DataLoader) and SSR HTML generator.- SSR: Wire
lexicalStateLoaderinto Apollo contexts for API and SSR.- Infra/Deps:
- Add Lexical, mdast utilities, KaTeX, Shiki, DOMPurify, LinkeDOM; remove remark/rehype/react-markdown/highlight-related code; minor Next.js alias for
canvas.Written by Cursor Bugbot for commit bf4dcce03a99be2537e7a8bb97ed8001ab00f3a6. 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 | ||||||
| mdast-util-to-markdown@2.1.0 ⏵ 2.1.2 | ||||||
| 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 |
[!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
|
Cannot update a component (SNReader) while rendering a different component (LexicalExtensionComposer). To locate the bad setState() call inside LexicalExtensionComposer, follow the stack trace as described in https://reactjs.org/link/setstate-in-render Error Component Stack
alternative pattern using context:
import dynamic from "next/dynamic"
import { createContext, useContext } from "react"
import { useRouter } from "next/router"
const HtmlContext = createContext("")
function HtmlFallback() {
const html = useContext(HtmlContext)
return <div dangerouslySetInnerHTML={{ __html: html }} />
}
const Reader = dynamic(() => import("./reader"), {
ssr: false,
loading: () => <HtmlFallback />,
})
export function SNReader({ html, ...props }) {
const router = useRouter()
// optional: avoid query flicker on first render in some cases
const showDebugHtml = router.isReady && router.query.html != null
if (showDebugHtml) {
return <div dangerouslySetInnerHTML={{ __html: html }} />
}
return (
<HtmlContext.Provider value={html}>
<Reader {...props} />
</HtmlContext.Provider>
)
}
This fits perfectly, thank you!
This PR can be merged without the previous one as QA is being done here, and new changes and fixes are being pushed here.
If it's better to do the original PR and then the cleanup PR, I can un-draft