import type { Block } from "../../lib/block"; import { SYSTEM_HEIGHT } from "./constants"; import { ExecView, Link, SystemLine, Text } from "./system-view"; import { ACTION_HEIGHT, ACTION_PAD_X, execActions, OUTPUT, output, OUTPUT_LINE, OUTPUT_PAD, PREVIEW_HEIGHT, } from "./system-layout"; /** System rows are a single fixed-height line that always fills available width. */ function systemMeasure(width: number) { return { height: SYSTEM_HEIGHT, fit: width }; } /** Commit indicator block with fixed height. */ function commit(sha: string, message: string, href?: string): Block { return { measure: systemMeasure, render: () => ( Committed {sha.slice(0, 7)} {message} ), }; } /** New pull request row. */ function pr(id: string, title: string, href?: string): Block { return { measure: systemMeasure, render: () => ( Opened PR {id} {title} ), }; } /** Pull request state or title update row. */ function prUpdate(id: string, href?: string, value?: string, title?: string): Block { return { measure: systemMeasure, render: () => value ? ( PR {id} {value} ) : ( Updated PR {id} {title} ), }; } /** GitHub pull request comment header. Markdown body renders as a following text block. */ function prComment(pr: string, author: string, url?: string, height = SYSTEM_HEIGHT): Block { return { measure: (width) => ({ height, fit: width }), render: () => ( {author} commented on PR {url ? ( {pr} ) : {pr}} ), }; } /** GitHub pull request review header. Markdown body renders as a following text block. */ function prReview( pr: string, action: "approved" | "commented" | "changes_requested" | "dismissed", reviewer: string, url?: string, height = SYSTEM_HEIGHT, ): Block { let verb = action === "approved" ? "approved" : action === "changes_requested" ? "requested changes on" : action === "dismissed" ? "dismissed a review on" : "reviewed"; return { measure: (width) => ({ height, fit: width }), render: () => ( {reviewer} {verb} {url ? ( {pr} ) : {pr}} ), }; } /** GitHub inline (code-line) review comment header. Body renders as a following text block. */ function prReviewComment( author: string, path: string, line?: number, url?: string, height = SYSTEM_HEIGHT, ): Block { let loc = line ? `${path}:${line}` : path; return { measure: (width) => ({ height, fit: width }), render: () => ( {author} commented on {url ? ( {loc} ) : {loc}} ), }; } /** Shared GitHub issue header ("{author} {verb} {#N}"). Body renders below. */ function issueLine( verb: string, issue: string, author: string, url?: string, height = SYSTEM_HEIGHT, ) { return ( {author} {verb} {url ? ( {issue} ) : {issue}} ); } /** Entry-point issue opened. Title + markdown body render as a following text block. */ function issueOpened(issue: string, author: string, url?: string, height = SYSTEM_HEIGHT): Block { return { measure: (width) => ({ height, fit: width }), render: () => issueLine("opened issue", issue, author, url, height), }; } /** GitHub issue comment header. Markdown body renders as a following text block. */ function issueComment(issue: string, author: string, url?: string, height = SYSTEM_HEIGHT): Block { return { measure: (width) => ({ height, fit: width }), render: () => issueLine("commented on issue", issue, author, url, height), }; } /** Entry-point issue closed or reopened. Single fixed-height line. */ function issueUpdate(issue: string, url?: string, value?: "open" | "closed"): Block { let verb = value === "closed" ? "closed issue" : "reopened issue"; return { measure: systemMeasure, render: () => ( {verb} {issue} ), }; } /** Link rendered as a compact GitHub-style action button. */ function githubAction(label: string, href: string): Block { return { measure: (width) => ({ height: ACTION_HEIGHT, fit: Math.min(width, Math.ceil(label.length * 7.5 + ACTION_PAD_X * 2)), }), render: () => ( {label} ), }; } /** Shell command execution row. */ function exec( id: string, input: string, exitCode?: number | string, aborted?: boolean, stdout = "", stderr = "", expanded = false, cwd?: string, ): Block { let pending = exitCode === undefined; let rows = output(input, stdout, stderr); let hasOutput = rows.length > 0; let actions = execActions({ hasOutput }); let waiting = pending && rows.length === 0; let visible = waiting ? ["..."] : rows.slice(0, OUTPUT); let hidden = rows.length > visible.length; let preview = !expanded && rows.length > 0 ? rows[0]! : ""; let lines = expanded ? visible.length + (hidden ? 1 : 0) : 0; let height = SYSTEM_HEIGHT + (preview ? PREVIEW_HEIGHT : 0) + (lines ? lines * OUTPUT_LINE + OUTPUT_PAD * 2 : 0); return { measure: (width) => ({ height, fit: width, }), render: () => (