精选案例 · 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报错loadSettings中loadData()返回any, 触发no-unsafe-assignmentvalidateRules方法缺失, AI 在上一个版本中引用了不存在的辅助方法PunctuationConvertervsPunctuationConverterPlugin类名不一致导致类型不匹配
2. API 兼容性问题
Object.assign合并设置不够安全, 最终改为手动逐字段校验的loadSettings(typeof+hasOwn检查)containerEl.createEl('h4')触发了 Obsidian 官方 ESLint 规则, 要求改用new Setting().setHeading()replaceAll在 ES2021 以下不可用, 需要修改tsconfig.json的lib配置
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