{"version":3,"file":"Markdown.cjs","names":[],"sources":["../../../src/components/Markdown/Markdown.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines — the renderer maps every block and inline node the\n * parser can emit to an element, and the length is that mapping — one arm per node\n * kind, no branching to extract.\n */\nimport { Fragment, useMemo, useRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nimport { useScrollOverflow } from \"@/hooks/use-scroll-overflow\";\nimport { cn } from \"@/utils/cn\";\n\nimport { CodeBlock } from \"../CodeBlock\";\nimport {\n    parseMarkdown,\n    type MarkdownAlign,\n    type MarkdownBlock,\n    type MarkdownInline,\n} from \"./markdown-parse\";\nimport styles from \"./Markdown.module.css\";\n\n/** DOM attributes this component redefines. */\ntype OverriddenDomProps = \"children\";\n\nexport interface MarkdownProps extends Omit<HTMLAttributes<HTMLDivElement>, OverriddenDomProps> {\n    /** The Markdown source. */\n    source: string;\n    /**\n     * Heading level the document's `#` maps to. Default 2.\n     *\n     * A comment body rendered inside a page whose `<h1>` is the page title must not\n     * emit a second `<h1>`; shifting the whole scale keeps the outline valid.\n     */\n    headingOffset?: number;\n    /** Render fenced code with `CodeBlock` (copy button, line numbers). Default `true`. */\n    highlightCode?: boolean;\n    /** Show line numbers in fenced code. Default `false`. */\n    showLineNumbers?: boolean;\n    /** Extra props for every link — `target=\"_blank\"`, an analytics handler. */\n    linkProps?: HTMLAttributes<HTMLAnchorElement> & { target?: string; rel?: string };\n}\n\n/** Render inline nodes. */\nfunction renderInline(nodes: readonly MarkdownInline[]): ReactNode {\n    return nodes.map((node, index) => {\n        const key = index;\n        switch (node.type) {\n            case \"text\":\n                return <Fragment key={key}>{node.value}</Fragment>;\n            case \"strong\":\n                return <strong key={key}>{renderInline(node.children)}</strong>;\n            case \"em\":\n                return <em key={key}>{renderInline(node.children)}</em>;\n            case \"del\":\n                return <del key={key}>{renderInline(node.children)}</del>;\n            case \"code\":\n                return (\n                    <code key={key} className={styles.inlineCode}>\n                        {node.value}\n                    </code>\n                );\n            case \"image\":\n                return <img key={key} className={styles.image} src={node.src} alt={node.alt} />;\n            case \"break\":\n                return <br key={key} />;\n            default:\n                return null;\n        }\n    });\n}\n\n/** CSS `text-align` for a table column. */\nfunction alignOf(align: MarkdownAlign): { textAlign: \"left\" | \"center\" | \"right\" } | undefined {\n    return align ? { textAlign: align } : undefined;\n}\n\n/**\n * Rendered Markdown, from a node tree — never from an HTML string.\n *\n * `dangerouslySetInnerHTML` appears nowhere in this component. That is what makes\n * rendering somebody else's Markdown safe by construction rather than by a promise\n * about escaping: a `<script>` in a comment is four characters of text, because text\n * is all a React child can be.\n *\n * Link and image URLs go through a scheme **allowlist** (`http`, `https`, `mailto`,\n * `tel`, `sms`, plus relative), so `[click](javascript:alert(1))` renders as plain\n * text. A blocklist would have to enumerate every spelling of `javascript:` and\n * would miss one.\n *\n * @example\n * <Markdown source={comentario.corpo} linkProps={{ target: \"_blank\", rel: \"noreferrer\" }} />\n */\nexport function Markdown({\n    source,\n    headingOffset = 2,\n    highlightCode = true,\n    showLineNumbers = false,\n    linkProps,\n    className,\n    ...rest\n}: MarkdownProps) {\n    const blocks = useMemo(() => parseMarkdown(source), [source]);\n\n    return (\n        <div className={cn(styles.root, className)} {...rest}>\n            {blocks.map((block, index) => (\n                <Block\n                    key={index}\n                    block={block}\n                    headingOffset={headingOffset}\n                    highlightCode={highlightCode}\n                    showLineNumbers={showLineNumbers}\n                    linkProps={linkProps}\n                />\n            ))}\n        </div>\n    );\n}\n\ninterface BlockProps {\n    block: MarkdownBlock;\n    headingOffset: number;\n    highlightCode: boolean;\n    showLineNumbers: boolean;\n    linkProps?: MarkdownProps[\"linkProps\"];\n}\n\n/** One block, and its children. */\nfunction Block({ block, headingOffset, highlightCode, showLineNumbers, linkProps }: BlockProps) {\n    const children = (nodes: readonly MarkdownBlock[]): ReactNode =>\n        nodes.map((child, index) => (\n            <Block\n                key={index}\n                block={child}\n                headingOffset={headingOffset}\n                highlightCode={highlightCode}\n                showLineNumbers={showLineNumbers}\n                linkProps={linkProps}\n            />\n        ));\n\n    const inline = (nodes: readonly MarkdownInline[]): ReactNode =>\n        nodes.map((node, index) =>\n            node.type === \"link\" ? (\n                <a key={index} className={styles.link} href={node.href} {...linkProps}>\n                    {renderInline(node.children)}\n                </a>\n            ) : (\n                <Fragment key={index}>{renderInline([node])}</Fragment>\n            ),\n        );\n\n    switch (block.type) {\n        case \"heading\": {\n            const level = Math.min(6, Math.max(1, block.level + headingOffset - 1));\n            const Tag = `h${level}` as \"h1\";\n            return (\n                <Tag className={styles[`h${level}` as keyof typeof styles]}>\n                    {inline(block.children)}\n                </Tag>\n            );\n        }\n        case \"paragraph\":\n            return <p>{inline(block.children)}</p>;\n        case \"code\":\n            return highlightCode ? (\n                <CodeBlock\n                    code={block.value}\n                    language={block.language ?? undefined}\n                    showLineNumbers={showLineNumbers}\n                />\n            ) : (\n                <pre>\n                    <code>{block.value}</code>\n                </pre>\n            );\n        case \"quote\":\n            return <blockquote className={styles.quote}>{children(block.children)}</blockquote>;\n        case \"rule\":\n            return <hr className={styles.rule} />;\n        case \"list\":\n            return block.ordered ? (\n                <ol className={styles.list} start={block.start === 1 ? undefined : block.start}>\n                    {block.items.map((item, index) => (\n                        <li key={index} className={styles.item}>\n                            {children(item)}\n                        </li>\n                    ))}\n                </ol>\n            ) : (\n                <ul className={styles.list}>\n                    {block.items.map((item, index) => (\n                        <li key={index} className={styles.item}>\n                            {children(item)}\n                        </li>\n                    ))}\n                </ul>\n            );\n        case \"table\":\n            return <Table block={block} inline={inline} />;\n        default:\n            return null;\n    }\n}\n\n/**\n * A table, in its own scroll box.\n *\n * The box only becomes a tab stop while it actually overflows: a scroll container\n * with nothing focusable inside cannot be reached by keyboard, and adding the stop\n * unconditionally would pollute the tab order with one entry per table on the page.\n */\nfunction Table({\n    block,\n    inline,\n}: {\n    block: Extract<MarkdownBlock, { type: \"table\" }>;\n    inline: (nodes: readonly MarkdownInline[]) => ReactNode;\n}) {\n    const scroll = useRef<HTMLDivElement | null>(null);\n    const overflowing = useScrollOverflow(scroll, \"horizontal\");\n\n    return (\n        <div\n            ref={scroll}\n            className={styles.tableScroll}\n            tabIndex={overflowing ? 0 : undefined}\n            role={overflowing ? \"region\" : undefined}\n            aria-label={overflowing ? \"Tabela rolável\" : undefined}\n        >\n            <table className={styles.table}>\n                <thead>\n                    <tr>\n                        {block.head.map((cell, index) => (\n                            <th key={index} style={alignOf(block.align[index] ?? null)}>\n                                {inline(cell)}\n                            </th>\n                        ))}\n                    </tr>\n                </thead>\n                <tbody>\n                    {block.rows.map((row, rowIndex) => (\n                        <tr key={rowIndex}>\n                            {row.map((cell, cellIndex) => (\n                                <td key={cellIndex} style={alignOf(block.align[cellIndex] ?? null)}>\n                                    {inline(cell)}\n                                </td>\n                            ))}\n                        </tr>\n                    ))}\n                </tbody>\n            </table>\n        </div>\n    );\n}\n"],"mappings":"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"}