8.9 KiB
8.9 KiB
Obsidian community plugin
Project overview
- Target: Obsidian Community Plugin (TypeScript → bundled JavaScript).
- Entry point:
main.tscompiled tomain.jsand loaded by Obsidian. - Required release artifacts:
main.js,manifest.json, and optionalstyles.css.
Environment & tooling
- Node.js: use current LTS (Node 18+ recommended).
- Package manager: npm.
- Bundler: esbuild (preferred). Rollup or webpack are acceptable if they bundle all external dependencies into
main.js. - Types:
obsidiantype definitions.
Install
npm install
Dev (watch)
npm run dev
Production build
npm run build
Linting
- To use eslint install eslint from terminal:
npm install -g eslint - To use eslint to analyze this project use this command:
eslint main.ts - eslint will then create a report with suggestions for code improvement by file and line number.
- If your source code is in a folder, such as
src, you can use eslint with this command to analyze all files in that folder:eslint .\src\
File & folder conventions
- Organize code into multiple files: Split functionality across separate modules rather than putting everything in
main.ts. - Source lives in
src/. Keepmain.tssmall and focused on plugin lifecycle (loading, unloading, registering commands). - Recommended file structure:
src/ main.ts # Plugin entry point, lifecycle management settings.ts # Settings interface and defaults commands/ # Command implementations command1.ts command2.ts ui/ # UI components, modals, views modal.ts view.ts utils/ # Utility functions, helpers helpers.ts constants.ts types.ts # TypeScript interfaces and types - Do not commit
node_modules/or build artifacts. - Keep the plugin small. Avoid large dependencies. Prefer browser-compatible packages.
- Generated output should be placed at the plugin root or
dist/depending on your build setup. Release artifacts must end up at the top level of the plugin folder in the vault (main.js,manifest.json,styles.css).
Manifest rules (manifest.json)
- Must include (non-exhaustive):
id(plugin ID; for local dev it should match the folder name)nameversion(Semantic Versioningx.y.z)minAppVersiondescriptionisDesktopOnly(boolean)- Optional:
author,authorUrl,fundingUrl(string or map)
- Never change
idafter release. Treat it as stable API. - Keep
minAppVersionaccurate when using newer APIs.
Building & running in Obsidian
- Manual install for testing: copy
main.js,manifest.json,styles.css(if any) to:<Vault>/.obsidian/plugins/<plugin-id>/ - Reload Obsidian and enable the plugin in Settings → Community plugins.
Commands & settings
- Any user-facing commands should be added via
this.addCommand(...). - If the plugin has configuration, provide a settings tab and sensible defaults.
- Persist settings using
this.loadData()/this.saveData(). - Use stable command IDs; avoid renaming once released.
Versioning & releases
- Bump
versioninmanifest.json(SemVer) and updateversions.jsonto map plugin version → minimum app version. - Create a GitHub release whose tag exactly matches
manifest.json'sversion(avoid a leadingvunless your repo uses it consistently). - Attach
manifest.json,main.js, andstyles.css(if present) to the release as individual assets. - After the initial release, follow the process to add/update your plugin in the community catalog as required.
Security, privacy, and compliance
Follow Obsidian's Developer Policies and Plugin Guidelines. In particular:
- Default to local/offline operation. Only make network requests when essential to the feature.
- No hidden telemetry. If you collect optional analytics or call third-party services, require explicit opt-in and document clearly in
README.mdand in settings. - Never execute remote code, fetch and eval scripts, or auto-update plugin code outside of normal releases.
- Minimize scope: read/write only what's necessary inside the vault. Do not access files outside the vault.
- Clearly disclose any external services used, data sent, and risks.
- Respect user privacy. Do not collect vault contents, filenames, or personal information unless absolutely necessary and explicitly consented.
- Avoid deceptive patterns, ads, or spammy notifications.
- Register and clean up all DOM, app, and interval listeners using the provided
register*helpers so the plugin unloads safely.
UX & copy guidelines (for UI text, commands, settings)
- Prefer sentence case for headings, buttons, and titles.
- Use clear, action-oriented imperatives in step-by-step copy.
- Use bold to indicate literal UI labels. Prefer "select" for interactions.
- Use arrow notation for navigation: Settings → Community plugins.
- Keep in-app strings short, consistent, and free of jargon.
Performance
- Keep startup light. Defer heavy work until needed.
- Avoid long-running tasks during
onload; use lazy initialization. - Batch disk access and avoid excessive vault scans.
- Debounce/throttle expensive operations in response to file system events.
Coding conventions
- TypeScript with
"strict": truepreferred. - Keep
main.tsminimal: Focus only on plugin lifecycle (onload, onunload, addCommand calls). Delegate all feature logic to separate modules. - Split large files: If any file exceeds ~200-300 lines, consider breaking it into smaller, focused modules.
- Use clear module boundaries: Each file should have a single, well-defined responsibility.
- Bundle everything into
main.js(no unbundled runtime deps). - Avoid Node/Electron APIs if you want mobile compatibility; set
isDesktopOnlyaccordingly. - Prefer
async/awaitover promise chains; handle errors gracefully.
Mobile
- Where feasible, test on iOS and Android.
- Don't assume desktop-only behavior unless
isDesktopOnlyistrue. - Avoid large in-memory structures; be mindful of memory and storage constraints.
Agent do/don't
Do
- Add commands with stable IDs (don't rename once released).
- Provide defaults and validation in settings.
- Write idempotent code paths so reload/unload doesn't leak listeners or intervals.
- Use
this.register*helpers for everything that needs cleanup.
Don't
- Introduce network calls without an obvious user-facing reason and documentation.
- Ship features that require cloud services without clear disclosure and explicit opt-in.
- Store or transmit vault contents unless essential and consented.
Common tasks
Organize code across multiple files
main.ts (minimal, lifecycle only):
import { Plugin } from "obsidian";
import { MySettings, DEFAULT_SETTINGS } from "./settings";
import { registerCommands } from "./commands";
export default class MyPlugin extends Plugin {
settings: MySettings;
async onload() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
registerCommands(this);
}
}
settings.ts:
export interface MySettings {
enabled: boolean;
apiKey: string;
}
export const DEFAULT_SETTINGS: MySettings = {
enabled: true,
apiKey: "",
};
commands/index.ts:
import { Plugin } from "obsidian";
import { doSomething } from "./my-command";
export function registerCommands(plugin: Plugin) {
plugin.addCommand({
id: "do-something",
name: "Do something",
callback: () => doSomething(plugin),
});
}
Add a command
this.addCommand({
id: "your-command-id",
name: "Do the thing",
callback: () => this.doTheThing(),
});
Persist settings
interface MySettings { enabled: boolean }
const DEFAULT_SETTINGS: MySettings = { enabled: true };
async onload() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
await this.saveData(this.settings);
}
Register listeners safely
this.registerEvent(this.app.workspace.on("file-open", f => { /* ... */ }));
this.registerDomEvent(window, "resize", () => { /* ... */ });
this.registerInterval(window.setInterval(() => { /* ... */ }, 1000));
Troubleshooting
- Plugin doesn't load after build: ensure
main.jsandmanifest.jsonare at the top level of the plugin folder under<Vault>/.obsidian/plugins/<plugin-id>/. - Commands not appearing: verify
addCommandruns afteronloadand IDs are unique. - Settings not persisting: ensure
loadData/saveDataare awaited and you re-render the UI after changes. - Mobile-only issues: confirm you're not using desktop-only APIs; check
isDesktopOnlyand adjust.
References
- Obsidian sample plugin: https://github.com/obsidianmd/obsidian-sample-plugin
- API documentation: https://docs.obsidian.md
- Developer policies: https://docs.obsidian.md/Developer+policies
- Plugin guidelines: https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines
- Style guide: https://help.obsidian.md/style-guide