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: () => (
),
};
}
/** User joined or left the channel. */
function presence(action: "join" | "leave", sender: string): Block {
return {
measure: systemMeasure,
render: () =>
action === "join"
? (
{sender}
joined the session
)
: (
{sender}
left the session
),
};
}
/** Call started or ended. */
function call(action: "start" | "end", sender: string): Block {
return {
measure: systemMeasure,
render: () =>
action === "start"
? (
{sender} started a call
)
: (
call ended
),
};
}
export {
call,
commit,
exec,
execActions,
githubAction,
issueComment,
issueOpened,
issueUpdate,
pr,
prComment,
presence,
prReview,
prReviewComment,
prUpdate,
};
export {
type ExecAction,
type ExecActionHandler,
type ExecActionPayload,
execStatusSlot,
} from "./system-layout";