mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
fix(web): stop auto-sending Ctrl+L from session selection paths (#99)
Claude Code 2.x treats Ctrl+L (\x0c) as a two-step "clear conversation" command (first press shows the confirmation prompt, second press clears). The frontend previously fired \x0c from three places to force Ink to redraw stale CUP-positioned frames in the tailed buffer; if a page refresh or SSE reconnect ran the same path twice within Claude's confirmation window the second \x0c silently nuked the user's conversation. Removed the \x0c sends from: - selectSession() — main offender, runs on every tab switch & page reload - restoreTerminalSize() — manual "restore size" button - sendPendingCtrlL() — dead code path (pendingCtrlL was never populated) Trade-off: occasional stale Ink frames immediately after refresh; the user's first keypress causes Ink to redraw and the artifact vanishes. Losing the conversation silently is far worse than a brief cosmetic glitch. Co-authored-by: Teigen <teigen@TeigendeMac-mini.local>
This commit is contained in:
+9
-13
@@ -2705,19 +2705,15 @@ class CodemanApp {
|
||||
});
|
||||
}
|
||||
|
||||
// Fire-and-forget resize + Ctrl+L to force Ink redraw.
|
||||
// Tailed buffers accumulate stale CUP-positioned Ink frames that overlap
|
||||
// in the viewport (e.g. duplicate "bypass permissions" bars). Ctrl+L
|
||||
// triggers a full Ink redraw which overwrites all stale frame content.
|
||||
// sendResize may be a no-op if dimensions match, so Ctrl+L is essential.
|
||||
this.sendResize(sessionId).then(() => {
|
||||
if (selectGen !== this._selectGeneration) return;
|
||||
fetch(`/api/sessions/${sessionId}/input`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ input: '\x0c' })
|
||||
}).catch(() => {});
|
||||
});
|
||||
// Fire-and-forget resize to nudge Ink via SIGWINCH on real size changes.
|
||||
// Previously we also sent Ctrl+L (\x0c) here to force a full Ink redraw,
|
||||
// but Claude Code 2.x treats Ctrl+L as a two-step "clear conversation"
|
||||
// command — if a page refresh or SSE reconnect ran selectSession twice
|
||||
// within Claude's confirmation window, the second \x0c silently wiped the
|
||||
// conversation. Stale Ink frames in the tailed buffer are a cosmetic
|
||||
// annoyance that disappear on the user's next keypress; data loss is not
|
||||
// acceptable. Do NOT re-introduce Ctrl+L here.
|
||||
this.sendResize(sessionId);
|
||||
|
||||
// Defer secondary panel updates so they don't block the main thread
|
||||
// after terminal content is already visible.
|
||||
|
||||
@@ -1704,7 +1704,8 @@ Object.assign(CodemanApp.prototype, {
|
||||
/**
|
||||
* Restore terminal size to match web UI dimensions.
|
||||
* Use this after mobile screen attachment has squeezed the terminal.
|
||||
* Sends resize to PTY and Ctrl+L to trigger Claude to redraw.
|
||||
* Sends only resize — SIGWINCH triggers Ink redraw on real dimension changes.
|
||||
* Ctrl+L is NOT sent here (Claude Code 2.x treats it as "clear conversation").
|
||||
*/
|
||||
async restoreTerminalSize() {
|
||||
if (!this.activeSessionId) {
|
||||
@@ -1719,16 +1720,10 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
|
||||
try {
|
||||
// Send resize to restore proper dimensions (with minimum enforcement)
|
||||
// Send resize to restore proper dimensions (with minimum enforcement).
|
||||
// The PTY's SIGWINCH on real dim change is enough for Ink to redraw.
|
||||
await this.sendResize(this.activeSessionId);
|
||||
|
||||
// Send Ctrl+L to trigger Claude to redraw at new size
|
||||
await fetch(`/api/sessions/${this.activeSessionId}/input`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ input: '\x0c' }),
|
||||
});
|
||||
|
||||
this.showToast(`Terminal restored to ${dims.cols}x${dims.rows}`, 'success');
|
||||
} catch (err) {
|
||||
console.error('Failed to restore terminal size:', err);
|
||||
@@ -1736,26 +1731,20 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
},
|
||||
|
||||
// Send Ctrl+L to fix display for newly created sessions once Claude is running
|
||||
sendPendingCtrlL(sessionId) {
|
||||
if (!this.pendingCtrlL || !this.pendingCtrlL.has(sessionId)) {
|
||||
return;
|
||||
}
|
||||
this.pendingCtrlL.delete(sessionId);
|
||||
|
||||
// Only send if this is the active session
|
||||
if (sessionId !== this.activeSessionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send resize + Ctrl+L to fix the display (with minimum dimension enforcement)
|
||||
this.sendResize(sessionId).then(() => {
|
||||
fetch(`/api/sessions/${sessionId}/input`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ input: '\x0c' }),
|
||||
});
|
||||
});
|
||||
// Legacy hook for newly-created sessions; kept as a no-op so the SSE
|
||||
// idle/working handlers can still call it without conditional guards.
|
||||
//
|
||||
// Originally this sent Ctrl+L (\x0c) when a flagged session first reached
|
||||
// idle/working to scrub mux-init junk from the screen. Two problems:
|
||||
// 1. `pendingCtrlL` was never actually populated anywhere (dead path).
|
||||
// 2. Claude Code 2.x interprets Ctrl+L as a two-step "clear conversation"
|
||||
// command — sending it from background flows risked nuking the user's
|
||||
// conversation if it coincided with another Ctrl+L (e.g. from
|
||||
// selectSession on page reload).
|
||||
// If a per-session display-fix is ever needed again, do it via sendResize
|
||||
// or an Ink-safe control sequence, NOT \x0c.
|
||||
sendPendingCtrlL(_sessionId) {
|
||||
// intentionally empty
|
||||
},
|
||||
|
||||
async copyTerminal() {
|
||||
|
||||
Reference in New Issue
Block a user