Imported from zdzDesigner/graber (
AGENTS.md). Install upstream withnpx skills add zdzDesigner/graber. Copyright stays with the author.
Coding Agent 开发指南 - 多平台论坛内容获取扩展
项目概述
这是一个 Chrome 扩展程序,用于从多种论坛(Discourse、V2EX、Reddit、GitHub Issues 等)获取帖子内容并下载为 txt 文件。采用适配器模式支持多平台扩展。
项目结构
├── manifest.json # 扩展配置文件 (Manifest V3)
├── content.js # 内容脚本,负责页面内容提取
├── background.js # 背景脚本,处理右键菜单逻辑
├── popup.html # 弹窗界面 HTML
├── popup.js # 弹窗界面逻辑,处理下载功能
├── adapters/ # 平台适配器目录
│ ├── base-adapter.js # 适配器基类(抽象接口)
│ ├── platform-adapter-manager.js # 适配器管理器
│ ├── discourse-adapter.js # Discourse 论坛适配器
│ ├── v2ex-adapter.js # V2EX 论坛适配器
│ ├── reddit-adapter.js # Reddit 适配器
│ └── github-issues-adapter.js # GitHub Issues 适配器
├── CHROME_PLUGIN_API.md # Chrome API 文档
└── PLATFORM_INTEGRATION.md # 平台集成指南
构建/测试命令
安装扩展
- 打开 Chrome 浏览器,访问
chrome://extensions/ - 开启开发者模式
- 点击"加载已解压的扩展程序",选择此项目目录
本地开发
# 无需构建步骤,直接加载即可
# 修改代码后点击扩展卡片上的刷新按钮即可重新加载
# 验证 JavaScript 语法
npx eslint *.js adapters/*.js
# 验证 Manifest JSON
node -e "console.log(JSON.parse(require('fs').readFileSync('manifest.json')))"
测试特定平台适配器
// 在浏览器控制台中测试特定适配器
const adapter = new DiscourseAdapter(); // 或 V2EXAdapter, RedditAdapter 等
console.log('平台检测:', adapter.detect());
const queue = new Map();
adapter.extractCurrentVisibleContent(queue);
console.log('提取内容:', Array.from(queue.entries()));
代码风格准则
JavaScript/HTML 准则
- 使用 2 空格缩进
- 使用驼峰命名法 (camelCase) 命名变量和函数
- 常量使用 UPPER_SNAKE_CASE
- 字符串使用双引号
- 行末不加分号
- 类定义使用条件检查避免重复声明:
if (typeof window.ClassName === 'undefined') { class ClassName { ... } }
适配器开发规范
- 所有适配器必须继承
BaseAdapter - 实现四个必需方法:
detect()、extractCurrentVisibleContent()、processNewPostElement()、getContentSelectors() detect()方法应返回布尔值,快速判断当前页面是否匹配extractCurrentVisibleContent()接收 Map 参数,按 postNumber 存储内容- 使用备选选择器策略,提高兼容性
类型规范
- 使用 JavaScript 而非 TypeScript
- 复杂对象使用 JSDoc 注释:
/** * @param {Map} contentQueue - 内容存储队列 * @returns {void} */
错误处理
- 使用 try-catch 包裹可能失败的操作
- 异步操作必须检查
chrome.runtime.lastError - 提供降级方案(fallback)
Chrome 扩展 API 使用
- 使用 Manifest V3 语法
- Content Scripts 使用
chrome.runtime.onMessage通信 - Background 脚本使用 Service Worker 生命周期
功能实现说明
权限说明
activeTab: 获取活动标签页信息contextMenus: 创建右键菜单项scripting: 向页面注入脚本
核心流程
- 用户通过右键菜单或弹窗按钮触发内容提取
content.js从页面中提取.topic-post .cooked元素的内容- 如果是 Discourse 页面,则按楼层顺序整理内容
- 将提取的内容通过弹窗展示或下载为 txt 文件
关键组件
content.js: 实现内容提取逻辑,优先匹配 Discourse 结构,提供兜底机制background.js: 处理右键菜单注册和点击事件popup.html/popup.js: 弹窗界面和下载逻辑adapters/: 适配器模式支持多平台(Discourse、V2EX、Reddit、GitHub Issues)
适配器模式说明
项目采用适配器模式来支持多个论坛平台:
- BaseAdapter: 抽象基类,定义所有适配器必须实现的接口
- PlatformAdapterManager: 管理所有适配器,负责检测当前页面平台并分派到对应适配器
- 具体适配器: 每个平台一个适配器(如 DiscourseAdapter、V2EXAdapter 等),实现平台特定的内容提取逻辑
适配器工作流程:
content.js初始化PlatformAdapterManager- 管理器遍历所有适配器,调用
detect()方法检测当前页面 - 第一个返回
true的适配器被选中 - 调用选中适配器的
extractCurrentVisibleContent()提取内容 - 如果页面使用虚拟滚动,通过
MutationObserver监测新内容并调用processNewPostElement()
扩展开发最佳实践
Content Scripts
- 小心处理与页面代码的冲突
- 使用内联样式以避免受页面 CSS 影响
- 使用
chrome.runtime.onMessage与其他组件通信 - 避免与页面脚本冲突,使用
window对象存储扩展全局变量
Background Script
- 遵循 Manifest V3 的 Service Worker 生命周期
- 高效处理用户交互事件
- 使用
chrome.scripting.executeScript注入脚本时注意执行顺序
用户交互
- 提供清晰的状态反馈 (加载中、成功、失败)
- 限制最大层级以避免影响页面元素
- 合理使用浏览器原生 API
适配器开发
- 每个适配器设置
this.name标识便于调试 detect()方法应快速返回,避免复杂 DOM 查询- 使用多个备选选择器提高鲁棒性
- 处理动态加载的内容(虚拟滚动)
- 保持向后兼容性(新旧选择器共存)
调试指南
Content Script 调试
// 在页面控制台中查看扩展日志
console.log("Extension:", message);
// 检查适配器是否被正确检测
const manager = window.platformAdapterManager;
console.log("活跃适配器:", manager.getActiveAdapter());
// 手动触发内容提取
const adapter = manager.getActiveAdapter();
const queue = new Map();
adapter.extractCurrentVisibleContent(queue);
Background Script 调试
- 访问
chrome://extensions/ - 找到扩展,点击"background page"或"service worker"链接
- 在开发者工具中查看日志
常见问题排查
// 检查权限
checkPermissions();
// 检查内容脚本是否注入
if (typeof window.platformAdapterManager === 'undefined') {
console.error("适配器管理器未初始化");
}
性能优化
- 最小化 DOM 查询,缓存选择器结果
- 使用
MutationObserver时记得disconnect() - 避免在滚动事件中频繁操作 DOM
- 使用高效的 CSS 选择器(ID > class > tag)
安全注意事项
- 避免使用
innerHTML,优先使用innerText或textContent - 验证所有从页面获取的数据
- 注意 CSP (Content Security Policy) 限制
- 权限最小化原则
最佳实践
- 添加详细的
console.log便于调试 - 每个适配器设置
this.name标识 - 支持虚拟滚动加载的动态内容
- 保持向后兼容性(新旧选择器共存)
维护注意事项
- 平台更新 DOM 结构时需要更新适配器选择器
- 使用多个备选选择器提高鲁棒性
- 记录关键业务逻辑变更原因
- 定期测试各平台适配器
新增平台适配器步骤
- 在
adapters/创建[platform]-adapter.js - 继承
BaseAdapter并实现四个必需方法 - 在
manifest.jsoncontent_scripts 中引入新适配器文件 - 在
platform-adapter-manager.js中注册新适配器 - 更新文档和测试
