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'
import { useRouter } from 'next/router'
import Link from 'next/link'
export function SearchText ({ text }) {
return (
{
return
},
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)) {
if (props['data-footnote-ref'] || typeof props['data-footnote-backref'] !== 'undefined') {
return (
{text}
)
}
return (
{text}
)
}
try {
// parse internal links and show as #
const url = new URL(href)
const { pathname, searchParams } = url
// ignore empty parts which exist due to pathname starting with '/'
const emptyPart = part => !!part
const parts = pathname.split('/').filter(emptyPart)
if (parts[0] === 'items' && /^[0-9]+$/.test(parts[1])) {
const itemId = parts[1]
// check for valid item page due to referral links like /items/123456/r/ekzyis
const itemPages = ['edit', 'ots', 'related']
const itemPage = itemPages.includes(parts[2]) ? parts[2] : null
if (itemPage) {
// parse https://stacker.news/items/1/related?commentId=2
// as #1/related
// and not #2
// since commentId will be ignored anyway
const linkText = `#${itemId}/${itemPage}`
return {linkText}
}
const commentId = searchParams.get('commentId')
const linkText = `#${commentId || itemId}`
return {linkText}
}
} catch {
// ignore invalid URLs
}
// if the link is to a youtube video, render the video
const youtube = href.match(/(https?:\/\/)?((www\.)?(youtube(-nocookie)?|youtube.googleapis)\.com.*(v\/|v=|vi=|vi\/|e\/|embed\/|user\/.*\/u\/\d+\/)|youtu\.be\/)(?[_0-9a-z-]+)((?:\?|&)(?:t|start)=(?\d+))?/i)
if (youtube?.groups?.id) {
return (
)
}
// assume the link is an image which will fallback to link if it's not
return {children}
},
img: Img
}}
remarkPlugins={[gfm, mention, sub]}
rehypePlugins={[rehypeInlineCodeProperty]}
>
{children}
{overflowing && !show &&
}