refactor: Extract terminal rendering logic to useTerminalRenderer composable
- Create useTerminalRenderer.ts with all xterm.js logic - Support custom theme, fontSize, fontFamily options - Add handleReplay() for proper visibility handling - Add getBufferContent() for copying terminal content - Refactor FloatingTerminal.vue to use composable - Refactor TerminalPage.vue to use composable - Server: Add request-replay message type for on-demand replay - Server: Remove auto-replay on connect (client requests when ready) - Fix xterm.js rendering issues with hidden containers (v-show)
This commit is contained in:
@@ -155,21 +155,19 @@ export function startTerminalServer() {
|
||||
session.clients.add(ws)
|
||||
wsToSession.set(ws, sessionId)
|
||||
|
||||
// Send connection info
|
||||
// Send connection info (include buffer size so client knows if replay is needed)
|
||||
ws.send(JSON.stringify({
|
||||
type: 'connected',
|
||||
sessionId: session.id,
|
||||
isNew: session.outputBuffer.length === 0
|
||||
isNew: session.outputBuffer.length === 0,
|
||||
hasHistory: session.outputBuffer.length > 0,
|
||||
bufferSize: session.outputBuffer.length
|
||||
}))
|
||||
|
||||
// Replay buffer if there's history
|
||||
if (session.outputBuffer.length > 0) {
|
||||
console.log(`[Terminal] Replaying ${session.outputBuffer.length} buffer entries`)
|
||||
ws.send(JSON.stringify({
|
||||
type: 'replay',
|
||||
data: session.outputBuffer.join('')
|
||||
}))
|
||||
}
|
||||
// DON'T auto-replay here!
|
||||
// Client will request replay when terminal is visible and ready.
|
||||
// This fixes xterm.js rendering issues with hidden containers.
|
||||
console.log(`[Terminal] Client connected, buffer has ${session.outputBuffer.length} chunks (client will request replay)`)
|
||||
|
||||
console.log(`[Terminal] Client joined session ${sessionId} (${session.clients.size} clients)`)
|
||||
} catch (e: any) {
|
||||
@@ -191,6 +189,33 @@ export function startTerminalServer() {
|
||||
} else if (msg.type === 'resize' && msg.cols && msg.rows) {
|
||||
session.pty.resize(msg.cols, msg.rows)
|
||||
console.log(`[Terminal] Session ${sessionId} resized to ${msg.cols}x${msg.rows}`)
|
||||
} else if (msg.type === 'request-replay') {
|
||||
// Client requests fresh replay (used when terminal becomes visible)
|
||||
console.log(`[Terminal] Replay requested, buffer has ${session.outputBuffer.length} chunks`)
|
||||
|
||||
if (session.outputBuffer.length > 0) {
|
||||
// If tailOnly specified, only send last N chunks (enough for a few screens)
|
||||
const tailOnly = msg.tailOnly === true
|
||||
const tailChunks = msg.chunks || 500 // Default ~500 chunks for tail
|
||||
|
||||
let data: string
|
||||
if (tailOnly && session.outputBuffer.length > tailChunks) {
|
||||
// Send only the tail - more efficient for large buffers
|
||||
data = session.outputBuffer.slice(-tailChunks).join('')
|
||||
console.log(`[Terminal] Replaying tail (${tailChunks}/${session.outputBuffer.length} chunks), ${data.length} bytes`)
|
||||
} else {
|
||||
data = session.outputBuffer.join('')
|
||||
console.log(`[Terminal] Replaying full buffer (${session.outputBuffer.length} chunks), ${data.length} bytes`)
|
||||
}
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
type: 'replay',
|
||||
data,
|
||||
isTail: tailOnly && session.outputBuffer.length > tailChunks
|
||||
}))
|
||||
} else {
|
||||
console.log('[Terminal] No buffer to replay')
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error('[Terminal] Error:', e)
|
||||
|
||||
Reference in New Issue
Block a user