diff --git a/pxterm/check_zindex.js b/pxterm/check_zindex.js new file mode 100644 index 0000000..6a0c696 --- /dev/null +++ b/pxterm/check_zindex.js @@ -0,0 +1,2 @@ +const fs = require('fs'); +console.log('Testing strategy...'); diff --git a/pxterm/clean_pointer.cjs b/pxterm/clean_pointer.cjs new file mode 100644 index 0000000..85cf9b9 --- /dev/null +++ b/pxterm/clean_pointer.cjs @@ -0,0 +1,13 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// remove multiline logs +code = code.replace(/console\.log\(\s*\[Scroll Deep\][ \w\W]*?\);/g, ''); + +// remove redundant empty else blocks +code = code.replace(/\s*\} else \{\s*\}/g, ''); + +// remove duplicated momentum logic +code = code.replace(/(\s*\/\/ 释放时触发滚行动量\s*if \(Math\.abs\(touchScrollVelocity\) > 0\.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal\(\)\) \{\s*touchScrollVelocity \*= 1\.35;\s*runTouchScrollMomentum\(\);\s*\})\s*\/\/ 释放时触发滚行动量\s*if \(Math\.abs\(touchScrollVelocity\) > 0\.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal\(\)\) \{\s*touchScrollVelocity \*= 1\.35;\s*runTouchScrollMomentum\(\);\s*\}/g, '$1'); + +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/clean_terminal.cjs b/pxterm/clean_terminal.cjs new file mode 100644 index 0000000..2491edd --- /dev/null +++ b/pxterm/clean_terminal.cjs @@ -0,0 +1,17 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// 1. Remove global event debug block +code = code.replace(/\/\/ \-\-\- 极致全局监听器,抓出是谁吃掉了 move \-\-\-[\s\S]*?\/\/ 用最顶级的 window capture 拦截,这样在任何人(包括 xterm 内部那些黑盒代码)前面执行\n\s*window\.addEventListener\(type, globalEventDebug, \{ capture: true, passive: false \}\);\n\s*\}\);\n/g, ''); + +// 2. Remove all console.log("[Scroll Deep]...") +code = code.replace(/\s*console\.log\(\s*`?\[Scroll Deep\].*?\);\n/g, '\n'); + +// 3. Remove useless commented out stopImmediatePropagation and preventDefault blocks left from debugging +const commentedOutRegex1 = /\s*\/\/ event\.stopImmediatePropagation\(\);/g; +code = code.replace(commentedOutRegex1, ''); + +const commentedOutRegex2 = /\s*\/\/ if \(event\.cancelable\) \{\n\s*\/\/ event\.preventDefault\(\); \/\/ 我们使用 JS 控制滚动,必须禁止系统原生滚动争抢\n\s*\/\/\}/g; +code = code.replace(commentedOutRegex2, ''); + +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/clean_vars.cjs b/pxterm/clean_vars.cjs new file mode 100644 index 0000000..9f0d416 --- /dev/null +++ b/pxterm/clean_vars.cjs @@ -0,0 +1,5 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); +code = code.replace(/let touchScrollLastY = 0;\n/g, ''); +code = code.replace(/let touchScrollLastTime = 0;\n/g, ''); +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/fix_bracket.js b/pxterm/fix_bracket.js new file mode 100644 index 0000000..4945226 --- /dev/null +++ b/pxterm/fix_bracket.js @@ -0,0 +1,21 @@ +import fs from 'fs'; +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// Find: +// if (Math.abs(touchScrollVelocity) > 0.2) { +// touchScrollVelocity *= 1.35; +// runTouchScrollMomentum(); +// }; +// And replace with: +// if (Math.abs(touchScrollVelocity) > 0.2) { +// touchScrollVelocity *= 1.35; +// runTouchScrollMomentum(); +// } +// }; + +code = code.replace(/if \(Math\.abs\(touchScrollVelocity\) > 0\.2\) \{\s*touchScrollVelocity \*= 1\.35;\s*runTouchScrollMomentum\(\);\s*\};/g, 'if (Math.abs(touchScrollVelocity) > 0.2) {\n touchScrollVelocity *= 1.35;\n runTouchScrollMomentum();\n }\n };'); + +// Also check pointerUp +code = code.replace(/if \(Math\.abs\(touchScrollVelocity\) > 0\.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal\(\)\) \{\s*touchScrollVelocity \*= 1\.35;\s*runTouchScrollMomentum\(\);\s*const context = \{/g, 'if (Math.abs(touchScrollVelocity) > 0.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal()) {\n touchScrollVelocity *= 1.35;\n runTouchScrollMomentum();\n }\n\n const context = {'); + +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/fix_bracket2.cjs b/pxterm/fix_bracket2.cjs new file mode 100644 index 0000000..acd66bc --- /dev/null +++ b/pxterm/fix_bracket2.cjs @@ -0,0 +1,8 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// The issue is an extra brace that was probably around clearTouchScrollMomentum (which I removed with regex `function clearTouchScrollMomentum[\s\S]*?\n\}\n`, but maybe it was `function clearTouchScrollMomentum() { ... } \n }` which left a flying `}`. + +code = code.replace(/wheelInertiaVelocity = 0;\n\}\n\}\n/g, 'wheelInertiaVelocity = 0;\n}\n\n'); + +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/fix_css.cjs b/pxterm/fix_css.cjs index 076fb4b..3cf8c5a 100644 --- a/pxterm/fix_css.cjs +++ b/pxterm/fix_css.cjs @@ -1,7 +1,8 @@ const fs = require('fs'); let code = fs.readFileSync('src/styles/main.css', 'utf8'); +code += ` -// If touch-action is missing from xterm-viewport we need to add it -code = code.replace(/\.terminal-wrapper \.xterm \.xterm-viewport \{/, '.terminal-wrapper .xterm .xterm-viewport {\n touch-action: none !important;'); - +/* Fix for native scrolling: .xterm-screen cannot be scrolled natively because it is not the scroll container. + To allow native scrolling, we would have to make .xterm-screen point-events: none, BUT we need selection on it. */ +`; fs.writeFileSync('src/styles/main.css', code); diff --git a/pxterm/fix_native.cjs b/pxterm/fix_native.cjs new file mode 100644 index 0000000..aae8a43 --- /dev/null +++ b/pxterm/fix_native.cjs @@ -0,0 +1,14 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// I need to ensure that pointermove/pointerdown is NOT swallowing the touch event in mobile safari. +// Let's remove stopImmediatePropagation in the new logic if it's there. +// If touchmove just calls event.stopImmediatePropagation(), then xterm doesn't see it, but DOES it stop the native scroll if xterm is absolute positioned over the viewport? + +// Wait, xterm's architecture: +// .xterm-screen is absolute positioned OVER .xterm-viewport. +// If user touches .xterm-screen (zIndex 1?), the touch is on .xterm-screen. +// .xterm-screen DOES NOT HAVE overflow-y: scroll. It's fixed height! +// Therefore, iOS panning gesture on .xterm-screen does NOT bubble to .xterm-viewport as a native scroll! + +console.log("Analyzing xterm architecture..."); diff --git a/pxterm/fix_passive.cjs b/pxterm/fix_passive.cjs new file mode 100644 index 0000000..4f6c456 --- /dev/null +++ b/pxterm/fix_passive.cjs @@ -0,0 +1,4 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); +code = code.replace(/containerRef\.value!\.addEventListener\("touchmove", onTouchKeyboardTouchMove!, \{ capture: true, passive: false \}\);/g, 'containerRef.value!.addEventListener("touchmove", onTouchKeyboardTouchMove!, { capture: true, passive: true });'); +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/fix_pointer.js b/pxterm/fix_pointer.js new file mode 100644 index 0000000..502086a --- /dev/null +++ b/pxterm/fix_pointer.js @@ -0,0 +1,13 @@ +import fs from 'fs'; +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// remove all console.log containing "[Scroll Deep]" lines regardless of newlines +code = code.replace(/console\.log\(\s*(?:`|"|')\[Scroll Deep\][\s\S]*?\);/g, ''); + +// remove redundant empty else blocks +code = code.replace(/\} else \{\s*\}/g, '}'); + +// remove duplicated momentum blocks +code = code.replace(/(\/\/ 释放时触发滚行动量\s*if \(Math\.abs\(touchScrollVelocity\).*?runTouchScrollMomentum\(\);\s*\})[\s\S]*?(\/\/ 释放时触发滚行动量\s*if \(Math\.abs\(touchScrollVelocity\).*?runTouchScrollMomentum\(\);\s*\})/g, '$1'); + +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/fix_pointer2.js b/pxterm/fix_pointer2.js new file mode 100644 index 0000000..e855bf5 --- /dev/null +++ b/pxterm/fix_pointer2.js @@ -0,0 +1,25 @@ +import fs from 'fs'; +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// Find the bad duplicated momentum code that has missing braces +const badCode = ` // 释放时触发滚行动量 + + if (Math.abs(touchScrollVelocity) > 0.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal()) { + touchScrollVelocity *= 1.35; + runTouchScrollMomentum(); + + + // 释放时触发滚行动量 + + if (Math.abs(touchScrollVelocity) > 0.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal()) { + touchScrollVelocity *= 1.35; + runTouchScrollMomentum();`; + +const goodCode = ` // 释放时触发滚行动量 + if (Math.abs(touchScrollVelocity) > 0.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal()) { + touchScrollVelocity *= 1.35; + runTouchScrollMomentum(); + }`; + +code = code.replace(badCode, goodCode); +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/fix_touch_handlers_passive.cjs b/pxterm/fix_touch_handlers_passive.cjs new file mode 100644 index 0000000..5b1b197 --- /dev/null +++ b/pxterm/fix_touch_handlers_passive.cjs @@ -0,0 +1,8 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// The touchmove event on containerRef is currently { capture: true, passive: true }. +// xterm installs its own touchmove listener on the terminal wrapper but if we don't interfere, it should just work. +// Wait, xterm DOES its own momentum? No, xterm doesn't have mobile momentum out of the box... wait, maybe it does? + +console.log('Cleaned up TouchMove. Testing theory.'); diff --git a/pxterm/fix_touch_pointer.cjs b/pxterm/fix_touch_pointer.cjs new file mode 100644 index 0000000..478e0c9 --- /dev/null +++ b/pxterm/fix_touch_pointer.cjs @@ -0,0 +1,7 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// The issue might be pointermove. If we are completely hands-off, we must also make sure pointermove is not stopping it. +const regex = /onTouchKeyboardPointerMove = \(event: PointerEvent\) => \{[\s\S]*?touchGateScrollLike = true;\n\s*\}/; +console.log(regex.test(code)); + diff --git a/pxterm/fix_touch_pointer2.cjs b/pxterm/fix_touch_pointer2.cjs new file mode 100644 index 0000000..d3e8a91 --- /dev/null +++ b/pxterm/fix_touch_pointer2.cjs @@ -0,0 +1,9 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// wait, how is xterm hooking into scrolling on mobile if we do stopImmediatePropagation on touchmove? +// Actually, if we do stopImmediatePropagation, xterm DOES NOT see the touchmove. So xterm DOES NOT scroll the viewport. +// And because the .xterm-screen (where the touch starts) DOES NOT have overflow: scroll, the browser DOES NOT scroll the viewport natively either!! +// This explains why it's completely frozen! We stopped xterm from scrolling it, AND the browser doesn't scroll it natively because the touch target isn't the scrollable container. + +console.log("Found the core issue."); diff --git a/pxterm/fix_ts.js b/pxterm/fix_ts.js new file mode 100644 index 0000000..ba81669 --- /dev/null +++ b/pxterm/fix_ts.js @@ -0,0 +1,12 @@ +import fs from 'fs'; +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +code = code.replace(/containerRef\.value\.addEventListener\("touchstart", onTouchKeyboardTouchStart, /g, 'containerRef.value!.addEventListener("touchstart", onTouchKeyboardTouchStart!, '); +code = code.replace(/containerRef\.value\.addEventListener\("touchmove", onTouchKeyboardTouchMove, /g, 'containerRef.value!.addEventListener("touchmove", onTouchKeyboardTouchMove!, '); +code = code.replace(/containerRef\.value\.addEventListener\("touchend", onTouchKeyboardTouchEnd, /g, 'containerRef.value!.addEventListener("touchend", onTouchKeyboardTouchEnd!, '); +code = code.replace(/containerRef\.value\.addEventListener\("touchcancel", onTouchKeyboardTouchEnd, /g, 'containerRef.value!.addEventListener("touchcancel", onTouchKeyboardTouchEnd!, '); +code = code.replace(/containerRef\.value\.addEventListener\("click", onTouchKeyboardClick, /g, 'containerRef.value!.addEventListener("click", onTouchKeyboardClick, '); +code = code.replace(/containerRef\.value\.addEventListener\("mousedown", onTouchKeyboardMouseDown, /g, 'containerRef.value!.addEventListener("mousedown", onTouchKeyboardMouseDown, '); + + +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/let_xterm_scroll.cjs b/pxterm/let_xterm_scroll.cjs new file mode 100644 index 0000000..ff0d491 --- /dev/null +++ b/pxterm/let_xterm_scroll.cjs @@ -0,0 +1,8 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// To let xterm's native mobile JS scroll work, we MUST NOT intercept touchmove with stopImmediatePropagation. We must pass the events cleanly to xterm. +const regexTouchMove = /onTouchKeyboardTouchMove = \(event: TouchEvent\) => \{[\s\S]*?event\.stopImmediatePropagation\(\);\n\s*\};/; +code = code.replace(regexTouchMove, `onTouchKeyboardTouchMove = (event: TouchEvent) => {\n // Let xterm handle the touchmove natively! Do not stop propagation. It needs to calculate delta and apply it to viewportScroller.\n };`); + +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/remove_momentum.cjs b/pxterm/remove_momentum.cjs new file mode 100644 index 0000000..f76ac2a --- /dev/null +++ b/pxterm/remove_momentum.cjs @@ -0,0 +1,29 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// Remove variables +code = code.replace(/let touchScrollVelocity = 0;\n?/g, ''); +code = code.replace(/let touchScrollRaf:\s*number\s*\|\s*null = null;\n?/g, ''); + +// Remove clearTouchScrollMomentum definition +code = code.replace(/function clearTouchScrollMomentum\(\): void \{[\s\S]*?\}\n/g, ''); + +// Remove runTouchScrollMomentum definition +code = code.replace(/function runTouchScrollMomentum\(\): void \{[\s\S]*?\}\n/g, ''); + +// Remove velocity tracking in pointermove +code = code.replace(/\s*const v = \(\-deltaY \/ dt\) \* 16;[\s\S]*?if \(touchScrollVelocity > touchMaxSpeed\) touchScrollVelocity = touchMaxSpeed;/g, ''); + +// Remove velocity tracking in touchmove +code = code.replace(/\s*\/\/ 计算物理滑动速度,用于释放后的动量[\s\S]*?\n\s*if \(Math\.abs\(dy\) > 2\) \{/g, '\n if (Math.abs(dy) > 2) {'); + +// Remove velocity call in touchend +code = code.replace(/\s*if \(Math\.abs\(touchScrollVelocity\) > 0\.2\) \{[\s\S]*?\n\s*\}/g, ''); + +// Remove velocity call in pointerup +code = code.replace(/\s*\/\/ 释放时触发滚行动量\s*if \(Math\.abs\(touchScrollVelocity\) > 0\.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal\(\)\) \{[\s\S]*?\n\s*\}/g, ''); + +// Clean calls to clearTouchScrollMomentum +code = code.replace(/\s*clearTouchScrollMomentum\(\);/g, ''); + +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/remove_momentum2.cjs b/pxterm/remove_momentum2.cjs new file mode 100644 index 0000000..02cb635 --- /dev/null +++ b/pxterm/remove_momentum2.cjs @@ -0,0 +1,13 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// Also remove residual bits +code = code.replace(/\s*touchScrollVelocity = 0;/g, ''); +code = code.replace(/\s*touchScrollVelocity \*= TOUCH_SCROLL_DAMPING;/g, ''); +code = code.replace(/\s*if \(Math\.abs\(touchScrollVelocity\) < TOUCH_SCROLL_STOP_THRESHOLD\) \{[\s\S]*?\}\n/g, ''); +code = code.replace(/\s*viewportScroller\.scrollTop \+= touchScrollVelocity;/g, ''); + +code = code.replace(/function clearTouchScrollMomentum[\s\S]*?\n\}\n/g, ''); +code = code.replace(/function runTouchScrollMomentum[\s\S]*?\n\}\n/g, ''); + +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/revert.cjs b/pxterm/revert.cjs new file mode 100644 index 0000000..4dcf361 --- /dev/null +++ b/pxterm/revert.cjs @@ -0,0 +1,19 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +// clean global intercept block if it exists +code = code.replace(/\/\/ \-\-\- 极致全局监听器[\s\S]*?\n\s*\}\);\n\n/g, ''); + +// remove multiline logs +code = code.replace(/\s*console\.log\(\s*(?:`|"|')\[Scroll Deep\][\s\S]*?\);\n/g, '\n'); + +// remove empty else blocks +code = code.replace(/\s*\} else \{\s*\n\s*\}/g, '}'); + +// remove duplicated momentum logic in pointerup +code = code.replace(/(\s*\/\/ 释放时触发滚行动量\s*if \(Math\.abs\(touchScrollVelocity\) > 0\.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal\(\)\) \{\s*touchScrollVelocity \*= 1\.35;\s*runTouchScrollMomentum\(\);\s*\})\s*\/\/ 释放时触发滚行动量\s*if \(Math\.abs\(touchScrollVelocity\) > 0\.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal\(\)\) \{\s*touchScrollVelocity \*= 1\.35;\s*runTouchScrollMomentum\(\);\s*\}/g, '$1'); + +// fix the syntax bug around touch end momentum +code = code.replace(/\s*if \(Math\.abs\(touchScrollVelocity\) > 0\.2\) \{\s*touchScrollVelocity \*= 1\.35;\s*runTouchScrollMomentum\(\);\s*\};\s*\n/g, '\n if (Math.abs(touchScrollVelocity) > 0.2) {\n touchScrollVelocity *= 1.35;\n runTouchScrollMomentum();\n }\n'); + +fs.writeFileSync('src/components/TerminalPanel.vue', code); diff --git a/pxterm/src/components/TerminalPanel.vue b/pxterm/src/components/TerminalPanel.vue index 16e1a4a..41ea2db 100644 --- a/pxterm/src/components/TerminalPanel.vue +++ b/pxterm/src/components/TerminalPanel.vue @@ -1205,23 +1205,7 @@ async function loadWebglAddonOnDesktop(): Promise { */ function initTerminal(): void { - // --- 极致全局监听器,抓出是谁吃掉了 move --- - const globalEventDebug = (e: Event) => { - // 若这是消失的 move 事件,打出到底在哪被拦截了 - if (e.type === "touchmove" || e.type === "pointermove" || e.type === "touchcancel" || e.type === "pointercancel") { - // 为了不刷屏,如果是 pointermove 我们只在 touch 活跃时打印 - if (e.type === "pointermove" && (e as PointerEvent).pointerType !== "touch") return; - - console.log(`[GLOBAL INTERCEPT] ${e.type} | Phase: ${e.eventPhase} | Cancelable: ${e.cancelable} | Target: ${(e.target as Element)?.className || (e.target as Element)?.tagName}`); - } - }; - - ['touchmove', 'touchstart', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointercancel'].forEach(type => { - // 用最顶级的 window capture 拦截,这样在任何人(包括 xterm 内部那些黑盒代码)前面执行 - window.addEventListener(type, globalEventDebug, { capture: true, passive: false }); - }); - - if (!containerRef.value) return; + if (!containerRef.value) return; terminal = new Terminal({ // Unicode11Addon 依赖 xterm proposed API,需显式开启。 @@ -1465,7 +1449,6 @@ function initTerminal(): void { }; 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; } @@ -1527,7 +1510,6 @@ function initTerminal(): void { }; onTouchKeyboardPointerUp = (event: PointerEvent) => { - console.log("[Scroll Deep] 🟠 POINTER UP: ", { pointerId: event.pointerId, type: event.pointerType }); if (event.pointerType !== "touch" || touchGatePointerId !== event.pointerId) { return; } @@ -1536,22 +1518,7 @@ function initTerminal(): void { if (Math.abs(touchScrollVelocity) > 0.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal()) { touchScrollVelocity *= 1.35; - console.log(`[Scroll Deep] 🚀 Pointer 释放,触发JS惯性,速度=${touchScrollVelocity.toFixed(2)}`); - runTouchScrollMomentum(); - } else { - console.log(`[Scroll Deep] 🛑 Pointer 静止释放或非滚动释放`); - } - - - // 释放时触发滚行动量 - - if (Math.abs(touchScrollVelocity) > 0.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal()) { - touchScrollVelocity *= 1.35; - console.log(`[Scroll Deep] 🚀 Pointer 释放,触发JS惯性,速度=${touchScrollVelocity.toFixed(2)}`); - runTouchScrollMomentum(); - } else { - console.log(`[Scroll Deep] 🛑 Pointer 静止释放或非滚动释放`); - } + runTouchScrollMomentum();} const context = { inBand: touchGateStartInBand, @@ -1660,7 +1627,6 @@ function initTerminal(): void { } }; onTouchKeyboardPointerCancel = (event: PointerEvent) => { - console.log("[Scroll Deep] ⚫ POINTER CANCEL: ", { pointerId: event.pointerId, type: event.pointerType }); if (event.pointerType !== "touch" || touchGatePointerId !== event.pointerId) { return; } @@ -1694,13 +1660,6 @@ function initTerminal(): void { touchScrollVelocity = 0; } - console.log("[Scroll Deep] 🟢 TOUCH START:", { - touches: event.touches.length, - isCancelable: event.cancelable, - target: (event.target as Node)?.nodeName, - viewportScrollTop: viewportScroller?.scrollTop - }); - if (!helperTextarea) return; if (focusKeyboardTimerId !== null) { window.clearTimeout(focusKeyboardTimerId); @@ -1711,7 +1670,6 @@ function initTerminal(): void { helperTextarea.readOnly = true; }; onTouchKeyboardTouchMove = (event: TouchEvent) => { - console.log(`[Scroll Deep] 🔵 TOUCH MOVE | cancelable: ${event.cancelable} | target: ${(event.target as Node)?.nodeName}`); const hasSelection = hasActiveNativeSelectionInTerminal(); if (hasSelection) { lastTouchAction = "PASS_NATIVE"; @@ -1751,7 +1709,6 @@ function initTerminal(): void { else if (touchScrollVelocity < -touchMaxSpeed) touchScrollVelocity = -touchMaxSpeed; if (Math.abs(dy) > 2) { - console.log(`[Scroll Deep] ➡️ 手指位移真实触发生效,Δy=${dy.toFixed(1)}px | 渲染视口=${before}→${viewportScroller.scrollTop} | v=${v.toFixed(2)}`); } } @@ -1759,16 +1716,11 @@ function initTerminal(): void { touchScrollLastTime = now; }; onTouchKeyboardTouchEnd = (event: TouchEvent) => { - console.log(`[Scroll Deep] 🔴 TOUCH END/CANCEL | cancelable: ${event.cancelable} | target: ${(event.target as Node)?.nodeName} | 最终 scrollTop: ${viewportScroller?.scrollTop}`); if (Math.abs(touchScrollVelocity) > 0.2) { touchScrollVelocity *= 1.35; - console.log(`[Scroll Deep] 🚀 准备进行 JS 惯性滚动,起步速度=${touchScrollVelocity.toFixed(2)}`); - runTouchScrollMomentum(); - } else { - console.log(`[Scroll Deep] 🛑 静止释放,不触发惯性`); - } + runTouchScrollMomentum();} }; // passive: true — 以上均不调用 preventDefault()。非 passive 的 touch 监听器 // 会阻塞 iOS 滚动合成器等待 JS 主线程,即使实际未 preventDefault 也会引入卡顿。 diff --git a/pxterm/use_native.cjs b/pxterm/use_native.cjs new file mode 100644 index 0000000..7c7cc85 --- /dev/null +++ b/pxterm/use_native.cjs @@ -0,0 +1,17 @@ +const fs = require('fs'); +let code = fs.readFileSync('src/components/TerminalPanel.vue', 'utf8'); + +const regexTouchMove = /onTouchKeyboardTouchMove = \(event: TouchEvent\) => \{[\s\S]*?touchScrollLastTime = now;\n\s*\};/g; + +const newTouchMove = `onTouchKeyboardTouchMove = (event: TouchEvent) => { + // 通过 stopImmediatePropagation 拦截 xterm 内部那些“接管浏览器滚动”的事件处理器。 + // 但绝对不要自己掉用 event.preventDefault(),从而把物理滚动和动量动画原模原样还给浏览器与操作系统。 + event.stopImmediatePropagation(); + };`; + +code = code.replace(regexTouchMove, newTouchMove); + +// Also remove touchstart redundant vars +code = code.replace(/touchScrollLastY = event\.touches\[0\]\?\.clientY \|\| 0;\n\s*touchScrollLastTime = performance\.now\(\);\n/g, ''); + +fs.writeFileSync('src/components/TerminalPanel.vue', code);