import type { SelectOption } from "@opentui/core" import { getPaneWidths, resolveRowBackground, resolveRowTextColor } from "./commit-history/layout" import { SectionDivider } from "./section-divider" import { getVisibleRange } from "../list-range" import type { CommitHistoryMode, FocusTarget } from "../types" import { fitLine, fitPathForWidth } from "../utils" import type { UiTheme } from "../theme" import { ViewFrame, resolveDenseVisibleRows, resolveViewContentWidth, resolveVisibleRows, } from "./view-frame" type CommitHistoryDialogProps = { open: boolean mode: CommitHistoryMode focus: FocusTarget terminalWidth: number terminalHeight: number currentBranch: string commitOptions: SelectOption[] commitIndex: number actionOptions: SelectOption[] actionIndex: number fileOptions: SelectOption[] fileIndex: number selectedCommitTitle: string selectedCommitPreviewTitle: string selectedFilePath: string diffText: string diffMessage: string | null diffFiletype: string | undefined diffView: "unified" | "split" onCommitClick: (index: number) => void onFileClick: (index: number) => void onActionClick: (index: number) => void onCommitScroll: (direction: "up" | "down") => void onFileScroll: (direction: "up" | "down") => void onActionScroll: (direction: "up" | "down") => void theme: UiTheme } export function CommitHistoryDialog({ open, mode, focus, terminalWidth, terminalHeight, currentBranch, commitOptions, commitIndex, actionOptions, actionIndex, fileOptions, fileIndex, selectedCommitTitle, selectedCommitPreviewTitle, selectedFilePath, diffText, diffMessage, diffFiletype, diffView, onCommitClick, onFileClick, onActionClick, onCommitScroll, onFileScroll, onActionScroll, theme, }: CommitHistoryDialogProps) { if (!open) return null const listVisibleRows = resolveVisibleRows(terminalHeight, 13) const commitVisibleRows = resolveDenseVisibleRows(listVisibleRows, 2) const actionVisibleRows = resolveDenseVisibleRows(listVisibleRows, 2) const contentWidth = resolveViewContentWidth(terminalWidth) const { commitsPaneWidth, filesPaneWidth } = getPaneWidths(contentWidth) const commitRowWidth = Math.max(commitsPaneWidth - 4, 1) const fileRowWidth = Math.max(filesPaneWidth - 4, 1) const commitRange = getVisibleRange( commitOptions.length, commitIndex, mode === "list" ? commitVisibleRows : listVisibleRows, ) const actionRange = getVisibleRange( actionOptions.length, actionIndex, mode === "action" ? actionVisibleRows : listVisibleRows, ) const fileRange = getVisibleRange(fileOptions.length, fileIndex, listVisibleRows) const leftPaneTitle = mode === "list" ? `commits (${commitOptions.length})` : "actions" const selectedPreviewTitle = mode === "action" ? selectedCommitTitle : selectedCommitPreviewTitle const rightPaneTitle = selectedFilePath || selectedPreviewTitle || "no selection" const commitsFocused = mode === "action" ? focus === "history-actions" : focus === "history-commits" const filesFocused = mode === "list" && focus === "history-files" return ( commit history branch: {currentBranch} {leftPaneTitle} { const direction = event.scroll?.direction if (direction !== "up" && direction !== "down") return event.preventDefault() event.stopPropagation() if (mode === "action") { onActionScroll(direction) } else { onCommitScroll(direction) } }} > {mode === "list" ? commitOptions .slice(commitRange.start, commitRange.end) .map((option, visibleIndex) => { const absoluteIndex = commitRange.start + visibleIndex const selected = absoluteIndex === commitIndex const subject = option.name ?? String(option.value ?? "") const metadata = option.description ?? "" const hash = String(option.value ?? "") .trim() .slice(0, 7) const prefix = selected ? "▶ " : " " const hashReservedWidth = hash ? hash.length + 1 : 0 const subjectWidth = Math.max( commitRowWidth - prefix.length - hashReservedWidth, 1, ) const metadataWidth = Math.max(commitRowWidth - 2, 1) const subjectLine = fitLine(subject, subjectWidth).padEnd(subjectWidth, " ") const commitTextColor = resolveRowTextColor({ selected, focused: focus === "history-commits", theme, }) const metadataTextColor = selected ? theme.colors.subtleText : theme.colors.hintText return ( { event.preventDefault() event.stopPropagation() onCommitClick(absoluteIndex) }} > {prefix} {subjectLine} {hash ? {hash} : null} {fitLine(metadata, metadataWidth)} ) }) : actionOptions .slice(actionRange.start, actionRange.end) .map((option, visibleIndex) => { const absoluteIndex = actionRange.start + visibleIndex const selected = absoluteIndex === actionIndex const optionName = option.name ?? String(option.value ?? "") const optionDescription = option.description ?? "" const prefix = selected ? "▶ " : " " const actionWidth = Math.max(commitRowWidth - prefix.length, 1) const detailWidth = Math.max(commitRowWidth - 2, 1) const actionLine = fitLine(optionName, actionWidth) const actionTextColor = resolveRowTextColor({ selected, focused: focus === "history-actions", theme, }) const detailTextColor = selected ? theme.colors.subtleText : theme.colors.hintText return ( { event.preventDefault() event.stopPropagation() onActionClick(absoluteIndex) }} > {prefix} {actionLine} {fitLine(optionDescription, detailWidth)} ) })} files ({fileOptions.length}) { const direction = event.scroll?.direction if (direction !== "up" && direction !== "down") return event.preventDefault() event.stopPropagation() onFileScroll(direction) }} > {fileOptions.slice(fileRange.start, fileRange.end).map((option, visibleIndex) => { const absoluteIndex = fileRange.start + visibleIndex const selected = absoluteIndex === fileIndex const optionName = option.name ?? String(option.value ?? "") const optionDescription = option.description ?? "" const prefix = optionDescription ? `${optionDescription} ` : "" const pathWidth = Math.max(fileRowWidth - prefix.length, 0) const label = `${prefix}${fitPathForWidth(optionName, pathWidth)}` return ( { event.preventDefault() event.stopPropagation() onFileClick(absoluteIndex) }} > {selected ? "▶ " : " "} {fitLine(label, fileRowWidth)} ) })} {rightPaneTitle} {diffMessage ? ( {diffMessage} ) : ( )} ) }