diff --git a/docs/ceilings.md b/docs/ceilings.md index 7e0ac466..c0f17cfa 100644 --- a/docs/ceilings.md +++ b/docs/ceilings.md @@ -1002,6 +1002,12 @@ work when nobody knows it and that the call site never shows. The mechanism and sibling covers keeps its pin. Notes not sent yet live in the window: closing it, or the browser, loses them. +- **On macOS, a JavaScript dialog a page opens before its first paint leaves the pane black behind the dialog's + card** (`internal/browser/chrome_view.go`): headless Chrome there paints a page some time after its load, and + a page held by a dialog cannot paint, so a screencast started then has no frame to send until the dialog is + answered (Chrome 155 on the macos-latest runner, measured). A dialog opened once the page has been drawn, as + one an agent's click opens, shows the page behind it. Linux and Windows paint before the load returns. + - **A session browser's downloads go straight to the user's Downloads folder** (`internal/browser/downloads.go`, `session_downloads.go`, `docs/browser-tab.md`): `XDG_DOWNLOAD_DIR` on Linux, `~/Downloads` on macOS and Windows, even for a Windows user who moved that known folder. Chrome writes each one under its download id into a staging diff --git a/internal/browser/dialogs_live_test.go b/internal/browser/dialogs_live_test.go index 2ed3c67f..ff42103a 100644 --- a/internal/browser/dialogs_live_test.go +++ b/internal/browser/dialogs_live_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/chromedp/cdproto/page" "github.com/chromedp/chromedp" "github.com/lichdotdev/lich/api" "github.com/lichdotdev/lich/internal/chromium" @@ -105,6 +106,16 @@ func TestChromeAPaneOpenedOnADialogShowsThePage(t *testing.T) { if err := p.Navigate(ctx, srv.URL); err != nil { t.Fatal(err) } + // The page is drawn once first, as a page the agent clicked on was: + // headless Chrome on macOS has not painted it when Navigate returns, and a + // dialog held open before its first paint leaves no frame to cast + // (Chrome 155, macos-latest runner, measured 2026-10-28). + if err := p.runFree(ctx, chromedp.ActionFunc(func(ctx context.Context) error { + _, err := page.CaptureScreenshot().Do(ctx) + return err + })); err != nil { + t.Fatal(err) + } if err := p.runFree(ctx, chromedp.Evaluate(`setTimeout(() => confirm('Delete?'), 0)`, nil)); err != nil { t.Fatal(err) }