Skip to content

Commit 1b9581b

Browse files
authored
add plugin guidelines (#899)
1 parent e4a4339 commit 1b9581b

1 file changed

Lines changed: 66 additions & 0 deletions

File tree

apps/obsidian/AGENTS.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,69 @@ Example: ( ![[lucide-cog.svg#icon]] )
3232
### Function guides
3333

3434
- Any function that deals with querying vault's frontmatter, default to using Datacore API first, then write fallback where you use `plugin.app.vault.getMarkdownFiles()` to iterate through each file's frontmatter
35+
36+
## Plugin Store Guidelines
37+
38+
These rules must be followed for the plugin to be accepted into the Obsidian community plugin store.
39+
40+
### Security
41+
42+
- Never use `innerHTML`, `outerHTML`, or `insertAdjacentHTML` with user-controlled content
43+
- Use Obsidian DOM helpers instead: `createEl()`, `createDiv()`, `createSpan()`
44+
45+
### App instance
46+
47+
- Always use `this.app` — never the global `app` object
48+
49+
### Event listeners
50+
51+
- Register all event listeners via `this.registerEvent()` so they are automatically cleaned up on plugin unload
52+
53+
### UI text
54+
55+
- Use sentence case in settings headings and labels (not title case)
56+
- Prefer `setHeading()` over raw HTML heading elements (`<h1>`, `<h2>`, etc.)
57+
58+
### Commands
59+
60+
- Do not set default hotkeys — they conflict with other plugins
61+
- Use the correct callback type: `callback` for always-available commands, `checkCallback` when the command is conditionally available, or the editor variants for editor-scoped commands
62+
- Do not manually prepend the plugin ID to command IDs — Obsidian adds it automatically
63+
64+
### Workspace
65+
66+
- Do not store references to custom views — look them up fresh each time they are needed
67+
Don't do this:
68+
`this.registerView(MY_VIEW_TYPE, () => this.view = new MyCustomView());`
69+
Do this instead:
70+
`this.registerView(MY_VIEW_TYPE, () => new MyCustomView());`
71+
To access the view from your plugin, use `Workspace.getActiveLeavesOfType():`
72+
73+
```
74+
for (let leaf of app.workspace.getActiveLeavesOfType(MY_VIEW_TYPE)) {
75+
let view = leaf.view;
76+
if (view instanceof MyCustomView) {
77+
// ...
78+
}
79+
}
80+
```
81+
82+
- Do not detach leaves during plugin unload
83+
84+
### Editor
85+
86+
- Prefer the Editor API over `Vault.modify()` when the note is currently open in the editor
87+
- Prefer `Vault.process()` instead of `Vault.modify()` to modify a file in the background
88+
89+
### Mobile compatibility
90+
91+
- Node.js and Electron APIs (`fs`, `crypto`, `os`) are unavailable on mobile
92+
- If the plugin targets mobile, use web API equivalents: `SubtleCrypto` instead of `crypto`, `navigator.clipboard` for clipboard access
93+
- Regex lookbehind assertions are not supported on some mobile — avoid them if possible
94+
95+
### Code quality
96+
97+
- Use `const`/`let` — never `var`
98+
- Prefer `async`/`await` over `.then()` chains
99+
- Minimize `console.log` — remove debug logs before shipping
100+
- Do not hardcode styles inline — use CSS classes and Obsidian's CSS variables

0 commit comments

Comments
 (0)