feat(markdown): add spoiler text

This commit is contained in:
Tom (plebeius.eth)
2025-01-28 15:12:39 +01:00
parent cbed83cd10
commit 9a08b9572b
3 changed files with 71 additions and 5 deletions
+60 -4
View File
@@ -125,31 +125,84 @@ const blockquoteToGreentext = () => (tree: any) => {
}); });
}; };
const spoilerToDiv = () => (tree: any) => {
tree.children.forEach((node: any) => {
if (node.type === 'paragraph') {
const text = node.children.map((child: any) => child.value).join('');
if (text.includes('§§SPOILER_START§§')) {
node.type = 'span';
node.data = {
hName: 'span',
};
node.children.forEach((child: any) => {
if (child.type === 'text') {
const parts = child.value.split(/(§§SPOILER_START§§.*?§§SPOILER_END§§)/);
if (parts.length > 1) {
const newChildren = parts.map((part: string) => {
const spoilerMatch = part.match(/§§SPOILER_START§§(.*?)§§SPOILER_END§§/);
if (spoilerMatch) {
return {
type: 'span',
data: {
hName: 'span',
hProperties: {
className: 'spoilertext',
},
},
children: [
{
type: 'text',
value: spoilerMatch[1],
},
],
};
}
return {
type: 'text',
value: part,
};
});
node.children = newChildren;
}
}
});
}
}
});
};
interface MarkdownProps { interface MarkdownProps {
content: string; content: string;
title?: string; title?: string;
} }
const Markdown = ({ content, title }: MarkdownProps) => { const Markdown = ({ content, title }: MarkdownProps) => {
const preprocessedContent = useMemo(() => {
if (!content) return '';
return content.replace(/<spoiler>([^<]*)<\/spoiler>/g, '§§SPOILER_START§§$1§§SPOILER_END§§').replace(/<img[^>]*src=['"]([^'"]+)['"][^>]*>/gi, '$1');
}, [content]);
const remarkPlugins: any[] = [[supersub]]; const remarkPlugins: any[] = [[supersub]];
if (content && content.length <= MAX_LENGTH_FOR_GFM) { if (preprocessedContent && preprocessedContent.length <= MAX_LENGTH_FOR_GFM) {
remarkPlugins.push([remarkGfm, { singleTilde: false }]); remarkPlugins.push([remarkGfm, { singleTilde: false }]);
} }
const customSchema = useMemo( const customSchema = useMemo(
() => ({ () => ({
...defaultSchema, ...defaultSchema,
tagNames: [...(defaultSchema.tagNames || []), 'div'], tagNames: [...(defaultSchema.tagNames || []), 'div', 'span'],
attributes: { attributes: {
...defaultSchema.attributes, ...defaultSchema.attributes,
div: ['className'], div: ['className'],
span: ['className'],
}, },
}), }),
[], [],
); );
remarkPlugins.push([blockquoteToGreentext]); remarkPlugins.push([blockquoteToGreentext]);
remarkPlugins.push([spoilerToDiv]);
const isInCatalogView = isCatalogView(useLocation().pathname, useParams()); const isInCatalogView = isCatalogView(useLocation().pathname, useParams());
@@ -162,7 +215,7 @@ const Markdown = ({ content, title }: MarkdownProps) => {
</span> </span>
)} )}
<ReactMarkdown <ReactMarkdown
children={content} children={preprocessedContent}
remarkPlugins={remarkPlugins} remarkPlugins={remarkPlugins}
rehypePlugins={[[rehypeSanitize, customSchema]]} rehypePlugins={[[rehypeSanitize, customSchema]]}
components={{ components={{
@@ -173,7 +226,10 @@ const Markdown = ({ content, title }: MarkdownProps) => {
h4: ({ children }) => <p className={styles.header}>{children}</p>, h4: ({ children }) => <p className={styles.header}>{children}</p>,
h5: ({ children }) => <p className={styles.header}>{children}</p>, h5: ({ children }) => <p className={styles.header}>{children}</p>,
h6: ({ children }) => <p className={styles.header}>{children}</p>, h6: ({ children }) => <p className={styles.header}>{children}</p>,
img: ({ src }) => <span>{src}</span>, img: ({ src, alt }) => {
const displayText = src || alt || 'image';
return <span>{displayText}</span>;
},
video: ({ src }) => <span>{src}</span>, video: ({ src }) => <span>{src}</span>,
iframe: ({ src }) => <span>{src}</span>, iframe: ({ src }) => <span>{src}</span>,
source: ({ src }) => <span>{src}</span>, source: ({ src }) => <span>{src}</span>,
+10
View File
@@ -26,6 +26,16 @@ hr {
color: var(--post-greentext-color); color: var(--post-greentext-color);
} }
.spoilertext {
color: #000 !important;
background: #000 !important;
text-decoration: none;
}
.spoilertext:hover {
color: #fff !important;
}
.capitalize { .capitalize {
text-transform: capitalize; text-transform: capitalize;
} }
+1 -1
View File
@@ -14584,7 +14584,7 @@ react-scripts@5.0.1:
optionalDependencies: optionalDependencies:
fsevents "^2.3.2" fsevents "^2.3.2"
react-virtuoso@^4.12.3: react-virtuoso@4.12.3:
version "4.12.3" version "4.12.3"
resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.12.3.tgz#beecf0582b31058c5a6ed3ec58fc43fd780e5844" resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.12.3.tgz#beecf0582b31058c5a6ed3ec58fc43fd780e5844"
integrity sha512-6X1p/sU7hecmjDZMAwN+r3go9EVjofKhwkUbVlL8lXhBZecPv9XVCkZ/kBPYOr0Mv0Vl5+Ziwgexg9Kh7+NNXQ== integrity sha512-6X1p/sU7hecmjDZMAwN+r3go9EVjofKhwkUbVlL8lXhBZecPv9XVCkZ/kBPYOr0Mv0Vl5+Ziwgexg9Kh7+NNXQ==