mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 00:49:41 +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.
|
// Fire-and-forget resize to nudge Ink via SIGWINCH on real size changes.
|
||||||
// Tailed buffers accumulate stale CUP-positioned Ink frames that overlap
|
// Previously we also sent Ctrl+L (\x0c) here to force a full Ink redraw,
|
||||||
// in the viewport (e.g. duplicate "bypass permissions" bars). Ctrl+L
|
// but Claude Code 2.x treats Ctrl+L as a two-step "clear conversation"
|
||||||
// triggers a full Ink redraw which overwrites all stale frame content.
|
// command — if a page refresh or SSE reconnect ran selectSession twice
|
||||||
// sendResize may be a no-op if dimensions match, so Ctrl+L is essential.
|
// within Claude's confirmation window, the second \x0c silently wiped the
|
||||||
this.sendResize(sessionId).then(() => {
|
// conversation. Stale Ink frames in the tailed buffer are a cosmetic
|
||||||
if (selectGen !== this._selectGeneration) return;
|
// annoyance that disappear on the user's next keypress; data loss is not
|
||||||
fetch(`/api/sessions/${sessionId}/input`, {
|
// acceptable. Do NOT re-introduce Ctrl+L here.
|
||||||
method: 'POST',
|
this.sendResize(sessionId);
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ input: '\x0c' })
|
|
||||||
}).catch(() => {});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Defer secondary panel updates so they don't block the main thread
|
// Defer secondary panel updates so they don't block the main thread
|
||||||
// after terminal content is already visible.
|
// after terminal content is already visible.
|
||||||
|
|||||||
@@ -1704,7 +1704,8 @@ Object.assign(CodemanApp.prototype, {
|
|||||||
/**
|
/**
|
||||||
* Restore terminal size to match web UI dimensions.
|
* Restore terminal size to match web UI dimensions.
|
||||||
* Use this after mobile screen attachment has squeezed the terminal.
|
* 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() {
|
async restoreTerminalSize() {
|
||||||
if (!this.activeSessionId) {
|
if (!this.activeSessionId) {
|
||||||
@@ -1719,16 +1720,10 @@ Object.assign(CodemanApp.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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);
|
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');
|
this.showToast(`Terminal restored to ${dims.cols}x${dims.rows}`, 'success');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to restore terminal size:', 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
|
// Legacy hook for newly-created sessions; kept as a no-op so the SSE
|
||||||
sendPendingCtrlL(sessionId) {
|
// idle/working handlers can still call it without conditional guards.
|
||||||
if (!this.pendingCtrlL || !this.pendingCtrlL.has(sessionId)) {
|
//
|
||||||
return;
|
// Originally this sent Ctrl+L (\x0c) when a flagged session first reached
|
||||||
}
|
// idle/working to scrub mux-init junk from the screen. Two problems:
|
||||||
this.pendingCtrlL.delete(sessionId);
|
// 1. `pendingCtrlL` was never actually populated anywhere (dead path).
|
||||||
|
// 2. Claude Code 2.x interprets Ctrl+L as a two-step "clear conversation"
|
||||||
// Only send if this is the active session
|
// command — sending it from background flows risked nuking the user's
|
||||||
if (sessionId !== this.activeSessionId) {
|
// conversation if it coincided with another Ctrl+L (e.g. from
|
||||||
return;
|
// 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.
|
||||||
// Send resize + Ctrl+L to fix the display (with minimum dimension enforcement)
|
sendPendingCtrlL(_sessionId) {
|
||||||
this.sendResize(sessionId).then(() => {
|
// intentionally empty
|
||||||
fetch(`/api/sessions/${sessionId}/input`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ input: '\x0c' }),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async copyTerminal() {
|
async copyTerminal() {
|
||||||
|
|||||||
Reference in New Issue
Block a user