authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-06-19 23:48:41-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-06-19 23:48:41-07:00
logdc82f792c3fc43a06edca916a1da297af714600a
treec004adab2ae56e5cd33d01f5723a316b75ba552e
parent335e0b59ea8a1bbc381402d1fbea268e05095f74
signature Signed by PGP key B5690EEEBB952194

[codex] Close right panel when its last tab closes (#3221)

Co-authored-by: Julius Marminge <julius@mac.lan>

2 files changed, 17 insertions(+), 9 deletions(-)

apps/web/src/rightPanelStore.test.ts+6-6
...@@ -338,12 +338,12 @@ describe("rightPanelStore", () => {...@@ -338,12 +338,12 @@ describe("rightPanelStore", () => {
338 });338 });
339 });339 });
340340
341 it("closing the final terminal pane removes its surface but keeps the panel open", () => {341 it("closing the final terminal pane removes its surface and closes the panel", () => {
342 useRightPanelStore.getState().openTerminal(refA, "term-1");342 useRightPanelStore.getState().openTerminal(refA, "term-1");
343 useRightPanelStore.getState().closeTerminal(refA, "terminal:term-1", "term-1");343 useRightPanelStore.getState().closeTerminal(refA, "terminal:term-1", "term-1");
344344
345 expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({345 expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({
346 isOpen: true,346 isOpen: false,
347 activeSurfaceId: null,347 activeSurfaceId: null,
348 surfaces: [],348 surfaces: [],
349 });349 });
...@@ -359,12 +359,12 @@ describe("rightPanelStore", () => {...@@ -359,12 +359,12 @@ describe("rightPanelStore", () => {
359 );359 );
360 });360 });
361361
362 it("closing the final surface leaves the panel open and empty", () => {362 it("closing the final surface closes the panel", () => {
363 useRightPanelStore.getState().openTerminal(refA, "term-1");363 useRightPanelStore.getState().openTerminal(refA, "term-1");
364 useRightPanelStore.getState().closeSurface(refA, "terminal:term-1");364 useRightPanelStore.getState().closeSurface(refA, "terminal:term-1");
365365
366 expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({366 expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({
367 isOpen: true,367 isOpen: false,
368 activeSurfaceId: null,368 activeSurfaceId: null,
369 surfaces: [],369 surfaces: [],
370 });370 });
...@@ -406,14 +406,14 @@ describe("rightPanelStore", () => {...@@ -406,14 +406,14 @@ describe("rightPanelStore", () => {
406 });406 });
407 });407 });
408408
409 it("closing all surfaces leaves the panel open and empty", () => {409 it("closing all surfaces closes the panel", () => {
410 useRightPanelStore.getState().openBrowser(refA, "tab-a");410 useRightPanelStore.getState().openBrowser(refA, "tab-a");
411 useRightPanelStore.getState().openFile(refA, "src/index.ts");411 useRightPanelStore.getState().openFile(refA, "src/index.ts");
412412
413 useRightPanelStore.getState().closeAllSurfaces(refA);413 useRightPanelStore.getState().closeAllSurfaces(refA);
414414
415 expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({415 expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({
416 isOpen: true,416 isOpen: false,
417 activeSurfaceId: null,417 activeSurfaceId: null,
418 surfaces: [],418 surfaces: [],
419 });419 });
apps/web/src/rightPanelStore.ts+11-3
...@@ -340,6 +340,7 @@ export const useRightPanelStore = create<RightPanelStoreState>()(...@@ -340,6 +340,7 @@ export const useRightPanelStore = create<RightPanelStoreState>()(
340 const fallback = surfaces[Math.min(index, surfaces.length - 1)] ?? null;340 const fallback = surfaces[Math.min(index, surfaces.length - 1)] ?? null;
341 return {341 return {
342 ...current,342 ...current,
343 isOpen: surfaces.length > 0 && current.isOpen,
343 surfaces,344 surfaces,
344 activeSurfaceId:345 activeSurfaceId:
345 current.activeSurfaceId === surfaceId346 current.activeSurfaceId === surfaceId
...@@ -378,9 +379,16 @@ export const useRightPanelStore = create<RightPanelStoreState>()(...@@ -378,9 +379,16 @@ export const useRightPanelStore = create<RightPanelStoreState>()(
378 const index = current.surfaces.findIndex((surface) => surface.id === surfaceId);379 const index = current.surfaces.findIndex((surface) => surface.id === surfaceId);
379 if (index < 0) return current;380 if (index < 0) return current;
380 const surfaces = current.surfaces.filter((surface) => surface.id !== surfaceId);381 const surfaces = current.surfaces.filter((surface) => surface.id !== surfaceId);
381 if (current.activeSurfaceId !== surfaceId) return { ...current, surfaces };382 if (current.activeSurfaceId !== surfaceId) {
383 return { ...current, isOpen: surfaces.length > 0 && current.isOpen, surfaces };
384 }
382 const fallback = surfaces[Math.min(index, surfaces.length - 1)] ?? null;385 const fallback = surfaces[Math.min(index, surfaces.length - 1)] ?? null;
383 return { ...current, surfaces, activeSurfaceId: fallback?.id ?? null };386 return {
387 ...current,
388 isOpen: surfaces.length > 0 && current.isOpen,
389 surfaces,
390 activeSurfaceId: fallback?.id ?? null,
391 };
384 }),392 }),
385 })),393 })),
386 closeOtherSurfaces: (ref, surfaceId) =>394 closeOtherSurfaces: (ref, surfaceId) =>
...@@ -417,7 +425,7 @@ export const useRightPanelStore = create<RightPanelStoreState>()(...@@ -417,7 +425,7 @@ export const useRightPanelStore = create<RightPanelStoreState>()(
417 byThreadKey: updateThread(state.byThreadKey, scopedThreadKey(ref), (current) =>425 byThreadKey: updateThread(state.byThreadKey, scopedThreadKey(ref), (current) =>
418 current.surfaces.length === 0426 current.surfaces.length === 0
419 ? current427 ? current
420 : { ...current, isOpen: true, surfaces: [], activeSurfaceId: null },428 : { ...current, isOpen: false, surfaces: [], activeSurfaceId: null },
421 ),429 ),
422 })),430 })),
423 reconcileBrowserSurfaces: (ref, tabIds) =>431 reconcileBrowserSurfaces: (ref, tabIds) =>