fix(maestro): keep the keyboard in the list while previewing

Two mounts were taking focus out of the sidebar mid-walk: the render that
put the multi-view on screen before the preview applied, and the render
that restores the previous pane when a preview ends. Both now mount
without focusing, so j/k keeps walking. Also marks the preview with a
badge on the canvas and drops the close button from its tab.
This commit is contained in:
Miguel Palhas
2026-09-03 10:52:43 +01:00
parent 6635cff46e
commit 2319b4e47b
3 changed files with 45 additions and 7 deletions
+20 -5
View File
@@ -110,6 +110,20 @@ export function createMultiView(opts: MultiViewOptions): MultiView {
tempTabId: string | null;
}
let preview: Preview | null = null;
/// Set while a preview-driven render runs: ending a preview re-mounts the
/// pane that was there, and that mount must not pull the keyboard out of the
/// sidebar the operator is still walking.
let quietRender = false;
/** Render without any mount taking the keyboard. */
function renderQuietly(): void {
quietRender = true;
try {
render();
} finally {
quietRender = false;
}
}
const isTempTab = (tabId: string) => preview?.tempTabId === tabId;
@@ -152,7 +166,7 @@ export function createMultiView(opts: MultiViewOptions): MultiView {
function previewSession(sessionId: string | null): void {
if (preview?.sessionId === sessionId) return;
if (sessionId === null) {
if (restorePreview()) render();
if (restorePreview()) renderQuietly();
return;
}
restorePreview();
@@ -176,7 +190,7 @@ export function createMultiView(opts: MultiViewOptions): MultiView {
layout.activeTabId = tab.id;
preview = { sessionId, prevActiveTabId, prevFocus: null, tempTabId: tab.id };
}
render();
renderQuietly();
}
/** Any user action that changes the layout for real ends the preview first,
@@ -385,7 +399,8 @@ export function createMultiView(opts: MultiViewOptions): MultiView {
}
render();
});
if (layout.tabs.length > 1) {
// A preview tab is not the operator's to close: it goes on its own.
if (layout.tabs.length > 1 && !isTempTab(t.id)) {
const close = document.createElement("span");
close.className = "mv-tab-close";
close.textContent = "✕";
@@ -555,7 +570,7 @@ export function createMultiView(opts: MultiViewOptions): MultiView {
// terminal, or j/k would stop walking the list after the first step.
pool.setVisible(
hosts.map((h) => ({ id: h.id, pane: h.host, info: h.info })),
{ focus: !preview },
{ focus: !preview && !quietRender },
);
syncFocus();
persist();
@@ -579,7 +594,7 @@ export function createMultiView(opts: MultiViewOptions): MultiView {
// Only the focused leaf's session is live on mobile; the rest park.
pool.setVisible(
focused.sessionId ? [{ id: focused.sessionId, pane: pane.host, info: infoFor(focused.sessionId) }] : [],
{ focus: !preview },
{ focus: !preview && !quietRender },
);
if (!preview) pool.setFocus(focused.sessionId ?? null);
sidebar.setHighlights({ open: realOpenSessions(), focused: focused.sessionId ?? null });