capsule-ai-cli
Version:
The AI Model Orchestrator - Intelligent multi-model workflows with device-locked licensing
160 lines • 5.38 kB
JavaScript
import chalk from 'chalk';
export class ScrollManager {
scrollPosition = 0;
totalLines = 0;
visibleHeight = 0;
resetScroll() {
this.scrollPosition = 0;
}
updateDimensions(totalLines, visibleHeight) {
this.totalLines = totalLines;
this.visibleHeight = visibleHeight;
this.validateScrollPosition();
}
scrollUp(lines = 1) {
this.scrollPosition += lines;
this.validateScrollPosition();
}
scrollDown(lines = 1) {
this.scrollPosition -= lines;
this.validateScrollPosition();
}
pageUp() {
const pageSize = Math.max(1, this.visibleHeight - 2);
this.scrollUp(pageSize);
}
pageDown() {
const pageSize = Math.max(1, this.visibleHeight - 2);
this.scrollDown(pageSize);
}
scrollToTop() {
this.scrollPosition = this.getMaxScrollPosition();
}
scrollToBottom() {
this.scrollPosition = 0;
}
getScrollPosition() {
return this.scrollPosition;
}
getVisibleStartIndex() {
const maxStartIdx = Math.max(0, this.totalLines - this.visibleHeight);
const startIdx = maxStartIdx - this.scrollPosition;
return Math.max(0, Math.min(startIdx, maxStartIdx));
}
getVisibleRange() {
const start = this.getVisibleStartIndex();
const end = Math.min(start + this.visibleHeight, this.totalLines);
return { start, end };
}
calculateScrollbar() {
const hasScrollbar = this.totalLines > this.visibleHeight;
if (!hasScrollbar) {
return {
height: this.visibleHeight,
thumbHeight: 0,
thumbPosition: 0,
hasScrollbar: false
};
}
const thumbHeight = Math.max(1, Math.floor((this.visibleHeight / this.totalLines) * this.visibleHeight));
const startIdx = this.getVisibleStartIndex();
const scrollPercentage = startIdx / Math.max(1, this.totalLines - this.visibleHeight);
const thumbPosition = Math.floor(scrollPercentage * (this.visibleHeight - thumbHeight));
return {
height: this.visibleHeight,
thumbHeight,
thumbPosition,
hasScrollbar: true
};
}
renderScrollbarCharacter(row) {
const config = this.calculateScrollbar();
if (!config.hasScrollbar) {
return '';
}
const startIdx = this.getVisibleStartIndex();
if (row === 0 && startIdx > 0) {
return chalk.yellow('▲');
}
else if (row === this.visibleHeight - 1 &&
startIdx + this.visibleHeight < this.totalLines) {
return chalk.yellow('▼');
}
if (row >= config.thumbPosition &&
row < config.thumbPosition + config.thumbHeight) {
return chalk.cyan('█');
}
return chalk.dim('│');
}
canScroll(direction) {
if (this.totalLines <= this.visibleHeight) {
return false;
}
if (direction === 'up') {
return this.scrollPosition < this.getMaxScrollPosition();
}
else {
return this.scrollPosition > 0;
}
}
getScrollPercentage() {
if (this.totalLines <= this.visibleHeight) {
return 100;
}
const startIdx = this.getVisibleStartIndex();
const maxStartIdx = Math.max(0, this.totalLines - this.visibleHeight);
if (maxStartIdx === 0) {
return 100;
}
return Math.round((startIdx / maxStartIdx) * 100);
}
getScrollStatus() {
if (this.totalLines <= this.visibleHeight) {
return 'All';
}
const percentage = this.getScrollPercentage();
if (percentage === 0) {
return 'Top';
}
else if (percentage === 100) {
return 'Bottom';
}
else {
return `${percentage}%`;
}
}
validateScrollPosition() {
const maxScroll = this.getMaxScrollPosition();
this.scrollPosition = Math.max(0, Math.min(this.scrollPosition, maxScroll));
}
getMaxScrollPosition() {
return Math.max(0, this.totalLines - this.visibleHeight);
}
handleResize(newVisibleHeight) {
const oldHeight = this.visibleHeight;
this.visibleHeight = newVisibleHeight;
if (oldHeight > 0 && newVisibleHeight > 0) {
const ratio = newVisibleHeight / oldHeight;
this.scrollPosition = Math.floor(this.scrollPosition * ratio);
}
this.validateScrollPosition();
}
scrollToLine(lineIndex) {
if (lineIndex < 0 || lineIndex >= this.totalLines) {
return;
}
const { start, end } = this.getVisibleRange();
if (lineIndex >= start && lineIndex < end) {
return;
}
const targetStartIdx = Math.max(0, lineIndex - Math.floor(this.visibleHeight / 2));
const maxStartIdx = Math.max(0, this.totalLines - this.visibleHeight);
const newStartIdx = Math.min(targetStartIdx, maxStartIdx);
this.scrollPosition = maxStartIdx - newStartIdx;
}
shouldAutoscroll() {
return this.scrollPosition === 0;
}
}
export const scrollManager = new ScrollManager();
//# sourceMappingURL=scroll-manager.js.map