Keep the claude pane prompt-only: compact height + auto transcript wipe
The pane was showing Claude Code's own transcript (tool calls, diffs) — redundant with the feed above. Two measures: - Default pane height is now compact (12 rows): room for the input box and status, (almost) none for transcript. Interactive grow to 75% is unchanged. - When a turn of the embedded session finishes streaming (Tap drop), schedule a ctrl-l into the PTY 400ms later: Claude Code clears the screen but keeps the conversation, leaving just the prompt UI. Residual: while a response is streaming, a few transcript rows can still scroll through the compact pane until the turn-end wipe fires. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
10
src/app.rs
10
src/app.rs
@@ -27,6 +27,10 @@ pub struct App {
|
|||||||
/// (AskUserQuestion/ExitPlanMode seen in the stream, answer not yet
|
/// (AskUserQuestion/ExitPlanMode seen in the stream, answer not yet
|
||||||
/// echoed back) — the UI gives the pane more rows while set.
|
/// echoed back) — the UI gives the pane more rows while set.
|
||||||
pub embed_grow: bool,
|
pub embed_grow: bool,
|
||||||
|
/// When set, the UI should send ctrl-l to the embedded claude at this
|
||||||
|
/// instant (a beat after a turn ends, once Claude Code has printed its
|
||||||
|
/// final transcript lines) so the pane stays prompt-only.
|
||||||
|
pub embed_clear_at: Option<Instant>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Client-side tools that render a large interactive UI in Claude Code.
|
/// Client-side tools that render a large interactive UI in Claude Code.
|
||||||
@@ -47,6 +51,7 @@ impl App {
|
|||||||
show_sessions: true,
|
show_sessions: true,
|
||||||
embed_session: None,
|
embed_session: None,
|
||||||
embed_grow: false,
|
embed_grow: false,
|
||||||
|
embed_clear_at: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,6 +343,11 @@ impl Drop for Tap {
|
|||||||
if let Some(i) = self.cur.take() {
|
if let Some(i) = self.cur.take() {
|
||||||
s.entries[i].done = true;
|
s.entries[i].done = true;
|
||||||
}
|
}
|
||||||
|
// A turn of the embedded session just finished streaming: schedule a
|
||||||
|
// transcript wipe shortly after Claude Code prints its final lines.
|
||||||
|
if a.embed_session.as_deref() == Some(a.sessions[self.sidx].key.as_str()) {
|
||||||
|
a.embed_clear_at = Some(Instant::now() + std::time::Duration::from_millis(400));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
src/term.rs
12
src/term.rs
@@ -137,6 +137,18 @@ impl EmbeddedTerm {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ask Claude Code to wipe its transcript from the visible screen
|
||||||
|
/// (ctrl-l clears the terminal but keeps the conversation; the live
|
||||||
|
/// prompt UI redraws immediately). Used to keep the pane prompt-only —
|
||||||
|
/// the feed above already shows everything the transcript would.
|
||||||
|
pub fn clear_screen(&self) {
|
||||||
|
let _ = self
|
||||||
|
.term
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.key_down(KeyCode::Char('l'), KeyModifiers::CTRL);
|
||||||
|
}
|
||||||
|
|
||||||
/// Forward a key press to the child. Returns false for keys we don't map.
|
/// Forward a key press to the child. Returns false for keys we don't map.
|
||||||
pub fn key(&self, k: ratatui::crossterm::event::KeyEvent) -> bool {
|
pub fn key(&self, k: ratatui::crossterm::event::KeyEvent) -> bool {
|
||||||
use ratatui::crossterm::event::KeyCode as CK;
|
use ratatui::crossterm::event::KeyCode as CK;
|
||||||
|
|||||||
35
src/ui.rs
35
src/ui.rs
@@ -83,6 +83,24 @@ fn event_loop(
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
loop {
|
loop {
|
||||||
terminal.draw(|f| draw(f, &app, eui))?;
|
terminal.draw(|f| draw(f, &app, eui))?;
|
||||||
|
// Scheduled transcript wipe (set by the tap when an embedded-session
|
||||||
|
// turn finishes): keeps the pane prompt-only.
|
||||||
|
let clear_due = {
|
||||||
|
let mut a = app.lock().unwrap();
|
||||||
|
if a.embed_clear_at.is_some_and(|t| std::time::Instant::now() >= t) {
|
||||||
|
a.embed_clear_at = None;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if clear_due {
|
||||||
|
if let Some(et) = &eui.term {
|
||||||
|
if !et.exited() {
|
||||||
|
et.clear_screen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if !event::poll(Duration::from_millis(33))? {
|
if !event::poll(Duration::from_millis(33))? {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -193,15 +211,20 @@ fn event_loop(
|
|||||||
|
|
||||||
fn draw(f: &mut Frame, app: &SharedApp, eui: &mut EmbedUi) {
|
fn draw(f: &mut Frame, app: &SharedApp, eui: &mut EmbedUi) {
|
||||||
let mut a = app.lock().unwrap();
|
let mut a = app.lock().unwrap();
|
||||||
// Embedded pane height: nothing when hidden; ~35% of the screen normally,
|
// Embedded pane height: nothing when hidden. The pane is meant to be
|
||||||
// 75% while the tap says an interactive prompt is on screen — Claude
|
// prompt-only (the feed above shows the context), so the default is just
|
||||||
// Code's question UI clips its option list to the rows it gets, so the
|
// tall enough for Claude Code's input box + status rows; while the tap
|
||||||
// prompt wants as much height as we can spare.
|
// says an interactive prompt is on screen it grows to 75% so the option
|
||||||
|
// list isn't clipped.
|
||||||
|
const EMBED_COMPACT: u16 = 12; // 10 inner rows + borders
|
||||||
let show_embed = eui.visible && eui.term.is_some();
|
let show_embed = eui.visible && eui.term.is_some();
|
||||||
let embed_h = if show_embed {
|
let embed_h = if show_embed {
|
||||||
let total = f.area().height;
|
let total = f.area().height;
|
||||||
let pct = if a.embed_grow { 75 } else { 35 };
|
if a.embed_grow {
|
||||||
((total as u32 * pct / 100) as u16).clamp(10, total.saturating_sub(6))
|
((total as u32 * 75 / 100) as u16).clamp(10, total.saturating_sub(6))
|
||||||
|
} else {
|
||||||
|
EMBED_COMPACT.min(total.saturating_sub(6))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user