fix(tiles): the count menu closes when the keyboard leaves it

Found live: closing the grid with a click starts the single view's
selection, which focuses its terminal when its replay lands, a few
hundred milliseconds later. A right-click on Tiles in between opened the
count menu with the keyboard in it, and the late focus then moved the
keyboard into the terminal while the menu stayed open (3 of 3 tries), so
the arrows, Enter or Escape meant for the menu went to the session's
PTY instead (an Escape arrived there as an ESC byte).

The menu now closes when the keyboard leaves it for another element, as
any menu does. A focus going nowhere (a click on a button in Safari,
which does not focus it) does not count, so a click on a count still
picks it. Live afterwards: the menu either closes as the terminal takes
the keyboard, or keeps it when the replay landed first; never open with
the keyboard elsewhere.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-10-08 05:13:43 +02:00
parent 4ad283c647
commit fcec77131c
4 changed files with 32 additions and 2 deletions
+18
View File
@@ -259,6 +259,24 @@ describe('the count menu', () => {
expect(menu()).toBeNull();
});
it('closes when the keyboard leaves it for something else (a late focus of the single view), not for nothing', () => {
const app = gridApp();
open(app);
// Inside: moving between counts keeps it.
menu()!.dispatch('focusout', { relatedTarget: item(2) });
expect(menu()).not.toBeNull();
// Nowhere (a click on a button in Safari focuses nothing): kept.
menu()!.dispatch('focusout', { relatedTarget: null });
expect(menu()).not.toBeNull();
// Another element, the single view's terminal: closed, the keyboard left there.
const textarea = new FakeEl();
textarea.className = 'xterm-helper-textarea';
textarea.focus();
menu()!.dispatch('focusout', { relatedTarget: textarea });
expect(menu()).toBeNull();
expect(activeElement()).toBe(textarea);
});
it('a second right-click closes it; a click on Tiles closes it and toggles', () => {
const app = gridApp();
open(app);