Windows TUI Height Fix — Bug Report for Developers
Problem Description
When running the TUI (qwen-proxy serve without --headless) on Windows 10, the following issues occur:
- Top menu bar (header/status) is not visible — content renders above the visible area of the terminal window
- Scrollbar appears on every data refresh — content overflows the visible area, causing the terminal to show a scrollbar
- The issue reproduces consistently on every data update cycle (every second)
Root Cause
On Windows, the terminal window title bar occupies an additional line that is counted in process.stdout.rows but is not visible to the user. The TUI uses process.stdout.rows directly without compensating for this offset, causing the calculated content height to exceed the actual visible area by multiple lines.
Additionally, when the TUI outputs exactly termRows lines, the Windows terminal creates a scroll overflow. Reducing the output by several extra lines prevents this behavior.
Applied Changes
All changes were applied directly in the compiled (bundled) files of the global npm installation.
Installation path: C:\Users\NEO\AppData\Roaming\npm\node_modules\qwen-proxy\dist\src\tui2\
1. state.js — Initial Viewport Initialization
File: dist/src/tui2/state.js
Lines: 28-33
Before:
function initialViewport() {
return {
cols: process.stdout.columns ?? 120,
rows: process.stdout.rows ?? 40,
};
}
After:
const WINDOWS_TITLE_OFFSET = typeof process !== "undefined" && process.platform === "win32" ? 1 : 0;
function initialViewport() {
return {
cols: process.stdout.columns ?? 120,
rows: (process.stdout.rows ?? 40) - WINDOWS_TITLE_OFFSET,
};
}
2. main.js — Resize and Periodic Tick Handlers
File: dist/src/tui2/main.js
Lines: 318-331
Before:
process.on("resize", () => {
dispatch({
type: "set-viewport",
cols: process.stdout.columns ?? currentState.viewportCols,
rows: process.stdout.rows ?? currentState.viewportRows,
});
});
tickTimer = setInterval(() => {
dispatch({ type: "tick", nowMs: Date.now() });
dispatch({
type: "set-viewport",
cols: process.stdout.columns ?? currentState.viewportCols,
rows: process.stdout.rows ?? currentState.viewportRows,
});
After:
const WINDOWS_TITLE_OFFSET = process.platform === "win32" ? 1 : 0;
process.on("resize", () => {
dispatch({
type: "set-viewport",
cols: process.stdout.columns ?? currentState.viewportCols,
rows: (process.stdout.rows ?? currentState.viewportRows) - WINDOWS_TITLE_OFFSET,
});
});
tickTimer = setInterval(() => {
dispatch({ type: "tick", nowMs: Date.now() });
dispatch({
type: "set-viewport",
cols: process.stdout.columns ?? currentState.viewportCols,
rows: (process.stdout.rows ?? currentState.viewportRows) - WINDOWS_TITLE_OFFSET,
});
3. app.js — Render Function
File: dist/src/tui2/app.js
Lines: ~764-776
Before:
const termRows = terminal?.rows ?? state.viewportRows ?? 40;
const sbW = this.sidebarWidth();
const mainW = Math.max(20, width - sbW - 1);
const sidebarLines = this.renderSidebar(sbW, termRows);
const header = this.renderHeader(mainW);
const footer = this.renderFooter(mainW);
const contentRows = Math.max(0, termRows - 2);
After:
const termRows = terminal?.rows ?? state.viewportRows ?? 40;
const windowsTitleOffset = process.platform === "win32" ? 1 : 0;
const sbW = this.sidebarWidth();
const mainW = Math.max(20, width - sbW - 1);
const sidebarLines = this.renderSidebar(sbW, termRows);
const header = this.renderHeader(mainW);
const footer = this.renderFooter(mainW);
const windowsExtra = process.platform === "win32" ? 6 : 0;
const contentRows = Math.max(0, termRows - 2 - windowsTitleOffset - windowsExtra);
Explanation:
windowsTitleOffset (-1 line): Compensates for the invisible title bar line on Windows counted in process.stdout.rows
windowsExtra (-6 lines): Prevents output overflow — on Windows, the terminal creates a scroll when the number of output lines equals or approaches termRows. Empirically determined value of 6 fully eliminates the issue.
- Total for Windows:
contentRows = termRows - 8 instead of termRows - 2
Recommendations for a Permanent Fix
Instead of hardcoding process.platform === "win32", consider:
-
Use the @mariozechner/pi-tui library API — check if it provides automatic height compensation for Windows. If available, use its API to get the real visible height.
-
Alternative approach — measure the actual visible area experimentally (e.g., write a character to the last line and check if it's visible) instead of platform-dependent code.
-
If platform-dependent approach is acceptable — extract the constant into a single config file and apply it everywhere contentRows/viewportRows is calculated.
Source Files (for Build)
These changes should be applied to the source files (before compilation into dist/):
| Bundled File |
Probable Source File (estimated) |
dist/src/tui2/state.js |
src/tui2/state.ts |
dist/src/tui2/main.js |
src/tui2/main.ts |
dist/src/tui2/app.js |
src/tui2/app.tsx or src/tui2/app.ts |
Note About Local Repository
The local repository at C:\EX2025\STORM\qwen-proxy does not contain TUI source files. The TUI code exists only in the global npm installation of the qwen-proxy package. The local project contains only the proxy server portion (src/index.js, src/qwen/, src/utils/).
The fixes were applied directly to the bundled files of the global installation:
C:\Users\NEO\AppData\Roaming\npm\node_modules\qwen-proxy\dist\src\tui2\state.js
C:\Users\NEO\AppData\Roaming\npm\node_modules\qwen-proxy\dist\src\tui2\main.js
C:\Users\NEO\AppData\Roaming\npm\node_modules\qwen-proxy\dist\src\tui2\app.js
Testing
After applying the fix to source code and rebuilding:
- Test on Windows 10 with Windows Terminal and standard cmd.exe
- Test on Linux/macOS to ensure the fix does not affect other platforms
- Verify that content correctly recalculates on window resize
Verified Working
The fix with contentRows = termRows - 8 for Windows has been tested and confirmed working — the header is fully visible and no unwanted scrolling occurs during data refresh cycles.
Windows TUI Height Fix — Bug Report for Developers
Problem Description
When running the TUI (
qwen-proxy servewithout--headless) on Windows 10, the following issues occur:Root Cause
On Windows, the terminal window title bar occupies an additional line that is counted in
process.stdout.rowsbut is not visible to the user. The TUI usesprocess.stdout.rowsdirectly without compensating for this offset, causing the calculated content height to exceed the actual visible area by multiple lines.Additionally, when the TUI outputs exactly
termRowslines, the Windows terminal creates a scroll overflow. Reducing the output by several extra lines prevents this behavior.Applied Changes
All changes were applied directly in the compiled (bundled) files of the global npm installation.
Installation path:
C:\Users\NEO\AppData\Roaming\npm\node_modules\qwen-proxy\dist\src\tui2\1.
state.js— Initial Viewport InitializationFile:
dist/src/tui2/state.jsLines: 28-33
Before:
After:
2.
main.js— Resize and Periodic Tick HandlersFile:
dist/src/tui2/main.jsLines: 318-331
Before:
After:
3.
app.js— Render FunctionFile:
dist/src/tui2/app.jsLines: ~764-776
Before:
After:
Explanation:
windowsTitleOffset(-1 line): Compensates for the invisible title bar line on Windows counted inprocess.stdout.rowswindowsExtra(-6 lines): Prevents output overflow — on Windows, the terminal creates a scroll when the number of output lines equals or approachestermRows. Empirically determined value of 6 fully eliminates the issue.contentRows = termRows - 8instead oftermRows - 2Recommendations for a Permanent Fix
Instead of hardcoding
process.platform === "win32", consider:Use the
@mariozechner/pi-tuilibrary API — check if it provides automatic height compensation for Windows. If available, use its API to get the real visible height.Alternative approach — measure the actual visible area experimentally (e.g., write a character to the last line and check if it's visible) instead of platform-dependent code.
If platform-dependent approach is acceptable — extract the constant into a single config file and apply it everywhere
contentRows/viewportRowsis calculated.Source Files (for Build)
These changes should be applied to the source files (before compilation into
dist/):dist/src/tui2/state.jssrc/tui2/state.tsdist/src/tui2/main.jssrc/tui2/main.tsdist/src/tui2/app.jssrc/tui2/app.tsxorsrc/tui2/app.tsNote About Local Repository
The local repository at
C:\EX2025\STORM\qwen-proxydoes not contain TUI source files. The TUI code exists only in the global npm installation of theqwen-proxypackage. The local project contains only the proxy server portion (src/index.js,src/qwen/,src/utils/).The fixes were applied directly to the bundled files of the global installation:
C:\Users\NEO\AppData\Roaming\npm\node_modules\qwen-proxy\dist\src\tui2\state.jsC:\Users\NEO\AppData\Roaming\npm\node_modules\qwen-proxy\dist\src\tui2\main.jsC:\Users\NEO\AppData\Roaming\npm\node_modules\qwen-proxy\dist\src\tui2\app.jsTesting
After applying the fix to source code and rebuilding:
Verified Working
The fix with
contentRows = termRows - 8for Windows has been tested and confirmed working — the header is fully visible and no unwanted scrolling occurs during data refresh cycles.