update at 2026-03-03 22:07:05
This commit is contained in:
2
pxterm/check_zindex.js
Normal file
2
pxterm/check_zindex.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
console.log('Testing strategy...');
|
||||||
13
pxterm/clean_pointer.cjs
Normal file
13
pxterm/clean_pointer.cjs
Normal file
@@ -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);
|
||||||
17
pxterm/clean_terminal.cjs
Normal file
17
pxterm/clean_terminal.cjs
Normal file
@@ -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);
|
||||||
5
pxterm/clean_vars.cjs
Normal file
5
pxterm/clean_vars.cjs
Normal file
@@ -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);
|
||||||
21
pxterm/fix_bracket.js
Normal file
21
pxterm/fix_bracket.js
Normal file
@@ -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);
|
||||||
8
pxterm/fix_bracket2.cjs
Normal file
8
pxterm/fix_bracket2.cjs
Normal file
@@ -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);
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
let code = fs.readFileSync('src/styles/main.css', 'utf8');
|
let code = fs.readFileSync('src/styles/main.css', 'utf8');
|
||||||
|
code += `
|
||||||
|
|
||||||
// If touch-action is missing from xterm-viewport we need to add it
|
/* Fix for native scrolling: .xterm-screen cannot be scrolled natively because it is not the scroll container.
|
||||||
code = code.replace(/\.terminal-wrapper \.xterm \.xterm-viewport \{/, '.terminal-wrapper .xterm .xterm-viewport {\n touch-action: none !important;');
|
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);
|
fs.writeFileSync('src/styles/main.css', code);
|
||||||
|
|||||||
14
pxterm/fix_native.cjs
Normal file
14
pxterm/fix_native.cjs
Normal file
@@ -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...");
|
||||||
4
pxterm/fix_passive.cjs
Normal file
4
pxterm/fix_passive.cjs
Normal file
@@ -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);
|
||||||
13
pxterm/fix_pointer.js
Normal file
13
pxterm/fix_pointer.js
Normal file
@@ -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);
|
||||||
25
pxterm/fix_pointer2.js
Normal file
25
pxterm/fix_pointer2.js
Normal file
@@ -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);
|
||||||
8
pxterm/fix_touch_handlers_passive.cjs
Normal file
8
pxterm/fix_touch_handlers_passive.cjs
Normal file
@@ -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.');
|
||||||
7
pxterm/fix_touch_pointer.cjs
Normal file
7
pxterm/fix_touch_pointer.cjs
Normal file
@@ -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));
|
||||||
|
|
||||||
9
pxterm/fix_touch_pointer2.cjs
Normal file
9
pxterm/fix_touch_pointer2.cjs
Normal file
@@ -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.");
|
||||||
12
pxterm/fix_ts.js
Normal file
12
pxterm/fix_ts.js
Normal file
@@ -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);
|
||||||
8
pxterm/let_xterm_scroll.cjs
Normal file
8
pxterm/let_xterm_scroll.cjs
Normal file
@@ -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);
|
||||||
29
pxterm/remove_momentum.cjs
Normal file
29
pxterm/remove_momentum.cjs
Normal file
@@ -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);
|
||||||
13
pxterm/remove_momentum2.cjs
Normal file
13
pxterm/remove_momentum2.cjs
Normal file
@@ -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);
|
||||||
19
pxterm/revert.cjs
Normal file
19
pxterm/revert.cjs
Normal file
@@ -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);
|
||||||
@@ -1205,23 +1205,7 @@ async function loadWebglAddonOnDesktop(): Promise<void> {
|
|||||||
*/
|
*/
|
||||||
function initTerminal(): void {
|
function initTerminal(): void {
|
||||||
|
|
||||||
// --- 极致全局监听器,抓出是谁吃掉了 move ---
|
if (!containerRef.value) return;
|
||||||
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;
|
|
||||||
|
|
||||||
terminal = new Terminal({
|
terminal = new Terminal({
|
||||||
// Unicode11Addon 依赖 xterm proposed API,需显式开启。
|
// Unicode11Addon 依赖 xterm proposed API,需显式开启。
|
||||||
@@ -1465,7 +1449,6 @@ function initTerminal(): void {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onTouchKeyboardPointerDown = (event: PointerEvent) => {
|
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") {
|
if (event.pointerType !== "touch") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1527,7 +1510,6 @@ function initTerminal(): void {
|
|||||||
|
|
||||||
};
|
};
|
||||||
onTouchKeyboardPointerUp = (event: PointerEvent) => {
|
onTouchKeyboardPointerUp = (event: PointerEvent) => {
|
||||||
console.log("[Scroll Deep] 🟠 POINTER UP: ", { pointerId: event.pointerId, type: event.pointerType });
|
|
||||||
if (event.pointerType !== "touch" || touchGatePointerId !== event.pointerId) {
|
if (event.pointerType !== "touch" || touchGatePointerId !== event.pointerId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1536,22 +1518,7 @@ function initTerminal(): void {
|
|||||||
|
|
||||||
if (Math.abs(touchScrollVelocity) > 0.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal()) {
|
if (Math.abs(touchScrollVelocity) > 0.2 && touchGateScrollLike && !hasActiveNativeSelectionInTerminal()) {
|
||||||
touchScrollVelocity *= 1.35;
|
touchScrollVelocity *= 1.35;
|
||||||
console.log(`[Scroll Deep] 🚀 Pointer 释放,触发JS惯性,速度=${touchScrollVelocity.toFixed(2)}`);
|
runTouchScrollMomentum();}
|
||||||
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 静止释放或非滚动释放`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
inBand: touchGateStartInBand,
|
inBand: touchGateStartInBand,
|
||||||
@@ -1660,7 +1627,6 @@ function initTerminal(): void {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
onTouchKeyboardPointerCancel = (event: PointerEvent) => {
|
onTouchKeyboardPointerCancel = (event: PointerEvent) => {
|
||||||
console.log("[Scroll Deep] ⚫ POINTER CANCEL: ", { pointerId: event.pointerId, type: event.pointerType });
|
|
||||||
if (event.pointerType !== "touch" || touchGatePointerId !== event.pointerId) {
|
if (event.pointerType !== "touch" || touchGatePointerId !== event.pointerId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1694,13 +1660,6 @@ function initTerminal(): void {
|
|||||||
touchScrollVelocity = 0;
|
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 (!helperTextarea) return;
|
||||||
if (focusKeyboardTimerId !== null) {
|
if (focusKeyboardTimerId !== null) {
|
||||||
window.clearTimeout(focusKeyboardTimerId);
|
window.clearTimeout(focusKeyboardTimerId);
|
||||||
@@ -1711,7 +1670,6 @@ function initTerminal(): void {
|
|||||||
helperTextarea.readOnly = true;
|
helperTextarea.readOnly = true;
|
||||||
};
|
};
|
||||||
onTouchKeyboardTouchMove = (event: TouchEvent) => {
|
onTouchKeyboardTouchMove = (event: TouchEvent) => {
|
||||||
console.log(`[Scroll Deep] 🔵 TOUCH MOVE | cancelable: ${event.cancelable} | target: ${(event.target as Node)?.nodeName}`);
|
|
||||||
const hasSelection = hasActiveNativeSelectionInTerminal();
|
const hasSelection = hasActiveNativeSelectionInTerminal();
|
||||||
if (hasSelection) {
|
if (hasSelection) {
|
||||||
lastTouchAction = "PASS_NATIVE";
|
lastTouchAction = "PASS_NATIVE";
|
||||||
@@ -1751,7 +1709,6 @@ function initTerminal(): void {
|
|||||||
else if (touchScrollVelocity < -touchMaxSpeed) touchScrollVelocity = -touchMaxSpeed;
|
else if (touchScrollVelocity < -touchMaxSpeed) touchScrollVelocity = -touchMaxSpeed;
|
||||||
|
|
||||||
if (Math.abs(dy) > 2) {
|
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;
|
touchScrollLastTime = now;
|
||||||
};
|
};
|
||||||
onTouchKeyboardTouchEnd = (event: TouchEvent) => {
|
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) {
|
if (Math.abs(touchScrollVelocity) > 0.2) {
|
||||||
touchScrollVelocity *= 1.35;
|
touchScrollVelocity *= 1.35;
|
||||||
console.log(`[Scroll Deep] 🚀 准备进行 JS 惯性滚动,起步速度=${touchScrollVelocity.toFixed(2)}`);
|
runTouchScrollMomentum();}
|
||||||
runTouchScrollMomentum();
|
|
||||||
} else {
|
|
||||||
console.log(`[Scroll Deep] 🛑 静止释放,不触发惯性`);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
// passive: true — 以上均不调用 preventDefault()。非 passive 的 touch 监听器
|
// passive: true — 以上均不调用 preventDefault()。非 passive 的 touch 监听器
|
||||||
// 会阻塞 iOS 滚动合成器等待 JS 主线程,即使实际未 preventDefault 也会引入卡顿。
|
// 会阻塞 iOS 滚动合成器等待 JS 主线程,即使实际未 preventDefault 也会引入卡顿。
|
||||||
|
|||||||
17
pxterm/use_native.cjs
Normal file
17
pxterm/use_native.cjs
Normal file
@@ -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);
|
||||||
Reference in New Issue
Block a user