import { type DiffLineRow, type DiffPreview } from '@wrongstack/tools/tool-diff'; import type React from 'react'; import { type Lang, type Token } from '../../highlight.js'; export type { DiffLineKind, DiffLineRow, DiffPreview } from '@wrongstack/tools/tool-diff'; /** * A parsed diff paired with the file it belongs to. Used when a tool * produces diffs for several files at once (currently `replace`); each * `DiffFilePreview` renders as one labeled `DiffFileBlock`. */ export interface DiffFilePreview { path: string; preview: DiffPreview; } interface OmittedDiffSummary { fileCount: number; added: number; removed: number; } /** Renderable file previews plus aggregate data for files omitted by the cap. */ export type DiffFilePreviews = DiffFilePreview[] & { omitted?: OmittedDiffSummary | undefined; }; /** Max code-block lines rendered before a "+N more" footer. */ export declare const MAX_CODE_LINES = 80; /** * Hard ceiling for one file's rendered diff preview. Diff rows are React/Ink * elements and therefore retained by the active virtual-history window; an * unbounded preview lets a single generated-file edit defeat virtualization. * Totals for the whole diff remain available through `added`/`removed`, while * the footer reports the hidden portion. */ export declare const DIFF_MAX_LINES = 200; /** Maximum number of per-file diff blocks retained for one tool entry. */ export declare const MULTI_DIFF_MAX_FILES = 20; /** Maximum rendered diff rows retained across every file in one tool entry. */ export declare const MULTI_DIFF_MAX_ROWS = 400; /** Syntax-highlighted, framed code block. */ declare function CodeBlockImpl({ code, lang, contentWidth, }: { code: string; lang: Lang; contentWidth: number; }): React.ReactElement; /** * Minimum number of files before a summary footer is rendered above the * per-file blocks. Below this threshold each file's own `… +N -M hidden` * footer carries enough signal; above it, a single aggregate line keeps * the screen from being drowned in per-file tail lines. * * This is the default when no user-tunable value is supplied. The * settings picker exposes `MULTI_DIFF_SUMMARY_THRESHOLD_PRESETS` so * users can raise the cutoff (e.g. for very wide terminals) or lower * it (e.g. for tiny scrollback), or set it to 0 to suppress the * summary entirely. */ export declare const MULTI_DIFF_SUMMARY_THRESHOLD = 5; /** * Aggregate stats across a list of per-file diffs — used to print a * single summary line at the top of a multi-file diff view when there * are enough files to make the rollup useful. */ export interface MultiDiffSummary { fileCount: number; added: number; removed: number; hiddenAdded: number; hiddenRemoved: number; /** Number of rendered files whose preview was truncated (has hidden rows). * Does not include files omitted entirely by the multi-file render ceiling; * those are tracked separately via {@link omittedFiles}. */ truncatedFiles: number; /** Files omitted entirely by the multi-file render ceiling. Guaranteed non-negative. */ omittedFiles: number; } /** * Sum the totals of a list of per-file diff previews. Files that were * parsed but have no rows (e.g. entirely empty after the no-op skip) are * excluded from the rollup so the summary reflects what the user will * actually see rendered below. */ export declare function summarizeMultiFileDiffs(items: DiffFilePreviews): MultiDiffSummary; /** * Format a multi-file diff summary as a single dim italic line, suitable * for rendering above the per-file blocks. Mirrors the per-file footer's * `… +N -M hidden` shape so a reader who has seen the footer recognises * the format. Returns `null` when there's nothing useful to surface * (no files, or below the user's threshold where the per-file footer * already covers the rollup). * * @param threshold User-tunable cutoff. Pass `MULTI_DIFF_SUMMARY_THRESHOLD` * for the default behaviour, `0` to suppress the summary entirely * (always returns null), or a positive number to set a custom cutoff. * A negative value is treated as "use default" so callers can pass an * `undefined`-coerced settings value without a separate branch. */ export declare function formatMultiDiffSummary(summary: MultiDiffSummary, threshold?: number): string | null; /** * One labeled diff — used to render a per-file block inside multi-file * diff views (e.g. when `replace` modifies several files). The path label * is rendered dim and italic so the file boundary is visible without * competing with the add/remove wash. */ export declare function DiffFileBlock({ path, preview, useColor, contentWidth, }: { path: string; preview: DiffPreview; /** Pass-through to {@link DiffBlock}. See that component for details. */ useColor?: boolean | undefined; /** Pass-through to {@link DiffBlock}. See that component for details. */ contentWidth?: number | undefined; }): React.ReactElement; /** * Human-readable change-size line for a diff — `Added N lines, removed M * lines` (Claude Code phrasing). Omits the zero side; returns `null` when * nothing changed so callers can skip the line entirely. */ export declare function formatDiffStats(added: number, removed: number): string | null; /** * Return the token list to render on a diff wash (`diffAddBg` / * `diffDelBg`). When `onWash` is false the input is returned unchanged — * plain-background rendering keeps the conventional dim comment look and * there is no contrast reason to intervene. * * When `onWash` is true, comment tokens are re-pointed from the * `syntax.comment` role to `syntax.commentOnWash` and lose their dim flag. * `syntax.comment` resolves to `theme.textMuted`, which is chosen to recede * against `theme.surface` and therefore falls below WCAG AA on either wash; * `syntax.commentOnWash` resolves to `theme.textSecondary`, which clears both * (≥ 4.5) while keeping the comment visually secondary. Because both sides * are ROLES, the promotion follows `/theme` automatically instead of pinning * one hardcoded grey for all 35 presets. * * Non-comment tokens pass through unchanged so the rest of the line keeps its * syntax palette on the wash. * * Exported for unit testing — `renderTokens` calls it before mapping to * `` elements so the override logic itself stays a pure function. */ export declare function applyWashTokens(tokens: Token[], onWash: boolean): Token[]; export declare function DiffBlock({ rows, hidden, added, removed, hiddenAdded, hiddenRemoved, useColor, lang, showStats, contentWidth, }: { rows: DiffLineRow[]; hidden: number; /** * Total lines added across the whole diff (not just the visible slice). * Surfaced in the `⎿ Added N lines, removed M lines` stats line so the * reader knows the change size even when the body is truncated. */ added?: number | undefined; /** Total lines removed across the whole diff (not just the visible slice). */ removed?: number | undefined; hiddenAdded?: number | undefined; hiddenRemoved?: number | undefined; /** * When true (default), added/removed rows get a dark green/maroon * background wash (Claude Code style) with normal-brightness, * syntax-highlighted foreground text. When false, only the `+`/`-` * markers get colored (bright green/red, bold) so the diff stays * readable on terminals that don't support truecolor backgrounds * (TERM=xterm, `NO_COLOR=1`, etc.). Pass `theme.supportsBackground` * from the entry-point. */ useColor?: boolean | undefined; /** * Syntax-highlight language for line bodies (derive from the touched * file's extension via `langFromPath`). `plain` disables highlighting. */ lang?: Lang | undefined; /** * Render the leading `⎿ Added N lines, removed M lines` stats line. * Callers that print their own header/stats (e.g. the Update(path) * entry header) pass `false` to avoid the duplicate. */ showStats?: boolean | undefined; /** * Terminal width available to this block. Line bodies longer than the * remaining budget hard-wrap onto continuation rows (blank gutter, blank * marker cell, same background wash) so the full line content is always * visible without ever flowing under the gutter. When omitted, wrapping * falls back to a 100-char budget. */ contentWidth?: number | undefined; }): React.ReactElement; /** * Parse a unified-diff string into a {@link DiffPreview}. Thin wrapper over the * shared, single-source-of-truth `parseUnifiedDiffPreview` in * @wrongstack/tools/tool-diff (which the WebUI and HQ also read). Kept as a * local export so this module's many callers and tests are unchanged. */ export declare function parseUnifiedDiff(diff: string, maxLines: number): DiffPreview; /** * Pull a unified-diff string out of a tool's JSON output, then turn it * into a small, structured preview suitable for colour-coded rendering. */ export declare function extractDiffPreview(toolName: string, output: string | undefined, input?: unknown | undefined): DiffPreview | undefined; /** * Pull one diff preview per file from a `replace` tool result. Each entry * has a `path` (best-effort: `results[i].path`, falling back to the input * argument when every result is for the same file) and a `preview` ready * for `DiffFileBlock` / `DiffBlock` rendering. * * Returns `undefined` when no per-file diff is recoverable (e.g. an empty * `results` array, no diff fields, or the result isn't a JSON object). * * Note: For a single entry point that handles `replace`, `diff`, and * `patch` (the three tools whose output can span multiple files), use * {@link extractMultiFileDiffs} instead — this function is kept for the * narrower replace-specific test cases. */ export declare function extractReplaceDiffs(toolName: string, output: string | undefined, input?: unknown | undefined): DiffFilePreviews | undefined; /** * Pull a list of per-file diffs from a tool result that may span multiple * files. Handles: * * - `replace`: JSON `{ results: [{ path, diff }, …] }` (path per result, * fallback to the input path when the result omits one). * - `diff`: JSON `{ diff: string }` where `diff` is a git-style multi-file * unified diff (split on `diff --git` headers). * - `patch`: either JSON `{ diff: string, files: string[] }` or a raw * unified-diff string (split on `diff --git` headers, falling back to * `--- a/` if no `diff --git` is present). * * Returns `undefined` when the tool isn't multi-file capable, the output * is missing/unparseable, or no per-file diff is recoverable. Returns an * empty array (not undefined) when the output parses but every entry has * an empty diff after trimming — the caller treats both as "nothing to * render" but the distinction is useful in tests. */ export declare function extractMultiFileDiffs(toolName: string, output: string | undefined, input?: unknown | undefined): DiffFilePreviews | undefined; /** * Syntax-highlighted code / diff block. * * Memoized: highlighting is the single most expensive thing in the transcript, * and a completed fence never changes again while later text streams in. */ export declare const CodeBlock: React.MemoExoticComponent; //# sourceMappingURL=code-block.d.ts.map