commit
This commit is contained in:
17
README.md
17
README.md
@@ -25,9 +25,9 @@ The full screen clears are 35x larger than incremental line clears - these are t
|
||||
claude-chill sits between your terminal and Claude Code:
|
||||
|
||||
1. **Intercepts sync blocks** - Catches those massive atomic updates
|
||||
2. **Truncates output** - Sends only the last N lines (default: 100) to your terminal
|
||||
3. **Preserves history** - Stores the full content in a buffer
|
||||
4. **Enables lookback** - Press a key to dump the buffer, then scroll up
|
||||
2. **Truncates full screen clears** - Only the 45% that are full redraws (94.5 KB avg) get truncated to the last N lines (default: 100). The 55% incremental updates (2.7 KB avg) pass through unchanged.
|
||||
3. **Preserves history** - Accumulates content in a buffer. Clears on full screen clear, accumulates otherwise.
|
||||
4. **Enables lookback** - Press a key to pause Claude and view the full history buffer
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -43,9 +43,16 @@ claude-chill -- claude --verbose # Use -- for command flags
|
||||
claude-chill -l 50 -- claude # Set max lines to 50
|
||||
```
|
||||
|
||||
## Lookback
|
||||
## Lookback Mode
|
||||
|
||||
Press `Ctrl+Shift+J` to dump the history buffer to terminal. Scroll up to see it. The next update from Claude will resume normal display.
|
||||
Press `Ctrl+Shift+J` (or your configured key) to enter lookback mode:
|
||||
|
||||
1. **Claude pauses** - Output from Claude is cached, input is blocked
|
||||
2. **History dumps** - The full history buffer is written to your terminal
|
||||
3. **Scroll freely** - Use your terminal's scrollback to review everything
|
||||
4. **Exit** - Press the lookback key again or `Ctrl+C` to resume
|
||||
|
||||
When you exit lookback mode, any cached output is processed and the current state is displayed.
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ pub struct Proxy {
|
||||
history: LineBuffer,
|
||||
sync_buffer: Vec<u8>,
|
||||
in_sync_block: bool,
|
||||
in_lookback_mode: bool,
|
||||
lookback_cache: Vec<u8>,
|
||||
input_buffer: Vec<u8>,
|
||||
output_buffer: Vec<u8>,
|
||||
sync_start_finder: memmem::Finder<'static>,
|
||||
@@ -114,6 +116,8 @@ impl Proxy {
|
||||
original_termios,
|
||||
sync_buffer: Vec::with_capacity(SYNC_BUFFER_CAPACITY),
|
||||
in_sync_block: false,
|
||||
in_lookback_mode: false,
|
||||
lookback_cache: Vec::new(),
|
||||
input_buffer: Vec::with_capacity(INPUT_BUFFER_CAPACITY),
|
||||
output_buffer: Vec::with_capacity(OUTPUT_BUFFER_CAPACITY),
|
||||
sync_start_finder: memmem::Finder::new(SYNC_START),
|
||||
@@ -197,6 +201,11 @@ impl Proxy {
|
||||
}
|
||||
|
||||
fn process_output(&mut self, data: &[u8], stdout_fd: i32) -> Result<()> {
|
||||
if self.in_lookback_mode {
|
||||
self.lookback_cache.extend_from_slice(data);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut pos = 0;
|
||||
|
||||
while pos < data.len() {
|
||||
@@ -261,36 +270,81 @@ impl Proxy {
|
||||
|
||||
fn process_input(&mut self, data: &[u8], stdout_fd: i32) -> Result<()> {
|
||||
let master_fd = self.pty_master.as_raw_fd();
|
||||
|
||||
for &byte in data {
|
||||
if self.in_lookback_mode && byte == 0x03 {
|
||||
self.input_buffer.clear();
|
||||
self.exit_lookback_mode(stdout_fd)?;
|
||||
continue;
|
||||
}
|
||||
|
||||
self.input_buffer.push(byte);
|
||||
|
||||
if self.input_buffer.len() > self.config.lookback_sequence.len() {
|
||||
let excess = self.input_buffer.len() - self.config.lookback_sequence.len();
|
||||
if !self.in_lookback_mode {
|
||||
write_all_raw(master_fd, &self.input_buffer[..excess])?;
|
||||
}
|
||||
self.input_buffer.drain(..excess);
|
||||
}
|
||||
|
||||
if self.input_buffer == self.config.lookback_sequence {
|
||||
self.enter_lookback_mode(stdout_fd)?;
|
||||
self.input_buffer.clear();
|
||||
if self.in_lookback_mode {
|
||||
self.exit_lookback_mode(stdout_fd)?;
|
||||
} else {
|
||||
self.enter_lookback_mode()?;
|
||||
}
|
||||
} else if !self
|
||||
.config
|
||||
.lookback_sequence
|
||||
.starts_with(&self.input_buffer)
|
||||
{
|
||||
if !self.in_lookback_mode {
|
||||
write_all_raw(master_fd, &self.input_buffer)?;
|
||||
}
|
||||
self.input_buffer.clear();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn enter_lookback_mode(&mut self, stdout_fd: i32) -> Result<()> {
|
||||
let header = b"\x1b[7m--- LOOKBACK: scroll up to see history ---\x1b[0m\r\n";
|
||||
write_all_raw(stdout_fd, header)?;
|
||||
fn enter_lookback_mode(&mut self) -> Result<()> {
|
||||
self.in_lookback_mode = true;
|
||||
self.lookback_cache.clear();
|
||||
|
||||
self.output_buffer.clear();
|
||||
self.history.append_all(&mut self.output_buffer);
|
||||
|
||||
let stdout_fd = io::stdout().as_raw_fd();
|
||||
write_all_raw(stdout_fd, CLEAR_SCREEN)?;
|
||||
write_all_raw(stdout_fd, CURSOR_HOME)?;
|
||||
write_all_raw(stdout_fd, &self.output_buffer)?;
|
||||
|
||||
let exit_msg = format!(
|
||||
"\r\n\x1b[7m--- LOOKBACK MODE: press {} or Ctrl+C to exit ---\x1b[0m\r\n",
|
||||
self.config.lookback_key
|
||||
);
|
||||
write_all_raw(stdout_fd, exit_msg.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exit_lookback_mode(&mut self, stdout_fd: i32) -> Result<()> {
|
||||
self.in_lookback_mode = false;
|
||||
|
||||
let cached = std::mem::take(&mut self.lookback_cache);
|
||||
if !cached.is_empty() {
|
||||
self.process_output(&cached, stdout_fd)?;
|
||||
}
|
||||
|
||||
self.output_buffer.clear();
|
||||
self.history.append_all(&mut self.output_buffer);
|
||||
|
||||
write_all_raw(stdout_fd, CLEAR_SCREEN)?;
|
||||
write_all_raw(stdout_fd, CURSOR_HOME)?;
|
||||
write_all_raw(stdout_fd, &self.output_buffer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user