Restore workspace header controls and polish tab management

This commit is contained in:
Mohamed Boudra
2026-03-02 12:23:04 +07:00
parent 18d4df339f
commit c8cef9bec8
22 changed files with 1132 additions and 504 deletions

View File

@@ -383,8 +383,7 @@ const shouldRun = !process.env.CI;
await waitForCondition(() => sawExit, 10000);
const next = await ctx.client.listTerminals(cwd);
expect(next.terminals).toHaveLength(1);
expect(next.terminals[0].id).not.toBe(terminalId);
expect(next.terminals).toHaveLength(0);
unsubscribeExit();
rmSync(cwd, { recursive: true, force: true });

View File

@@ -200,13 +200,15 @@ describe("TerminalManager", () => {
expect(manager.getTerminal(id)).toBeUndefined();
});
it("removes cwd entry when last terminal is killed", async () => {
it("keeps cwd entry when last terminal is killed (but does not auto-recreate)", async () => {
manager = createTerminalManager();
const terminals = await manager.getTerminals("/tmp");
manager.killTerminal(terminals[0].id);
expect(manager.listDirectories()).not.toContain("/tmp");
expect(manager.listDirectories()).toContain("/tmp");
const remaining = await manager.getTerminals("/tmp");
expect(remaining).toHaveLength(0);
});
it("keeps cwd entry when other terminals remain", async () => {
@@ -239,8 +241,7 @@ describe("TerminalManager", () => {
expect(manager.getTerminal(exitedId)).toBeUndefined();
const remaining = await manager.getTerminals("/tmp");
expect(remaining).toHaveLength(1);
expect(remaining[0].id).not.toBe(exitedId);
expect(remaining).toHaveLength(0);
});
});
@@ -250,7 +251,7 @@ describe("TerminalManager", () => {
expect(manager.listDirectories()).toEqual([]);
});
it("returns all cwds with active terminals", async () => {
it("returns all cwds that have ever had terminals", async () => {
manager = createTerminalManager();
await manager.getTerminals("/tmp");
await manager.getTerminals("/home");

View File

@@ -35,6 +35,7 @@ export function createTerminalManager(): TerminalManager {
const terminalExitUnsubscribeById = new Map<string, () => void>();
const terminalsChangedListeners = new Set<TerminalsChangedListener>();
const defaultEnvByRootCwd = new Map<string, Record<string, string>>();
const knownDirectories = new Set<string>();
function assertAbsolutePath(cwd: string): void {
if (!cwd.startsWith("/")) {
@@ -134,8 +135,12 @@ export function createTerminalManager(): TerminalManager {
async getTerminals(cwd: string): Promise<TerminalSession[]> {
assertAbsolutePath(cwd);
let terminals = terminalsByCwd.get(cwd);
if (!terminals || terminals.length === 0) {
const terminals = terminalsByCwd.get(cwd);
if (terminals && terminals.length > 0) {
return terminals;
}
if (!knownDirectories.has(cwd)) {
const inheritedEnv = resolveDefaultEnvForCwd(cwd);
const session = registerSession(
await createTerminal({
@@ -144,11 +149,14 @@ export function createTerminalManager(): TerminalManager {
...(inheritedEnv ? { env: inheritedEnv } : {}),
})
);
terminals = [session];
terminalsByCwd.set(cwd, terminals);
const created = [session];
terminalsByCwd.set(cwd, created);
knownDirectories.add(cwd);
emitTerminalsChanged({ cwd });
return created;
}
return terminals;
return [];
},
async createTerminal(options: {
@@ -158,6 +166,7 @@ export function createTerminalManager(): TerminalManager {
}): Promise<TerminalSession> {
assertAbsolutePath(options.cwd);
knownDirectories.add(options.cwd);
const terminals = terminalsByCwd.get(options.cwd) ?? [];
const defaultName = `Terminal ${terminals.length + 1}`;
const inheritedEnv = resolveDefaultEnvForCwd(options.cwd);
@@ -194,13 +203,14 @@ export function createTerminalManager(): TerminalManager {
},
listDirectories(): string[] {
return Array.from(terminalsByCwd.keys());
return Array.from(knownDirectories);
},
killAll(): void {
for (const id of Array.from(terminalsById.keys())) {
removeSessionById(id, { kill: true });
}
knownDirectories.clear();
},
subscribeTerminalsChanged(listener: TerminalsChangedListener): () => void {