Files
peoplelib/AGENTS.md
T
lofyer 381c07733a feat: 支持 macOS arm64 打包,修复图标未入库
新增 build-mac.js,产出 ad-hoc 签名的 .app 与 DMG。只能在 macOS 上
构建:DMG 依赖 hdiutil,且 Apple Silicon 拒绝执行未签名二进制,改名
与改 plist 后必须用 codesign 重签。

按官方 darwin-arm64 运行时的真实结构处理四处易错点:用 ditto 解压以
保留 framework 符号链接、删除重打包后失效的 ElectronAsarIntegrity、
为改名后的 helper 补上 CFBundleExecutable、签名由内向外且单独签
Squirrel 的 ShipIt。

打包版数据目录改为按平台决定:macOS 走 appData,避免写进 DMG 挂载后
只读的 .app 内部并在升级覆盖时丢失书库;Windows 便携版行为不变。
窗口图标在非 Windows 平台改用 PNG。

.gitignore 的 dist 规则补上前导斜杠。此前它同时匹配 icons/dist/,
导致构建与单测依赖的图标从未入库,新克隆的仓库无法构建。
2026-08-03 16:18:47 +08:00

6.6 KiB
Raw Blame History

AGENTS

面向在本仓库工作的 AI 代理。约定之外的部分按代码里的既有写法照做。

项目性质

Electron 桌面应用:多源文献检索 + 本地书库 + 内置阅读器(PDF/EPUB/MOBI/AZW+ 批注笔记 + AI 助手。 Windows x64 免安装版,用户数据在程序同级 data/

语言与风格

  • 界面文案、提交信息、注释一律中文;标识符英文。
  • 默认不写注释。只在原因不显然时写:隐藏约束、易踩的坑、绕过某个具体 bug 的原因。不要复述代码在做什么。
  • 不用 emoji。散文里避免破折号。
  • 回复简洁,不主动扩展用户没要求的事。

硬性约束

安全边界不可绕过

  • 渲染层永远不能传任意路径读盘。所有文件访问必须过 main.jsresolveReadable(),它只放行书库中真实登记的条目。
  • 分段读取会话(src/reader/range-sessions.js)与 sender 绑定,句柄不透明,webContents 销毁即回收。新增 IPC 时必须校验 sender。
  • 所有 ipcMain.handlewrap(),同步抛出也要变成 { 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/rangeClosereader:bytes 直接拒绝 PDF。

worker 选择策略在 pdf-adapter.mjs

  • ≤256 MB:用官方 workerpdf.worker.min.mjs),即使是 range 模式。
  • 256 MB:用稀疏 workerpdf.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 需要选中文本;pagedocument 不依赖选区。
  • document 无论多短都强制弹确认框,并提示可能超出模型上下文限制。
  • 正文在 ai-client.jsMAX_CHARS 截断,保留首尾(结论常在末尾,只留开头会让模型答非所问)。
  • 图像只用 JPEG 单一编码路径,1600px / 目标 400 KB。不要加格式回退(PNG 等),维护成本高于收益;视觉模型按像素图块计费,格式不影响费用。
  • 三种协议:Anthropic /messages、OpenAI Responses /responses、兼容 Chat /chat/completions。图像负载格式各不相同,改动时三条都要验。

测试

npm test                                          # Node 单测
npx electron src/_test/electron/<name>.integration.js   # Electron 集成

集成套件:ai-scopereader-featureslibrary-notesannotationdownloadcoverstartup

要求:

  • 完成任务前跑单测 + 相关集成套件。
  • 单测里有对源码的正则断言(锁死关键约定)。改了被断言的代码要同步更新断言,不要为了让测试过而弱化断言。
  • 集成测试断言"真正离开进程的内容"(真实本地 HTTP 服务收到的 body、真实渲染出的像素),不要 stub 渲染层。
  • 不要为了测试往生产代码里加 window.__test 之类的全局钩子,通过真实 UI 断言。
  • library-notes 偶发失败,重跑确认再判断。

构建

npm run build     # Windows,输出 dist/PeopleLib-windows-x64/
npm run build:mac # macOS arm64,输出 .app 与 .dmg,只能在 macOS 上跑

Windows

  • 输出目录固定,不带版本号。改名会导致 data/ 被遗留在旧目录。
  • 构建保留 data/,但会清空其余内容。构建前必须退出该目录下运行中的 PeopleLib.exe,否则清理到一半失败,目录处于不完整状态。
  • 涉及 data/ 的操作前后做逐文件哈希比对,确认书库、笔记、批注未被改动。

macOSbuild-mac.js,易踩坑):

  • 不能交叉构建。DMG 需要 hdiutil,且 Apple Silicon 内核直接拒绝执行未签名二进制;改名与改 plist 会让 Electron 原始签名失效,必须用 macOS 的 codesign 重签(ad-hoc --sign - 即可)。
  • 解压官方 zip 必须用 dittoElectron Framework.framework 内有符号链接,用 Node 或 unzip 解压会展开成副本,签名随即失效。
  • 重打包后 Info.plist 里的 ElectronAsarIntegrity 必须删除,否则启动即报完整性错误。
  • helper 的 plist 没有 CFBundleExecutable,靠 bundle 名推断可执行文件名。重命名 helper 后必须显式补上该字段,否则渲染进程起不来,界面一片空白。
  • 签名严格由内向外:嵌套可执行文件 → helper → framework → 外层 .app。带版本的 framework 签 Versions/ASquirrel.frameworkResources/ShipIt 是独立可执行文件,要单独签。
  • 数据目录走 ~/Library/Application Support/PeopleLib不要沿用 Windows 的便携布局:.app 在 DMG 里只读,且升级覆盖会删掉用户书库。
  • .icnsnpm run icons:icns 生成,纯 Node 实现(icns 自 10.7 起内嵌 PNG),不依赖 macOS 的 iconutil。窗口图标在非 Windows 平台用 PNG.ico 只有 Windows 认。

仓库

两个远端,用途不同:

  • github(公开):只放 README 与截图,不推源码、不推 BUILD.md。历史与本地无共同祖先,用独立的 orphan/docs 提交推送。
  • origin(私有):完整源码。

其他:

  • 根目录 dist/ 已 gitignore,规则写作 /dist/必须保留前导斜杠:不加会连 icons/dist/ 一起忽略,新克隆的仓库缺图标,构建直接失败。诊断产物(*-diagnostic.pngprobe*.json)也已忽略,不要提交。
  • 未跟踪文件视为用户资产,不要删除或覆盖。清理前先看 git status --porcelain
  • 提交前 git diff --cached 检查是否混入密钥。