capsule-ai-cli
Version:
The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing
127 lines • 5.22 kB
JavaScript
import { TerminalController } from './terminal-controller.js';
export class LayoutManager {
minMessageHeight = 3;
baseFooterHeight = 6;
calculateLayout(options) {
const { columns: terminalWidth, rows: terminalHeight } = TerminalController.getSize();
let suggestionHeight = 0;
let minMessageHeight = this.minMessageHeight;
if (options.showingSuggestions && options.suggestionCount > 0) {
if (options.isSlashCommand) {
minMessageHeight = 1;
const minOtherHeight = this.baseFooterHeight + minMessageHeight;
const availableForSuggestions = Math.max(0, terminalHeight - minOtherHeight);
suggestionHeight = Math.min(options.suggestionCount, availableForSuggestions);
}
else {
const minOtherHeight = 10;
const availableRows = Math.max(0, terminalHeight - minOtherHeight);
suggestionHeight = Math.min(options.suggestionCount, Math.max(3, Math.floor(availableRows * 0.6)));
}
}
let inputHeight = options.inputLineCount || 1;
const maxInputHeight = this.calculateMaxInputHeight(terminalHeight, suggestionHeight, minMessageHeight);
if (inputHeight > maxInputHeight) {
inputHeight = maxInputHeight;
}
let footerHeight = this.baseFooterHeight + inputHeight - 1;
if (suggestionHeight > 0) {
footerHeight += suggestionHeight + 1;
}
const messageAreaHeight = Math.max(minMessageHeight, terminalHeight - footerHeight);
const animationRow = messageAreaHeight + 1;
const inputTopRow = animationRow + 1;
const inputContentRow = inputTopRow + 1;
const inputBottomRow = inputContentRow + inputHeight;
const suggestionStartRow = inputBottomRow + 2;
let footerRow = suggestionStartRow;
if (suggestionHeight > 0) {
footerRow = suggestionStartRow + suggestionHeight + 1;
}
footerRow = Math.min(footerRow, terminalHeight - 1);
return {
terminalWidth,
terminalHeight,
messageAreaHeight,
footerHeight,
inputHeight,
suggestionHeight,
animationRow,
inputTopRow,
inputContentRow,
inputBottomRow,
suggestionStartRow,
footerRow
};
}
calculateMaxInputHeight(terminalHeight, suggestionHeight, minMessageHeight) {
const fixedComponents = 5;
const reservedHeight = fixedComponents + suggestionHeight + minMessageHeight;
const availableForInput = terminalHeight - reservedHeight;
return Math.max(1, availableForInput);
}
isLayoutValid(layout) {
return (layout.messageAreaHeight >= 1 &&
layout.footerRow < layout.terminalHeight &&
layout.inputHeight >= 1);
}
calculateVisibleRange(totalLines, messageAreaHeight, scrollPosition) {
const maxStartIdx = Math.max(0, totalLines - messageAreaHeight);
const startIdx = Math.max(0, Math.min(maxStartIdx - scrollPosition, maxStartIdx));
const endIdx = Math.min(startIdx + messageAreaHeight, totalLines);
return {
start: startIdx,
end: endIdx
};
}
calculateCursorPosition(inputContentRow, inputHeight, currentLineLength, terminalHeight, terminalWidth) {
const cursorRow = Math.min(inputContentRow + inputHeight - 1, terminalHeight - 1);
const cursorCol = Math.min(3 + currentLineLength, terminalWidth);
return {
row: cursorRow,
col: cursorCol
};
}
canFitSuggestions(terminalHeight, currentFooterHeight, suggestionCount) {
const availableSpace = terminalHeight - currentFooterHeight - this.minMessageHeight;
return availableSpace >= Math.min(3, suggestionCount);
}
calculateFooterLayout(terminalWidth) {
const minPadding = 2;
const separatorSpace = 3;
const totalContentWidth = terminalWidth - minPadding;
const maxLeftContentWidth = Math.floor(totalContentWidth * 0.6);
const maxRightContentWidth = Math.floor(totalContentWidth * 0.4) - separatorSpace;
return {
maxLeftContentWidth,
maxRightContentWidth,
paddingSpace: minPadding
};
}
getLayoutMode(terminalWidth, terminalHeight) {
if (terminalWidth < 80 || terminalHeight < 24) {
return 'compact';
}
else if (terminalWidth >= 120 && terminalHeight >= 40) {
return 'spacious';
}
return 'normal';
}
adjustForLayoutMode(layout, mode) {
const adjusted = { ...layout };
switch (mode) {
case 'compact':
if (adjusted.suggestionHeight > 3) {
adjusted.suggestionHeight = 3;
}
break;
case 'spacious':
break;
default:
break;
}
return adjusted;
}
}
export const layoutManager = new LayoutManager();
//# sourceMappingURL=layout-manager.js.map