Files
chatpapers/docs/development.md
T
2026-07-20 14:40:17 +08:00

15 KiB
Raw Permalink Blame History

ChatPapers 开发文档

版本:v0.2(草案)
更新日期:2026-07-20
关联需求:requirements.md


1. 技术选型

1.1 平台与形态

选择 理由
宿主 Zotero 9 Desktop 需求明确;2026 年已有多款插件 bump 至 9.*
扩展形态 Bootstrapped Extension Zotero 官方推荐路径(非 WebExtension 受限 API
清单 manifest.json + bootstrap.js Zotero 7+ 标准;9 需注意 bootstrap 导出方式
目标版本字段 strict_min_version: "9.0"strict_max_version: "9.*" 与需求一致;若兼容 8 需额外回归

1.2 语言与工具链

选择 说明
语言 TypeScript 类型安全;配合 zotero-types
构建 esbuild(经 zotero-plugin-scaffold 社区主流;热重载友好
脚手架 zotero-plugin-template + zotero-plugin-scaffold 一键 build / start / release
UI Toolkit zotero-plugin-toolkit(注意 Z9 API 迁移) 菜单、快捷键、进度等;部分旧 API 已迁至原生
本地化 Fluent.ftl Zotero 未来方向;避免 DTD
包管理 pnpm 或 npm 团队自定,文档示例用 npm

1.3 Zotero 9 开发注意点

根据社区插件迁移经验,落地时需特别注意:

  1. bootstrap.js 导出方式
    Zotero 9 通过 scope[method] 解析生命周期函数。bootstrap 入口应产出顶层 function install/startup/shutdown/uninstall/...,避免被 esbuild IIFE 包死导致钩子找不到。常见做法:对 bootstrap.ts transpile 不 bundle,或显式配置 esbuild 保留全局函数。

  2. 废弃 ChromeUtils.import
    Firefox 128+ / Zotero 9 路径下优先使用 ESM / 官方推荐的模块加载方式,避免遗留 JSM import。

  3. UI 注册
    优先使用 Zotero 原生 PreferencePane、菜单注入点;Toolkit 大版本升级后核对 Breaking Changes。

  4. 无重启启停
    shutdown / onMainWindowUnload 必须移除 DOM、监听、定时器、未完成的 fetch AbortController。

1.4 参考文档


2. 工程结构(目标)

以 template 为起点,建议收敛为以下结构:

chatpapers/
├── addon/                      # 打包进 xpi 的静态资源
│   ├── bootstrap.js            # 由 bootstrap.ts 转译生成(顶层函数)
│   ├── manifest.json
│   ├── prefs.js                # 默认偏好
│   ├── locale/
│   │   ├── en-US/chatpapers.ftl
│   │   └── zh-CN/chatpapers.ftl
│   └── content/                # 图标、样式、偏好 XHTML 等
├── src/
│   ├── index.ts                # 插件主入口(bundle
│   ├── addon.ts                # Addon 单例、生命周期编排
│   ├── hooks.ts                # startup/shutdown/window hooks 业务
│   ├── modules/
│   │   ├── ui/
│   │   │   ├── readerPane.ts   # PDF Reader 侧边栏注入
│   │   │   ├── chatView.ts     # 对话 UI
│   │   │   └── prefs.ts        # 偏好面板
│   │   ├── pdf/
│   │   │   ├── extractor.ts    # 文本提取适配层
│   │   │   └── context.ts      # 上下文裁剪 / 分块
│   │   ├── llm/
│   │   │   ├── client.ts       # OpenAI 兼容客户端(stream / models
│   │   │   ├── providers.ts    # Ollama / LM Studio / OpenRouter 等预设
│   │   │   ├── prompts.ts      # 系统提示与总结模板
│   │   │   └── types.ts
│   │   ├── storage/
│   │   │   ├── prefs.ts        # Preference 读写封装
│   │   │   └── sessions.ts     # 会话持久化
│   │   └── zotero/
│   │       ├── notes.ts        # 创建/追加 Note
│   │       └── items.ts        # 元数据读取
│   └── utils/
│       ├── logger.ts
│       └── abort.ts
├── docs/
│   ├── requirements.md
│   └── development.md
├── zotero-plugin.config.ts
├── package.json
├── tsconfig.json
└── readme.md

命名约定

  • 插件 IDyehongyu@njau.edu.cn
  • Pref 前缀:extensions.chatpapers.*
  • Fluent 文件:chatpapers.ftl(避免与其它插件冲突)
  • 日志前缀:[ChatPapers]

3. 架构设计

3.1 分层

┌─────────────────────────────────────────┐
│  UI LayerReader Pane / Prefs / Menus) │
├─────────────────────────────────────────┤
│  Application Services                    │
│  ChatSession · Summarize · SaveNote      │
├─────────────────────────────────────────┤
│  Adapters                                │
│  PdfExtractor · LlmClient · PrefStore    │
├─────────────────────────────────────────┤
│  Zotero Platform APIs                    │
└─────────────────────────────────────────┘

原则:

  • UI 不直接 fetch;统一经 LlmClient
  • PDF 提取细节隔离在 PdfExtractor,便于 Z9 API 变更时单点替换
  • 所有偏好读写经 PrefStore,禁止散落魔法字符串

3.2 核心时序:提问

User 发送
  → ChatView 收集:input + selection? + session history
  → ContextBuildermetadata + pdfText(截断) + messages
  → LlmClient.chatStream(messages, AbortSignal)
  → 逐 chunk 更新 UI
  → 完成:写入 SessionStore

3.3 核心时序:保存笔记

User 点击「保存为笔记」
  → 取当前 Assistant 消息(或总结全文)
  → Notes.createChildNote(item, { title, bodyHtmlOrMd })
  → Toast / 状态提示成功

Markdown → Zotero Note:优先转为简单 HTML(标题、段落、列表),保持可读。


4. 关键模块设计要点

4.1 Reader 侧边栏

  • 在 Reader 窗口加载后注册侧边栏 Tab(具体 DOM/API 以 Zotero 9 Reader 为准,实现前用 DevTools 探测现网结构)
  • Pane 与 itemID / attachmentKey 绑定;切换 PDF 时重置或切换会话
  • 卸载时移除 Tab 与事件,防止重复注入

4.2 PDF 文本提取

实现顺序建议:

  1. 优先:使用 Zotero 已索引全文 / Reader 内可获取的文本 API(若可用)
  2. 次选:通过 PDF.js / 附件本地路径做页面级提取
  3. 失败:UI 提示「无文本层」,不发起无效大请求

ContextBuilder 策略(MVP):

  • len(text) <= maxContextChars:全文注入
  • 否则:标题页元数据 + 前 N 字 + 后 M 字,或「先让模型做分段摘要」二选一(由需求待决策项决定)

4.3 LLM Client

MVP 以 单一 OpenAI 兼容客户端 覆盖 Ollama、LM Studio、OpenRouter 及各类云端;差异用 Provider 预设表 表达,而不是为每个厂商写一套协议。

POST {baseUrl}/chat/completions
Authorization: Bearer {apiKey}   # 本地预设可省略
Content-Type: application/json

{
  "model": "...",
  "messages": [...],
  "stream": true,
  "temperature": 0.3,
  "max_tokens": 2048
}

Provider 预设(与需求 §4.4 对齐)

预设 默认 Base URL Key 额外行为
Ollama http://127.0.0.1:11434/v1 可空 可选 GET /v1/models 刷新列表;连接失败提示启动 Ollama
LM Studio http://127.0.0.1:1234/v1 可空 可选拉取 models;提示开启 Local Server
OpenRouter https://openrouter.ai/api/v1 必填 附加可选头 HTTP-RefererX-Title
OpenAI / DeepSeek / 硅基流动 各官方 /v1 必填 标准 Bearer
自定义 用户输入 按需 无额外头

建议代码结构:

src/modules/llm/
  ├── client.ts          # 统一 chatStream / listModels
  ├── providers.ts       # 预设常量:id → { baseUrl, needsKey, headers? }
  ├── url.ts             # Base URL 规范化(补 /v1、去尾斜杠)
  ├── prompts.ts
  └── types.ts

要求:

  • 解析 SSE data: {...};处理 [DONE]
  • AbortController 支持停止生成
  • 统一错误类型:AuthError / RateLimitError / NetworkError / LocalServerDown / ProviderError
  • 禁止将 API Key 打进日志;verbose 模式下对 Header 脱敏
  • Base URL 规范化:兼容 http://127.0.0.1:11434.../v1
  • 切换预设时:写入默认 Base URL,不覆盖用户已改过的自定义 URL(可用「重置为预设默认」按钮)

本地联调建议:

  • Ollamaollama pull qwen2.5 → Base URL 默认 → model 填实际名
  • LM Studio:加载模型并 Start Server → 端口若非 1234 则改 URL
  • OpenRouter:填 Key + 模型如 openai/gpt-4o-mini

4.4 会话存储

MVP 可选方案:

方案 优点 缺点
A. Preferences 存 JSON 实现快 容量与性能差
B. 插件数据目录 JSON 文件 清晰、易清理 需处理路径与并发
C. IndexedDB / SQLite 可扩展 实现成本高

建议 MVP 用 B,路径形如:{profile}/chatpapers/sessions/{itemKey}-{attachmentKey}.json

4.5 偏好面板

使用 Zotero.PreferencePanes.registerZ9 推荐原生 API)注册:

  • Provider:下拉预设(Ollama / LM Studio / OpenRouter / … / 自定义)+「重置为预设默认」
  • 基本:Base URL、Key(本地可空)、Model、「刷新模型列表」
  • 生成:temperature、maxTokens、timeout、maxContextChars
  • OpenRouter 高级:Referer / Title(可折叠)
  • 提示词:system / summary 可编辑文本框
  • 隐私说明:明确当前预设是本地还是云端

5. 开发环境搭建

5.1 前置依赖

  • Node.js 20+LTS
  • 已安装 Zotero 9 桌面版
  • Git

5.2 初始化(计划命令)

需求确认后,推荐从官方模板生成:

# 示例:使用 zotero-plugin-template 克隆后改名
npx tiged windingwind/zotero-plugin-template chatpapers
cd chatpapers
npm install

随后修改:

  • package.jsonaddonName / addonID / addonRef
  • addon/manifest.jsonstrict_*_version 设为 Zotero 9
  • 清理 demo 代码,按本文结构迁入模块

5.3 本地调试

典型脚本(以 scaffold 为准):

npm run start      # 启动 Zotero + 加载未打包插件 + 热重载
npm run build      # 产出 build/ 与 xpi
npm run lint

调试技巧:

  1. 在 Zotero 中打开错误控制台(查看 [ChatPapers] 日志)
  2. 对 Reader 窗口使用独立 DevTools(若可用)检查侧边栏 DOM
  3. 用错误 Base URL 验证错误提示路径
  4. 用本地 Ollamahttp://127.0.0.1:11434/v1)验证兼容协议

5.4 环境变量

Scaffold 通常需要本机 Zotero 可执行文件路径,例如:

# Windows 示例(按实际安装路径调整)
set ZOTERO_PLUGIN_ZOTERO_CMD=C:\Program Files\Zotero\zotero.exe

具体变量名以当前 zotero-plugin-scaffold 文档为准。


6. 编码规范

  1. 只改任务相关代码;不顺手大重构模板无关文件
  2. TypeScript strict;避免无必要 any
  3. 异步:统一 async/await;外部请求必须可 Abort
  4. UI 字符串全部走 Fluent,禁止硬编码中文/英文(开发期临时除外,合并前清掉)
  5. 公共工具放 utils/;业务不互相深层耦合
  6. 每个模块顶部保持短注释(「做什么」),避免长篇叙述

6.1 建议的 npm scripts

{
  "scripts": {
    "start": "zotero-plugin serve",
    "build": "zotero-plugin build",
    "lint": "eslint .",
    "release": "zotero-plugin release"
  }
}

(实际命令以模板生成结果为准。)


7. 测试策略

层级 内容 时机
手工冒烟 安装、侧边栏、提问、总结、存笔记、禁用清理 每个里程碑
单元测试 ContextBuilder 截断、URL 规范化、错误映射 M2+
集成(可选) scaffold + Mocha 在真实 Zotero 中跑 M4

最低验收清单见需求文档里程碑 M1–M4。

手工测试用例(MVP

  1. 未配置 API Key → 发送时友好提示
  2. 错误 Key → 401 可读错误
  3. 正常 PDF → 流式回复(分别用 Ollama / OpenRouter 各测一次)
  4. 选中文本提问 → 上下文包含选区
  5. 超长 PDF → 截断提示仍可回答
  6. 无文本 PDF → 明确失败原因
  7. Ollama 未启动 → LocalServerDown 友好提示
  8. 保存笔记 → 条目下可见 Note
  9. 禁用插件 → 侧边栏与菜单消失,无报错

8. 发布流程

  1. 更新版本号(package.json / manifest.json
  2. npm run build 生成 .xpi
  3. GitHub Release 上传 .xpi
  4. 配置 update_url 指向 updates.jsonScaffold 可生成)
  5. 在干净 Zotero 9 Profile 中验证安装与更新

版本号遵循 SemVerMAJOR.MINOR.PATCH


9. 实施计划(与需求里程碑对齐)

阶段 工程任务 预估
M0 确认需求待决策项;冻结插件 ID / 协议 0.5d
M1 从 template 初始化;偏好页;Reader 占位 Pane 23d
M2 PDF 提取 + 非流式/流式 Client + 单轮对话 35d
M3 多轮 UI、总结模板、存 Note、Abort 34d
M4 会话持久化、i18n、日志脱敏、打包与 README 使用说明 23d

以上为单人全职粗估,随 Reader API 熟悉度波动。


10. 目录外约定

  • 密钥与真实 API Key 不得提交仓库;提供 .env.example(若需要)仅含路径类配置
  • docs/ 变更随功能演进;重大行为变更先更新需求再改代码
  • Issue / PR 标题建议前缀:feat: / fix: / docs: / chore:

11. 下一步(工程开工清单)

需求方确认 requirements.md §10 待决策事项 后,按序执行:

  1. zotero-plugin-template 初始化仓库代码
  2. 改名与 Zotero 9 manifest
  3. 落地 PrefStore + 空 Reader Pane
  4. 实现 LlmClient 与最小 Chat UI
  5. 接通 PDF 文本与「保存为笔记」

如需,可在确认待决策项后直接进入 M1 脚手架初始化。