152 lines
7.5 KiB
Markdown
152 lines
7.5 KiB
Markdown
# Architecture Overview
|
||
|
||
> 本文档从整体视角拆分自 `detaildesign.md`,聚焦架构分层、核心模块职责与交互关系。补充细粒度时序与图片处理细节请参见:
|
||
> - `image-pipeline.md`
|
||
> - `render-service-blueprint.md`
|
||
> - `diagrams.md`
|
||
|
||
## 1. 分层结构
|
||
```
|
||
UI / Interaction (NotePreview)
|
||
├─ Toolbar (复制/上传/发草稿/图片集/导出/批量)
|
||
└─ 状态维护 (当前笔记、主题、代码高亮、公众号 AppId)
|
||
|
||
Rendering & Composition (ArticleRender + MarkedParser + Extensions)
|
||
├─ Markdown 预处理 (frontmatter 剥离 / 可选图片语法转换)
|
||
├─ Gallery 展开 (短代码 & 块级 figure src|link → wikilink 列表)
|
||
├─ 语法扩展 (LocalFile: wikilink 图片、文件嵌入、Excalidraw、SVG)
|
||
├─ 行级轻语法 (applyCustomInlineBlocks: fig / ||r||g||b||y||)
|
||
├─ 延迟元素缓存 (Mermaid / Excalidraw)
|
||
└─ 样式注入 (主题 + 代码高亮 + 自定义 CSS)
|
||
|
||
Assets & Settings Layer
|
||
├─ AssetsManager (themes, highlights, customCSS)
|
||
└─ NMPSettings (frontmatter key 映射 / 功能开关 / 微信配置 / galleryPrePath & galleryNumPic & defaultCoverPic)
|
||
|
||
Resource & Media Layer
|
||
├─ LocalImageManager (图片登记 / 上传 / 替换 / base64 嵌入)
|
||
├─ Image Conversion (WebP -> JPG wasm)
|
||
└─ html-to-image (Mermaid/Excalidraw 转 PNG)
|
||
|
||
Cover Fallback Chain (逻辑横切 Concern)
|
||
frontmatter cover/image → 正文首本地图片 → gallery 生成列表首图 → defaultCoverPic (配置) → 空
|
||
|
||
Debug Logging & Throttle
|
||
- 输出:当前文件路径、封面决策(包含 defaultCoverPic 触发)
|
||
- 节流:同路径 3 秒内不重复打印
|
||
|
||
WeChat Integration Layer
|
||
├─ Token 代理 (wxGetToken)
|
||
├─ 草稿创建 (wxAddDraft / wxAddDraftImages)
|
||
├─ 素材批量获取 (wxBatchGetMaterial)
|
||
└─ 上传图片 (wxUploadImage)
|
||
|
||
Utilities & Helpers
|
||
├─ debounce, applyCSS, waitForLayoutReady
|
||
└─ CardDataManager (代码卡片序列化/恢复)
|
||
```
|
||
|
||
## 2. 核心模块职责矩阵
|
||
| 模块 | 关键职责 | 输入 | 输出 | 失败模式 |
|
||
|------|----------|------|------|----------|
|
||
| NotePreview | 交互、调度、批处理 | 用户操作 / 活动文件 | 渲染触发、发布调用 | 文件为空 / 未配置公众号 |
|
||
| ArticleRender | 渲染 + 元数据 + 发布协调 | Markdown + Settings | HTML + DraftArticle | 解析错误 / token 失败 |
|
||
| MarkedParser + Extensions | Markdown Token 化扩展 | 预处理文本 | Token 树 -> HTML 片段 | 不支持语法 / 路径缺失 |
|
||
| LocalFile | Wikilink/嵌入/Excalidraw/SVG | Token.raw | 标准 HTML / 占位 | 文件不存在 / AuthKey 缺失 |
|
||
| LocalImageManager | 图片生命周期 | 图片引用集合 | 上传后的 URL 替换结果 | 上传失败 / 转码失败 |
|
||
| AssetsManager | 主题 & 高亮加载 | 文件系统 | CSS 文本 | 主题缺失 |
|
||
| NMPSettings | 全局配置 | 存储数据 | 配置实例 | AuthKey 无效 |
|
||
| WeChat API | 外部接口封装 | 请求结构 | 响应 JSON | HTTP 非 200 / errcode |
|
||
| CardDataManager | 代码块序列化 | HTML fragment | 可复制安全内容 | 序列化不匹配 |
|
||
|
||
## 3. 关键交互流程简述
|
||
### 3.1 渲染触发
|
||
1. 用户打开/切换文件 → `file-open` 事件。
|
||
2. `NotePreview.update()` → 清理旧图片缓存 → `ArticleRender.renderMarkdown()`。
|
||
3. 读取 Markdown → (frontmatter 剥离 + 可选图片语法转换) → Marked 解析扩展 → HTML section → 延迟元素二次渲染。
|
||
4. 更新 UI 下拉:主题 / 高亮 / 公众号。
|
||
|
||
### 3.2 上传图片
|
||
1. 用户点击“上传图片” → `ArticleRender.uploadImages()`。
|
||
2. 获取 token → 缓存元素转图片 → 本地/远程图片逐步上传 → DOM src 替换 → 复制 HTML 到剪贴板。
|
||
|
||
### 3.3 发布草稿
|
||
1. “发草稿” → token → 缓存元素转图片。
|
||
2. 上传本地 & 远程图片 → 封面决策(thumb_media_id/frontmatter/正文首图/默认素材)→ 构造 `DraftArticle`。
|
||
3. 调用 `wxAddDraft` → 返回 media_id。
|
||
|
||
### 3.4 发布图片集 (newspic)
|
||
与草稿类似,但构造 `DraftImages`,content 使用纯文本,图片列表为 `media_id` 数组。
|
||
|
||
## 4. 元数据 & 封面策略抽象
|
||
逻辑函数(伪):
|
||
```ts
|
||
function resolveCover(originalMarkdown: string, fmCover?: string, thumbMediaId?: string): string | null {
|
||
if (thumbMediaId) return null; // 已有 media_id 不再上传本地封面
|
||
if (fmCover) return fmCover; // frontmatter 指定
|
||
const body = stripFrontmatter(originalMarkdown);
|
||
const candidates = collectImageOrder(body); // wikilink + markdown
|
||
// 若正文无本地候选再尝试 gallery 展开结果
|
||
if (!candidates.length) {
|
||
const galleryList = collectGalleryFirstImages(body);
|
||
if (galleryList.length) candidates.push(galleryList[0]);
|
||
}
|
||
if (candidates.length) return `![[${candidates[0]}]]`;
|
||
// 最终 defaultCoverPic 由外层 getMetadata 注入(若配置)
|
||
return null;
|
||
}
|
||
```
|
||
|
||
Fallback 扩展:`getMetadata()` 在上述返回为空且存在 `defaultCoverPic` 时作为最终封面。
|
||
|
||
## 5. 图片收集策略统一要点
|
||
- Wikilink:解析时即登记(路径 → vault file)。
|
||
- Markdown 图片:可配置预处理转 Wikilink,以复用同一逻辑(避免重复正则后处理)。
|
||
- DOM 补偿策略(如后续新渲染管线未登记图片):可在上传前扫描 `<img>` 回填缺失项。
|
||
|
||
## 6. 错误处理分层
|
||
| 层 | 代表错误 | 处理策略 |
|
||
|----|----------|----------|
|
||
| 输入层 | 没有活动文件 | 静默返回 / UI 提示 |
|
||
| 渲染层 | Marked 解析异常 | try/catch 显示“渲染失败” HTML |
|
||
| 媒体层 | 图片上传失败 | Notice + 继续其他图片 |
|
||
| 转码层 | wasm 未加载 | 降级:保持 webp 原样尝试上传 |
|
||
| 发布层 | token 获取失败 | 抛异常终止流程 |
|
||
| API 层 | errcode != 0 | 聚合错误信息 + 建议手动复制 |
|
||
|
||
## 7. 性能关注点
|
||
| 项目 | 当前 | 潜在优化 |
|
||
|------|------|----------|
|
||
| 图片上传 | 串行 | 并发队列 + 重试退避 |
|
||
| Mermaid/Excalidraw | 前台转 PNG | 结果缓存 / worker 线程 |
|
||
| 主题加载 | 每次刷新全部组合 | 按需拼接 + 缓存 hash |
|
||
| Markdown 解析 | 单线程 | 增量渲染(监听 diff) |
|
||
|
||
## 8. 可扩展挂载点 (未来)
|
||
| Hook | 说明 | 预期参数 |
|
||
|------|------|----------|
|
||
| beforeRender | Markdown 转 HTML 前 | { markdown, file } |
|
||
| afterRender | HTML 完成但未上传 | { html, metadata } |
|
||
| beforeUploadImages | 上传前 | { images[] } |
|
||
| afterUploadImages | 上传后 | { mapping } |
|
||
| beforePublish | 调用 wxAddDraft 前 | { draft } |
|
||
| afterPublish | 成功返回 | { media_id } |
|
||
|
||
## 9. 风险摘录
|
||
- 多管线并存(旧 ArticleRender vs 新 RenderService 占位)易导致图片未登记 → 需统一抽象。
|
||
- 自动封面选取对远程图片/非图片扩展尚无过滤完备策略。
|
||
- 正则预处理与 Marked tokenizer 逻辑耦合度高,重构时需单测保护。
|
||
- 默认封面文件不存在导致封面缺失但用户误以为已设置 → 需未来加入存在性校验与 Notice。
|
||
- link/caption 属性当前解析未输出 → 未来转型时注意兼容老内容。
|
||
|
||
## 10. 索引 / 交叉引用
|
||
| 文档 | 内容概述 |
|
||
|------|----------|
|
||
| detaildesign.md | 全量深度设计描述 |
|
||
| image-pipeline.md | 图片解析、上传、封面、正则清单 |
|
||
| render-service-blueprint.md | 新一代渲染服务设计计划 |
|
||
| diagrams.md | 架构 / 时序 / 类关系图 (Mermaid) |
|
||
|
||
---
|
||
> 若修改架构(新增层或跨层依赖),需同步更新本文件与 `diagrams.md`。
|