$npx -y skills add gapmiss/obsidian-plugin-skill --skill obsidianComprehensive guidelines for Obsidian.md plugin development including ESLint rules from eslint-plugin-obsidianmd v0.4.1, TypeScript best practices, memory management, API usage (requestUrl vs fetch), UI/UX standards, popout window compatibility, community.obsidian.md submission p
| 1 | # Obsidian Plugin Development Guidelines |
| 2 | |
| 3 | Follow these comprehensive guidelines derived from the official Obsidian ESLint plugin rules, submission requirements, and best practices. |
| 4 | |
| 5 | ## Getting Started |
| 6 | |
| 7 | ### Quick Start Tool |
| 8 | |
| 9 | For new plugin projects, an interactive boilerplate generator is available: |
| 10 | - **Script**: `tools/create-plugin.js` in the skill repository |
| 11 | - **Command**: Invoke `create-plugin` using your agent's method (`/create-plugin`, `$create-plugin`, or `@create-plugin`) |
| 12 | - Generates minimal, best-practice boilerplate with no sample code |
| 13 | - Detects existing projects and only adds missing files |
| 14 | |
| 15 | Recommend the boilerplate generator when users ask how to create a new plugin, want to start a new project, or need help setting up the basic structure. |
| 16 | |
| 17 | --- |
| 18 | |
| 19 | ## Rules Reference (eslint-plugin-obsidianmd v0.4.1) |
| 20 | |
| 21 | ### Submission & Naming |
| 22 | | # | Rule | ✅ Do | ❌ Don't | |
| 23 | |---|------|--------|----------| |
| 24 | | 1 | Plugin ID | Omit "obsidian"; don't end with "plugin" | Include "obsidian" or end with "plugin" | |
| 25 | | 2 | Plugin name | Omit "Obsidian"; don't end with "Plugin" | Include "Obsidian" or end with "Plugin" | |
| 26 | | 3 | Plugin name | Don't start with "Obsi" or end with "dian" | Start with "Obsi" or end with "dian" | |
| 27 | | 4 | Description | Omit "Obsidian", "This plugin", etc. | Use "Obsidian" or "This plugin" | |
| 28 | | 5 | Description | End with `.?!)` punctuation | Leave description without terminal punctuation | |
| 29 | |
| 30 | ### Memory & Lifecycle |
| 31 | | # | Rule | ✅ Do | ❌ Don't | |
| 32 | |---|------|--------|----------| |
| 33 | | 6 | Event cleanup | Use `registerEvent()` for automatic cleanup | Register events without cleanup | |
| 34 | | 6a | DOM events | Use `registerDomEvent()` on the plugin or owning component | Pair `addEventListener` with manual `removeEventListener` cleanup | |
| 35 | | 7 | View references | Return views/components directly | Store view references in plugin properties or pass plugin as component to `MarkdownRenderer` | |
| 36 | | 8 | Leaf detachment | Let Obsidian handle leaf cleanup | Call `detachLeavesOfType()` in `onunload` | |
| 37 | |
| 38 | ### Type Safety |
| 39 | | # | Rule | ✅ Do | ❌ Don't | |
| 40 | |---|------|--------|----------| |
| 41 | | 9 | TFile/TFolder | Use `instanceof` for type checking | Cast to TFile/TFolder; use `any`; use `var` | |
| 42 | | 10 | DOM instanceof | Use `.instanceOf(T)` for DOM Nodes/UIEvents | Use `instanceof` for cross-window DOM checks | |
| 43 | |
| 44 | ### UI/UX |
| 45 | | # | Rule | ✅ Do | ❌ Don't | |
| 46 | |---|------|--------|----------| |
| 47 | | 11 | UI text | Sentence case — "Advanced settings" | Title Case — "Advanced Settings" | |
| 48 | | 12 | JSON locale | Sentence case in JSON locale files (`recommendedWithLocalesEn`) | Title case in locale JSON | |
| 49 | | 13 | TS/JS locale | Sentence case in TS/JS locale modules | Title case in locale modules | |
| 50 | |
| 51 | > **Note (v0.4.0):** `ui/sentence-case` is now enabled (`warn`) and enforced on inline UI strings — it was disabled in v0.3.0. Use the `recommendedWithLocalesEn` config to also check English locale files (rules 12–13). |
| 52 | | 14 | Command names | Omit "command" in command names/IDs | Include "command" in names/IDs | |
| 53 | | 15 | Command IDs | Omit plugin ID/name from command IDs/names | Duplicate plugin ID in command IDs | |
| 54 | | 16 | Hotkeys | No default hotkeys | Set default hotkeys | |
| 55 | | 17 | Settings headings | Use `.setHeading()` | Create manual HTML headings; use "General", "settings", or plugin name in headings | |
| 56 | |
| 57 | ### API Best Practices |
| 58 | | # | Rule | ✅ Do | ❌ Don't | |
| 59 | |---|------|--------|----------| |
| 60 | | 18 | Active file edits | Use Editor API | Use `Vault.modify()` for active file edits | |
| 61 | | 19 | Background file mods | Use `Vault.process()` | Use `Vault.modify()` for background modifications | |
| 62 | | 20 | File deletion | Use `FileManager.trashFile()` | Use `Vault.trash()` or `Vault.delete()` directly | |
| 63 | | 21 | File lookup | Use `Vault.getAbstractFileByPath()` | Iterate all files with `Vault.getFiles().find()` | |
| 64 | | 22 | User paths | Use `normalizePath()` | Hardcode `.obsidian` path; use raw user paths | |
| 65 | | 23 | OS detection | Use `Platform` API | Use `navigator.platform`/`userAgent` | |
| 66 | | 24 | Network requests | Use `requestUrl()` | Use `fetch()` | |
| 67 | | 25 | Logging | Minimize console logging; none in `onload`/`onunload` in production | Use `console.log` in `onload`/`onunload` | |
| 68 | | 26 | Input suggest | Use built-in `AbstractInputSuggest` | Copy Liam's `TextInputSuggest` implementation | |
| 69 | | 27 | API compatibility | Check `minAppVersion` for API availability | Use APIs not available in declared minAppVersion | |
| 70 | | 28 | Language detection | Use Obsidian's `getLanguage()` |