From 2d490c6fcab94ded5e0c14eba5d782d4b7ef1d5d Mon Sep 17 00:00:00 2001 From: murashit Date: Mon, 22 Jun 2026 15:03:03 +0900 Subject: [PATCH] Represent restored panel presence in threads view --- src/features/threads-view/state.ts | 10 +++----- src/styles/40-threads-view.css | 10 -------- src/workspace/panel-coordinator.ts | 25 +++++++++++++++++--- tests/features/threads-view/renderer.test.ts | 4 ++-- tests/main.test.ts | 18 ++++++++++++++ 5 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/features/threads-view/state.ts b/src/features/threads-view/state.ts index c704e58a..a41617c2 100644 --- a/src/features/threads-view/state.ts +++ b/src/features/threads-view/state.ts @@ -11,7 +11,7 @@ import { type ThreadRenameLifecycleState as SharedThreadRenameLifecycleState, } from "../threads/rename-lifecycle"; -type ThreadsLiveStatus = "pending" | "running" | "draft" | "offline" | "open"; +type ThreadsLiveStatus = "pending" | "running" | "open"; interface ThreadsLiveState { status: ThreadsLiveStatus; @@ -33,10 +33,8 @@ export type ThreadsRenameLifecycleEvent = | { type: "auto-name-finished"; generatingState: ThreadsGeneratingRenameState }; const STATUS_PRIORITY: Record = { - pending: 4, - running: 3, - draft: 2, - offline: 1, + pending: 2, + running: 1, open: 0, }; @@ -127,7 +125,5 @@ function snapshotsForThreads(snapshots: OpenCodexPanelSnapshot[]): Map - leaf.view instanceof CodexChatView ? [this.openPanelSnapshotWithFocus(workspacePanelSurface(leaf.view).openPanelSnapshot())] : [], - ); + return leaves.flatMap((leaf, index) => { + if (leaf.view instanceof CodexChatView) + return [this.openPanelSnapshotWithFocus(workspacePanelSurface(leaf.view).openPanelSnapshot())]; + const restoredSnapshot = restoredPanelSnapshot(leaf, index); + return restoredSnapshot ? [restoredSnapshot] : []; + }); } async focusOpenPanel(viewId: string, threadId: string | null = null): Promise { @@ -438,3 +441,19 @@ function restoredThreadId(leaf: WorkspaceLeaf): string | null { const threadId = (state as { threadId?: unknown }).threadId; return typeof threadId === "string" && threadId.length > 0 ? threadId : null; } + +function restoredPanelSnapshot(leaf: WorkspaceLeaf, index: number): OpenCodexPanelSnapshot | null { + const threadId = restoredThreadId(leaf); + if (!threadId) return null; + return { + viewId: `restored:${String(index)}:${threadId}`, + threadId, + turnLifecycle: { kind: "idle" }, + pendingApprovals: 0, + pendingUserInputs: 0, + pendingMcpElicitations: 0, + hasComposerDraft: false, + connected: false, + lastFocused: false, + }; +} diff --git a/tests/features/threads-view/renderer.test.ts b/tests/features/threads-view/renderer.test.ts index e9042522..faef7210 100644 --- a/tests/features/threads-view/renderer.test.ts +++ b/tests/features/threads-view/renderer.test.ts @@ -104,7 +104,7 @@ describe("threads view renderer decisions", () => { new Map(), )[0]?.live, ).toMatchObject({ - status: "draft", + status: "open", }); expect( threadRows( @@ -113,7 +113,7 @@ describe("threads view renderer decisions", () => { new Map(), )[0]?.live, ).toMatchObject({ - status: "offline", + status: "open", }); expect( threadRows( diff --git a/tests/main.test.ts b/tests/main.test.ts index 4c6879b6..34fe16d6 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -99,6 +99,24 @@ describe("CodexPanelPlugin workspace panel reconciliation", () => { expect(restoredLeaf.view).toBeInstanceOf(CodexChatView); }); + it("includes deferred restored panels in open panel snapshots", async () => { + const restoredLeaf = leaf({ state: { threadId: "thread-1", threadTitle: "Restored thread" } }); + const plugin = await pluginWithLeaves([restoredLeaf]); + + expect(panels(plugin).getOpenPanelSnapshots()).toMatchObject([ + { + threadId: "thread-1", + turnLifecycle: { kind: "idle" }, + pendingApprovals: 0, + pendingUserInputs: 0, + pendingMcpElicitations: 0, + hasComposerDraft: false, + connected: false, + lastFocused: false, + }, + ]); + }); + it("focuses an already open thread before reusing an empty panel", async () => { const { CodexChatView } = await import("../src/features/chat/host/view"); const openLeaf = leaf();