mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Add reconnect slash command
This commit is contained in:
parent
6db8038649
commit
127d72b77a
10 changed files with 48 additions and 8 deletions
|
|
@ -38,7 +38,7 @@ Use **Codex Panel: Open thread...** to search non-archived threads from a picker
|
|||
|
||||
Codex Panel supports Codex App Server workflows that fit a persistent panel in Obsidian:
|
||||
|
||||
- Manage thread history from the panel or slash commands: clear (`/clear`), resume (`/resume`), rename (`/rename`), auto-name, fork (`/fork`), roll back (`/rollback`), compact (`/compact`), and archive (`/archive`) Codex threads.
|
||||
- Manage thread history from slash commands or the panel: clear (`/clear`), resume (`/resume`), reconnect (`/reconnect`), rename (`/rename`), auto-name, fork (`/fork`), roll back (`/rollback`), compact (`/compact`), and archive (`/archive`) Codex threads.
|
||||
- Compose with Codex-aware completions for slash commands, enabled skills (`$skill-name`), recent threads, models, and supported reasoning efforts; use `/help` to show available slash commands.
|
||||
- Reference another non-archived thread without switching away from the current one (`/refer`).
|
||||
- Control subsequent turns from slash commands, with the same state visible and lightly adjustable in the composer status row: Plan mode (`/plan`), fast mode (`/fast`), approval auto-review (`/auto-review`), model (`/model`), and reasoning effort (`/reasoning`).
|
||||
|
|
|
|||
|
|
@ -209,6 +209,7 @@ export function createChatViewControllerAssembly(host: ChatViewControllerAssembl
|
|||
rollbackThread: (threadId) => threadActions.rollbackThread(threadId),
|
||||
archiveThread: (threadId) => threadActions.archiveThread(threadId),
|
||||
renameThread: (threadId, name) => threadRename.rename(threadId, name),
|
||||
reconnect: () => reconnectActions.reconnectPanel(),
|
||||
},
|
||||
runtime: {
|
||||
toggleFastMode: () => runtimeSettings.toggleFastMode(),
|
||||
|
|
|
|||
|
|
@ -31,6 +31,13 @@ export const SLASH_COMMANDS = [
|
|||
surface: "panelAction",
|
||||
detail: "Resume a recent Codex thread.",
|
||||
},
|
||||
{
|
||||
command: "/reconnect",
|
||||
usage: "/reconnect",
|
||||
argsKind: "none",
|
||||
surface: "panelAction",
|
||||
detail: "Reconnect to Codex app-server and resume the active thread.",
|
||||
},
|
||||
{
|
||||
command: "/refer",
|
||||
usage: "/refer <thread> <message>",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export interface ChatReconnectControllerHost {
|
|||
export class ChatReconnectController {
|
||||
constructor(private readonly host: ChatReconnectControllerHost) {}
|
||||
|
||||
async reconnectFromToolbar(): Promise<void> {
|
||||
async reconnectPanel(): Promise<void> {
|
||||
const threadId = this.host.threadState.activeThreadId();
|
||||
this.host.panelState.closePanels();
|
||||
this.host.invalidateConnectionWork();
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export interface SlashCommandThreadPort {
|
|||
rollbackThread: (threadId: string) => Promise<void>;
|
||||
archiveThread: (threadId: string) => Promise<void>;
|
||||
renameThread: (threadId: string, name: string) => Promise<void>;
|
||||
reconnect: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface SlashCommandRuntimePort {
|
||||
|
|
@ -53,18 +54,23 @@ export class SlashCommandController {
|
|||
constructor(private readonly host: SlashCommandControllerHost) {}
|
||||
|
||||
async execute(command: SlashCommandName, args: string): Promise<SlashCommandExecutionResult | undefined> {
|
||||
const client = this.host.currentClient();
|
||||
if (!client) return;
|
||||
const state = this.host.state.snapshot();
|
||||
const client = this.host.currentClient();
|
||||
if (!client && command !== "reconnect") return;
|
||||
return runSlashCommand(command, args, {
|
||||
activeThreadId: state.activeThreadId,
|
||||
listedThreads: state.listedThreads,
|
||||
startNewThread: () => this.host.threads.startNewThread(),
|
||||
resumeThread: (threadId) => this.host.threads.resumeThread(threadId),
|
||||
referThread: (thread, message) => this.referencedThreadInput(client, thread, message),
|
||||
reconnect: () => this.host.threads.reconnect(),
|
||||
referThread: (thread, message) => {
|
||||
if (!client) return Promise.resolve(null);
|
||||
return this.referencedThreadInput(client, thread, message);
|
||||
},
|
||||
forkThread: (threadId) => this.host.threads.forkThread(threadId),
|
||||
rollbackThread: (threadId) => this.host.threads.rollbackThread(threadId),
|
||||
compactThread: async (threadId) => {
|
||||
if (!client) return;
|
||||
await client.compactThread(threadId);
|
||||
},
|
||||
archiveThread: (threadId) => this.host.threads.archiveThread(threadId),
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ export interface SlashCommandExecutionContext {
|
|||
compactThread: (threadId: string) => Promise<void>;
|
||||
archiveThread: (threadId: string, saveMarkdown?: boolean) => Promise<void>;
|
||||
renameThread: (threadId: string, name: string) => Promise<void>;
|
||||
reconnect: () => Promise<void>;
|
||||
toggleFastMode: () => void | Promise<void>;
|
||||
toggleCollaborationMode: () => void | Promise<void>;
|
||||
toggleAutoReview: () => void | Promise<void>;
|
||||
|
|
@ -76,6 +77,11 @@ export async function executeSlashCommand(
|
|||
return;
|
||||
}
|
||||
|
||||
if (command === "reconnect") {
|
||||
await context.reconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (command === "refer") {
|
||||
const parsed = parseReferArgs(args);
|
||||
if (!parsed) {
|
||||
|
|
|
|||
|
|
@ -590,7 +590,7 @@ export class CodexChatView extends ItemView {
|
|||
toggleStatusPanel: () => {
|
||||
this.toolbarPanels.toggleStatus();
|
||||
},
|
||||
connect: () => void this.reconnectActions.reconnectFromToolbar(),
|
||||
connect: () => void this.reconnectActions.reconnectPanel(),
|
||||
refreshStatus: () => void this.refreshStatusPanel(),
|
||||
resumeThread: (threadId) => void this.threadSelection.selectThreadFromToolbar(threadId),
|
||||
startArchiveThread: (threadId) => {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ function context(overrides: Partial<SlashCommandExecutionContext> = {}): SlashCo
|
|||
compactThread: vi.fn().mockResolvedValue(undefined),
|
||||
archiveThread: vi.fn().mockResolvedValue(undefined),
|
||||
renameThread: vi.fn().mockResolvedValue(undefined),
|
||||
reconnect: vi.fn().mockResolvedValue(undefined),
|
||||
toggleFastMode: vi.fn(),
|
||||
toggleCollaborationMode: vi.fn(),
|
||||
toggleAutoReview: vi.fn(),
|
||||
|
|
@ -91,6 +92,14 @@ describe("slash commands", () => {
|
|||
expect(ctx.resumeThread).toHaveBeenCalledWith("latest");
|
||||
});
|
||||
|
||||
it("reconnects the panel for /reconnect", async () => {
|
||||
const ctx = context();
|
||||
|
||||
await executeSlashCommand("reconnect", "", ctx);
|
||||
|
||||
expect(ctx.reconnect).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("resumes a thread by id argument", async () => {
|
||||
const ctx = context({
|
||||
listedThreads: [thread({ id: "thread-alpha", name: "Alpha" }), thread({ id: "thread-beta", name: "Beta" })],
|
||||
|
|
@ -363,6 +372,7 @@ describe("slash commands", () => {
|
|||
title: "Panel actions",
|
||||
rows: expect.arrayContaining([
|
||||
{ key: "/clear", value: "Clear the current panel and start a fresh Codex thread." },
|
||||
{ key: "/reconnect", value: "Reconnect to Codex app-server and resume the active thread." },
|
||||
{ key: "/archive <thread>", value: "Archive the selected Codex thread." },
|
||||
{ key: "/rename <thread> <name>", value: "Rename the selected Codex thread." },
|
||||
]),
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ describe("ChatReconnectController", () => {
|
|||
const { host, stateStore } = createHost();
|
||||
const controller = new ChatReconnectController(host);
|
||||
|
||||
await controller.reconnectFromToolbar();
|
||||
await controller.reconnectPanel();
|
||||
|
||||
expect(stateStore.getState().openDetails.size).toBe(0);
|
||||
expect(host.invalidateConnectionWork).toHaveBeenCalledOnce();
|
||||
|
|
@ -69,7 +69,7 @@ describe("ChatReconnectController", () => {
|
|||
});
|
||||
const controller = new ChatReconnectController(host);
|
||||
|
||||
await controller.reconnectFromToolbar();
|
||||
await controller.reconnectPanel();
|
||||
|
||||
expect(host.addSystemMessage).toHaveBeenCalledWith("resume failed");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ function createHost(overrides: SlashCommandHostOverrides = {}) {
|
|||
rollbackThread: vi.fn().mockResolvedValue(undefined),
|
||||
archiveThread: vi.fn().mockResolvedValue(undefined),
|
||||
renameThread: vi.fn().mockResolvedValue(undefined),
|
||||
reconnect: vi.fn().mockResolvedValue(undefined),
|
||||
...threadOverrides,
|
||||
};
|
||||
const runtime: SlashCommandRuntimePort = {
|
||||
|
|
@ -128,6 +129,15 @@ describe("SlashCommandController", () => {
|
|||
expect(host.status.setStatus).toHaveBeenCalledWith("Compaction requested.");
|
||||
});
|
||||
|
||||
it("runs reconnect even when there is no current app-server client", async () => {
|
||||
const { host } = createHost({ currentClient: () => null });
|
||||
const controller = new SlashCommandController(host);
|
||||
|
||||
await controller.execute("reconnect", "");
|
||||
|
||||
expect(host.threads.reconnect).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("reports unreadable referenced threads", async () => {
|
||||
const { host, stateStore, threadTurnsList } = createHost();
|
||||
stateStore.dispatch({
|
||||
|
|
|
|||
Loading…
Reference in a new issue