fix(mobile): route Claude terminal gestures

This commit is contained in:
lior
2026-08-10 13:21:15 +03:00
parent 6831d79127
commit 92ae46246c
3 changed files with 234 additions and 3 deletions
+32 -1
View File
@@ -664,6 +664,14 @@ Object.assign(CodemanApp.prototype, {
isTouching = true;
didScroll = false;
tapStartedWithTerminalFocus = this._isMobileTerminalInputFocused();
const touchStartIntent = this._classifyMobileTerminalTap(touchLastX, touchLastY);
if (touchStartIntent !== 'input') {
// Cancel xterm/browser focus before the compatibility click can
// open the OS keyboard. Content taps are re-emitted as SGR on
// touchend; history taps deliberately remain inert.
ev.preventDefault();
this._blurMobileTerminalInput();
}
lastTime = 0;
if (scrollFrame) {
cancelAnimationFrame(scrollFrame);
@@ -671,7 +679,7 @@ Object.assign(CodemanApp.prototype, {
}
}
},
{ passive: true }
{ passive: false }
);
container.addEventListener(
@@ -690,6 +698,7 @@ Object.assign(CodemanApp.prototype, {
// fling, so a jittery tap would both position the cursor AND scroll.
if (!didScroll) return;
ev.preventDefault();
touchLastX = ev.touches[0].clientX;
const delta = touchLastY - touchY; // positive = scroll down
pixelAccum += delta;
velocity = delta * 1.2;
@@ -3368,11 +3377,13 @@ Object.assign(CodemanApp.prototype, {
const rows = Math.max(1, this.terminal.rows || 1);
const lines = [];
const wrappedRows = [];
let hasVisibleContent = false;
for (let row = 0; row < rows; row++) {
const line = buffer.getLine(buffer.viewportY + row);
const text = line?.translateToString?.(true) || '';
lines.push(text);
wrappedRows.push(Boolean(line?.isWrapped));
if (text.trim()) hasVisibleContent = true;
}
if (!hasVisibleContent) return 'input';
@@ -3403,6 +3414,17 @@ Object.assign(CodemanApp.prototype, {
}
const tappedRow = pos.row - 1;
let logicalLineStart = tappedRow;
while (logicalLineStart > 0 && wrappedRows[logicalLineStart]) logicalLineStart--;
let logicalLineEnd = tappedRow;
while (logicalLineEnd + 1 < rows && wrappedRows[logicalLineEnd + 1]) logicalLineEnd++;
const tappedLine = lines.slice(logicalLineStart, logicalLineEnd + 1).join('');
if (
mode === 'claude' &&
/^\s*[•·]\s*Working\b.*(?:background|esc to interrupt)/i.test(tappedLine)
) {
return 'content';
}
if (menuSelectionVisible) return 'content';
if (promptRow >= 0) {
const inputEnd = cursorRow >= promptRow ? cursorRow : promptRow;
@@ -3652,6 +3674,15 @@ Object.assign(CodemanApp.prototype, {
return true;
},
// Claude keeps most transcript history inside its own TUI rather than xterm
// scrollback. On verified versions, route a touch drag through the same SGR
// wheel path as desktop. Codex keeps the existing local touch behavior.
_shouldForwardTouchScrollToApp() {
const session = this.sessions?.get(this.activeSessionId);
if (session?.mode !== 'claude') return false;
return this._shouldForwardWheelToApp({ shiftKey: false });
},
// Encode wheel ticks as SGR reports (button 64 = up, 65 = down) at the pointer
// cell. Reports are coalesced into one fire-and-forget write per ~40ms: a
// trackpad emits dozens of wheel events per second and each send becomes a