Fix layout to avoid terminal scrolling and show repo on list screen
This commit is contained in:
parent
64f147d10c
commit
524ad77ea6
74
src/tui.ts
74
src/tui.ts
|
|
@ -120,8 +120,8 @@ export class TuiEngine {
|
|||
process.stdin.resume();
|
||||
process.stdin.on('keypress', this.handleKeypress.bind(this));
|
||||
|
||||
// Hide standard cursor
|
||||
process.stdout.write('\x1B[?25l');
|
||||
// Enter alternate screen buffer and hide standard cursor
|
||||
process.stdout.write('\x1B[?1049h\x1B[?25l');
|
||||
|
||||
// Bootstrap if config exists
|
||||
if (this.state.config.url && this.state.config.owner && this.state.config.repo) {
|
||||
|
|
@ -137,8 +137,8 @@ export class TuiEngine {
|
|||
*/
|
||||
public stop() {
|
||||
this.stopSpinner();
|
||||
// Show standard cursor
|
||||
process.stdout.write('\x1B[?25h');
|
||||
// Exit alternate screen buffer and show standard cursor
|
||||
process.stdout.write('\x1B[?1049l\x1B[?25h');
|
||||
if (process.stdin.isTTY) {
|
||||
process.stdin.setRawMode(false);
|
||||
}
|
||||
|
|
@ -670,8 +670,8 @@ export class TuiEngine {
|
|||
const cols = process.stdout.columns || 80;
|
||||
const rows = process.stdout.rows || 24;
|
||||
|
||||
// Clear terminal screen and reset cursor position
|
||||
process.stdout.write('\x1B[2J\x1B[H');
|
||||
// Clear terminal screen, clear scrollback buffer, and reset cursor position
|
||||
process.stdout.write('\x1B[2J\x1B[3J\x1B[H');
|
||||
|
||||
if (this.state.screen === 'setup') {
|
||||
this.renderSetupScreen(cols, rows);
|
||||
|
|
@ -794,19 +794,64 @@ export class TuiEngine {
|
|||
// Header Bar
|
||||
const spinnerStr = this.state.loading ? chalk.bold.cyan(SPINNER_FRAMES[spinnerIndex]) + ' ' : '';
|
||||
const instanceName = normalizeUrl(this.state.config.url).replace(/^https?:\/\//, '');
|
||||
const rightHeader = `repo: ${chalk.bold.cyan(`${this.state.config.owner}/${this.state.config.repo}`)} `;
|
||||
const rightLen = stripAnsi(rightHeader).length;
|
||||
|
||||
// We want the total line length to fit exactly within 'cols', so max left side width is cols - rightLen - 1
|
||||
const maxLeftWidth = cols - rightLen - 1;
|
||||
|
||||
// Spinner plain length is 2 if loading, 0 otherwise
|
||||
const spinnerPlainLen = this.state.loading ? 2 : 0;
|
||||
const titlePlain = 'Forgejo Issue Explorer';
|
||||
const titleColor = chalk.bold.hex('#4A90E2')(titlePlain);
|
||||
|
||||
let leftHeader = ` ${spinnerStr}${chalk.bold.hex('#4A90E2')('Forgejo Issue Explorer')} ─ ${chalk.bold.white(instanceName)}`;
|
||||
const instanceColor = ` ─ ${chalk.bold.white(instanceName)}`;
|
||||
const instancePlain = ` ─ ${instanceName}`;
|
||||
|
||||
let statsColor = '';
|
||||
let statsPlain = '';
|
||||
let shortStatsColor = '';
|
||||
let shortStatsPlain = '';
|
||||
|
||||
if (this.state.totalIssuesCount > 0) {
|
||||
const totalPages = Math.ceil(this.state.totalIssuesCount / this.state.issuesPerPage);
|
||||
leftHeader += ` ─ Page ${chalk.bold.white(this.state.currentPage)} of ${chalk.bold.white(totalPages)} (${chalk.yellow(this.state.totalIssuesCount)} matching)`;
|
||||
statsColor = ` ─ Page ${chalk.bold.white(this.state.currentPage)} of ${chalk.bold.white(totalPages)} (${chalk.yellow(this.state.totalIssuesCount)} matching)`;
|
||||
statsPlain = ` ─ Page ${this.state.currentPage} of ${totalPages} (${this.state.totalIssuesCount} matching)`;
|
||||
|
||||
shortStatsColor = ` ─ P. ${chalk.bold.white(this.state.currentPage)}/${chalk.bold.white(totalPages)} (${chalk.yellow(this.state.totalIssuesCount)})`;
|
||||
shortStatsPlain = ` ─ P. ${this.state.currentPage}/${totalPages} (${this.state.totalIssuesCount})`;
|
||||
} else if (!this.state.loading) {
|
||||
leftHeader += ' ─ (0 issues found)';
|
||||
statsColor = ' ─ (0 issues found)';
|
||||
statsPlain = ' ─ (0 issues found)';
|
||||
|
||||
shortStatsColor = ' ─ (0)';
|
||||
shortStatsPlain = ' ─ (0)';
|
||||
}
|
||||
|
||||
const rightHeader = `repo: ${chalk.bold.cyan(`${this.state.config.owner}/${this.state.config.repo}`)} `;
|
||||
let leftHeader = '';
|
||||
|
||||
// Attempt 1: Full header: spinner + Title + instance + stats
|
||||
if (1 + spinnerPlainLen + titlePlain.length + instancePlain.length + statsPlain.length <= maxLeftWidth) {
|
||||
leftHeader = ` ${spinnerStr}${titleColor}${instanceColor}${statsColor}`;
|
||||
}
|
||||
// Attempt 2: Drop instance, keep full stats
|
||||
else if (1 + spinnerPlainLen + titlePlain.length + statsPlain.length <= maxLeftWidth) {
|
||||
leftHeader = ` ${spinnerStr}${titleColor}${statsColor}`;
|
||||
}
|
||||
// Attempt 3: Drop instance, use short stats
|
||||
else if (1 + spinnerPlainLen + titlePlain.length + shortStatsPlain.length <= maxLeftWidth) {
|
||||
leftHeader = ` ${spinnerStr}${titleColor}${shortStatsColor}`;
|
||||
}
|
||||
// Attempt 4: Drop stats, keep title + shortened instance if it fits
|
||||
else if (1 + spinnerPlainLen + titlePlain.length + instancePlain.length <= maxLeftWidth) {
|
||||
leftHeader = ` ${spinnerStr}${titleColor}${instanceColor}`;
|
||||
}
|
||||
// Attempt 5: Just Title
|
||||
else {
|
||||
leftHeader = ` ${spinnerStr}${titleColor}`;
|
||||
}
|
||||
|
||||
const leftLen = stripAnsi(leftHeader).length;
|
||||
const rightLen = stripAnsi(rightHeader).length;
|
||||
let spacesCount = cols - leftLen - rightLen;
|
||||
if (spacesCount < 1) spacesCount = 1;
|
||||
|
||||
|
|
@ -839,8 +884,8 @@ export class TuiEngine {
|
|||
);
|
||||
console.log(chalk.bold.hex('#4A90E2')('├' + '─'.repeat(cols - 2) + '┤'));
|
||||
|
||||
// Table Content Height available
|
||||
const tableHeight = rows - 7; // Header: 1, Box borders: 2, Table headers: 2, Bottom filter line: 1, Keyboard help: 1
|
||||
// Table Content Height available (rows - 9 ensures total printed lines is rows - 1, preventing terminal scrolling)
|
||||
const tableHeight = rows - 9;
|
||||
|
||||
if (this.state.issues.length === 0) {
|
||||
const msg = this.state.loading ? 'Fetching issues from server...' : (this.state.error ? `Error: ${this.state.error}` : 'No issues found.');
|
||||
|
|
@ -997,7 +1042,8 @@ export class TuiEngine {
|
|||
}
|
||||
|
||||
// Paginate/Scroll calculations
|
||||
const displayHeight = rows - 6; // Header: 1, borders: 2, meta: 2, help: 1
|
||||
const metaHeight = labels ? 3 : 2; // Meta 1, optionally Meta 2 (Labels), and Separator
|
||||
const displayHeight = rows - 5 - metaHeight; // Header: 1, Borders: 2, Meta lines, Help: 1, and -1 to avoid scrolling
|
||||
const maxScroll = Math.max(0, contentLines.length - displayHeight);
|
||||
|
||||
// Bounds check scroll offset
|
||||
|
|
|
|||
Loading…
Reference in New Issue