diff --git a/src/client/src/components/shared.ts b/src/client/src/components/shared.ts index f24d491..d6099a7 100644 --- a/src/client/src/components/shared.ts +++ b/src/client/src/components/shared.ts @@ -197,7 +197,7 @@ export const statusBarStyles = css` export const autocompleteStyles = css` :host { display: block; } - .menu { position: absolute; left: 0; right: 0; bottom: calc(100% + 6px); max-height: 260px; overflow: auto; border: 1px solid #30363d; border-radius: 8px; background: #161b22; box-shadow: 0 10px 30px #0008; } + .menu { position: absolute; left: 0; right: 0; bottom: calc(100% + 6px); z-index: 10; max-height: 260px; overflow: auto; border: 1px solid #30363d; border-radius: 8px; background: #161b22; box-shadow: 0 10px 30px #0008; } button { display: grid; grid-template-columns: minmax(120px, 1fr) auto; gap: 4px 10px; width: 100%; border: 0; border-bottom: 1px solid #30363d; border-radius: 0; background: transparent; color: #e6edf3; padding: 8px 10px; text-align: left; cursor: pointer; } button:last-child { border-bottom: 0; } button.selected, button:hover { background: #0d2847; } @@ -239,7 +239,7 @@ export const actionPaletteStyles = css` `; export const promptEditorStyles = css` - :host { display: block; color: #e6edf3; font: 14px system-ui, sans-serif; } + :host { position: relative; z-index: 5; display: block; color: #e6edf3; font: 14px system-ui, sans-serif; } footer { display: grid; grid-template-columns: 1fr auto; gap: 8px; padding: 12px; border-top: 1px solid #30363d; } footer.shell-mode { border-top-color: #3fb950; background: #0f1b12; } .editor-wrap { position: relative; min-width: 0; } diff --git a/src/server/sessions/sessionCommandService.test.ts b/src/server/sessions/sessionCommandService.test.ts index 8aee72f..53a19ef 100644 --- a/src/server/sessions/sessionCommandService.test.ts +++ b/src/server/sessions/sessionCommandService.test.ts @@ -96,16 +96,22 @@ describe("SessionCommandService", () => { expect(active.runtime.session.compact).toHaveBeenCalledWith("focus on tests"); }); - it("creates fork selection requests and responds with selected entry", async () => { - const active = activeSession(); + it("creates fork selection requests from newest message to oldest and responds with selected entry", async () => { + const active = activeSession({ + getUserMessagesForForking: vi.fn(() => [ + { entryId: "oldest", text: "oldest message" }, + { entryId: "middle", text: "middle message" }, + { entryId: "newest", text: "newest message" }, + ]), + }); const service = new SessionCommandService(() => getActive(active), vi.fn(), { publish: vi.fn() } as never); const result = await service.run("s1", "/fork"); - expect(result).toMatchObject({ type: "select", title: "Fork from message", options: [{ value: "m1" }] }); + expect(result).toMatchObject({ type: "select", title: "Fork from message", options: [{ value: "newest" }, { value: "middle" }, { value: "oldest" }] }); if (result.type !== "select") throw new Error("Expected select result"); - await expect(service.respond("s1", result.requestId, "m1")).resolves.toMatchObject({ type: "done", message: "Session forked", session: { id: "s1" } }); - expect(active.runtime.fork).toHaveBeenCalledWith("m1"); - await expect(service.respond("s1", result.requestId, "m1")).resolves.toEqual({ type: "unsupported", message: "Command request expired" }); + await expect(service.respond("s1", result.requestId, "newest")).resolves.toMatchObject({ type: "done", message: "Session forked", session: { id: "s1" } }); + expect(active.runtime.fork).toHaveBeenCalledWith("newest"); + await expect(service.respond("s1", result.requestId, "newest")).resolves.toEqual({ type: "unsupported", message: "Command request expired" }); }); }); diff --git a/src/server/sessions/sessionCommandService.ts b/src/server/sessions/sessionCommandService.ts index fd44370..66765cc 100644 --- a/src/server/sessions/sessionCommandService.ts +++ b/src/server/sessions/sessionCommandService.ts @@ -93,7 +93,7 @@ export class SessionCommandService { type: "select", requestId, title: "Fork from message", - options: messages.map((message) => ({ value: message.entryId, label: truncate(message.text, 140) })), + options: [...messages].reverse().map((message) => ({ value: message.entryId, label: truncate(message.text, 140) })), }; }