From f8d3876c68ea7c1aa82d0e86861370e9a22d3999 Mon Sep 17 00:00:00 2001 From: Vassiliy Yegorov Date: Mon, 15 Jun 2026 10:08:59 +0700 Subject: [PATCH] =?UTF-8?q?fix(app):=20settings=20live-apply=20=E2=80=94?= =?UTF-8?q?=20font=20on=20open=20terminals,=20daemon=20uptime?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Font change now applies to already-open terminals: the WebGL renderer caches glyphs in a texture atlas keyed by the old font/size, so the live re-apply effect now calls webglAddon.clearTextureAtlas() (via a new ref) after updating fontFamily/fontSize, before refitting. - Daemon uptime now reflects a restart: the Settings daemon section ticks every second for live uptime, and Stop/Restart trigger an onReload callback that re-fetches health/status in App so a restarted daemon's new started_at_ms is shown instead of the stale value. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/src/App.tsx | 2 +- app/src/Settings.tsx | 14 ++++++++++---- app/src/TerminalView.tsx | 10 +++++++++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/app/src/App.tsx b/app/src/App.tsx index 600a539..528fbeb 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -170,7 +170,7 @@ export function App() { /> )} - {settingsOpen && config && setSettingsOpen(false)} />} + {settingsOpen && config && setSettingsOpen(false)} onReload={() => { void loadHealth(); void refresh(); }} />} {wizard && { setWizard(false); setActiveId(id); void refresh(); }} onCancel={() => setWizard(false)} />} {deleteTarget && ( void }) { +export function Settings({ config, health, onClose, onReload }: { config: ConfigView; health: DaemonHealth | null; onClose: () => void; onReload: () => void }) { const ref = useRef(null); useEffect(() => { ref.current?.focus(); }, []); @@ -57,7 +57,7 @@ export function Settings({ config, health, onClose }: { config: ConfigView; heal setShellLocal(e.target.value)} onBlur={() => void setConfig({ default_shell: shellLocal })} style={{ width: "100%", padding: 8, marginBottom: 18, background: COLORS.bgPanel, color: COLORS.textPrimary, border: `1px solid ${COLORS.borderStrong}`, borderRadius: 8 }} /> - + ); @@ -70,8 +70,14 @@ function fmtUptime(ms: number): string { return `${Math.floor(s / 3600)}h ${Math.floor((s % 3600) / 60)}m`; } -function DaemonSection({ health }: { health: DaemonHealth | null }) { +function DaemonSection({ health, onReload }: { health: DaemonHealth | null; onReload: () => void }) { const [confirm, setConfirm] = useState(null); + // Tick so uptime counts up live while the modal is open. + const [, setTick] = useState(0); + useEffect(() => { + const t = setInterval(() => setTick((n) => n + 1), 1000); + return () => clearInterval(t); + }, []); return (
Daemon
@@ -92,7 +98,7 @@ function DaemonSection({ health }: { health: DaemonHealth | null }) {
- diff --git a/app/src/TerminalView.tsx b/app/src/TerminalView.tsx index cc5817a..4b31dd2 100644 --- a/app/src/TerminalView.tsx +++ b/app/src/TerminalView.tsx @@ -22,6 +22,7 @@ export function TerminalView({ surfaceId, font, palette }: { surfaceId: string; const ref = useRef(null); const termRef = useRef(null); const fitRef = useRef(null); + const webglRef = useRef(null); useEffect(() => { if (!ref.current) return; @@ -39,7 +40,9 @@ export function TerminalView({ surfaceId, font, palette }: { surfaceId: string; termRef.current = term; try { - term.loadAddon(new WebglAddon()); + const webgl = new WebglAddon(); + term.loadAddon(webgl); + webglRef.current = webgl; } catch { // webgl unavailable → fall back to canvas/dom renderer silently } @@ -99,6 +102,7 @@ export function TerminalView({ surfaceId, font, palette }: { surfaceId: string; term.dispose(); termRef.current = null; fitRef.current = null; + webglRef.current = null; }; }, [surfaceId]); // eslint-disable-line react-hooks/exhaustive-deps @@ -110,6 +114,10 @@ export function TerminalView({ surfaceId, font, palette }: { surfaceId: string; if (font) { t.options.fontFamily = `'${font.family}', monospace`; t.options.fontSize = font.size; + // The WebGL renderer caches rasterized glyphs in a texture atlas keyed by + // the old font/size; without clearing it the grid keeps rendering stale + // glyphs after a font change. + webglRef.current?.clearTextureAtlas(); } if (palette) t.options.theme = xtermTheme(palette); requestAnimationFrame(() => { try { fitRef.current?.fit(); } catch { /* ignore */ } });