mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-27 15:17:38 +00:00
feat: implement fitHorizontally mode and buffer trimming
- Add fitHorizontally mode to vibe-terminal-buffer component - Scale font size to fit entire terminal width when enabled - Trim blank lines from bottom of buffer to reduce data transfer - Always show content from top down (not centered on cursor) - Match behavior of terminal.ts fitTerminal implementation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
aa0658acc7
commit
4a9ee48427
6 changed files with 80 additions and 320 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import { LitElement, html } from 'lit';
|
import { LitElement, html } from 'lit';
|
||||||
import { customElement, property, state } from 'lit/decorators.js';
|
import { customElement, property, state } from 'lit/decorators.js';
|
||||||
|
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
||||||
import { TerminalRenderer, type BufferCell } from '../utils/terminal-renderer.js';
|
import { TerminalRenderer, type BufferCell } from '../utils/terminal-renderer.js';
|
||||||
|
|
||||||
interface BufferSnapshot {
|
interface BufferSnapshot {
|
||||||
|
|
@ -28,6 +29,7 @@ export class VibeTerminalBuffer extends LitElement {
|
||||||
@state() private loading = false;
|
@state() private loading = false;
|
||||||
@state() private actualRows = 0;
|
@state() private actualRows = 0;
|
||||||
@state() private displayedFontSize = 14;
|
@state() private displayedFontSize = 14;
|
||||||
|
@state() private containerCols = 80; // Calculated columns that fit
|
||||||
|
|
||||||
private container: HTMLElement | null = null;
|
private container: HTMLElement | null = null;
|
||||||
private pollTimer: NodeJS.Timeout | null = null;
|
private pollTimer: NodeJS.Timeout | null = null;
|
||||||
|
|
@ -86,29 +88,38 @@ export class VibeTerminalBuffer extends LitElement {
|
||||||
private calculateDimensions() {
|
private calculateDimensions() {
|
||||||
if (!this.container) return;
|
if (!this.container) return;
|
||||||
|
|
||||||
|
const containerWidth = this.container.clientWidth;
|
||||||
const containerHeight = this.container.clientHeight;
|
const containerHeight = this.container.clientHeight;
|
||||||
const lineHeight = this.fontSize * 1.2;
|
|
||||||
const newActualRows = Math.floor(containerHeight / lineHeight);
|
|
||||||
|
|
||||||
if (this.fitHorizontally && this.buffer) {
|
if (this.fitHorizontally && this.buffer) {
|
||||||
// Calculate font size to fit terminal width
|
// Horizontal fitting: calculate fontSize to fit buffer.cols characters in container width
|
||||||
const containerWidth = this.container.clientWidth;
|
const targetCharWidth = containerWidth / this.buffer.cols;
|
||||||
const charWidth = this.fontSize * 0.6; // Approximate char width
|
|
||||||
const requiredWidth = this.buffer.cols * charWidth;
|
|
||||||
|
|
||||||
if (requiredWidth > containerWidth) {
|
// Estimate font size needed (assuming monospace font with ~0.6 char/font ratio)
|
||||||
const scale = containerWidth / requiredWidth;
|
const calculatedFontSize = targetCharWidth / 0.6;
|
||||||
this.displayedFontSize = Math.floor(this.fontSize * scale);
|
this.displayedFontSize = Math.max(4, Math.min(32, Math.floor(calculatedFontSize)));
|
||||||
} else {
|
|
||||||
this.displayedFontSize = this.fontSize;
|
// Calculate actual rows with new font size
|
||||||
|
const lineHeight = this.displayedFontSize * 1.2;
|
||||||
|
const newActualRows = Math.max(1, Math.floor(containerHeight / lineHeight));
|
||||||
|
|
||||||
|
if (newActualRows !== this.actualRows) {
|
||||||
|
this.actualRows = newActualRows;
|
||||||
|
this.fetchBuffer();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Normal mode: use original font size and calculate cols that fit
|
||||||
this.displayedFontSize = this.fontSize;
|
this.displayedFontSize = this.fontSize;
|
||||||
}
|
const lineHeight = this.fontSize * 1.2;
|
||||||
|
const charWidth = this.fontSize * 0.6;
|
||||||
|
|
||||||
if (newActualRows !== this.actualRows) {
|
const newActualRows = Math.max(1, Math.floor(containerHeight / lineHeight));
|
||||||
this.actualRows = newActualRows;
|
this.containerCols = Math.max(20, Math.floor(containerWidth / charWidth));
|
||||||
this.fetchBuffer();
|
|
||||||
|
if (newActualRows !== this.actualRows) {
|
||||||
|
this.actualRows = newActualRows;
|
||||||
|
this.fetchBuffer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,12 +159,9 @@ export class VibeTerminalBuffer extends LitElement {
|
||||||
return; // No changes
|
return; // No changes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch buffer data - request at least one full terminal screen worth of lines
|
// Always fetch the entire buffer to show all content
|
||||||
// This ensures we get the complete visible terminal state
|
|
||||||
const linesToFetch = Math.max(this.actualRows, stats.rows);
|
|
||||||
const lines = Math.min(linesToFetch, stats.totalRows);
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`/api/sessions/${this.sessionId}/buffer?lines=${lines}&format=json`
|
`/api/sessions/${this.sessionId}/buffer?viewportY=0&lines=${stats.totalRows}&format=json`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
|
@ -164,14 +172,6 @@ export class VibeTerminalBuffer extends LitElement {
|
||||||
this.lastModified = stats.lastModified;
|
this.lastModified = stats.lastModified;
|
||||||
this.error = null;
|
this.error = null;
|
||||||
|
|
||||||
// Debug logging
|
|
||||||
console.log(`Buffer loaded for ${this.sessionId}:`, {
|
|
||||||
cols: this.buffer.cols,
|
|
||||||
rows: this.buffer.rows,
|
|
||||||
cellCount: this.buffer.cells.length,
|
|
||||||
firstLineSample: this.buffer.cells[0]?.slice(0, 10),
|
|
||||||
});
|
|
||||||
|
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching buffer:', error);
|
console.error('Error fetching buffer:', error);
|
||||||
|
|
@ -210,24 +210,37 @@ export class VibeTerminalBuffer extends LitElement {
|
||||||
|
|
||||||
const lineHeight = this.displayedFontSize * 1.2;
|
const lineHeight = this.displayedFontSize * 1.2;
|
||||||
|
|
||||||
// Render lines - if we have more lines than can fit, show the bottom portion
|
// In fitHorizontally mode, we show all content scaled to fit
|
||||||
const startIndex = Math.max(0, this.buffer.cells.length - this.actualRows);
|
// Otherwise, we show from the top and let it overflow
|
||||||
const visibleCells = this.buffer.cells.slice(startIndex);
|
if (this.fitHorizontally) {
|
||||||
|
// Render all lines - the buffer is already trimmed of blank lines from the bottom
|
||||||
|
return this.buffer.cells.map((row, index) => {
|
||||||
|
const isCursorLine = index === this.buffer.cursorY;
|
||||||
|
const cursorCol = isCursorLine ? this.buffer.cursorX : -1;
|
||||||
|
const lineContent = TerminalRenderer.renderLineFromCells(row, cursorCol);
|
||||||
|
|
||||||
return visibleCells.map((row, index) => {
|
return html`
|
||||||
const actualIndex = startIndex + index;
|
<div class="terminal-line" style="height: ${lineHeight}px; line-height: ${lineHeight}px;">
|
||||||
const isCursorLine = actualIndex === this.buffer.cursorY;
|
${unsafeHTML(lineContent)}
|
||||||
const cursorCol = isCursorLine ? this.buffer.cursorX : -1;
|
</div>
|
||||||
const lineContent = TerminalRenderer.renderLineFromCells(row, cursorCol);
|
`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Show only what fits in the viewport
|
||||||
|
const visibleCells = this.buffer.cells.slice(0, this.actualRows);
|
||||||
|
|
||||||
return html`
|
return visibleCells.map((row, index) => {
|
||||||
<div
|
const isCursorLine = index === this.buffer.cursorY;
|
||||||
class="terminal-line"
|
const cursorCol = isCursorLine ? this.buffer.cursorX : -1;
|
||||||
style="height: ${lineHeight}px; line-height: ${lineHeight}px;"
|
const lineContent = TerminalRenderer.renderLineFromCells(row, cursorCol);
|
||||||
.innerHTML=${lineContent}
|
|
||||||
></div>
|
return html`
|
||||||
`;
|
<div class="terminal-line" style="height: ${lineHeight}px; line-height: ${lineHeight}px;">
|
||||||
});
|
${unsafeHTML(lineContent)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -214,6 +214,9 @@ export class TerminalRenderer {
|
||||||
const b = cell.fg & 0xff;
|
const b = cell.fg & 0xff;
|
||||||
style += `color: rgb(${r}, ${g}, ${b});`;
|
style += `color: rgb(${r}, ${g}, ${b});`;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Default foreground color if not specified
|
||||||
|
style += `color: #d4d4d4;`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get background color
|
// Get background color
|
||||||
|
|
|
||||||
|
|
@ -284,13 +284,31 @@ export class TerminalManager {
|
||||||
cells.push(rowCells);
|
cells.push(rowCells);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trim blank lines from the bottom
|
||||||
|
let lastNonBlankRow = cells.length - 1;
|
||||||
|
while (lastNonBlankRow >= 0) {
|
||||||
|
const row = cells[lastNonBlankRow];
|
||||||
|
const hasContent = row.some(
|
||||||
|
(cell) =>
|
||||||
|
cell.char !== ' ' ||
|
||||||
|
cell.fg !== undefined ||
|
||||||
|
cell.bg !== undefined ||
|
||||||
|
cell.attributes !== undefined
|
||||||
|
);
|
||||||
|
if (hasContent) break;
|
||||||
|
lastNonBlankRow--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep at least one row
|
||||||
|
const trimmedCells = cells.slice(0, Math.max(1, lastNonBlankRow + 1));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cols: terminal.cols,
|
cols: terminal.cols,
|
||||||
rows: actualLines,
|
rows: trimmedCells.length,
|
||||||
viewportY: actualViewportY,
|
viewportY: actualViewportY,
|
||||||
cursorX,
|
cursorX,
|
||||||
cursorY,
|
cursorY,
|
||||||
cells,
|
cells: trimmedCells,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test script for the new buffer component
|
|
||||||
*/
|
|
||||||
|
|
||||||
const http = require('http');
|
|
||||||
|
|
||||||
const BASE_URL = 'http://localhost:3000';
|
|
||||||
|
|
||||||
function httpGet(url) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
http.get(url, (res) => {
|
|
||||||
let data = '';
|
|
||||||
res.on('data', (chunk) => {
|
|
||||||
data += chunk;
|
|
||||||
});
|
|
||||||
res.on('end', () => {
|
|
||||||
if (res.statusCode === 200) {
|
|
||||||
try {
|
|
||||||
resolve(JSON.parse(data));
|
|
||||||
} catch (e) {
|
|
||||||
resolve(data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).on('error', reject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function testBufferEndpoints(sessionId) {
|
|
||||||
console.log(`\n=== Testing buffer endpoints for session ${sessionId} ===`);
|
|
||||||
|
|
||||||
// Test stats endpoint
|
|
||||||
console.log('\n1. Testing /buffer/stats endpoint:');
|
|
||||||
try {
|
|
||||||
const stats = await httpGet(`${BASE_URL}/api/sessions/${sessionId}/buffer/stats`);
|
|
||||||
console.log(' ✅ Stats:', JSON.stringify(stats, null, 2));
|
|
||||||
} catch (error) {
|
|
||||||
console.error(' ❌ Error:', error.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test JSON buffer endpoint
|
|
||||||
console.log('\n2. Testing /buffer endpoint with JSON format:');
|
|
||||||
try {
|
|
||||||
const buffer = await httpGet(`${BASE_URL}/api/sessions/${sessionId}/buffer?lines=10&format=json`);
|
|
||||||
console.log(' ✅ Buffer dimensions:', `${buffer.cols}x${buffer.rows}`);
|
|
||||||
console.log(' ✅ Viewport Y:', buffer.viewportY);
|
|
||||||
console.log(' ✅ Cursor position:', `(${buffer.cursorX}, ${buffer.cursorY})`);
|
|
||||||
console.log(' ✅ Cell count:', buffer.cells.length);
|
|
||||||
|
|
||||||
// Show sample of first line
|
|
||||||
if (buffer.cells.length > 0) {
|
|
||||||
const firstLine = buffer.cells[0];
|
|
||||||
const text = firstLine.map(cell => cell.char).join('');
|
|
||||||
console.log(' ✅ First line preview:', text.substring(0, 40) + '...');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(' ❌ Error:', error.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bottom-up lines (without viewportY)
|
|
||||||
console.log('\n3. Testing bottom-up lines (no viewportY):');
|
|
||||||
try {
|
|
||||||
const buffer = await httpGet(`${BASE_URL}/api/sessions/${sessionId}/buffer?lines=5&format=json`);
|
|
||||||
console.log(' ✅ Got', buffer.rows, 'lines from bottom');
|
|
||||||
console.log(' ✅ ViewportY:', buffer.viewportY);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(' ❌ Error:', error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
console.log('Buffer Component Test Script');
|
|
||||||
console.log('============================');
|
|
||||||
|
|
||||||
// First get list of sessions
|
|
||||||
console.log('\nFetching sessions...');
|
|
||||||
try {
|
|
||||||
const sessions = await httpGet(`${BASE_URL}/api/sessions`);
|
|
||||||
console.log(`Found ${sessions.length} sessions`);
|
|
||||||
|
|
||||||
if (sessions.length === 0) {
|
|
||||||
console.log('\nNo sessions found. Please create a session first.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test first running session
|
|
||||||
const runningSessions = sessions.filter(s => s.status === 'running');
|
|
||||||
if (runningSessions.length === 0) {
|
|
||||||
console.log('\nNo running sessions found. Testing with first session...');
|
|
||||||
await testBufferEndpoints(sessions[0].id);
|
|
||||||
} else {
|
|
||||||
console.log(`\nTesting with running session: ${runningSessions[0].command}`);
|
|
||||||
await testBufferEndpoints(runningSessions[0].id);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error:', error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (require.main === module) {
|
|
||||||
main().catch(console.error);
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
// Test script for the new buffer endpoint
|
|
||||||
|
|
||||||
const BASE_URL = 'http://localhost:3000';
|
|
||||||
|
|
||||||
async function testBufferEndpoint() {
|
|
||||||
try {
|
|
||||||
// First, get list of sessions
|
|
||||||
const sessionsRes = await fetch(`${BASE_URL}/api/sessions`);
|
|
||||||
const sessions = await sessionsRes.json();
|
|
||||||
|
|
||||||
if (sessions.length === 0) {
|
|
||||||
console.log('No sessions available. Create a session first.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sessionId = sessions[0].id;
|
|
||||||
console.log(`Testing with session: ${sessionId}`);
|
|
||||||
|
|
||||||
// Test buffer endpoint
|
|
||||||
const bufferRes = await fetch(`${BASE_URL}/api/sessions/${sessionId}/buffer?viewportY=0&lines=24`);
|
|
||||||
|
|
||||||
if (!bufferRes.ok) {
|
|
||||||
console.error('Buffer request failed:', bufferRes.status, await bufferRes.text());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const buffer = await bufferRes.arrayBuffer();
|
|
||||||
const bytes = new Uint8Array(buffer);
|
|
||||||
|
|
||||||
console.log(`Received ${bytes.length} bytes`);
|
|
||||||
|
|
||||||
// Parse header
|
|
||||||
if (bytes.length < 16) {
|
|
||||||
console.error('Buffer too small for header');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const magic = (bytes[1] << 8) | bytes[0];
|
|
||||||
const version = bytes[2];
|
|
||||||
const flags = bytes[3];
|
|
||||||
const cols = (bytes[5] << 8) | bytes[4];
|
|
||||||
const rows = (bytes[7] << 8) | bytes[6];
|
|
||||||
const viewportY = (bytes[9] << 8) | bytes[8];
|
|
||||||
const cursorX = (bytes[11] << 8) | bytes[10];
|
|
||||||
const cursorY = (bytes[13] << 8) | bytes[12];
|
|
||||||
|
|
||||||
console.log('\nHeader:');
|
|
||||||
console.log(` Magic: 0x${magic.toString(16)} (${magic === 0x5654 ? 'Valid' : 'Invalid'})`);
|
|
||||||
console.log(` Version: ${version}`);
|
|
||||||
console.log(` Flags: ${flags}`);
|
|
||||||
console.log(` Terminal: ${cols}x${rows}`);
|
|
||||||
console.log(` ViewportY: ${viewportY}`);
|
|
||||||
console.log(` Cursor: (${cursorX}, ${cursorY})`);
|
|
||||||
|
|
||||||
// Sample first few cells
|
|
||||||
console.log('\nFirst few cells:');
|
|
||||||
let offset = 16;
|
|
||||||
for (let i = 0; i < Math.min(10, bytes.length - 16); i++) {
|
|
||||||
if (offset >= bytes.length) break;
|
|
||||||
|
|
||||||
const byte = bytes[offset];
|
|
||||||
if (byte === 0xFF) {
|
|
||||||
// RLE marker
|
|
||||||
const count = bytes[offset + 1];
|
|
||||||
console.log(` RLE: ${count} repeated cells`);
|
|
||||||
offset += 2;
|
|
||||||
} else if (byte === 0xFE) {
|
|
||||||
// Empty line marker
|
|
||||||
const count = bytes[offset + 1];
|
|
||||||
console.log(` Empty lines: ${count}`);
|
|
||||||
offset += 2;
|
|
||||||
} else if (byte & 0x80) {
|
|
||||||
// Extended cell
|
|
||||||
console.log(` Extended cell at offset ${offset}`);
|
|
||||||
offset += 4; // Skip for now
|
|
||||||
} else {
|
|
||||||
// Basic cell
|
|
||||||
const char = String.fromCharCode(byte);
|
|
||||||
const attrs = bytes[offset + 1];
|
|
||||||
const fg = bytes[offset + 2];
|
|
||||||
const bg = bytes[offset + 3];
|
|
||||||
console.log(` Cell: '${char}' fg=${fg} bg=${bg} attrs=${attrs}`);
|
|
||||||
offset += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Test failed:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testBufferEndpoint();
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
// Test the terminal manager directly
|
|
||||||
async function testTerminalManager() {
|
|
||||||
console.log('Testing Terminal Manager...\n');
|
|
||||||
|
|
||||||
const { TerminalManager } = require('./dist/terminal-manager.js');
|
|
||||||
const controlDir = path.join(process.env.HOME, '.vibetunnel/control');
|
|
||||||
|
|
||||||
const tm = new TerminalManager(controlDir);
|
|
||||||
|
|
||||||
// Get a specific session
|
|
||||||
const sessionId = process.argv[2] || '725f848c-c6d7-4bd4-8030-b83b20b1ee45';
|
|
||||||
|
|
||||||
console.log(`Testing session: ${sessionId}`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get terminal
|
|
||||||
const terminal = await tm.getTerminal(sessionId);
|
|
||||||
console.log('Terminal created successfully');
|
|
||||||
|
|
||||||
// Wait for content to load
|
|
||||||
console.log('Waiting for content to load...');
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
|
|
||||||
// Get buffer stats
|
|
||||||
const stats = await tm.getBufferStats(sessionId);
|
|
||||||
console.log('\nBuffer stats:', stats);
|
|
||||||
|
|
||||||
// Get buffer snapshot
|
|
||||||
const snapshot = await tm.getBufferSnapshot(sessionId, undefined, 10);
|
|
||||||
console.log('\nBuffer snapshot:');
|
|
||||||
console.log('- Dimensions:', `${snapshot.cols}x${snapshot.rows}`);
|
|
||||||
console.log('- ViewportY:', snapshot.viewportY);
|
|
||||||
console.log('- Cursor:', `(${snapshot.cursorX}, ${snapshot.cursorY})`);
|
|
||||||
|
|
||||||
// Check content
|
|
||||||
if (snapshot.cells.length > 0) {
|
|
||||||
console.log('\nFirst line content:');
|
|
||||||
const firstLine = snapshot.cells[0];
|
|
||||||
const text = firstLine.map(cell => cell.char).join('');
|
|
||||||
console.log(`"${text}"`);
|
|
||||||
|
|
||||||
// Check for non-space content
|
|
||||||
let hasContent = false;
|
|
||||||
for (const row of snapshot.cells) {
|
|
||||||
const rowText = row.map(cell => cell.char).join('').trim();
|
|
||||||
if (rowText.length > 0) {
|
|
||||||
hasContent = true;
|
|
||||||
console.log(`\nFound content: "${rowText}"`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasContent) {
|
|
||||||
console.log('\n⚠️ Warning: All lines appear to be empty!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close terminal
|
|
||||||
tm.closeTerminal(sessionId);
|
|
||||||
console.log('\nTerminal closed');
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testTerminalManager().catch(console.error);
|
|
||||||
Loading…
Reference in a new issue