From a26a2384c48f3b54ce459d0f0422d1d906c67269 Mon Sep 17 00:00:00 2001 From: kualta Date: Sun, 22 Sep 2024 11:12:21 +0400 Subject: [PATCH] fix markdown whitespace --- package-lock.json | 211 ++++++++++++++++++++++++++++++++---- package.json | 1 + src/components/Markdown.tsx | 34 ++---- src/utils/parseContent.ts | 41 +++++++ 4 files changed, 241 insertions(+), 46 deletions(-) create mode 100644 src/utils/parseContent.ts diff --git a/package-lock.json b/package-lock.json index 3892363..4023639 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,25 +15,25 @@ "@lens-protocol/react-web": "^2.3.1", "@lens-protocol/wagmi": "^4.1.4", "@next/bundle-analyzer": "^13.5.4", - "@radix-ui/react-accordion": "*", - "@radix-ui/react-avatar": "*", - "@radix-ui/react-collapsible": "*", - "@radix-ui/react-context-menu": "*", - "@radix-ui/react-dialog": "*", - "@radix-ui/react-dropdown-menu": "*", - "@radix-ui/react-hover-card": "*", - "@radix-ui/react-label": "*", - "@radix-ui/react-progress": "*", - "@radix-ui/react-radio-group": "*", + "@radix-ui/react-accordion": "next", + "@radix-ui/react-avatar": "next", + "@radix-ui/react-collapsible": "next", + "@radix-ui/react-context-menu": "next", + "@radix-ui/react-dialog": "next", + "@radix-ui/react-dropdown-menu": "next", + "@radix-ui/react-hover-card": "next", + "@radix-ui/react-label": "next", + "@radix-ui/react-progress": "next", + "@radix-ui/react-radio-group": "next", "@radix-ui/react-scroll-area": "^1.1.0", - "@radix-ui/react-separator": "*", - "@radix-ui/react-slot": "*", - "@radix-ui/react-switch": "*", - "@radix-ui/react-tabs": "*", - "@radix-ui/react-toast": "*", - "@radix-ui/react-toggle": "*", - "@radix-ui/react-tooltip": "*", - "@radix-ui/react-use-layout-effect": "*", + "@radix-ui/react-separator": "next", + "@radix-ui/react-slot": "next", + "@radix-ui/react-switch": "next", + "@radix-ui/react-tabs": "next", + "@radix-ui/react-toast": "next", + "@radix-ui/react-toggle": "next", + "@radix-ui/react-tooltip": "next", + "@radix-ui/react-use-layout-effect": "next", "@reduxjs/toolkit": "^1.9.5", "@tanstack/react-query": "^5.29.0", "@types/got": "^9.6.12", @@ -47,7 +47,7 @@ "embla-carousel-react": "^8.1.5", "embla-carousel-wheel-gestures": "^8.0.1", "emoji-picker-react": "^4.11.1", - "eslint-config-next": "*", + "eslint-config-next": "latest", "got": "^13.0.0", "i18next": "^23.2.3", "jwt-decode": "^4.0.0", @@ -76,6 +76,7 @@ "react-redux": "^8.0.5", "react-syntax-highlighter": "^15.5.0", "redux": "^4.2.1", + "remark-breaks": "^4.0.0", "remark-gfm": "^3.0.1", "screenfull": "^6.0.2", "simple-swizzle": "^0.2.2", @@ -13966,6 +13967,18 @@ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -18465,6 +18478,83 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-newline-to-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-newline-to-break/-/mdast-util-newline-to-break-2.0.0.tgz", + "integrity": "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-find-and-replace": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-newline-to-break/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-newline-to-break/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/mdast-util-newline-to-break/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-newline-to-break/node_modules/mdast-util-find-and-replace": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", + "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-newline-to-break/node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-newline-to-break/node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-phrasing": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", @@ -22388,6 +22478,89 @@ } } }, + "node_modules/remark-breaks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-4.0.0.tgz", + "integrity": "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-newline-to-break": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-breaks/node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/remark-breaks/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/remark-breaks/node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-breaks/node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-breaks/node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-breaks/node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-gfm": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz", diff --git a/package.json b/package.json index 5ce4d9b..0404bbf 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "react-redux": "^8.0.5", "react-syntax-highlighter": "^15.5.0", "redux": "^4.2.1", + "remark-breaks": "^4.0.0", "remark-gfm": "^3.0.1", "screenfull": "^6.0.2", "simple-swizzle": "^0.2.2", diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx index cfc37b8..f876b2b 100644 --- a/src/components/Markdown.tsx +++ b/src/components/Markdown.tsx @@ -1,20 +1,21 @@ import ReactMarkdown from "react-markdown"; import type { Components } from "react-markdown/lib/ast-to-react"; +import remarkBreaks from "remark-breaks"; import remarkGfm from "remark-gfm"; import { getBaseUrl } from "~/utils/getBaseUrl"; +import { parseContent } from "~/utils/parseContent"; import { CommunityHandle } from "./communities/CommunityHandle"; import { UserLazyHandle } from "./user/UserLazyHandle"; const BASE_URL = getBaseUrl(); - const Markdown: React.FC<{ content: string }> = ({ content }) => { - const processedText = replaceHandles(parseLinks(content)); + const processedText = parseContent(content).replaceHandles().parseLinks().toString(); return ( = ({ content }) => { ); }; -const replaceHandles = (content: string): string => { - if (!content) return content; - const userHandleRegex = /(? { - const parts = match.slice(1).split("/"); - const handle = parts.length > 1 ? parts[1] : parts[0]; - return `${BASE_URL}u/${handle}`; - }) - .replace(communityHandleRegex, (match) => `${BASE_URL}c${match}`); -}; - -const parseLinks = (content: string): string => { - const linkRegex = /(https?:\/\/\S+)/gi; - return content.replace(linkRegex, (match) => { - const linkWithoutProtocol = match.replace(/^https?:\/\//, ''); - return `[${linkWithoutProtocol}](${match})`; - }); -}; - const CustomLink: Components["a"] = ({ node, ...props }) => { const { href, children } = props; if (href?.startsWith(BASE_URL)) { @@ -59,4 +39,4 @@ const CustomLink: Components["a"] = ({ node, ...props }) => { return {children}; }; -export default Markdown; \ No newline at end of file +export default Markdown; diff --git a/src/utils/parseContent.ts b/src/utils/parseContent.ts new file mode 100644 index 0000000..471cb49 --- /dev/null +++ b/src/utils/parseContent.ts @@ -0,0 +1,41 @@ +import { getBaseUrl } from "./getBaseUrl"; + +class ContentParser { + private content: string; + + constructor(content: string) { + this.content = content; + } + + replaceHandles(): ContentParser { + if (!this.content) return this; + const userHandleRegex = /(? { + const parts = match.slice(1).split("/"); + const handle = parts.length > 1 ? parts[1] : parts[0]; + return `${BASE_URL}u/${handle}`; + }) + .replace(communityHandleRegex, (match) => `${BASE_URL}c${match}`); + return this; + } + + parseLinks(): ContentParser { + const linkRegex = /(https?:\/\/\S+)/gi; + this.content = this.content.replace(linkRegex, (match) => { + const linkWithoutProtocol = match.replace(/^https?:\/\//, ""); + return `[${linkWithoutProtocol}](${match})`; + }); + return this; + } + + toString(): string { + return this.content; + } +} + +export function parseContent(content: string): ContentParser { + return new ContentParser(content); +}