@republicroad/seal-editor 1.37.0 → 1.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"simulate-run-detail-qZE4KheI.js","names":[],"sources":["../src/reui/code-block/code-block-highlight.tsx","../src/reui/code-block/code-block.tsx","../src/components/decision-graph/simulator/simulate-run-detail.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport type { ShikiTransformer } from 'shiki';\n\n/**\n * One themed slice of a line. `color`/`colorDark` emit as `--cb-c`/`--cb-cd`\n * custom properties, so one rule pair on the `<pre>` theme-switches the whole\n * block. An uncoloured token keeps both undefined and renders as a bare text\n * node with no wrapper.\n */\nexport type CodeBlockToken = {\n content: string;\n color?: string;\n colorDark?: string;\n fontStyle?: CodeBlockFontStyle;\n word?: boolean;\n};\n\nexport type CodeBlockFontStyle = 'italic' | 'bold' | 'underline';\n\nexport type CodeBlockDiffKind = 'add' | 'remove';\n\nexport type CodeBlockLevel = 'error' | 'warning' | 'info';\n\n/**\n * Per-line presentation state, resolved once at highlight time.\n *\n * Two sources merge here: the props on the root, and the classes a shiki\n * transformer put on the line, so `@shikijs/transformers` notation such as\n * `[!code ++]` reaches the same place without the renderer knowing shiki\n * exists. Props win on conflict, because notation lives in the source string,\n * which a consumer often does not control.\n */\nexport type CodeBlockLineState = {\n highlighted?: boolean;\n diff?: CodeBlockDiffKind;\n focused?: boolean;\n level?: CodeBlockLevel;\n};\n\n/**\n * A rendered line. `number` is the DISPLAYED number (offset by `startLine`);\n * line specs address source lines, so changing `startLine` never invalidates\n * them. Instances are reused across passes when unchanged, which is what lets\n * the row be a plain `React.memo` and a streamed chunk cost one re-render.\n */\nexport type CodeBlockLine = {\n tokens: CodeBlockToken[];\n number: number;\n text: string;\n state?: CodeBlockLineState;\n /**\n * Replaces the counter-driven gutter number for this row - a unified patch\n * shows \"old new\" pairs, a hunk header shows dots. Rendered verbatim.\n */\n gutter?: string;\n};\n\n/** Source lines, as `[2, 3, 4]` or as a range string such as `\"2-4,7\"`. */\nexport type CodeBlockLineSpec = number[] | string;\n\nexport type CodeBlockDiffSpec = {\n added?: CodeBlockLineSpec;\n removed?: CodeBlockLineSpec;\n};\n\nexport type CodeBlockLevelSpec = Partial<Record<CodeBlockLevel, CodeBlockLineSpec>>;\n\n/** A word to mark, optionally restricted to some source lines. */\nexport type CodeBlockWordSpec = string | { word: string; lines?: CodeBlockLineSpec };\n\nexport type CodeBlockThemes = { light: string; dark: string };\n\n/**\n * shiki's own transformer type, imported type-only so it erases at compile\n * time and costs the bundle nothing. The previous structural stand-in\n * (`Record<string, unknown>`) rejected every real `ShikiTransformer`: an\n * interface without an index signature is not assignable to it.\n */\nexport type CodeBlockTransformer = ShikiTransformer;\n\nexport type CodeBlockHighlightOptions = {\n language?: string;\n themes?: CodeBlockThemes;\n transformers?: CodeBlockTransformer[];\n startLine?: number;\n /**\n * Distinguishes same-signature blocks in the line-reuse cache. Without it,\n * two identically-configured streams evict each other's previous document.\n * The root passes its own instance id.\n */\n instanceKey?: string;\n highlightedLines?: CodeBlockLineSpec;\n highlightedWords?: CodeBlockWordSpec[];\n focusedLines?: CodeBlockLineSpec;\n diff?: CodeBlockDiffSpec;\n lineLevels?: CodeBlockLevelSpec;\n};\n\n/** What a `CodeBlockLineActions` render prop receives. */\nexport type CodeBlockLineActionContext = {\n line: number;\n text: string;\n state?: CodeBlockLineState;\n};\n\nexport type CodeBlockLineActionsRender = (context: CodeBlockLineActionContext) => ReactNode;\n\n/* -------------------------------------------------------------------------- */\n/* Languages */\n/* -------------------------------------------------------------------------- */\n\n/**\n * A STATIC map, never ``import(`shiki/langs/${lang}.mjs`)``: a template import\n * makes bundlers bundle all ~200 grammars. Written out, each entry is its own\n * lazy chunk. To add a language, add a line: that is the intended extension\n * point of this file.\n */\nexport const codeBlockLanguages: Record<string, () => Promise<unknown>> = {\n bash: () => import('shiki/langs/bash.mjs'),\n c: () => import('shiki/langs/c.mjs'),\n cpp: () => import('shiki/langs/cpp.mjs'),\n csharp: () => import('shiki/langs/csharp.mjs'),\n css: () => import('shiki/langs/css.mjs'),\n diff: () => import('shiki/langs/diff.mjs'),\n docker: () => import('shiki/langs/docker.mjs'),\n go: () => import('shiki/langs/go.mjs'),\n graphql: () => import('shiki/langs/graphql.mjs'),\n html: () => import('shiki/langs/html.mjs'),\n java: () => import('shiki/langs/java.mjs'),\n javascript: () => import('shiki/langs/javascript.mjs'),\n json: () => import('shiki/langs/json.mjs'),\n jsx: () => import('shiki/langs/jsx.mjs'),\n kotlin: () => import('shiki/langs/kotlin.mjs'),\n markdown: () => import('shiki/langs/markdown.mjs'),\n php: () => import('shiki/langs/php.mjs'),\n python: () => import('shiki/langs/python.mjs'),\n ruby: () => import('shiki/langs/ruby.mjs'),\n rust: () => import('shiki/langs/rust.mjs'),\n scss: () => import('shiki/langs/scss.mjs'),\n shell: () => import('shiki/langs/shellscript.mjs'),\n sql: () => import('shiki/langs/sql.mjs'),\n swift: () => import('shiki/langs/swift.mjs'),\n toml: () => import('shiki/langs/toml.mjs'),\n tsx: () => import('shiki/langs/tsx.mjs'),\n typescript: () => import('shiki/langs/typescript.mjs'),\n vue: () => import('shiki/langs/vue.mjs'),\n yaml: () => import('shiki/langs/yaml.mjs'),\n};\n\n/** Spellings a consumer is likely to pass, mapped onto the map above. */\nconst LANGUAGE_ALIASES: Record<string, string> = {\n 'c++': 'cpp',\n 'c#': 'csharp',\n 'cs': 'csharp',\n 'dockerfile': 'docker',\n 'htm': 'html',\n 'js': 'javascript',\n 'jsonc': 'json',\n 'md': 'markdown',\n 'mdx': 'markdown',\n 'py': 'python',\n 'rb': 'ruby',\n 'rs': 'rust',\n 'sh': 'shell',\n 'shellscript': 'shell',\n 'ts': 'typescript',\n 'yml': 'yaml',\n 'zsh': 'shell',\n};\n\nexport const codeBlockThemes: Record<string, () => Promise<unknown>> = {\n 'github-light': () => import('shiki/themes/github-light.mjs'),\n 'github-dark': () => import('shiki/themes/github-dark.mjs'),\n /**\n * Design-token theming: every token colour becomes a `var(--code-token-*)`\n * reference, so the palette lives in the consumer's stylesheet and follows\n * their themes. Pass as BOTH sides. Variables consumed (prefix `--code-`):\n * foreground, token-constant, token-string, token-comment, token-keyword,\n * token-parameter, token-function, token-string-expression,\n * token-punctuation, token-link.\n */\n 'css-variables': async () => {\n const { createCssVariablesTheme } = await import('shiki/core');\n return createCssVariablesTheme({\n name: 'css-variables',\n variablePrefix: '--code-',\n fontStyle: true,\n });\n },\n};\n\nexport const DEFAULT_CODE_BLOCK_THEMES: CodeBlockThemes = {\n light: 'github-light',\n dark: 'github-dark',\n};\n\n/** Resolves an alias and reports whether the grammar is actually available. */\nexport function resolveCodeBlockLanguage(language?: string): string | undefined {\n if (!language) return undefined;\n const normalized = language.trim().toLowerCase();\n const resolved = LANGUAGE_ALIASES[normalized] ?? normalized;\n return resolved in codeBlockLanguages ? resolved : undefined;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Pure helpers */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Turns `[2, 3]` or `\"2-4,7\"` into a set of source line numbers.\n *\n * Deliberately total: a reversed range, a negative bound or outright garbage\n * yields an empty set rather than throwing. These values often come from user\n * content or from a model, and a code viewer that crashes on a bad range is\n * worse than one that shows no highlight.\n */\nexport function parseLineSpec(spec?: CodeBlockLineSpec): Set<number> {\n const out = new Set<number>();\n if (!spec) return out;\n\n if (Array.isArray(spec)) {\n for (const value of spec) {\n if (Number.isInteger(value) && value > 0) out.add(value);\n }\n return out;\n }\n\n for (const part of spec.split(',')) {\n const trimmed = part.trim();\n if (!trimmed) continue;\n\n const range = trimmed.match(/^(\\d+)\\s*-\\s*(\\d+)$/);\n if (range) {\n const start = Number(range[1]);\n const end = Number(range[2]);\n if (start > 0 && end >= start) {\n for (let line = start; line <= end; line += 1) out.add(line);\n }\n continue;\n }\n\n if (/^\\d+$/.test(trimmed)) {\n const single = Number(trimmed);\n if (single > 0) out.add(single);\n }\n }\n\n return out;\n}\n\n/** Normalises line endings so every downstream offset is LF-based. */\nexport function normalizeCode(code: string): string {\n return code.replace(/\\r\\n?/g, '\\n');\n}\n\n/**\n * Character ranges for `highlightedWords`, as shiki `decorations`.\n *\n * shiki rejects overlapping decorations, so a later match that would overlap an\n * earlier one is dropped rather than passed through to throw.\n */\nexport function buildWordDecorations(\n code: string,\n words?: CodeBlockWordSpec[],\n): { start: number; end: number; properties: { class: string } }[] {\n if (!words?.length) return [];\n\n const lineStarts: number[] = [0];\n for (let index = 0; index < code.length; index += 1) {\n if (code[index] === '\\n') lineStarts.push(index + 1);\n }\n\n const lineAt = (offset: number) => {\n let low = 0;\n let high = lineStarts.length - 1;\n while (low < high) {\n const mid = Math.ceil((low + high) / 2);\n if (lineStarts[mid] <= offset) low = mid;\n else high = mid - 1;\n }\n return low + 1;\n };\n\n const taken: { start: number; end: number }[] = [];\n const out: { start: number; end: number; properties: { class: string } }[] = [];\n\n for (const entry of words) {\n const word = typeof entry === 'string' ? entry : entry.word;\n if (!word) continue;\n const limit = typeof entry === 'string' ? undefined : parseLineSpec(entry.lines);\n\n let from = code.indexOf(word);\n while (from !== -1) {\n const to = from + word.length;\n const withinLimit = !limit || limit.size === 0 || limit.has(lineAt(from));\n const overlaps = taken.some((r) => from < r.end && to > r.start);\n\n if (withinLimit && !overlaps) {\n taken.push({ start: from, end: to });\n out.push({ start: from, end: to, properties: { class: 'cb-word' } });\n }\n from = code.indexOf(word, from + word.length);\n }\n }\n\n return out.sort((a, b) => a.start - b.start);\n}\n\n/**\n * Strips `[!code ...]` notation from a copy payload: the transformer already\n * drops it from RENDERED output, but the raw `code` string would paste the\n * comment into someone's editor.\n */\nexport function stripNotationComments(code: string): string {\n return code\n .split('\\n')\n .map((line) => line.replace(/\\s*(?:\\/\\/|#|--|;|%|<!--|\\/\\*)\\s*\\[!code[^\\]]*\\]\\s*(?:-->|\\*\\/)?\\s*$/, ''))\n .join('\\n');\n}\n\n/**\n * Plain, unhighlighted lines. One token per line, no colour.\n *\n * This is both the `highlight={false}` renderer and the first paint of a\n * streaming block, so the tail of a stream is readable before its grammar pass\n * lands and no frame is ever blank.\n */\nexport function toPlainLines(code: string, startLine = 1): CodeBlockLine[] {\n return normalizeCode(code)\n .split('\\n')\n .map((text, index) => ({\n tokens: text ? [{ content: text }] : [],\n number: startLine + index,\n text,\n }));\n}\n\n/* -------------------------------------------------------------------------- */\n/* Engine */\n/* -------------------------------------------------------------------------- */\n\ntype HighlighterLike = {\n codeToHast: (code: string, options: Record<string, unknown>) => unknown;\n getLoadedLanguages: () => string[];\n loadLanguage: (lang: unknown) => Promise<void>;\n loadTheme: (theme: unknown) => Promise<void>;\n};\n\nlet highlighterPromise: Promise<HighlighterLike> | null = null;\nconst loadedLanguages = new Set<string>();\nconst loadedThemes = new Set<string>();\n\n/**\n * One highlighter per page, on the JavaScript regex engine: oniguruma needs\n * WebAssembly, which forces `'wasm-unsafe-eval'` into every consumer's CSP.\n * `forgiving` keeps an inexpressible grammar pattern from taking the block\n * down.\n */\nasync function loadHighlighter() {\n if (!highlighterPromise) {\n highlighterPromise = (async () => {\n const [{ createHighlighterCore }, { createJavaScriptRegexEngine }] = await Promise.all([\n import('shiki/core'),\n import('shiki/engine/javascript'),\n ]);\n\n return (await createHighlighterCore({\n themes: [],\n langs: [],\n engine: createJavaScriptRegexEngine({ forgiving: true }),\n })) as unknown as HighlighterLike;\n })();\n }\n\n return highlighterPromise;\n}\n\n/**\n * Themes register per name, on demand, exactly like languages.\n *\n * Baking the FIRST caller's pair into the singleton looks correct until a page\n * holds two blocks with different `themes` props: the second silently renders\n * in the first one's colours, because the singleton never learns about the\n * request. Loading by name makes every block's prop actually mean something.\n */\nconst warnedThemes = new Set<string>();\n\nasync function ensureTheme(highlighter: HighlighterLike, name: string, side: 'light' | 'dark' = 'light') {\n /* Falls back to the MATCHING side (an unknown dark theme used to fall back\n to github-light, silently rendering light colours in dark mode). */\n const resolved = name in codeBlockThemes ? name : DEFAULT_CODE_BLOCK_THEMES[side];\n if (resolved !== name && process.env.NODE_ENV !== 'production' && !warnedThemes.has(name)) {\n warnedThemes.add(name);\n console.warn(\n `[code-block] Unknown theme \"${name}\" - falling back to \"${resolved}\". ` +\n 'Register it in codeBlockThemes to use it.',\n );\n }\n if (loadedThemes.has(resolved)) return resolved;\n await highlighter.loadTheme(await codeBlockThemes[resolved]());\n loadedThemes.add(resolved);\n return resolved;\n}\n\nasync function ensureLanguage(highlighter: HighlighterLike, language: string) {\n if (loadedLanguages.has(language)) return;\n const loader = codeBlockLanguages[language];\n if (!loader) return;\n await highlighter.loadLanguage(await loader());\n loadedLanguages.add(language);\n}\n\n/* -------------------------------------------------------------------------- */\n/* hast to normalised lines */\n/* -------------------------------------------------------------------------- */\n\ntype HastNode = {\n type: string;\n tagName?: string;\n value?: string;\n properties?: Record<string, unknown>;\n children?: HastNode[];\n};\n\nconst FONT_STYLE_BY_DECLARATION: Record<string, CodeBlockFontStyle> = {\n 'font-style:italic': 'italic',\n 'font-weight:bold': 'bold',\n 'text-decoration:underline': 'underline',\n};\n\n/** Splits shiki's inline `style` string into the fields a token carries. */\nfunction readTokenStyle(style: unknown): Omit<CodeBlockToken, 'content'> {\n if (typeof style !== 'string') return {};\n\n const out: Omit<CodeBlockToken, 'content'> = {};\n for (const declaration of style.split(';')) {\n const trimmed = declaration.trim();\n if (!trimmed) continue;\n\n const separator = trimmed.indexOf(':');\n if (separator === -1) continue;\n\n const property = trimmed.slice(0, separator).trim();\n const value = trimmed.slice(separator + 1).trim();\n\n if (property === 'color') out.color = value;\n else if (property === '--shiki-dark') out.colorDark = value;\n else {\n const fontStyle = FONT_STYLE_BY_DECLARATION[`${property}:${value}`];\n if (fontStyle) out.fontStyle = fontStyle;\n }\n }\n return out;\n}\n\n/**\n * shiki emits raw `class` (string or array), not hast's `className`; reading\n * only `className` finds nothing, which looks like a transformer that never\n * ran.\n */\nfunction classListOf(node: HastNode): string[] {\n const value = node.properties?.class ?? node.properties?.className;\n if (Array.isArray(value)) return value.map(String);\n if (typeof value === 'string') return value.split(/\\s+/).filter(Boolean);\n return [];\n}\n\n/** Maps the classes shiki transformers put on a line onto line state. */\nfunction stateFromClasses(classes: string[]): CodeBlockLineState | undefined {\n const state: CodeBlockLineState = {};\n if (classes.includes('highlighted')) state.highlighted = true;\n if (classes.includes('focused')) state.focused = true;\n if (classes.includes('diff')) {\n if (classes.includes('add')) state.diff = 'add';\n else if (classes.includes('remove')) state.diff = 'remove';\n }\n for (const level of ['error', 'warning', 'info'] as const) {\n if (classes.includes(level)) state.level = level;\n }\n return Object.keys(state).length ? state : undefined;\n}\n\nfunction collectTokens(node: HastNode, out: CodeBlockToken[], inWord: boolean): void {\n for (const child of node.children ?? []) {\n if (child.type === 'text') {\n if (!child.value) continue;\n out.push(inWord ? { content: child.value, word: true } : { content: child.value });\n continue;\n }\n if (child.type !== 'element') continue;\n\n const classes = classListOf(child);\n const childInWord = inWord || classes.includes('cb-word');\n const style = readTokenStyle(child.properties?.style);\n const hasStyle = Boolean(style.color || style.colorDark || style.fontStyle);\n\n /* A styled leaf is a token; a wrapper (a decoration span) is descended into\n so its own children keep their individual colours. */\n const onlyText = (child.children ?? []).every((grandChild) => grandChild.type === 'text');\n\n if (hasStyle && onlyText) {\n const content = (child.children ?? []).map((c) => c.value ?? '').join('');\n if (!content) continue;\n out.push({\n content,\n ...style,\n ...(childInWord ? { word: true } : {}),\n });\n continue;\n }\n\n collectTokens(child, out, childInWord);\n }\n}\n\nfunction findCodeElement(root: HastNode): HastNode | undefined {\n if (root.type === 'element' && root.tagName === 'code') return root;\n for (const child of root.children ?? []) {\n const found = findCodeElement(child);\n if (found) return found;\n }\n return undefined;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Identity reuse */\n/* -------------------------------------------------------------------------- */\n\nconst MAX_TRACKED_DOCUMENTS = 24;\n\n/**\n * Previous result per option signature, so a growing stream can reuse lines.\n * Keyed WITH the caller's instanceKey: without it, two same-configured blocks\n * evicted each other's entry on every interleaved pass. Bounded above because\n * module state outlives requests on the server.\n */\nconst previousDocuments = new Map<string, CodeBlockLine[]>();\n\nfunction sameLine(a: CodeBlockLine, b: CodeBlockLine): boolean {\n if (a.number !== b.number || a.text !== b.text) return false;\n if (a.tokens.length !== b.tokens.length) return false;\n if (JSON.stringify(a.state ?? null) !== JSON.stringify(b.state ?? null)) {\n return false;\n }\n for (let index = 0; index < a.tokens.length; index += 1) {\n const left = a.tokens[index];\n const right = b.tokens[index];\n if (\n left.content !== right.content ||\n left.color !== right.color ||\n left.colorDark !== right.colorDark ||\n left.fontStyle !== right.fontStyle ||\n left.word !== right.word\n ) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Swaps freshly built lines for the previous pass's objects where nothing\n * changed. This is why streaming is cheap: the row is a reference-equality\n * `memo`, so without this, appending one token to a 400 line file re-renders\n * 400 subtrees per chunk.\n */\nfunction reuseUnchangedLines(key: string, next: CodeBlockLine[]): CodeBlockLine[] {\n const previous = previousDocuments.get(key);\n\n if (previous) {\n for (let index = 0; index < next.length; index += 1) {\n const before = previous[index];\n if (before && sameLine(before, next[index])) next[index] = before;\n }\n }\n\n previousDocuments.set(key, next);\n if (previousDocuments.size > MAX_TRACKED_DOCUMENTS) {\n const oldest = previousDocuments.keys().next().value;\n if (oldest !== undefined) previousDocuments.delete(oldest);\n }\n\n return next;\n}\n\n/* -------------------------------------------------------------------------- */\n/* highlightCode */\n/* -------------------------------------------------------------------------- */\n\nfunction applyPropState(lines: CodeBlockLine[], options: CodeBlockHighlightOptions): void {\n const highlighted = parseLineSpec(options.highlightedLines);\n const focused = parseLineSpec(options.focusedLines);\n const added = parseLineSpec(options.diff?.added);\n const removed = parseLineSpec(options.diff?.removed);\n const levels = {\n error: parseLineSpec(options.lineLevels?.error),\n warning: parseLineSpec(options.lineLevels?.warning),\n info: parseLineSpec(options.lineLevels?.info),\n };\n\n lines.forEach((line, index) => {\n const source = index + 1;\n const state: CodeBlockLineState = { ...line.state };\n\n if (highlighted.has(source)) state.highlighted = true;\n if (focused.has(source)) state.focused = true;\n if (added.has(source)) state.diff = 'add';\n else if (removed.has(source)) state.diff = 'remove';\n for (const level of ['error', 'warning', 'info'] as const) {\n if (levels[level].has(source)) state.level = level;\n }\n\n line.state = Object.keys(state).length ? state : undefined;\n });\n}\n\n/**\n * Highlights `code` into the renderer's line shape. Built on `codeToHast` so\n * consumer transformers run and their line classes land in `line.state`,\n * making prop state and `[!code ++]` notation one feature, not two code\n * paths. Safe in a server component: the result is plain JSON.\n */\nexport async function highlightCode(code: string, options: CodeBlockHighlightOptions = {}): Promise<CodeBlockLine[]> {\n const source = normalizeCode(code);\n const startLine = options.startLine ?? 1;\n const language = resolveCodeBlockLanguage(options.language);\n\n if (!language) return toPlainLines(source, startLine);\n\n const themes = options.themes ?? DEFAULT_CODE_BLOCK_THEMES;\n const signature = JSON.stringify([\n options.instanceKey ?? null,\n language,\n themes,\n startLine,\n options.highlightedLines ?? null,\n options.highlightedWords ?? null,\n options.focusedLines ?? null,\n options.diff ?? null,\n options.lineLevels ?? null,\n (options.transformers ?? []).length,\n ]);\n\n let root: HastNode;\n try {\n const highlighter = await loadHighlighter();\n const [light, dark] = await Promise.all([\n ensureTheme(highlighter, themes.light),\n ensureTheme(highlighter, themes.dark),\n ]);\n await ensureLanguage(highlighter, language);\n\n root = highlighter.codeToHast(source, {\n lang: language,\n themes: { light, dark },\n defaultColor: 'light',\n cssVariablePrefix: '--shiki-',\n decorations: buildWordDecorations(source, options.highlightedWords),\n ...(options.transformers?.length ? { transformers: options.transformers } : {}),\n }) as HastNode;\n } catch {\n /* A missing grammar, an unloadable theme or a transformer throwing must not\n take the surface down. Plain text is always readable. */\n return toPlainLines(source, startLine);\n }\n\n const codeElement = findCodeElement(root);\n if (!codeElement) return toPlainLines(source, startLine);\n\n const lines: CodeBlockLine[] = [];\n for (const child of codeElement.children ?? []) {\n if (child.type !== 'element') continue;\n const tokens: CodeBlockToken[] = [];\n collectTokens(child, tokens, false);\n lines.push({\n tokens,\n number: startLine + lines.length,\n text: tokens.map((token) => token.content).join(''),\n state: stateFromClasses(classListOf(child)),\n });\n }\n\n if (!lines.length) return toPlainLines(source, startLine);\n\n applyPropState(lines, options);\n return reuseUnchangedLines(signature, lines);\n}\n\n/** Test seam: drops the singleton and every cached document. */\nexport function resetCodeBlockHighlighter(): void {\n highlighterPromise = null;\n loadedLanguages.clear();\n loadedThemes.clear();\n previousDocuments.clear();\n}\n\n/* -------------------------------------------------------------------------- */\n/* Unified diff */\n/* -------------------------------------------------------------------------- */\n\nexport type CodeBlockPatchFile = {\n /** New-side path, or the old one for a deletion. */\n file: string;\n /** Unified view: context, removed and added lines with dual gutter labels. */\n lines: CodeBlockLine[];\n added: number;\n removed: number;\n hunks: { header: string; at: number }[];\n};\n\n/**\n * A `git diff` / unified patch, as renderable per-file line sets: feed each\n * file's `lines` to the `lines` prop and the diff tints, `+`/`-` glyphs and\n * dual old/new gutter numbers all come from the parse - no `diff` prop\n * arithmetic against a hand-concatenated string. Tokens are plain; patches\n * read by tint, not grammar.\n */\nexport function parseUnifiedDiff(patch: string): CodeBlockPatchFile[] {\n const files: CodeBlockPatchFile[] = [];\n let current: CodeBlockPatchFile | null = null;\n let oldNumber = 0;\n let newNumber = 0;\n let width = 4;\n\n const push = (text: string, state: CodeBlockLineState | undefined, gutter: string) => {\n if (!current) return;\n current.lines.push({\n number: current.lines.length + 1,\n text,\n tokens: [{ content: text }],\n state,\n gutter,\n });\n };\n const pad = (value: number | null) => (value === null ? '' : String(value)).padStart(width);\n\n for (const raw of normalizeCode(patch).split('\\n')) {\n const fileHeader = raw.match(/^diff --git a\\/(.+) b\\/(.+)$/);\n const plusHeader = raw.match(/^\\+\\+\\+ (?:b\\/)?(.+)$/);\n if (fileHeader || plusHeader) {\n const name = fileHeader ? fileHeader[2] : plusHeader![1];\n if (name !== '/dev/null' && (!current || current.file !== name)) {\n current = { file: name, lines: [], added: 0, removed: 0, hunks: [] };\n files.push(current);\n }\n continue;\n }\n if (/^(---|index |old mode|new mode|new file|deleted file|similarity|rename |Binary )/.test(raw)) {\n continue;\n }\n\n const hunk = raw.match(/^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@(.*)$/);\n if (hunk && current) {\n oldNumber = Number(hunk[1]);\n newNumber = Number(hunk[3]);\n width = Math.max(\n String(oldNumber + Number(hunk[2] ?? 0)).length,\n String(newNumber + Number(hunk[4] ?? 0)).length,\n 2,\n );\n current.hunks.push({ header: raw, at: current.lines.length + 1 });\n push(hunk[5].trim() || raw, { level: 'info' }, `${'·'.padStart(width)} ${'·'.padStart(width)}`);\n continue;\n }\n if (!current || (!raw && files.length === 0)) continue;\n\n if (raw.startsWith('+')) {\n push(raw.slice(1), { diff: 'add' }, `${pad(null)} ${pad(newNumber)}`);\n newNumber += 1;\n current.added += 1;\n } else if (raw.startsWith('-')) {\n push(raw.slice(1), { diff: 'remove' }, `${pad(oldNumber)} ${pad(null)}`);\n oldNumber += 1;\n current.removed += 1;\n } else if (raw.startsWith(' ') || raw === '') {\n if (current.lines.length === 0 && raw === '') continue;\n push(raw.slice(1), undefined, `${pad(oldNumber)} ${pad(newNumber)}`);\n oldNumber += 1;\n newNumber += 1;\n }\n }\n\n return files;\n}\n","'use client';\n\n// Title: Code Block\n// Description: Shadcn code block with Shiki highlighting, streaming, diffs, folding and per-line interaction for AI chat UIs.\n// Vendored from reui (via the playground port) and trimmed for seal-editor: the\n// markdown-transcript helpers, ANSI terminal renderer and download button are\n// cut; affordances woven into the core (streaming plumbing, line-action\n// registry) stay so the render path stays upstream-shaped.\nimport { Button } from '#components/ui/button';\nimport { cn } from '#lib/utils';\nimport {\n Children,\n createContext,\n isValidElement,\n memo,\n useCallback,\n useContext,\n useDeferredValue,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { CSSProperties, ComponentProps, KeyboardEvent as ReactKeyboardEvent, ReactNode } from 'react';\n\nimport { highlightCode, resolveCodeBlockLanguage, stripNotationComments, toPlainLines } from './code-block-highlight';\nimport type {\n CodeBlockDiffSpec,\n CodeBlockLevelSpec,\n CodeBlockLine,\n CodeBlockLineActionsRender,\n CodeBlockLineSpec,\n CodeBlockThemes,\n CodeBlockToken,\n CodeBlockTransformer,\n CodeBlockWordSpec,\n} from './code-block-highlight';\n\n/* -------------------------------------------------------------------------- */\n/* Context */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Two contexts: the document changes on every streamed chunk, the config does\n * not. Splitting them keeps a stream from re-rendering the chrome per chunk.\n */\ntype CodeBlockConfigValue = {\n language?: string;\n resolvedLanguage?: string;\n showLineNumbers: boolean;\n wrap: boolean;\n setWrap: (wrap: boolean) => void;\n wrapControlled: boolean;\n expanded: boolean;\n setExpanded: (expanded: boolean) => void;\n collapsible: boolean;\n streaming: boolean;\n contentId: string;\n};\n\ntype CodeBlockDocumentValue = {\n code: string;\n lines: CodeBlockLine[];\n selected: Set<number>;\n selectable: boolean;\n toggleLine: (line: number, extend: boolean) => void;\n clearSelection: () => void;\n foldable: boolean;\n foldRegions: CodeBlockFoldRegion[];\n folded: Set<number>;\n toggleFold: (start: number) => void;\n setFolded: (folded: number[]) => void;\n};\n\nconst CodeBlockConfigContext = createContext<CodeBlockConfigValue | null>(null);\nconst CodeBlockDocumentContext = createContext<CodeBlockDocumentValue | null>(null);\n\n/** Lets the copy button tell a header placement from a floating one. */\nconst CodeBlockHeaderContext = createContext(false);\n\n/**\n * The root prepares every prop the code surface needs and publishes the\n * bundle here. `CodeBlockContent` reads it to render the surface wherever the\n * consumer composed it - typically inside their own ScrollArea - and the root\n * renders its built-in scrolling surface only when no `CodeBlockContent` is\n * found among its children.\n */\nconst CodeBlockSurfacePropsContext = createContext<CodeBlockSurfaceProps | null>(null);\n\n/**\n * `CodeBlockLineActions` renders nothing itself: it registers its render prop\n * here and the ACTIVE ROW renders it, so actions stay aligned when soft wrap\n * gives lines different heights.\n */\nexport type CodeBlockLineActionsSide = 'end' | 'gutter';\n\nconst CodeBlockActionsContext = createContext<{\n register: (owner: object, render: CodeBlockLineActionsRender | null, side?: CodeBlockLineActionsSide) => void;\n} | null>(null);\n\n/* Next still server-renders \"use client\" files, and a bare useLayoutEffect\n logs on every SSR pass. Same alias the event calendar ships. */\nconst useIsoLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n\nfunction useInternalConfig(part: string): CodeBlockConfigValue {\n const context = useContext(CodeBlockConfigContext);\n if (!context) {\n throw new Error(`${part} must be used within a CodeBlock`);\n }\n return context;\n}\n\nfunction useCodeBlockConfig(part: string): CodeBlockConfigValue & { code: string } {\n const context = useContext(CodeBlockConfigContext);\n const document = useContext(CodeBlockDocumentContext);\n if (!context || !document) {\n throw new Error(`${part} must be used within a CodeBlock`);\n }\n /* `code` lives on the DOCUMENT context (it changes per streamed chunk);\n keeping it out of the config is what lets chrome that ignores the source\n skip those re-renders. The public hook still returns it. */\n return useMemo(() => ({ ...context, code: document.code }), [context, document.code]);\n}\n\n/* -------------------------------------------------------------------------- */\n/* Styling */\n/* -------------------------------------------------------------------------- */\n\n/* Radius per style. Every style is named, including the zero rungs: a missing\n rung is a style someone forgot. The parity suite enforces all-eight-or-one. */\nconst ROOT_GHOST_CLASS = '[--code-block-bg:transparent]';\n\nconst ROOT_RADIUS_CLASS = '[--code-block-radius:var(--radius-lg)]';\n\n/*\n * Colour vars use the BASE theme tokens (--card, --primary, ...), never the\n * --color-* twins: those are static @theme literals that ignore a nested\n * `.dark` scope, which once painted a white gutter cell on a forced-dark\n * block. Tints are one system, 8% light / 12% dark (yellow a touch more),\n * matching the *-light badge weights. This item ships the same cssVars.\n */\nconst ROOT_TOKEN_CLASS = [\n '[--code-block-padding:--spacing(3)]',\n '[--code-block-gutter-width:--spacing(9)]',\n '[--code-block-gutter-gap:--spacing(2.5)]',\n /* The fold channel sits between the numbers and the code, so both the number\n cell's padding and the gutter width are expressed against it and a block\n without folding pays nothing for it. */\n '[--code-block-fold-width:--spacing(4.5)]',\n '[--code-block-number-pad:var(--code-block-gutter-gap)]',\n 'data-[gutter-channel]:[--code-block-number-pad:calc(var(--code-block-gutter-gap)+var(--code-block-fold-width))]',\n /* A diff column breathes: the +/- glyph gets a wider channel. */\n 'data-[has-diff]:[--code-block-gutter-gap:--spacing(4)]',\n '[--code-block-line-height:1.5rem]',\n '[--code-block-font-size:0.8125rem]',\n '[--code-block-copy-inset:--spacing(2)]',\n /* Where a PINNED control starts. With a header composed in, the root's top\n edge is the header, so an unadjusted inset drops the copy button onto the\n title row instead of the code. The header's own `min-h-9` is the height\n assumed here, plus its 1px bottom border; override the variable for a\n header built taller than one row. */\n '[--code-block-header-height:--spacing(9)]',\n '[--code-block-copy-top:var(--code-block-copy-inset)]',\n 'data-[has-header]:[--code-block-copy-top:calc(var(--code-block-header-height)+1px+var(--code-block-copy-inset))]',\n /* Room the expanded block keeps under the last line for the floating\n \"Show less\" control. */\n '[--code-block-expand-clearance:--spacing(9)]',\n /* What sits behind a sticky line number while the code scrolls under it.\n `ghost` blanks it, because there the surface belongs to the wrapper. */\n '[--code-block-bg:var(--card)]',\n '[--code-block-highlight-bg:color-mix(in_oklch,var(--primary)_6%,transparent)]',\n 'dark:[--code-block-highlight-bg:color-mix(in_oklch,var(--primary)_12%,transparent)]',\n '[--code-block-highlight-bar:color-mix(in_oklch,var(--primary)_60%,transparent)]',\n '[--code-block-diff-add-bg:color-mix(in_oklch,var(--success)_8%,transparent)]',\n 'dark:[--code-block-diff-add-bg:color-mix(in_oklch,var(--success)_12%,transparent)]',\n '[--code-block-diff-remove-bg:color-mix(in_oklch,var(--destructive)_8%,transparent)]',\n 'dark:[--code-block-diff-remove-bg:color-mix(in_oklch,var(--destructive)_12%,transparent)]',\n '[--code-block-error-bg:color-mix(in_oklch,var(--destructive)_8%,transparent)]',\n 'dark:[--code-block-error-bg:color-mix(in_oklch,var(--destructive)_12%,transparent)]',\n '[--code-block-warning-bg:color-mix(in_oklch,var(--warning)_10%,transparent)]',\n 'dark:[--code-block-warning-bg:color-mix(in_oklch,var(--warning)_14%,transparent)]',\n '[--code-block-info-bg:color-mix(in_oklch,var(--info)_8%,transparent)]',\n 'dark:[--code-block-info-bg:color-mix(in_oklch,var(--info)_12%,transparent)]',\n '[--code-block-caret-color:var(--primary)]',\n].join(' ');\n\nconst ROOT_BASE_CLASS = 'group/code-block relative flex min-w-0 flex-col rounded-(--code-block-radius) text-left';\n\nconst ROOT_SURFACE_CLASS = 'border border-border bg-card text-card-foreground bg-clip-padding';\n\n/**\n * The token colour switch: tokens carry only `--cb-c` / `--cb-cd`, and these\n * two rules recolour the whole block per theme instead of a `dark:` utility on\n * every span. The `color:` hint is load-bearing: `text-[var(--x)]` is\n * type-ambiguous to Tailwind and emits NO rule at all, which renders as a\n * highlighter that silently failed. Same reason the font size uses `length:`.\n */\nconst TOKEN_COLOR_CLASS =\n '[&_[data-slot=code-block-token]]:text-[color:var(--cb-c,currentColor)] dark:[&_[data-slot=code-block-token]]:text-[color:var(--cb-cd,currentColor)]';\n\n/**\n * Line numbers are a CSS counter, never DOM: a pseudo-element cannot join a\n * text selection, so copying yields exact source, and the gutter costs zero\n * elements. `startLine` is a counter reset on the pre.\n */\nconst LINE_NUMBER_CLASS = [\n '[[data-code-line-numbers]_&]:before:pointer-events-none',\n '[[data-code-line-numbers]_&]:before:sticky',\n '[[data-code-line-numbers]_&]:before:left-0',\n '[[data-code-line-numbers]_&]:before:z-10',\n '[[data-code-line-numbers]_&]:before:-ml-(--code-block-gutter-width)',\n '[[data-code-line-numbers]_&]:before:inline-block',\n '[[data-code-line-numbers]_&]:before:w-(--code-block-gutter-width)',\n '[[data-code-line-numbers]_&]:before:pl-(--code-block-padding)',\n '[[data-code-line-numbers]_&]:before:pr-(--code-block-number-pad)',\n '[[data-code-line-numbers]_&]:before:bg-(--code-block-bg)',\n '[[data-code-line-numbers]_&]:before:text-right',\n '[[data-code-line-numbers]_&]:before:tabular-nums',\n '[[data-code-line-numbers]_&]:before:text-muted-foreground/50',\n '[[data-code-line-numbers]_&]:before:select-none',\n '[[data-code-line-numbers]_&]:before:[counter-increment:cb-line]',\n '[[data-code-line-numbers]_&]:before:content-[counter(cb-line)]',\n /* A line carrying its own gutter label (a patch's dual numbers, a hunk\n marker) renders that instead of the counter. */\n '[[data-code-line-numbers]_&[data-gutter]]:before:content-[attr(data-gutter)]',\n '[[data-code-line-numbers]_&[data-gutter]]:before:whitespace-pre',\n].join(' ');\n\n/** Diff glyphs ride in the same pseudo-element family, so they never copy. */\n/**\n * The diff glyph gets its own channel, centred in the left padding (no\n * numbers) or in the number-to-code gap. A fixed `left-1` had ~1px of\n * clearance and would land on the first character.\n */\nconst LINE_DIFF_CLASS = [\n 'data-[diff]:after:absolute',\n /* Above the number cell. That cell is sticky with an opaque backdrop at z-10,\n so a glyph sharing its column is painted out at the default z. */\n 'data-[diff]:after:z-20',\n 'data-[diff]:after:left-0',\n 'data-[diff]:after:w-(--code-block-padding)',\n 'data-[diff]:after:text-center',\n '[[data-code-line-numbers]_&]:data-[diff]:after:-left-(--code-block-gutter-gap)',\n '[[data-code-line-numbers]_&]:data-[diff]:after:w-(--code-block-gutter-gap)',\n 'data-[diff]:after:select-none',\n 'data-[diff=add]:after:text-success',\n 'data-[diff=remove]:after:text-destructive',\n \"data-[diff=add]:after:content-['+']\",\n \"data-[diff=remove]:after:content-['-']\",\n].join(' ');\n\nconst LINE_STATE_CLASS = [\n 'data-[highlighted]:bg-(--code-block-highlight-bg)',\n 'data-[highlighted]:shadow-[inset_2px_0_0_0_var(--code-block-highlight-bar)]',\n 'data-[diff=add]:bg-(--code-block-diff-add-bg)',\n 'data-[diff=remove]:bg-(--code-block-diff-remove-bg)',\n 'data-[level=error]:bg-(--code-block-error-bg)',\n 'data-[level=warning]:bg-(--code-block-warning-bg)',\n 'data-[level=info]:bg-(--code-block-info-bg)',\n 'data-[selected]:bg-accent',\n /* Arrow navigation needs a visible position (WCAG 2.4.7); gated on the\n selectable listbox so plain blocks stay inert on hover. */\n '[[data-selectable]_&]:data-[active]:bg-muted/40',\n].join(' ');\n\n/**\n * Focus mode dims the rest and clears on hover.\n *\n * Blur alone makes a snippet unreadable if the reader wanted the surrounding\n * context after all, so hovering the block restores everything.\n */\n/**\n * Motion is streaming-only and compositor-only: rows keep identity across\n * chunks, so the entry animation fires once per NEW line and a static block\n * never animates. Tokens deliberately do not animate (the plain tail gaining\n * colour remounts them mid-read). Reduced motion disables the row entry.\n */\nconst LINE_MOTION_CLASS =\n '[[data-streaming]_&]:animate-in [[data-streaming]_&]:fade-in-0 [[data-streaming]_&]:slide-in-from-bottom-1 [[data-streaming]_&]:duration-150 [[data-streaming]_&]:ease-out motion-reduce:animate-none';\n\n/**\n * Deliberately no per-token animation. The highlighter runs a chunk behind\n * the stream and REPLACES the plain-text fallback lines with tokenised ones,\n * which remounts every token span; a mount-keyed fade therefore re-flashed\n * whole lines that were already readable, 300ms behind the caret. Colour\n * arriving instantly reads as highlighting; re-fading reads as a glitch.\n */\nconst TOKEN_MOTION_CLASS = '';\n\nconst LINE_FOCUS_CLASS =\n 'data-[blurred]:opacity-40 data-[blurred]:blur-[1.5px] data-[blurred]:transition-[opacity,filter] group-hover/code-block:data-[blurred]:opacity-100 group-hover/code-block:data-[blurred]:blur-none';\n\n/**\n * `data-code-line`, not `data-line`: rehype-pretty-code apps ship a global\n * `[data-line] span { color !important }` rule, and reusing its attribute\n * rendered every token monochrome. Cost a real investigation.\n */\nconst LINE_BASE_CLASS = cn(\n 'relative block min-h-(--code-block-line-height) w-full px-(--code-block-padding) leading-(--code-block-line-height)',\n /* With numbers on, the left inset lives inside the sticky cell instead, so\n paying it here too would restore the double count the gutter just lost. */\n '[[data-code-line-numbers]_&]:pl-0',\n);\n\n/* -------------------------------------------------------------------------- */\n/* Folding */\n/* -------------------------------------------------------------------------- */\n\nexport type CodeBlockFoldRegion = {\n /** Source line that owns the toggle and stays visible when folded. */\n start: number;\n /** Last source line the region swallows. */\n end: number;\n};\n\nconst INDENT_RE = /^[ \\t]*/;\n\n/**\n * Regions come from indentation, not the grammar: shiki returns tokens, no\n * AST, and indentation is language-agnostic where a brace matcher is not.\n * A tab counts as two columns; only self-consistency matters.\n */\nfunction computeFoldRegions(lines: CodeBlockLine[]): CodeBlockFoldRegion[] {\n const indent = (text: string) => (INDENT_RE.exec(text)?.[0] ?? '').replace(/\\t/g, ' ').length;\n const blank = (text: string) => text.trim().length === 0;\n\n /* One pass with an open-region stack instead of a nested scan per line:\n the old shape was quadratic on monotonically indenting files and re-ran\n per streamed chunk. A region closes when a non-blank line returns to its\n opener's indent; trailing blanks belong to whatever follows. */\n const regions: CodeBlockFoldRegion[] = [];\n const stack: { start: number; indent: number; last: number }[] = [];\n let previous: { number: number; indent: number } | null = null;\n\n for (const line of lines) {\n if (blank(line.text)) continue;\n const own = indent(line.text);\n\n while (stack.length && own <= stack[stack.length - 1].indent) {\n const open = stack.pop()!;\n if (open.last > open.start) {\n regions.push({ start: open.start, end: open.last });\n }\n }\n for (const open of stack) open.last = line.number;\n\n if (previous && own > previous.indent) {\n stack.push({\n start: previous.number,\n indent: previous.indent,\n last: line.number,\n });\n }\n previous = { number: line.number, indent: own };\n }\n\n while (stack.length) {\n const open = stack.pop()!;\n if (open.last > open.start) regions.push({ start: open.start, end: open.last });\n }\n\n return regions.sort((a, b) => a.start - b.start);\n}\n\n/** Frozen so the disabled path keeps a stable identity across renders. */\nconst EMPTY_FOLD_REGIONS: CodeBlockFoldRegion[] = [];\n\n/* -------------------------------------------------------------------------- */\n/* Tokens */\n/* -------------------------------------------------------------------------- */\n\ntype TokenStyle = CSSProperties & Record<'--cb-c' | '--cb-cd', string>;\n\nfunction tokenStyle(token: CodeBlockToken): CSSProperties | undefined {\n if (!token.color && !token.colorDark && !token.fontStyle) return undefined;\n\n const style: Partial<TokenStyle> = {};\n if (token.color) style['--cb-c'] = token.color;\n if (token.colorDark) style['--cb-cd'] = token.colorDark;\n if (token.fontStyle === 'italic') style.fontStyle = 'italic';\n if (token.fontStyle === 'bold') style.fontWeight = 700;\n if (token.fontStyle === 'underline') style.textDecoration = 'underline';\n return style as CSSProperties;\n}\n\nfunction CodeBlockTokens({ tokens }: { tokens: CodeBlockToken[] }) {\n return (\n <>\n {tokens.map((token, index) => {\n const style = tokenStyle(token);\n\n /* An unstyled token needs no element at all. Whitespace and\n punctuation are most of a file, so this removes most of the spans. */\n if (!style && !token.word) {\n return <span key={index}>{token.content}</span>;\n }\n\n return (\n <span\n key={index}\n data-slot='code-block-token'\n data-word={token.word || undefined}\n className={cn(token.word && 'bg-primary/10 ring-primary/20 rounded-sm px-0.5 py-px ring-1 ring-inset')}\n style={style}\n >\n {token.content}\n </span>\n );\n })}\n </>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Line */\n/* -------------------------------------------------------------------------- */\n\ntype CodeBlockLineRowProps = {\n line: CodeBlockLine;\n selectable: boolean;\n selected: boolean;\n focusMode: boolean;\n active: boolean;\n caret: boolean;\n actions?: CodeBlockLineActionsRender | null;\n actionsSide?: CodeBlockLineActionsSide;\n domIdBase?: string;\n startLine?: number;\n onSelect?: (line: number, extend: boolean) => void;\n foldable?: boolean;\n foldRegion?: CodeBlockFoldRegion;\n folded?: boolean;\n onToggleFold?: (start: number) => void;\n};\n\n/**\n * Plain reference-equality `memo`, on purpose: the highlighter returns the\n * SAME object for an unchanged line, so a streamed chunk re-renders one row.\n * A custom comparator would spend that win walking every token.\n */\nconst CodeBlockLineRow = memo(function CodeBlockLineRow({\n line,\n selectable,\n selected,\n focusMode,\n active,\n caret,\n actions,\n actionsSide = 'end',\n domIdBase,\n startLine = 1,\n onSelect,\n foldable: _,\n foldRegion,\n folded,\n onToggleFold,\n}: CodeBlockLineRowProps) {\n const state = line.state;\n const blurred = focusMode && !state?.focused;\n const lineDomId = domIdBase ? `${domIdBase}-L${line.number}` : undefined;\n const hiddenCount = foldRegion ? foldRegion.end - foldRegion.start : 0;\n /* The fold toggle owns the channel, so a fold-start row keeps its chevron\n and sends the action back to the row end. */\n const inGutter = actionsSide === 'gutter' && !foldRegion;\n\n return (\n <span\n data-slot='code-block-line'\n data-code-line={line.number}\n data-gutter={line.gutter}\n id={lineDomId}\n data-active={active || undefined}\n data-highlighted={state?.highlighted || undefined}\n data-diff={state?.diff}\n data-focused={state?.focused || undefined}\n data-blurred={blurred || undefined}\n data-level={state?.level}\n data-selected={selected || undefined}\n role={selectable ? 'option' : undefined}\n aria-selected={selectable ? selected : undefined}\n tabIndex={selectable ? -1 : undefined}\n onMouseDown={\n /* Shift-click is line-range selection here, but the browser also reads\n it as \"extend the text selection\", which paints a stray highlight\n across the block. Suppressing it only for the shift case leaves plain\n drag-to-select, and therefore copying, untouched. */\n selectable\n ? (event) => {\n if (event.shiftKey) event.preventDefault();\n }\n : undefined\n }\n onClick={selectable && onSelect ? (event) => onSelect(line.number, event.shiftKey) : undefined}\n className={cn(\n LINE_BASE_CLASS,\n LINE_NUMBER_CLASS,\n LINE_STATE_CLASS,\n LINE_DIFF_CLASS,\n LINE_FOCUS_CLASS,\n LINE_MOTION_CLASS,\n selectable && 'cursor-pointer',\n )}\n /**\n * Every row re-seeds the counter, making its number absolute rather\n * than ordinal. That serves two features at once: folding can remove\n * rows without renumbering the tail, and `content-visibility` can skip\n * offscreen rows - a style-contained row cannot read a shared counter,\n * which once rendered every gutter number as 1.\n */\n style={{ counterReset: `cb-line ${line.number - 1}` }}\n >\n {foldRegion && onToggleFold ? (\n <button\n type='button'\n /* Inside a role=\"option\" row a focusable descendant is invalid ARIA,\n so the toggle leaves the tab order when the block is selectable;\n pointer use is unaffected. */\n tabIndex={selectable ? -1 : undefined}\n data-slot='code-block-fold-toggle'\n data-state={folded ? 'folded' : 'unfolded'}\n aria-expanded={!folded}\n aria-label={\n folded\n ? `Unfold lines ${foldRegion.start + startLine - 1} to ${foldRegion.end + startLine - 1}`\n : `Fold lines ${foldRegion.start + startLine - 1} to ${foldRegion.end + startLine - 1}`\n }\n onClick={(event) => {\n /* The row itself may be a selection target, and a fold is not a\n selection. */\n event.stopPropagation();\n onToggleFold(foldRegion.start);\n }}\n className={cn(\n /* Styled as a ghost xs button rather than composed from Button:\n the ghost variant paints aria-expanded:bg-muted, which would\n keep every UNFOLDED toggle lit, and its icon rungs do not fit\n an 18px channel. Hover feedback only; folded state reads\n through color. */\n /* Square, centred in the line box: height matches the width and\n the translate makes up the difference to the 24px line. Open\n regions read darker than folded ones. */\n 'text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:ring-ring/50 sticky z-20 inline-flex size-(--code-block-fold-width) shrink-0 translate-y-[calc((var(--code-block-line-height)-var(--code-block-fold-width))/2)] cursor-pointer items-center justify-center rounded-[4px] align-top outline-none focus-visible:ring-2',\n /* Margins cancel: -(4px + width) then +4px, so the toggle paints\n in the channel, sits 4px from the code, and the code still\n starts where a row without a toggle does. An uncancelled\n margin slid every foldable row and clipped characters. The\n spacing() shorthand is written as a rem literal because\n `mr-(--spacing(1))` is not valid Tailwind and generates no\n rule at all - measured live as a 4px drift. */\n '-ml-[calc(var(--code-block-fold-width)+0.25rem)]',\n 'mr-[0.25rem]',\n 'left-[calc(var(--code-block-gutter-width)-var(--code-block-fold-width)-0.25rem)]',\n folded && 'text-muted-foreground/60',\n )}\n >\n <svg\n aria-hidden='true'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2.5'\n strokeLinecap='round'\n strokeLinejoin='round'\n className='size-3'\n >\n <path d={folded ? 'm9 18 6-6-6-6' : 'm6 9 6 6 6-6'} />\n </svg>\n </button>\n ) : null}\n {actions && active ? (\n <span\n data-slot='code-block-line-actions'\n data-side={inGutter ? 'gutter' : 'end'}\n contentEditable={false}\n /* An action is a control, not line content. Without this the click\n bubbles to a selectable row and toggles the very selection the\n action just consumed, so \"add these 6 lines\" left line 6 selected\n behind it. The consumer's own handler has already run by here. */\n onClick={(event) => event.stopPropagation()}\n className={cn(\n /* Inside the <pre>, so the code surface's mono face, pre\n whitespace and token color would cascade into real buttons\n placed here. Reset to app typography at the boundary. */\n 'text-foreground z-20 items-center gap-1 font-sans whitespace-normal select-none',\n !inGutter && 'absolute end-2 top-0 flex h-(--code-block-line-height)',\n /* ABSOLUTE and centred on the row's START EDGE: `left-0` is where\n the code column begins (the row box already excludes the\n gutter), and the -50% translate hangs the control half over the\n number column, half over the code - the editor treatment. Being\n out of flow on BOTH axes is the point: an in-flow version\n pushed the code right by its own width, and an earlier block\n level one broke the row onto a new line. The control travels\n with the code on a horizontal scroll; it cannot also be sticky. */\n inGutter && 'absolute top-1/2 left-0 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center',\n )}\n >\n {actions({ line: line.number, text: line.text, state })}\n </span>\n ) : null}\n <span data-slot='code-block-line-content'>\n {/* An empty line needs an explicit break. Copying a selection relies on\n the browser emitting a newline between block-level rows, and a row\n with no text at all contributes neither text nor separator, so a\n blank line silently vanishes from the pasted source. */}\n {line.tokens.length === 0 ? <br /> : null}\n <CodeBlockTokens tokens={line.tokens} />\n {folded && hiddenCount > 0 ? (\n /* `select-none` is doing real work: a manual drag-select over a\n folded block would otherwise paste this chip into the middle of\n the source. The copy button reads the raw code and never sees it. */\n <button\n type='button'\n tabIndex={selectable ? -1 : undefined}\n data-slot='code-block-fold-marker'\n aria-label={`Unfold ${hiddenCount} hidden lines`}\n onClick={(event) => {\n event.stopPropagation();\n onToggleFold?.(line.number);\n }}\n /* Same centring as the fold toggle: chip height matches the fold\n channel and the translate makes up the difference to the line\n box, so the label sits on the code baseline's visual centre\n instead of riding high. */\n className='bg-muted/80 text-muted-foreground hover:bg-muted ml-2 inline-flex h-(--code-block-fold-width) translate-y-[calc((var(--code-block-line-height)-var(--code-block-fold-width))/2)] cursor-pointer items-center rounded-sm px-1.5 align-top text-[0.8em] leading-none select-none'\n >\n {`... ${hiddenCount} lines`}\n </button>\n ) : null}\n {caret ? (\n <span\n data-slot='code-block-caret'\n aria-hidden='true'\n /* An underline caret: a thin strip on the baseline, the shape a\n terminal uses, instead of a bold block that outweighs the text\n it follows. */\n className='ml-0.5 inline-block h-[0.12em] w-[0.55em] translate-y-[0.02em] animate-pulse rounded-[1px] bg-(--code-block-caret-color) motion-reduce:animate-none'\n />\n ) : null}\n </span>\n </span>\n );\n});\n\n/* -------------------------------------------------------------------------- */\n/* Root */\n/* -------------------------------------------------------------------------- */\n\nexport type CodeBlockProps = {\n code?: string;\n language?: string;\n lines?: CodeBlockLine[];\n themes?: CodeBlockThemes;\n highlight?: boolean;\n showLineNumbers?: boolean;\n startLine?: number;\n wrap?: boolean;\n defaultWrap?: boolean;\n onWrapChange?: (wrap: boolean) => void;\n maxLines?: number;\n variant?: 'default' | 'ghost';\n label?: string;\n highlightedLines?: CodeBlockLineSpec;\n highlightedWords?: CodeBlockWordSpec[];\n focusedLines?: CodeBlockLineSpec;\n diff?: CodeBlockDiffSpec;\n lineLevels?: CodeBlockLevelSpec;\n transformers?: CodeBlockTransformer[];\n streaming?: boolean;\n selectable?: boolean;\n selectedLines?: number[];\n defaultSelectedLines?: number[];\n onSelectedLinesChange?: (lines: number[]) => void;\n /** Screen-reader text announced when a stream finishes, for localisation. */\n completeAnnouncement?: string;\n /** Collapsed state under `maxLines`, controlled. */\n expanded?: boolean;\n defaultExpanded?: boolean;\n onExpandedChange?: (expanded: boolean) => void;\n /** Detects fold regions from indentation and renders a toggle per region. */\n foldable?: boolean;\n /**\n * Replaces the indentation heuristic with your own regions (source-numbered,\n * like every line spec) - grammar folding, JSON blocks, patch hunks.\n * Implies nothing about `foldable`; pass both.\n */\n foldRegions?: CodeBlockFoldRegion[];\n /** Folded regions by start line, 1-based within `code`. Controlled. */\n folded?: number[];\n /** Folded regions by their start line, uncontrolled. */\n defaultFolded?: number[];\n onFoldedChange?: (folded: number[]) => void;\n children?: ReactNode;\n} & Omit<ComponentProps<'div'>, 'children' | 'onSelect'>;\n\n/**\n * The root. `<CodeBlock code={code} language=\"tsx\" />` is a complete block;\n * passing `lines` from a server component skips the client highlighter and\n * shiki entirely. Children are CHROME ONLY: the root renders the code surface\n * itself, so child order never matters and a wrapper cannot break scrolling.\n */\nfunction CodeBlock({\n code,\n language,\n lines: linesProp,\n themes,\n highlight = true,\n showLineNumbers = false,\n startLine = 1,\n wrap: wrapProp,\n defaultWrap = false,\n onWrapChange,\n maxLines,\n variant = 'default',\n label,\n highlightedLines,\n highlightedWords,\n focusedLines,\n diff,\n lineLevels,\n transformers,\n streaming = false,\n selectable = false,\n selectedLines,\n defaultSelectedLines,\n onSelectedLinesChange,\n completeAnnouncement,\n expanded: expandedProp,\n defaultExpanded = false,\n onExpandedChange,\n foldable = false,\n foldRegions: foldRegionsProp,\n folded,\n defaultFolded,\n onFoldedChange,\n className,\n children,\n ...props\n}: CodeBlockProps) {\n const contentId = useId();\n const source = code ?? '';\n const resolvedLanguage = resolveCodeBlockLanguage(language);\n\n /* The deferred copy is what a fast stream tokenises against. React keeps the\n previous render on screen while the new one is prepared, so the block never\n blanks and the main thread is never blocked by a chunk. */\n const deferredSource = useDeferredValue(source);\n\n const plainLines = useMemo(() => toPlainLines(deferredSource, startLine), [deferredSource, startLine]);\n\n /* The result is tagged with the exact inputs that produced it. Without the\n tag, swapping `code` kept rendering the PREVIOUS document for the whole\n async highlight pass - or spliced two files together when the old result\n was shorter than the new source. */\n const [highlighted, setHighlighted] = useState<{\n source: string;\n spec: string;\n lines: CodeBlockLine[];\n } | null>(null);\n\n const shouldHighlight = !linesProp && highlight && Boolean(resolvedLanguage) && source.length > 0;\n\n /* Presentation props enter the effect as ONE serialized key: they are\n inline literals at most call sites, and reference deps would re-tokenize\n per parent render. The effect parses its inputs back out of the key, so\n used and depended-on values cannot drift. `transformers` holds functions\n and stays a reference dep - hoist it to module scope. */\n const specKey = JSON.stringify([\n themes ?? null,\n startLine,\n highlightedLines ?? null,\n highlightedWords ?? null,\n focusedLines ?? null,\n diff ?? null,\n lineLevels ?? null,\n ]);\n\n const transformersWarnedRef = useRef(false);\n const previousTransformersRef = useRef(transformers);\n useEffect(() => {\n if (\n process.env.NODE_ENV !== 'production' &&\n streaming &&\n previousTransformersRef.current !== transformers &&\n !transformersWarnedRef.current\n ) {\n transformersWarnedRef.current = true;\n console.warn(\n '[code-block] `transformers` changed identity during a stream, which ' +\n 're-tokenizes the whole document per chunk. Hoist the array to ' +\n 'module scope or memoize it.',\n );\n }\n previousTransformersRef.current = transformers;\n }, [transformers, streaming]);\n\n useEffect(() => {\n if (!shouldHighlight) {\n setHighlighted(null);\n return;\n }\n\n const [\n specThemes,\n specStartLine,\n specHighlightedLines,\n specHighlightedWords,\n specFocusedLines,\n specDiff,\n specLineLevels,\n ] = JSON.parse(specKey) as [\n CodeBlockThemes | null,\n number,\n CodeBlockLineSpec | null,\n CodeBlockWordSpec[] | null,\n CodeBlockLineSpec | null,\n CodeBlockDiffSpec | null,\n CodeBlockLevelSpec | null,\n ];\n\n let active = true;\n void highlightCode(deferredSource, {\n language,\n instanceKey: contentId,\n themes: specThemes ?? undefined,\n transformers,\n startLine: specStartLine,\n highlightedLines: specHighlightedLines ?? undefined,\n highlightedWords: specHighlightedWords ?? undefined,\n focusedLines: specFocusedLines ?? undefined,\n diff: specDiff ?? undefined,\n lineLevels: specLineLevels ?? undefined,\n }).then((next) => {\n if (active) {\n setHighlighted({ source: deferredSource, spec: specKey, lines: next });\n }\n });\n\n return () => {\n active = false;\n };\n }, [shouldHighlight, deferredSource, language, transformers, specKey]);\n\n /* Plain text is the floor, never a blank frame: the highlighter runs a\n chunk behind a stream, so its lines show as-is and the remainder appends\n as plain text that gains colour a frame later. */\n const lines = useMemo(() => {\n if (linesProp) return linesProp;\n if (!shouldHighlight || !highlighted) return plainLines;\n /* The merge below is only valid under the STREAMING invariant: the\n highlighted source must be a prefix of what is on screen, with the same\n presentation spec. A swapped `code` or spec falls back to plain text\n for one highlight pass instead of showing the previous document. */\n if (highlighted.spec !== specKey || !deferredSource.startsWith(highlighted.source)) {\n return plainLines;\n }\n if (highlighted.lines.length >= plainLines.length) return highlighted.lines;\n return [...highlighted.lines, ...plainLines.slice(highlighted.lines.length)];\n }, [linesProp, shouldHighlight, highlighted, plainLines, deferredSource, specKey]);\n\n const focusMode = useMemo(() => lines.some((line) => line.state?.focused), [lines]);\n\n const hasDiff = useMemo(() => lines.some((line) => line.state?.diff), [lines]);\n\n const [internalWrap, setInternalWrap] = useState(defaultWrap);\n const wrap = wrapProp ?? internalWrap;\n const setWrap = useCallback(\n (next: boolean) => {\n if (wrapProp === undefined) setInternalWrap(next);\n onWrapChange?.(next);\n },\n [wrapProp, onWrapChange],\n );\n\n const collapsible = typeof maxLines === 'number' && lines.length > maxLines;\n const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);\n const expanded = expandedProp ?? internalExpanded;\n const setExpanded = useCallback(\n (next: boolean) => {\n if (expandedProp === undefined) setInternalExpanded(next);\n onExpandedChange?.(next);\n },\n [expandedProp, onExpandedChange],\n );\n const isExpanded = collapsible ? expanded : true;\n\n const [internalSelected, setInternalSelected] = useState<number[]>(defaultSelectedLines ?? []);\n const selection = selectedLines ?? internalSelected;\n const selectedSet = useMemo(() => new Set(selection), [selection]);\n const lastSelectedRef = useRef<number | null>(null);\n\n /* Latest-value refs, so the toggle callbacks below can be created ONCE.\n With state in their dep lists every selection or fold change minted new\n identities, which reached every row through `onSelect`/`onToggleFold` and\n busted the row memo the streaming design rests on. */\n const selectionStateRef = useRef({\n selection,\n selectedSet,\n controlled: selectedLines !== undefined,\n onSelectedLinesChange,\n });\n selectionStateRef.current = {\n selection,\n selectedSet,\n controlled: selectedLines !== undefined,\n onSelectedLinesChange,\n };\n\n const commitSelection = useCallback((next: number[]) => {\n if (!selectionStateRef.current.controlled) setInternalSelected(next);\n selectionStateRef.current.onSelectedLinesChange?.(next);\n }, []);\n\n const toggleLine = useCallback(\n (line: number, extend: boolean) => {\n const anchor = lastSelectedRef.current;\n const { selection, selectedSet } = selectionStateRef.current;\n\n if (extend && anchor !== null) {\n const from = Math.min(anchor, line);\n const to = Math.max(anchor, line);\n const range: number[] = [];\n for (let value = from; value <= to; value += 1) range.push(value);\n commitSelection(range);\n return;\n }\n\n lastSelectedRef.current = line;\n commitSelection(\n selectedSet.has(line)\n ? selection.filter((value) => value !== line)\n : [...selection, line].sort((a, b) => a - b),\n );\n },\n [commitSelection],\n );\n\n /* Regions are SOURCE-numbered (1-based within `code`), like every other\n line spec, so `folded` values and `highlightedLines` values interchange. */\n const foldRegions = useMemo(() => {\n if (!foldable) return EMPTY_FOLD_REGIONS;\n if (foldRegionsProp) return foldRegionsProp;\n return computeFoldRegions(lines).map((region) => ({\n start: region.start - startLine + 1,\n end: region.end - startLine + 1,\n }));\n }, [foldable, foldRegionsProp, lines, startLine]);\n\n const foldStarts = useMemo(() => new Map(foldRegions.map((region) => [region.start, region])), [foldRegions]);\n\n const [internalFolded, setInternalFolded] = useState<number[]>(defaultFolded ?? []);\n const foldedValue = folded ?? internalFolded;\n const foldedSet = useMemo(() => new Set(foldedValue), [foldedValue]);\n\n /* Same latest-value shape as selection, for the same row-memo reason. */\n const foldStateRef = useRef({\n foldedValue,\n foldedSet,\n controlled: folded !== undefined,\n onFoldedChange,\n });\n foldStateRef.current = {\n foldedValue,\n foldedSet,\n controlled: folded !== undefined,\n onFoldedChange,\n };\n\n const commitFolded = useCallback((next: number[]) => {\n if (!foldStateRef.current.controlled) setInternalFolded(next);\n foldStateRef.current.onFoldedChange?.(next);\n }, []);\n\n const toggleFold = useCallback(\n (start: number) => {\n const { foldedValue, foldedSet } = foldStateRef.current;\n commitFolded(\n foldedSet.has(start)\n ? foldedValue.filter((value) => value !== start)\n : [...foldedValue, start].sort((a, b) => a - b),\n );\n },\n [commitFolded],\n );\n\n /**\n * A line is hidden when ANY folded region covers it, and that one rule is\n * what makes nesting work without a tree: folding an outer region swallows\n * the inner toggles as well, their own folded state survives untouched\n * underneath, and unfolding the outer one restores exactly what the reader\n * left behind rather than a flattened block.\n */\n const renderedLines = useMemo(() => {\n if (!foldable || foldedSet.size === 0) return lines;\n const hidden = new Set<number>();\n for (const start of foldedSet) {\n const region = foldStarts.get(start);\n if (!region) continue;\n for (let n = region.start + 1; n <= region.end; n += 1) {\n hidden.add(n + startLine - 1);\n }\n }\n return lines.filter((line) => !hidden.has(line.number));\n }, [foldable, foldedSet, foldStarts, lines, startLine]);\n\n const configValue = useMemo<CodeBlockConfigValue>(\n () => ({\n language,\n resolvedLanguage,\n showLineNumbers,\n wrap,\n setWrap,\n wrapControlled: wrapProp !== undefined,\n expanded: isExpanded,\n setExpanded,\n collapsible,\n streaming,\n contentId,\n }),\n [\n language,\n resolvedLanguage,\n showLineNumbers,\n wrap,\n setWrap,\n wrapProp,\n isExpanded,\n collapsible,\n streaming,\n contentId,\n ],\n );\n\n const clearSelection = useCallback(() => commitSelection([]), [commitSelection]);\n\n const documentValue = useMemo<CodeBlockDocumentValue>(\n () => ({\n code: source,\n lines,\n selected: selectedSet,\n selectable,\n toggleLine,\n clearSelection,\n foldable,\n foldRegions,\n folded: foldedSet,\n toggleFold,\n setFolded: commitFolded,\n }),\n [\n source,\n lines,\n selectedSet,\n selectable,\n toggleLine,\n clearSelection,\n foldable,\n foldRegions,\n foldedSet,\n toggleFold,\n commitFolded,\n ],\n );\n\n const visibleLines = collapsible && !isExpanded ? maxLines : undefined;\n\n /* Detected from the element type rather than a `:has()` selector. This repo\n has measured `:has()` at 85-120ms of style invalidation per DOM mutation,\n and a streaming block mutates on every chunk, so the primitive uses none. */\n const hasHeader = containsElementType(children, CodeBlockHeader);\n\n const [lineActions, setLineActions] = useState<CodeBlockLineActionsRender | null>(null);\n const [actionsSide, setActionsSide] = useState<CodeBlockLineActionsSide>('end');\n const actionsOwnerRef = useRef<object | null>(null);\n const actionsRegistry = useMemo(\n () => ({\n /* Wrapped in a setter callback: a render prop IS a function, so passing\n it to setState bare would run it as a state updater. Ownership makes\n a stale unmount a no-op and makes a SECOND live group loud in dev -\n the registry holds one render prop, so the second silently won. */\n register: (owner: object, render: CodeBlockLineActionsRender | null, side: CodeBlockLineActionsSide = 'end') => {\n if (render) {\n if (process.env.NODE_ENV !== 'production' && actionsOwnerRef.current && actionsOwnerRef.current !== owner) {\n console.warn(\n '[code-block] Two CodeBlockLineActions are mounted; the block ' +\n 'renders ONE action group and the later mount replaces the ' +\n 'earlier one.',\n );\n }\n actionsOwnerRef.current = owner;\n } else if (actionsOwnerRef.current !== owner) {\n return;\n } else {\n actionsOwnerRef.current = null;\n }\n setLineActions(() => render);\n setActionsSide(side);\n },\n }),\n [],\n );\n\n /* One channel between the numbers and the code, shared by the fold toggle\n and a gutter-side action. Reserved only when something asks for it, so a\n plain block keeps the gutter it always had. */\n /* Only FOLDING reserves gutter space. A gutter-side action floats over the\n row's start edge instead of widening anything, so a block with actions is\n geometrically identical to one without. */\n const gutterChannel = foldable;\n\n /* Walked rather than registered, so server HTML never carries a doubled\n surface. Sees through plain wrappers; a consumer component boundary is\n opaque, which the dev-mode guard in CodeBlockContent reports. */\n const hasContent = containsElementType(children, CodeBlockContent);\n\n const surfaceProps: CodeBlockSurfaceProps = {\n lines: renderedLines,\n gutterMax: lines[lines.length - 1]?.number ?? startLine,\n foldable,\n gutterChannel,\n actionsSide,\n foldStarts,\n foldedSet,\n onToggleFold: toggleFold,\n expandClearance: collapsible && isExpanded,\n wrap,\n startLine,\n showLineNumbers,\n focusMode,\n selectable,\n selectedSet,\n toggleLine,\n visibleLines,\n streaming,\n label: label ?? (language ? `${language} code` : 'Code'),\n contentId,\n actions: lineActions,\n completeAnnouncement,\n builtInSurfaceRendered: !hasContent,\n };\n\n return (\n <CodeBlockConfigContext.Provider value={configValue}>\n <CodeBlockDocumentContext.Provider value={documentValue}>\n <CodeBlockActionsContext.Provider value={actionsRegistry}>\n <div\n data-slot='code-block'\n data-variant={variant}\n data-streaming={streaming || undefined}\n data-has-diff={hasDiff || undefined}\n data-foldable={foldable || undefined}\n data-gutter-channel={gutterChannel || undefined}\n data-has-header={hasHeader || undefined}\n className={cn(\n ROOT_BASE_CLASS,\n ROOT_RADIUS_CLASS,\n ROOT_TOKEN_CLASS,\n variant === 'default' && ROOT_SURFACE_CLASS,\n variant === 'ghost' && ROOT_GHOST_CLASS,\n className,\n )}\n {...props}\n >\n <CodeBlockSurfacePropsContext.Provider value={surfaceProps}>\n {children}\n {!hasContent && <CodeBlockSurface {...surfaceProps} />}\n </CodeBlockSurfacePropsContext.Provider>\n </div>\n </CodeBlockActionsContext.Provider>\n </CodeBlockDocumentContext.Provider>\n </CodeBlockConfigContext.Provider>\n );\n}\n\n/**\n * The code surface as a composable part: inside your own ScrollArea the block\n * stops scrolling internally and the ancestor owns both axes (`maxLines`'\n * height cap no longer applies). Omitted, the root renders the same surface\n * itself, exactly as before.\n */\nfunction CodeBlockContent({ className }: { className?: string }) {\n const surface = useContext(CodeBlockSurfacePropsContext);\n if (!surface) {\n throw new Error('CodeBlockContent must be used within a CodeBlock');\n }\n\n /* The render-time walk cannot see through a consumer's own component\n boundary; when that happens the root also renders its built-in surface\n and the code appears twice. Silent double content is the worst failure\n mode, so development says exactly what to do. */\n useEffect(() => {\n if (process.env.NODE_ENV === 'production') return;\n if (surface.builtInSurfaceRendered) {\n console.error(\n 'CodeBlockContent is hidden behind a component boundary, so CodeBlock ' +\n 'also rendered its built-in surface and the code appears twice. ' +\n \"Compose <CodeBlockContent /> directly in the CodeBlock's children \" +\n '(plain wrappers like a ScrollArea are fine).',\n );\n }\n }, [surface.builtInSurfaceRendered]);\n\n return <CodeBlockSurface {...surface} scroll={false} className={className} />;\n}\n\n/**\n * Sees through plain element wrappers (a ScrollArea, a div) but not through a\n * consumer's own component boundary - unrendered children are opaque. Compose\n * `CodeBlockContent` directly in the block's subtree.\n */\nfunction containsElementType(node: ReactNode, type: unknown): boolean {\n for (const child of Children.toArray(node)) {\n if (!isValidElement(child)) continue;\n if (child.type === type) return true;\n const inner = (child.props as { children?: ReactNode }).children;\n if (inner && containsElementType(inner, type)) return true;\n }\n return false;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Surface */\n/* -------------------------------------------------------------------------- */\n\ntype CodeBlockSurfaceProps = {\n lines: CodeBlockLine[];\n wrap: boolean;\n startLine: number;\n showLineNumbers: boolean;\n focusMode: boolean;\n selectable: boolean;\n selectedSet: Set<number>;\n toggleLine: (line: number, extend: boolean) => void;\n visibleLines?: number;\n gutterMax: number;\n foldable: boolean;\n gutterChannel: boolean;\n actionsSide: CodeBlockLineActionsSide;\n foldStarts: Map<number, CodeBlockFoldRegion>;\n foldedSet: Set<number>;\n onToggleFold: (start: number) => void;\n expandClearance: boolean;\n streaming: boolean;\n label: string;\n contentId: string;\n actions?: CodeBlockLineActionsRender | null;\n completeAnnouncement?: string;\n /** true when the root rendered its own surface (no CodeBlockContent found). */\n builtInSurfaceRendered?: boolean;\n /** false = no scroll container of its own; an ancestor scrolls instead. */\n scroll?: boolean;\n className?: string;\n};\n\nfunction CodeBlockSurface({\n lines,\n wrap,\n startLine,\n showLineNumbers,\n focusMode,\n selectable,\n selectedSet,\n toggleLine,\n visibleLines,\n gutterMax,\n foldable,\n gutterChannel,\n actionsSide,\n foldStarts,\n foldedSet,\n onToggleFold,\n expandClearance,\n streaming,\n label,\n contentId,\n actions,\n completeAnnouncement,\n scroll = true,\n className,\n}: CodeBlockSurfaceProps) {\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const preRef = useRef<HTMLPreElement | null>(null);\n const [activeLine, setActiveLine] = useState<number | null>(null);\n const stickRef = useRef(true);\n\n /* Stick-to-bottom disengages the moment the reader scrolls up, and re-engages\n when they come back. Reading the scroll position on the event is enough\n here because nothing is written back during the same frame. */\n /* Announced once when a stream ends, so a screen reader learns the code is\n complete without hearing it assembled character by character. */\n const [announcement, setAnnouncement] = useState('');\n const wasStreaming = useRef(streaming);\n useEffect(() => {\n if (wasStreaming.current && !streaming) {\n setAnnouncement(completeAnnouncement ?? `Code generation complete, ${lines.length} lines.`);\n }\n wasStreaming.current = streaming;\n }, [streaming, lines.length, completeAnnouncement]);\n\n /* Stick-to-bottom follows whichever element ACTUALLY scrolls - writing\n scrollTop to the composed mode's inert viewport was a silent no-op.\n Resolved per commit: a short block becomes its own scroller the moment\n it outgrows its cap. */\n const resolveScroller = useCallback((): HTMLElement | null => {\n const viewport = viewportRef.current;\n if (!viewport) return null;\n if (viewport.scrollHeight > viewport.clientHeight + 1) return viewport;\n\n let node: HTMLElement | null = viewport.parentElement;\n while (node && node !== document.body) {\n const overflowY = getComputedStyle(node).overflowY;\n if (\n (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') &&\n node.scrollHeight > node.clientHeight + 1\n ) {\n return node;\n }\n node = node.parentElement;\n }\n\n /* An AI transcript's default shape scrolls the PAGE. The walk above never\n reaches the root elements, so without this branch the stream wrote\n scrollTop to an inert viewport and drifted off screen. */\n const page = document.scrollingElement as HTMLElement | null;\n if (page && page.scrollHeight > page.clientHeight + 1) return page;\n\n return viewport;\n }, []);\n\n const handleScroll = useCallback(() => {\n const viewport = viewportRef.current;\n if (!viewport) return;\n const distance = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight;\n stickRef.current = distance < 24;\n }, []);\n\n /* The ancestor case needs its own listener: the reader's intent has to be\n recorded BEFORE the next chunk grows the content, or the distance already\n includes what just arrived. Re-attached per chunk rather than diffed,\n which is a handful of listener swaps a second and keeps this correct when\n the scrolling element changes underneath. */\n useEffect(() => {\n if (!streaming) return;\n const scroller = resolveScroller();\n if (!scroller || scroller === viewportRef.current) return;\n\n const onScroll = () => {\n const distance = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight;\n stickRef.current = distance < 24;\n };\n\n /* Page scrolls fire on window, not on the scrolling element. */\n const target: EventTarget = scroller === document.scrollingElement ? window : scroller;\n target.addEventListener('scroll', onScroll, { passive: true });\n return () => target.removeEventListener('scroll', onScroll);\n }, [streaming, lines.length, resolveScroller]);\n\n useIsoLayoutEffect(() => {\n if (!streaming || !stickRef.current) return;\n const scroller = resolveScroller();\n if (scroller) scroller.scrollTop = scroller.scrollHeight;\n }, [lines, streaming, resolveScroller]);\n\n /* One listener for the whole surface rather than a handler per line: the\n active row is resolved from the event target, so the floating action group\n costs one element no matter how long the file is. Focus is tracked as well\n as hover, or the actions would be unreachable without a pointer. */\n const trackActive = useCallback(\n (event: { target: EventTarget | null }) => {\n if (!actions && !selectable) return;\n const target = event.target as HTMLElement | null;\n const row = target?.closest?.('[data-code-line]') as HTMLElement | null;\n const value = row?.getAttribute('data-code-line');\n\n /* No row under the pointer means the gutter, which lives in the <pre>'s\n PADDING and so is covered by no row element. Keep the active line\n rather than clearing it: clearing here unmounted a gutter action the\n moment the pointer left the code toward it, so the control vanished\n before it could be pressed. `onPointerLeave` still clears on exit. */\n if (!value) return;\n setActiveLine(Number(value));\n },\n [actions, selectable],\n );\n\n const clearActive = useCallback(() => setActiveLine(null), []);\n\n /* Rows and keyboard speak DISPLAYED numbers (the gutter, data-code-line);\n selection state speaks SOURCE numbers like every other line spec. This is\n the one conversion point between them. */\n const toggleDisplayed = useCallback(\n (displayed: number, extend: boolean) => toggleLine(displayed - startLine + 1, extend),\n [toggleLine, startLine],\n );\n\n const handleKeyDown = useCallback(\n (event: ReactKeyboardEvent<HTMLElement>) => {\n if (!selectable) return;\n\n if (event.key === 'Escape') {\n setActiveLine(null);\n return;\n }\n\n /* Toggle IN PLACE. Shift+Arrow selects while moving, but without this a\n keyboard user could never deselect a single line. */\n if ((event.key === 'Enter' || event.key === ' ') && activeLine !== null) {\n event.preventDefault();\n toggleDisplayed(activeLine, event.shiftKey);\n return;\n }\n\n const isArrow = event.key === 'ArrowDown' || event.key === 'ArrowUp';\n if (!isArrow && event.key !== 'Home' && event.key !== 'End') return;\n event.preventDefault();\n\n /* Step by RENDERED index, not by number arithmetic: folding removes\n rows, and numeric stepping walked the active line into hidden lines\n where it silently vanished. */\n let next: number;\n if (event.key === 'Home') {\n next = lines[0]?.number ?? startLine;\n } else if (event.key === 'End') {\n next = lines[lines.length - 1]?.number ?? startLine;\n } else {\n const step = event.key === 'ArrowDown' ? 1 : -1;\n const index = lines.findIndex((line) => line.number === activeLine);\n const nextIndex =\n index === -1 ? (step === 1 ? 0 : lines.length - 1) : Math.min(lines.length - 1, Math.max(0, index + step));\n next = lines[nextIndex]?.number ?? startLine;\n }\n\n setActiveLine(next);\n const row = preRef.current?.querySelector<HTMLElement>(`[data-code-line=\"${next}\"]`);\n row?.scrollIntoView({ block: 'nearest' });\n if (isArrow && event.shiftKey) toggleDisplayed(next, true);\n },\n [selectable, lines, startLine, activeLine, toggleDisplayed],\n );\n\n const lastLineNumber = lines[lines.length - 1]?.number;\n\n /* `startLine` is just a counter reset. Gutter width follows the widest\n number in the DOCUMENT (not the folded view, or folding the tail would\n shift every row); `ch` is one digit in the mono `pre`, and the floor of 2\n stops the column twitching as a stream crosses line 9. Lines carrying\n their own gutter label (a patch's dual numbers) widen it to the longest\n label instead. */\n const gutterDigits = Math.max(\n 2,\n String(gutterMax ?? startLine).length,\n ...lines.map((line) => line.gutter?.length ?? 0),\n );\n\n const surfaceStyle = {\n '--code-block-gutter-width': gutterChannel\n ? `calc(${gutterDigits}ch + var(--code-block-fold-width) + var(--code-block-gutter-gap) + var(--code-block-padding))`\n : `calc(${gutterDigits}ch + var(--code-block-gutter-gap) + var(--code-block-padding))`,\n 'counterReset': `cb-line ${startLine - 1}`,\n } as CSSProperties;\n\n return (\n <div\n data-slot='code-block-content'\n className={cn(\n 'relative min-w-0 rounded-[inherit]',\n /* Clipping is the scroll-mode contract only: composed, a clipped pre\n would hide its own width from the ancestor's viewport and the\n horizontal scrollbar would never render. */\n scroll && 'flex-1 overflow-hidden',\n )}\n >\n {/* A polite status, deliberately NOT an aria-live region on the code\n itself: a live region over streaming code narrates every token. */}\n <span role='status' aria-live='polite' className='sr-only'>\n {announcement}\n </span>\n {/* A plain overflow container, deliberately not a ScrollArea primitive.\n The scroll must stay INSIDE the block (the sticky gutter, the\n stick-to-bottom stream and `maxLines` all anchor to it), but nothing\n here needs custom scrollbar machinery, and dropping it leaves the\n block with no UI-library dependency at all - which is why both twins\n are the same file. Compose Card, Frame, or your own ScrollArea\n around the block for outer framing. */}\n <div\n ref={viewportRef}\n data-slot='code-block-viewport'\n id={contentId}\n role='region'\n aria-label={label}\n aria-busy={streaming || undefined}\n aria-activedescendant={selectable && activeLine !== null ? `${contentId}-L${activeLine}` : undefined}\n tabIndex={0}\n onScroll={handleScroll}\n onKeyDown={handleKeyDown}\n className={cn(\n 'focus-visible:ring-ring/50 relative min-w-0 rounded-[inherit] outline-none focus-visible:ring-[3px]',\n /* Composed under an ancestor scroller the surface scrolls NOTHING\n itself: both axes belong to the ancestor, whose viewport picks up\n the pre's overflowing width, and the sticky number cell anchors\n to that viewport instead. Mixed ownership shipped once and read\n as two scrollbar styles on one block. */\n scroll && '[scrollbar-width:thin] overflow-auto',\n scroll && visibleLines && 'max-h-(--cb-max-height)',\n className,\n )}\n style={\n scroll && visibleLines\n ? ({\n '--cb-max-height': `calc(${visibleLines} * var(--code-block-line-height) + 2 * var(--code-block-padding))`,\n } as CSSProperties)\n : undefined\n }\n >\n <pre\n ref={preRef}\n dir='ltr'\n data-code-line-numbers={showLineNumbers || undefined}\n data-wrap={wrap || undefined}\n role={selectable ? 'listbox' : undefined}\n data-selectable={selectable || undefined}\n aria-multiselectable={selectable || undefined}\n aria-label={selectable ? `${label} lines` : undefined}\n onPointerOver={trackActive}\n onPointerLeave={clearActive}\n onFocus={trackActive}\n className={cn(\n 'w-max min-w-full py-(--code-block-padding) font-mono text-(length:--code-block-font-size)',\n TOKEN_COLOR_CLASS,\n TOKEN_MOTION_CLASS,\n /* `min-w-0` must beat the `min-w-full` above, or the pre keeps\n a floor of 100% of a scroll container that is itself wider\n than the viewport, and wrapping still leaves a stray\n horizontal scrollbar. */\n wrap && 'w-full max-w-full min-w-0 break-words whitespace-pre-wrap',\n showLineNumbers && 'pl-(--code-block-gutter-width)',\n /* The expand control floats over the surface in BOTH states, so\n an expanded block reserves a row of clearance rather than\n growing a bordered footer under the code. */\n expandClearance && 'pb-(--code-block-expand-clearance)',\n )}\n style={surfaceStyle}\n >\n {/* `content-visibility: auto` is a measured dead end on BOTH axes:\n its style containment breaks the shared gutter counter\n (every number renders as 1), and per-row it still fails -\n the paint containment clips the number cell, which is\n deliberately pulled left OUT of the row's box (verified in\n Chrome: forcing one row visible restored only that row's\n number). Windowing here means rendering fewer rows, not\n containing them. */}\n {/* presentation, so the listbox owns its options directly. */}\n <code data-slot='code-block-code' role={selectable ? 'presentation' : undefined}>\n {lines.map((line) => (\n <CodeBlockLineRow\n key={line.number}\n line={line}\n selectable={selectable}\n selected={selectedSet.has(line.number - startLine + 1)}\n focusMode={focusMode}\n active={activeLine === line.number}\n caret={streaming && line.number === lastLineNumber}\n actions={activeLine === line.number ? actions : null}\n actionsSide={actionsSide}\n domIdBase={contentId}\n startLine={startLine}\n onSelect={toggleDisplayed}\n foldable={foldable}\n foldRegion={foldStarts.get(line.number - startLine + 1)}\n folded={foldedSet.has(line.number - startLine + 1)}\n onToggleFold={onToggleFold}\n />\n ))}\n </code>\n </pre>\n </div>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Chrome */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Opt-in header. Absent, the block renders no chrome, so it nests in a Card or\n * Frame without a doubled bar. Presence flips `data-has-header` on the root,\n * detected from the element type rather than a `:has()` selector.\n */\nfunction CodeBlockHeader({ className, ...props }: ComponentProps<'div'>) {\n return (\n <CodeBlockHeaderContext.Provider value={true}>\n <div\n data-slot='code-block-header'\n className={cn('border-border flex min-h-9 shrink-0 items-center gap-2 border-b px-3 py-0 text-sm', className)}\n {...props}\n />\n </CodeBlockHeaderContext.Provider>\n );\n}\n\nfunction CodeBlockTitle({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot='code-block-title'\n className={cn('text-muted-foreground min-w-0 truncate font-mono text-xs', className)}\n {...props}\n />\n );\n}\n\n/**\n * The resolved language as a label, no props needed. It shows what the\n * highlighter actually resolved, so an unsupported grammar becomes visible\n * instead of silently rendering plain.\n */\nfunction CodeBlockLanguage({ className, children, ...props }: ComponentProps<'span'>) {\n const { language, resolvedLanguage } = useInternalConfig('CodeBlockLanguage');\n const label = children ?? resolvedLanguage ?? language;\n\n if (!label) return null;\n\n return (\n <span\n data-slot='code-block-language'\n data-unsupported={!resolvedLanguage || undefined}\n className={cn(\n 'border-border bg-muted/60 text-muted-foreground inline-flex shrink-0 items-center rounded-md border px-1.5 py-0.5 font-mono text-[0.6875rem] leading-none uppercase',\n className,\n )}\n {...props}\n >\n {label}\n </span>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Copy button */\n/* -------------------------------------------------------------------------- */\n\nfunction CopyIcon({ copied }: { copied: boolean }) {\n return copied ? (\n <svg\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'\n aria-hidden='true'\n >\n <path d='M20 6 9 17l-5-5' />\n </svg>\n ) : (\n <svg\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'\n aria-hidden='true'\n >\n <rect width='14' height='14' x='8' y='8' rx='2' ry='2' />\n <path d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2' />\n </svg>\n );\n}\n\nexport type CodeBlockCopyButtonProps = {\n value?: string;\n timeout?: number;\n onCopy?: (value: string) => void;\n onCopyError?: (error: unknown) => void;\n /** Accessible names, for localisation. */\n labels?: { copy?: string; copied?: string };\n /** `pinned` floats over the surface; `inline` sits in a header. */\n position?: 'auto' | 'pinned' | 'inline';\n alwaysVisible?: boolean;\n} & Omit<ComponentProps<typeof Button>, 'value' | 'onCopy'>;\n\n/**\n * Copy button. Inline inside a header, pinned over the surface anywhere else.\n * Pinning is absolute against the root, not `position: sticky`: sticky still\n * slides away under HORIZONTAL scroll, and code scrolls sideways constantly.\n * Notation comments are stripped from the payload, matching what is rendered.\n */\nfunction CodeBlockCopyButton({\n value,\n timeout = 2000,\n onCopy,\n onCopyError,\n labels,\n position = 'auto',\n alwaysVisible = false,\n className,\n variant = 'ghost',\n size,\n children,\n ...props\n}: CodeBlockCopyButtonProps) {\n const context = useContext(CodeBlockConfigContext);\n const inHeader = useContext(CodeBlockHeaderContext);\n const [copied, setCopied] = useState(false);\n const [copyFailed, setCopyFailed] = useState(false);\n\n const resolvedPosition = position === 'auto' ? (inHeader ? 'inline' : 'pinned') : position;\n\n /* A header is chrome, so the button takes the smaller icon rung there and the\n full one when it floats over the code. Both are per-style ladders in the\n shadcn button, so this stays style-aware instead of pinning a pixel size. */\n const resolvedSize = size ?? (resolvedPosition === 'inline' ? 'icon-sm' : 'icon');\n\n useEffect(() => {\n if (!copied || timeout === 0) return;\n const id = window.setTimeout(() => setCopied(false), timeout);\n return () => window.clearTimeout(id);\n }, [copied, timeout]);\n\n useEffect(() => {\n if (!copyFailed) return;\n const id = window.setTimeout(() => setCopyFailed(false), 2000);\n return () => window.clearTimeout(id);\n }, [copyFailed]);\n\n const document_ = useContext(CodeBlockDocumentContext);\n\n const handleClick = useCallback(() => {\n /* Server-highlighted blocks pass `lines` and no `code`, so the document\n text is the fallback; without it this rendered a labelled control that\n did nothing on the exact path the docs advertise. */\n const fallback = document_?.code || document_?.lines.map((line) => line.text).join('\\n');\n const payload = stripNotationComments(value ?? fallback ?? '');\n if (!payload) return;\n if (typeof navigator === 'undefined' || !navigator.clipboard?.writeText) {\n return;\n }\n\n navigator.clipboard.writeText(payload).then(\n () => {\n setCopied(true);\n onCopy?.(payload);\n },\n /* Rejection is routine: a denied permission, or a document that lost\n focus. Without the handler it surfaced as an unhandled rejection in\n the consumer's error monitoring. */\n (error: unknown) => {\n setCopyFailed(true);\n onCopyError?.(error);\n },\n );\n }, [value, context, document_, onCopy, onCopyError]);\n\n return (\n <Button\n type='button'\n data-slot='code-block-copy'\n data-copied={copied || undefined}\n data-copy-failed={copyFailed || undefined}\n data-position={resolvedPosition}\n aria-label={copied ? (labels?.copied ?? 'Copied') : (labels?.copy ?? 'Copy code')}\n variant={variant}\n size={resolvedSize}\n onClick={handleClick}\n className={cn(\n 'shrink-0 [&_svg]:size-3.5',\n resolvedPosition === 'pinned' &&\n /* Physical `right`, not logical `end`: the code surface is forced\n dir=\"ltr\", so in an RTL app the logical inset landed the button\n over the first characters instead of the trailing edge. */\n 'absolute top-(--code-block-copy-top) right-(--code-block-copy-inset) z-20',\n resolvedPosition === 'pinned' &&\n !alwaysVisible &&\n 'opacity-0 transition-opacity group-hover/code-block:opacity-100 focus-visible:opacity-100',\n className,\n )}\n {...props}\n >\n {children ?? <CopyIcon copied={copied} />}\n </Button>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Wrap and expand */\n/* -------------------------------------------------------------------------- */\n\nfunction CodeBlockWrapToggle({\n className,\n children,\n variant = 'ghost',\n size = 'sm',\n onClick,\n ...props\n}: ComponentProps<typeof Button>) {\n const { wrap, setWrap } = useInternalConfig('CodeBlockWrapToggle');\n\n return (\n <Button\n type='button'\n data-slot='code-block-wrap-toggle'\n data-state={wrap ? 'on' : 'off'}\n aria-pressed={wrap}\n variant={variant}\n size={size}\n onClick={(event) => {\n onClick?.(event);\n if (!event.defaultPrevented) setWrap(!wrap);\n }}\n className={cn('shrink-0', className)}\n {...props}\n >\n {children ?? (wrap ? 'No wrap' : 'Wrap')}\n </Button>\n );\n}\n\n/**\n * Expands a block collapsed by `maxLines`.\n *\n * Renders nothing when the content is shorter than the cut, so a consumer can\n * always compose it without guarding on line count themselves.\n */\nfunction CodeBlockExpandButton({\n className,\n children,\n variant = 'ghost',\n size = 'sm',\n onClick,\n ...props\n}: ComponentProps<typeof Button>) {\n const { expanded, setExpanded, collapsible, contentId } = useInternalConfig('CodeBlockExpandButton');\n\n if (!collapsible) return null;\n\n return (\n <div\n data-slot='code-block-expand'\n data-state={expanded ? 'expanded' : 'collapsed'}\n className={cn(\n /* Both states float over the surface. Expanded used to rejoin the flex\n column as a bordered strip, which read as a card footer and moved the\n block's bottom edge on the first click; the surface reserves a row of\n clearance instead, so only the label changes. */\n 'absolute inset-x-0 bottom-0 z-10 order-last flex items-end justify-center pt-10 pb-2',\n /* The fade means \"there is more below\", so it belongs to the collapsed\n state only. Expanded, the surface already reserves a clear row for\n this control, and keeping the gradient just dimmed the last real\n line of code. */\n !expanded && 'from-card via-card/85 bg-gradient-to-t to-transparent',\n className,\n )}\n >\n <Button\n type='button'\n aria-expanded={expanded}\n aria-controls={contentId}\n variant={variant}\n size={size}\n onClick={(event) => {\n onClick?.(event);\n if (!event.defaultPrevented) setExpanded(!expanded);\n }}\n {...props}\n >\n {children ?? (expanded ? 'Show less' : 'Show more')}\n </Button>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Line actions */\n/* -------------------------------------------------------------------------- */\n\nexport type CodeBlockLineActionsProps = {\n children: CodeBlockLineActionsRender;\n /**\n * `end` floats the group over the end of the active row. `gutter` puts it in\n * the channel beside the line number, which is where a \"add this line\"\n * affordance belongs. A fold toggle owns that channel when both are on, so a\n * gutter action on a fold-start row falls back to `end`.\n */\n side?: CodeBlockLineActionsSide;\n};\n\n/**\n * Actions on the hovered or focused line. ONE group exists for the whole\n * block, rendered inside the active row: a 2,000 line file mounts one button\n * group, not 2,000.\n */\nfunction CodeBlockLineActions({ children, side = 'end' }: CodeBlockLineActionsProps) {\n const registry = useContext(CodeBlockActionsContext);\n const owner = useRef({}).current;\n\n useEffect(() => {\n registry?.register(owner, children, side);\n return () => registry?.register(owner, null);\n }, [registry, owner, children, side]);\n\n return null;\n}\n\n/**\n * Live folding for anything composed inside the block - a \"fold all\" control,\n * a region count. Mirrors `useCodeBlockSelection`; region and start numbers\n * are source-based like every line spec.\n */\nfunction useCodeBlockFolding() {\n const document = useContext(CodeBlockDocumentContext);\n if (!document) {\n throw new Error('useCodeBlockFolding must be used within a CodeBlock');\n }\n\n const { foldable, foldRegions, folded, toggleFold, setFolded } = document;\n const foldedStarts = useMemo(() => [...folded].sort((a, b) => a - b), [folded]);\n const foldAll = useCallback(() => setFolded(foldRegions.map((region) => region.start)), [setFolded, foldRegions]);\n const unfoldAll = useCallback(() => setFolded([]), [setFolded]);\n\n return {\n foldable,\n regions: foldRegions,\n foldedStarts,\n toggleFold,\n foldAll,\n unfoldAll,\n };\n}\n\n/**\n * Live selection for anything composed inside the block - a line action, a\n * header control. Returns the selected lines as a sorted array plus the same\n * toggle the rows use, so an \"add selection to chat\" affordance can act on the\n * whole range instead of only the hovered line.\n */\nfunction useCodeBlockSelection() {\n const document = useContext(CodeBlockDocumentContext);\n if (!document) {\n throw new Error('useCodeBlockSelection must be used within a CodeBlock');\n }\n\n const { selected, selectable, toggleLine, clearSelection } = document;\n const selectedLines = useMemo(() => [...selected].sort((a, b) => a - b), [selected]);\n\n return { selectable, selectedLines, toggleLine, clearSelection };\n}\n\n/* Re-exported so a consumer renders patch reviews from one import path. */\nexport { parseUnifiedDiff } from './code-block-highlight';\nexport type {\n CodeBlockDiffSpec,\n CodeBlockHighlightOptions,\n CodeBlockLevelSpec,\n CodeBlockLine,\n CodeBlockLineActionsRender,\n CodeBlockLineSpec,\n CodeBlockLineState,\n CodeBlockPatchFile,\n CodeBlockThemes,\n CodeBlockToken,\n CodeBlockTransformer,\n CodeBlockWordSpec,\n} from './code-block-highlight';\n\nexport {\n CodeBlock,\n CodeBlockCopyButton,\n CodeBlockExpandButton,\n CodeBlockHeader,\n CodeBlockLanguage,\n CodeBlockLineActions,\n CodeBlockTitle,\n CodeBlockContent,\n CodeBlockWrapToggle,\n useCodeBlockConfig,\n useCodeBlockFolding,\n useCodeBlockSelection,\n};\n","import { CodeBlock, CodeBlockCopyButton, CodeBlockExpandButton } from '#reui/code-block/code-block';\nimport json5 from 'json5';\nimport React from 'react';\n\nimport { useT } from '../../../theming/i18n';\n\n/**\n * 单笔 Run 的输出/错误详情(code-block 渲染)——被 simulate-runs-panel 以\n * React.lazy 挂载,使 code-block 组件连同 shiki 全部留在动态 chunk\n * (field-picker-popup 同款隔离),index 主包零增重。Suspense fallback =\n * 面板内的裸 <pre>(渐进增强,chunk 加载期间内容不缺席)。\n */\nexport const SimulateRunDetail: React.FC<{ detail: unknown }> = ({ detail }) => {\n const t = useT();\n const code = json5.stringify(detail, undefined, 2) ?? 'null';\n\n return (\n <div className='relative' data-testid='simulate-run-detail'>\n <CodeBlock\n code={code}\n language='json'\n variant='ghost'\n foldable\n maxLines={16}\n showLineNumbers={false}\n className='max-h-64 rounded-md bg-muted/60 font-mono text-[10px] leading-relaxed'\n >\n <CodeBlockExpandButton className='text-[10px]' />\n </CodeBlock>\n <CodeBlockCopyButton\n value={code}\n position='pinned'\n alwaysVisible\n className='absolute right-1.5 top-1.5 size-5 text-muted-foreground opacity-60 hover:opacity-100'\n labels={{ copy: t('dg.simulation.copy'), copied: t('dg.simulation.copied') }}\n />\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"simulate-run-detail-B3f93RKl.js","names":[],"sources":["../src/reui/code-block/code-block-highlight.tsx","../src/reui/code-block/code-block.tsx","../src/components/decision-graph/simulator/simulate-run-detail.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport type { ShikiTransformer } from 'shiki';\n\n/**\n * One themed slice of a line. `color`/`colorDark` emit as `--cb-c`/`--cb-cd`\n * custom properties, so one rule pair on the `<pre>` theme-switches the whole\n * block. An uncoloured token keeps both undefined and renders as a bare text\n * node with no wrapper.\n */\nexport type CodeBlockToken = {\n content: string;\n color?: string;\n colorDark?: string;\n fontStyle?: CodeBlockFontStyle;\n word?: boolean;\n};\n\nexport type CodeBlockFontStyle = 'italic' | 'bold' | 'underline';\n\nexport type CodeBlockDiffKind = 'add' | 'remove';\n\nexport type CodeBlockLevel = 'error' | 'warning' | 'info';\n\n/**\n * Per-line presentation state, resolved once at highlight time.\n *\n * Two sources merge here: the props on the root, and the classes a shiki\n * transformer put on the line, so `@shikijs/transformers` notation such as\n * `[!code ++]` reaches the same place without the renderer knowing shiki\n * exists. Props win on conflict, because notation lives in the source string,\n * which a consumer often does not control.\n */\nexport type CodeBlockLineState = {\n highlighted?: boolean;\n diff?: CodeBlockDiffKind;\n focused?: boolean;\n level?: CodeBlockLevel;\n};\n\n/**\n * A rendered line. `number` is the DISPLAYED number (offset by `startLine`);\n * line specs address source lines, so changing `startLine` never invalidates\n * them. Instances are reused across passes when unchanged, which is what lets\n * the row be a plain `React.memo` and a streamed chunk cost one re-render.\n */\nexport type CodeBlockLine = {\n tokens: CodeBlockToken[];\n number: number;\n text: string;\n state?: CodeBlockLineState;\n /**\n * Replaces the counter-driven gutter number for this row - a unified patch\n * shows \"old new\" pairs, a hunk header shows dots. Rendered verbatim.\n */\n gutter?: string;\n};\n\n/** Source lines, as `[2, 3, 4]` or as a range string such as `\"2-4,7\"`. */\nexport type CodeBlockLineSpec = number[] | string;\n\nexport type CodeBlockDiffSpec = {\n added?: CodeBlockLineSpec;\n removed?: CodeBlockLineSpec;\n};\n\nexport type CodeBlockLevelSpec = Partial<Record<CodeBlockLevel, CodeBlockLineSpec>>;\n\n/** A word to mark, optionally restricted to some source lines. */\nexport type CodeBlockWordSpec = string | { word: string; lines?: CodeBlockLineSpec };\n\nexport type CodeBlockThemes = { light: string; dark: string };\n\n/**\n * shiki's own transformer type, imported type-only so it erases at compile\n * time and costs the bundle nothing. The previous structural stand-in\n * (`Record<string, unknown>`) rejected every real `ShikiTransformer`: an\n * interface without an index signature is not assignable to it.\n */\nexport type CodeBlockTransformer = ShikiTransformer;\n\nexport type CodeBlockHighlightOptions = {\n language?: string;\n themes?: CodeBlockThemes;\n transformers?: CodeBlockTransformer[];\n startLine?: number;\n /**\n * Distinguishes same-signature blocks in the line-reuse cache. Without it,\n * two identically-configured streams evict each other's previous document.\n * The root passes its own instance id.\n */\n instanceKey?: string;\n highlightedLines?: CodeBlockLineSpec;\n highlightedWords?: CodeBlockWordSpec[];\n focusedLines?: CodeBlockLineSpec;\n diff?: CodeBlockDiffSpec;\n lineLevels?: CodeBlockLevelSpec;\n};\n\n/** What a `CodeBlockLineActions` render prop receives. */\nexport type CodeBlockLineActionContext = {\n line: number;\n text: string;\n state?: CodeBlockLineState;\n};\n\nexport type CodeBlockLineActionsRender = (context: CodeBlockLineActionContext) => ReactNode;\n\n/* -------------------------------------------------------------------------- */\n/* Languages */\n/* -------------------------------------------------------------------------- */\n\n/**\n * A STATIC map, never ``import(`shiki/langs/${lang}.mjs`)``: a template import\n * makes bundlers bundle all ~200 grammars. Written out, each entry is its own\n * lazy chunk. To add a language, add a line: that is the intended extension\n * point of this file.\n */\nexport const codeBlockLanguages: Record<string, () => Promise<unknown>> = {\n bash: () => import('shiki/langs/bash.mjs'),\n c: () => import('shiki/langs/c.mjs'),\n cpp: () => import('shiki/langs/cpp.mjs'),\n csharp: () => import('shiki/langs/csharp.mjs'),\n css: () => import('shiki/langs/css.mjs'),\n diff: () => import('shiki/langs/diff.mjs'),\n docker: () => import('shiki/langs/docker.mjs'),\n go: () => import('shiki/langs/go.mjs'),\n graphql: () => import('shiki/langs/graphql.mjs'),\n html: () => import('shiki/langs/html.mjs'),\n java: () => import('shiki/langs/java.mjs'),\n javascript: () => import('shiki/langs/javascript.mjs'),\n json: () => import('shiki/langs/json.mjs'),\n jsx: () => import('shiki/langs/jsx.mjs'),\n kotlin: () => import('shiki/langs/kotlin.mjs'),\n markdown: () => import('shiki/langs/markdown.mjs'),\n php: () => import('shiki/langs/php.mjs'),\n python: () => import('shiki/langs/python.mjs'),\n ruby: () => import('shiki/langs/ruby.mjs'),\n rust: () => import('shiki/langs/rust.mjs'),\n scss: () => import('shiki/langs/scss.mjs'),\n shell: () => import('shiki/langs/shellscript.mjs'),\n sql: () => import('shiki/langs/sql.mjs'),\n swift: () => import('shiki/langs/swift.mjs'),\n toml: () => import('shiki/langs/toml.mjs'),\n tsx: () => import('shiki/langs/tsx.mjs'),\n typescript: () => import('shiki/langs/typescript.mjs'),\n vue: () => import('shiki/langs/vue.mjs'),\n yaml: () => import('shiki/langs/yaml.mjs'),\n};\n\n/** Spellings a consumer is likely to pass, mapped onto the map above. */\nconst LANGUAGE_ALIASES: Record<string, string> = {\n 'c++': 'cpp',\n 'c#': 'csharp',\n 'cs': 'csharp',\n 'dockerfile': 'docker',\n 'htm': 'html',\n 'js': 'javascript',\n 'jsonc': 'json',\n 'md': 'markdown',\n 'mdx': 'markdown',\n 'py': 'python',\n 'rb': 'ruby',\n 'rs': 'rust',\n 'sh': 'shell',\n 'shellscript': 'shell',\n 'ts': 'typescript',\n 'yml': 'yaml',\n 'zsh': 'shell',\n};\n\nexport const codeBlockThemes: Record<string, () => Promise<unknown>> = {\n 'github-light': () => import('shiki/themes/github-light.mjs'),\n 'github-dark': () => import('shiki/themes/github-dark.mjs'),\n /**\n * Design-token theming: every token colour becomes a `var(--code-token-*)`\n * reference, so the palette lives in the consumer's stylesheet and follows\n * their themes. Pass as BOTH sides. Variables consumed (prefix `--code-`):\n * foreground, token-constant, token-string, token-comment, token-keyword,\n * token-parameter, token-function, token-string-expression,\n * token-punctuation, token-link.\n */\n 'css-variables': async () => {\n const { createCssVariablesTheme } = await import('shiki/core');\n return createCssVariablesTheme({\n name: 'css-variables',\n variablePrefix: '--code-',\n fontStyle: true,\n });\n },\n};\n\nexport const DEFAULT_CODE_BLOCK_THEMES: CodeBlockThemes = {\n light: 'github-light',\n dark: 'github-dark',\n};\n\n/** Resolves an alias and reports whether the grammar is actually available. */\nexport function resolveCodeBlockLanguage(language?: string): string | undefined {\n if (!language) return undefined;\n const normalized = language.trim().toLowerCase();\n const resolved = LANGUAGE_ALIASES[normalized] ?? normalized;\n return resolved in codeBlockLanguages ? resolved : undefined;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Pure helpers */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Turns `[2, 3]` or `\"2-4,7\"` into a set of source line numbers.\n *\n * Deliberately total: a reversed range, a negative bound or outright garbage\n * yields an empty set rather than throwing. These values often come from user\n * content or from a model, and a code viewer that crashes on a bad range is\n * worse than one that shows no highlight.\n */\nexport function parseLineSpec(spec?: CodeBlockLineSpec): Set<number> {\n const out = new Set<number>();\n if (!spec) return out;\n\n if (Array.isArray(spec)) {\n for (const value of spec) {\n if (Number.isInteger(value) && value > 0) out.add(value);\n }\n return out;\n }\n\n for (const part of spec.split(',')) {\n const trimmed = part.trim();\n if (!trimmed) continue;\n\n const range = trimmed.match(/^(\\d+)\\s*-\\s*(\\d+)$/);\n if (range) {\n const start = Number(range[1]);\n const end = Number(range[2]);\n if (start > 0 && end >= start) {\n for (let line = start; line <= end; line += 1) out.add(line);\n }\n continue;\n }\n\n if (/^\\d+$/.test(trimmed)) {\n const single = Number(trimmed);\n if (single > 0) out.add(single);\n }\n }\n\n return out;\n}\n\n/** Normalises line endings so every downstream offset is LF-based. */\nexport function normalizeCode(code: string): string {\n return code.replace(/\\r\\n?/g, '\\n');\n}\n\n/**\n * Character ranges for `highlightedWords`, as shiki `decorations`.\n *\n * shiki rejects overlapping decorations, so a later match that would overlap an\n * earlier one is dropped rather than passed through to throw.\n */\nexport function buildWordDecorations(\n code: string,\n words?: CodeBlockWordSpec[],\n): { start: number; end: number; properties: { class: string } }[] {\n if (!words?.length) return [];\n\n const lineStarts: number[] = [0];\n for (let index = 0; index < code.length; index += 1) {\n if (code[index] === '\\n') lineStarts.push(index + 1);\n }\n\n const lineAt = (offset: number) => {\n let low = 0;\n let high = lineStarts.length - 1;\n while (low < high) {\n const mid = Math.ceil((low + high) / 2);\n if (lineStarts[mid] <= offset) low = mid;\n else high = mid - 1;\n }\n return low + 1;\n };\n\n const taken: { start: number; end: number }[] = [];\n const out: { start: number; end: number; properties: { class: string } }[] = [];\n\n for (const entry of words) {\n const word = typeof entry === 'string' ? entry : entry.word;\n if (!word) continue;\n const limit = typeof entry === 'string' ? undefined : parseLineSpec(entry.lines);\n\n let from = code.indexOf(word);\n while (from !== -1) {\n const to = from + word.length;\n const withinLimit = !limit || limit.size === 0 || limit.has(lineAt(from));\n const overlaps = taken.some((r) => from < r.end && to > r.start);\n\n if (withinLimit && !overlaps) {\n taken.push({ start: from, end: to });\n out.push({ start: from, end: to, properties: { class: 'cb-word' } });\n }\n from = code.indexOf(word, from + word.length);\n }\n }\n\n return out.sort((a, b) => a.start - b.start);\n}\n\n/**\n * Strips `[!code ...]` notation from a copy payload: the transformer already\n * drops it from RENDERED output, but the raw `code` string would paste the\n * comment into someone's editor.\n */\nexport function stripNotationComments(code: string): string {\n return code\n .split('\\n')\n .map((line) => line.replace(/\\s*(?:\\/\\/|#|--|;|%|<!--|\\/\\*)\\s*\\[!code[^\\]]*\\]\\s*(?:-->|\\*\\/)?\\s*$/, ''))\n .join('\\n');\n}\n\n/**\n * Plain, unhighlighted lines. One token per line, no colour.\n *\n * This is both the `highlight={false}` renderer and the first paint of a\n * streaming block, so the tail of a stream is readable before its grammar pass\n * lands and no frame is ever blank.\n */\nexport function toPlainLines(code: string, startLine = 1): CodeBlockLine[] {\n return normalizeCode(code)\n .split('\\n')\n .map((text, index) => ({\n tokens: text ? [{ content: text }] : [],\n number: startLine + index,\n text,\n }));\n}\n\n/* -------------------------------------------------------------------------- */\n/* Engine */\n/* -------------------------------------------------------------------------- */\n\ntype HighlighterLike = {\n codeToHast: (code: string, options: Record<string, unknown>) => unknown;\n getLoadedLanguages: () => string[];\n loadLanguage: (lang: unknown) => Promise<void>;\n loadTheme: (theme: unknown) => Promise<void>;\n};\n\nlet highlighterPromise: Promise<HighlighterLike> | null = null;\nconst loadedLanguages = new Set<string>();\nconst loadedThemes = new Set<string>();\n\n/**\n * One highlighter per page, on the JavaScript regex engine: oniguruma needs\n * WebAssembly, which forces `'wasm-unsafe-eval'` into every consumer's CSP.\n * `forgiving` keeps an inexpressible grammar pattern from taking the block\n * down.\n */\nasync function loadHighlighter() {\n if (!highlighterPromise) {\n highlighterPromise = (async () => {\n const [{ createHighlighterCore }, { createJavaScriptRegexEngine }] = await Promise.all([\n import('shiki/core'),\n import('shiki/engine/javascript'),\n ]);\n\n return (await createHighlighterCore({\n themes: [],\n langs: [],\n engine: createJavaScriptRegexEngine({ forgiving: true }),\n })) as unknown as HighlighterLike;\n })();\n }\n\n return highlighterPromise;\n}\n\n/**\n * Themes register per name, on demand, exactly like languages.\n *\n * Baking the FIRST caller's pair into the singleton looks correct until a page\n * holds two blocks with different `themes` props: the second silently renders\n * in the first one's colours, because the singleton never learns about the\n * request. Loading by name makes every block's prop actually mean something.\n */\nconst warnedThemes = new Set<string>();\n\nasync function ensureTheme(highlighter: HighlighterLike, name: string, side: 'light' | 'dark' = 'light') {\n /* Falls back to the MATCHING side (an unknown dark theme used to fall back\n to github-light, silently rendering light colours in dark mode). */\n const resolved = name in codeBlockThemes ? name : DEFAULT_CODE_BLOCK_THEMES[side];\n if (resolved !== name && process.env.NODE_ENV !== 'production' && !warnedThemes.has(name)) {\n warnedThemes.add(name);\n console.warn(\n `[code-block] Unknown theme \"${name}\" - falling back to \"${resolved}\". ` +\n 'Register it in codeBlockThemes to use it.',\n );\n }\n if (loadedThemes.has(resolved)) return resolved;\n await highlighter.loadTheme(await codeBlockThemes[resolved]());\n loadedThemes.add(resolved);\n return resolved;\n}\n\nasync function ensureLanguage(highlighter: HighlighterLike, language: string) {\n if (loadedLanguages.has(language)) return;\n const loader = codeBlockLanguages[language];\n if (!loader) return;\n await highlighter.loadLanguage(await loader());\n loadedLanguages.add(language);\n}\n\n/* -------------------------------------------------------------------------- */\n/* hast to normalised lines */\n/* -------------------------------------------------------------------------- */\n\ntype HastNode = {\n type: string;\n tagName?: string;\n value?: string;\n properties?: Record<string, unknown>;\n children?: HastNode[];\n};\n\nconst FONT_STYLE_BY_DECLARATION: Record<string, CodeBlockFontStyle> = {\n 'font-style:italic': 'italic',\n 'font-weight:bold': 'bold',\n 'text-decoration:underline': 'underline',\n};\n\n/** Splits shiki's inline `style` string into the fields a token carries. */\nfunction readTokenStyle(style: unknown): Omit<CodeBlockToken, 'content'> {\n if (typeof style !== 'string') return {};\n\n const out: Omit<CodeBlockToken, 'content'> = {};\n for (const declaration of style.split(';')) {\n const trimmed = declaration.trim();\n if (!trimmed) continue;\n\n const separator = trimmed.indexOf(':');\n if (separator === -1) continue;\n\n const property = trimmed.slice(0, separator).trim();\n const value = trimmed.slice(separator + 1).trim();\n\n if (property === 'color') out.color = value;\n else if (property === '--shiki-dark') out.colorDark = value;\n else {\n const fontStyle = FONT_STYLE_BY_DECLARATION[`${property}:${value}`];\n if (fontStyle) out.fontStyle = fontStyle;\n }\n }\n return out;\n}\n\n/**\n * shiki emits raw `class` (string or array), not hast's `className`; reading\n * only `className` finds nothing, which looks like a transformer that never\n * ran.\n */\nfunction classListOf(node: HastNode): string[] {\n const value = node.properties?.class ?? node.properties?.className;\n if (Array.isArray(value)) return value.map(String);\n if (typeof value === 'string') return value.split(/\\s+/).filter(Boolean);\n return [];\n}\n\n/** Maps the classes shiki transformers put on a line onto line state. */\nfunction stateFromClasses(classes: string[]): CodeBlockLineState | undefined {\n const state: CodeBlockLineState = {};\n if (classes.includes('highlighted')) state.highlighted = true;\n if (classes.includes('focused')) state.focused = true;\n if (classes.includes('diff')) {\n if (classes.includes('add')) state.diff = 'add';\n else if (classes.includes('remove')) state.diff = 'remove';\n }\n for (const level of ['error', 'warning', 'info'] as const) {\n if (classes.includes(level)) state.level = level;\n }\n return Object.keys(state).length ? state : undefined;\n}\n\nfunction collectTokens(node: HastNode, out: CodeBlockToken[], inWord: boolean): void {\n for (const child of node.children ?? []) {\n if (child.type === 'text') {\n if (!child.value) continue;\n out.push(inWord ? { content: child.value, word: true } : { content: child.value });\n continue;\n }\n if (child.type !== 'element') continue;\n\n const classes = classListOf(child);\n const childInWord = inWord || classes.includes('cb-word');\n const style = readTokenStyle(child.properties?.style);\n const hasStyle = Boolean(style.color || style.colorDark || style.fontStyle);\n\n /* A styled leaf is a token; a wrapper (a decoration span) is descended into\n so its own children keep their individual colours. */\n const onlyText = (child.children ?? []).every((grandChild) => grandChild.type === 'text');\n\n if (hasStyle && onlyText) {\n const content = (child.children ?? []).map((c) => c.value ?? '').join('');\n if (!content) continue;\n out.push({\n content,\n ...style,\n ...(childInWord ? { word: true } : {}),\n });\n continue;\n }\n\n collectTokens(child, out, childInWord);\n }\n}\n\nfunction findCodeElement(root: HastNode): HastNode | undefined {\n if (root.type === 'element' && root.tagName === 'code') return root;\n for (const child of root.children ?? []) {\n const found = findCodeElement(child);\n if (found) return found;\n }\n return undefined;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Identity reuse */\n/* -------------------------------------------------------------------------- */\n\nconst MAX_TRACKED_DOCUMENTS = 24;\n\n/**\n * Previous result per option signature, so a growing stream can reuse lines.\n * Keyed WITH the caller's instanceKey: without it, two same-configured blocks\n * evicted each other's entry on every interleaved pass. Bounded above because\n * module state outlives requests on the server.\n */\nconst previousDocuments = new Map<string, CodeBlockLine[]>();\n\nfunction sameLine(a: CodeBlockLine, b: CodeBlockLine): boolean {\n if (a.number !== b.number || a.text !== b.text) return false;\n if (a.tokens.length !== b.tokens.length) return false;\n if (JSON.stringify(a.state ?? null) !== JSON.stringify(b.state ?? null)) {\n return false;\n }\n for (let index = 0; index < a.tokens.length; index += 1) {\n const left = a.tokens[index];\n const right = b.tokens[index];\n if (\n left.content !== right.content ||\n left.color !== right.color ||\n left.colorDark !== right.colorDark ||\n left.fontStyle !== right.fontStyle ||\n left.word !== right.word\n ) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Swaps freshly built lines for the previous pass's objects where nothing\n * changed. This is why streaming is cheap: the row is a reference-equality\n * `memo`, so without this, appending one token to a 400 line file re-renders\n * 400 subtrees per chunk.\n */\nfunction reuseUnchangedLines(key: string, next: CodeBlockLine[]): CodeBlockLine[] {\n const previous = previousDocuments.get(key);\n\n if (previous) {\n for (let index = 0; index < next.length; index += 1) {\n const before = previous[index];\n if (before && sameLine(before, next[index])) next[index] = before;\n }\n }\n\n previousDocuments.set(key, next);\n if (previousDocuments.size > MAX_TRACKED_DOCUMENTS) {\n const oldest = previousDocuments.keys().next().value;\n if (oldest !== undefined) previousDocuments.delete(oldest);\n }\n\n return next;\n}\n\n/* -------------------------------------------------------------------------- */\n/* highlightCode */\n/* -------------------------------------------------------------------------- */\n\nfunction applyPropState(lines: CodeBlockLine[], options: CodeBlockHighlightOptions): void {\n const highlighted = parseLineSpec(options.highlightedLines);\n const focused = parseLineSpec(options.focusedLines);\n const added = parseLineSpec(options.diff?.added);\n const removed = parseLineSpec(options.diff?.removed);\n const levels = {\n error: parseLineSpec(options.lineLevels?.error),\n warning: parseLineSpec(options.lineLevels?.warning),\n info: parseLineSpec(options.lineLevels?.info),\n };\n\n lines.forEach((line, index) => {\n const source = index + 1;\n const state: CodeBlockLineState = { ...line.state };\n\n if (highlighted.has(source)) state.highlighted = true;\n if (focused.has(source)) state.focused = true;\n if (added.has(source)) state.diff = 'add';\n else if (removed.has(source)) state.diff = 'remove';\n for (const level of ['error', 'warning', 'info'] as const) {\n if (levels[level].has(source)) state.level = level;\n }\n\n line.state = Object.keys(state).length ? state : undefined;\n });\n}\n\n/**\n * Highlights `code` into the renderer's line shape. Built on `codeToHast` so\n * consumer transformers run and their line classes land in `line.state`,\n * making prop state and `[!code ++]` notation one feature, not two code\n * paths. Safe in a server component: the result is plain JSON.\n */\nexport async function highlightCode(code: string, options: CodeBlockHighlightOptions = {}): Promise<CodeBlockLine[]> {\n const source = normalizeCode(code);\n const startLine = options.startLine ?? 1;\n const language = resolveCodeBlockLanguage(options.language);\n\n if (!language) return toPlainLines(source, startLine);\n\n const themes = options.themes ?? DEFAULT_CODE_BLOCK_THEMES;\n const signature = JSON.stringify([\n options.instanceKey ?? null,\n language,\n themes,\n startLine,\n options.highlightedLines ?? null,\n options.highlightedWords ?? null,\n options.focusedLines ?? null,\n options.diff ?? null,\n options.lineLevels ?? null,\n (options.transformers ?? []).length,\n ]);\n\n let root: HastNode;\n try {\n const highlighter = await loadHighlighter();\n const [light, dark] = await Promise.all([\n ensureTheme(highlighter, themes.light),\n ensureTheme(highlighter, themes.dark),\n ]);\n await ensureLanguage(highlighter, language);\n\n root = highlighter.codeToHast(source, {\n lang: language,\n themes: { light, dark },\n defaultColor: 'light',\n cssVariablePrefix: '--shiki-',\n decorations: buildWordDecorations(source, options.highlightedWords),\n ...(options.transformers?.length ? { transformers: options.transformers } : {}),\n }) as HastNode;\n } catch {\n /* A missing grammar, an unloadable theme or a transformer throwing must not\n take the surface down. Plain text is always readable. */\n return toPlainLines(source, startLine);\n }\n\n const codeElement = findCodeElement(root);\n if (!codeElement) return toPlainLines(source, startLine);\n\n const lines: CodeBlockLine[] = [];\n for (const child of codeElement.children ?? []) {\n if (child.type !== 'element') continue;\n const tokens: CodeBlockToken[] = [];\n collectTokens(child, tokens, false);\n lines.push({\n tokens,\n number: startLine + lines.length,\n text: tokens.map((token) => token.content).join(''),\n state: stateFromClasses(classListOf(child)),\n });\n }\n\n if (!lines.length) return toPlainLines(source, startLine);\n\n applyPropState(lines, options);\n return reuseUnchangedLines(signature, lines);\n}\n\n/** Test seam: drops the singleton and every cached document. */\nexport function resetCodeBlockHighlighter(): void {\n highlighterPromise = null;\n loadedLanguages.clear();\n loadedThemes.clear();\n previousDocuments.clear();\n}\n\n/* -------------------------------------------------------------------------- */\n/* Unified diff */\n/* -------------------------------------------------------------------------- */\n\nexport type CodeBlockPatchFile = {\n /** New-side path, or the old one for a deletion. */\n file: string;\n /** Unified view: context, removed and added lines with dual gutter labels. */\n lines: CodeBlockLine[];\n added: number;\n removed: number;\n hunks: { header: string; at: number }[];\n};\n\n/**\n * A `git diff` / unified patch, as renderable per-file line sets: feed each\n * file's `lines` to the `lines` prop and the diff tints, `+`/`-` glyphs and\n * dual old/new gutter numbers all come from the parse - no `diff` prop\n * arithmetic against a hand-concatenated string. Tokens are plain; patches\n * read by tint, not grammar.\n */\nexport function parseUnifiedDiff(patch: string): CodeBlockPatchFile[] {\n const files: CodeBlockPatchFile[] = [];\n let current: CodeBlockPatchFile | null = null;\n let oldNumber = 0;\n let newNumber = 0;\n let width = 4;\n\n const push = (text: string, state: CodeBlockLineState | undefined, gutter: string) => {\n if (!current) return;\n current.lines.push({\n number: current.lines.length + 1,\n text,\n tokens: [{ content: text }],\n state,\n gutter,\n });\n };\n const pad = (value: number | null) => (value === null ? '' : String(value)).padStart(width);\n\n for (const raw of normalizeCode(patch).split('\\n')) {\n const fileHeader = raw.match(/^diff --git a\\/(.+) b\\/(.+)$/);\n const plusHeader = raw.match(/^\\+\\+\\+ (?:b\\/)?(.+)$/);\n if (fileHeader || plusHeader) {\n const name = fileHeader ? fileHeader[2] : plusHeader![1];\n if (name !== '/dev/null' && (!current || current.file !== name)) {\n current = { file: name, lines: [], added: 0, removed: 0, hunks: [] };\n files.push(current);\n }\n continue;\n }\n if (/^(---|index |old mode|new mode|new file|deleted file|similarity|rename |Binary )/.test(raw)) {\n continue;\n }\n\n const hunk = raw.match(/^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@(.*)$/);\n if (hunk && current) {\n oldNumber = Number(hunk[1]);\n newNumber = Number(hunk[3]);\n width = Math.max(\n String(oldNumber + Number(hunk[2] ?? 0)).length,\n String(newNumber + Number(hunk[4] ?? 0)).length,\n 2,\n );\n current.hunks.push({ header: raw, at: current.lines.length + 1 });\n push(hunk[5].trim() || raw, { level: 'info' }, `${'·'.padStart(width)} ${'·'.padStart(width)}`);\n continue;\n }\n if (!current || (!raw && files.length === 0)) continue;\n\n if (raw.startsWith('+')) {\n push(raw.slice(1), { diff: 'add' }, `${pad(null)} ${pad(newNumber)}`);\n newNumber += 1;\n current.added += 1;\n } else if (raw.startsWith('-')) {\n push(raw.slice(1), { diff: 'remove' }, `${pad(oldNumber)} ${pad(null)}`);\n oldNumber += 1;\n current.removed += 1;\n } else if (raw.startsWith(' ') || raw === '') {\n if (current.lines.length === 0 && raw === '') continue;\n push(raw.slice(1), undefined, `${pad(oldNumber)} ${pad(newNumber)}`);\n oldNumber += 1;\n newNumber += 1;\n }\n }\n\n return files;\n}\n","'use client';\n\n// Title: Code Block\n// Description: Shadcn code block with Shiki highlighting, streaming, diffs, folding and per-line interaction for AI chat UIs.\n// Vendored from reui (via the playground port) and trimmed for seal-editor: the\n// markdown-transcript helpers, ANSI terminal renderer and download button are\n// cut; affordances woven into the core (streaming plumbing, line-action\n// registry) stay so the render path stays upstream-shaped.\nimport { Button } from '#components/ui/button';\nimport { cn } from '#lib/utils';\nimport {\n Children,\n createContext,\n isValidElement,\n memo,\n useCallback,\n useContext,\n useDeferredValue,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { CSSProperties, ComponentProps, KeyboardEvent as ReactKeyboardEvent, ReactNode } from 'react';\n\nimport { highlightCode, resolveCodeBlockLanguage, stripNotationComments, toPlainLines } from './code-block-highlight';\nimport type {\n CodeBlockDiffSpec,\n CodeBlockLevelSpec,\n CodeBlockLine,\n CodeBlockLineActionsRender,\n CodeBlockLineSpec,\n CodeBlockThemes,\n CodeBlockToken,\n CodeBlockTransformer,\n CodeBlockWordSpec,\n} from './code-block-highlight';\n\n/* -------------------------------------------------------------------------- */\n/* Context */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Two contexts: the document changes on every streamed chunk, the config does\n * not. Splitting them keeps a stream from re-rendering the chrome per chunk.\n */\ntype CodeBlockConfigValue = {\n language?: string;\n resolvedLanguage?: string;\n showLineNumbers: boolean;\n wrap: boolean;\n setWrap: (wrap: boolean) => void;\n wrapControlled: boolean;\n expanded: boolean;\n setExpanded: (expanded: boolean) => void;\n collapsible: boolean;\n streaming: boolean;\n contentId: string;\n};\n\ntype CodeBlockDocumentValue = {\n code: string;\n lines: CodeBlockLine[];\n selected: Set<number>;\n selectable: boolean;\n toggleLine: (line: number, extend: boolean) => void;\n clearSelection: () => void;\n foldable: boolean;\n foldRegions: CodeBlockFoldRegion[];\n folded: Set<number>;\n toggleFold: (start: number) => void;\n setFolded: (folded: number[]) => void;\n};\n\nconst CodeBlockConfigContext = createContext<CodeBlockConfigValue | null>(null);\nconst CodeBlockDocumentContext = createContext<CodeBlockDocumentValue | null>(null);\n\n/** Lets the copy button tell a header placement from a floating one. */\nconst CodeBlockHeaderContext = createContext(false);\n\n/**\n * The root prepares every prop the code surface needs and publishes the\n * bundle here. `CodeBlockContent` reads it to render the surface wherever the\n * consumer composed it - typically inside their own ScrollArea - and the root\n * renders its built-in scrolling surface only when no `CodeBlockContent` is\n * found among its children.\n */\nconst CodeBlockSurfacePropsContext = createContext<CodeBlockSurfaceProps | null>(null);\n\n/**\n * `CodeBlockLineActions` renders nothing itself: it registers its render prop\n * here and the ACTIVE ROW renders it, so actions stay aligned when soft wrap\n * gives lines different heights.\n */\nexport type CodeBlockLineActionsSide = 'end' | 'gutter';\n\nconst CodeBlockActionsContext = createContext<{\n register: (owner: object, render: CodeBlockLineActionsRender | null, side?: CodeBlockLineActionsSide) => void;\n} | null>(null);\n\n/* Next still server-renders \"use client\" files, and a bare useLayoutEffect\n logs on every SSR pass. Same alias the event calendar ships. */\nconst useIsoLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;\n\nfunction useInternalConfig(part: string): CodeBlockConfigValue {\n const context = useContext(CodeBlockConfigContext);\n if (!context) {\n throw new Error(`${part} must be used within a CodeBlock`);\n }\n return context;\n}\n\nfunction useCodeBlockConfig(part: string): CodeBlockConfigValue & { code: string } {\n const context = useContext(CodeBlockConfigContext);\n const document = useContext(CodeBlockDocumentContext);\n if (!context || !document) {\n throw new Error(`${part} must be used within a CodeBlock`);\n }\n /* `code` lives on the DOCUMENT context (it changes per streamed chunk);\n keeping it out of the config is what lets chrome that ignores the source\n skip those re-renders. The public hook still returns it. */\n return useMemo(() => ({ ...context, code: document.code }), [context, document.code]);\n}\n\n/* -------------------------------------------------------------------------- */\n/* Styling */\n/* -------------------------------------------------------------------------- */\n\n/* Radius per style. Every style is named, including the zero rungs: a missing\n rung is a style someone forgot. The parity suite enforces all-eight-or-one. */\nconst ROOT_GHOST_CLASS = '[--code-block-bg:transparent]';\n\nconst ROOT_RADIUS_CLASS = '[--code-block-radius:var(--radius-lg)]';\n\n/*\n * Colour vars use the BASE theme tokens (--card, --primary, ...), never the\n * --color-* twins: those are static @theme literals that ignore a nested\n * `.dark` scope, which once painted a white gutter cell on a forced-dark\n * block. Tints are one system, 8% light / 12% dark (yellow a touch more),\n * matching the *-light badge weights. This item ships the same cssVars.\n */\nconst ROOT_TOKEN_CLASS = [\n '[--code-block-padding:--spacing(3)]',\n '[--code-block-gutter-width:--spacing(9)]',\n '[--code-block-gutter-gap:--spacing(2.5)]',\n /* The fold channel sits between the numbers and the code, so both the number\n cell's padding and the gutter width are expressed against it and a block\n without folding pays nothing for it. */\n '[--code-block-fold-width:--spacing(4.5)]',\n '[--code-block-number-pad:var(--code-block-gutter-gap)]',\n 'data-[gutter-channel]:[--code-block-number-pad:calc(var(--code-block-gutter-gap)+var(--code-block-fold-width))]',\n /* A diff column breathes: the +/- glyph gets a wider channel. */\n 'data-[has-diff]:[--code-block-gutter-gap:--spacing(4)]',\n '[--code-block-line-height:1.5rem]',\n '[--code-block-font-size:0.8125rem]',\n '[--code-block-copy-inset:--spacing(2)]',\n /* Where a PINNED control starts. With a header composed in, the root's top\n edge is the header, so an unadjusted inset drops the copy button onto the\n title row instead of the code. The header's own `min-h-9` is the height\n assumed here, plus its 1px bottom border; override the variable for a\n header built taller than one row. */\n '[--code-block-header-height:--spacing(9)]',\n '[--code-block-copy-top:var(--code-block-copy-inset)]',\n 'data-[has-header]:[--code-block-copy-top:calc(var(--code-block-header-height)+1px+var(--code-block-copy-inset))]',\n /* Room the expanded block keeps under the last line for the floating\n \"Show less\" control. */\n '[--code-block-expand-clearance:--spacing(9)]',\n /* What sits behind a sticky line number while the code scrolls under it.\n `ghost` blanks it, because there the surface belongs to the wrapper. */\n '[--code-block-bg:var(--card)]',\n '[--code-block-highlight-bg:color-mix(in_oklch,var(--primary)_6%,transparent)]',\n 'dark:[--code-block-highlight-bg:color-mix(in_oklch,var(--primary)_12%,transparent)]',\n '[--code-block-highlight-bar:color-mix(in_oklch,var(--primary)_60%,transparent)]',\n '[--code-block-diff-add-bg:color-mix(in_oklch,var(--success)_8%,transparent)]',\n 'dark:[--code-block-diff-add-bg:color-mix(in_oklch,var(--success)_12%,transparent)]',\n '[--code-block-diff-remove-bg:color-mix(in_oklch,var(--destructive)_8%,transparent)]',\n 'dark:[--code-block-diff-remove-bg:color-mix(in_oklch,var(--destructive)_12%,transparent)]',\n '[--code-block-error-bg:color-mix(in_oklch,var(--destructive)_8%,transparent)]',\n 'dark:[--code-block-error-bg:color-mix(in_oklch,var(--destructive)_12%,transparent)]',\n '[--code-block-warning-bg:color-mix(in_oklch,var(--warning)_10%,transparent)]',\n 'dark:[--code-block-warning-bg:color-mix(in_oklch,var(--warning)_14%,transparent)]',\n '[--code-block-info-bg:color-mix(in_oklch,var(--info)_8%,transparent)]',\n 'dark:[--code-block-info-bg:color-mix(in_oklch,var(--info)_12%,transparent)]',\n '[--code-block-caret-color:var(--primary)]',\n].join(' ');\n\nconst ROOT_BASE_CLASS = 'group/code-block relative flex min-w-0 flex-col rounded-(--code-block-radius) text-left';\n\nconst ROOT_SURFACE_CLASS = 'border border-border bg-card text-card-foreground bg-clip-padding';\n\n/**\n * The token colour switch: tokens carry only `--cb-c` / `--cb-cd`, and these\n * two rules recolour the whole block per theme instead of a `dark:` utility on\n * every span. The `color:` hint is load-bearing: `text-[var(--x)]` is\n * type-ambiguous to Tailwind and emits NO rule at all, which renders as a\n * highlighter that silently failed. Same reason the font size uses `length:`.\n */\nconst TOKEN_COLOR_CLASS =\n '[&_[data-slot=code-block-token]]:text-[color:var(--cb-c,currentColor)] dark:[&_[data-slot=code-block-token]]:text-[color:var(--cb-cd,currentColor)]';\n\n/**\n * Line numbers are a CSS counter, never DOM: a pseudo-element cannot join a\n * text selection, so copying yields exact source, and the gutter costs zero\n * elements. `startLine` is a counter reset on the pre.\n */\nconst LINE_NUMBER_CLASS = [\n '[[data-code-line-numbers]_&]:before:pointer-events-none',\n '[[data-code-line-numbers]_&]:before:sticky',\n '[[data-code-line-numbers]_&]:before:left-0',\n '[[data-code-line-numbers]_&]:before:z-10',\n '[[data-code-line-numbers]_&]:before:-ml-(--code-block-gutter-width)',\n '[[data-code-line-numbers]_&]:before:inline-block',\n '[[data-code-line-numbers]_&]:before:w-(--code-block-gutter-width)',\n '[[data-code-line-numbers]_&]:before:pl-(--code-block-padding)',\n '[[data-code-line-numbers]_&]:before:pr-(--code-block-number-pad)',\n '[[data-code-line-numbers]_&]:before:bg-(--code-block-bg)',\n '[[data-code-line-numbers]_&]:before:text-right',\n '[[data-code-line-numbers]_&]:before:tabular-nums',\n '[[data-code-line-numbers]_&]:before:text-muted-foreground/50',\n '[[data-code-line-numbers]_&]:before:select-none',\n '[[data-code-line-numbers]_&]:before:[counter-increment:cb-line]',\n '[[data-code-line-numbers]_&]:before:content-[counter(cb-line)]',\n /* A line carrying its own gutter label (a patch's dual numbers, a hunk\n marker) renders that instead of the counter. */\n '[[data-code-line-numbers]_&[data-gutter]]:before:content-[attr(data-gutter)]',\n '[[data-code-line-numbers]_&[data-gutter]]:before:whitespace-pre',\n].join(' ');\n\n/** Diff glyphs ride in the same pseudo-element family, so they never copy. */\n/**\n * The diff glyph gets its own channel, centred in the left padding (no\n * numbers) or in the number-to-code gap. A fixed `left-1` had ~1px of\n * clearance and would land on the first character.\n */\nconst LINE_DIFF_CLASS = [\n 'data-[diff]:after:absolute',\n /* Above the number cell. That cell is sticky with an opaque backdrop at z-10,\n so a glyph sharing its column is painted out at the default z. */\n 'data-[diff]:after:z-20',\n 'data-[diff]:after:left-0',\n 'data-[diff]:after:w-(--code-block-padding)',\n 'data-[diff]:after:text-center',\n '[[data-code-line-numbers]_&]:data-[diff]:after:-left-(--code-block-gutter-gap)',\n '[[data-code-line-numbers]_&]:data-[diff]:after:w-(--code-block-gutter-gap)',\n 'data-[diff]:after:select-none',\n 'data-[diff=add]:after:text-success',\n 'data-[diff=remove]:after:text-destructive',\n \"data-[diff=add]:after:content-['+']\",\n \"data-[diff=remove]:after:content-['-']\",\n].join(' ');\n\nconst LINE_STATE_CLASS = [\n 'data-[highlighted]:bg-(--code-block-highlight-bg)',\n 'data-[highlighted]:shadow-[inset_2px_0_0_0_var(--code-block-highlight-bar)]',\n 'data-[diff=add]:bg-(--code-block-diff-add-bg)',\n 'data-[diff=remove]:bg-(--code-block-diff-remove-bg)',\n 'data-[level=error]:bg-(--code-block-error-bg)',\n 'data-[level=warning]:bg-(--code-block-warning-bg)',\n 'data-[level=info]:bg-(--code-block-info-bg)',\n 'data-[selected]:bg-accent',\n /* Arrow navigation needs a visible position (WCAG 2.4.7); gated on the\n selectable listbox so plain blocks stay inert on hover. */\n '[[data-selectable]_&]:data-[active]:bg-muted/40',\n].join(' ');\n\n/**\n * Focus mode dims the rest and clears on hover.\n *\n * Blur alone makes a snippet unreadable if the reader wanted the surrounding\n * context after all, so hovering the block restores everything.\n */\n/**\n * Motion is streaming-only and compositor-only: rows keep identity across\n * chunks, so the entry animation fires once per NEW line and a static block\n * never animates. Tokens deliberately do not animate (the plain tail gaining\n * colour remounts them mid-read). Reduced motion disables the row entry.\n */\nconst LINE_MOTION_CLASS =\n '[[data-streaming]_&]:animate-in [[data-streaming]_&]:fade-in-0 [[data-streaming]_&]:slide-in-from-bottom-1 [[data-streaming]_&]:duration-150 [[data-streaming]_&]:ease-out motion-reduce:animate-none';\n\n/**\n * Deliberately no per-token animation. The highlighter runs a chunk behind\n * the stream and REPLACES the plain-text fallback lines with tokenised ones,\n * which remounts every token span; a mount-keyed fade therefore re-flashed\n * whole lines that were already readable, 300ms behind the caret. Colour\n * arriving instantly reads as highlighting; re-fading reads as a glitch.\n */\nconst TOKEN_MOTION_CLASS = '';\n\nconst LINE_FOCUS_CLASS =\n 'data-[blurred]:opacity-40 data-[blurred]:blur-[1.5px] data-[blurred]:transition-[opacity,filter] group-hover/code-block:data-[blurred]:opacity-100 group-hover/code-block:data-[blurred]:blur-none';\n\n/**\n * `data-code-line`, not `data-line`: rehype-pretty-code apps ship a global\n * `[data-line] span { color !important }` rule, and reusing its attribute\n * rendered every token monochrome. Cost a real investigation.\n */\nconst LINE_BASE_CLASS = cn(\n 'relative block min-h-(--code-block-line-height) w-full px-(--code-block-padding) leading-(--code-block-line-height)',\n /* With numbers on, the left inset lives inside the sticky cell instead, so\n paying it here too would restore the double count the gutter just lost. */\n '[[data-code-line-numbers]_&]:pl-0',\n);\n\n/* -------------------------------------------------------------------------- */\n/* Folding */\n/* -------------------------------------------------------------------------- */\n\nexport type CodeBlockFoldRegion = {\n /** Source line that owns the toggle and stays visible when folded. */\n start: number;\n /** Last source line the region swallows. */\n end: number;\n};\n\nconst INDENT_RE = /^[ \\t]*/;\n\n/**\n * Regions come from indentation, not the grammar: shiki returns tokens, no\n * AST, and indentation is language-agnostic where a brace matcher is not.\n * A tab counts as two columns; only self-consistency matters.\n */\nfunction computeFoldRegions(lines: CodeBlockLine[]): CodeBlockFoldRegion[] {\n const indent = (text: string) => (INDENT_RE.exec(text)?.[0] ?? '').replace(/\\t/g, ' ').length;\n const blank = (text: string) => text.trim().length === 0;\n\n /* One pass with an open-region stack instead of a nested scan per line:\n the old shape was quadratic on monotonically indenting files and re-ran\n per streamed chunk. A region closes when a non-blank line returns to its\n opener's indent; trailing blanks belong to whatever follows. */\n const regions: CodeBlockFoldRegion[] = [];\n const stack: { start: number; indent: number; last: number }[] = [];\n let previous: { number: number; indent: number } | null = null;\n\n for (const line of lines) {\n if (blank(line.text)) continue;\n const own = indent(line.text);\n\n while (stack.length && own <= stack[stack.length - 1].indent) {\n const open = stack.pop()!;\n if (open.last > open.start) {\n regions.push({ start: open.start, end: open.last });\n }\n }\n for (const open of stack) open.last = line.number;\n\n if (previous && own > previous.indent) {\n stack.push({\n start: previous.number,\n indent: previous.indent,\n last: line.number,\n });\n }\n previous = { number: line.number, indent: own };\n }\n\n while (stack.length) {\n const open = stack.pop()!;\n if (open.last > open.start) regions.push({ start: open.start, end: open.last });\n }\n\n return regions.sort((a, b) => a.start - b.start);\n}\n\n/** Frozen so the disabled path keeps a stable identity across renders. */\nconst EMPTY_FOLD_REGIONS: CodeBlockFoldRegion[] = [];\n\n/* -------------------------------------------------------------------------- */\n/* Tokens */\n/* -------------------------------------------------------------------------- */\n\ntype TokenStyle = CSSProperties & Record<'--cb-c' | '--cb-cd', string>;\n\nfunction tokenStyle(token: CodeBlockToken): CSSProperties | undefined {\n if (!token.color && !token.colorDark && !token.fontStyle) return undefined;\n\n const style: Partial<TokenStyle> = {};\n if (token.color) style['--cb-c'] = token.color;\n if (token.colorDark) style['--cb-cd'] = token.colorDark;\n if (token.fontStyle === 'italic') style.fontStyle = 'italic';\n if (token.fontStyle === 'bold') style.fontWeight = 700;\n if (token.fontStyle === 'underline') style.textDecoration = 'underline';\n return style as CSSProperties;\n}\n\nfunction CodeBlockTokens({ tokens }: { tokens: CodeBlockToken[] }) {\n return (\n <>\n {tokens.map((token, index) => {\n const style = tokenStyle(token);\n\n /* An unstyled token needs no element at all. Whitespace and\n punctuation are most of a file, so this removes most of the spans. */\n if (!style && !token.word) {\n return <span key={index}>{token.content}</span>;\n }\n\n return (\n <span\n key={index}\n data-slot='code-block-token'\n data-word={token.word || undefined}\n className={cn(token.word && 'bg-primary/10 ring-primary/20 rounded-sm px-0.5 py-px ring-1 ring-inset')}\n style={style}\n >\n {token.content}\n </span>\n );\n })}\n </>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Line */\n/* -------------------------------------------------------------------------- */\n\ntype CodeBlockLineRowProps = {\n line: CodeBlockLine;\n selectable: boolean;\n selected: boolean;\n focusMode: boolean;\n active: boolean;\n caret: boolean;\n actions?: CodeBlockLineActionsRender | null;\n actionsSide?: CodeBlockLineActionsSide;\n domIdBase?: string;\n startLine?: number;\n onSelect?: (line: number, extend: boolean) => void;\n foldable?: boolean;\n foldRegion?: CodeBlockFoldRegion;\n folded?: boolean;\n onToggleFold?: (start: number) => void;\n};\n\n/**\n * Plain reference-equality `memo`, on purpose: the highlighter returns the\n * SAME object for an unchanged line, so a streamed chunk re-renders one row.\n * A custom comparator would spend that win walking every token.\n */\nconst CodeBlockLineRow = memo(function CodeBlockLineRow({\n line,\n selectable,\n selected,\n focusMode,\n active,\n caret,\n actions,\n actionsSide = 'end',\n domIdBase,\n startLine = 1,\n onSelect,\n foldable: _,\n foldRegion,\n folded,\n onToggleFold,\n}: CodeBlockLineRowProps) {\n const state = line.state;\n const blurred = focusMode && !state?.focused;\n const lineDomId = domIdBase ? `${domIdBase}-L${line.number}` : undefined;\n const hiddenCount = foldRegion ? foldRegion.end - foldRegion.start : 0;\n /* The fold toggle owns the channel, so a fold-start row keeps its chevron\n and sends the action back to the row end. */\n const inGutter = actionsSide === 'gutter' && !foldRegion;\n\n return (\n <span\n data-slot='code-block-line'\n data-code-line={line.number}\n data-gutter={line.gutter}\n id={lineDomId}\n data-active={active || undefined}\n data-highlighted={state?.highlighted || undefined}\n data-diff={state?.diff}\n data-focused={state?.focused || undefined}\n data-blurred={blurred || undefined}\n data-level={state?.level}\n data-selected={selected || undefined}\n role={selectable ? 'option' : undefined}\n aria-selected={selectable ? selected : undefined}\n tabIndex={selectable ? -1 : undefined}\n onMouseDown={\n /* Shift-click is line-range selection here, but the browser also reads\n it as \"extend the text selection\", which paints a stray highlight\n across the block. Suppressing it only for the shift case leaves plain\n drag-to-select, and therefore copying, untouched. */\n selectable\n ? (event) => {\n if (event.shiftKey) event.preventDefault();\n }\n : undefined\n }\n onClick={selectable && onSelect ? (event) => onSelect(line.number, event.shiftKey) : undefined}\n className={cn(\n LINE_BASE_CLASS,\n LINE_NUMBER_CLASS,\n LINE_STATE_CLASS,\n LINE_DIFF_CLASS,\n LINE_FOCUS_CLASS,\n LINE_MOTION_CLASS,\n selectable && 'cursor-pointer',\n )}\n /**\n * Every row re-seeds the counter, making its number absolute rather\n * than ordinal. That serves two features at once: folding can remove\n * rows without renumbering the tail, and `content-visibility` can skip\n * offscreen rows - a style-contained row cannot read a shared counter,\n * which once rendered every gutter number as 1.\n */\n style={{ counterReset: `cb-line ${line.number - 1}` }}\n >\n {foldRegion && onToggleFold ? (\n <button\n type='button'\n /* Inside a role=\"option\" row a focusable descendant is invalid ARIA,\n so the toggle leaves the tab order when the block is selectable;\n pointer use is unaffected. */\n tabIndex={selectable ? -1 : undefined}\n data-slot='code-block-fold-toggle'\n data-state={folded ? 'folded' : 'unfolded'}\n aria-expanded={!folded}\n aria-label={\n folded\n ? `Unfold lines ${foldRegion.start + startLine - 1} to ${foldRegion.end + startLine - 1}`\n : `Fold lines ${foldRegion.start + startLine - 1} to ${foldRegion.end + startLine - 1}`\n }\n onClick={(event) => {\n /* The row itself may be a selection target, and a fold is not a\n selection. */\n event.stopPropagation();\n onToggleFold(foldRegion.start);\n }}\n className={cn(\n /* Styled as a ghost xs button rather than composed from Button:\n the ghost variant paints aria-expanded:bg-muted, which would\n keep every UNFOLDED toggle lit, and its icon rungs do not fit\n an 18px channel. Hover feedback only; folded state reads\n through color. */\n /* Square, centred in the line box: height matches the width and\n the translate makes up the difference to the 24px line. Open\n regions read darker than folded ones. */\n 'text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:ring-ring/50 sticky z-20 inline-flex size-(--code-block-fold-width) shrink-0 translate-y-[calc((var(--code-block-line-height)-var(--code-block-fold-width))/2)] cursor-pointer items-center justify-center rounded-[4px] align-top outline-none focus-visible:ring-2',\n /* Margins cancel: -(4px + width) then +4px, so the toggle paints\n in the channel, sits 4px from the code, and the code still\n starts where a row without a toggle does. An uncancelled\n margin slid every foldable row and clipped characters. The\n spacing() shorthand is written as a rem literal because\n `mr-(--spacing(1))` is not valid Tailwind and generates no\n rule at all - measured live as a 4px drift. */\n '-ml-[calc(var(--code-block-fold-width)+0.25rem)]',\n 'mr-[0.25rem]',\n 'left-[calc(var(--code-block-gutter-width)-var(--code-block-fold-width)-0.25rem)]',\n folded && 'text-muted-foreground/60',\n )}\n >\n <svg\n aria-hidden='true'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2.5'\n strokeLinecap='round'\n strokeLinejoin='round'\n className='size-3'\n >\n <path d={folded ? 'm9 18 6-6-6-6' : 'm6 9 6 6 6-6'} />\n </svg>\n </button>\n ) : null}\n {actions && active ? (\n <span\n data-slot='code-block-line-actions'\n data-side={inGutter ? 'gutter' : 'end'}\n contentEditable={false}\n /* An action is a control, not line content. Without this the click\n bubbles to a selectable row and toggles the very selection the\n action just consumed, so \"add these 6 lines\" left line 6 selected\n behind it. The consumer's own handler has already run by here. */\n onClick={(event) => event.stopPropagation()}\n className={cn(\n /* Inside the <pre>, so the code surface's mono face, pre\n whitespace and token color would cascade into real buttons\n placed here. Reset to app typography at the boundary. */\n 'text-foreground z-20 items-center gap-1 font-sans whitespace-normal select-none',\n !inGutter && 'absolute end-2 top-0 flex h-(--code-block-line-height)',\n /* ABSOLUTE and centred on the row's START EDGE: `left-0` is where\n the code column begins (the row box already excludes the\n gutter), and the -50% translate hangs the control half over the\n number column, half over the code - the editor treatment. Being\n out of flow on BOTH axes is the point: an in-flow version\n pushed the code right by its own width, and an earlier block\n level one broke the row onto a new line. The control travels\n with the code on a horizontal scroll; it cannot also be sticky. */\n inGutter && 'absolute top-1/2 left-0 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center',\n )}\n >\n {actions({ line: line.number, text: line.text, state })}\n </span>\n ) : null}\n <span data-slot='code-block-line-content'>\n {/* An empty line needs an explicit break. Copying a selection relies on\n the browser emitting a newline between block-level rows, and a row\n with no text at all contributes neither text nor separator, so a\n blank line silently vanishes from the pasted source. */}\n {line.tokens.length === 0 ? <br /> : null}\n <CodeBlockTokens tokens={line.tokens} />\n {folded && hiddenCount > 0 ? (\n /* `select-none` is doing real work: a manual drag-select over a\n folded block would otherwise paste this chip into the middle of\n the source. The copy button reads the raw code and never sees it. */\n <button\n type='button'\n tabIndex={selectable ? -1 : undefined}\n data-slot='code-block-fold-marker'\n aria-label={`Unfold ${hiddenCount} hidden lines`}\n onClick={(event) => {\n event.stopPropagation();\n onToggleFold?.(line.number);\n }}\n /* Same centring as the fold toggle: chip height matches the fold\n channel and the translate makes up the difference to the line\n box, so the label sits on the code baseline's visual centre\n instead of riding high. */\n className='bg-muted/80 text-muted-foreground hover:bg-muted ml-2 inline-flex h-(--code-block-fold-width) translate-y-[calc((var(--code-block-line-height)-var(--code-block-fold-width))/2)] cursor-pointer items-center rounded-sm px-1.5 align-top text-[0.8em] leading-none select-none'\n >\n {`... ${hiddenCount} lines`}\n </button>\n ) : null}\n {caret ? (\n <span\n data-slot='code-block-caret'\n aria-hidden='true'\n /* An underline caret: a thin strip on the baseline, the shape a\n terminal uses, instead of a bold block that outweighs the text\n it follows. */\n className='ml-0.5 inline-block h-[0.12em] w-[0.55em] translate-y-[0.02em] animate-pulse rounded-[1px] bg-(--code-block-caret-color) motion-reduce:animate-none'\n />\n ) : null}\n </span>\n </span>\n );\n});\n\n/* -------------------------------------------------------------------------- */\n/* Root */\n/* -------------------------------------------------------------------------- */\n\nexport type CodeBlockProps = {\n code?: string;\n language?: string;\n lines?: CodeBlockLine[];\n themes?: CodeBlockThemes;\n highlight?: boolean;\n showLineNumbers?: boolean;\n startLine?: number;\n wrap?: boolean;\n defaultWrap?: boolean;\n onWrapChange?: (wrap: boolean) => void;\n maxLines?: number;\n variant?: 'default' | 'ghost';\n label?: string;\n highlightedLines?: CodeBlockLineSpec;\n highlightedWords?: CodeBlockWordSpec[];\n focusedLines?: CodeBlockLineSpec;\n diff?: CodeBlockDiffSpec;\n lineLevels?: CodeBlockLevelSpec;\n transformers?: CodeBlockTransformer[];\n streaming?: boolean;\n selectable?: boolean;\n selectedLines?: number[];\n defaultSelectedLines?: number[];\n onSelectedLinesChange?: (lines: number[]) => void;\n /** Screen-reader text announced when a stream finishes, for localisation. */\n completeAnnouncement?: string;\n /** Collapsed state under `maxLines`, controlled. */\n expanded?: boolean;\n defaultExpanded?: boolean;\n onExpandedChange?: (expanded: boolean) => void;\n /** Detects fold regions from indentation and renders a toggle per region. */\n foldable?: boolean;\n /**\n * Replaces the indentation heuristic with your own regions (source-numbered,\n * like every line spec) - grammar folding, JSON blocks, patch hunks.\n * Implies nothing about `foldable`; pass both.\n */\n foldRegions?: CodeBlockFoldRegion[];\n /** Folded regions by start line, 1-based within `code`. Controlled. */\n folded?: number[];\n /** Folded regions by their start line, uncontrolled. */\n defaultFolded?: number[];\n onFoldedChange?: (folded: number[]) => void;\n children?: ReactNode;\n} & Omit<ComponentProps<'div'>, 'children' | 'onSelect'>;\n\n/**\n * The root. `<CodeBlock code={code} language=\"tsx\" />` is a complete block;\n * passing `lines` from a server component skips the client highlighter and\n * shiki entirely. Children are CHROME ONLY: the root renders the code surface\n * itself, so child order never matters and a wrapper cannot break scrolling.\n */\nfunction CodeBlock({\n code,\n language,\n lines: linesProp,\n themes,\n highlight = true,\n showLineNumbers = false,\n startLine = 1,\n wrap: wrapProp,\n defaultWrap = false,\n onWrapChange,\n maxLines,\n variant = 'default',\n label,\n highlightedLines,\n highlightedWords,\n focusedLines,\n diff,\n lineLevels,\n transformers,\n streaming = false,\n selectable = false,\n selectedLines,\n defaultSelectedLines,\n onSelectedLinesChange,\n completeAnnouncement,\n expanded: expandedProp,\n defaultExpanded = false,\n onExpandedChange,\n foldable = false,\n foldRegions: foldRegionsProp,\n folded,\n defaultFolded,\n onFoldedChange,\n className,\n children,\n ...props\n}: CodeBlockProps) {\n const contentId = useId();\n const source = code ?? '';\n const resolvedLanguage = resolveCodeBlockLanguage(language);\n\n /* The deferred copy is what a fast stream tokenises against. React keeps the\n previous render on screen while the new one is prepared, so the block never\n blanks and the main thread is never blocked by a chunk. */\n const deferredSource = useDeferredValue(source);\n\n const plainLines = useMemo(() => toPlainLines(deferredSource, startLine), [deferredSource, startLine]);\n\n /* The result is tagged with the exact inputs that produced it. Without the\n tag, swapping `code` kept rendering the PREVIOUS document for the whole\n async highlight pass - or spliced two files together when the old result\n was shorter than the new source. */\n const [highlighted, setHighlighted] = useState<{\n source: string;\n spec: string;\n lines: CodeBlockLine[];\n } | null>(null);\n\n const shouldHighlight = !linesProp && highlight && Boolean(resolvedLanguage) && source.length > 0;\n\n /* Presentation props enter the effect as ONE serialized key: they are\n inline literals at most call sites, and reference deps would re-tokenize\n per parent render. The effect parses its inputs back out of the key, so\n used and depended-on values cannot drift. `transformers` holds functions\n and stays a reference dep - hoist it to module scope. */\n const specKey = JSON.stringify([\n themes ?? null,\n startLine,\n highlightedLines ?? null,\n highlightedWords ?? null,\n focusedLines ?? null,\n diff ?? null,\n lineLevels ?? null,\n ]);\n\n const transformersWarnedRef = useRef(false);\n const previousTransformersRef = useRef(transformers);\n useEffect(() => {\n if (\n process.env.NODE_ENV !== 'production' &&\n streaming &&\n previousTransformersRef.current !== transformers &&\n !transformersWarnedRef.current\n ) {\n transformersWarnedRef.current = true;\n console.warn(\n '[code-block] `transformers` changed identity during a stream, which ' +\n 're-tokenizes the whole document per chunk. Hoist the array to ' +\n 'module scope or memoize it.',\n );\n }\n previousTransformersRef.current = transformers;\n }, [transformers, streaming]);\n\n useEffect(() => {\n if (!shouldHighlight) {\n setHighlighted(null);\n return;\n }\n\n const [\n specThemes,\n specStartLine,\n specHighlightedLines,\n specHighlightedWords,\n specFocusedLines,\n specDiff,\n specLineLevels,\n ] = JSON.parse(specKey) as [\n CodeBlockThemes | null,\n number,\n CodeBlockLineSpec | null,\n CodeBlockWordSpec[] | null,\n CodeBlockLineSpec | null,\n CodeBlockDiffSpec | null,\n CodeBlockLevelSpec | null,\n ];\n\n let active = true;\n void highlightCode(deferredSource, {\n language,\n instanceKey: contentId,\n themes: specThemes ?? undefined,\n transformers,\n startLine: specStartLine,\n highlightedLines: specHighlightedLines ?? undefined,\n highlightedWords: specHighlightedWords ?? undefined,\n focusedLines: specFocusedLines ?? undefined,\n diff: specDiff ?? undefined,\n lineLevels: specLineLevels ?? undefined,\n }).then((next) => {\n if (active) {\n setHighlighted({ source: deferredSource, spec: specKey, lines: next });\n }\n });\n\n return () => {\n active = false;\n };\n }, [shouldHighlight, deferredSource, language, transformers, specKey]);\n\n /* Plain text is the floor, never a blank frame: the highlighter runs a\n chunk behind a stream, so its lines show as-is and the remainder appends\n as plain text that gains colour a frame later. */\n const lines = useMemo(() => {\n if (linesProp) return linesProp;\n if (!shouldHighlight || !highlighted) return plainLines;\n /* The merge below is only valid under the STREAMING invariant: the\n highlighted source must be a prefix of what is on screen, with the same\n presentation spec. A swapped `code` or spec falls back to plain text\n for one highlight pass instead of showing the previous document. */\n if (highlighted.spec !== specKey || !deferredSource.startsWith(highlighted.source)) {\n return plainLines;\n }\n if (highlighted.lines.length >= plainLines.length) return highlighted.lines;\n return [...highlighted.lines, ...plainLines.slice(highlighted.lines.length)];\n }, [linesProp, shouldHighlight, highlighted, plainLines, deferredSource, specKey]);\n\n const focusMode = useMemo(() => lines.some((line) => line.state?.focused), [lines]);\n\n const hasDiff = useMemo(() => lines.some((line) => line.state?.diff), [lines]);\n\n const [internalWrap, setInternalWrap] = useState(defaultWrap);\n const wrap = wrapProp ?? internalWrap;\n const setWrap = useCallback(\n (next: boolean) => {\n if (wrapProp === undefined) setInternalWrap(next);\n onWrapChange?.(next);\n },\n [wrapProp, onWrapChange],\n );\n\n const collapsible = typeof maxLines === 'number' && lines.length > maxLines;\n const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);\n const expanded = expandedProp ?? internalExpanded;\n const setExpanded = useCallback(\n (next: boolean) => {\n if (expandedProp === undefined) setInternalExpanded(next);\n onExpandedChange?.(next);\n },\n [expandedProp, onExpandedChange],\n );\n const isExpanded = collapsible ? expanded : true;\n\n const [internalSelected, setInternalSelected] = useState<number[]>(defaultSelectedLines ?? []);\n const selection = selectedLines ?? internalSelected;\n const selectedSet = useMemo(() => new Set(selection), [selection]);\n const lastSelectedRef = useRef<number | null>(null);\n\n /* Latest-value refs, so the toggle callbacks below can be created ONCE.\n With state in their dep lists every selection or fold change minted new\n identities, which reached every row through `onSelect`/`onToggleFold` and\n busted the row memo the streaming design rests on. */\n const selectionStateRef = useRef({\n selection,\n selectedSet,\n controlled: selectedLines !== undefined,\n onSelectedLinesChange,\n });\n selectionStateRef.current = {\n selection,\n selectedSet,\n controlled: selectedLines !== undefined,\n onSelectedLinesChange,\n };\n\n const commitSelection = useCallback((next: number[]) => {\n if (!selectionStateRef.current.controlled) setInternalSelected(next);\n selectionStateRef.current.onSelectedLinesChange?.(next);\n }, []);\n\n const toggleLine = useCallback(\n (line: number, extend: boolean) => {\n const anchor = lastSelectedRef.current;\n const { selection, selectedSet } = selectionStateRef.current;\n\n if (extend && anchor !== null) {\n const from = Math.min(anchor, line);\n const to = Math.max(anchor, line);\n const range: number[] = [];\n for (let value = from; value <= to; value += 1) range.push(value);\n commitSelection(range);\n return;\n }\n\n lastSelectedRef.current = line;\n commitSelection(\n selectedSet.has(line)\n ? selection.filter((value) => value !== line)\n : [...selection, line].sort((a, b) => a - b),\n );\n },\n [commitSelection],\n );\n\n /* Regions are SOURCE-numbered (1-based within `code`), like every other\n line spec, so `folded` values and `highlightedLines` values interchange. */\n const foldRegions = useMemo(() => {\n if (!foldable) return EMPTY_FOLD_REGIONS;\n if (foldRegionsProp) return foldRegionsProp;\n return computeFoldRegions(lines).map((region) => ({\n start: region.start - startLine + 1,\n end: region.end - startLine + 1,\n }));\n }, [foldable, foldRegionsProp, lines, startLine]);\n\n const foldStarts = useMemo(() => new Map(foldRegions.map((region) => [region.start, region])), [foldRegions]);\n\n const [internalFolded, setInternalFolded] = useState<number[]>(defaultFolded ?? []);\n const foldedValue = folded ?? internalFolded;\n const foldedSet = useMemo(() => new Set(foldedValue), [foldedValue]);\n\n /* Same latest-value shape as selection, for the same row-memo reason. */\n const foldStateRef = useRef({\n foldedValue,\n foldedSet,\n controlled: folded !== undefined,\n onFoldedChange,\n });\n foldStateRef.current = {\n foldedValue,\n foldedSet,\n controlled: folded !== undefined,\n onFoldedChange,\n };\n\n const commitFolded = useCallback((next: number[]) => {\n if (!foldStateRef.current.controlled) setInternalFolded(next);\n foldStateRef.current.onFoldedChange?.(next);\n }, []);\n\n const toggleFold = useCallback(\n (start: number) => {\n const { foldedValue, foldedSet } = foldStateRef.current;\n commitFolded(\n foldedSet.has(start)\n ? foldedValue.filter((value) => value !== start)\n : [...foldedValue, start].sort((a, b) => a - b),\n );\n },\n [commitFolded],\n );\n\n /**\n * A line is hidden when ANY folded region covers it, and that one rule is\n * what makes nesting work without a tree: folding an outer region swallows\n * the inner toggles as well, their own folded state survives untouched\n * underneath, and unfolding the outer one restores exactly what the reader\n * left behind rather than a flattened block.\n */\n const renderedLines = useMemo(() => {\n if (!foldable || foldedSet.size === 0) return lines;\n const hidden = new Set<number>();\n for (const start of foldedSet) {\n const region = foldStarts.get(start);\n if (!region) continue;\n for (let n = region.start + 1; n <= region.end; n += 1) {\n hidden.add(n + startLine - 1);\n }\n }\n return lines.filter((line) => !hidden.has(line.number));\n }, [foldable, foldedSet, foldStarts, lines, startLine]);\n\n const configValue = useMemo<CodeBlockConfigValue>(\n () => ({\n language,\n resolvedLanguage,\n showLineNumbers,\n wrap,\n setWrap,\n wrapControlled: wrapProp !== undefined,\n expanded: isExpanded,\n setExpanded,\n collapsible,\n streaming,\n contentId,\n }),\n [\n language,\n resolvedLanguage,\n showLineNumbers,\n wrap,\n setWrap,\n wrapProp,\n isExpanded,\n collapsible,\n streaming,\n contentId,\n ],\n );\n\n const clearSelection = useCallback(() => commitSelection([]), [commitSelection]);\n\n const documentValue = useMemo<CodeBlockDocumentValue>(\n () => ({\n code: source,\n lines,\n selected: selectedSet,\n selectable,\n toggleLine,\n clearSelection,\n foldable,\n foldRegions,\n folded: foldedSet,\n toggleFold,\n setFolded: commitFolded,\n }),\n [\n source,\n lines,\n selectedSet,\n selectable,\n toggleLine,\n clearSelection,\n foldable,\n foldRegions,\n foldedSet,\n toggleFold,\n commitFolded,\n ],\n );\n\n const visibleLines = collapsible && !isExpanded ? maxLines : undefined;\n\n /* Detected from the element type rather than a `:has()` selector. This repo\n has measured `:has()` at 85-120ms of style invalidation per DOM mutation,\n and a streaming block mutates on every chunk, so the primitive uses none. */\n const hasHeader = containsElementType(children, CodeBlockHeader);\n\n const [lineActions, setLineActions] = useState<CodeBlockLineActionsRender | null>(null);\n const [actionsSide, setActionsSide] = useState<CodeBlockLineActionsSide>('end');\n const actionsOwnerRef = useRef<object | null>(null);\n const actionsRegistry = useMemo(\n () => ({\n /* Wrapped in a setter callback: a render prop IS a function, so passing\n it to setState bare would run it as a state updater. Ownership makes\n a stale unmount a no-op and makes a SECOND live group loud in dev -\n the registry holds one render prop, so the second silently won. */\n register: (owner: object, render: CodeBlockLineActionsRender | null, side: CodeBlockLineActionsSide = 'end') => {\n if (render) {\n if (process.env.NODE_ENV !== 'production' && actionsOwnerRef.current && actionsOwnerRef.current !== owner) {\n console.warn(\n '[code-block] Two CodeBlockLineActions are mounted; the block ' +\n 'renders ONE action group and the later mount replaces the ' +\n 'earlier one.',\n );\n }\n actionsOwnerRef.current = owner;\n } else if (actionsOwnerRef.current !== owner) {\n return;\n } else {\n actionsOwnerRef.current = null;\n }\n setLineActions(() => render);\n setActionsSide(side);\n },\n }),\n [],\n );\n\n /* One channel between the numbers and the code, shared by the fold toggle\n and a gutter-side action. Reserved only when something asks for it, so a\n plain block keeps the gutter it always had. */\n /* Only FOLDING reserves gutter space. A gutter-side action floats over the\n row's start edge instead of widening anything, so a block with actions is\n geometrically identical to one without. */\n const gutterChannel = foldable;\n\n /* Walked rather than registered, so server HTML never carries a doubled\n surface. Sees through plain wrappers; a consumer component boundary is\n opaque, which the dev-mode guard in CodeBlockContent reports. */\n const hasContent = containsElementType(children, CodeBlockContent);\n\n const surfaceProps: CodeBlockSurfaceProps = {\n lines: renderedLines,\n gutterMax: lines[lines.length - 1]?.number ?? startLine,\n foldable,\n gutterChannel,\n actionsSide,\n foldStarts,\n foldedSet,\n onToggleFold: toggleFold,\n expandClearance: collapsible && isExpanded,\n wrap,\n startLine,\n showLineNumbers,\n focusMode,\n selectable,\n selectedSet,\n toggleLine,\n visibleLines,\n streaming,\n label: label ?? (language ? `${language} code` : 'Code'),\n contentId,\n actions: lineActions,\n completeAnnouncement,\n builtInSurfaceRendered: !hasContent,\n };\n\n return (\n <CodeBlockConfigContext.Provider value={configValue}>\n <CodeBlockDocumentContext.Provider value={documentValue}>\n <CodeBlockActionsContext.Provider value={actionsRegistry}>\n <div\n data-slot='code-block'\n data-variant={variant}\n data-streaming={streaming || undefined}\n data-has-diff={hasDiff || undefined}\n data-foldable={foldable || undefined}\n data-gutter-channel={gutterChannel || undefined}\n data-has-header={hasHeader || undefined}\n className={cn(\n ROOT_BASE_CLASS,\n ROOT_RADIUS_CLASS,\n ROOT_TOKEN_CLASS,\n variant === 'default' && ROOT_SURFACE_CLASS,\n variant === 'ghost' && ROOT_GHOST_CLASS,\n className,\n )}\n {...props}\n >\n <CodeBlockSurfacePropsContext.Provider value={surfaceProps}>\n {children}\n {!hasContent && <CodeBlockSurface {...surfaceProps} />}\n </CodeBlockSurfacePropsContext.Provider>\n </div>\n </CodeBlockActionsContext.Provider>\n </CodeBlockDocumentContext.Provider>\n </CodeBlockConfigContext.Provider>\n );\n}\n\n/**\n * The code surface as a composable part: inside your own ScrollArea the block\n * stops scrolling internally and the ancestor owns both axes (`maxLines`'\n * height cap no longer applies). Omitted, the root renders the same surface\n * itself, exactly as before.\n */\nfunction CodeBlockContent({ className }: { className?: string }) {\n const surface = useContext(CodeBlockSurfacePropsContext);\n if (!surface) {\n throw new Error('CodeBlockContent must be used within a CodeBlock');\n }\n\n /* The render-time walk cannot see through a consumer's own component\n boundary; when that happens the root also renders its built-in surface\n and the code appears twice. Silent double content is the worst failure\n mode, so development says exactly what to do. */\n useEffect(() => {\n if (process.env.NODE_ENV === 'production') return;\n if (surface.builtInSurfaceRendered) {\n console.error(\n 'CodeBlockContent is hidden behind a component boundary, so CodeBlock ' +\n 'also rendered its built-in surface and the code appears twice. ' +\n \"Compose <CodeBlockContent /> directly in the CodeBlock's children \" +\n '(plain wrappers like a ScrollArea are fine).',\n );\n }\n }, [surface.builtInSurfaceRendered]);\n\n return <CodeBlockSurface {...surface} scroll={false} className={className} />;\n}\n\n/**\n * Sees through plain element wrappers (a ScrollArea, a div) but not through a\n * consumer's own component boundary - unrendered children are opaque. Compose\n * `CodeBlockContent` directly in the block's subtree.\n */\nfunction containsElementType(node: ReactNode, type: unknown): boolean {\n for (const child of Children.toArray(node)) {\n if (!isValidElement(child)) continue;\n if (child.type === type) return true;\n const inner = (child.props as { children?: ReactNode }).children;\n if (inner && containsElementType(inner, type)) return true;\n }\n return false;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Surface */\n/* -------------------------------------------------------------------------- */\n\ntype CodeBlockSurfaceProps = {\n lines: CodeBlockLine[];\n wrap: boolean;\n startLine: number;\n showLineNumbers: boolean;\n focusMode: boolean;\n selectable: boolean;\n selectedSet: Set<number>;\n toggleLine: (line: number, extend: boolean) => void;\n visibleLines?: number;\n gutterMax: number;\n foldable: boolean;\n gutterChannel: boolean;\n actionsSide: CodeBlockLineActionsSide;\n foldStarts: Map<number, CodeBlockFoldRegion>;\n foldedSet: Set<number>;\n onToggleFold: (start: number) => void;\n expandClearance: boolean;\n streaming: boolean;\n label: string;\n contentId: string;\n actions?: CodeBlockLineActionsRender | null;\n completeAnnouncement?: string;\n /** true when the root rendered its own surface (no CodeBlockContent found). */\n builtInSurfaceRendered?: boolean;\n /** false = no scroll container of its own; an ancestor scrolls instead. */\n scroll?: boolean;\n className?: string;\n};\n\nfunction CodeBlockSurface({\n lines,\n wrap,\n startLine,\n showLineNumbers,\n focusMode,\n selectable,\n selectedSet,\n toggleLine,\n visibleLines,\n gutterMax,\n foldable,\n gutterChannel,\n actionsSide,\n foldStarts,\n foldedSet,\n onToggleFold,\n expandClearance,\n streaming,\n label,\n contentId,\n actions,\n completeAnnouncement,\n scroll = true,\n className,\n}: CodeBlockSurfaceProps) {\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const preRef = useRef<HTMLPreElement | null>(null);\n const [activeLine, setActiveLine] = useState<number | null>(null);\n const stickRef = useRef(true);\n\n /* Stick-to-bottom disengages the moment the reader scrolls up, and re-engages\n when they come back. Reading the scroll position on the event is enough\n here because nothing is written back during the same frame. */\n /* Announced once when a stream ends, so a screen reader learns the code is\n complete without hearing it assembled character by character. */\n const [announcement, setAnnouncement] = useState('');\n const wasStreaming = useRef(streaming);\n useEffect(() => {\n if (wasStreaming.current && !streaming) {\n setAnnouncement(completeAnnouncement ?? `Code generation complete, ${lines.length} lines.`);\n }\n wasStreaming.current = streaming;\n }, [streaming, lines.length, completeAnnouncement]);\n\n /* Stick-to-bottom follows whichever element ACTUALLY scrolls - writing\n scrollTop to the composed mode's inert viewport was a silent no-op.\n Resolved per commit: a short block becomes its own scroller the moment\n it outgrows its cap. */\n const resolveScroller = useCallback((): HTMLElement | null => {\n const viewport = viewportRef.current;\n if (!viewport) return null;\n if (viewport.scrollHeight > viewport.clientHeight + 1) return viewport;\n\n let node: HTMLElement | null = viewport.parentElement;\n while (node && node !== document.body) {\n const overflowY = getComputedStyle(node).overflowY;\n if (\n (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') &&\n node.scrollHeight > node.clientHeight + 1\n ) {\n return node;\n }\n node = node.parentElement;\n }\n\n /* An AI transcript's default shape scrolls the PAGE. The walk above never\n reaches the root elements, so without this branch the stream wrote\n scrollTop to an inert viewport and drifted off screen. */\n const page = document.scrollingElement as HTMLElement | null;\n if (page && page.scrollHeight > page.clientHeight + 1) return page;\n\n return viewport;\n }, []);\n\n const handleScroll = useCallback(() => {\n const viewport = viewportRef.current;\n if (!viewport) return;\n const distance = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight;\n stickRef.current = distance < 24;\n }, []);\n\n /* The ancestor case needs its own listener: the reader's intent has to be\n recorded BEFORE the next chunk grows the content, or the distance already\n includes what just arrived. Re-attached per chunk rather than diffed,\n which is a handful of listener swaps a second and keeps this correct when\n the scrolling element changes underneath. */\n useEffect(() => {\n if (!streaming) return;\n const scroller = resolveScroller();\n if (!scroller || scroller === viewportRef.current) return;\n\n const onScroll = () => {\n const distance = scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight;\n stickRef.current = distance < 24;\n };\n\n /* Page scrolls fire on window, not on the scrolling element. */\n const target: EventTarget = scroller === document.scrollingElement ? window : scroller;\n target.addEventListener('scroll', onScroll, { passive: true });\n return () => target.removeEventListener('scroll', onScroll);\n }, [streaming, lines.length, resolveScroller]);\n\n useIsoLayoutEffect(() => {\n if (!streaming || !stickRef.current) return;\n const scroller = resolveScroller();\n if (scroller) scroller.scrollTop = scroller.scrollHeight;\n }, [lines, streaming, resolveScroller]);\n\n /* One listener for the whole surface rather than a handler per line: the\n active row is resolved from the event target, so the floating action group\n costs one element no matter how long the file is. Focus is tracked as well\n as hover, or the actions would be unreachable without a pointer. */\n const trackActive = useCallback(\n (event: { target: EventTarget | null }) => {\n if (!actions && !selectable) return;\n const target = event.target as HTMLElement | null;\n const row = target?.closest?.('[data-code-line]') as HTMLElement | null;\n const value = row?.getAttribute('data-code-line');\n\n /* No row under the pointer means the gutter, which lives in the <pre>'s\n PADDING and so is covered by no row element. Keep the active line\n rather than clearing it: clearing here unmounted a gutter action the\n moment the pointer left the code toward it, so the control vanished\n before it could be pressed. `onPointerLeave` still clears on exit. */\n if (!value) return;\n setActiveLine(Number(value));\n },\n [actions, selectable],\n );\n\n const clearActive = useCallback(() => setActiveLine(null), []);\n\n /* Rows and keyboard speak DISPLAYED numbers (the gutter, data-code-line);\n selection state speaks SOURCE numbers like every other line spec. This is\n the one conversion point between them. */\n const toggleDisplayed = useCallback(\n (displayed: number, extend: boolean) => toggleLine(displayed - startLine + 1, extend),\n [toggleLine, startLine],\n );\n\n const handleKeyDown = useCallback(\n (event: ReactKeyboardEvent<HTMLElement>) => {\n if (!selectable) return;\n\n if (event.key === 'Escape') {\n setActiveLine(null);\n return;\n }\n\n /* Toggle IN PLACE. Shift+Arrow selects while moving, but without this a\n keyboard user could never deselect a single line. */\n if ((event.key === 'Enter' || event.key === ' ') && activeLine !== null) {\n event.preventDefault();\n toggleDisplayed(activeLine, event.shiftKey);\n return;\n }\n\n const isArrow = event.key === 'ArrowDown' || event.key === 'ArrowUp';\n if (!isArrow && event.key !== 'Home' && event.key !== 'End') return;\n event.preventDefault();\n\n /* Step by RENDERED index, not by number arithmetic: folding removes\n rows, and numeric stepping walked the active line into hidden lines\n where it silently vanished. */\n let next: number;\n if (event.key === 'Home') {\n next = lines[0]?.number ?? startLine;\n } else if (event.key === 'End') {\n next = lines[lines.length - 1]?.number ?? startLine;\n } else {\n const step = event.key === 'ArrowDown' ? 1 : -1;\n const index = lines.findIndex((line) => line.number === activeLine);\n const nextIndex =\n index === -1 ? (step === 1 ? 0 : lines.length - 1) : Math.min(lines.length - 1, Math.max(0, index + step));\n next = lines[nextIndex]?.number ?? startLine;\n }\n\n setActiveLine(next);\n const row = preRef.current?.querySelector<HTMLElement>(`[data-code-line=\"${next}\"]`);\n row?.scrollIntoView({ block: 'nearest' });\n if (isArrow && event.shiftKey) toggleDisplayed(next, true);\n },\n [selectable, lines, startLine, activeLine, toggleDisplayed],\n );\n\n const lastLineNumber = lines[lines.length - 1]?.number;\n\n /* `startLine` is just a counter reset. Gutter width follows the widest\n number in the DOCUMENT (not the folded view, or folding the tail would\n shift every row); `ch` is one digit in the mono `pre`, and the floor of 2\n stops the column twitching as a stream crosses line 9. Lines carrying\n their own gutter label (a patch's dual numbers) widen it to the longest\n label instead. */\n const gutterDigits = Math.max(\n 2,\n String(gutterMax ?? startLine).length,\n ...lines.map((line) => line.gutter?.length ?? 0),\n );\n\n const surfaceStyle = {\n '--code-block-gutter-width': gutterChannel\n ? `calc(${gutterDigits}ch + var(--code-block-fold-width) + var(--code-block-gutter-gap) + var(--code-block-padding))`\n : `calc(${gutterDigits}ch + var(--code-block-gutter-gap) + var(--code-block-padding))`,\n 'counterReset': `cb-line ${startLine - 1}`,\n } as CSSProperties;\n\n return (\n <div\n data-slot='code-block-content'\n className={cn(\n 'relative min-w-0 rounded-[inherit]',\n /* Clipping is the scroll-mode contract only: composed, a clipped pre\n would hide its own width from the ancestor's viewport and the\n horizontal scrollbar would never render. */\n scroll && 'flex-1 overflow-hidden',\n )}\n >\n {/* A polite status, deliberately NOT an aria-live region on the code\n itself: a live region over streaming code narrates every token. */}\n <span role='status' aria-live='polite' className='sr-only'>\n {announcement}\n </span>\n {/* A plain overflow container, deliberately not a ScrollArea primitive.\n The scroll must stay INSIDE the block (the sticky gutter, the\n stick-to-bottom stream and `maxLines` all anchor to it), but nothing\n here needs custom scrollbar machinery, and dropping it leaves the\n block with no UI-library dependency at all - which is why both twins\n are the same file. Compose Card, Frame, or your own ScrollArea\n around the block for outer framing. */}\n <div\n ref={viewportRef}\n data-slot='code-block-viewport'\n id={contentId}\n role='region'\n aria-label={label}\n aria-busy={streaming || undefined}\n aria-activedescendant={selectable && activeLine !== null ? `${contentId}-L${activeLine}` : undefined}\n tabIndex={0}\n onScroll={handleScroll}\n onKeyDown={handleKeyDown}\n className={cn(\n 'focus-visible:ring-ring/50 relative min-w-0 rounded-[inherit] outline-none focus-visible:ring-[3px]',\n /* Composed under an ancestor scroller the surface scrolls NOTHING\n itself: both axes belong to the ancestor, whose viewport picks up\n the pre's overflowing width, and the sticky number cell anchors\n to that viewport instead. Mixed ownership shipped once and read\n as two scrollbar styles on one block. */\n scroll && '[scrollbar-width:thin] overflow-auto',\n scroll && visibleLines && 'max-h-(--cb-max-height)',\n className,\n )}\n style={\n scroll && visibleLines\n ? ({\n '--cb-max-height': `calc(${visibleLines} * var(--code-block-line-height) + 2 * var(--code-block-padding))`,\n } as CSSProperties)\n : undefined\n }\n >\n <pre\n ref={preRef}\n dir='ltr'\n data-code-line-numbers={showLineNumbers || undefined}\n data-wrap={wrap || undefined}\n role={selectable ? 'listbox' : undefined}\n data-selectable={selectable || undefined}\n aria-multiselectable={selectable || undefined}\n aria-label={selectable ? `${label} lines` : undefined}\n onPointerOver={trackActive}\n onPointerLeave={clearActive}\n onFocus={trackActive}\n className={cn(\n 'w-max min-w-full py-(--code-block-padding) font-mono text-(length:--code-block-font-size)',\n TOKEN_COLOR_CLASS,\n TOKEN_MOTION_CLASS,\n /* `min-w-0` must beat the `min-w-full` above, or the pre keeps\n a floor of 100% of a scroll container that is itself wider\n than the viewport, and wrapping still leaves a stray\n horizontal scrollbar. */\n wrap && 'w-full max-w-full min-w-0 break-words whitespace-pre-wrap',\n showLineNumbers && 'pl-(--code-block-gutter-width)',\n /* The expand control floats over the surface in BOTH states, so\n an expanded block reserves a row of clearance rather than\n growing a bordered footer under the code. */\n expandClearance && 'pb-(--code-block-expand-clearance)',\n )}\n style={surfaceStyle}\n >\n {/* `content-visibility: auto` is a measured dead end on BOTH axes:\n its style containment breaks the shared gutter counter\n (every number renders as 1), and per-row it still fails -\n the paint containment clips the number cell, which is\n deliberately pulled left OUT of the row's box (verified in\n Chrome: forcing one row visible restored only that row's\n number). Windowing here means rendering fewer rows, not\n containing them. */}\n {/* presentation, so the listbox owns its options directly. */}\n <code data-slot='code-block-code' role={selectable ? 'presentation' : undefined}>\n {lines.map((line) => (\n <CodeBlockLineRow\n key={line.number}\n line={line}\n selectable={selectable}\n selected={selectedSet.has(line.number - startLine + 1)}\n focusMode={focusMode}\n active={activeLine === line.number}\n caret={streaming && line.number === lastLineNumber}\n actions={activeLine === line.number ? actions : null}\n actionsSide={actionsSide}\n domIdBase={contentId}\n startLine={startLine}\n onSelect={toggleDisplayed}\n foldable={foldable}\n foldRegion={foldStarts.get(line.number - startLine + 1)}\n folded={foldedSet.has(line.number - startLine + 1)}\n onToggleFold={onToggleFold}\n />\n ))}\n </code>\n </pre>\n </div>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Chrome */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Opt-in header. Absent, the block renders no chrome, so it nests in a Card or\n * Frame without a doubled bar. Presence flips `data-has-header` on the root,\n * detected from the element type rather than a `:has()` selector.\n */\nfunction CodeBlockHeader({ className, ...props }: ComponentProps<'div'>) {\n return (\n <CodeBlockHeaderContext.Provider value={true}>\n <div\n data-slot='code-block-header'\n className={cn('border-border flex min-h-9 shrink-0 items-center gap-2 border-b px-3 py-0 text-sm', className)}\n {...props}\n />\n </CodeBlockHeaderContext.Provider>\n );\n}\n\nfunction CodeBlockTitle({ className, ...props }: ComponentProps<'div'>) {\n return (\n <div\n data-slot='code-block-title'\n className={cn('text-muted-foreground min-w-0 truncate font-mono text-xs', className)}\n {...props}\n />\n );\n}\n\n/**\n * The resolved language as a label, no props needed. It shows what the\n * highlighter actually resolved, so an unsupported grammar becomes visible\n * instead of silently rendering plain.\n */\nfunction CodeBlockLanguage({ className, children, ...props }: ComponentProps<'span'>) {\n const { language, resolvedLanguage } = useInternalConfig('CodeBlockLanguage');\n const label = children ?? resolvedLanguage ?? language;\n\n if (!label) return null;\n\n return (\n <span\n data-slot='code-block-language'\n data-unsupported={!resolvedLanguage || undefined}\n className={cn(\n 'border-border bg-muted/60 text-muted-foreground inline-flex shrink-0 items-center rounded-md border px-1.5 py-0.5 font-mono text-[0.6875rem] leading-none uppercase',\n className,\n )}\n {...props}\n >\n {label}\n </span>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Copy button */\n/* -------------------------------------------------------------------------- */\n\nfunction CopyIcon({ copied }: { copied: boolean }) {\n return copied ? (\n <svg\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'\n aria-hidden='true'\n >\n <path d='M20 6 9 17l-5-5' />\n </svg>\n ) : (\n <svg\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'\n aria-hidden='true'\n >\n <rect width='14' height='14' x='8' y='8' rx='2' ry='2' />\n <path d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2' />\n </svg>\n );\n}\n\nexport type CodeBlockCopyButtonProps = {\n value?: string;\n timeout?: number;\n onCopy?: (value: string) => void;\n onCopyError?: (error: unknown) => void;\n /** Accessible names, for localisation. */\n labels?: { copy?: string; copied?: string };\n /** `pinned` floats over the surface; `inline` sits in a header. */\n position?: 'auto' | 'pinned' | 'inline';\n alwaysVisible?: boolean;\n} & Omit<ComponentProps<typeof Button>, 'value' | 'onCopy'>;\n\n/**\n * Copy button. Inline inside a header, pinned over the surface anywhere else.\n * Pinning is absolute against the root, not `position: sticky`: sticky still\n * slides away under HORIZONTAL scroll, and code scrolls sideways constantly.\n * Notation comments are stripped from the payload, matching what is rendered.\n */\nfunction CodeBlockCopyButton({\n value,\n timeout = 2000,\n onCopy,\n onCopyError,\n labels,\n position = 'auto',\n alwaysVisible = false,\n className,\n variant = 'ghost',\n size,\n children,\n ...props\n}: CodeBlockCopyButtonProps) {\n const context = useContext(CodeBlockConfigContext);\n const inHeader = useContext(CodeBlockHeaderContext);\n const [copied, setCopied] = useState(false);\n const [copyFailed, setCopyFailed] = useState(false);\n\n const resolvedPosition = position === 'auto' ? (inHeader ? 'inline' : 'pinned') : position;\n\n /* A header is chrome, so the button takes the smaller icon rung there and the\n full one when it floats over the code. Both are per-style ladders in the\n shadcn button, so this stays style-aware instead of pinning a pixel size. */\n const resolvedSize = size ?? (resolvedPosition === 'inline' ? 'icon-sm' : 'icon');\n\n useEffect(() => {\n if (!copied || timeout === 0) return;\n const id = window.setTimeout(() => setCopied(false), timeout);\n return () => window.clearTimeout(id);\n }, [copied, timeout]);\n\n useEffect(() => {\n if (!copyFailed) return;\n const id = window.setTimeout(() => setCopyFailed(false), 2000);\n return () => window.clearTimeout(id);\n }, [copyFailed]);\n\n const document_ = useContext(CodeBlockDocumentContext);\n\n const handleClick = useCallback(() => {\n /* Server-highlighted blocks pass `lines` and no `code`, so the document\n text is the fallback; without it this rendered a labelled control that\n did nothing on the exact path the docs advertise. */\n const fallback = document_?.code || document_?.lines.map((line) => line.text).join('\\n');\n const payload = stripNotationComments(value ?? fallback ?? '');\n if (!payload) return;\n if (typeof navigator === 'undefined' || !navigator.clipboard?.writeText) {\n return;\n }\n\n navigator.clipboard.writeText(payload).then(\n () => {\n setCopied(true);\n onCopy?.(payload);\n },\n /* Rejection is routine: a denied permission, or a document that lost\n focus. Without the handler it surfaced as an unhandled rejection in\n the consumer's error monitoring. */\n (error: unknown) => {\n setCopyFailed(true);\n onCopyError?.(error);\n },\n );\n }, [value, context, document_, onCopy, onCopyError]);\n\n return (\n <Button\n type='button'\n data-slot='code-block-copy'\n data-copied={copied || undefined}\n data-copy-failed={copyFailed || undefined}\n data-position={resolvedPosition}\n aria-label={copied ? (labels?.copied ?? 'Copied') : (labels?.copy ?? 'Copy code')}\n variant={variant}\n size={resolvedSize}\n onClick={handleClick}\n className={cn(\n 'shrink-0 [&_svg]:size-3.5',\n resolvedPosition === 'pinned' &&\n /* Physical `right`, not logical `end`: the code surface is forced\n dir=\"ltr\", so in an RTL app the logical inset landed the button\n over the first characters instead of the trailing edge. */\n 'absolute top-(--code-block-copy-top) right-(--code-block-copy-inset) z-20',\n resolvedPosition === 'pinned' &&\n !alwaysVisible &&\n 'opacity-0 transition-opacity group-hover/code-block:opacity-100 focus-visible:opacity-100',\n className,\n )}\n {...props}\n >\n {children ?? <CopyIcon copied={copied} />}\n </Button>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Wrap and expand */\n/* -------------------------------------------------------------------------- */\n\nfunction CodeBlockWrapToggle({\n className,\n children,\n variant = 'ghost',\n size = 'sm',\n onClick,\n ...props\n}: ComponentProps<typeof Button>) {\n const { wrap, setWrap } = useInternalConfig('CodeBlockWrapToggle');\n\n return (\n <Button\n type='button'\n data-slot='code-block-wrap-toggle'\n data-state={wrap ? 'on' : 'off'}\n aria-pressed={wrap}\n variant={variant}\n size={size}\n onClick={(event) => {\n onClick?.(event);\n if (!event.defaultPrevented) setWrap(!wrap);\n }}\n className={cn('shrink-0', className)}\n {...props}\n >\n {children ?? (wrap ? 'No wrap' : 'Wrap')}\n </Button>\n );\n}\n\n/**\n * Expands a block collapsed by `maxLines`.\n *\n * Renders nothing when the content is shorter than the cut, so a consumer can\n * always compose it without guarding on line count themselves.\n */\nfunction CodeBlockExpandButton({\n className,\n children,\n variant = 'ghost',\n size = 'sm',\n onClick,\n ...props\n}: ComponentProps<typeof Button>) {\n const { expanded, setExpanded, collapsible, contentId } = useInternalConfig('CodeBlockExpandButton');\n\n if (!collapsible) return null;\n\n return (\n <div\n data-slot='code-block-expand'\n data-state={expanded ? 'expanded' : 'collapsed'}\n className={cn(\n /* Both states float over the surface. Expanded used to rejoin the flex\n column as a bordered strip, which read as a card footer and moved the\n block's bottom edge on the first click; the surface reserves a row of\n clearance instead, so only the label changes. */\n 'absolute inset-x-0 bottom-0 z-10 order-last flex items-end justify-center pt-10 pb-2',\n /* The fade means \"there is more below\", so it belongs to the collapsed\n state only. Expanded, the surface already reserves a clear row for\n this control, and keeping the gradient just dimmed the last real\n line of code. */\n !expanded && 'from-card via-card/85 bg-gradient-to-t to-transparent',\n className,\n )}\n >\n <Button\n type='button'\n aria-expanded={expanded}\n aria-controls={contentId}\n variant={variant}\n size={size}\n onClick={(event) => {\n onClick?.(event);\n if (!event.defaultPrevented) setExpanded(!expanded);\n }}\n {...props}\n >\n {children ?? (expanded ? 'Show less' : 'Show more')}\n </Button>\n </div>\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Line actions */\n/* -------------------------------------------------------------------------- */\n\nexport type CodeBlockLineActionsProps = {\n children: CodeBlockLineActionsRender;\n /**\n * `end` floats the group over the end of the active row. `gutter` puts it in\n * the channel beside the line number, which is where a \"add this line\"\n * affordance belongs. A fold toggle owns that channel when both are on, so a\n * gutter action on a fold-start row falls back to `end`.\n */\n side?: CodeBlockLineActionsSide;\n};\n\n/**\n * Actions on the hovered or focused line. ONE group exists for the whole\n * block, rendered inside the active row: a 2,000 line file mounts one button\n * group, not 2,000.\n */\nfunction CodeBlockLineActions({ children, side = 'end' }: CodeBlockLineActionsProps) {\n const registry = useContext(CodeBlockActionsContext);\n const owner = useRef({}).current;\n\n useEffect(() => {\n registry?.register(owner, children, side);\n return () => registry?.register(owner, null);\n }, [registry, owner, children, side]);\n\n return null;\n}\n\n/**\n * Live folding for anything composed inside the block - a \"fold all\" control,\n * a region count. Mirrors `useCodeBlockSelection`; region and start numbers\n * are source-based like every line spec.\n */\nfunction useCodeBlockFolding() {\n const document = useContext(CodeBlockDocumentContext);\n if (!document) {\n throw new Error('useCodeBlockFolding must be used within a CodeBlock');\n }\n\n const { foldable, foldRegions, folded, toggleFold, setFolded } = document;\n const foldedStarts = useMemo(() => [...folded].sort((a, b) => a - b), [folded]);\n const foldAll = useCallback(() => setFolded(foldRegions.map((region) => region.start)), [setFolded, foldRegions]);\n const unfoldAll = useCallback(() => setFolded([]), [setFolded]);\n\n return {\n foldable,\n regions: foldRegions,\n foldedStarts,\n toggleFold,\n foldAll,\n unfoldAll,\n };\n}\n\n/**\n * Live selection for anything composed inside the block - a line action, a\n * header control. Returns the selected lines as a sorted array plus the same\n * toggle the rows use, so an \"add selection to chat\" affordance can act on the\n * whole range instead of only the hovered line.\n */\nfunction useCodeBlockSelection() {\n const document = useContext(CodeBlockDocumentContext);\n if (!document) {\n throw new Error('useCodeBlockSelection must be used within a CodeBlock');\n }\n\n const { selected, selectable, toggleLine, clearSelection } = document;\n const selectedLines = useMemo(() => [...selected].sort((a, b) => a - b), [selected]);\n\n return { selectable, selectedLines, toggleLine, clearSelection };\n}\n\n/* Re-exported so a consumer renders patch reviews from one import path. */\nexport { parseUnifiedDiff } from './code-block-highlight';\nexport type {\n CodeBlockDiffSpec,\n CodeBlockHighlightOptions,\n CodeBlockLevelSpec,\n CodeBlockLine,\n CodeBlockLineActionsRender,\n CodeBlockLineSpec,\n CodeBlockLineState,\n CodeBlockPatchFile,\n CodeBlockThemes,\n CodeBlockToken,\n CodeBlockTransformer,\n CodeBlockWordSpec,\n} from './code-block-highlight';\n\nexport {\n CodeBlock,\n CodeBlockCopyButton,\n CodeBlockExpandButton,\n CodeBlockHeader,\n CodeBlockLanguage,\n CodeBlockLineActions,\n CodeBlockTitle,\n CodeBlockContent,\n CodeBlockWrapToggle,\n useCodeBlockConfig,\n useCodeBlockFolding,\n useCodeBlockSelection,\n};\n","import { CodeBlock, CodeBlockCopyButton, CodeBlockExpandButton } from '#reui/code-block/code-block';\nimport json5 from 'json5';\nimport React from 'react';\n\nimport { useT } from '../../../theming/i18n';\n\n/**\n * 单笔 Run 的输出/错误详情(code-block 渲染)——被 simulate-runs-panel 以\n * React.lazy 挂载,使 code-block 组件连同 shiki 全部留在动态 chunk\n * (field-picker-popup 同款隔离),index 主包零增重。Suspense fallback =\n * 面板内的裸 <pre>(渐进增强,chunk 加载期间内容不缺席)。\n */\nexport const SimulateRunDetail: React.FC<{ detail: unknown }> = ({ detail }) => {\n const t = useT();\n const code = json5.stringify(detail, undefined, 2) ?? 'null';\n\n return (\n <div className='relative' data-testid='simulate-run-detail'>\n <CodeBlock\n code={code}\n language='json'\n variant='ghost'\n foldable\n maxLines={16}\n showLineNumbers={false}\n className='max-h-64 rounded-md bg-muted/60 font-mono text-[10px] leading-relaxed'\n >\n <CodeBlockExpandButton className='text-[10px]' />\n </CodeBlock>\n <CodeBlockCopyButton\n value={code}\n position='pinned'\n alwaysVisible\n className='absolute right-1.5 top-1.5 size-5 text-muted-foreground opacity-60 hover:opacity-100'\n labels={{ copy: t('dg.simulation.copy'), copied: t('dg.simulation.copied') }}\n />\n </div>\n );\n};\n"],"mappings":"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"}