fix mobile terminal drawer sizing (#799)
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user