{"version":3,"file":"CodeBlock.cjs","names":[],"sources":["../../../src/components/CodeBlock/CodeBlock.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — code and language drive the scanner; filename,\n * showLineNumbers, highlightLines, maxHeight and wrap are the presentation a docs\n * page picks per block, and copyable plus label are the affordance and its\n * accessible name. All independent, all used in the SDK's own docs gallery.\n */\nimport { Fragment, useMemo } from \"react\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport { CopyButton } from \"../CopyButton\";\nimport { resolveLanguage, tokenizeLines } from \"./tokenize\";\nimport styles from \"./CodeBlock.module.css\";\n\nexport interface CodeBlockProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n    /** The source to display. Leading and trailing blank lines are trimmed. */\n    code: string;\n    /** Grammar name or alias (`ts`, `bash`, `json`…). Unknown values render plain. */\n    language?: string;\n    /** Shown in the header, usually a file path. */\n    filename?: ReactNode;\n    /** Number the lines. Default `false`. */\n    showLineNumbers?: boolean;\n    /** 1-based line numbers to mark as the point of the snippet. */\n    highlightLines?: readonly number[];\n    /** Offer a copy button. Default `true`. */\n    copyable?: boolean;\n    /** Cap the height in px or any CSS length; the body scrolls past it. */\n    maxHeight?: number | string;\n    /** Wrap long lines instead of scrolling sideways. Default `false`. */\n    wrap?: boolean;\n    /**\n     * Accessible name for the code region. Defaults to naming the language, and\n     * to the filename when there is one.\n     */\n    label?: string;\n}\n\n/**\n * A read-only code sample: syntax colours, optional line numbers, copy button.\n *\n * The `<pre>` is always focusable. A code block is scrollable and holds nothing\n * focusable inside, so without a tab stop a keyboard user can see there is more\n * code past the edge and has no way to reach it. It is the one scroll container\n * where the stop is unconditional rather than measured — a code sample is\n * expected to be reachable, read and selected on its own.\n *\n * Colours come from an approximate scanner, not a parser — see {@link tokenize}.\n *\n * @example\n * <CodeBlock code={snippet} language=\"ts\" filename=\"src/main.ts\" showLineNumbers />\n * <CodeBlock code={log} language=\"bash\" maxHeight={280} />\n */\nexport function CodeBlock({\n    code,\n    language,\n    filename,\n    showLineNumbers = false,\n    highlightLines,\n    copyable = true,\n    maxHeight,\n    wrap = false,\n    label,\n    className,\n    ...rest\n}: CodeBlockProps) {\n    const trimmed = useMemo(() => code.replace(/^\\n+/, \"\").replace(/\\s+$/, \"\"), [code]);\n    const lines = useMemo(() => tokenizeLines(trimmed, language), [trimmed, language]);\n    const resolved = resolveLanguage(language);\n    const marked = useMemo(() => new Set(highlightLines ?? []), [highlightLines]);\n\n    const name =\n        label ??\n        (filename && typeof filename === \"string\"\n            ? `Código: ${filename}`\n            : `Bloco de código${resolved === \"plain\" ? \"\" : ` em ${resolved}`}`);\n\n    const hasHeader = Boolean(filename) || copyable;\n\n    return (\n        <div className={cn(styles.wrapper, className)} {...rest}>\n            {hasHeader && (\n                <div className={styles.header}>\n                    <span className={styles.filename}>\n                        {filename ?? (resolved === \"plain\" ? \"\" : resolved)}\n                    </span>\n                    {copyable && <CopyButton value={trimmed} className={styles.copy} />}\n                </div>\n            )}\n\n            <pre\n                className={cn(styles.pre, wrap && styles.wrap)}\n                style={{ maxHeight }}\n                tabIndex={0}\n                role=\"group\"\n                aria-label={name}\n            >\n                <code className={styles.code} data-language={resolved}>\n                    {lines.map((tokens, index) => (\n                        <Fragment key={index}>\n                            <span\n                                className={cn(styles.line, marked.has(index + 1) && styles.marked)}\n                            >\n                                {showLineNumbers && (\n                                    /*\n                                     * The number is decoration: it must not land in the\n                                     * clipboard when the reader selects the snippet, and a\n                                     * screen reader announcing \"one const two import\" adds\n                                     * nothing. `user-select: none` in CSS plus this.\n                                     */\n                                    <span className={styles.lineNumber} aria-hidden=\"true\">\n                                        {index + 1}\n                                    </span>\n                                )}\n                                <span className={styles.lineContent}>\n                                    {tokens.map((token, tokenIndex) => (\n                                        <span\n                                            key={tokenIndex}\n                                            className={styles[token.kind]}\n                                            data-token={token.kind}\n                                        >\n                                            {token.value}\n                                        </span>\n                                    ))}\n                                </span>\n                            </span>\n                            {/*\n                             * A real newline character, and outside the line box.\n                             * It has to be real so that selecting the snippet by\n                             * hand and copying yields the source instead of one\n                             * run-together line. It has to be outside because the\n                             * line is an inline-block: a newline within one is\n                             * consumed inside that box, and every line ends up\n                             * side by side on a single row. The last one is\n                             * dropped so the block does not end in blank space.\n                             */}\n                            {index < lines.length - 1 ? \"\\n\" : null}\n                        </Fragment>\n                    ))}\n                </code>\n            </pre>\n        </div>\n    );\n}\n"],"mappings":"sMAsDA,SAAgB,EAAU,CACtB,OACA,WACA,WACA,kBAAkB,GAClB,iBACA,WAAW,GACX,YACA,OAAO,GACP,QACA,YACA,GAAG,GACY,CACf,IAAM,GAAA,EAAU,EAAA,QAAA,KAAc,EAAK,QAAQ,OAAQ,EAAE,CAAC,CAAC,QAAQ,OAAQ,EAAE,EAAG,CAAC,CAAI,CAAC,EAC5E,GAAA,EAAQ,EAAA,QAAA,KAAc,EAAA,cAAc,EAAS,CAAQ,EAAG,CAAC,EAAS,CAAQ,CAAC,EAC3E,EAAW,EAAA,gBAAgB,CAAQ,EACnC,GAAA,EAAS,EAAA,QAAA,KAAc,IAAI,IAAI,GAAkB,CAAC,CAAC,EAAG,CAAC,CAAc,CAAC,EAEtE,EACF,IACC,GAAY,OAAO,GAAa,SAC3B,WAAW,IACX,kBAAkB,IAAa,QAAU,GAAK,OAAO,OAEzD,EAAY,EAAQ,GAAa,EAEvC,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAS,EAAG,GAAI,EAAnD,SAAA,CACK,IACG,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,OAAvB,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,SACnB,SAAA,IAAa,IAAa,QAAU,GAAK,EACxC,CAAA,EACL,IAAY,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAY,MAAO,EAAS,UAAW,EAAA,QAAO,IAAO,CAAA,CACjE,CAGT,CAAA,GAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,IAAK,GAAQ,EAAA,QAAO,IAAI,EAC7C,MAAO,CAAE,WAAU,EACnB,SAAU,EACV,KAAK,QACL,aAAY,EAEZ,UAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,KAAM,gBAAe,EACxC,SAAA,EAAM,KAAK,EAAQ,KAChB,EAAA,EAAA,KAAA,CAAC,EAAA,SAAD,CAAA,SAAA,EACI,EAAA,EAAA,KAAA,CAAC,OAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,EAAO,IAAI,EAAQ,CAAC,GAAK,EAAA,QAAO,MAAM,EADrE,SAAA,CAGK,IAOG,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,WAAY,cAAY,OAC3C,SAAA,EAAQ,CACP,CAAA,GAEV,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,YACnB,SAAA,EAAO,KAAK,EAAO,KAChB,EAAA,EAAA,IAAA,CAAC,OAAD,CAEI,UAAW,EAAA,QAAO,EAAM,MACxB,aAAY,EAAM,KAEjB,SAAA,EAAM,KACL,EALG,CAKH,CACT,CACC,CAAA,CACJ,CAWL,CAAA,EAAA,EAAQ,EAAM,OAAS,EAAI;EAAO,IAC7B,CAAA,EAtCK,CAsCL,CACb,CACC,CAAA,CACL,CAAA,CACJ,GAEb"}