import { Fragment, useMemo } from 'react' import type { ReactNode } from 'react' /** * Dil anahtarlarında tutulan düz açıklama metnini okunur bir yapıya çevirir. * Metnin kendisi HTML taşımaz; biçim yalnızca aşağıdaki sözleşmeden türetilir, * böylece veritabanından gelen içerik sayfaya HTML olarak enjekte edilmez. * * - `\n` ile ayrılmış her satır bir blok, * - `• ` ile başlayan ardışık satırlar madde listesi, * - `Etiket: ...` biçimindeki satır başlığı kalın, * - madde içindeki `Ad → açıklama` kalıbında ok öncesi kalın, * - nokta içeren kod benzeri belirteçler ve adresler tek aralıklı rozet. */ const CODE_TOKEN = /((?:https?:\/\/\S+)|(?:[A-Za-z][\w-]*(?:\.[\w*-]+)+))/g const LABEL_LINE = /^([^:]{2,48}):\s+(.*)$/ const BULLET_HEAD = /^(.+?)\s→\s(.*)$/ const withCode = (text: string, keyPrefix: string): ReactNode[] => text.split(CODE_TOKEN).map((part, index) => { if (index % 2 === 0) return {part} // Cümle sonundaki noktalama rozetin içinde kalmasın. const trimmed = part.replace(/[.,;:]+$/, '') // "e.g." gibi kısaltmalar kod değildir; rozet yalnızca adres ve tanımlayıcılara. if (!/^https?:/.test(trimmed) && !/[A-Z]/.test(trimmed)) { return {part} } const rest = part.slice(trimmed.length) return ( {trimmed} {rest} ) }) const Bullet = ({ text, itemKey }: { text: string; itemKey: string }) => { const head = BULLET_HEAD.exec(text) if (!head) { return
  • {withCode(text, itemKey)}
  • } return (
  • {head[1]} {' → '} {withCode(head[2], itemKey)}
  • ) } const RichText = ({ text }: { text: string }) => { const blocks = useMemo(() => { const lines = text.split('\n').filter((line) => line.trim().length > 0) const grouped: { type: 'line' | 'list'; items: string[] }[] = [] lines.forEach((line) => { const trimmed = line.trim() const isBullet = trimmed.startsWith('•') const last = grouped[grouped.length - 1] if (isBullet && last?.type === 'list') { last.items.push(trimmed.replace(/^•\s*/, '')) } else if (isBullet) { grouped.push({ type: 'list', items: [trimmed.replace(/^•\s*/, '')] }) } else { grouped.push({ type: 'line', items: [trimmed] }) } }) return grouped }, [text]) return (
    {blocks.map((block, blockIndex) => { if (block.type === 'list') { return ( ) } const line = block.items[0] const label = LABEL_LINE.exec(line) if (blockIndex === 0) { return (

    {withCode(line, `b${blockIndex}`)}

    ) } if (!label) { return

    {withCode(line, `b${blockIndex}`)}

    } return (

    {label[1]}: {withCode(label[2], `b${blockIndex}`)}

    ) })}
    ) } RichText.displayName = 'TooltipRichText' export default RichText