fix(markdown): render single newlines as line breaks

Add remark-breaks plugin to render single newlines as <br> tags,
matching GitHub Flavored Markdown behavior.

Fixes https://github.com/usememos/memos/issues/5277

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steven 2025-11-26 20:13:57 +08:00
parent ef033d21de
commit 5a16f8009f
3 changed files with 23 additions and 1 deletions

View File

@ -56,6 +56,7 @@
"react-simple-pull-to-refresh": "^1.3.3",
"react-use": "^17.6.0",
"rehype-raw": "^7.0.0",
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.1",
"tailwind-merge": "^3.4.0",
"tailwindcss": "^4.1.17",

View File

@ -146,6 +146,9 @@ importers:
rehype-raw:
specifier: ^7.0.0
version: 7.0.0
remark-breaks:
specifier: ^4.0.0
version: 4.0.0
remark-gfm:
specifier: ^4.0.1
version: 4.0.1
@ -2258,6 +2261,9 @@ packages:
mdast-util-mdxjs-esm@2.0.1:
resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==}
mdast-util-newline-to-break@2.0.0:
resolution: {integrity: sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==}
mdast-util-phrasing@4.1.0:
resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==}
@ -2611,6 +2617,9 @@ packages:
rehype-raw@7.0.0:
resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
remark-breaks@4.0.0:
resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==}
remark-gfm@4.0.1:
resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
@ -5022,6 +5031,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
mdast-util-newline-to-break@2.0.0:
dependencies:
'@types/mdast': 4.0.4
mdast-util-find-and-replace: 3.0.2
mdast-util-phrasing@4.1.0:
dependencies:
'@types/mdast': 4.0.4
@ -5548,6 +5562,12 @@ snapshots:
hast-util-raw: 9.1.0
vfile: 6.0.3
remark-breaks@4.0.0:
dependencies:
'@types/mdast': 4.0.4
mdast-util-newline-to-break: 2.0.0
unified: 11.0.5
remark-gfm@4.0.1:
dependencies:
'@types/mdast': 4.0.4

View File

@ -2,6 +2,7 @@ import { observer } from "mobx-react-lite";
import { memo, useEffect, useRef, useState } from "react";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";
import remarkBreaks from "remark-breaks";
import remarkGfm from "remark-gfm";
import useCurrentUser from "@/hooks/useCurrentUser";
import { cn } from "@/lib/utils";
@ -98,7 +99,7 @@ const MemoContent = observer((props: Props) => {
onDoubleClick={onMemoContentDoubleClick}
>
<ReactMarkdown
remarkPlugins={[remarkGfm, remarkTag, remarkPreserveType]}
remarkPlugins={[remarkGfm, remarkBreaks, remarkTag, remarkPreserveType]}
rehypePlugins={[rehypeRaw]}
components={{
// Conditionally render custom components based on AST node type