♻️ feat: implement session management for PTY sessions in the server
- Add SessionManager class to handle PTY sessions with WebSocket connections. - Implement methods for creating, retrieving, and destroying sessions. - Handle PTY output and WebSocket messages for terminal interaction. - Ensure graceful session destruction and cleanup. feat: initialize web application with Next.js and Tailwind CSS - Create initial Next.js application structure with TypeScript support. - Set up Tailwind CSS for styling with custom theme configurations. - Add ESLint configuration for code quality and consistency. feat: implement chat API and UI components - Create chat API route to handle chat requests and responses. - Develop chat layout with sidebar, header, chat window, and input components. - Integrate Zustand for state management of conversations and messages. - Add utility functions for formatting dates and managing class names. chore: add environment variables and configuration files - Create .env.example for environment variable setup. - Add configuration files for PostCSS, Tailwind CSS, and TypeScript. - Set up package.json with necessary dependencies and scripts for development.
This commit is contained in:
66
src/server/web/scrollback-buffer.ts
Normal file
66
src/server/web/scrollback-buffer.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Circular byte buffer for PTY scrollback replay.
|
||||
* Stores the last `capacity` bytes of PTY output; oldest bytes are silently
|
||||
* discarded when the buffer is full.
|
||||
*/
|
||||
export class ScrollbackBuffer {
|
||||
private buf: Buffer;
|
||||
private writePos = 0; // next write position in the ring
|
||||
private stored = 0; // bytes currently stored (≤ capacity)
|
||||
readonly capacity: number;
|
||||
|
||||
constructor(capacityBytes = 100 * 1024) {
|
||||
this.capacity = capacityBytes;
|
||||
this.buf = Buffer.allocUnsafe(capacityBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append PTY output to the buffer.
|
||||
* Uses 'binary' (latin1) encoding to preserve raw byte values from node-pty.
|
||||
*/
|
||||
write(data: string): void {
|
||||
const src = Buffer.from(data, "binary");
|
||||
const len = src.length;
|
||||
if (len === 0) return;
|
||||
|
||||
if (len >= this.capacity) {
|
||||
// Incoming chunk larger than the whole buffer — keep only the tail
|
||||
src.copy(this.buf, 0, len - this.capacity);
|
||||
this.writePos = 0;
|
||||
this.stored = this.capacity;
|
||||
return;
|
||||
}
|
||||
|
||||
const end = this.writePos + len;
|
||||
if (end <= this.capacity) {
|
||||
src.copy(this.buf, this.writePos);
|
||||
} else {
|
||||
// Wrap around the ring boundary
|
||||
const tailLen = this.capacity - this.writePos;
|
||||
src.copy(this.buf, this.writePos, 0, tailLen);
|
||||
src.copy(this.buf, 0, tailLen);
|
||||
}
|
||||
this.writePos = end % this.capacity;
|
||||
this.stored = Math.min(this.stored + len, this.capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all buffered bytes in chronological order (oldest first).
|
||||
* The returned Buffer is a copy and safe to send over a WebSocket.
|
||||
*/
|
||||
read(): Buffer {
|
||||
if (this.stored === 0) return Buffer.alloc(0);
|
||||
|
||||
if (this.stored < this.capacity) {
|
||||
// Buffer hasn't wrapped yet — contiguous region starting at index 0
|
||||
return Buffer.from(this.buf.subarray(0, this.stored));
|
||||
}
|
||||
|
||||
// Buffer is full and has wrapped — oldest data starts at writePos
|
||||
const out = Buffer.allocUnsafe(this.capacity);
|
||||
const tailLen = this.capacity - this.writePos;
|
||||
this.buf.copy(out, 0, this.writePos); // tail (oldest)
|
||||
this.buf.copy(out, tailLen, 0, this.writePos); // head (newest)
|
||||
return out;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user