Compare commits

..

3 Commits

Author SHA1 Message Date
Bas Nijholt
e1a8ceb9e6 fix: Pass local_host to _get_glances_address in containers route (#172)
The get_containers_rows_by_host endpoint was missing the local_host
parameter when calling _get_glances_address, causing it to use the
external IP instead of the container name for the local host.

This caused "Connection timed out" errors on the live-stats page
when the web UI container couldn't reach its own host via the
external IP (hairpin NAT issue).
2026-01-18 20:23:21 +00:00
Bas Nijholt
ed450c65e5 web: Fix sidebar filter clear button not appearing (#171)
Change the filter input event handler from onkeyup to oninput.
The onkeyup event only fires when a key is physically pressed and
released, which means the clear button never appeared when:
- Pasting text
- Using browser autofill
- Setting value programmatically

The oninput event fires whenever the value changes regardless of
the input method, making the clear button work reliably.
2026-01-18 20:21:37 +00:00
Bas Nijholt
0f84864a06 web: Show host name in live-stats error messages (#170)
Error rows now include the host name (e.g., "nuc: Connection refused")
and have proper id/data-host attributes so they get replaced in place
instead of accumulating on each refresh interval.
2026-01-18 20:08:39 +00:00
7 changed files with 9 additions and 39 deletions

View File

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

View File

@@ -31,29 +31,7 @@
.editor-wrapper.env-wrapper { height: 250px; }
.editor-wrapper.viewer-wrapper { height: 300px; }
/* 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;
}
}
/* Terminal - no custom CSS needed, using h-full class in HTML */
/* Prevent save button resize when text changes */
#save-btn, #save-config-btn {

View File

@@ -14,9 +14,6 @@ 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',
@@ -139,19 +136,12 @@ 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; touch-action: pan-y;"></div>
<div id="console-terminal" class="w-full bg-base-300 rounded-lg overflow-hidden resize-y" style="height: 384px; min-height: 200px;"></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..." onkeyup="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..." oninput="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] lg:h-[300px] border border-white/10 resize-y overflow-hidden" style="touch-action: pan-y;">
<div id="terminal-container" class="bg-[#1a1a2e] rounded-lg h-[300px] border border-white/10 resize-y overflow-hidden">
<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" style="touch-action: pan-y;">
<div id="exec-terminal-container" class="bg-[#1a1a2e] rounded-lg h-[400px] border border-white/10 hidden">
<div id="exec-terminal" class="h-full"></div>
</div>
{% endcall %}