From a670460db40cf802c9ffad4b3328dd8a5df2410c Mon Sep 17 00:00:00 2001 From: Matias Palma Date: Sat, 18 Apr 2026 22:07:34 -0400 Subject: [PATCH] fix: spawn pty with actual terminal size to avoid initial reflow --- apps/desktop/src-tauri/src/pty.rs | 6 ++++-- apps/desktop/src/api/tauri.ts | 2 +- apps/desktop/src/components/Terminal.tsx | 21 ++++++++++++++++++--- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/apps/desktop/src-tauri/src/pty.rs b/apps/desktop/src-tauri/src/pty.rs index d367aaec..f6078f12 100644 --- a/apps/desktop/src-tauri/src/pty.rs +++ b/apps/desktop/src-tauri/src/pty.rs @@ -16,6 +16,8 @@ pub fn spawn_pty( app: AppHandle, state: tauri::State<'_, Arc>>>, cwd: Option, + rows: Option, + cols: Option, ) -> Result<(), String> { // If session exists, it will be replaced (dropping old resources kills the previous PTY) @@ -51,8 +53,8 @@ pub fn spawn_pty( let pair = pty_system .openpty(PtySize { - rows: 24, - cols: 80, + rows: rows.unwrap_or(24), + cols: cols.unwrap_or(80), pixel_width: 0, pixel_height: 0, }) diff --git a/apps/desktop/src/api/tauri.ts b/apps/desktop/src/api/tauri.ts index 3bee5584..c24afe88 100644 --- a/apps/desktop/src/api/tauri.ts +++ b/apps/desktop/src/api/tauri.ts @@ -43,7 +43,7 @@ export interface TauriInvokeMap { "ollama_proxy": { args: { method: string; url: string; body: OllamaRequest }; return: { status: number; body: string } }; "check_update": { args: { url: string }; return: { version: string; body?: string | null } | null }; "install_update": { args: { url: string }; return: void }; - "spawn_pty": { args: { cwd?: string }; return: void }; + "spawn_pty": { args: { cwd?: string; rows?: number; cols?: number }; return: void }; "write_to_pty": { args: { data: string }; return: void }; "resize_pty": { args: { rows: number; cols: number }; return: void }; "kill_pty": { args: undefined; return: void }; diff --git a/apps/desktop/src/components/Terminal.tsx b/apps/desktop/src/components/Terminal.tsx index 2a2dcbec..49d2be3b 100644 --- a/apps/desktop/src/components/Terminal.tsx +++ b/apps/desktop/src/components/Terminal.tsx @@ -14,6 +14,7 @@ const Terminal: React.FC = () => { const { t } = useL10n(); const terminalRef = useRef(null); const xtermRef = useRef(null); + const fitAddonRef = useRef(null); const initialized = useRef(false); const activePtyPath = useRef(undefined); // tracks last spawned path @@ -59,6 +60,7 @@ const Terminal: React.FC = () => { term.loadAddon(fitAddon); term.open(terminalRef.current); xtermRef.current = term; + fitAddonRef.current = fitAddon; term.onData((data) => { invoke("write_to_pty", { data }).catch(e => console.error("PTY write error:", e)); @@ -85,6 +87,7 @@ const Terminal: React.FC = () => { resizeObserver.disconnect(); term.dispose(); xtermRef.current = null; + fitAddonRef.current = null; }; }, []); @@ -111,14 +114,26 @@ const Terminal: React.FC = () => { xtermRef.current.write(event.payload); } }); - + if (isCanceled) { u(); return; } - + unlisten = u; - await invoke("spawn_pty", { cwd: targetPath }); + + // Fit the terminal to its container before spawning so the shell + // starts with the correct rows/cols and avoids a reflow on the first prompt. + const term = xtermRef.current; + if (term && fitAddonRef.current && terminalRef.current?.clientWidth) { + fitAddonRef.current.fit(); + } + + await invoke("spawn_pty", { + cwd: targetPath, + rows: term?.rows, + cols: term?.cols, + }); } catch (err) { if (!isCanceled && xtermRef.current) { xtermRef.current.writeln("\x1b[31m" + t('terminal.error_connect') + "\x1b[0m " + err);