Imported from zhanglongxiao111/obsidian-image-manager (
AGENTS.md). Install upstream withnpx skills add zhanglongxiao111/obsidian-image-manager. Copyright stays with the author.
AGENTS.md — 小芒果素材库 Obsidian 插件
本文件为 AI Agent 提供项目上下文。修改项目前必须通读。
项目定位
这是一个 Obsidian 桌面端插件(xmg-image-manager),用于管理"小芒果"公众号的图片素材。核心能力:
- 瀑布流浏览
02-资料库/02-素材库/下的图片 - 用固定字段 + 补充标签 + 描述,对每张图片做结构化打标
- 自动扫描 Markdown 文章,记录图片被引用关系
- 所有数据存入 SQLite(通过
sql.js,不依赖原生模块) - 提供 GIF 重新截取工作台(依赖 ffmpeg)
- 外部 Agent 通过
tools/xmg_image_db.py脚本读写同一数据库
目录结构
<仓库根目录>/ ← 源码根目录(本仓库)
├── src/
│ ├── main.ts ← 插件主体:数据库类 + 扫描器 + Plugin 入口
│ ├── image-manager-view.ts ← 瀑布流视图:筛选 + 卡片 + 拖拽 + 删除
│ ├── modals.ts ← 弹窗:编辑信息 / 删除确认 / 图片预览
│ ├── gif-workbench-modal.ts ← GIF 重新截取工作台弹窗
│ ├── settings-tab.ts ← 插件设置页
│ ├── constants.ts ← 固定字段定义 FIELD_GROUPS + 默认设置
│ ├── types.ts ← 共享类型定义
│ ├── utils.ts ← 通用工具函数
│ ├── errors.ts ← DatabaseLockedError
│ ├── ffmpeg.ts ← ffmpeg 调用 + GIF 导出
│ └── video-time.ts ← 视频时间格式化 / 解析
├── tools/
│ └── xmg_image_db.py ← Agent 写库脚本(Python CLI)
├── styles.css ← 视图样式
├── manifest.json ← Obsidian 插件元信息
├── esbuild.config.mjs ← 构建脚本,产物输出到 vault 插件目录
├── package.json
├── tsconfig.json
└── README.md ← 面向人类的完整文档
运行时目录:
| 用途 | 路径 |
|---|---|
| Obsidian 加载目录 | <Vault>\.obsidian\plugins\xmg-image-manager\ |
| 正式数据库 | <Vault>\06-工具箱\01-共享工具\xmg-image-manager\image-assets.sqlite |
| 锁文件 | <数据库路径>.lock |
| 素材图片根目录 | <Vault>\02-资料库\02-素材库\ |
技术栈
| 层面 | 技术 |
|---|---|
| 运行环境 | Obsidian Desktop(Electron + Node.js) |
| 语言 | TypeScript 5.8 / Python 3 |
| 数据库 | SQLite via sql.js(纯 WASM,无原生依赖) |
| 构建 | esbuild(CJS 格式,target es2022) |
| 视频处理 | ffmpeg(本地可执行文件) |
| 外部依赖 | obsidian、sql.js(仅两个) |
架构概览
核心类
XmgImageManagerPlugin (main.ts)
├── ImageAssetDatabase ← SQLite 读写、锁机制、schema 维护
├── ImageScanner ← 扫描素材文件 + 扫描 Markdown 引用
├── ImageManagerView ← 瀑布流 UI + 筛选 + 卡片渲染
│ └── GifWorkbenchModal ← GIF 重新截取弹窗
├── AssetInfoModal ← 编辑素材信息弹窗
├── XmgImageManagerSettingTab ← 设置页
└── tools/xmg_image_db.py ← 外部 Agent CLI(独立进程)
数据流
图片文件 Markdown 文章
│ │
▼ ▼
ImageScanner.scanAssets() ImageScanner.scanArticleRefs()
│ │
└──────► ImageAssetDatabase ◄──┘
│
▼
SQLite 文件
│
┌─────────┼─────────┐
▼ ▼ ▼
瀑布流视图 编辑弹窗 Agent 脚本
数据库表
| 表 | 用途 |
|---|---|
assets |
图片索引(路径、大小、修改时间、是否缺失) |
tags + asset_tags |
自由补充标签 |
article_refs |
图片被哪些 Markdown 引用 |
descriptions |
图片描述(内容、使用建议、封面建议) |
field_options + asset_fields |
固定字段选项和关系 |
asset_video_sources |
GIF 来源视频记录 + 画面裁剪坐标(crop_x/y/w/h) |
asset_generation_sources |
AI 生成图片提示词 |
scan_logs |
扫描日志 |
固定字段
7 组固定字段是主要筛选入口,定义在 src/constants.ts 的 FIELD_GROUPS:
| 分类 (category) | 中文名 | 多选/单选 | 选项 |
|---|---|---|---|
material_type |
素材类型 | 单选 | 二维码、表情包、视频GIF、视频截图、教学场景照片、动作示意照片、图解、封面图、其他 |
source |
素材来源 | 单选 | AI生成、视频截取、人工上传、秀米导出、网络参考、其他 |
person |
人物 | 多选 | 小芒果、Vita、其他人物、无人 |
scene |
场景 | 多选 | 雪道、室内训练、比赛、器材、社群引导、公众号排版 |
action |
技术动作 | 多选 | 前刃、后刃、换刃、刻滑、基础站姿、脚踝、膝盖、重心 |
action_scope |
动作范围 | 单选 | 全身、上半身、下半身、脚踝、膝盖、髋部、雪板、器材、无 |
usage |
用途 | 多选 | 文章正文、开头引入、步骤讲解、错误示范、对比图、结尾关注、封面 |
筛选逻辑:同组"或",跨组"且"。
关键机制
SQLite 读写锁
插件和 Agent 共用同一 SQLite 文件,通过文件锁(.lock)互斥:
- 插件写入:
withWriteTransaction()→ 创建.lock→ 从磁盘重新加载 → BEGIN → ensureSchema → 写入 → COMMIT → 落盘 → 释放锁 - Agent 写入:
write_lock()上下文管理器 → 创建.lock→ 连接 → ensure_schema → 写入 → commit → 释放锁 - 只读查询不需要锁,但会检查锁是否存在(存在则拒绝)
构建流程
npm run build # esbuild 打包 → 输出到 .obsidian/plugins/xmg-image-manager/
构建脚本自动复制:main.js、styles.css、manifest.json、sql-wasm-browser.wasm
默认输出到源码同级的 xiaomangguo Vault,可通过 $env:XMG_VAULT_ROOT 覆盖。
扫描触发
- 初始化时:
onLayoutReady→ 自动执行一次完整扫描 - 文件变更时:
create/modify/delete/rename事件 → 800ms 防抖 → 选择性重新扫描 - 手动触发:工具栏按钮 / 命令面板
Agent CLI
tools/xmg_image_db.py 提供以下命令:
| 命令 | 读写 | 用途 |
|---|---|---|
list-assets |
只读 | 列出所有素材 |
list-undescribed |
只读 | 列出未描述素材 |
get-asset |
只读 | 按 ID 查询 |
get-asset-by-path |
只读 | 按路径查询 |
list-refs |
只读 | 查询引用文章 |
list-video-sources |
只读 | 查询视频来源 |
export-video-sources |
只读 | 导出 GIF 片段候选 |
upsert-file |
写入 | 按路径登记素材 |
set-tags |
写入 | 设置补充标签 |
set-fields |
写入 | 设置固定字段 |
describe |
写入 | 写入描述 + 可选固定字段 |
set-video-source |
写入 | 写入 GIF 来源视频 |
export-jianying |
只读 | 导出视频源片段到剪映草稿(需 pyJianYingDraft) |
register-asset |
写入 | 一次性登记新素材(路径 + 描述 + 字段 + 提示词) |
所有输出为 JSON,方便管道处理。
强制规则
以下规则无条件遵守,不可因"效率"或"简化"而跳过。
- 不要移动、删除、重命名正式图片素材文件
- 不要修改 Markdown 文章正文——扫描引用只更新 SQLite
- 不要裸写 SQLite——插件侧用
ImageAssetDatabase方法,Agent 侧用tools/xmg_image_db.py - 改固定字段选项时,三处必须同步:
src/constants.ts→FIELD_GROUPStools/xmg_image_db.py→FIELD_GROUPSREADME.md→ 固定字段说明
- 改完插件代码必须构建——Obsidian 只加载编译产物
- 构建后需要在 Obsidian 重新加载插件——关闭再开启,或重启 Obsidian
- 写入前必须获取锁——锁存在时不写入,提示用户稍后重试
- 不做会丢数据的迁移——用
CREATE TABLE IF NOT EXISTS+ 兼容式补字段 - 测试写入不要污染正式库——用
$env:XMG_IMAGE_DB_PATH指向临时副本
验证清单
每次交付改动前执行:
Set-Location "C:\path\to\obsidian-image-manager"
npx tsc --noEmit # TypeScript 类型检查
npm run build # 构建
python -m py_compile tools\xmg_image_db.py # Python 语法检查
改了数据库读写时追加:
python tools\xmg_image_db.py list-assets
python tools\xmg_image_db.py list-undescribed
常见陷阱
| 问题 | 原因 | 解决 |
|---|---|---|
| 插件加载失败 | 缺少 main.js 或 sql-wasm-browser.wasm |
执行 npm run build |
| 界面没更新 | Obsidian 缓存旧 main.js |
重新加载插件或重启 Obsidian |
| 写入被拒绝 | .lock 残留 |
确认无写入进程后手动删除 |
| 引用数不对 | 素材或引用未扫描 | 先扫素材、再扫引用 |
sql.js 模块找不到 |
本地依赖未安装 | 执行 npm install |
| Python 脚本路径错误 | 绝对路径或非 vault 内路径 | 使用 vault 相对路径 |
用户画像
- 仓库维护者:通过 GitHub 维护和发布插件源码
- 内容创作者:小芒果(非技术用户),通过 Obsidian UI 操作素材库
- 使用场景:单板滑雪教学公众号的图文内容生产
- 设计原则:界面清楚、直接、少填字;固定字段用按钮不用手打;缩略图不裁切