diff --git a/packages/tui/src/component/prompt/index.tsx b/packages/tui/src/component/prompt/index.tsx index fafc4e64f2..199ecc269b 100644 --- a/packages/tui/src/component/prompt/index.tsx +++ b/packages/tui/src/component/prompt/index.tsx @@ -640,6 +640,7 @@ export function Prompt(props: PromptProps) { "prompt.submit", "prompt.editor", "prompt.editor_context.clear", + "prompt.images.view", "prompt.stash", "prompt.stash.pop", "prompt.stash.list", @@ -1510,7 +1511,7 @@ export function Prompt(props: PromptProps) { - setFailed(true)} - /> - - {failed() ? "No preview" : (file.mention?.text ?? `Image ${index() + 1}`)} - + + No preview + + } + > + setFailed(true)} + /> + ) }} diff --git a/packages/tui/src/config/v1/keybind.ts b/packages/tui/src/config/v1/keybind.ts index 9787f9ad5d..b3ed88ced2 100644 --- a/packages/tui/src/config/v1/keybind.ts +++ b/packages/tui/src/config/v1/keybind.ts @@ -164,6 +164,7 @@ export const Definitions = { prompt_submit: keybind("none", "Submit prompt"), prompt_editor_context_clear: keybind("none", "Clear editor context"), + prompt_images_view: keybind("i", "View image attachments"), prompt_skills: keybind("none", "Open skill selector"), prompt_stash: keybind("none", "Stash prompt"), prompt_stash_pop: keybind("none", "Pop stashed prompt"), @@ -364,6 +365,7 @@ export const CommandMap = { display_thinking: "session.toggle.thinking", prompt_submit: "prompt.submit", prompt_editor_context_clear: "prompt.editor_context.clear", + prompt_images_view: "prompt.images.view", prompt_skills: "prompt.skills", prompt_stash: "prompt.stash", prompt_stash_pop: "prompt.stash.pop", diff --git a/packages/tui/test/config.test.tsx b/packages/tui/test/config.test.tsx index a9d5a007fe..20de66ce65 100644 --- a/packages/tui/test/config.test.tsx +++ b/packages/tui/test/config.test.tsx @@ -86,6 +86,14 @@ test("resolves a session move keybind", () => { expect(config.keybinds.get("session.move")).toMatchObject([{ key: "ctrl+o" }]) }) +test("resolves the image viewer keybind", () => { + const defaults = resolve({}, { terminalSuspend: true }) + const overridden = resolve({ keybinds: { prompt_images_view: "ctrl+shift+i" } }, { terminalSuspend: true }) + + expect(defaults.keybinds.get("prompt.images.view")).toMatchObject([{ key: "i" }]) + expect(overridden.keybinds.get("prompt.images.view")).toMatchObject([{ key: "ctrl+shift+i" }]) +}) + test("resolves message navigation defaults", () => { const config = resolve({}, { terminalSuspend: true }) diff --git a/packages/tui/test/prompt/clipboard.test.ts b/packages/tui/test/prompt/clipboard.test.ts index f7e772ffc2..41f315b366 100644 --- a/packages/tui/test/prompt/clipboard.test.ts +++ b/packages/tui/test/prompt/clipboard.test.ts @@ -318,6 +318,9 @@ test("renders at most three left-aligned cropped thumbnails", async () => { expect(prompt.setup.renderer.root.findDescendantById("prompt-image-preview-3")).toBeUndefined() const frame = await prompt.setup.waitForFrame((frame) => frame.includes("+1 more")) expect(frame).toMatch(/^┃ █/m) + expect(frame.match(/\[Image 1\]/g)).toHaveLength(1) + expect(frame.match(/\[Image 2\]/g)).toHaveLength(1) + expect(frame.match(/\[Image 3\]/g)).toHaveLength(1) await prompt.setup.mockMouse.click(49, 1, MouseButtons.LEFT) await prompt.setup.waitForFrame((frame) => frame.includes("Image 4 of 4")) @@ -331,13 +334,20 @@ test("renders at most three left-aligned cropped thumbnails", async () => { } }) -test("opens thumbnails by their exact mouse bounds and from the command palette", async () => { +test("opens image attachments by keyboard, mouse, and command palette", async () => { const prompt = await mountPrompt(readPngClipboard, true) try { await pasteImages(prompt, 2) const thumbnail = prompt.setup.renderer.root.findDescendantById("prompt-image-preview-1") if (!(thumbnail instanceof ImageRenderable)) throw new Error("Second image thumbnail did not render") + + prompt.setup.mockInput.pressKey("x", { ctrl: true }) + prompt.setup.mockInput.pressKey("i") + await prompt.setup.waitForFrame((frame) => frame.includes("Image 1 of 2")) + prompt.setup.mockInput.pressCtrlC() + await prompt.setup.waitForFrame((frame) => !frame.includes("Image 1 of 2")) + await prompt.setup.mockMouse.click(14, 1, MouseButtons.LEFT) await prompt.setup.waitForFrame((frame) => frame.includes("Image 1 of 2")) @@ -369,7 +379,8 @@ test("opens thumbnails by their exact mouse bounds and from the command palette" prompt.setup.mockInput.pressKey("p", { ctrl: true }) await prompt.setup.waitForFrame((frame) => frame.includes("Commands")) for (const key of "view image attachments") prompt.setup.mockInput.pressKey(key) - await prompt.setup.waitForFrame((frame) => frame.includes("View image attachments")) + const palette = await prompt.setup.waitForFrame((frame) => frame.includes("View image attachments")) + expect(palette).toContain("ctrl+x i") prompt.setup.mockInput.pressEnter() await prompt.setup.waitForFrame((frame) => frame.includes("Image 1 of 2")) } finally {