fix(desktop): retain terminal focus after tab actions

Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz>
Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz>
This commit is contained in:
npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf
2026-08-02 21:39:12 -04:00
parent 1350422539
commit 050cfc89e3
2 changed files with 34 additions and 3 deletions

View File

@@ -159,6 +159,30 @@ test("mounted IME paths neither toggle nor emit preedit text", async () => {
assert.deepEqual(calls.input, ["か"]);
});
test("tab actions restore terminal input focus", async () => {
const { view } = fixture();
await ready(view);
toggleChord();
const input = view.getByLabelText("Terminal input");
await waitFor(() => assert.equal(document.activeElement, input));
const actions = [
["select", view.getByRole("tab")],
["close", view.getByLabelText("Close SHELL")],
["new", view.getByLabelText("New Buzz Term tab")],
];
for (const [label, target] of actions) {
target.focus();
assert.equal(document.activeElement, target, `${label} takes focus`);
fireEvent.click(target);
assert.equal(
document.activeElement,
input,
`${label} restores terminal focus`,
);
}
});
const EMPTY_FRAME = {
cursor: { column: 0, line: 0, visible: false },
full: false,

View File

@@ -391,6 +391,13 @@ export function TerminalSubstrate({
gridRef.current?.paint(context, TERMINAL_CELL_METRICS, terminalPalette);
}, [activeSessionId, cursorPainted, frames, terminalPalette]);
const runTabAction = (action: () => void) => {
action();
if (owner === "terminal") {
textareaRef.current?.focus({ preventScroll: true });
}
};
return (
<section
aria-label="Buzz Term"
@@ -431,7 +438,7 @@ export function TerminalSubstrate({
aria-selected={session.active}
className="buzz-terminal-tab-select"
disabled={session.closing}
onClick={() => onSelectSession(session.id)}
onClick={() => runTabAction(() => onSelectSession(session.id))}
role="tab"
type="button"
>
@@ -444,7 +451,7 @@ export function TerminalSubstrate({
aria-label={`Close ${session.title}`}
className="buzz-terminal-close"
disabled={session.closing}
onClick={() => onCloseSession(session.id)}
onClick={() => runTabAction(() => onCloseSession(session.id))}
type="button"
>
×
@@ -454,7 +461,7 @@ export function TerminalSubstrate({
<button
aria-label="New Buzz Term tab"
className="buzz-terminal-new-tab"
onClick={onNewSession}
onClick={() => runTabAction(onNewSession)}
type="button"
>
+