const fs = require('fs'); let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); // Convert Touch Events to Pointer Events for manual scroll tracking. // Since touchmove vanishes but pointermove remains active (with target changing from SPAN to DIV, etc) // Let's modify pointer_move to handle scrolling let pointerMoveCode = ` onTouchKeyboardPointerMove = (event: PointerEvent) => { // 保持之前的检查,跳除非 touch 事件等 if (event.pointerType !== "touch" || touchGatePointerId !== event.pointerId) { return; } const dx = event.clientX - touchGateStartX; const dy = event.clientY - touchGateStartY; const absDx = Math.abs(dx); const absDy = Math.abs(dy); if (absDx > TOUCH_KEYBOARD_TAP_MAX_MOVE_PX || absDy > TOUCH_KEYBOARD_TAP_MAX_MOVE_PX) { touchGateMoved = true; } if (absDy > absDx && absDy > TOUCH_KEYBOARD_TAP_MAX_MOVE_PX) { touchGateScrollLike = true; } // 我们不再拦截 touchmove,改用 pointermove 来计算自己的滚动量 const hasSelection = hasActiveNativeSelectionInTerminal(); if (!hasSelection) { const now = performance.now(); const dt = now - touchScrollLastTime; const currentY = event.clientY; const stepDy = currentY - touchScrollLastY; // 屏蔽初次的巨大跳跃(手指刚按下时) if (dt > 0 && Math.abs(stepDy) < 200 && touchScrollLastTime > 0) { if (viewportScroller && stepDy !== 0) { viewportScroller.scrollTop -= stepDy; } const v = (-stepDy / dt) * 16; if (touchScrollVelocity * v > 0) { touchScrollVelocity = touchScrollVelocity * 0.5 + v * 0.5; } else { touchScrollVelocity = v; } const touchMaxSpeed = 120; if (touchScrollVelocity > touchMaxSpeed) touchScrollVelocity = touchMaxSpeed; else if (touchScrollVelocity < -touchMaxSpeed) touchScrollVelocity = -touchMaxSpeed; } touchScrollLastY = currentY; touchScrollLastTime = now; } // 由于使用了 pointer,不要无脑阻止原生行为,但我们要阻止 xterm 被选中 // event.stopImmediatePropagation(); // 我们前面通过 touch-action: none 已经避免了系统层面缩放 };`; let pointerDownCode = ` onTouchKeyboardPointerDown = (event: PointerEvent) => { console.log("[Scroll Deep] 🟡 POINTER DOWN: ", { pointerId: event.pointerId, type: event.pointerType, target: (event.target as Node)?.nodeName }); if (event.pointerType !== "touch") { return; } if (!helperTextarea) { return; } touchGatePointerId = event.pointerId; touchGateStartX = event.clientX; touchGateStartY = event.clientY; touchGateMoved = false; touchGateScrollLike = false; touchGateStartInBand = isTouchInCursorActivationBand(event.clientY); touchGateHadSelectionAtStart = hasActiveNativeSelectionInTerminal(); // == 初始化滚动参数 == clearTouchScrollMomentum(); touchScrollLastY = event.clientY; touchScrollLastTime = performance.now(); touchScrollVelocity = 0; if (DEBUG_TOUCH_FOCUS) { console.log("[TouchFocus] pointerdown", { inBand: touchGateStartInBand, selectionStart: touchGateHadSelectionAtStart, }); } event.stopImmediatePropagation(); if (event.cancelable) { event.preventDefault(); } helperTextarea.readOnly = true; if (sessionStore.state !== "connected") { return; } };`; let pointerUpCode = ` onTouchKeyboardPointerUp = (event: PointerEvent) => { console.log("[Scroll Deep] 🟠 POINTER UP: ", { pointerId: event.pointerId, type: event.pointerType }); if (event.pointerType !== "touch" || touchGatePointerId !== event.pointerId) { return; } // 释放时触发滚行动量 const threshold = 0.2; if (Math.abs(touchScrollVelocity) > threshold && touchGateScrollLike && !hasActiveNativeSelectionInTerminal()) { touchScrollVelocity *= 1.35; console.log(\`[Scroll Deep] 🚀 Pointer 释放,触发JS惯性,速度=\${touchScrollVelocity.toFixed(2)}\`); runTouchScrollMomentum(); } else { console.log(\`[Scroll Deep] 🛑 Pointer 静止释放或非滚动释放\`); } `; // Now apply pointer changes code = code.replace(/onTouchKeyboardPointerDown = \(event: PointerEvent\) => \{[\s\S]*?if \(sessionStore\.state \!\=\= "connected"\) \{\n\s*return;\n\s*\}\n\s*\};/m, pointerDownCode); code = code.replace(/onTouchKeyboardPointerMove = \(event: PointerEvent\) => \{[\s\S]*?event\.stopImmediatePropagation\(\);\n\s*\};/m, pointerMoveCode); code = code.replace(/onTouchKeyboardPointerUp = \(event: PointerEvent\) => \{[\s\S]*?if \(event\.pointerType \!\=\= "touch" \|\| touchGatePointerId \!\=\= event.pointerId\) \{\n\s*return;\n\s*\}/m, pointerUpCode); fs.writeFileSync('src/components/TerminalPanel.vue', code);