# MarkSnap **Repository Path**: lls0229/mark-snap ## Basic Information - **Project Name**: MarkSnap - **Description**: 从零构建 Edge 浏览器插件:一键剪藏网页文章到语雀知识库 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-22 - **Last Updated**: 2026-05-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 从零构建 Edge 浏览器插件:一键剪藏网页文章到语雀知识库 ## 背景 日常浏览技术文章和博客时,经常想把优质内容保存到语雀知识库,方便后续检索和整理。但手动操作太繁琐——复制标题、粘贴正文、下载图片、上传到语雀、整理格式,一篇文章至少要花几分钟。 于是花了一天时间撸了一个 Edge 扩展,实现**一键抓取网页文章正文,自动转为 Markdown,图片下载上传到语雀,直接入库**。这篇文章记录完整的架构设计和实现过程。 ## 功能演示 | 触发方式 | 操作 | 适用场景 | | --- | --- | --- | | 工具栏弹窗 | 点击扩展图标 → 预览内容 → 选择知识库 → 保存 | 需要编辑标题、选择目标知识库 | | 右键菜单 | 页面右键 → "保存到语雀" | 一键快存到默认知识库 | ## 技术选型 扩展基于 **Manifest V3** 标准开发,Edge 和 Chrome 通用(两者都是 Chromium 内核)。 | 能力 | 方案 | 说明 | | --- | --- | --- | | 文章正文提取 | Mozilla Readability | Firefox 阅读模式同款引擎,自动去除导航/广告/侧边栏 | | HTML → Markdown | Turndown.js | 最成熟的 JS Markdown 转换库 | | 图片中转 | Service Worker 下载 → 语雀附件 API 上传 | 解决外链图片防盗链/失效问题 | | 认证 | 语雀 Personal Token | 用户自行生成,存储在 chrome.storage | | UI | 原生 HTML/CSS/JS | 零依赖,弹窗 420px 宽度 | ## 项目结构 ``` MarkSnap/ ├── manifest.json # MV3 扩展配置 ├── background/ │ └── service-worker.js # 右键菜单、图片下载上传、API 调度 ├── content/ │ └── content.js # 页面内容提取 (Readability + Turndown) ├── popup/ │ ├── popup.html # 弹出面板 │ ├── popup.css # 420px 卡片风格 │ └── popup.js # 预览渲染、知识库选择、保存交互 ├── options/ │ ├── options.html # 设置页 │ ├── options.css │ └── options.js # Token 验证、知识库列表加载 ├── lib/ │ ├── readability.js # Mozilla Readability │ ├── turndown.js # Turndown │ └── yuque-api.js # 语雀 API 封装(备用) ├── icons/ # SVG 图标 16/48/128 └── _locales/zh_CN/messages.json # 中文本地化 ``` ## 核心流程图 ``` 用户触发(点击图标 / 右键菜单) │ ▼ chrome.scripting.executeScript 注入到当前页面 │ ▼ Readability.js 提取正文 → 干净 HTML │ ▼ Turndown.js HTML → Markdown(含自定义表格规则) │ ▼ 正则提取图片 URL → Service Worker 逐张下载 │ ▼ 语雀附件 API POST /repos/{namespace}/attachments │ ▼ 替换 Markdown 中图片 URL 为语雀地址 │ ▼ POST /repos/{namespace}/docs 创建文档 │ ▼ PUT /repos/{namespace}/toc 挂载到目录树 │ ▼ chrome.notifications 通知用户 ``` ## 关键实现细节 ### 1. 文章正文提取 Mozilla Readability 是 Firefox 阅读模式背后的引擎,能从任意网页中识别正文区域。核心调用只有三行: ```javascript const doc = document.cloneNode(true); const reader = new Readability(doc); const article = reader.parse(); // article.title → 标题 // article.content → 正文 HTML // article.byline → 作者 // article.siteName → 站点名 ``` 注意需要 `cloneNode(true)` 创建文档副本,避免修改原始 DOM。 ### 2. 表格转换(踩坑) Turndown 默认**不处理 HTML 表格**——`
| / | 文本 // 构建 Markdown 管道表格: // | Header 1 | Header 2 | // | -------- | -------- | // | Cell 1 | Cell 2 | } }); ``` 需要处理的边界情况: - `colspan` 属性 → 展开为多列 - 嵌套表格 → `closest('table') !== node` 跳过 - 单元格含 `|` 字符 → 转义为 `\|` - 多行文本 → 压缩为单行 - 无 `` 的表格 → 第一行当作表头 ### 3. 图片中转 网页中的图片直接引用外链有两个问题:防盗链(referer 限制)和链接失效。解决方案是下载后上传到语雀附件: ```javascript // 1. 下载图片(带 referer 绕过防盗链) const resp = await fetch(imageUrl, { headers: { Referer: pageUrl } }); const blob = await resp.blob(); // 2. 上传到语雀 const formData = new FormData(); formData.append('file', blob, filename); await fetch(`${YUQUE_API}/repos/${namespace}/attachments`, { method: 'POST', headers: { 'X-Auth-Token': token }, body: formData }); ``` 容错:单张图片上传失败时保留原始 URL,不影响整篇文档保存。 ### 4. 双触发路径 弹窗和右键菜单走两套不同的代码路径,但共享同一套提取逻辑: **弹窗路径**(用户可预览编辑): ``` popup.js → chrome.tabs.sendMessage(tabId, {action: 'extract'}) → content.js 返回结果 → 用户确认后 → chrome.runtime.sendMessage({action: 'save'}) → service-worker.js 执行保存 ``` **右键菜单路径**(一键快存): ``` service-worker.js → chrome.contextMenus.onClicked → chrome.scripting.executeScript 注入提取函数 → 直接保存到默认知识库 → chrome.notifications 通知结果 ``` 弹窗优先通过已注入的 content script 通信;如果 content script 未就绪,自动回退到 `chrome.scripting.executeScript` 注入。 ### 5. 语雀 API 交互 语雀开放 API 使用 Personal Token 认证,所有请求带 `X-Auth-Token` 头: ```javascript // 创建文档 POST https://www.yuque.com/api/v2/repos/{namespace}/docs { "title": "文章标题", "body": "Markdown 正文", "format": "markdown", "public": 0 } // 挂载到目录树(否则文档处于游离状态) PUT https://www.yuque.com/api/v2/repos/{namespace}/toc { "action": "appendNode", "action_mode": "child", "doc_ids": [docId] } ``` 关键点:创建文档后**必须调用目录树 API** 挂载,否则文档不会出现在知识库目录中。 ## 配置与使用 ### Token 获取 1. 登录 [语雀](https://www.yuque.com) 2. 进入 [Token 设置](https://www.yuque.com/settings/tokens) 3. 新建 Token,勾选「文档读写」权限 4. 复制 Token 到插件设置页 ### 加载到 Edge 1. 打开 `edge://extensions/` 2. 开启「开发人员模式」 3. 点击「加载解压缩的扩展」 4. 选择 `MarkSnap` 目录 5. 右键扩展图标 → 扩展选项 → 填入 Token → 验证 → 选择默认知识库 ## Manifest V3 注意事项 这个项目踩过的 MV3 相关坑: | 问题 | 解决 | | --- | --- | | Service Worker 不能访问 DOM | 通过 `chrome.scripting.executeScript` 在页面上下文执行 | | 不能使用 eval / new Function | 所有逻辑写为静态函数字符串传入 | | Service Worker 可能被终止 | 图片上传等耗时操作做好错误处理 | | Readability.js 是 CommonJS 模块 | 追加 `window.Readability = Readability` 暴露为全局变量 | | Content Script 可能未注入 | 弹窗中 try-catch 回退到 scripting API | ## 完整代码 项目打包为 `MarkSnap.zip`(约 52KB),包含全部源码。解压后直接在 Edge 加载即可使用。 ## 后续可扩展方向 - **支持更多知识库平台**:Notion、飞书文档等(API 结构类似,扩展 adapter 层即可) - **支持 Lake 格式**:语雀原生 JSON 格式,排版能力更强但转换复杂 - **批量抓取**:对列表页批量提取所有链接指向的文章 - **即时预览**:弹窗内渲染 Markdown(引入简易渲染器) - **标签/分类**:保存时指定语雀文档标签 - **快捷键**:`Ctrl+Shift+Y` 一键保存当前页面 --- 整个项目从零到可用大约花了几个小时,核心逻辑不到 500 行代码。Mozilla Readability + Turndown + 语雀 API 三板斧的组合出乎意料地好用。如果你也有类似需求,直接拿这份代码改吧。 |
|---|