Fix Windows terminal background rendering and add /debug command

- Strip carriage return characters from bash tool output to fix background padding on Windows
- Add hidden /debug command to write rendered lines to debug log for TUI debugging
- Document /debug command in README.md development section
This commit is contained in:
badlogic 2025-12-02 13:22:41 +01:00
parent 4432fc9b72
commit 7b1f975ca1
3 changed files with 63 additions and 3 deletions

View file

@ -1156,6 +1156,19 @@ import { getPackageDir, getThemeDir, getPackageJsonPath, getReadmePath, getChang
**Never use `__dirname` directly** for resolving package assets. The `paths.ts` module handles the differences between execution modes automatically.
### Debug Command
The `/debug` command is a hidden development feature (not shown in autocomplete) that writes all currently rendered lines with their visible widths and ANSI escape sequences to `~/.pi/agent/pi-debug.log`. This is useful for debugging TUI rendering issues, especially when lines don't extend to the terminal edge or contain unexpected invisible characters.
```
/debug
```
The debug log includes:
- Terminal width at time of capture
- Total number of rendered lines
- Each line with its index, visible width, and JSON-escaped content showing all ANSI codes
## License
MIT

View file

@ -83,8 +83,9 @@ export class ToolExecutionComponent extends Container {
const textBlocks = this.result.content?.filter((c: any) => c.type === "text") || [];
const imageBlocks = this.result.content?.filter((c: any) => c.type === "image") || [];
// Strip ANSI codes from raw output (bash may emit colors/formatting)
let output = textBlocks.map((c: any) => stripAnsi(c.text || "")).join("\n");
// Strip ANSI codes and carriage returns from raw output
// (bash may emit colors/formatting, and Windows may include \r)
let output = textBlocks.map((c: any) => stripAnsi(c.text || "").replace(/\r/g, "")).join("\n");
// Add indicator for images
if (imageBlocks.length > 0) {

View file

@ -1,3 +1,6 @@
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import type { Agent, AgentEvent, AgentState, ThinkingLevel } from "@mariozechner/pi-agent-core";
import type { AssistantMessage, Message, Model } from "@mariozechner/pi-ai";
import type { SlashCommand } from "@mariozechner/pi-tui";
@ -12,8 +15,8 @@ import {
Text,
TruncatedText,
TUI,
visibleWidth,
} from "@mariozechner/pi-tui";
import { exec } from "child_process";
import { getChangelogPath, parseChangelog } from "../changelog.js";
import { exportSessionToHtml } from "../export-html.js";
@ -415,6 +418,13 @@ export class TuiRenderer {
return;
}
// Check for /debug command
if (text === "/debug") {
this.handleDebugCommand();
this.editor.setText("");
return;
}
// Check for file-based slash commands
text = expandSlashCommand(text, this.fileCommands);
@ -1530,6 +1540,42 @@ export class TuiRenderer {
this.ui.requestRender();
}
private handleDebugCommand(): void {
// Force a render and capture all lines with their widths
const width = (this.ui as any).terminal.columns;
const allLines = this.ui.render(width);
const debugLogPath = path.join(os.homedir(), ".pi", "agent", "pi-debug.log");
const debugData = [
`Debug output at ${new Date().toISOString()}`,
`Terminal width: ${width}`,
`Total lines: ${allLines.length}`,
"",
"=== All rendered lines with visible widths ===",
...allLines.map((line, idx) => {
const vw = visibleWidth(line);
const escaped = JSON.stringify(line);
return `[${idx}] (w=${vw}) ${escaped}`;
}),
"",
].join("\n");
fs.mkdirSync(path.dirname(debugLogPath), { recursive: true });
fs.writeFileSync(debugLogPath, debugData);
// Show confirmation
this.chatContainer.addChild(new Spacer(1));
this.chatContainer.addChild(
new Text(
theme.fg("accent", "✓ Debug log written") + "\n" + theme.fg("muted", `~/.pi/agent/pi-debug.log`),
1,
1,
),
);
this.ui.requestRender();
}
private updatePendingMessagesDisplay(): void {
this.pendingMessagesContainer.clear();