obsidian
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseObsidian Plugin Development Guidelines
Obsidian插件开发指南
You are assisting with Obsidian plugin development. Follow these comprehensive guidelines derived from the official Obsidian ESLint plugin rules, submission requirements, and best practices.
你正在协助进行Obsidian插件开发,请遵循这份基于官方Obsidian ESLint插件规则、提交要求及最佳实践制定的综合指南。
Getting Started
入门指南
Quick Start Tool
快速启动工具
For new plugin projects, an interactive boilerplate generator is available:
- Script: in the skill repository
tools/create-plugin.js - Slash command: for guided setup
/create-plugin - Generates minimal, best-practice boilerplate with no sample code
- Detects existing projects and only adds missing files
- All generated code follows these guidelines automatically
针对新插件项目,我们提供了交互式模板生成器:
- 脚本:技能仓库中的
tools/create-plugin.js - 斜杠命令:使用进行引导式设置
/create-plugin - 生成遵循最佳实践的极简模板,无示例代码
- 可检测现有项目,仅添加缺失文件
- 所有生成的代码将自动遵循本指南规范
When to Suggest the Tool
推荐使用该工具的场景
Recommend the boilerplate generator when users:
- Ask "how do I create a new Obsidian plugin?"
- Want to start a new plugin project
- Need help setting up the basic structure
- Want to ensure they start with best practices
当用户出现以下需求时,推荐使用该模板生成器:
- 询问“如何创建新的Obsidian插件?”
- 想要启动新的插件项目
- 需要帮助搭建基础项目结构
- 希望从遵循最佳实践的基础开始开发
Core Principles
核心原则
- Memory Safety: Prevent memory leaks through proper resource management
- Type Safety: Use proper type narrowing and avoid unsafe casts
- API Best Practices: Follow Obsidian's recommended patterns
- User Experience: Maintain consistency in UI/UX across plugins
- Platform Compatibility: Ensure cross-platform support (including iOS)
- Accessibility: Make all features keyboard and screen reader accessible
- 内存安全:通过合理的资源管理防止内存泄漏
- 类型安全:使用正确的类型收窄,避免不安全的类型转换
- API最佳实践:遵循Obsidian推荐的使用模式
- 用户体验:保持插件间UI/UX的一致性
- 平台兼容性:确保跨平台支持(包括iOS)
- 可访问性:所有功能需支持键盘操作和屏幕阅读器
Quick Reference
快速参考
Top 27 Most Critical Rules
27条最关键规则
Submission & Naming:
- Plugin ID: no "obsidian", can't end with "plugin" - Validation bot enforced
- Plugin name: no "Obsidian", can't end with "Plugin" - Validation bot enforced
- Plugin name: can't start with "Obsi" or end with "dian" - Validation bot enforced
- Description: no "Obsidian", "This plugin", etc. - Validation bot enforced
- Description must end with punctuation - Validation bot enforced
.?!)
Memory & Lifecycle:
6. Use for automatic cleanup - Prevents memory leaks
7. Don't store view references in plugin - Causes memory leaks
registerEvent()Type Safety:
8. Use instead of type casting - Type safety for TFile/TFolder
instanceofUI/UX:
9. Use sentence case for all UI text - "Advanced settings" not "Advanced Settings"
10. No "command" in command names/IDs - Redundant
11. No plugin ID in command IDs - Obsidian auto-namespaces
12. No default hotkeys - Avoid conflicts
13. Use for settings headings - Not manual HTML
.setHeading()API Best Practices:
14. Use Editor API for active file edits - Preserves cursor position
15. Use for background file mods - Prevents conflicts
16. Use for user paths - Cross-platform compatibility
17. Use API for OS detection - Not navigator
18. Use instead of - Bypasses CORS restrictions
19. No console.log in onload/onunload in production - Pollutes console
Vault.process()normalizePath()PlatformrequestUrl()fetch()Styling:
20. Use Obsidian CSS variables - Respects user themes
21. Scope CSS to plugin containers - Prevents style conflicts
Accessibility (MANDATORY):
22. Make all interactive elements keyboard accessible - Accessibility required
23. Provide ARIA labels for icon buttons - Accessibility required
24. Define clear focus indicators - Use
:focus-visibleSecurity & Compatibility:
25. Don't use / - Security risk (XSS)
26. Avoid regex lookbehind - iOS < 16.4 incompatibility
innerHTMLouterHTMLCode Quality:
27. Remove all sample/template code - MyPlugin, SampleModal, etc.
提交与命名规范:
- 插件ID:不得包含"obsidian",不能以"plugin"结尾 - 由验证机器人强制执行
- 插件名称:不得包含"Obsidian",不能以"Plugin"结尾 - 由验证机器人强制执行
- 插件名称:不能以"Obsi"开头或"dian"结尾 - 由验证机器人强制执行
- 描述:不得包含"Obsidian"、"This plugin"等内容 - 由验证机器人强制执行
- 描述必须以等标点结尾 - 由验证机器人强制执行
.?!)
内存与生命周期:
6. 使用进行自动清理 - 防止内存泄漏
7. 不要在插件中存储视图引用 - 会导致内存泄漏
registerEvent()类型安全:
8. 使用替代类型转换 - 为TFile/TFolder提供类型安全保障
instanceofUI/UX规范:
9. 所有UI文本使用句首大写格式 - 如“Advanced settings”而非“Advanced Settings”
10. 命令名称/ID中不得包含"command" - 属于冗余内容
11. 命令ID中不得包含插件ID - Obsidian会自动添加命名空间
12. 不要设置默认快捷键 - 避免冲突
13. 使用设置配置项标题 - 而非手动编写HTML
.setHeading()API最佳实践:
14. 使用Editor API编辑活跃文件 - 保留光标位置
15. 使用进行后台文件修改 - 防止冲突
16. 使用处理用户路径 - 保证跨平台兼容性
17. 使用 API检测操作系统 - 而非navigator
18. 使用替代 - 绕过CORS限制
19. 生产环境下的onload/onunload中禁止使用console.log - 会污染控制台
Vault.process()normalizePath()PlatformrequestUrl()fetch()样式规范:
20. 使用Obsidian CSS变量 - 适配用户主题
21. 将CSS作用域限定在插件容器内 - 防止样式冲突
可访问性(强制要求):
22. 所有交互元素需支持键盘操作 - 可访问性强制要求
23. 为图标按钮提供ARIA标签 - 可访问性强制要求
24. 定义清晰的焦点指示器 - 使用
:focus-visible安全与兼容性:
25. 禁止使用/ - 存在安全风险(XSS)
26. 避免使用正则表达式后行断言 - iOS 16.4以下版本不兼容
innerHTMLouterHTML代码质量:
27. 移除所有示例/模板代码 - 如MyPlugin、SampleModal等
Detailed Guidelines
详细指南
For comprehensive information on specific topics, see the reference files:
如需特定主题的全面信息,请参考以下参考文档:
Memory Management & Lifecycle
内存管理与生命周期
- Using ,
registerEvent(),addCommand(),registerDomEvent()registerInterval() - Avoiding view references in plugin
- Not using plugin as component
- Proper leaf cleanup
- 、
registerEvent()、addCommand()、registerDomEvent()的使用方法registerInterval() - 避免在插件中存储视图引用
- 不要将插件作为组件使用
- 正确的leaf清理方式
Type Safety
类型安全
- Using instead of type casting
instanceof - Avoiding type
any - Using and
constoverletvar
- 使用替代类型转换
instanceof - 避免使用类型
any - 使用和
const而非letvar
UI/UX Standards
UI/UX标准
- Sentence case enforcement
- Command naming conventions
- Settings and configuration best practices
- 句首大写格式的强制执行
- 命令命名规范
- 配置项设置的最佳实践
File & Vault Operations
文件与Vault操作
- View access patterns
- Editor vs Vault API
- Atomic file operations
- File management
- Path handling
- 视图访问模式
- Editor与Vault API的对比
- 原子化文件操作
- 文件管理
- 路径处理
CSS Styling Best Practices
CSS样式最佳实践
- Avoiding inline styles
- Using Obsidian CSS variables
- Scoping plugin styles
- Theme support
- Spacing and layout
- 避免内联样式
- 使用Obsidian CSS变量
- 插件样式的作用域限定
- 主题支持
- 间距与布局
Accessibility (A11y)
可访问性(A11y)
- Keyboard navigation (MANDATORY)
- ARIA labels and roles (MANDATORY)
- Tooltips and accessibility
- Focus management (MANDATORY)
- Focus visible styles (MANDATORY)
- Screen reader support (MANDATORY)
- Mobile and touch accessibility (MANDATORY)
- Accessibility checklist
- 键盘导航(强制要求)
- ARIA标签与角色(强制要求)
- 工具提示与可访问性
- 焦点管理(强制要求)
- 焦点可见样式(强制要求)
- 屏幕阅读器支持(强制要求)
- 移动端与触控可访问性(强制要求)
- 可访问性检查清单
Code Quality & Best Practices
代码质量与最佳实践
- Removing sample code
- Security best practices
- Platform compatibility
- API usage best practices
- Async/await patterns
- DOM helpers
- 移除示例代码
- 安全最佳实践
- 平台兼容性
- API使用最佳实践
- Async/await模式
- DOM辅助工具
Plugin Submission Requirements
插件提交要求
- Repository structure
- Submission process
- Semantic versioning
- Testing checklist
- 仓库结构
- 提交流程
- 语义化版本
- 测试检查清单
Essential Do's and Don'ts
核心注意事项
Do's ✅
建议做法 ✅
Memory & Lifecycle:
- Use ,
registerEvent(),addCommand(),registerDomEvent()registerInterval() - Return views/components directly (don't store unnecessarily)
Type Safety:
- Use for type checking (not type casting)
instanceof - Use specific types or instead of
unknownany - Use and
const(notlet)var
API Usage:
- Use (not global
this.app)app - Use Editor API for active file edits
- Use for background file modifications
Vault.process() - Use for YAML
FileManager.processFrontMatter() - Use for deletions
fileManager.trashFile() - Use for user-defined paths
normalizePath() - Use API for OS detection
Platform - Use for autocomplete
AbstractInputSuggest - Use direct file lookups (not vault iteration)
- Use instead of
requestUrl()for network requestsfetch()
UI/UX:
- Use sentence case for all UI text
- Use for settings headings
.setHeading() - Use Obsidian DOM helpers (,
createDiv(),createSpan())createEl() - Use with
window.setTimeout/setIntervaltypenumber
Styling:
- Move all styles to CSS
- Use Obsidian CSS variables for all styling
- Scope CSS to plugin containers
- Support both light and dark themes via CSS variables
- Follow Obsidian's 4px spacing grid
Accessibility (MANDATORY):
- Make all interactive elements keyboard accessible
- Provide ARIA labels for icon buttons
- Define clear focus indicators using
:focus-visible - Use for tooltips
data-tooltip-position - Ensure minimum touch target size (44×44px)
- Manage focus properly in modals
- Test with keyboard navigation
Code Quality:
- Use async/await (not Promise chains)
- Remove all sample/template code
- Test on mobile (if not desktop-only)
- Follow semantic versioning
- Minimize console logging (no console.log in onload/onunload in production)
内存与生命周期:
- 使用、
registerEvent()、addCommand()、registerDomEvent()registerInterval() - 直接返回视图/组件(避免不必要的存储)
类型安全:
- 使用进行类型检查(而非类型转换)
instanceof - 使用特定类型或替代
unknownany - 使用和
const(而非let)var
API使用:
- 使用(而非全局
this.app)app - 使用Editor API编辑活跃文件
- 使用进行后台文件修改
Vault.process() - 使用处理YAML
FileManager.processFrontMatter() - 使用执行删除操作
fileManager.trashFile() - 使用处理用户定义的路径
normalizePath() - 使用API检测操作系统
Platform - 使用实现自动补全
AbstractInputSuggest - 使用直接文件查找(而非遍历vault)
- 使用替代
requestUrl()发起网络请求fetch()
UI/UX:
- 所有UI文本使用句首大写格式
- 使用设置配置项标题
.setHeading() - 使用Obsidian DOM辅助工具(、
createDiv()、createSpan())createEl() - 使用类型的
numberwindow.setTimeout/setInterval
样式规范:
- 将所有样式移至CSS文件
- 所有样式使用Obsidian CSS变量
- 将CSS作用域限定在插件容器内
- 通过CSS变量同时支持亮色与暗色主题
- 遵循Obsidian的4px间距网格规范
可访问性(强制要求):
- 所有交互元素需支持键盘操作
- 为图标按钮提供ARIA标签
- 使用定义清晰的焦点指示器
:focus-visible - 使用设置工具提示位置
data-tooltip-position - 确保最小触控目标尺寸为44×44px
- 在模态框中正确管理焦点
- 通过键盘导航进行测试
代码质量:
- 使用async/await(而非Promise链)
- 移除所有示例/模板代码
- 在移动端进行测试(若非仅桌面端插件)
- 遵循语义化版本规范
- 尽量减少控制台日志输出(生产环境下的onload/onunload中禁止使用console.log)
Don'ts ❌
禁止做法 ❌
Memory & Lifecycle:
- Don't store view references in plugin properties
- Don't pass plugin as component to MarkdownRenderer
- Don't detach leaves in
onunload()
Type Safety:
- Don't cast to TFile/TFolder (use )
instanceof - Don't use type
any - Don't use
var
API Usage:
- Don't use global object
app - Don't use for active file edits
Vault.modify() - Don't hardcode path (use
.obsidian)vault.configDir - Don't use (use Platform API)
navigator.platform/userAgent - Don't iterate vault when direct lookup exists
- Don't use (use
fetch()instead)requestUrl()
UI/UX:
- Don't use Title Case in UI (use sentence case)
- Don't include "command" in command names/IDs
- Don't duplicate plugin ID in command IDs
- Don't set default hotkeys
- Don't create manual HTML headings (use )
.setHeading() - Don't use "General", "settings", or plugin name in settings headings
Styling:
- Don't assign styles via JavaScript
- Don't hardcode colors, sizes, or spacing (use CSS variables)
- Don't use broad CSS selectors (scope to plugin)
- Don't manually switch themes (CSS variables adapt automatically)
- Don't create or
<link>elements (use<style>file)styles.css
Security & Compatibility:
- Don't use /
innerHTML(XSS risk)outerHTML - Don't use regex lookbehind (iOS < 16.4 incompatibility)
Accessibility:
- Don't create inaccessible interactive elements
- Don't use icon buttons without ARIA labels
- Don't remove focus indicators without alternatives
- Don't make touch targets smaller than 44×44px
Code Quality:
- Don't use Promise chains (use async/await)
- Don't use (use Obsidian helpers)
document.createElement - Don't keep sample class names (MyPlugin, SampleModal, etc.)
- Don't use console.log in onload/onunload (pollutes console in production)
内存与生命周期:
- 不要在插件属性中存储视图引用
- 不要将插件作为组件传递给MarkdownRenderer
- 不要在中分离leaf
onunload()
类型安全:
- 不要强制转换为TFile/TFolder(使用)
instanceof - 不要使用类型
any - 不要使用
var
API使用:
- 不要使用全局对象
app - 不要使用编辑活跃文件
Vault.modify() - 不要硬编码路径(使用
.obsidian)vault.configDir - 不要使用(使用Platform API)
navigator.platform/userAgent - 当存在直接查找方式时,不要遍历vault
- 不要使用(使用
fetch()替代)requestUrl()
UI/UX:
- 不要在UI中使用标题大小写(使用句首大写)
- 命令名称/ID中不要包含"command"
- 命令ID中不要重复插件ID
- 不要设置默认快捷键
- 不要手动编写HTML标题(使用)
.setHeading() - 配置项标题中不要使用"General"、"settings"或插件名称
样式规范:
- 不要通过JavaScript设置样式
- 不要硬编码颜色、尺寸或间距(使用CSS变量)
- 不要使用宽泛的CSS选择器(限定在插件作用域内)
- 不要手动切换主题(CSS变量会自动适配)
- 不要创建或
<link>元素(使用<style>文件)styles.css
安全与兼容性:
- 不要使用/
innerHTML(存在XSS风险)outerHTML - 不要使用正则表达式后行断言(iOS 16.4以下版本不兼容)
可访问性:
- 不要创建无法通过键盘访问的交互元素
- 不要使用无ARIA标签的图标按钮
- 不要移除焦点指示器而不提供替代方案
- 不要设置小于44×44px的触控目标
代码质量:
- 不要使用Promise链(使用async/await)
- 不要使用(使用Obsidian辅助工具)
document.createElement - 不要保留示例类名(如MyPlugin、SampleModal等)
- 生产环境下的onload/onunload中不要使用console.log(会污染控制台)
When Reviewing/Writing Code
代码评审/编写时的检查清单
Use this checklist for code review and implementation:
- Memory management: Are components and views properly managed?
- Type safety: Using instead of casts?
instanceof - UI text: Is everything in sentence case?
- Command naming: No redundant words?
- File operations: Using preferred APIs?
- Mobile compatibility: No iOS-incompatible features?
- Sample code: Removed all boilerplate?
- Manifest: Correct version, valid structure?
- Accessibility: Keyboard navigation, ARIA labels, focus indicators?
- Testing: Can you use the plugin without a mouse?
- Touch targets: Are all interactive elements at least 44×44px?
- Focus styles: Using and proper CSS variables?
:focus-visible
进行代码评审与实现时,请使用以下检查清单:
- 内存管理:组件与视图是否得到合理管理?
- 类型安全:是否使用而非强制类型转换?
instanceof - UI文本:是否全部使用句首大写格式?
- 命令命名:是否无冗余词汇?
- 文件操作:是否使用推荐的API?
- 移动端兼容性:是否存在iOS不兼容的功能?
- 示例代码:是否已移除所有模板代码?
- Manifest文件:版本是否正确、结构是否合法?
- 可访问性:是否支持键盘导航、是否添加ARIA标签、是否有焦点指示器?
- 测试:是否可以不使用鼠标操作插件?
- 触控目标:所有交互元素的尺寸是否至少为44×44px?
- 焦点样式:是否使用及正确的CSS变量?
:focus-visible
Common Patterns
常见模式
Proper Command Registration
正确的命令注册方式
typescript
// ✅ CORRECT
this.addCommand({
id: 'insert-timestamp',
name: 'Insert timestamp',
editorCallback: (editor: Editor, view: MarkdownView) => {
editor.replaceSelection(new Date().toISOString());
}
});typescript
// ✅ CORRECT
this.addCommand({
id: 'insert-timestamp',
name: 'Insert timestamp',
editorCallback: (editor: Editor, view: MarkdownView) => {
editor.replaceSelection(new Date().toISOString());
}
});Safe Type Narrowing
安全的类型收窄
typescript
// ✅ CORRECT
const file = this.app.vault.getAbstractFileByPath(path);
if (file instanceof TFile) {
// TypeScript now knows it's a TFile
await this.app.vault.read(file);
}typescript
// ✅ CORRECT
const file = this.app.vault.getAbstractFileByPath(path);
if (file instanceof TFile) {
// TypeScript now knows it's a TFile
await this.app.vault.read(file);
}Keyboard Accessible Button
支持键盘操作的按钮
typescript
// ✅ CORRECT
const button = containerEl.createEl('button', {
attr: {
'aria-label': 'Open settings',
'data-tooltip-position': 'top'
}
});
button.setText('⚙️');
button.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
performAction();
}
});typescript
// ✅ CORRECT
const button = containerEl.createEl('button', {
attr: {
'aria-label': 'Open settings',
'data-tooltip-position': 'top'
}
});
button.setText('⚙️');
button.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
performAction();
}
});Themed CSS
适配主题的CSS
css
/* ✅ CORRECT */
.my-plugin-modal {
background: var(--modal-background);
color: var(--text-normal);
padding: var(--size-4-4);
border-radius: var(--radius-m);
font-size: var(--font-ui-medium);
}
.my-plugin-button:focus-visible {
outline: 2px solid var(--interactive-accent);
outline-offset: 2px;
}css
/* ✅ CORRECT */
.my-plugin-modal {
background: var(--modal-background);
color: var(--text-normal);
padding: var(--size-4-4);
border-radius: var(--radius-m);
font-size: var(--font-ui-medium);
}
.my-plugin-button:focus-visible {
outline: 2px solid var(--interactive-accent);
outline-offset: 2px;
}Additional Resources
额外资源
- ESLint Plugin: (install for automatic checking)
eslint-plugin-obsidianmd - Obsidian API Docs: https://docs.obsidian.md
- Sample Plugin: https://github.com/obsidianmd/obsidian-sample-plugin
- Community: Obsidian Discord, Forum
- ESLint插件:(安装后可自动检查)
eslint-plugin-obsidianmd - Obsidian API文档:https://docs.obsidian.md
- 示例插件:https://github.com/obsidianmd/obsidian-sample-plugin
- 社区:Obsidian Discord、官方论坛
Important Notes
重要提示
- These guidelines are based on which is under active development
eslint-plugin-obsidianmd - Rules marked as auto-fixable can be automatically corrected with ESLint's flag
--fix - Accessibility is NOT optional - all interactive elements must be keyboard accessible
- Always test on mobile devices if your plugin is not desktop-only
When helping with Obsidian plugin development, proactively apply these rules and suggest improvements based on these guidelines. Refer to the detailed reference files for comprehensive information on specific topics.
- 本指南基于仍在积极开发中的制定
eslint-plugin-obsidianmd - 标记为可自动修复的规则可通过ESLint的标志自动修正
--fix - 可访问性为强制要求 - 所有交互元素必须支持键盘操作
- 若非仅桌面端插件,请务必在移动设备上进行测试
在协助Obsidian插件开发时,请主动应用这些规则并基于本指南提出改进建议。如需特定主题的全面信息,请参考详细的参考文档。