1
0
mirror of https://github.com/spacebarchat/client.git synced 2024-11-21 18:02:32 +01:00

markdown v2

This commit is contained in:
Puyodead1 2023-09-20 01:26:44 -04:00
parent 1ab749a762
commit 9afc5b118d
No known key found for this signature in database
GPG Key ID: A4FA4FEC0DD353FC
8 changed files with 467 additions and 725 deletions

View File

@ -49,15 +49,12 @@
"react-select-search": "^4.1.6",
"react-spinners": "^0.13.8",
"react-string-replace": "^1.1.1",
"react-syntax-highlighter": "^15.5.0",
"react-use-error-boundary": "^3.0.0",
"rehype-prism": "^2.2.2",
"rehype-react": "^8.0.0",
"remark-gfm": "^4.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.0.0",
"remark-gfm": "^3.0.1",
"reoverlay": "^1.0.3",
"styled-components": "^5.3.10",
"unified": "^11.0.3"
"unist-util-visit": "^5.0.0"
},
"devDependencies": {
"@craco/craco": "^7.1.0",
@ -65,9 +62,9 @@
"@types/jest": "^27.5.2",
"@types/loadable__component": "^5.13.5",
"@types/node": "^16.18.50",
"@types/prismjs": "^1.26.0",
"@types/react": "^18.2.15",
"@types/react": "^18.2.22",
"@types/react-dom": "^18.2.7",
"@types/react-syntax-highlighter": "^15.5.7",
"@types/styled-components": "^5.1.27",
"@typescript-eslint/eslint-plugin": "^6.7.0",
"@typescript-eslint/parser": "^6.7.0",

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,11 @@ const Wrapper = styled(Container)<{ size: number }>`
height: ${(props) => props.size}px;
border-radius: 50%;
position: relative;
&:hover {
text-decoration: underline;
cursor: pointer;
}
`;
interface Props {

View File

@ -1,4 +1,5 @@
import { Suspense, lazy } from "react";
import { withErrorBoundary } from "react-use-error-boundary";
const Renderer = lazy(() => import("./RemarkRenderer"));
@ -6,7 +7,7 @@ export interface MarkdownProps {
content: string;
}
export default function Markdown(props: MarkdownProps) {
function Markdown(props: MarkdownProps) {
if (!props.content) return null;
return (
@ -15,3 +16,5 @@ export default function Markdown(props: MarkdownProps) {
</Suspense>
);
}
export default withErrorBoundary(Markdown);

View File

@ -1,121 +1,28 @@
// adapted from Revite
// https://github.com/revoltchat/revite/blob/fe63c6633f32b54aa1989cb34627e72bb3377efd/src/components/markdown/RemarkRenderer.tsx
import React from "react";
import * as prod from "react/jsx-runtime";
import rehypePrism from "rehype-prism";
import rehypeReact from "rehype-react";
import { ReactMarkdown } from "react-markdown/lib/react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { dracula } from "react-syntax-highlighter/dist/esm/styles/prism";
import remarkGfm from "remark-gfm";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import styled from "styled-components";
import { unified } from "unified";
import Link from "../Link";
import { MarkdownProps } from "./Markdown";
import RenderCodeblock from "./plugins/Codeblock";
import "./prism";
/**
* Null element
*/
const Null: React.FC = () => null;
const Container = styled.div`
// remove the excessive left padding in lists
ul,
ol {
padding: 0 0 0 15px;
}
/**
* Custom Markdown components
*/
const components = {
// emoji: RenderEmoji,
// mention: RenderMention,
// spoiler: RenderSpoiler,
// channel: RenderChannel,
a: Link,
p: styled.p`
margin: 0;
> code {
padding: 1px 4px;
flex-shrink: 0;
}
`,
h1: styled.h1`
margin: 0.2em 0;
`,
h2: styled.h2`
margin: 0.2em 0;
`,
h3: styled.h3`
margin: 0.2em 0;
`,
h4: styled.h4`
margin: 0.2em 0;
`,
h5: styled.h5`
margin: 0.2em 0;
`,
h6: styled.h6`
margin: 0.2em 0;
`,
pre: RenderCodeblock,
code: styled.code`
color: var(--text);
background: var(--background-secondary);
font-size: 90%;
font-family: var(--font-family-code);
border-radius: 4px;
box-decoration-break: clone;
`,
table: styled.table`
border-collapse: collapse;
th,
td {
padding: 6px;
border: 1px solid var(--background-teritary);
}
`,
ul: styled.ul`
list-style-position: inside;
padding: 0;
margin: 0.2em 0;
`,
ol: styled.ol`
list-style-position: inside;
padding: 0;
margin: 0.2em 0;
`,
blockquote: styled.blockquote`
blockquote {
margin: 2px 0;
padding: 2px 0;
background: red;
padding: 5px;
background-color: var(--background-secondary);
width: fit-content;
border-radius: 4px;
border-inline-start: 4px solid var(--background-teritary);
> * {
margin: 0 8px;
}
`,
// Block image elements
img: Null,
// Catch literally everything else just in case
video: Null,
figure: Null,
picture: Null,
source: Null,
audio: Null,
script: Null,
style: Null,
};
const render = unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkRehype)
.use(rehypePrism)
// @ts-expect-error typescript doesn't like this
.use(rehypeReact, { Fragment: prod.Fragment, jsx: prod.jsx, jsxs: prod.jsxs, components });
border-inline-start: 4px solid var(--background-tertiary);
}
`;
/**
* Regex for matching execessive recursion of blockquotes and lists
@ -176,11 +83,42 @@ function sanitize(content: string) {
export default React.memo(({ content }: MarkdownProps) => {
const sanitizedContent = React.useMemo(() => sanitize(content), [content]);
const [parsedContent, setParsedContent] = React.useState<React.ReactElement>(null!);
React.useEffect(() => {
render.process(sanitizedContent).then((file) => setParsedContent(file.result));
}, [sanitizedContent]);
return parsedContent;
return (
<Container>
<ReactMarkdown
remarkPlugins={[[remarkGfm, { singleTilde: false }]]}
children={sanitizedContent}
// @ts-expect-error idk what is going on here, but this is correct
components={{
code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || "");
return !inline && match ? (
<SyntaxHighlighter
children={String(children).replace(/\n$/, "")}
language={match[1]}
// @ts-expect-error oh fk off typescript, dark is ok for you but not dracula? ok
style={dracula}
PreTag="section" // parent tag
{...props}
/>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
a({ node, href, children, ...props }) {
return (
<Link href={href} target="_blank" rel="noopener noreferrer" {...props}>
{children}
</Link>
);
},
blockquote({ node, children }) {
return <blockquote>{children}</blockquote>;
},
}}
/>
</Container>
);
});

View File

@ -0,0 +1,97 @@
import Prism from "prismjs";
import "prismjs/components/prism-actionscript";
import "prismjs/components/prism-ada";
import "prismjs/components/prism-apacheconf";
import "prismjs/components/prism-applescript";
// import "prismjs/components/prism-arduino"; // throws `land2 is undefined`
import "prismjs/components/prism-asciidoc";
import "prismjs/components/prism-asm6502";
import "prismjs/components/prism-autohotkey";
import "prismjs/components/prism-autoit";
import "prismjs/components/prism-bash.min.js";
import "prismjs/components/prism-basic";
import "prismjs/components/prism-batch";
import "prismjs/components/prism-brainfuck";
import "prismjs/components/prism-c.min.js";
import "prismjs/components/prism-clojure";
import "prismjs/components/prism-coffeescript";
import "prismjs/components/prism-coq";
import "prismjs/components/prism-cpp.min.js";
import "prismjs/components/prism-csharp";
import "prismjs/components/prism-dart";
import "prismjs/components/prism-diff";
import "prismjs/components/prism-dns-zone-file";
import "prismjs/components/prism-docker";
import "prismjs/components/prism-ebnf";
import "prismjs/components/prism-elixir";
import "prismjs/components/prism-elm";
import "prismjs/components/prism-erlang";
import "prismjs/components/prism-excel-formula";
import "prismjs/components/prism-fortran";
import "prismjs/components/prism-fsharp";
import "prismjs/components/prism-glsl";
import "prismjs/components/prism-gml";
import "prismjs/components/prism-go";
import "prismjs/components/prism-graphql";
import "prismjs/components/prism-haml";
import "prismjs/components/prism-haskell";
import "prismjs/components/prism-haxe";
import "prismjs/components/prism-http";
import "prismjs/components/prism-ini";
import "prismjs/components/prism-java";
import "prismjs/components/prism-javastacktrace";
import "prismjs/components/prism-json.min.js";
import "prismjs/components/prism-json5.min.js";
import "prismjs/components/prism-jsstacktrace";
import "prismjs/components/prism-kotlin";
import "prismjs/components/prism-latex";
import "prismjs/components/prism-less";
import "prismjs/components/prism-lisp";
import "prismjs/components/prism-livescript";
import "prismjs/components/prism-llvm";
import "prismjs/components/prism-lua";
import "prismjs/components/prism-makefile";
import "prismjs/components/prism-markdown";
import "prismjs/components/prism-matlab";
import "prismjs/components/prism-mel";
import "prismjs/components/prism-mizar";
import "prismjs/components/prism-mongodb";
import "prismjs/components/prism-moonscript";
import "prismjs/components/prism-nasm";
import "prismjs/components/prism-nginx";
import "prismjs/components/prism-nim";
import "prismjs/components/prism-objectivec";
import "prismjs/components/prism-ocaml";
import "prismjs/components/prism-perl";
import "prismjs/components/prism-powershell";
import "prismjs/components/prism-processing";
import "prismjs/components/prism-protobuf";
import "prismjs/components/prism-python";
import "prismjs/components/prism-qml";
import "prismjs/components/prism-r";
import "prismjs/components/prism-reason";
import "prismjs/components/prism-ruby";
import "prismjs/components/prism-rust";
import "prismjs/components/prism-sass";
import "prismjs/components/prism-scala";
import "prismjs/components/prism-scheme";
import "prismjs/components/prism-scss";
import "prismjs/components/prism-shell-session";
import "prismjs/components/prism-sml";
import "prismjs/components/prism-sql";
import "prismjs/components/prism-stan";
import "prismjs/components/prism-swift";
import "prismjs/components/prism-toml";
import "prismjs/components/prism-typescript";
import "prismjs/components/prism-vim";
import "prismjs/components/prism-visual-basic";
import "prismjs/components/prism-wasm";
import "prismjs/components/prism-wolfram";
import "prismjs/components/prism-xquery";
import "prismjs/components/prism-yaml";
import "prismjs/themes/prism-tomorrow.css";
// import "./prism.css"; // TODO: customizable theme and default theme selection
export default Prism;

View File

@ -7,6 +7,12 @@ import { MessageLike } from "../../stores/objects/Message";
const Container = styled.div`
font-size: 16px;
font-weight: var(--font-weight-medium);
user-select: none;
&:hover {
text-decoration: underline;
cursor: pointer;
}
`;
interface Props {

View File

@ -70,6 +70,7 @@ export const MessageContent = styled.div`
flex-direction: column;
justify-content: center;
padding-right: 48px;
word-wrap: anywhere;
`;
export const DetailBase = styled.div`