import styles from './text.module.css'
import ReactMarkdown from 'react-markdown'
import YouTube from 'react-youtube'
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, useEffect } 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'
import { rehypeInlineCodeProperty } from '../lib/md'
import { Button } from 'react-bootstrap'
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 ({ nofollow, imgproxyUrls, children, tab, ...outerProps }) {
const [overflowing, setOverflowing] = useState(false)
const [show, setShow] = useState(false)
const containerRef = useRef(null)
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])
// all the reactStringReplace calls are to facilitate search highlighting
const slugger = useRef(new GithubSlugger())
const Heading = useCallback(({ children, node, ...props }) => {
const [copied, setCopied] = useState(false)
const { noFragments, topLevel } = outerProps
const id = useMemo(() =>
noFragments ? undefined : slugger?.current?.slug(toString(node).replace(/[^\w\-\s]+/gi, '')), [node, noFragments, slugger])
const h = useMemo(() => {
if (topLevel) {
return node?.TagName
}
const h = parseInt(node?.tagName?.replace('h', '') || 0)
if (h < 4) return `h${h + 3}`
return 'h6'
}, [node, topLevel])
const Icon = copied ? Thumb : LinkIcon
return (
{React.createElement(h || node?.tagName, { 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'
/>
}
)
}, [outerProps, slugger.current])
const Table = useCallback(({ node, ...props }) =>
, [])
const Code = useCallback(({ node, inline, className, children, style, ...props }) => {
return inline
? (
{children}
)
: (
{children}
)
}, [])
const P = useCallback(({ children, node, ...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 (
)
})