# Image Editor
**English**: Annotate and crop images in place. Right-click any image in your vault to open the built-in editor — draw, add text or mosaic, crop — then save to replace the original instantly.
## Features (English)
- Right-click any embedded image (reading mode / live preview) to open the built-in editor
- Annotation tools: pen, line, arrow, rectangle, ellipse, text, mosaic — with undo / redo and keyboard shortcuts
- Crop with rule-of-thirds guides; apply or cancel at any time
- Save to replace the original image, or export as a new PNG file
- Non-PNG images (JPG / WebP / BMP / GIF) are auto-converted to PNG and all vault links are updated automatically
在 Obsidian 文档里**右键图片 → 编辑图片**,直接打开内置图片编辑器做标注和裁剪,点「完成」后原图被替换、文档里的图片立即刷新。
## 功能
| 能力 | 说明 |
| --- | --- |
| 右键唤起 | 在阅读模式 / 实时预览里右键任意内嵌图片,菜单出现「编辑图片」 |
| 多开 | 每张图一个独立标签页,可同时开多个编辑器;同一张图重复右键会聚焦已开的那一个 |
| 标注 | 画笔、直线、箭头、矩形、椭圆、文本、马赛克(矩形 / 椭圆支持半透明填充) |
| 裁剪 | 拖出选区,8 个手柄微调,支持三分参考线,可撤销 |
| 视图 | 滚轮缩放(以光标为中心)、空格或抓手拖拽平移、适应窗口 / 1:1 |
| 历史 | 撤销 / 重做,最多 40 步;裁剪也进历史栈 |
| 保存 | 覆盖原图,或另存为新 PNG 不动原图 |
## 安装
把这三个文件放进 `<你的库>/.obsidian/plugins/image-editor-xhz/`:
```
main.js
manifest.json
styles.css
```
然后在 Obsidian「设置 → 第三方插件」里刷新并启用 **Image Editor (标注 / 裁剪)**。
也可以直接:
```bash
npm install
npm run build # 类型检查 + 压缩打包,产出 main.js
```
开发时用 `npm run dev` 进入 esbuild 监听模式。
## 使用
### 打开编辑器
1. 在文档中的图片上右键 → **编辑图片**
2. 命令面板:`编辑光标处的图片`(光标放在那一行即可)
3. 命令面板:`打开图片进行编辑…`(模糊搜索全库图片)
4. 文件管理器里右键图片文件 → **编辑图片**
### 快捷键
| 按键 | 作用 |
| --- | --- |
| `P` `L` `A` `R` `O` `T` `M` `C` `H` | 画笔 / 直线 / 箭头 / 矩形 / 椭圆 / 文本 / 马赛克 / 裁剪 / 抓手 |
| `Ctrl/⌘ + Z` | 撤销 |
| `Ctrl/⌘ + Shift + Z`、`Ctrl/⌘ + Y` | 重做 |
| `Ctrl/⌘ + S` | 保存并替换原图 |
| `Enter` | 裁剪模式下应用裁剪 |
| `Esc` | 取消文本输入 / 退出裁剪 |
| 滚轮 | 以光标为中心缩放 |
| `Shift + 滚轮` | 水平平移 |
| 空格 + 拖拽、中键拖拽 | 平移视图 |
文本工具:点一下图片,出现输入框,`Ctrl + Enter` 确认,`Esc` 取消。
### 保存行为
- **原图是 PNG**:直接覆盖原文件,文档里的图片会立刻刷新。
- **原图是 JPG / WebP / BMP**:PNG 是有损格式之外的新格式,插件会另存为同名 `.png`,
并**自动把全库 md 里指向原图的链接改到新文件**(wikilink 和 `![]()` 都支持,
含 `|300`、`#center` 参数以及带空格路径)。是否删除原图可在设置里选,默认不动。
- **另存为**:生成 `原名-edited.png`,原图保持不变。
- 关闭标签页时如果有未保存的修改,会弹出提醒(标题栏也会带 `*` 标记)。
### 设置
设置 → 第三方插件 → Image Editor:
- 默认工具 / 颜色 / 线宽 / 文本字号
- 保存后自动关闭编辑器
- 转换格式后是否把原图移入回收站
- 阅读模式右键菜单兜底(阅读模式下 Obsidian 不一定广播右键事件,开启后会往弹出的菜单里补一个「编辑图片」)
## 实现要点
- **分层绘制**:`base`(底图)+ `composite`(底图叠所有形状)+ 屏幕 canvas。
所有形状存的是**原始图像像素坐标**,缩放、裁剪、导出都不失真。
马赛克需要读取已合成像素,所以必须在 composite 层做。
- **多开**:单个 view type + `workspace.getLeaf('tab')`,每个 leaf 一个 `ItemView` 实例,
内部按 `state.file` 区分图片;同一路径会被重定向到已开的标签页。
- **图片定位**:DOM 路径(阅读模式 `.internal-embed`、实时预览 `
` 的 `app://` src)
加上源码路径(`editor.posAtMouse` + 行内 embed 正则)双保险。
- **即时刷新**:用 `vault.modifyBinary` 写入,Obsidian 会重新生成带 mtime 的图片地址,
文档里的渲染随之更新,不需要重启或手动刷新。
## 项目结构
```
src/
main.ts 插件入口:右键菜单、命令、设置
editor-view.ts 编辑器视图:工具栏、保存、链接迁移
canvas-engine.ts 绘制引擎:形状、裁剪、历史、导出
image-resolver.ts 从右键位置 / 光标解析出图片文件
image-loader.ts 二进制 →
icons.ts 内联 SVG 图标(不依赖 lucide 版本)
utils.ts / types.ts
test/ 冒烟测试 + 逻辑测试(npm test)
```
`npm test` 会用 stub 顶替 obsidian,在 Node 里跑 29 项用例,覆盖链接替换正则、
路径清洗、几何计算以及引擎的绘制 / 撤销 / 裁剪 / 导出流程。