AtomCode 图片附件与剪贴板系统分析
持续深度分析 — 图片附件协议、剪贴板集成、Markdown 渲染 2026-06-23
1. 图片附件系统
1.1 ImageInput 结构体
rust
struct ImageInput { // 2 个字段
media_type: String, // 媒体类型 (如 "image/png", "image/jpeg")
data: String, // Base64 编码图片数据
}1
2
3
4
2
3
4
1.2 图片附件协议
用户操作:
┌───────────────────────────────────┐
│ Ctrl+V (粘贴剪贴板图片) │
│ /paste (粘贴文本 + 图片) │
│ 拖拽图片到 TUI 窗口 (Windows) │
│ 附件菜单 → 上传图片 │
└───────────────┬───────────────────┘
│
▼
剪贴板读取 → 图片数据 (Base64)
│
▼
ImageInput {
media_type: "image/png",
data: "iVBORw0KGgo..." (Base64)
}
│
▼
POST /chat 或 POST /live/message
{
message: "...",
images: [{ media_type, data }]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1.3 ChatRequest 中的图片字段
rust
struct ChatRequest { // 5 个字段
session_id: String,
message: String,
images: Option<Vec<ImageInput>>, // 可选图片
provider: Option<String>,
model: Option<String>,
}1
2
3
4
5
6
7
2
3
4
5
6
7
1.4 LiveMessageReq 中的图片字段
rust
struct LiveMessageReq { // 4 个字段
session_id: String,
message: String,
images: Option<Vec<ImageInput>>, // 可选图片
provider: Option<String>,
model: Option<String>,
}1
2
3
4
5
6
7
2
3
4
5
6
7
2. 剪贴板系统
2.1 跨平台剪贴板
Linux/macOS: CLIPBOARD, PRIMARY, SECONDARY 选择区
- 使用 X11 选择区协议
- CLIPBOARD: 标准 Ctrl+C/Ctrl+V
- PRIMARY: 鼠标选中自动复制
Windows: Ctrl+V 回退方案
- "Attach an image from the clipboard (Windows fallback for Ctrl+V)"1
2
3
4
5
6
7
2
3
4
5
6
7
2.2 支持的 MIME 类型
text/plain;charset=utf-8 — 纯文本
text/plain;charset=UTF-8 — UTF-8 文本
STRING — 兼容字符串
UTF8_STRING — UTF-8 字符串
image/png — PNG 图片
image/jpeg — JPEG 图片
text/html — HTML 格式文本1
2
3
4
5
6
7
2
3
4
5
6
7
2.3 剪贴板安全检测
x-kde-passwordManagerHint — KDE 密码管理器禁止读取剪贴板
incorrect type received from clipboard — 类型不匹配1
2
2
3. WebUI 中的附件菜单
从嵌入式 JS 提取的附件菜单字符串:
javascript
"attach.menu": "Add",
"attach.image": "Upload image",
"attach.file": "Upload file",
"attach.skill": "Choose skill",
"attach.imageSoon": "Coming soon",
"attach.removeImage": "Remove image",
"attach.tooLarge": "Images must be under {mb}M; oversized images were skipped.",
"attach.dismissError": "Dismiss",
"attach.skillsEmpty": "No skills yet. Add one at .atomcode/skills/<name>/SKILL.md...",
"attach.skillsLoading":"Loading"1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
3.1 附件限制
| 限制 | 值 |
|---|---|
| 最大图片大小 | {mb}M(可配置,默认约 5-10MB) |
| 超限行为 | 自动跳过超大图片,显示提示 |
| 支持格式 | PNG, JPEG |
3.2 WebUI 附件组件
javascript
// 附件插入组件
Zr = {
onInsert: tt, // 文本插入回调
onPickFile: () => D(!0), // 文件选择
onAddImages: rt, // 图片添加
}1
2
3
4
5
6
2
3
4
5
6
4. 图片大小限制与处理
上传统计:
1. 用户粘贴/拖拽图片
2. 检测图片大小 > 阈值 → 跳过
3. 编码为 Base64
4. 构造 ImageInput { media_type, data }
5. 发送到 daemon
服务端:
1. 接收 Base64 + media_type
2. 转发给 LLM provider (OpenAI/Anthropic 原生支持图片)
3. 流式返回文本响应1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
5. 剪贴板事件(TUI)
crossterm 事件:
EnableBracketedPaste — 启用括号粘贴模式
DisableBracketedPaste — 禁用括号粘贴模式
EnableFocusChange — 启用焦点事件
DisableFocusChange — 禁用焦点事件
PopKeyboardEnhancementFlags — 键盘增强标记弹出
PushKeyboardEnhancementFlags — 键盘增强标记推入1
2
3
4
5
6
7
2
3
4
5
6
7
6. WebUI 粘贴流程
从嵌入式 JS 提取的粘贴处理:
javascript
// textarea 上的 onPaste 处理
<textarea
class="message-input"
rows={2}
placeholder={s("chat.inputPlaceholder")}
value={u}
onInput={Qe}
onKeyDown={He}
onPaste={at} // 粘贴事件处理
/>1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
onPaste 处理器 (at) 负责:
- 读取剪贴板文本
- 读取剪贴板图片(如存在)
- 将文本插入 textarea
- 将图片添加到待发送队列
- 调用
onAddImages显示预览
7. 附件大小限制配置
rust
// 超限错误消息
"Images must be under {mb}M; oversized images were skipped."1
2
2
8. 新覆盖的盲区
| # | 之前未覆盖的领域 | 状态 | 发现 |
|---|---|---|---|
| 1 | ImageInput 结构体 | ✅ | 2 字段:media_type + base64 data |
| 2 | 剪贴板系统 | ✅ | X11 CLIPBOARD/PRIMARY + Windows 回退 |
| 3 | 附件菜单 UI | ✅ | 上传/图片/Skill 三个入口 |
| 4 | 图片大小限制 | ✅ | 可配置 MB 阈值 + 超限跳过 |
| 5 | TUI 粘贴事件 | ✅ | crossterm bracketed paste 事件 |
| 6 | MIME 类型支持 | ✅ | 文本/图片/HTML 剪贴板格式 |
逆向命令索引
bash
# 提取图片相关结构体
strings atomcode.bin.bak | grep -E "struct ImagePart|struct ImageInput|ContentBlock::Image" | sort -u
# 输出: struct ImagePart with 2 elements struct ImageInput with 2 elements struct variant ContentBlock::Image with 2 elements
# 提取支持的图片 MIME 类型
strings atomcode.bin.bak | grep -E "image/png|image/jpeg|image/webp|image/gif" | sort -u | head -5
# 提取剪贴板相关
strings atomcode.bin.bak | grep -iE "clipboard|CLIPBOARD|PRIMARY" | sort -u | head -51
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
本报告由持续分析循环生成