karanb192/claude-code-mods/tree/main/plugins/image-peek
image-peek
在 macOS 上预览提示标记处粘贴的图片。支持 Ghostty,并通过启动覆盖选项实验性支持 iTerm2 和 Herdr。Claude Code 2.1.287+。
关于这个 mod
Image Peek
将文字光标移到已粘贴的 [Image #1] 标记上即可看到图片,移开后隐藏。宽窗口会显示较大的预览窗格,图片居中放在深色画布上;窄窗口使用提示列上方的区域。键盘焦点仍留在提示列。
此版本首先面向 macOS 和 Ghostty,并要求 Claude Code 2.1.287 或更高版本。我们已在实际 Claude CLI 中测试光标选择、原生粘贴、重新加载和清理。一张用户提供的 Ghostty 截图确认了大图和深色预览画布。
iTerm2 和本机 Herdr 会话也能被识别,并可使用下面的启动覆盖选项。它们的图像像素仍需端到端视觉检查。
安装
在 shell 中运行,然后在 Ghostty 里启动新的 Claude 会话:
claude plugin marketplace add karanb192/claude-code-mods
claude plugin install image-peek@claude-code-mods
如果已经添加过市集,请先用 claude plugin marketplace update claude-code-mods 更新,再进行安装。Image Peek 的改动合并到市集的 main 分支后,此条目才会可用。
不需要浏览器、编译器、单独的 Mac 应用或 API 金钥。Claude 会加载插件,macOS 提供剪贴板读取器。Claude Code 2.1.287+ 默认启用插件。
要在不安装的情况下尝试本机检出版本,请从仓库根目录运行:
claude --plugin-dir ./plugins/image-peek
使用
iTerm2 和 Herdr
在 Ghostty 内使用 iTerm2 3.7.3+ 或 Herdr 0.9.1+。启用图像渲染器并启动新的 Claude 进程:
CLAUDE_CODE_FORCE_TERMINAL_IMAGES=1 claude
对于本机检出版本,请从仓库根目录运行:
CLAUDE_CODE_FORCE_TERMINAL_IMAGES=1 claude --plugin-dir ./plugins/image-peek
Claude Code 2.1.287 的渲染器会把终端报告的名称与 Kitty 和 Ghostty 比对,即使图形查询已经收到回应也是如此。iTerm2 和 Herdr 可能无法通过这项名称检查。这个按进程设置的覆盖选项会绕过名称检查和图像文件能力探测,不会更改 shell 设置,也不会安装其他应用。仅重新加载插件不够,因为 Claude 会在插件启动前初始化这些能力。
iTerm2 3.7.3 修复了 Claude 使用的 Unicode 图像占位符渲染问题。Herdr 0.9.1 支持由文件提供的 Kitty 图像和 Unicode 位置。它的 terminal.kitty_graphics 设置默认值为 true;如果曾将它停用,请重新启用,并依照 Herdr 的重新启动/重新连接说明操作。此预览路径需要本机 Mac 会话,以及具备图形能力的外层终端。覆盖选项无法为不相容的终端或传输方式添加图形支持。
预览附件
- 复制图片,然后使用平常的图片粘贴快捷键将它粘贴到 Claude 的提示列。
- 用方向键将文字光标移到 [Image #N] 标记内部或正旁边。预览会自动出现。
- 移到周围文字上即可隐藏。回到标记处会看到同一张缓存图片。
预览跟随文字光标,而不是鼠标悬停。对话会显示在预览旁边或上方。整张图片会放进可用的宽度和高度内,并为标签保留一行。窗格最多请求窗口宽度约 72%,再根据图片比例调整。Claude 可能会保留之前选择的宽度;如果窗格太窄,请拖动分隔线。
内嵌回退预览较小,因为 Claude 会把提示列上方的区域限制在终端高度约一半以内,还要包含提示列和底部其他内容。没有缩放或浮动覆盖层。关闭窗格后,它会一直隐藏,直到光标离开标记。
Claude 使用自己的主题绘制窗格外框。深色终端搭配浅色 Claude 主题时,深色画布周围会留下明亮的条带。请在 Claude 的 /theme 菜单中选择 Dark,让周围的外框也变暗;这会改变 Claude 的全部界面颜色。Image Peek 不会改变你的主题。
/image-peek off 会停止新的捕获并隐藏预览。/image-peek on 会恢复对新粘贴内容的捕获。/image-peek 会报告当前设置。这些命令不会呼叫模型。
此版本的限制
Claude 的提示 API 会公开标记和光标,却不会提供草稿附件的字节内容。因此,Image Peek 在检测到新的原生图片标记时,会读取 Mac 剪贴板。它每 120 ms 检查一次草稿,并保存当时的剪贴板图片。
- 一次粘贴一张图片,等预览出现后再复制另一张。如果粘贴和捕获之间剪贴板发生变化,预览可能显示较新的剪贴板图片。这是便利预览,不代表已提交附件的内容。
- 支持 PNG 和 TIFF 剪贴板图像数据。文件路径、拖放附件,以及插件加载时已经存在的图片,都不是支持的捕获路径。请从剪贴板重新粘贴图片。
- 同一次检查收到两个新标记时会显示「Preview unavailable」,因为无法恢复它们各自的剪贴板内容。
- 每个会话只保留最近 24 次捕获。较旧的标记、恢复的会话,以及停用期间粘贴的内容,可能会显示「Preview unavailable」。同一会话内的正常热重新加载会保留已捕获的预览。
- 捕获会拒绝超过 32 MiB 的输入或输出,以及超过 64 million pixels 的解码图片。macOS 能够解码动画图片时,会将它表示为单个 PNG 画格。
- 清除、恢复、压缩和正常退出会移除该会话的临时图片。崩溃、强制终止或清理失败可能会在 macOS 临时目录下的 claude-image-peek/ 留下文件。
- 只有在 macOS 且环境识别出 Ghostty、iTerm2 或 Herdr 时才会开始捕获。其他终端会收到通知,不会开始剪贴板捕获。iTerm2 和 Herdr 需要配合上面的启动覆盖选项及 Claude Code 2.1.287。SSH 和嵌套的 tmux/screen 会话未经验证。
验证
从仓库根目录运行:
claude plugin validate plugins/image-peek --strict
claude plugin test plugins/image-peek
Claude Code 2.1.287 上的严格验证器报告:
❯ types ./types/index.d.ts declares on $: nothing (no EngineInterface member)
❯ types ./types/index.d.ts declares state: image-peek.session
❯ ./register.ts hooks: session.start, prompt.edit, prompt.fill, command.run{command=image-peek}, session.end, session.compact, ui.render{component=AbovePrompt}, ui.render{component=Pane, requestId=image-peek}, ui.close{id=image-peek}
❯ ./register.ts calls: $.clock.every, $.command.register, $.env.get, $.process.run, $.prompt.read, $.session.id, $.state.get, $.state.set (via save), $.ui.close, $.ui.invalidate, $.ui.log, $.ui.open (via update), $.ui.panes, $.ui.resolve
❯ ./register.ts env writes: nothing
❯ ./register.ts env reads: CLAUDE_CODE_FORCE_TERMINAL_IMAGES, GHOSTTY_RESOURCES_DIR, HERDR_ENV, TERM_PROGRAM
❯ ./register.ts state writes: image-peek.session
❯ ./register.ts state reads: image-peek.session
测试涵盖光标边界、分开的捕获、含糊的粘贴、输入的标记替代内容、不支持的终端、启用/停用、捕获失败、缓存淘汰和清理失败。实际 CLI 检查也测试了原生图片粘贴、离开并返回标记、热重新加载和正常退出清理。该终端渲染了 Image 元素的替代文字,因此没有验证图像像素。这些检查期间没有提交模型工作阶段。
实际 CLI 中的受控布局探测对两种表面使用了同一个 180-column、48-row 终端。内嵌区域稳定为 15 rows,并将横向图片放入 50 by 14 cells。请求 128-column 窗格提供 128 by 40 cell body,并将该图片放入 126 by 34 cells。这验证了可用布局空间,不代表已验证渲染像素或色彩。
要进行完整的互动检查,请在 Ghostty 中粘贴两张不同的图片,分别选取每个标记,调整窗口大小,确认较大的深色预览会出现和消失,同时键盘焦点不移动。再让对话输出显示在提示列上方重复测试。重新加载时,插件会关闭先前布局留下的任何窗格。
威胁模型
达到 L2:启动本机进程,并写入临时图片文件。
- **读取:**草稿文字和光标、会话 ID、四个终端环境变量,以及检测到新图片标记后的 PNG/TIFF 剪贴板数据。草稿文字在内存中解析,不会由此插件保存或发送。
- **运行:**使用 /usr/bin/uname -s 检查平台,使用捆绑的剪贴板辅助程序执行 /usr/bin/osascript -l JavaScript。使用固定参数数组;提示列文字不会成为 shell 命令。
- **发送:**不发送网络请求、模型呼叫或提示提交。你发送提示时,Claude 对附件的正常处理不变。
- **持久化:**本机会话状态保存图片路径、尺寸、观察到的标记 ID,以及开/关设置。临时 PNG 使用私有会话目录(0700)和文件(0600),最多保留 24 次捕获。正常会话清理会移除文件,但储存的路径可能在文件移除后仍存在。
- **恶意输入:**会话目录名称限制为 UUID 字符;已存在的非目录缓存路径会被拒绝。尺寸检查限制接受的图片数据,但 macOS 仍会解码剪贴板图片。捕获期间剪贴板改变会丢弃图片;捕获前发生的改变无法与原始粘贴建立关联。验证器会列出 $.process.run,因此除了 hooks 模组也要检查辅助程序。
文件和 API 参考
- hooks/register.ts:检测标记选择、捕获新图片并绘制预览。
- hooks/clipboard.js:macOS 剪贴板捕获与临时文件清理。
- hooks/selection.ts:标记边界和图像尺寸。
- types/index.d.ts:插件的会话状态形状。
- tests/register.test.ts:Claude 的原生插件测试。
安装
请先查看作者 README 确认 marketplace 和插件名称;命令可能随仓库结构改变。
claude plugin marketplace add karanb192/claude-code-mods claude plugin install image-peek
原文 / README
Image Peek
Move the text cursor onto a pasted [Image #1] marker to see its image. Move away to hide it. Wide windows get a large preview pane with the image centered on a dark canvas. Narrow windows use the area above the prompt. Keyboard focus stays in the prompt.
This first version targets macOS and Ghostty with Claude Code 2.1.287 or later. Cursor selection, native paste, reload and cleanup have been exercised in the actual Claude CLI. A user-provided Ghostty screenshot confirms the large image and dark preview canvas.
iTerm2 and local Herdr sessions are also recognized, with the startup override below. Their image pixels still need an end-to-end visual check.
Install
Run in your shell, then start a new Claude session in Ghostty:
claude plugin marketplace add karanb192/claude-code-mods
claude plugin install image-peek@claude-code-mods
If you already added the marketplace, update it with claude plugin marketplace update claude-code-mods before installing. This entry becomes available when the Image Peek change is merged into the marketplace's main branch.
No browser, compiler, separate Mac app or API key is needed. Claude loads the mod and macOS supplies the clipboard reader. Mods are enabled by default in Claude Code 2.1.287+.
To try a local checkout without installing, run from the repository root:
claude --plugin-dir ./plugins/image-peek
Use
iTerm2 and Herdr
Use iTerm2 3.7.3+ or Herdr 0.9.1+ inside Ghostty. Start a new Claude process with the image renderer enabled:
CLAUDE_CODE_FORCE_TERMINAL_IMAGES=1 claude
For a local checkout, from the repository root:
CLAUDE_CODE_FORCE_TERMINAL_IMAGES=1 claude --plugin-dir ./plugins/image-peek
Claude Code 2.1.287's renderer checks the terminal's reported name against Kitty and Ghostty even after a graphics query receives a reply. iTerm2 and Herdr can fail this name check. This per-process override bypasses that check and the image-file capability probe. It does not change your shell settings or install another application. A mod reload is insufficient because Claude initializes these capabilities before the mod starts.
iTerm2 3.7.3 fixes rendering of the Unicode image placeholders that Claude uses. Herdr 0.9.1 supports file-backed Kitty images and Unicode placements. Its terminal.kitty_graphics setting defaults to true; if you disabled it, enable it and follow Herdr's restart/reattach instructions. This preview route requires a local Mac session and a graphics-capable outer terminal. The override cannot add graphics support to an incompatible terminal or transport.
Preview an attachment
- Copy an image and paste it into Claude's prompt using the usual image-paste shortcut.
- Use the arrow keys to put the text cursor inside or directly beside its
[Image #N]marker. The preview appears automatically. - Move into the surrounding text to hide it. Return to the marker to see the same cached image.
This follows the text cursor, not mouse hover. The conversation remains visible beside or above the preview. The whole image fits within the available width and height, reserving one row for its label. The pane requests up to about 72% of the window's width, adjusted for the image's proportions. Claude may retain a width you previously chose; drag the divider if that makes the pane too narrow.
The inline fallback is smaller because Claude limits the above-prompt area to roughly half the terminal height, including the prompt and other bottom content. There is no zoom or floating overlay. Closing the pane dismisses it until the cursor leaves the marker.
Claude draws the pane's outer frame using its own theme. A Light Claude theme in a dark terminal leaves bright strips around the dark canvas. Choose Dark in Claude's /theme menu to make the surrounding frame dark too; this changes all Claude UI colors. Image Peek does not change your theme.
/image-peek off stops new captures and hides the preview. /image-peek on resumes capture for new pastes. /image-peek reports the current setting. These commands do not call a model.
Limits of this version
Claude's prompt API exposes the marker and cursor but does not provide the draft attachment bytes. Image Peek therefore reads the Mac clipboard when it detects a new native image marker. It checks the draft every 120 ms and saves that clipboard image once.
- Paste one image at a time and wait for the preview before copying another image. If the clipboard changes between the paste and capture, the preview can show the newer clipboard image. It is a convenience preview, not proof of the submitted attachment's contents.
- PNG and TIFF clipboard image data are supported. File paths, drag-and-drop attachments and images that were already present when the mod loaded are not supported capture routes. Re-paste the image from the clipboard.
- Two new markers arriving in the same check produce “Preview unavailable”, since their individual clipboard contents cannot be recovered.
- Only the most recent 24 captures are kept per session. Older markers, resumed sessions and pastes made while disabled may show “Preview unavailable”. A normal hot reload within the same session preserves captured previews.
- Capture rejects input or output over 32 MiB and decoded images over 64 million pixels. Animated images are represented as a single PNG frame when macOS can decode them.
- Clear, resume, compaction and normal exit remove that session's temporary images. A crash, forced termination or cleanup failure can leave files in the macOS temporary directory under
claude-image-peek/. - Capture starts only on macOS when the environment identifies Ghostty, iTerm2 or Herdr. Other terminals receive a notice and no clipboard capture starts. iTerm2 and Herdr need the startup override above with Claude Code 2.1.287. SSH and nested tmux/screen sessions are not validated.
Validation
Run from the repository root:
claude plugin validate plugins/image-peek --strict
claude plugin test plugins/image-peek
The strict validator on Claude Code 2.1.287 reported:
❯ types ./types/index.d.ts declares on $: nothing (no EngineInterface member)
❯ types ./types/index.d.ts declares state: image-peek.session
❯ ./register.ts hooks: session.start, prompt.edit, prompt.fill, command.run{command=image-peek}, session.end, session.compact, ui.render{component=AbovePrompt}, ui.render{component=Pane, requestId=image-peek}, ui.close{id=image-peek}
❯ ./register.ts calls: $.clock.every, $.command.register, $.env.get, $.process.run, $.prompt.read, $.session.id, $.state.get, $.state.set (via save), $.ui.close, $.ui.invalidate, $.ui.log, $.ui.open (via update), $.ui.panes, $.ui.resolve
❯ ./register.ts env writes: nothing
❯ ./register.ts env reads: CLAUDE_CODE_FORCE_TERMINAL_IMAGES, GHOSTTY_RESOURCES_DIR, HERDR_ENV, TERM_PROGRAM
❯ ./register.ts state writes: image-peek.session
❯ ./register.ts state reads: image-peek.session
Tests cover cursor boundaries, separate captures, ambiguous pastes, typed marker substitutes, unsupported terminals, enable/disable, failed capture, cache eviction and cleanup failure. A live CLI check also exercised native image paste, leaving and returning to the marker, hot reload and normal-exit cleanup. That terminal rendered the Image element's alternative text, so it did not verify image pixels. No model turn was submitted during these checks.
A controlled layout probe in the actual CLI used the same 180-column, 48-row terminal for both surfaces. The inline area settled at 15 rows and fitted a landscape image into 50 by 14 cells. A requested 128-column pane provided a 128 by 40 cell body and fitted that image into 126 by 34 cells. This verifies available layout space, not rendered pixels or colors.
For a full interaction check, paste two distinct images in Ghostty, select each marker, resize the window, and confirm the larger dark preview appears and disappears without moving keyboard focus. Repeat with conversation output above the prompt. On reload, the plugin closes any pane left from its earlier layout.
Threat model
Reach L2: starts local processes and writes temporary image files.
- Reads: draft text and cursor, session ID, four terminal environment variables, and PNG/TIFF clipboard data after detecting a new image marker. Draft text is parsed in memory and is not saved or sent by this plugin.
- Runs:
/usr/bin/uname -sto check the platform and/usr/bin/osascript -l JavaScriptwith the bundled clipboard helper. Fixed argument arrays are used; prompt text never becomes a shell command. - Sends: no network requests, model calls or prompt submissions. Claude's normal handling of an attachment when you send your prompt is unchanged.
- Persists: local session state holds image paths, dimensions, observed marker IDs and the on/off setting. Temporary PNGs use private session directories (0700) and files (0600), with at most 24 retained captures. Normal session cleanup removes the files; stored paths can outlive them.
- Hostile input: session directory names are restricted to UUID characters; existing non-directory cache paths are rejected. Size checks bound accepted image data, but macOS still decodes the clipboard image. A clipboard change during capture discards it; a change before capture cannot be tied back to the original paste. The validator lists
$.process.run, so review the helper as well as the hooks module.
Files and API references
hooks/register.ts: detects marker selection, captures new images and draws the preview.hooks/clipboard.js: macOS clipboard capture and temporary-file cleanup.hooks/selection.ts: marker boundaries and image sizing.types/index.d.ts: the plugin's session-state shape.tests/register.test.ts: Claude's native mod tests.
The implementation uses Claude's engine interface and native interface elements. Generated engine declarations are local development files and are not shipped.

