diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..33a0ffe --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,91 @@ +# AGENTS + +面向在本仓库工作的 AI 代理。约定之外的部分按代码里的既有写法照做。 + +## 项目性质 + +Electron 桌面应用:多源文献检索 + 本地书库 + 内置阅读器(PDF/EPUB/MOBI/AZW)+ 批注笔记 + AI 助手。 +Windows x64 免安装版,用户数据在程序同级 `data/`。 + +## 语言与风格 + +- 界面文案、提交信息、注释一律中文;标识符英文。 +- 默认不写注释。只在原因不显然时写:隐藏约束、易踩的坑、绕过某个具体 bug 的原因。不要复述代码在做什么。 +- 不用 emoji。散文里避免破折号。 +- 回复简洁,不主动扩展用户没要求的事。 + +## 硬性约束 + +### 安全边界不可绕过 + +- 渲染层永远不能传任意路径读盘。所有文件访问必须过 `main.js` 的 `resolveReadable()`,它只放行书库中真实登记的条目。 +- 分段读取会话(`src/reader/range-sessions.js`)与 sender 绑定,句柄不透明,webContents 销毁即回收。新增 IPC 时必须校验 sender。 +- 所有 `ipcMain.handle` 走 `wrap()`,同步抛出也要变成 `{ ok:false }`,否则渲染层的 `await` 无 catch,界面会永远卡在加载中。 +- 不要把密钥、token 写进日志或错误信息。AI Key 经 `safeStorage` 加密后落盘。 + +### 依赖 + +- 前端第三方库全部 vendored 在 `src/ui/vendor/`,版本在 `package.json` 中锁死(不用 `^`)。不要引入新的运行时依赖,除非用户明确要求。 +- `src/ui/vendor/pdf.worker.range.mjs` 是**手工 patch 过的** PDF.js worker,不要用上游文件覆盖。见下文。 + +## PDF 分段读取(易踩坑) + +超大 PDF 不整文件读入内存,走 `reader:rangeOpen/rangeRead/rangeClose`。`reader:bytes` 直接拒绝 PDF。 + +worker 选择策略在 `pdf-adapter.mjs`: + +- ≤256 MB:用**官方** worker(`pdf.worker.min.mjs`),即使是 range 模式。 +- >256 MB:用稀疏 worker(`pdf.worker.range.mjs`)。 + +稀疏 worker 的基础缓冲区是空的(`new Uint8Array(0)`),因此**任何对 `stream.bytes.buffer` 直接建视图的代码都会抛 RangeError**。已知踩过的坑:`preEvaluateFont` 里的 `ToUnicode` 哈希,错误表现是字体静默降级成不可见的 `ErrorFont`(文本项数量正常,但画布上没有墨迹)。必须用 `stream.getByteRange(start, end)`。 + +改动这个 worker 后,用真实 PDF 对比三条路径的渲染结果(文本项数 + 非白像素数):官方 worker 全量数据 / 官方 worker range / 稀疏 worker range。三者应一致。 + +## AI 助手 + +- 上下文范围:`selection | page | document | page-image | region-image`。 +- 只有 `selection` 需要选中文本;`page` 和 `document` 不依赖选区。 +- `document` 无论多短都强制弹确认框,并提示可能超出模型上下文限制。 +- 正文在 `ai-client.js` 按 `MAX_CHARS` 截断,保留首尾(结论常在末尾,只留开头会让模型答非所问)。 +- 图像只用 JPEG 单一编码路径,1600px / 目标 400 KB。**不要加格式回退**(PNG 等),维护成本高于收益;视觉模型按像素图块计费,格式不影响费用。 +- 三种协议:Anthropic `/messages`、OpenAI Responses `/responses`、兼容 Chat `/chat/completions`。图像负载格式各不相同,改动时三条都要验。 + +## 测试 + +```bash +npm test # Node 单测 +npx electron src/_test/electron/.integration.js # Electron 集成 +``` + +集成套件:`ai-scope`、`reader-features`、`library-notes`、`annotation`、`download`、`cover`、`startup`。 + +要求: + +- 完成任务前跑单测 + 相关集成套件。 +- 单测里有对源码的正则断言(锁死关键约定)。改了被断言的代码要同步更新断言,不要为了让测试过而弱化断言。 +- 集成测试断言"真正离开进程的内容"(真实本地 HTTP 服务收到的 body、真实渲染出的像素),不要 stub 渲染层。 +- 不要为了测试往生产代码里加 `window.__test` 之类的全局钩子,通过真实 UI 断言。 +- `library-notes` 偶发失败,重跑确认再判断。 + +## 构建 + +```bash +npm run build # 输出 dist/PeopleLib-windows-x64/ +``` + +- 输出目录固定,不带版本号。改名会导致 `data/` 被遗留在旧目录。 +- 构建保留 `data/`,但会清空其余内容。**构建前必须退出该目录下运行中的 `PeopleLib.exe`**,否则清理到一半失败,目录处于不完整状态。 +- 涉及 `data/` 的操作前后做逐文件哈希比对,确认书库、笔记、批注未被改动。 + +## 仓库 + +两个远端,用途不同: + +- `github`(公开):**只放 README 与截图**,不推源码、不推 `BUILD.md`。历史与本地无共同祖先,用独立的 orphan/docs 提交推送。 +- `origin`(私有):完整源码。 + +其他: + +- `dist/` 已 gitignore。诊断产物(`*-diagnostic.png`、`probe*.json`)也已忽略,不要提交。 +- 未跟踪文件视为用户资产,不要删除或覆盖。清理前先看 `git status --porcelain`。 +- 提交前 `git diff --cached` 检查是否混入密钥。 diff --git a/README.md b/README.md index 2e7bcf3..63d18ce 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ DRM 保护的 MOBI/AZW/AZW3、KFX、Topaz 以及损坏或不兼容的文件不 ## 开发 -源码运行、打包发布、代理与账号配置、数据位置、项目结构与测试见 [BUILD.md](BUILD.md)。 +源码运行、打包发布、代理与账号配置、数据位置、项目结构与测试说明见源码仓库中的 `BUILD.md`。本仓库只发布二进制与使用说明。 ## 免责声明