perf(workspace): switching workspaces keeps their terminals mounted
Panes were keyed by slot alone, so slot 0 of the next workspace reused the previous one's pane: every switch disposed its terminals and attached new ones (a WebSocket each, the whole screen replayed). The live panes of the last 4 workspaces left now stay mounted as hidden siblings in the same grid, keyed by paneKey (lane x 10 + slot), so going back is instant. Measured on 3 workspaces x 2-3 shell panes, switching back to one already opened: script 210 -> 52 ms, main thread 370 -> 120 ms, long tasks 120 -> 0 ms, terminal re-attaches 2-3 -> 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -675,6 +675,56 @@ describe("console tabs", () => {
|
||||
expect(screen.getByTestId("console-tab-new")).toBeDisabled();
|
||||
});
|
||||
|
||||
// Switching workspaces used to hand slot 0 of the new workspace the SAME
|
||||
// pane as the old one's slot 0: its terminal was disposed and a new one
|
||||
// attached, replaying the whole screen — the lag of every switch.
|
||||
it("keeps a workspace's terminals mounted (hidden) while another workspace is shown", async () => {
|
||||
withRuns([live(0), { ...live(0), id: "agenthub-lane-2", laneId: 2, slot: 0 }]);
|
||||
function Go() {
|
||||
const [, setParams] = useSearchParams();
|
||||
return (
|
||||
<>
|
||||
{[1, 2].map((id) => (
|
||||
<button
|
||||
key={id}
|
||||
data-testid={`go-${id}`}
|
||||
onClick={() => setParams({ lane: String(id), run: `agenthub-lane-${id}` })}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
render(
|
||||
<MemoryRouter initialEntries={["/workspace?lane=1&run=agenthub-lane-1"]}>
|
||||
<Go />
|
||||
<Workspace />
|
||||
</MemoryRouter>
|
||||
);
|
||||
await settle();
|
||||
await settle();
|
||||
const term = (id: string) =>
|
||||
screen.getAllByTestId("terminal-view").find((el) => el.dataset.runId === id);
|
||||
const first = term("agenthub-lane-1");
|
||||
expect(first).toBeDefined();
|
||||
const user = userEvent.setup();
|
||||
await user.click(screen.getByTestId("go-2"));
|
||||
await settle();
|
||||
const second = term("agenthub-lane-2");
|
||||
expect(second?.closest(".hidden")).toBeNull();
|
||||
// Lane 1's terminal is the same element, only hidden.
|
||||
expect(term("agenthub-lane-1")).toBe(first);
|
||||
expect(first!.closest(".hidden")).not.toBeNull();
|
||||
await user.click(screen.getByTestId("go-1"));
|
||||
await settle();
|
||||
expect(term("agenthub-lane-1")).toBe(first);
|
||||
expect(first!.closest(".hidden")).toBeNull();
|
||||
expect(term("agenthub-lane-2")).toBe(second);
|
||||
// Only the shown workspace's tabs are in the tab bar.
|
||||
expect(
|
||||
screen.getAllByRole("tab").filter((t) => t.dataset.testid?.startsWith("console-tab-"))
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("with tabs from every workspace on, shows another workspace's session and selects it on click", async () => {
|
||||
window.localStorage.setItem("agenthub.workspace.allTabs", "1");
|
||||
withRuns([live(0), { ...live(0), id: "agenthub-lane-2-1", laneId: 2, slot: 1 }]);
|
||||
|
||||
Reference in New Issue
Block a user