fix: terminal rendering and dashboard issues
- Fix escape sequence display: filter DA1 responses that can be split across socket reads - Fix font rendering: use ghostty-web renderer API (setFontFamily/remeasureFont) - Fix sparklines: update slug-to-service mapping when containers are added/removed - Improve typeahead thumbnails: increase to 96x72px (4:3 ratio)
This commit is contained in:
@@ -6,6 +6,7 @@ import asyncio
|
||||
import contextlib
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import socket
|
||||
from collections import deque
|
||||
from dataclasses import dataclass
|
||||
@@ -27,6 +28,10 @@ REPLAY_BUFFER_SIZE = 256 * 1024 # 256KB
|
||||
DEFAULT_SCREEN_WIDTH = 132
|
||||
DEFAULT_SCREEN_HEIGHT = 45
|
||||
|
||||
# Pattern to filter out terminal device attribute responses that cause display issues
|
||||
# These are responses to queries that shouldn't be displayed as text
|
||||
DA_RESPONSE_PATTERN = re.compile(rb'\x1b\[\?[\d;]+c')
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DockerExecSpec:
|
||||
@@ -173,8 +178,8 @@ class DockerExecSession(Session):
|
||||
sock.close()
|
||||
detail = body.decode("utf-8", errors="replace")
|
||||
raise RuntimeError(f"Docker API exec start failed ({status}): {detail}")
|
||||
if body:
|
||||
self._pending_output += body
|
||||
# Don't save body from HTTP upgrade - it contains protocol handshake data,
|
||||
# not real terminal output (e.g., device attribute responses like "\x1b[?1;10;0c")
|
||||
sock.settimeout(None)
|
||||
return sock
|
||||
|
||||
@@ -297,6 +302,11 @@ class DockerExecSession(Session):
|
||||
data = await queue.get()
|
||||
if not data:
|
||||
break
|
||||
# Filter out device attribute responses that can cause display issues
|
||||
# when split across socket reads
|
||||
data = DA_RESPONSE_PATTERN.sub(b'', data)
|
||||
if not data:
|
||||
continue
|
||||
await self._add_to_replay_buffer(data)
|
||||
await self._update_screen(data)
|
||||
if self._connector:
|
||||
|
||||
@@ -360,12 +360,19 @@ class LocalServer:
|
||||
def _on_docker_container_added(self, slug: str, name: str, command: str) -> None:
|
||||
"""Callback when a Docker container is added."""
|
||||
log.info("Container added to dashboard: %s -> %s", name, slug)
|
||||
# Update slug-to-service mapping for sparklines
|
||||
if self._docker_stats:
|
||||
self._slug_to_service[slug] = name
|
||||
log.debug("Added sparkline mapping: %s -> %s", slug, name)
|
||||
# Notify SSE subscribers about dashboard change
|
||||
self._notify_activity("__dashboard__")
|
||||
|
||||
def _on_docker_container_removed(self, slug: str) -> None:
|
||||
"""Callback when a Docker container is removed."""
|
||||
log.info("Container removed from dashboard: %s", slug)
|
||||
# Remove slug-to-service mapping
|
||||
if self._docker_stats and slug in self._slug_to_service:
|
||||
del self._slug_to_service[slug]
|
||||
# Invalidate any cached screenshots
|
||||
self._screenshot_cache.pop(slug, None)
|
||||
self._screenshot_cache_etag.pop(slug, None)
|
||||
@@ -769,7 +776,7 @@ class LocalServer:
|
||||
.floating-results .search-query {{ font-size: 18px; font-weight: bold; color: #3b82f6; }}
|
||||
.floating-results .result-item {{ display: flex; align-items: center; gap: 12px; padding: 12px; margin: 6px 0; border: 1px solid #334155; border-radius: 6px; cursor: pointer; transition: all 0.15s; }}
|
||||
.floating-results .result-item:hover, .floating-results .result-item.active {{ background: #334155; border-color: #3b82f6; }}
|
||||
.floating-results .result-thumb {{ width: 72px; height: 40px; flex: 0 0 auto; border-radius: 4px; border: 1px solid #334155; background: #0b1220; object-fit: contain; }}
|
||||
.floating-results .result-thumb {{ width: 96px; height: 72px; flex: 0 0 auto; border-radius: 4px; border: 1px solid #334155; background: #0b1220; object-fit: contain; }}
|
||||
.floating-results .result-content {{ display: flex; flex-direction: column; gap: 2px; }}
|
||||
.floating-results .result-title {{ font-weight: bold; margin-bottom: 4px; }}
|
||||
.floating-results .result-meta {{ font-size: 12px; color: #94a3b8; }}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -515,9 +515,12 @@ class WebTerminal {
|
||||
// Wait for fonts to load before fitting to ensure correct measurements
|
||||
this.waitForFonts().then(() => {
|
||||
console.log("[webterm:init] Fonts loaded, reapplying font family and fitting...");
|
||||
this.terminal.options.fontFamily = this.fontFamily;
|
||||
if (typeof (this.terminal as unknown as { loadFonts?: () => void }).loadFonts === "function") {
|
||||
(this.terminal as unknown as { loadFonts: () => void }).loadFonts();
|
||||
// Use renderer's setFontFamily method to properly update fonts
|
||||
const renderer = (this.terminal as unknown as { renderer?: { setFontFamily: (family: string) => void; remeasureFont: () => void } }).renderer;
|
||||
if (renderer) {
|
||||
renderer.setFontFamily(this.fontFamily);
|
||||
renderer.remeasureFont();
|
||||
console.log("[webterm:init] Font family updated via renderer");
|
||||
}
|
||||
this.fit();
|
||||
console.log("[webterm:init] fit() completed");
|
||||
|
||||
Reference in New Issue
Block a user