jacobinwwey_obsidian-NotEMD/docs/architecture.zh-CN.md
aliyun1121003339 bac0fb8dc1 feat(diagrams): add managed CircuitikZ runtime and fix history scrolling
Add an optional ownership-scoped Tectonic environment with secure installation, native compile verification, and topology-preserving repair support. Harden CircuitikZ generation and preview routing, fix the history drawer scroll model, and synchronize bilingual architecture, user guidance, and localized README documentation.
2026-07-20 03:49:24 +08:00

18 KiB
Raw Permalink Blame History

Notemd 系统架构总览

更新2026-07-10

系统架构

flowchart TB
    subgraph User["Obsidian 用户界面"]
        CMD["命令面板"]
        SIDEBAR["Notemd 工作台"]
        SETTINGS["设置标签页"]
    end

    subgraph Plugin["NotemdPlugin (src/main.ts)"]
        LOAD["loadSettings / saveSettings"]
        DISPATCH["命令分发"]
        BATCH["批量处理"]
    end

    subgraph LLM["LLM 调用管道"]
        PROV["提供商注册<br/>(src/llmProviders.ts)"]
        TOKEN["令牌解析<br/>(resolveProviderTokenLimit)"]
        CACHE["响应缓存<br/>(llmResponseCache)"]
        TRANS["传输层<br/>5 个运行时"]
    end

    subgraph Diagram["图表平台"]
        PROMPT["规格提示<br/>(diagramSpecPrompt)"]
        GEN["生成服务<br/>(generateDiagramArtifact)"]
        PARSE["规格解析<br/>(parseDiagramSpecResponse)"]
        RENDER["渲染服务<br/>(RendererRegistry)"]
        HOST["预览宿主<br/>(IframeRenderHost)"]
    end

    subgraph Output["输出"]
        VAULT["Vault 文件<br/>(.md, .canvas, .json)"]
        PREVIEW["图表预览弹窗"]
        EXPORT["源文件 / SVG / PNG / PDF 导出"]
    end

    CMD --> DISPATCH
    SIDEBAR --> DISPATCH
    SETTINGS --> LOAD

    DISPATCH --> PROV
    PROV --> TOKEN
    TOKEN --> CACHE
    CACHE --> TRANS
    TRANS --> GEN
    TRANS --> BATCH

    GEN --> PROMPT
    PROMPT --> PARSE
    PARSE --> RENDER
    RENDER --> HOST
    HOST --> PREVIEW
    HOST --> EXPORT
    RENDER --> VAULT
    BATCH --> VAULT

LLM 调用管道

sequenceDiagram
    participant User as 用户
    participant Plugin as NotemdPlugin
    participant Provider as 提供商注册
    participant Token as 令牌解析
    participant Cache as 响应缓存
    participant Transport as 传输层
    participant API as LLM API

    User->>Plugin: 执行操作(处理、翻译、生成)
    Plugin->>Provider: getLLMProviderDefinition(name)
    Provider-->>Plugin: LLMProviderDefinition传输协议、API 密钥模式等)
    Plugin->>Token: resolveProviderTokenLimit(provider, model, maxTokens)
    Token->>Token: KNOWN_MODEL_MAX_OUTPUT_TOKENS 查表
    Token-->>Plugin: 令牌上限 (number | undefined)
    Plugin->>Cache: buildCacheKey(provider, model, prompt, content)
    Plugin->>Cache: getCachedResponse(cacheKey)
    
    alt 缓存命中
        Cache-->>Plugin: 缓存响应
        Plugin-->>User: 结果
    else 缓存未命中
        Plugin->>Transport: callLLM(provider, prompt, content, settings)
        Note over Transport: 分发至 5 个运行时之一<br/>openai-compatible | anthropic | google<br/>azure-openai | ollama
        Transport->>API: HTTP 请求(含重试逻辑)
        API-->>Transport: 响应
        Transport-->>Plugin: 结果
        Plugin->>Cache: setCachedResponse(cacheKey, result)
        Plugin-->>User: 结果
    end

令牌解析逻辑

用户配置 (maxTokens, provider.maxOutputTokens)
  → resolveProviderTokenLimit()
    → 连接测试? → 返回 1
    → 提供商 maxOutputTokens 覆盖已设置?
      → 已知模型? → min(覆盖值, 已知上限)
      → 未知模型? → 覆盖值(直接使用)
    → 全局 maxTokens 已设置?
      → 已知模型?
        → maxTokens === DEFAULT → 已知模型上限(自动)
        → 否则 → min(maxTokens, 已知上限)
      → 未知模型?
        → maxTokens === DEFAULT → undefinedAPI 自行决定Cline 对齐)
        → 否则 → maxTokens用户值
    → 否则 → 已知上限 ?? undefined

支持的传输协议

传输协议 提供商数量 协议
openai-compatible 22 个提供商 OpenAI Chat Completions API
anthropic 1 个 Anthropic Messages API
google 1 个 Google Gemini API
azure-openai 1 个 Azure OpenAI Deployment API
ollama 1 个 Ollama Native API

图表渲染平台

flowchart LR
    subgraph Input["输入"]
        MD["Markdown 内容"]
        INTENT["首选意图<br/>(可选)"]
    end

    subgraph Spec["规格层"]
        PLAN["DiagramPlan<br/>(意图推断)"]
        PROMPT2["DiagramSpec 提示"]
        LLM["LLM 调用"]
        PARSE2["规格解析器"]
        VALIDATE["规格验证器"]
    end

    subgraph Render["渲染层"]
        REGISTRY["RendererRegistry<br/>8 个渲染器"]
        SERVICE["RendererService"]
        CACHE2["RenderCache"]
    end

    subgraph Target["输出目标"]
        MERMAID["Mermaid<br/>流程图、时序、类、ER、状态、思维导图"]
        CANVAS["JSON Canvas<br/>(画布图)"]
        VEGA["Vega-Lite<br/>(数据图表)"]
        HTML["HTML 回退"]
        FIGURE["可编辑 HTML/SVG"]
        BOARD["Draw.io / Drawnix"]
        CIRCUIT["Circuitikz"]
    end

    subgraph Host["预览层"]
        IFRAME["IframeRenderHost"]
        MODAL["DiagramPreviewModal"]
        EXPORT2["源文件 / SVG / PNG / PDF 导出"]
    end

    MD --> PLAN
    INTENT --> PLAN
    PLAN --> PROMPT2
    PROMPT2 --> LLM
    LLM --> PARSE2
    PARSE2 --> VALIDATE
    VALIDATE --> SERVICE
    SERVICE --> REGISTRY
    REGISTRY --> MERMAID
    REGISTRY --> CANVAS
    REGISTRY --> VEGA
    REGISTRY --> HTML
    REGISTRY --> FIGURE
    REGISTRY --> BOARD
    REGISTRY --> CIRCUIT
    MERMAID --> IFRAME
    CANVAS --> IFRAME
    VEGA --> IFRAME
    IFRAME --> MODAL
    MODAL --> EXPORT2

支持的图表意图

意图 渲染目标 渲染器 预览 导出
mindmap mermaid MermaidRenderer 弹窗/iframe SVG、PNG
flowchart mermaid MermaidRenderer 弹窗/iframe SVG、PNG
sequence mermaid MermaidRenderer 弹窗/iframe SVG、PNG
classDiagram mermaid MermaidRenderer 弹窗/iframe SVG、PNG
erDiagram mermaid MermaidRenderer 弹窗/iframe SVG、PNG
stateDiagram mermaid MermaidRenderer 弹窗/iframe SVG、PNG
canvasMap json-canvas JsonCanvasRenderer 弹窗/iframe 源文件、SVG、PNG、PDF
dataChart vega-lite VegaLiteRenderer 弹窗/iframe沙盒 源文件、SVG、PNG、PDF
circuit circuitikz CircuitikzRenderer SVG companion 或 source-only 预览 .tex、SVG、PNG、PDF

显式渲染目标

Generate diagramPreview diagram 而言,规格优先 pipeline 可以在意图推断之外显式指定渲染目标。标准的 Summarise as Mermaid diagram 命令仍保持 Mermaid 兼容输出。

渲染目标 Artifact 边界 Runtime 依赖策略
editable-html-svg 带语义 inline SVG 的自包含 HTML 不依赖外部编辑器 runtime
drawio .drawio XML 加 SVG/MD review companion 插件内不嵌入 diagrams.net runtime
drawnix .drawnix JSON 子集加 SVG/MD review companion 插件内不嵌入 Drawnix 或 Plait runtime
circuitikz 经过验证的 .tex 源文件加 SVG/MD review companion 预览/导出零依赖;桌面端可选本机编译器或托管 Tectonic

Circuitikz 支持仍然是受约束的。前端设置无需开启 Developer mode 就会显示 Circuit (Circuitikz) 首选图表类型与 Circuitikz + SVG preview 首选渲染目标,但 renderer 只接受经过验证的 DiagramSpec(intent: "circuit", circuitSpec)。它会写出确定性的 circuitikz TeX 和可审阅的 SVG companion。桌面用户随后可以复用自定义/系统编译器,或在 Vault 外显式安装固定版本 Tectonic 0.16.9,用于编译诊断、原生 PDF 证据与受保护的修复验收;移动端与常规预览/导出不会加载桌面进程代码。

托管运行时边界按所有权而不是目录名称判断。下载资产经过主机白名单、体积上限和 checksum 校验,解压拒绝链接与路径穿越,在 staging 中通过 smoke 后才在文件系统锁内激活。已有路径必须在规范化 realpath 解析后仍位于配置的运行时根目录内。删除只接受有效 Notemd pointer 或安装目录内所有权证据;过期锁恢复会先把已声明的死亡 owner 锁原子隔离,再复核 owner 与 claim token 后删除。

模块地图

模块 职责
src/main.ts 插件入口、命令注册、流程编排
src/llmProviders.ts 26 个提供商定义、元数据、KNOWN_MODEL 表
src/llmUtils.ts 传输分发、令牌解析、重试、响应缓存
src/fileUtils.ts 文件处理、Mermaid 修复、概念提取
src/searchUtils.ts 网络搜索、Tavily/DuckDuckGo 集成
src/translate.ts 翻译管道(含分块)
src/promptUtils.ts 任务提示词(旧版 + spec-first
src/diagram/ 图表领域模型、适配器、渲染器
src/rendering/ 渲染宿主、预览、导出、主题
src/ui/ 设置标签页、侧边栏、弹窗、欢迎页
src/i18n/ 22 种语言、任务语言策略
src/operations/ operation registry、host adapter、capability/contract 导出、可复用命令编排
src/batchProgressStore.ts 中断恢复批量状态持久化
src/providerDiagnostics.ts LLM 提供商连接诊断

CLI 边界现实

当前宿主事实必须明确写清:

  • 可选的 obsidian-cli 包装器可能提供 native 等桌面/调试入口,但当前 Windows Study 主机并未安装npm 上同名的旧包早于官方 CLI且会遮蔽 obsidian 可执行文件,因此不能作为安全替代品
  • 官方 obsidian CLI 已支持 commandscommand id=<command-id>eval,能够列出/执行插件命令,也可直接调用 maintainer bridge
  • scripts/invoke-maintainer-cli-operation.js 在兼容包装器存在时优先使用 obsidian-cli native eval,只在命令不存在时回退到官方 obsidian eval;如果包装器已存在但执行失败,则原样暴露失败,不会静默掩盖
  • 但这仍然只是命令触发表面,不是成熟的插件集成协议:它还缺少类型化参数、返回结果契约、能力元数据和稳定自动化语义

因此Notemd 的未来 CLI 路线仍不能停留在“把 sidebar 按钮搬到终端”。真正值得抽取的是已经开始具备独立形态的低层能力:

  • src/providerDiagnostics.ts
  • src/diagram/diagramGenerationService.ts
  • src/workflowButtons.ts
  • src/batchProgressStore.ts
  • LLMProviderConfig.localOnly 这类 config/profile 语义

当前架构缺口在于:src/main.ts 仍持有过多 orchestration、UI 生命周期和 Obsidian runtime 耦合。在形成宿主无关 operation 层之前,插件 command IDs 虽然已经可以被官方 CLI 触发,但它们仍然只是产品表面,不应被当成稳定工程 API。

不过这个缺口已经比之前更小了:

  • src/operations/diagramGenerateOperation.ts 现在承接命令层之下可复用的 diagram 执行逻辑
  • src/operations/providerDiagnosticCommand.ts 现在承接命令层之下的 provider diagnostic command orchestration
  • src/operations/diagramCommandHostAdapter.ts 现在承接 Mermaid/artifact 保存收尾、直接 Vega-Lite 预览编排,以及公共 diagram command wrapperrunGenerateDiagramCommandWithHostrunPreviewExperimentalDiagramCommandWithHost
  • src/operations/configProfileCommands.ts 现在承接 provider profile 导入导出与 CLI capability/contract 导出编排
  • src/operations/providerDiagnosticReportPersistence.ts 现在承接带冲突规避的 provider diagnostic report 文件创建逻辑
  • src/operations/providerDiagnosticCommandHostAdapter.ts 现在承接开发者诊断命令的宿主装载、报告落盘接线与 notice 整形逻辑
  • src/operations/configProfileCommandHostAdapter.ts 现在承接 config/profile 状态持久化、CLI 导出 notice 整形与导入导出错误映射逻辑
  • src/operations/providerConnectionTestCommandHostAdapter.ts 现在承接共享 provider 连接测试的 settings 装载,以及底层测试 runner 与交互式 busy/reporter wrapper并已被命令路径与设置页共同复用
  • src/operations/noteProcessingCommandHostAdapter.ts 现在不仅承接 process-current-add-linksprocess-folder-add-linksbatch-generate-from-titlesgenerate-from-titleresearch-and-summarize,还继续承接 translate-current-filebatch-translate-folderextract-concepts-currentextract-concepts-folderextract-original-textextract-concepts-and-generate-titles 的 busy-guard、reporter 生命周期、notice/error-log 编排逻辑
  • src/operations/utilityCommandHostAdapter.ts 现在也已承接当前文件 duplicate check、duplicate cleanup、batch Mermaid fix 与 single/batch formula fix 的 command orchestrationcheck-for-duplicates 已不再内联写在命令注册里
  • src/operations/utilityCommandHostAdapter.ts 现在也已承接 duplicate cleanup 与 batch Mermaid fix 的删除确认、无文件 notice 与成功 notice 语义,这些用户侧效果已不再从 src/fileUtils.ts 泄漏出来
  • src/operations/registry.ts 现在也已覆盖剩余 selection/export 邻接自动化表面:editor.create-link-and-generateprovider.profile.exportprovider.profile.importcli.capability-manifest.exportcli.invocation-contract.export 已进入与前几批相同的 registry/capability/contract 表面
  • 第一批 src/fileUtils.ts 子切片也已经完成 write-heavy contract enrichment 验证:processFile() 现在返回 ProcessFileResultgenerateContentForTitle() 返回 GenerateContentForTitleResultbatchGenerateContentForTitles() 返回 BatchGenerateContentForTitlesResultrunProcessFolderWithNotemdCommandWithHost() 现在也会返回带 savedCountfileResultserrorscancelledBatchProcessFolderResult
  • src/fileUtils.ts 现在不再自行决定“无可处理 Markdown 文件”的用户侧批量生成结果;它只返回结构化 batch state这一 no-file notice 语义改由 src/operations/noteProcessingCommandHostAdapter.ts 承接
  • src/fileUtils.ts 的剩余尾部现在也已落地:batchFixMermaidSyntaxInFolder() 返回 BatchMermaidFixResultcheckAndRemoveDuplicateConceptNotes() 返回 ConceptDedupeResult,破坏性确认由 host adapter 注入batch Mermaid 的无文件处理也已从 utility-owned 改为 host-owned
  • src/operations/registry.ts 现在也直接建模了 file.process-add-linksfile.process-folder-add-linkscontent.generate-from-titlecontent.batch-generate-from-titlesmermaid.batch-fixconcept.dedupetranslate.*formula.* 的 richer result schema因此 capability export 与 invocation-contract export 不再把这些流程压平成仅路径或仅计数语义
  • src/fileUtils.tssrc/extractOriginalText.ts 现在已经接受更窄的 runtime context而不是直接依赖具体 NotemdPlugin 类,这说明边界正在从 wrapper 抽离继续推进到 utility 对宿主类型耦合的削弱
  • src/main.ts 现在主要保留命令注册、host 构造,以及更深一层的 diagram 执行 helper先前最高价值的公共 direct command surface 现在已经改为通过 host adapter 代理,不再内联 busy/reporter/preview 生命周期逻辑
  • 新落地的 direct-surface wrapper 批次已经覆盖 testLlmConnectionCommandgenerateDiagramCommandpreviewExperimentalDiagramCommand;这些表面现在都具备结构化 result 边界,而不是 fire-and-forget 的 UI glue
  • 最新一层细化是:diagram.generate 应被理解为“宿主无关 generation contract”而不是对当前 active-file 命令的另一种命名。它在 operation-level 上的 safe / read-only 元数据描述的是显式的 sourceMarkdown -> DiagramGenerationResult core映射过去的 command binding 仍然要如实保留 requires-active-file / write-file 语义。
  • 当前真正剩余的缺口因此已经不是公共 command entrypoint 本身:diagram.previewprovider.connection.test 现已具备 typed contractsave/artifact 的实质执行路径也已进入 src/operations/diagramCommandExecution.ts,而 diagram.generate 现在也会返回显式的 follow-through 细节(kindoutputPathpreviewOpenedautoFixAttemptedartifactTarget),同时继续保留向后兼容的顶层 outputPath / previewOpened 字段。
  • 维护者本地语义核验层现在也不再只是文字说明:npm run verify:diagram-semantics 已能生成无 secrets 的 Markdown 检查模板其中包含仓库硬门、vault 感知的 CLI 检查命令,以及 Mermaid / JSON Canvas / Vega-Lite 的证据区块,不依赖仓库中跟踪的 vault 路径或 live 凭据。
  • 下一阶段顺序已经明确:先把 diagram.generate 保持为宿主无关 core把这批已落地的 typed follow-through 视作其下的 command-completion 层,再做 packaging / semantic verification 的后续收敛,最后才重开更强 public CLI 声明或更大规模的结构重排。

关键设计决策

  1. 规格优先图表生成LLM 输出结构化 DiagramSpec JSON而非原始 Mermaid 语法。解耦意图与渲染器。
  2. 传输驱动分发OpenAI-compatible 提供商共享一个运行时。无逐提供商代码路径。
  3. Cline 对齐令牌解析:未知模型由 API 提供商自行决定。已知模型使用元数据表。
  4. operation-core 与 command-binding 分层registry 中的 operation 元数据可以描述可复用的宿主无关 core而当前出货命令本身仍保留 active-file、write-file 或 preview-bound 的真实产品语义。diagram.generate 是当前最明确的证明案例。
  5. Iframe 宿主预览Vega-Lite 和 HTML 在沙盒 iframe 中渲染。Mermaid 内联渲染。
  6. 本地存储提供商配置API 密钥可设备本地保留,工作流设置可同步。
  7. 响应缓存5 分钟 TTL 内相同 LLM 调用返回缓存结果。

验证

  • npm run build — TypeScript 编译 + esbuild 打包
  • npm test -- --runInBand — 完整 Jest 矩阵当前为 137 套件、871 项测试;若在 /.worktrees/ checkout 中验证,请改用 npx jest --runInBand --config /tmp/notemd-worktree-jest.cjs,因为仓库默认 Jest ignore 规则会排除 worktree 路径
  • npm run audit:i18n-ui — 无硬编码 UI 字符串
  • npm run audit:render-host — 渲染宿主自包含于 main.js
  • git diff --check — 空白符卫生