/** * Shared box-drawing chrome for fullscreen overlays (the `/copy` picker, the * plan-review overlay, …). Every helper paints with `theme.boxRound` glyphs * (rounded corners, sharp tee/cross junctions) and the `border`/`accent` theme * colors so all outlined overlays read identically. */ import { padding, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui"; import { theme } from "../theme/theme"; /** Pad or truncate a (possibly ANSI-styled) string to exactly `width` columns. */ export function fit(text: string, width: number): string { if (width <= 0) return ""; const w = visibleWidth(text); if (w === width) return text; if (w < width) return text + padding(width - w); const cut = truncateToWidth(text, width); const cw = visibleWidth(cut); return cw < width ? cut + padding(width - cw) : cut; } function paint(s: string): string { return theme.fg("border", s); } /** Top border with an optional accent-colored title inset into the rule. */ export function topBorder(width: number, title: string): string { const box = theme.boxRound; const inner = Math.max(0, width - 2); if (!title) return paint(box.topLeft + box.horizontal.repeat(inner) + box.topRight); const shown = truncateToWidth(` ${title} `, Math.max(0, inner - 2)); const fillWidth = Math.max(0, inner - 1 - visibleWidth(shown)); return ( paint(box.topLeft + box.horizontal) + theme.bold(theme.fg("accent", shown)) + paint(box.horizontal.repeat(fillWidth) + box.topRight) ); } /** A horizontal rule with left/right tees, splitting overlay sections. */ export function divider(width: number): string { const box = theme.boxRound; return paint(box.teeRight + box.horizontal.repeat(Math.max(0, width - 2)) + box.teeLeft); } export function bottomBorder(width: number): string { const box = theme.boxRound; return paint(box.bottomLeft + box.horizontal.repeat(Math.max(0, width - 2)) + box.bottomRight); } /** Wrap pre-styled content in vertical borders with single-column insets. */ export function row(content: string, width: number): string { const box = theme.boxRound; return `${paint(box.vertical)} ${fit(content, Math.max(0, width - 4))} ${paint(box.vertical)}`; } /** * Column index (0-based) of the inner divider for a two-column layout whose * sidebar content area is `sidebarWidth` columns wide. The layout is * `│ sidebar │ body │` with a single-column inset on every side, so the divider * vertical sits at `sidebarWidth + 3` and the body content area is * {@link splitBodyWidth} columns. */ function splitDividerCol(sidebarWidth: number): number { return sidebarWidth + 3; } /** Body content width for a two-column overlay of total `width`. */ export function splitBodyWidth(width: number, sidebarWidth: number): number { return Math.max(0, width - sidebarWidth - 7); } /** Top border carrying the title, split by a `┬` over the column divider. */ export function topBorderSplit(width: number, title: string, sidebarWidth: number): string { const box = theme.boxRound; const dividerCol = splitDividerCol(sidebarWidth); const leftLen = Math.max(0, dividerCol - 1); const rightLen = Math.max(0, width - 2 - dividerCol); let left: string; if (!title) { left = paint(box.topLeft + box.horizontal.repeat(leftLen)); } else { const shown = truncateToWidth(` ${title} `, Math.max(0, leftLen - 1)); const fillWidth = Math.max(0, leftLen - 1 - visibleWidth(shown)); left = paint(box.topLeft + box.horizontal) + theme.bold(theme.fg("accent", shown)) + paint(box.horizontal.repeat(fillWidth)); } return left + paint(box.teeDown + box.horizontal.repeat(rightLen) + box.topRight); } /** Section rule that closes the sidebar column with a `┴` over the divider. */ export function dividerSplit(width: number, sidebarWidth: number): string { const box = theme.boxRound; const dividerCol = splitDividerCol(sidebarWidth); const leftLen = Math.max(0, dividerCol - 1); const rightLen = Math.max(0, width - 2 - dividerCol); return paint( box.teeRight + box.horizontal.repeat(leftLen) + box.teeUp + box.horizontal.repeat(rightLen) + box.teeLeft, ); } /** A two-column content row: `│ sidebar │ body │`, each inset by one column. */ export function splitRow(sidebar: string, body: string, width: number, sidebarWidth: number): string { const box = theme.boxRound; const bodyWidth = splitBodyWidth(width, sidebarWidth); const bar = paint(box.vertical); return `${bar} ${fit(sidebar, sidebarWidth)} ${bar} ${fit(body, bodyWidth)} ${bar}`; }