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 React, { useState, memo, useRef, useCallback, useMemo, useEffect } from 'react' import MediaOrLink from './media-or-link' import { IMGPROXY_URL_REGEXP, decodeProxyUrl } from '@/lib/url' import reactStringReplace from 'react-string-replace' import { Button } from 'react-bootstrap' import { useRouter } from 'next/router' import Link from 'next/link' import { UNKNOWN_LINK_REL } from '@/lib/constants' import isEqual from 'lodash/isEqual' import UserPopover from './user-popover' import ItemPopover from './item-popover' import classNames from 'classnames' import { CarouselProvider, useCarousel } from './carousel' import rehypeSN from '@/lib/rehype-sn' import remarkUnicode from '@/lib/remark-unicode' import Embed from './embed' import remarkMath from 'remark-math' import rehypeMathjax from 'rehype-mathjax' const rehypeSNStyled = () => rehypeSN({ stylers: [{ startTag: '', endTag: '', className: styles.superscript }, { startTag: '', endTag: '', className: styles.subscript }] }) const remarkPlugins = [gfm, remarkUnicode, [remarkMath, { singleDollarTextMath: false }]] const rehypePlugins = [rehypeSNStyled, rehypeMathjax] export function SearchText ({ text }) { return (

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

) } // this is one of the slowest components to render export default memo(function Text ({ rel = UNKNOWN_LINK_REL, imgproxyUrls, children, tab, itemId, outlawed, topLevel }) { const [overflowing, setOverflowing] = useState(false) const router = useRouter() const [show, setShow] = useState(false) const containerRef = useRef(null) // if we are navigating to a hash, show the full text useEffect(() => { setShow(router.asPath.includes('#') && !router.asPath.includes('#itemfn-')) const handleRouteChange = (url, { shallow }) => { setShow(url.includes('#') && !url.includes('#itemfn-')) } router.events.on('hashChangeStart', handleRouteChange) return () => { router.events.off('hashChangeStart', handleRouteChange) } }, [router.asPath, router.events]) // clip item and give it a`show full text` button if we are overflowing useEffect(() => { const container = containerRef.current if (!container || overflowing) return function checkOverflow () { setOverflowing(container.scrollHeight > window.innerHeight * 2) } let resizeObserver if (!overflowing && 'ResizeObserver' in window) { resizeObserver = new window.ResizeObserver(checkOverflow).observe(container) } window.addEventListener('resize', checkOverflow) checkOverflow() return () => { window.removeEventListener('resize', checkOverflow) resizeObserver?.disconnect() } }, [containerRef.current, setOverflowing]) const TextMediaOrLink = useCallback(props => { return }, [outlawed, imgproxyUrls, topLevel, rel]) const components = useMemo(() => ({ h1: ({ node, id, ...props }) =>

, h2: ({ node, id, ...props }) =>

, h3: ({ node, id, ...props }) =>

, h4: ({ node, id, ...props }) =>

, h5: ({ node, id, ...props }) =>

, h6: ({ node, id, ...props }) =>
, table: Table, p: P, code: Code, mention: Mention, sub: Sub, item: Item, footnote: Footnote, headlink: ({ node, href, ...props }) => , autolink: ({ href, ...props }) => , a: ({ node, href, children, ...props }) => { // if outlawed, render the link as text if (outlawed) { return href } // eslint-disable-next-line return {children} }, img: TextMediaOrLink, embed: Embed }), [outlawed, rel, TextMediaOrLink, topLevel]) const carousel = useCarousel() const markdownContent = useMemo(() => ( {children} ), [components, remarkPlugins, rehypePlugins, children, itemId]) const showOverflow = useCallback(() => setShow(true), [setShow]) return (
{ carousel && tab !== 'preview' ? markdownContent : {markdownContent} } {overflowing && !show && ( )}
) }, isEqual) function Mention ({ children, node, href, name, id }) { return ( {children} ) } function Sub ({ children, node, href, ...props }) { return {children} } function Item ({ children, node, href, id }) { return ( {children} ) } function Footnote ({ children, node, ...props }) { return ( {children} ) } function MediaLink ({ node, src, outlawed, imgproxyUrls, rel = UNKNOWN_LINK_REL, ...props }) { const url = IMGPROXY_URL_REGEXP.test(src) ? decodeProxyUrl(src) : src // if outlawed, render the media link as text if (outlawed) { return url } const srcSet = imgproxyUrls?.[url] return } function Table ({ node, ...props }) { return ( ) } function Code ({ node, inline, className, children, style, ...props }) { return inline ? ( {children} ) : ( {children} ) } function P ({ children, node, onlyImages, somethingBefore, somethingAfter, ...props }) { return (
{children}
) }