mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
fix: sticky scroll on tab switch - keep terminal at bottom after new output
Add isTerminalAtBottom() helper and modify batchTerminalWrite() to implement sticky scroll behavior. When SSE terminal data arrives after tab switch, terminal now stays scrolled to Claude input if user was already at bottom. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
Claudeman is a Claude Code session manager with a web interface and autonomous Ralph Loop. It spawns Claude CLI processes via PTY, streams output in real-time via SSE, and supports scheduled/timed runs.
|
||||
|
||||
**Version**: 0.1349
|
||||
**Version**: 0.1350
|
||||
|
||||
**Tech Stack**: TypeScript (ES2022/NodeNext, strict mode), Node.js, Fastify, Server-Sent Events, node-pty
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claudeman",
|
||||
"version": "0.1349",
|
||||
"version": "0.1350",
|
||||
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@@ -380,6 +380,7 @@ class ClaudemanApp {
|
||||
// Terminal write batching
|
||||
this.pendingWrites = '';
|
||||
this.writeFrameScheduled = false;
|
||||
this._wasAtBottomBeforeWrite = true; // Default to true for sticky scroll
|
||||
|
||||
// Render debouncing
|
||||
this.renderSessionTabsTimeout = null;
|
||||
@@ -564,7 +565,25 @@ class ClaudemanApp {
|
||||
this.terminal.writeln('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if terminal viewport is at or near the bottom.
|
||||
* Used to implement "sticky scroll" behavior - keep user at bottom if they were there.
|
||||
*/
|
||||
isTerminalAtBottom() {
|
||||
if (!this.terminal) return true;
|
||||
const buffer = this.terminal.buffer.active;
|
||||
// viewportY is the top line of the viewport, baseY is where scrollback starts
|
||||
// If viewportY >= baseY, we're showing the latest content (at bottom)
|
||||
// Allow 2 lines tolerance for edge cases
|
||||
return buffer.viewportY >= buffer.baseY - 2;
|
||||
}
|
||||
|
||||
batchTerminalWrite(data) {
|
||||
// Check if at bottom BEFORE adding data (captures user's scroll position)
|
||||
// Only update if not already scheduled (preserve the first check's result)
|
||||
if (!this.writeFrameScheduled) {
|
||||
this._wasAtBottomBeforeWrite = this.isTerminalAtBottom();
|
||||
}
|
||||
this.pendingWrites += data;
|
||||
if (!this.writeFrameScheduled) {
|
||||
this.writeFrameScheduled = true;
|
||||
@@ -572,6 +591,10 @@ class ClaudemanApp {
|
||||
if (this.pendingWrites && this.terminal) {
|
||||
this.terminal.write(this.pendingWrites);
|
||||
this.pendingWrites = '';
|
||||
// Sticky scroll: if user was at bottom, keep them there after new output
|
||||
if (this._wasAtBottomBeforeWrite) {
|
||||
this.terminal.scrollToBottom();
|
||||
}
|
||||
}
|
||||
this.writeFrameScheduled = false;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user