返回案例库

精选案例 · Agent / 实践案例

Enthusjast - AI 辅助开发 Obsidian 插件 Punctuation Converter

这个案例围绕「Enthusjast - AI 辅助开发 Obsidian 插件 Punctuation Converter」记录了一条真实 AI 实践线索,正文重点集中在「背景」「使用的 AI 工具」,适合先按任务意图阅读再判断复用。

案例速读

README 标题「Enthusjast - AI 辅助开发 Obsidian 插件 Punctuation Converter」下已经出现运行/配置路径、脚本或接口线索,正文重点集中在「背景」「使用的 AI 工具」,比纯概念介绍更适合进入精选阅读流。 这篇案例的阅读价值在于,它把真实任务、模型辅助过程和可迁移做法放在同一个上下文里,读者可以从 「Enthusjast - AI 辅助开发 Obsidian 插件 Punctuation Converter」、「背景」、「使用的 AI 工具」、「开发过程」 进入正文。

  • 建议重点看 可参考其中的运行与配置路径、包含可迁移的命令、脚本或接口线索、继续补充结果证据后推荐度会更高。结合 Agent / 实践案例 和「任务驱动用户、AI 实践者」这一受众定位,它更适合作为任务检索后的精读材料,而不是只看一句短摘要后快速跳过。
  • 正文目录和原始材料仍然是判断依据;导读只帮助你更快定位阅读重点。
看点
Enthusjast - AI 辅助开发 Obsidian 插件 Punctuation Converter
读者
任务驱动用户、AI 实践者
复用
可参考其中的运行与配置路径
结构
12 个目录入口

原文内容

Enthusjast - AI 辅助开发 Obsidian 插件 Punctuation Converter

少年班学院25级 / Enthusjast

从零开始, 用 AI 词元开发一个完整的 Obsidian 社区插件, 并解决一路上遇到的类型错误, API 兼容性和 ESLint 规范问题.


背景

我平时用 Obsidian 写笔记, 常需要在中英文之间切换, 中文标点和英文标点混在一起既不美观也影响代码片段. Obsidian 社区插件商店里没有找到完全符合需求的标点转换插件, 于是决定自己写一个.

但我之前完全没接触过 Obsidian 插件开发, 对 TypeScript 也只是略懂. 正好学校推出了"词元计划", 提供了 Qwen3.6 等大模型的 API 额度, 于是我决定全程用 AI 辅助完成这个插件.

使用的 AI 工具

  • Qwen3.6 (通过词元计划 API) - 编写全部代码, 调试, 讲解
  • Claude Code - 代码审查、ESLint 合规修复、文章润色

开发过程

第一阶段: 从零学习 Obsidian 插件开发

我的第一个问题是, “obsidian 插件开发是怎样的”. AI 给出了完整的概述: 技术栈 (TypeScript + Electron), 项目结构, 核心 API (addCommand, PluginSettingTab, Vault 等), 这让我快速建立了整体认知.

第二阶段: 生成初始代码

我直接告诉 AI, “写一个插件, 在文档编辑过程中自动将中文标点转化为英文标点”. AI 第一次给出的方案基于 CodeMirror 的 change 事件监听 (仅支持 Source 模式).

随后在对话中补充了详细的需求文档, AI 重新生成了三文件结构:

  • main.ts - 插件主逻辑
  • settings.ts - 设置面板 (22 条转换规则可独立开关)
  • view.ts - 侧边栏视图 (后来移除了)

第三阶段: 修 bug - 这才是占时间的大头

代码生成只是开始, 接下来是反复修 bug 的过程, 这也是最有价值的学习环节. 遇到的问题包括:

1. 类型系统问题 (TypeScript 严格模式)

  • (editor as any).cm 访问 CodeMirror 内部 API, 触发 no-unsafe-member-access 报错
  • loadSettingsloadData() 返回 any, 触发 no-unsafe-assignment
  • validateRules 方法缺失, AI 在上一个版本中引用了不存在的辅助方法
  • PunctuationConverter vs PunctuationConverterPlugin 类名不一致导致类型不匹配

2. API 兼容性问题

  • Object.assign 合并设置不够安全, 最终改为手动逐字段校验的 loadSettings (typeof + hasOwn 检查)
  • containerEl.createEl('h4') 触发了 Obsidian 官方 ESLint 规则, 要求改用 new Setting().setHeading()
  • replaceAll 在 ES2021 以下不可用, 需要修改 tsconfig.jsonlib 配置

3. 运行时问题

  • 转换 ¥ 字符时出现问题 (转义冲突), 最终从规则列表移除
  • CodeMirror .cm 方案在 Live Preview 模式下完全失效, 最终改用 setInterval 轮询 editor.getValue() (150ms 间隔)

4. ESLint 合规问题 (最多轮次)

  • Unexpected any - AI 反复尝试各种写法 (Record<string, unknown>, Object.hasOwn, in 运算符), 最终找到通过所有规则的版本

第四阶段: 最终方案

最终插件精简为两个源文件:

文件 职责
src/main.ts 插件入口, 编辑器监听 (150ms 轮询), 全文转换命令
src/settings.ts 22 条转换规则定义, 设置面板 UI

核心实现抉择: 轮询而非事件驱动

Obsidian 官方没有暴露跨模式 (Source / Live Preview) 通用的编辑事件 API. CodeMirror 的 change 事件只在 Source 模式可用. 最终采用 setInterval(150ms) 轮询 editor.getValue(), 虽然不够优雅, 但兼容所有编辑模式, 且性能开销可忽略.

设置加载的防御性编程

async loadSettings() {
  const raw = (await this.loadData()) as unknown;
  let enabled = DEFAULT_SETTINGS.enabled;
  let rules = [...DEFAULT_SETTINGS.rules];

  if (typeof raw === 'object' && raw !== null) {
    if ('enabled' in raw) {
      const enabledVal = (raw as { enabled?: unknown }).enabled;
      if (typeof enabledVal === 'boolean') enabled = enabledVal;
    }
    if ('rules' in raw) {
      const rulesVal = (raw as { rules?: unknown }).rules;
      if (Array.isArray(rulesVal)) rules = this.validateRules(rulesVal);
    }
  }
  this.settings = { enabled, rules };
}

逐字段校验类型, 避免任何 any 类型污染, 这是反复和 AI 磨合才得到的写法.


效果

  • 插件已完整实现所有功能: 实时转换, 全文一键转换, 规则可配置, 启用/禁用开关
  • 通过 TypeScript 严格模式 + ESLint(eslint-plugin-obsidianmd 推荐规则)的完整检查
  • 代码在本地已完成完整开发, 可在 Obsidian 中直接加载使用, 目前已提交至 Obsidian 社区等待审核上架.

心得与建议

1. AI 能快速生成框架, 但细节需要你懂

AI 第一次生成的代码看起来很完整, 但一跑类型检查就爆出一堆错误. 如果你完全不懂 TypeScript, 可能连错误信息都看不懂. 我的做法是让 AI 写代码的同时, 遇到报错就把错误信息贴回去让它解释, 这个过程本身就是最好的学习.

2. 类型安全类的报错是最磨人的

any 类型问题, 类型不匹配, 方法签名冲突 - 这些占了整个对话的 60% 以上. 如果用 JavaScript 写可能早跑通了, 但 TypeScript 的严格检查帮我提前发现了不少运行时隐患, 我觉得这个代价是值得的.

3. 分阶段提需求效果好

我先让 AI 生成基础版本, 跑通后再逐步提需求 (加设置面板, 加命令, 修 ESLint). 每次只解决一个问题, 比一次性提复杂需求效率高很多. 一开始我尝试过把所有需求写在一段话里, 生成的代码反而需要更多轮修 bug.

4. AI 不一定了解特定框架规范

Obsidian 社区有一套自己的 ESLint 规则 (禁止直接用 createEl('h4') 等), AI 一开始不知道, 是我的报错信息让它学会的. 我试过口头描述和直接贴报错, 后者明显效率更高 - AI 看到具体规则编号就能立刻修正.

5. 同一个问题多轮对话才能彻底解决

Unsafe assignment of an 'any' value 这个问题至少讨论了 5-6 轮, AI 尝试了 Object.assign,as 类型断言,Record<string, unknown>,Object.hasOwn 等不同方案, 最终才找到通过 ESLint 的写法. 我觉得耐心很重要, 每次失败都在缩小问题范围.

6. 让 AI 解释代码帮助很大

中间我让 AI 逐段讲解了 main.ts, 这对我理解 Obsidian 插件机制帮助很大. 现在我习惯每生成一个关键模块就让它讲一遍, 比自己硬看代码快得多.


技术栈

  • AI 工具: Qwen3.6 (通过词元计划 API)
  • 语言: TypeScript
  • 框架: Obsidian Plugin API
  • 构建工具: esbuild, tsc
  • 代码检查: ESLint (eslint-plugin-obsidianmd)
  • 版本控制: Git

返回顶部