From 66d981bb2230f3ec891e13f7d1d722cae68e11a4 Mon Sep 17 00:00:00 2001 From: Bailey Pumfleet Date: Thu, 15 Oct 2026 12:37:03 -0500 Subject: [PATCH] fix(app): a focused terminal no longer shows a tall browser caret at its left edge ghostty-web makes the terminal's host contenteditable and focuses it, so the browser drew its own text caret there, as tall as the canvas. The host now has a transparent caret-color; the terminal draws its own cursor. Co-Authored-By: Claude Opus 5.5 --- app/e2e/terminal-keys.spec.ts | 17 +++++++++++++++++ app/src/lib/terminal.ts | 4 ++++ docs/changelog.mdx | 6 +++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/app/e2e/terminal-keys.spec.ts b/app/e2e/terminal-keys.spec.ts index 174ac60f..322ae526 100644 --- a/app/e2e/terminal-keys.spec.ts +++ b/app/e2e/terminal-keys.spec.ts @@ -48,3 +48,20 @@ for (const renderer of ["ghostty", "xterm"] as const) { await expect.poll(() => sent(page)).toBe("\x1b[13;2u\x1bb\x1bf\x01\x05\x15\r"); }); } + +// ghostty-web makes the terminal's host contenteditable and focuses it, so +// without caret-color the browser blinks its own caret, the canvas's height, +// at the left edge beside the terminal's cursor. +test("ghostty: a focused terminal shows no browser caret", async ({ app }) => { + mockOnly("the shell is on the mock's gpu box"); + await app.open({ prefs: { terminal: { renderer: "ghostty" } } }); + const page = app.page; + await openShell(page); + const caret = () => + page.evaluate(() => { + const a = document.activeElement as HTMLElement | null; + const host = a?.closest("[contenteditable=true]"); + return host ? getComputedStyle(host).caretColor : "no editable host"; + }); + await expect.poll(caret).toBe("rgba(0, 0, 0, 0)"); +}); diff --git a/app/src/lib/terminal.ts b/app/src/lib/terminal.ts index 0cfac1d5..56c009c4 100644 --- a/app/src/lib/terminal.ts +++ b/app/src/lib/terminal.ts @@ -523,6 +523,10 @@ async function createGhostty(host: HTMLElement, colors: TerminalColors, prefs: T hung.stop(); delete (t as { focus?: unknown }).focus; } + // open() makes the host contenteditable and focus() focuses the host, so + // the browser blinked its own text caret there, as tall as the canvas, at + // the terminal's left edge. The terminal draws its own cursor. + host.style.caretColor = "transparent"; keepLastColumn(t); oneReadPerFrame(t); const frames = drawOnDemand(t, host, prefs.cursorBlink); diff --git a/docs/changelog.mdx b/docs/changelog.mdx index f9d50dc8..ffa1c4a6 100644 --- a/docs/changelog.mdx +++ b/docs/changelog.mdx @@ -9,7 +9,11 @@ upgrades with `berth upgrade BOX`, or by running the [install command](/getting-started/add-a-box#with-the-install-command) again. - +## Unreleased +- **No more tall blinking bar at a terminal's left edge.** With the Ghostty + renderer, a focused terminal showed the browser's own text cursor down its + whole height, next to the terminal's real cursor. Only the real one shows + now. ## v0.3.22 - **Mac terminal keys work in terminals.** ⇧↵ starts a new line in Claude