fix: font initialization and DA1 response filtering
- Use terminal.loadFonts() API for proper font re-measurement after web fonts load - Add documentation referencing ghostty-web commit feab41f9a8e4491f - Handle DA1 responses split across socket reads with escape sequence buffering - Update Makefile push target to explicitly push current tag
This commit is contained in:
@@ -97,5 +97,12 @@ bump-patch: ## Bump patch version and create git tag
|
||||
git tag "v$$NEW"; \
|
||||
echo "Bumped version: $$OLD -> $$NEW (tagged v$$NEW)"
|
||||
|
||||
push: ## Push commits and tags to origin
|
||||
git push origin main --tags
|
||||
push: ## Push commits and current tag to origin
|
||||
@TAG=$$(git describe --tags --exact-match 2>/dev/null); \
|
||||
git push origin main; \
|
||||
if [ -n "$$TAG" ]; then \
|
||||
echo "Pushing tag $$TAG..."; \
|
||||
git push origin "$$TAG"; \
|
||||
else \
|
||||
echo "No tag on current commit"; \
|
||||
fi
|
||||
|
||||
@@ -29,9 +29,15 @@ 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
|
||||
# These are responses to queries that shouldn't be displayed as text.
|
||||
# Matches complete DA1/DA2 responses like \x1b[?1;10;0c or \x1b[?64;1;2;...c
|
||||
DA_RESPONSE_PATTERN = re.compile(rb'\x1b\[\?[\d;]+c')
|
||||
|
||||
# Pattern to detect partial DA responses at end of data (incomplete escape sequence)
|
||||
# Matches: \x1b, \x1b[, \x1b[?, \x1b[?1, \x1b[?1;, \x1b[?1;10, etc.
|
||||
# These need to be held back until more data arrives to see if they complete
|
||||
DA_PARTIAL_PATTERN = re.compile(rb'\x1b(?:\[(?:\?[\d;]*)?)?$')
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DockerExecSpec:
|
||||
@@ -69,6 +75,8 @@ class DockerExecSession(Session):
|
||||
self._last_snapshot_counter = 0
|
||||
self._exec_id: str | None = None
|
||||
self._pending_output = b""
|
||||
# Buffer for handling escape sequences split across socket reads
|
||||
self._escape_buffer = b""
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
@@ -302,11 +310,25 @@ 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
|
||||
# Prepend any buffered partial escape sequence from previous read
|
||||
if self._escape_buffer:
|
||||
data = self._escape_buffer + data
|
||||
self._escape_buffer = b""
|
||||
|
||||
# Filter out complete DA1/DA2 responses (e.g., \x1b[?1;10;0c)
|
||||
data = DA_RESPONSE_PATTERN.sub(b'', data)
|
||||
if not data:
|
||||
continue
|
||||
|
||||
# Check for partial escape sequence at end that might be a DA response
|
||||
# Hold it back until we get more data to see if it completes
|
||||
match = DA_PARTIAL_PATTERN.search(data)
|
||||
if match:
|
||||
self._escape_buffer = data[match.start():]
|
||||
data = data[:match.start()]
|
||||
if not data:
|
||||
continue
|
||||
|
||||
await self._add_to_replay_buffer(data)
|
||||
await self._update_screen(data)
|
||||
if self._connector:
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -513,19 +513,31 @@ class WebTerminal {
|
||||
});
|
||||
|
||||
// Wait for fonts to load before fitting to ensure correct measurements
|
||||
//
|
||||
// FONT INITIALIZATION (ghostty-web):
|
||||
// -----------------------------------
|
||||
// The font stack is set in two places:
|
||||
// 1. At Terminal construction time via ITerminalOptions.fontFamily
|
||||
// - This sets the initial font for the renderer
|
||||
// 2. After web fonts load via terminal.loadFonts()
|
||||
// - This re-measures font metrics and triggers a full re-render
|
||||
//
|
||||
// The loadFonts() method (added in ghostty-web commit feab41f9a8e4491f):
|
||||
// - Calls renderer.remeasureFont() to recalculate cell dimensions
|
||||
// - Calls handleFontChange() to resize canvas and re-render
|
||||
//
|
||||
// DO NOT manually set terminal.options.fontFamily or call renderer methods
|
||||
// directly - use the public loadFonts() API which handles the full chain.
|
||||
//
|
||||
// See: https://github.com/rcarmo/ghostty-web/commit/feab41f9a8e4491f04688a6620974c3f7762a3d9
|
||||
this.waitForFonts().then(() => {
|
||||
console.log("[webterm:init] Fonts loaded, reapplying font family and fitting...");
|
||||
// IMPORTANT: Font updates require BOTH steps to work correctly:
|
||||
// 1. Set terminal.options.fontFamily - stores the font stack for future reference
|
||||
// 2. Call renderer.setFontFamily() + remeasureFont() - applies the font and recalculates metrics
|
||||
// Without step 1, the font stack is lost and defaults are used on re-render.
|
||||
// Without step 2, the renderer doesn't know about the new fonts.
|
||||
this.terminal.options.fontFamily = this.fontFamily;
|
||||
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");
|
||||
console.log("[webterm:init] Fonts loaded, triggering font reload...");
|
||||
// Use the public loadFonts() API which properly handles font re-measurement
|
||||
// and triggers handleFontChange() internally. This is the correct approach
|
||||
// per ghostty-web commit feab41f9a8e4491f04688a6620974c3f7762a3d9
|
||||
if (typeof (this.terminal as unknown as { loadFonts?: () => void }).loadFonts === "function") {
|
||||
(this.terminal as unknown as { loadFonts: () => void }).loadFonts();
|
||||
console.log("[webterm:init] terminal.loadFonts() called");
|
||||
}
|
||||
this.fit();
|
||||
console.log("[webterm:init] fit() completed");
|
||||
|
||||
Reference in New Issue
Block a user