Compare commits

..

1 Commits

Author SHA1 Message Date
Bas Nijholt
772b0518ed web: Improve terminal scroll behavior on mobile
- Add native scroll momentum with -webkit-overflow-scrolling: touch
- Add overscroll-behavior: contain to prevent pull-to-refresh interference
- Add mobile-specific max-height (50vh) and touch-action: pan-y
- Detect touch devices and enable xterm.js smoothScrollDuration
- Increase scrollSensitivity for more responsive touch scrolling
2026-01-18 03:44:42 -08:00
7 changed files with 39 additions and 9 deletions

View File

@@ -253,8 +253,7 @@ async def get_containers_rows_by_host(host_name: str) -> HTMLResponse:
return HTMLResponse("")
host = config.hosts[host_name]
local_host = config.get_local_host_from_web_stack()
glances_address = _get_glances_address(host_name, host, config.glances_stack, local_host)
glances_address = _get_glances_address(host_name, host, config.glances_stack)
t0 = time.monotonic()
containers, error = await fetch_container_stats(host_name, glances_address)
@@ -269,8 +268,7 @@ async def get_containers_rows_by_host(host_name: str) -> HTMLResponse:
error,
)
return HTMLResponse(
f'<tr id="error-{host_name}" class="text-error" data-host="{host_name}">'
f'<td colspan="12" class="text-center py-2">{host_name}: {error}</td></tr>'
f'<tr class="text-error"><td colspan="12" class="text-center py-2">Error: {error}</td></tr>'
)
if not containers:

View File

@@ -31,7 +31,29 @@
.editor-wrapper.env-wrapper { height: 250px; }
.editor-wrapper.viewer-wrapper { height: 300px; }
/* Terminal - no custom CSS needed, using h-full class in HTML */
/* Terminal - mobile touch scrolling improvements */
.xterm-viewport {
/* Allow native scroll momentum on mobile */
-webkit-overflow-scrolling: touch;
/* Prevent pull-to-refresh from interfering */
overscroll-behavior: contain;
}
/* Mobile-specific terminal adjustments */
@media (max-width: 1023px) {
#terminal-container,
#console-terminal,
#exec-terminal {
/* Use viewport height on mobile for better sizing */
min-height: 200px;
max-height: 50vh;
}
/* Ensure terminal content is scrollable within container */
.xterm-screen {
touch-action: pan-y;
}
}
/* Prevent save button resize when text changes */
#save-btn, #save-config-btn {

View File

@@ -14,6 +14,9 @@ const ANSI = {
CRLF: '\r\n'
};
// Detect mobile/touch device
const IS_TOUCH_DEVICE = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
// Terminal color theme (dark mode matching PicoCSS)
const TERMINAL_THEME = {
background: '#1a1a2e',
@@ -136,12 +139,19 @@ function whenXtermReady(callback, maxAttempts = 20) {
function createTerminal(container, extraOptions = {}, onResize = null) {
container.innerHTML = '';
// Mobile-optimized terminal options
const mobileOptions = IS_TOUCH_DEVICE ? {
smoothScrollDuration: 125, // Smoother scroll animation on touch
scrollSensitivity: 3, // Increased sensitivity for touch scrolling
} : {};
const term = new Terminal({
convertEol: true,
theme: TERMINAL_THEME,
fontSize: 13,
fontFamily: 'Monaco, Menlo, "Ubuntu Mono", monospace',
scrollback: 5000,
...mobileOptions,
...extraOptions
});

View File

@@ -21,7 +21,7 @@
<!-- Terminal -->
{% call collapse("Terminal", checked=True, icon=terminal(), subtitle="Full shell access to selected host") %}
<div id="console-terminal" class="w-full bg-base-300 rounded-lg overflow-hidden resize-y" style="height: 384px; min-height: 200px;"></div>
<div id="console-terminal" class="w-full bg-base-300 rounded-lg overflow-hidden resize-y" style="height: 384px; min-height: 200px; touch-action: pan-y;"></div>
{% endcall %}
<!-- Editor -->

View File

@@ -13,7 +13,7 @@
<h4 class="text-xs uppercase tracking-wide text-base-content/60 px-3 py-1">Stacks <span class="opacity-50" id="sidebar-count">({{ stacks | length }})</span></h4>
<div class="px-2 mb-2 flex flex-col gap-1">
<label class="input input-xs flex items-center gap-2 bg-base-200">
{{ search(14) }}<input type="text" id="sidebar-filter" placeholder="Filter..." oninput="sidebarFilter()" /><button type="button" id="sidebar-filter-clear" class="hidden opacity-50 hover:opacity-100 cursor-pointer" onclick="clearSidebarFilter()">{{ x(12) }}</button>
{{ search(14) }}<input type="text" id="sidebar-filter" placeholder="Filter..." onkeyup="sidebarFilter()" /><button type="button" id="sidebar-filter-clear" class="hidden opacity-50 hover:opacity-100 cursor-pointer" onclick="clearSidebarFilter()">{{ x(12) }}</button>
</label>
<select id="sidebar-host-select" class="select select-xs bg-base-200 w-full" onchange="sidebarFilter()">
<option value="">All hosts</option>

View File

@@ -7,7 +7,7 @@
<span id="terminal-spinner" class="loading loading-spinner loading-sm hidden"></span>
</div>
<div class="collapse-content">
<div id="terminal-container" class="bg-[#1a1a2e] rounded-lg h-[300px] border border-white/10 resize-y overflow-hidden">
<div id="terminal-container" class="bg-[#1a1a2e] rounded-lg h-[300px] lg:h-[300px] border border-white/10 resize-y overflow-hidden" style="touch-action: pan-y;">
<div id="terminal-output" class="h-full"></div>
</div>
</div>

View File

@@ -71,7 +71,7 @@
hx-swap="innerHTML">
<span class="loading loading-spinner loading-sm"></span> Loading containers...
</div>
<div id="exec-terminal-container" class="bg-[#1a1a2e] rounded-lg h-[400px] border border-white/10 hidden">
<div id="exec-terminal-container" class="bg-[#1a1a2e] rounded-lg h-[400px] border border-white/10 hidden" style="touch-action: pan-y;">
<div id="exec-terminal" class="h-full"></div>
</div>
{% endcall %}