mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 15:03:31 +00:00
* Add session renaming in interactive mode resume picker Session list now displays last message timestamp as modified time instead of file mtime. Ctrl+N enters rename mode in the interactive resume picker, allowing quick session renaming without leaving the selector. Rename hint is shown only in interactive mode, not in the CLI --resume picker./ * Add docs entry for renaming in picker * Update shortcut to ctrl+r for session renaming
101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
import { beforeAll, describe, expect, it, vi } from "vitest";
|
|
import type { SessionInfo } from "../src/core/session-manager.js";
|
|
import { SessionSelectorComponent } from "../src/modes/interactive/components/session-selector.js";
|
|
import { initTheme } from "../src/modes/interactive/theme/theme.js";
|
|
|
|
async function flushPromises(): Promise<void> {
|
|
await new Promise<void>((resolve) => {
|
|
setImmediate(resolve);
|
|
});
|
|
}
|
|
|
|
function makeSession(overrides: Partial<SessionInfo> & { id: string }): SessionInfo {
|
|
return {
|
|
path: overrides.path ?? `/tmp/${overrides.id}.jsonl`,
|
|
id: overrides.id,
|
|
cwd: overrides.cwd ?? "",
|
|
name: overrides.name,
|
|
created: overrides.created ?? new Date(0),
|
|
modified: overrides.modified ?? new Date(0),
|
|
messageCount: overrides.messageCount ?? 1,
|
|
firstMessage: overrides.firstMessage ?? "hello",
|
|
allMessagesText: overrides.allMessagesText ?? "hello",
|
|
};
|
|
}
|
|
|
|
// Kitty keyboard protocol encoding for Ctrl+R
|
|
const CTRL_R = "\x1b[114;5u";
|
|
|
|
describe("session selector rename", () => {
|
|
beforeAll(() => {
|
|
initTheme("dark");
|
|
});
|
|
|
|
it("shows rename hint in interactive /resume picker configuration", async () => {
|
|
const sessions = [makeSession({ id: "a" })];
|
|
const selector = new SessionSelectorComponent(
|
|
async () => sessions,
|
|
async () => [],
|
|
() => {},
|
|
() => {},
|
|
() => {},
|
|
() => {},
|
|
{ showRenameHint: true },
|
|
);
|
|
await flushPromises();
|
|
|
|
const output = selector.render(120).join("\n");
|
|
expect(output).toContain("ctrl+r");
|
|
expect(output).toContain("rename");
|
|
});
|
|
|
|
it("does not show rename hint in --resume picker configuration", async () => {
|
|
const sessions = [makeSession({ id: "a" })];
|
|
const selector = new SessionSelectorComponent(
|
|
async () => sessions,
|
|
async () => [],
|
|
() => {},
|
|
() => {},
|
|
() => {},
|
|
() => {},
|
|
{ showRenameHint: false },
|
|
);
|
|
await flushPromises();
|
|
|
|
const output = selector.render(120).join("\n");
|
|
expect(output).not.toContain("ctrl+r");
|
|
expect(output).not.toContain("rename");
|
|
});
|
|
|
|
it("enters rename mode on Ctrl+R and submits with Enter", async () => {
|
|
const sessions = [makeSession({ id: "a", name: "Old" })];
|
|
const renameSession = vi.fn(async () => {});
|
|
|
|
const selector = new SessionSelectorComponent(
|
|
async () => sessions,
|
|
async () => [],
|
|
() => {},
|
|
() => {},
|
|
() => {},
|
|
() => {},
|
|
{ renameSession, showRenameHint: true },
|
|
);
|
|
await flushPromises();
|
|
|
|
selector.getSessionList().handleInput(CTRL_R);
|
|
await flushPromises();
|
|
|
|
// Rename mode layout
|
|
const output = selector.render(120).join("\n");
|
|
expect(output).toContain("Rename Session");
|
|
expect(output).not.toContain("Resume Session");
|
|
|
|
// Type and submit
|
|
selector.handleInput("X");
|
|
selector.handleInput("\r");
|
|
await flushPromises();
|
|
|
|
expect(renameSession).toHaveBeenCalledTimes(1);
|
|
expect(renameSession).toHaveBeenCalledWith(sessions[0]!.path, "XOld");
|
|
});
|
|
});
|