Use current panel for chat resume selection

This commit is contained in:
murashit 2026-05-25 18:10:40 +09:00
parent 71bf983749
commit d949518be2
3 changed files with 31 additions and 13 deletions

View file

@ -78,6 +78,7 @@ export interface CodexChatHost {
readonly vaultPath: string;
openThreadInNewView(threadId: string): Promise<unknown>;
openThreadInAvailableView(threadId: string): Promise<void>;
focusThreadInOpenView(threadId: string): Promise<boolean>;
openTurnDiff(state: ChatTurnDiffViewState): Promise<void>;
notifyThreadArchived(threadId: string): void;
notifyThreadRenamed(threadId: string, name: string): void;
@ -1252,7 +1253,8 @@ export class CodexChatView extends ItemView {
this.addSystemMessage("Finish or interrupt the current turn before switching threads.");
return;
}
await this.plugin.openThreadInAvailableView(threadId);
if (await this.plugin.focusThreadInOpenView(threadId)) return;
await this.resumeThread(threadId);
}
private closeToolbarPanelOnOutsidePointer(event: PointerEvent): void {

View file

@ -220,10 +220,10 @@ describe("CodexChatView connection lifecycle", () => {
expect(client.startTurn).toHaveBeenCalledWith("thread-new", "/vault", [{ type: "text", text: "hello", text_elements: [] }]);
});
it("routes slash resume through the shared panel selection path", async () => {
const openThreadInAvailableView = vi.fn().mockResolvedValue(undefined);
it("focuses an open panel instead of resuming a duplicate slash resume thread", async () => {
const focusThreadInOpenView = vi.fn().mockResolvedValue(true);
const host = chatHost({
openThreadInAvailableView,
focusThreadInOpenView,
});
connectionMock.state.client = connectedClient({
listThreads: vi.fn().mockResolvedValue({ data: [threadFixture("thread-1")] }),
@ -234,23 +234,38 @@ describe("CodexChatView connection lifecycle", () => {
view.setComposerText("/resume thread-1");
await (view as unknown as { submitComposerAction: () => Promise<void> }).submitComposerAction();
expect(openThreadInAvailableView).toHaveBeenCalledWith("thread-1");
expect(focusThreadInOpenView).toHaveBeenCalledWith("thread-1");
expect(connectionMock.state.client["resumeThread"]).not.toHaveBeenCalled();
});
it("resumes slash resume threads in the current panel when they are not already open", async () => {
const focusThreadInOpenView = vi.fn().mockResolvedValue(false);
const client = connectedClient({
listThreads: vi.fn().mockResolvedValue({ data: [threadFixture("thread-1")] }),
threadTurnsList: vi.fn().mockResolvedValue({ data: [turnWithUserMessage("restored prompt")], nextCursor: null }),
});
connectionMock.state.client = client;
const view = await chatView({
host: chatHost({
focusThreadInOpenView,
}),
});
await view.connect();
view.setComposerText("/resume thread-1");
await (view as unknown as { submitComposerAction: () => Promise<void> }).submitComposerAction();
expect(focusThreadInOpenView).toHaveBeenCalledWith("thread-1");
expect(client.resumeThread).toHaveBeenCalledWith("thread-1", "/vault");
});
it("keeps resumed messages pinned to bottom after slash resume in the same empty panel", async () => {
const client = connectedClient({
listThreads: vi.fn().mockResolvedValue({ data: [threadFixture("thread-1")] }),
threadTurnsList: vi.fn().mockResolvedValue({ data: [turnWithUserMessage("restored prompt")], nextCursor: null }),
});
connectionMock.state.client = client;
const view = await chatView({
host: chatHost({
openThreadInAvailableView: async (threadId) => {
await view.openThread(threadId);
},
}),
});
const view = await chatView();
await view.onOpen();
await view.connect();
@ -549,6 +564,7 @@ function chatHost(overrides: Partial<CodexChatHost> = {}): CodexChatHost {
vaultPath: "/vault",
openThreadInNewView: vi.fn(),
openThreadInAvailableView: vi.fn().mockResolvedValue(undefined),
focusThreadInOpenView: vi.fn().mockResolvedValue(false),
openTurnDiff: vi.fn(),
notifyThreadArchived: vi.fn(),
notifyThreadRenamed: vi.fn(),

View file

@ -234,9 +234,9 @@ function chatView(CodexChatViewCtor: typeof CodexChatView, leaf: TestLeaf) {
{
settings: { ...DEFAULT_SETTINGS, codexPath: "codex", sendShortcut: "enter" },
vaultPath: "/vault",
openNewPanel: vi.fn(),
openThreadInNewView: vi.fn(),
openThreadInAvailableView: vi.fn(),
focusThreadInOpenView: vi.fn(),
openTurnDiff: vi.fn(),
notifyThreadArchived: vi.fn(),
notifyThreadRenamed: vi.fn(),