Reduce terminal and dashboard memory footprint
Frontend terminal: - Share single TextDecoder across all instances (was one per terminal) - Write binary WS frames as Uint8Array directly to ghostty-web, avoiding intermediate string allocation from TextDecoder.decode() - Reduce default scrollback to 200 on mobile (was 1000; Safari mobile has ~300MB budget across all tabs) - Pause heartbeat watchdog when tab is hidden to reduce WS traffic and message queue growth for background terminals Dashboard screenshots: - Revoke old object URL before creating new one (reduces peak memory by not having two decoded bitmaps alive simultaneously) - Re-lookup card from cardsBySlug in fetch callback to avoid orphaned blob URLs when renderTiles() runs during in-flight requests - Remove thumbnailCache writes (was redundant with activeObjectURLBySlug) Server: - Halve replay buffer from 256KB to 128KB per session
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@ package webterm
|
||||
|
||||
import "sync"
|
||||
|
||||
const replayBufferSize = 256 * 1024
|
||||
const replayBufferSize = 128 * 1024
|
||||
|
||||
type ReplayBuffer struct {
|
||||
mu sync.Mutex
|
||||
|
||||
+4
-6
@@ -1270,7 +1270,6 @@ func (s *LocalServer) handleRoot(w http.ResponseWriter, r *http.Request) {
|
||||
return;
|
||||
}
|
||||
screenshotRequestInFlight = true;
|
||||
const img = card.img;
|
||||
const url = '/screenshot.svg?route_key=' + encodeURIComponent(slug);
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 5000);
|
||||
@@ -1290,14 +1289,13 @@ func (s *LocalServer) handleRoot(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
.then((blob) => {
|
||||
if (!blob) return;
|
||||
const currentCard = cardsBySlug[slug];
|
||||
if (!currentCard || !currentCard.img) return;
|
||||
const previous = activeObjectURLBySlug[slug];
|
||||
if (previous) URL.revokeObjectURL(previous);
|
||||
const objectURL = URL.createObjectURL(blob);
|
||||
activeObjectURLBySlug[slug] = objectURL;
|
||||
img.src = objectURL;
|
||||
thumbnailCache[slug] = { src: objectURL, updatedAt: Date.now() };
|
||||
if (previous) {
|
||||
URL.revokeObjectURL(previous);
|
||||
}
|
||||
currentCard.img.src = objectURL;
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -26,6 +26,9 @@ async function getSharedGhostty(): Promise<Ghostty> {
|
||||
return sharedGhostty;
|
||||
}
|
||||
|
||||
/** Shared TextDecoder (stateless for UTF-8, safe to share) */
|
||||
const sharedTextDecoder = new TextDecoder();
|
||||
|
||||
/** Default font stack - prefers system monospace, falls back through programming fonts */
|
||||
const DEFAULT_FONT_FAMILY =
|
||||
'ui-monospace, "SFMono-Regular", "FiraCode Nerd Font", "FiraMono Nerd Font", ' +
|
||||
@@ -660,7 +663,6 @@ class WebTerminal {
|
||||
private terminal: Terminal;
|
||||
private fitAddon: FitAddon;
|
||||
private socket: WebSocket | null = null;
|
||||
private readonly textDecoder = new TextDecoder();
|
||||
private element: HTMLElement;
|
||||
private wsUrl: string;
|
||||
private reconnectAttempts = 0;
|
||||
@@ -741,10 +743,11 @@ class WebTerminal {
|
||||
const fontFamily = config.fontFamily?.trim() || DEFAULT_FONT_FAMILY;
|
||||
const fontSize = config.fontSize ?? 16;
|
||||
|
||||
const defaultScrollback = isMobileDevice() ? 200 : 1000;
|
||||
const options: ITerminalOptions = {
|
||||
fontFamily,
|
||||
fontSize,
|
||||
scrollback: config.scrollback ?? 1000,
|
||||
scrollback: config.scrollback ?? defaultScrollback,
|
||||
cursorBlink: true,
|
||||
cursorStyle: "block",
|
||||
theme: themeToUse,
|
||||
@@ -905,7 +908,12 @@ class WebTerminal {
|
||||
|
||||
// Focus terminal when returning to the tab
|
||||
this.addTrackedListener(document, "visibilitychange", () => {
|
||||
if (!document.hidden) {
|
||||
if (document.hidden) {
|
||||
this.stopHeartbeatWatchdog();
|
||||
} else {
|
||||
if (this.socket?.readyState === WebSocket.OPEN) {
|
||||
this.startHeartbeatWatchdog();
|
||||
}
|
||||
restoreFocus();
|
||||
}
|
||||
});
|
||||
@@ -1634,9 +1642,8 @@ class WebTerminal {
|
||||
private handleMessage(data: string | ArrayBuffer | Blob): void {
|
||||
this.lastMessageAt = Date.now();
|
||||
if (data instanceof ArrayBuffer) {
|
||||
// Binary data - write directly to terminal
|
||||
const text = this.textDecoder.decode(data);
|
||||
this.terminal.write(text);
|
||||
// Binary data - write directly to terminal as Uint8Array (avoids string allocation)
|
||||
this.terminal.write(new Uint8Array(data));
|
||||
return;
|
||||
}
|
||||
if (data instanceof Blob) {
|
||||
|
||||
Reference in New Issue
Block a user