import styles from './text.module.css' import ReactMarkdown from 'react-markdown' import gfm from 'remark-gfm' import { LightAsync as SyntaxHighlighter } from 'react-syntax-highlighter' import atomDark from 'react-syntax-highlighter/dist/cjs/styles/prism/atom-dark' import mention from '../lib/remark-mention' import sub from '../lib/remark-sub' import React, { useState, memo, useRef, useCallback, useMemo } from 'react' import GithubSlugger from 'github-slugger' import LinkIcon from '../svgs/link.svg' import Thumb from '../svgs/thumb-up-fill.svg' import { toString } from 'mdast-util-to-string' import copy from 'clipboard-copy' import ZoomableImage, { decodeOriginalUrl } from './image' import { IMGPROXY_URL_REGEXP } from '../lib/url' import reactStringReplace from 'react-string-replace' export function SearchText ({ text }) { return (

{reactStringReplace(text, /\*\*\*([^*]+)\*\*\*/g, (match, i) => { return {match} })}

) } function Heading ({ level, slugger, noFragments, topLevel, children, node, ...props }) { const [copied, setCopied] = useState(false) const id = useMemo(() => noFragments ? undefined : slugger.slug(toString(node).replace(/[^\w\-\s]+/gi, '')), [node, noFragments, slugger]) const h = useMemo(() => { switch (level) { case 1: return topLevel ? 'h1' : 'h4' case 2: return topLevel ? 'h2' : 'h5' case 3: return topLevel ? 'h3' : 'h6' case 4: return topLevel ? 'h4' : 'h6' case 5: return topLevel ? 'h5' : 'h6' case 6: return 'h6' default: return 'h6' } }, [level, topLevel]) const Icon = copied ? Thumb : LinkIcon return ( {React.createElement(h, { id, ...props }, children)} {!noFragments && topLevel && { const location = new URL(window.location) location.hash = `${id}` copy(location.href) setTimeout(() => setCopied(false), 1500) setCopied(true) }} width={18} height={18} className='fill-grey' /> } ) } // this is one of the slowest components to render export default memo(function Text ({ nofollow, imgproxyUrls, children, tab, ...outerProps }) { // all the reactStringReplace calls are to facilitate search highlighting const slugger = useRef(new GithubSlugger()) const Table = useCallback(({ node, ...props }) => , []) const Code = useCallback(({ node, inline, className, children, style, ...props }) => { return inline ? ( {children} ) : ( {children} ) }, []) const P = useCallback(({ children, ...props }) =>
{children}
, []) const Img = useCallback(({ node, src, ...props }) => { const url = IMGPROXY_URL_REGEXP.test(src) ? decodeOriginalUrl(src) : src const srcSet = imgproxyUrls?.[url] return }, [imgproxyUrls, outerProps, tab]) return (
, h2: (props) => , h3: (props) => , h4: (props) => , h5: (props) => , h6: (props) => , table: Table, p: P, code: Code, a: ({ node, href, children, ...props }) => { children = children ? Array.isArray(children) ? children : [children] : [] // don't allow zoomable images to be wrapped in links if (children.some(e => e?.props?.node?.tagName === 'img')) { return <>{children} } // If [text](url) was parsed as and text is not empty and not a link itself, // we don't render it as an image since it was probably a conscious choice to include text. const text = children[0] if (!!text && !/^https?:\/\//.test(text)) { return {text} } // assume the link is an image which will fallback to link if it's not return {children} }, img: Img }} remarkPlugins={[gfm, mention, sub]} > {children}
) })