import { visibleWidth } from '../../tui/index.js';
import { theme } from '../theme/runtime.js';
const TRANSCRIPT_SCROLLBAR_TRACK = '\u2502';
const TRANSCRIPT_SCROLLBAR_THUMB = '\u2588';
export class InteractiveShellLayout {
    terminal;
    top;
    bottom;
    constructor(terminal, top, bottom) {
        this.terminal = terminal;
        this.top = top;
        this.bottom = bottom;
    }
    invalidate() {
        this.top.invalidate();
        for (const child of this.bottom) {
            child.invalidate?.();
        }
    }
    render(width) {
        const rows = Math.max(1, this.terminal.rows);
        const bottomLines = this.bottom.flatMap((child) => child.render(width));
        const topViewportHeight = Math.max(0, rows - bottomLines.length);
        this.top.setViewportHeight(topViewportHeight);
        const { lines: renderedTopLines, showScrollbar } = this.renderTop(width, topViewportHeight);
        let topLines = renderedTopLines;
        if (showScrollbar) {
            topLines = this.renderTopScrollbar(topLines, width, topViewportHeight);
        }
        let lines = [...topLines, ...bottomLines];
        if (lines.length > rows) {
            lines = lines.slice(lines.length - rows);
        }
        while (lines.length < rows) {
            lines.push('');
        }
        return lines;
    }
    renderTop(width, viewportHeight) {
        if (viewportHeight <= 0) {
            return { lines: [], showScrollbar: false };
        }
        if (width <= 1) {
            return { lines: this.top.render(width), showScrollbar: false };
        }
        const scrollState = this.captureTopScrollState();
        const narrowLines = this.top.render(width - 1);
        if (this.top.getContentHeight() > viewportHeight) {
            return { lines: narrowLines, showScrollbar: true };
        }
        this.restoreTopScrollState(scrollState);
        const fullWidthLines = this.top.render(width);
        if (this.top.getContentHeight() <= viewportHeight) {
            return { lines: fullWidthLines, showScrollbar: false };
        }
        // Some components can change line counts in non-monotonic ways with width.
        // Fall back to the scrollable width so the gutter always has room.
        this.restoreTopScrollState(scrollState);
        return { lines: this.top.render(width - 1), showScrollbar: true };
    }
    captureTopScrollState() {
        return {
            atBottom: this.top.isAtBottom(),
            scrollTop: this.top.getScrollTop()
        };
    }
    restoreTopScrollState(state) {
        if (state.atBottom) {
            this.top.scrollToBottom();
            return;
        }
        this.top.scrollTo(state.scrollTop);
    }
    renderTopScrollbar(lines, width, viewportHeight) {
        const contentWidth = Math.max(0, width - 1);
        const contentHeight = Math.max(this.top.getContentHeight(), 1);
        const maxScrollTop = Math.max(0, contentHeight - viewportHeight);
        const thumbHeight = Math.max(1, Math.min(viewportHeight, Math.ceil((viewportHeight / contentHeight) * viewportHeight)));
        const thumbTravel = Math.max(0, viewportHeight - thumbHeight);
        const thumbTop = maxScrollTop === 0 ? 0 : Math.round((this.top.getScrollTop() / maxScrollTop) * thumbTravel);
        return lines.map((line, index) => {
            const padding = ' '.repeat(Math.max(0, contentWidth - visibleWidth(line)));
            const gutter = index >= thumbTop && index < thumbTop + thumbHeight
                ? theme.fg('accent', TRANSCRIPT_SCROLLBAR_THUMB)
                : theme.fg('muted', TRANSCRIPT_SCROLLBAR_TRACK);
            return `${line}${padding}${gutter}`;
        });
    }
}
//# sourceMappingURL=interactive-shell-layout.js.map