fix mobile terminal drawer sizing (#799)

This commit is contained in:
ekko
2026-05-16 21:01:00 +08:00
committed by GitHub
parent f7556e6204
commit 048a0ad87e
3 changed files with 144 additions and 7 deletions
@@ -86,9 +86,10 @@ function handleClose() {
.drawer-panel { .drawer-panel {
position: fixed; position: fixed;
top: 0; top: 0;
right: -900px; right: min(-1180px, -88vw);
width: 900px; width: min(1180px, 88vw);
height: 100vh; height: calc(100 * var(--vh));
max-height: calc(100 * var(--vh));
background: $bg-card; background: $bg-card;
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15); box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
display: flex; display: flex;
@@ -180,5 +181,3 @@ function handleClose() {
overflow: auto; overflow: auto;
} }
</style> </style>
@@ -103,6 +103,9 @@ let activeFitAddon: FitAddon | null = null;
let resizeObserver: ResizeObserver | null = null; let resizeObserver: ResizeObserver | null = null;
let reconnectAttempts = 0; let reconnectAttempts = 0;
const MAX_RECONNECT_ATTEMPTS = 3; const MAX_RECONNECT_ATTEMPTS = 3;
let touchScrollLastY: number | null = null;
let touchScrollRemainder = 0;
const TOUCH_SCROLL_LINE_PX = 18;
// ─── Computed ────────────────────────────────────────────────── // ─── Computed ──────────────────────────────────────────────────
@@ -356,6 +359,35 @@ function sendResize() {
} catch {} } catch {}
} }
function handleTerminalTouchStart(event: TouchEvent) {
if (event.touches.length !== 1) {
touchScrollLastY = null;
touchScrollRemainder = 0;
return;
}
touchScrollLastY = event.touches[0].clientY;
touchScrollRemainder = 0;
}
function handleTerminalTouchMove(event: TouchEvent) {
if (!activeTerm || event.touches.length !== 1 || touchScrollLastY === null) return;
const nextY = event.touches[0].clientY;
touchScrollRemainder += touchScrollLastY - nextY;
touchScrollLastY = nextY;
const lines = Math.trunc(touchScrollRemainder / TOUCH_SCROLL_LINE_PX);
if (lines === 0) return;
activeTerm.scrollLines(lines);
touchScrollRemainder -= lines * TOUCH_SCROLL_LINE_PX;
event.preventDefault();
}
function handleTerminalTouchEnd() {
touchScrollLastY = null;
touchScrollRemainder = 0;
}
// ─── Theme ─────────────────────────────────────────────────────── // ─── Theme ───────────────────────────────────────────────────────
function applyTheme(themeName: string) { function applyTheme(themeName: string) {
@@ -517,7 +549,15 @@ onUnmounted(() => {
</div> </div>
</header> </header>
<div class="terminal-container"> <div class="terminal-container">
<div ref="terminalRef" class="terminal-xterm" :style="{ backgroundColor: terminalBg }" /> <div
ref="terminalRef"
class="terminal-xterm"
:style="{ backgroundColor: terminalBg }"
@touchstart="handleTerminalTouchStart"
@touchmove="handleTerminalTouchMove"
@touchend="handleTerminalTouchEnd"
@touchcancel="handleTerminalTouchEnd"
/>
</div> </div>
</div> </div>
</div> </div>
@@ -798,4 +838,35 @@ onUnmounted(() => {
display: none !important; display: none !important;
} }
} }
@media (max-width: $breakpoint-mobile) {
.terminal-panel-drawer {
height: calc(100 * var(--vh));
max-height: calc(100 * var(--vh));
}
.terminal-main {
min-height: 0;
}
.terminal-container {
margin-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
}
.terminal-xterm {
:deep(.xterm-viewport),
:deep(.xterm-scrollable-element) {
touch-action: pan-y;
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
scrollbar-width: thin !important;
}
:deep(.xterm-viewport::-webkit-scrollbar),
:deep(.xterm-scrollable-element::-webkit-scrollbar) {
display: block !important;
width: 6px !important;
}
}
}
</style> </style>
@@ -245,6 +245,9 @@ let activeTerm: Terminal | null = null;
let activeFitAddon: FitAddon | null = null; let activeFitAddon: FitAddon | null = null;
let resizeObserver: ResizeObserver | null = null; let resizeObserver: ResizeObserver | null = null;
let mobileQuery: MediaQueryList | null = null; let mobileQuery: MediaQueryList | null = null;
let touchScrollLastY: number | null = null;
let touchScrollRemainder = 0;
const TOUCH_SCROLL_LINE_PX = 18;
// ─── Computed ────────────────────────────────────────────────── // ─── Computed ──────────────────────────────────────────────────
@@ -495,6 +498,35 @@ function sendResize() {
} catch {} } catch {}
} }
function handleTerminalTouchStart(event: TouchEvent) {
if (event.touches.length !== 1) {
touchScrollLastY = null;
touchScrollRemainder = 0;
return;
}
touchScrollLastY = event.touches[0].clientY;
touchScrollRemainder = 0;
}
function handleTerminalTouchMove(event: TouchEvent) {
if (!activeTerm || event.touches.length !== 1 || touchScrollLastY === null) return;
const nextY = event.touches[0].clientY;
touchScrollRemainder += touchScrollLastY - nextY;
touchScrollLastY = nextY;
const lines = Math.trunc(touchScrollRemainder / TOUCH_SCROLL_LINE_PX);
if (lines === 0) return;
activeTerm.scrollLines(lines);
touchScrollRemainder -= lines * TOUCH_SCROLL_LINE_PX;
event.preventDefault();
}
function handleTerminalTouchEnd() {
touchScrollLastY = null;
touchScrollRemainder = 0;
}
// ─── Theme ─────────────────────────────────────────────────────── // ─── Theme ───────────────────────────────────────────────────────
function applyTheme(themeName: string) { function applyTheme(themeName: string) {
@@ -689,7 +721,15 @@ onUnmounted(() => {
</div> </div>
</header> </header>
<div class="terminal-container"> <div class="terminal-container">
<div ref="terminalRef" class="terminal-xterm" :style="{ backgroundColor: terminalBg }" /> <div
ref="terminalRef"
class="terminal-xterm"
:style="{ backgroundColor: terminalBg }"
@touchstart="handleTerminalTouchStart"
@touchmove="handleTerminalTouchMove"
@touchend="handleTerminalTouchEnd"
@touchcancel="handleTerminalTouchEnd"
/>
</div> </div>
</div> </div>
</div> </div>
@@ -980,6 +1020,19 @@ onUnmounted(() => {
// ─── Mobile ───────────────────────────────────────────────────── // ─── Mobile ─────────────────────────────────────────────────────
@media (max-width: $breakpoint-mobile) { @media (max-width: $breakpoint-mobile) {
.terminal-panel {
height: calc(100 * var(--vh));
max-height: calc(100 * var(--vh));
}
.terminal-main {
min-height: 0;
}
.terminal-container {
margin-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
}
.session-close-btn { .session-close-btn {
display: flex; display: flex;
} }
@@ -1011,6 +1064,20 @@ onUnmounted(() => {
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
:deep(.xterm-viewport),
:deep(.xterm-scrollable-element) {
touch-action: pan-y;
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
scrollbar-width: thin !important;
}
:deep(.xterm-viewport::-webkit-scrollbar),
:deep(.xterm-scrollable-element::-webkit-scrollbar) {
display: block !important;
width: 6px !important;
}
} }
} }
</style> </style>