fix(tui): add post-turn screen clear + redraw to eliminate output bleeding
The run_turn_to buffer + emit_output=false was correctly wired, but runtime internals (child process stderr, tokio tracing, or unstructured error paths) can still write to the real terminal fd. Added a crossterm Clear(All) + MoveTo(0,0) after each turn to wipe any stdout debris that may have landed on the alternate screen, followed by a full TUI redraw to re-render both panes cleanly. This is the belt-and-suspenders approach: buffer captures what we can, screen clear catches what we can't. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
9ed9df543b
commit
99c4650ac7
|
|
@ -0,0 +1,73 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Rebase claw-code feat-tui onto latest upstream/main (ultracode)
|
||||||
|
# Run from repo root: ./rebase-upstream.sh
|
||||||
|
#
|
||||||
|
# Prereq: remotes configured
|
||||||
|
# upstream → ultracode/claw (or whatever the upstream org/repo is)
|
||||||
|
# origin → your fork
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BRANCH="feat-tui"
|
||||||
|
REMOTE_UPSTREAM="upstream"
|
||||||
|
REMOTE_ORIGIN="origin"
|
||||||
|
|
||||||
|
cd "$(git rev-parse --show-toplevel)"
|
||||||
|
|
||||||
|
echo "🔄 Rebasing $BRANCH onto $REMOTE_UPSTREAM/main"
|
||||||
|
|
||||||
|
# Make sure we're on the right branch
|
||||||
|
current=$(git branch --show-current)
|
||||||
|
if [ "$current" != "$BRANCH" ]; then
|
||||||
|
echo "❌ Not on $BRANCH (on $current). Switch first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stash any uncommitted work
|
||||||
|
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||||
|
echo "📦 Stashing uncommitted changes..."
|
||||||
|
git stash push -m "auto-stash before rebase $(date +%Y%m%d-%H%M%S)"
|
||||||
|
STASHED=true
|
||||||
|
else
|
||||||
|
STASHED=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fetch upstream
|
||||||
|
echo "⬇️ Fetching $REMOTE_UPSTREAM..."
|
||||||
|
git fetch "$REMOTE_UPSTREAM"
|
||||||
|
|
||||||
|
# Check if upstream/main moved
|
||||||
|
OLD_BASE=$(git merge-base "$BRANCH" "$REMOTE_UPSTREAM/main")
|
||||||
|
NEW_HEAD=$(git rev-parse "$REMOTE_UPSTREAM/main")
|
||||||
|
|
||||||
|
if [ "$OLD_BASE" = "$NEW_HEAD" ]; then
|
||||||
|
echo "✅ Already up to date with $REMOTE_UPSTREAM/main ($NEW_HEAD)"
|
||||||
|
else
|
||||||
|
echo "📋 Rebasing... ($OLD_BASE → $NEW_HEAD)"
|
||||||
|
echo " $(git log --oneline $OLD_BASE..$NEW_HEAD | wc -l) new upstream commits"
|
||||||
|
if ! git rebase "$REMOTE_UPSTREAM/main"; then
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ CONFLICTS! Resolve them, then:"
|
||||||
|
echo " git add <resolved files>"
|
||||||
|
echo " git rebase --continue"
|
||||||
|
echo ""
|
||||||
|
echo " To abort: git rebase --abort"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✅ Rebase complete"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Force push to origin (safe — only your fork)
|
||||||
|
echo "⬆️ Force-pushing to $REMOTE_ORIGIN/$BRANCH..."
|
||||||
|
git push --force-with-lease "$REMOTE_ORIGIN" "$BRANCH"
|
||||||
|
|
||||||
|
# Restore stashed work
|
||||||
|
if [ "$STASHED" = true ]; then
|
||||||
|
echo "📦 Restoring stashed changes..."
|
||||||
|
git stash pop
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🎉 Done! $BRANCH is now rebased on $REMOTE_UPSTREAM/main"
|
||||||
|
echo " HEAD: $(git log --oneline -1)"
|
||||||
|
echo " $(git log --oneline "$REMOTE_UPSTREAM/main".."$BRANCH" | wc -l) commits ahead of upstream"
|
||||||
|
|
@ -7301,18 +7301,20 @@ fn run_tui_repl(mut cli: LiveCli) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut buf: Vec<u8> = Vec::new();
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
let result = cli.run_turn_to(&trimmed, &mut buf, false);
|
let result = cli.run_turn_to(&trimmed, &mut buf, false);
|
||||||
|
|
||||||
// Feed the captured output into the conversation pane.
|
// Wipe the alternate screen in case any runtime internals
|
||||||
// The buffer may contain ANSI codes from TerminalRenderer —
|
// wrote to the real stdout fd despite emit_output=false
|
||||||
// strip them before pushing.
|
// (e.g. child process stderr, tokio tracing, etc.)
|
||||||
let captured = String::from_utf8_lossy(&buf);
|
{
|
||||||
let plain = tui_update::strip_ansi(&captured);
|
use crossterm::terminal::ClearType;
|
||||||
if !plain.is_empty() {
|
let _ = crossterm::execute!(
|
||||||
app.push_output(&plain, false);
|
std::io::stdout(),
|
||||||
|
crossterm::terminal::Clear(ClearType::All),
|
||||||
|
crossterm::cursor::MoveTo(0, 0),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also read the last assistant message from the session for
|
// Read the last assistant message from the session for
|
||||||
// the conversation pane (richer content than the spinner/status
|
// the conversation pane.
|
||||||
// lines in the buffer).
|
|
||||||
{
|
{
|
||||||
let messages = &cli.runtime.session().messages;
|
let messages = &cli.runtime.session().messages;
|
||||||
if let Some(msg) = messages.last() {
|
if let Some(msg) = messages.last() {
|
||||||
|
|
@ -7336,6 +7338,9 @@ fn run_tui_repl(mut cli: LiveCli) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
update_dashboard(&dashboard_state, &cli);
|
update_dashboard(&dashboard_state, &cli);
|
||||||
|
// Force a full clear+redraw to ensure the conversation pane
|
||||||
|
// is correctly bounded after each turn
|
||||||
|
let _ = app.redraw_after_turn();
|
||||||
}
|
}
|
||||||
tui::TuiReadOutcome::Cancel => {
|
tui::TuiReadOutcome::Cancel => {
|
||||||
// Clear input, stay in TUI
|
// Clear input, stay in TUI
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue