Compare commits

...

93 commits

Author SHA1 Message Date
1aa3371ff6 rename 2026-02-14 23:49:36 +01:00
Lishid
dc2fa22c4d
Fix typo in ESLint plugin link 2025-12-30 13:09:48 -05:00
Johannes Theiner
2323eddbb1
Merge pull request #164 from joethei/master
Include eslint config
2025-12-18 11:53:40 +01:00
Johannes Theiner
911b773800 Merge remote-tracking branch 'upstream/master'
# Conflicts:
#	README.md
#	src/main.ts
2025-11-14 15:24:54 +01:00
Johannes Theiner
123fc4a870 update eslint plugin version
remove builtin-modules external dependency
2025-11-14 14:28:33 +01:00
Johannes Theiner
0eca98adda update package.json 2025-11-10 22:27:46 +01:00
Johannes Theiner
92cba25647 update sample plugin with eslint instructions 2025-11-10 12:26:07 +01:00
Johannes Theiner
9984b040b6 remove testing code 2025-10-17 12:36:26 +02:00
Johannes Theiner
e2a64e0534
Merge pull request #151 from adamu/main
Prefix unused variables with _
2025-09-17 18:12:44 +02:00
Johannes Theiner
8933f6ce64
Merge pull request #142 from johannrichard/declutter-version-bump
build: only write new minAppVersion requirements to `versions.json`
2025-09-17 17:54:33 +02:00
Steph Ango
db97f5f629
Merge pull request #155 from obsidianmd/agents-md
AGENTS.md
2025-09-05 09:12:10 -07:00
Steph Ango
9673533aa9 language 2025-09-05 09:10:47 -07:00
Steph Ango
33075ecd13 use forward slashes for cross OS compatibility 2025-09-05 09:04:04 -07:00
Steph Ango
188bb6120f small copy tweaks 2025-09-05 09:01:58 -07:00
Steph Ango
a4398b8ecc Corrections based on feedback 2025-09-05 08:58:49 -07:00
Steph Ango
ce4fc8c209 First pass 2025-09-04 15:28:25 -07:00
Adam Millerchip
f16c1401b3 Prefix unused variables with _ 2025-08-28 14:38:40 +09:00
Johannes Theiner
fa2fac56be Remove node 18 from action 2025-07-04 13:29:58 +02:00
Johannes Theiner
e0485eb3ba add lock file to repo 2025-07-04 13:28:27 +02:00
Johannes Theiner
dbc9096f81 Add lint action 2025-07-04 13:27:53 +02:00
Johannes Theiner
90290a8af5 Update to use released eslint plugin 2025-07-03 12:00:06 +02:00
Johannes Theiner
68ab547f2c Update to use released eslint plugin 2025-06-02 12:10:29 +02:00
Johann Richard
3fe07677b5
build: only write new minAppVersion requirements to versions.json
Only add a new version requirements if `minAppVersion` is not already in `versions.json`. Should declutter `versions.json`.
2025-04-28 08:38:38 +02:00
Johannes Theiner
6d09ce3e39 Add license, fixes #98 2025-01-27 21:38:01 +00:00
Johannes Theiner
ba04056938
Merge remote-tracking branch 'origin/master' 2025-01-02 16:48:30 +01:00
Johannes Theiner
55f5080882
Add linting of dependencies, throwing errors when including using telemetry libraries 2025-01-02 16:48:03 +01:00
Johannes Theiner
20002ffc65
Merge branch 'obsidianmd:master' into master 2024-11-20 17:02:47 +01:00
Johannes Theiner
ee04e2f81f
Merge pull request #120 from claremacrae/fix-readme-api-comment
Remove statement that API is unstable
2024-11-11 21:25:18 +01:00
Clare Macrae
92050ad841
Remove statement that API is unstable 2024-11-11 19:58:31 +00:00
Steph Ango
daa0cba23c
Merge pull request #78 from aleksey-rowan/patch-1
Minify production build
2024-08-21 11:22:30 -07:00
Johannes Theiner
fa15a2558e
Merge remote-tracking branch 'origin/master'
# Conflicts:
#	tsconfig.json
2024-07-31 23:00:52 +02:00
Johannes Theiner
15e343739e
Add stylelint and configure eslint to add custom plugin.
- Split up main.ts into multiple files.
2024-07-31 22:59:59 +02:00
Johannes Theiner
bfa0145644
Merge pull request #91 from revolter/patch-1
Fix inconsistent indentation
2024-07-31 22:06:34 +02:00
Johannes Theiner
893e489e84
Merge pull request #92 from revolter/patch-2
Fix incorrect TypeScript spelling
2024-07-31 21:47:47 +02:00
Johannes Theiner
d05f42d8c5
Merge pull request #95 from tobiasvl/patch-1
Update README.md
2024-07-31 20:18:53 +02:00
Tobias V. Langhoff
47ec36c11e
Update README.md
Update link to plugin guidelines
2024-03-02 22:30:14 +01:00
Johannes Theiner
b9a0e401e0
configure eslint 2024-02-29 13:18:14 +01:00
iulianOnofrei (U-lee-aan)
de770934b2
Fix incorrect TypeScript spelling 2024-01-21 15:20:39 +02:00
iulianOnofrei (U-lee-aan)
f4debcda6b
Fix inconsistent indentation 2024-01-19 21:14:58 +02:00
Aleksey Rowan
7330e7499a
build: minify prod build
closes #70
2023-11-15 14:10:43 -05:00
Aleksey Rowan
e60294b950
Update manifest description (#77) 2023-11-15 12:17:04 -05:00
Lishid
7112f01bc6
Update README.md 2023-07-25 15:17:26 -04:00
Johannes Theiner
e8f03522bc
Adapt to plugin guidelines (#65)
- remove header in settings
- remove logging of changed settings value
2023-07-17 12:36:25 -04:00
Erica Xu
9be2b5d748
Update manifest.json 2023-07-11 11:37:36 -04:00
Alexander Pozdneev
2aee08d3d5
Remove old info about styles.css (#56) 2023-05-01 14:48:46 -04:00
Tim Rogers
0b5e5a2f6e
Upgrade esbuild to v0.17.x (#47) 2023-01-25 13:49:50 -05:00
INOUE Takuya
49fba8aa1f
fix .eslintignore (#48) 2023-01-19 10:06:51 -05:00
Erica Xu
747672e2f8
Merge pull request #42 from obsidianmd/funding-url
Add funding URL documentation
2022-12-05 15:57:04 -05:00
Erica Xu
d3bb1e6775
Update README.md 2022-12-05 15:55:39 -05:00
Erica Xu
b376f9c428
Add funding URL documentation 2022-12-05 15:49:16 -05:00
Erica Xu
97d84bc54b
Update manifest.json 2022-12-05 15:28:53 -05:00
lishid
866d3b8f0d Build: Target ES2018. 2022-08-20 20:25:17 -04:00
lishid
64e8649877 Update sample css file. 2022-08-09 13:39:02 -04:00
lishid
b46f6c9322 Update for 0.15 2022-08-09 13:38:50 -04:00
lishid
04432b2ebf Upgrade dependencies, add strictNullChecks. 2022-06-24 15:41:39 -04:00
Lishid
00967d9040
Update esbuild.config.mjs 2022-06-19 20:50:11 -04:00
Henré Botha
f85d2b74b5
Fix README headings (#29) 2022-05-07 11:15:23 -04:00
Xiao Meng
f690c04577
use LF instead of CRLF (#28) 2022-04-15 14:13:31 -04:00
Lishid
24918c946d
Update README.md 2022-04-04 22:20:58 -04:00
lishid
af0d47c19c Always use latest obsidian package. 2022-03-09 10:29:32 -05:00
Lishid
43a3b5eaeb
Update package.json 2022-02-24 00:17:16 -05:00
pseudometa
4b06e44f8f
Update .gitignore (#25) 2022-01-28 10:34:53 -05:00
aidenlx
f3286063d3
add version bump script (#10) 2022-01-22 16:13:50 -05:00
Federico Granata
4f502e92eb
Update README.md (#24) 2022-01-21 11:15:02 -05:00
Federico Granata
67e05fe677
remove package-lock.json from .gitignore (#23) 2022-01-21 11:14:49 -05:00
fyears
fe035a3008
add type check (#22) 2022-01-16 10:07:19 -05:00
lishid
a25092b586 Update esbuild script to include codemirror packages as external. 2022-01-10 09:01:38 -05:00
lishid
d006ed755a Update tsconfig. 2021-11-06 19:05:49 -04:00
lishid
db18a36e65 Update build script to include external node packages. 2021-11-01 15:18:11 -04:00
TfTHacker
6fdd374cb8
Adding eslint for code .\src\ (#17) 2021-10-30 14:04:07 -04:00
Phillip
02ac033b15
Use ESBuild API instead of passing command line arguments (#16) 2021-10-25 11:45:15 -04:00
lishid
e013825c21 Add comment for gitignore. 2021-10-23 04:58:40 -04:00
lishid
3afc9d78ab Update sample plugin to use ESBuild. 2021-10-19 13:34:58 -04:00
fyears
5f95bce9e1 preserve and load settings correctly 2021-10-10 11:11:02 -04:00
Tokuhiro Matsuno
c228a70223 Add data.json to .gitignore
Prevent unintended disclosure of data.json.
2021-04-28 11:54:41 -04:00
lishid
3b38a36a07 Upgrade dependencies. 2021-04-23 17:01:55 -04:00
GitMurf
26721337a7 Update README - Obsidian API update instructions
Add the following to the "First time developing plugins?" section:

For updates to the Obsidian API run `npm update` in the command line under your repo folder.
2021-04-23 16:59:52 -04:00
lishid
f3f000a6b2 Update dependencies. 2021-04-12 19:06:03 -04:00
lishid
e1efb17322 Rollup: Don't include source code in distribution. 2021-04-06 08:48:36 -04:00
Maciej Beimcik
123b00ca52 a note regarding "use this template" button
This button is not visible if you are not logged in to GitHub.
It may be a bit confusing for first-timers so I suggest the note to be added
2021-03-09 16:17:18 -05:00
Konstantin
5355e3337e Update README.md
fix one little misstype
2021-03-07 20:54:45 -05:00
Clemens Ertle
00cf028cc3 add banner to bundle 2021-03-04 16:18:09 -05:00
Lishid
ceb3837850
Update README.md 2021-03-01 12:59:44 -05:00
lishid
ed0e0a6e90 Target ES6. 2021-01-31 12:55:17 -05:00
lishid
ae30a5a418 Update best practice for settings. 2021-01-24 15:41:47 -05:00
lishid
cfe4d17ce7 Add example for settings. 2020-12-22 11:24:00 -05:00
lishid
318cb2b055 Added versions.json 2020-11-10 02:52:56 -05:00
lishid
9162f82010 Removed init. 2020-10-29 21:41:47 -04:00
lishid
6e8aab9616 Add more examples for hooks. 2020-10-29 18:02:57 -04:00
lishid
99f4035214 Add example for modifying CodeMirror. 2020-10-29 17:36:03 -04:00
lishid
2d6aebfe0a Update manifest.json 2020-10-28 21:33:32 -04:00
lishid
26d068d237 Update readme. 2020-10-28 21:13:03 -04:00
lishid
08add6702a Update readme. 2020-10-28 20:48:24 -04:00
20 changed files with 5875 additions and 176 deletions

10
.editorconfig Normal file
View file

@ -0,0 +1,10 @@
# top-most EditorConfig file
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = tab
indent_size = 4
tab_width = 4

28
.github/workflows/lint.yml vendored Normal file
View file

@ -0,0 +1,28 @@
name: Node.js build
on:
push:
branches: ["**"]
pull_request:
branches: ["**"]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x, 22.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- run: npm ci
- run: npm run build --if-present
- run: npm run lint

17
.gitignore vendored
View file

@ -1,11 +1,22 @@
# vscode
.vscode
# Intellij # Intellij
*.iml *.iml
.idea .idea
# npm # npm
node_modules node_modules
package-lock.json
# build # Don't include the compiled main.js file in the repo.
# They should be uploaded to GitHub releases instead.
main.js main.js
*.js.map
# Exclude sourcemaps
*.map
# obsidian
data.json
# Exclude macOS Finder (System Explorer) View States
.DS_Store

1
.npmrc Normal file
View file

@ -0,0 +1 @@
tag-version-prefix=""

251
AGENTS.md Normal file
View file

@ -0,0 +1,251 @@
# Obsidian community plugin
## Project overview
- Target: Obsidian Community Plugin (TypeScript → bundled JavaScript).
- Entry point: `main.ts` compiled to `main.js` and loaded by Obsidian.
- Required release artifacts: `main.js`, `manifest.json`, and optional `styles.css`.
## Environment & tooling
- Node.js: use current LTS (Node 18+ recommended).
- **Package manager: npm** (required for this sample - `package.json` defines npm scripts and dependencies).
- **Bundler: esbuild** (required for this sample - `esbuild.config.mjs` and build scripts depend on it). Alternative bundlers like Rollup or webpack are acceptable for other projects if they bundle all external dependencies into `main.js`.
- Types: `obsidian` type definitions.
**Note**: This sample project has specific technical dependencies on npm and esbuild. If you're creating a plugin from scratch, you can choose different tools, but you'll need to replace the build configuration accordingly.
### Install
```bash
npm install
```
### Dev (watch)
```bash
npm run dev
```
### Production build
```bash
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/`. Keep `main.ts` small and focused on plugin lifecycle (loading, unloading, registering commands).
- **Example 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 build artifacts**: Never commit `node_modules/`, `main.js`, or other generated files to version control.
- 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)
- `name`
- `version` (Semantic Versioning `x.y.z`)
- `minAppVersion`
- `description`
- `isDesktopOnly` (boolean)
- Optional: `author`, `authorUrl`, `fundingUrl` (string or map)
- Never change `id` after release. Treat it as stable API.
- Keep `minAppVersion` accurate when using newer APIs.
- Canonical requirements are coded here: https://github.com/obsidianmd/obsidian-releases/blob/master/.github/workflows/validate-plugin-entry.yml
## Testing
- 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 `version` in `manifest.json` (SemVer) and update `versions.json` to map plugin version → minimum app version.
- Create a GitHub release whose tag exactly matches `manifest.json`'s `version`. Do not use a leading `v`.
- Attach `manifest.json`, `main.js`, and `styles.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.md` and 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": true` preferred.
- **Keep `main.ts` minimal**: 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 `isDesktopOnly` accordingly.
- Prefer `async/await` over promise chains; handle errors gracefully.
## Mobile
- Where feasible, test on iOS and Android.
- Don't assume desktop-only behavior unless `isDesktopOnly` is `true`.
- 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):
```ts
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**:
```ts
export interface MySettings {
enabled: boolean;
apiKey: string;
}
export const DEFAULT_SETTINGS: MySettings = {
enabled: true,
apiKey: "",
};
```
**commands/index.ts**:
```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
```ts
this.addCommand({
id: "your-command-id",
name: "Do the thing",
callback: () => this.doTheThing(),
});
```
### Persist settings
```ts
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
```ts
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.js` and `manifest.json` are at the top level of the plugin folder under `<Vault>/.obsidian/plugins/<plugin-id>/`.
- Build issues: if `main.js` is missing, run `npm run build` or `npm run dev` to compile your TypeScript source code.
- Commands not appearing: verify `addCommand` runs after `onload` and IDs are unique.
- Settings not persisting: ensure `loadData`/`saveData` are awaited and you re-render the UI after changes.
- Mobile-only issues: confirm you're not using desktop-only APIs; check `isDesktopOnly` and 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

5
LICENSE Normal file
View file

@ -0,0 +1,5 @@
Copyright (C) 2020-2025 by Dynalist Inc.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View file

@ -1,22 +1,90 @@
## Obsidian Sample Plugin # Obsidian Sample Plugin
This is a sample plugin for Obsidian (https://obsidian.md). This is a sample plugin for Obsidian (https://obsidian.md).
This project uses Typescript to provide type checking and documentation. This project uses TypeScript to provide type checking and documentation.
The repo contains the latest plugin API (obsidian.d.ts) in Typescript Definition format, which contains TSDoc comments describing what it does. The repo depends on the latest plugin API (obsidian.d.ts) in TypeScript Definition format, which contains TSDoc comments describing what it does.
**Note:** The Obsidian API is still in early alpha and is subject to change at any time! This sample plugin demonstrates some of the basic functionality the plugin API can do.
- Adds a ribbon icon, which shows a Notice when clicked.
- Adds a command "Open modal (simple)" which opens a Modal.
- Adds a plugin setting tab to the settings page.
- Registers a global click event and output 'click' to the console.
- Registers a global interval which logs 'setInterval' to the console.
### How to use ## First time developing plugins?
Quick starting guide for new plugin devs:
- Check if [someone already developed a plugin for what you want](https://obsidian.md/plugins)! There might be an existing plugin similar enough that you can partner up with.
- Make a copy of this repo as a template with the "Use this template" button (login to GitHub if you don't see it).
- Clone your repo to a local development folder. For convenience, you can place this folder in your `.obsidian/plugins/your-plugin-name` folder.
- Install NodeJS, then run `npm i` in the command line under your repo folder.
- Run `npm run dev` to compile your plugin from `main.ts` to `main.js`.
- Make changes to `main.ts` (or create new `.ts` files). Those changes should be automatically compiled into `main.js`.
- Reload Obsidian to load the new version of your plugin.
- Enable plugin in settings window.
- For updates to the Obsidian API run `npm update` in the command line under your repo folder.
## Releasing new releases
- Update your `manifest.json` with your new version number, such as `1.0.1`, and the minimum Obsidian version required for your latest release.
- Update your `versions.json` file with `"new-plugin-version": "minimum-obsidian-version"` so older versions of Obsidian can download an older version of your plugin that's compatible.
- Create new GitHub release using your new version number as the "Tag version". Use the exact version number, don't include a prefix `v`. See here for an example: https://github.com/obsidianmd/obsidian-sample-plugin/releases
- Upload the files `manifest.json`, `main.js`, `styles.css` as binary attachments. Note: The manifest.json file must be in two places, first the root path of your repository and also in the release.
- Publish the release.
> You can simplify the version bump process by running `npm version patch`, `npm version minor` or `npm version major` after updating `minAppVersion` manually in `manifest.json`.
> The command will bump version in `manifest.json` and `package.json`, and add the entry for the new version to `versions.json`
## Adding your plugin to the community plugin list
- Check the [plugin guidelines](https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines).
- Publish an initial version.
- Make sure you have a `README.md` file in the root of your repo.
- Make a pull request at https://github.com/obsidianmd/obsidian-releases to add your plugin.
## How to use
- Clone this repo. - Clone this repo.
- `npm i` or `yarn` to install dependencies - Make sure your NodeJS is at least v16 (`node --version`).
- `npm i` or `yarn` to install dependencies.
- `npm run dev` to start compilation in watch mode. - `npm run dev` to start compilation in watch mode.
### How to install the plugin ## Manually installing the plugin
- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `vault/.obsidian/plugins/plugin-id/`. - Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`.
### API Documentation ## Improve code quality with eslint
- [ESLint](https://eslint.org/) is a tool that analyzes your code to quickly find problems. You can run ESLint against your plugin to find common bugs and ways to improve your code.
- This project already has eslint preconfigured, you can invoke a check by running`npm run lint`
- Together with a custom eslint [plugin](https://github.com/obsidianmd/eslint-plugin) for Obsidan specific code guidelines.
- A GitHub action is preconfigured to automatically lint every commit on all branches.
See https://github.com/obsidianmd/obsidian-api ## Funding URL
You can include funding URLs where people who use your plugin can financially support it.
The simple way is to set the `fundingUrl` field to your link in your `manifest.json` file:
```json
{
"fundingUrl": "https://buymeacoffee.com"
}
```
If you have multiple URLs, you can also do:
```json
{
"fundingUrl": {
"Buy Me a Coffee": "https://buymeacoffee.com",
"GitHub Sponsor": "https://github.com/sponsors",
"Patreon": "https://www.patreon.com/"
}
}
```
## API Documentation
See https://docs.obsidian.md

49
esbuild.config.mjs Normal file
View file

@ -0,0 +1,49 @@
import esbuild from "esbuild";
import process from "process";
import { builtinModules } from 'node:module';
const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;
const prod = (process.argv[2] === "production");
const context = await esbuild.context({
banner: {
js: banner,
},
entryPoints: ["src/main.ts"],
bundle: true,
external: [
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtinModules],
format: "cjs",
target: "es2018",
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "main.js",
minify: prod,
});
if (prod) {
await context.rebuild();
process.exit(0);
} else {
await context.watch();
}

34
eslint.config.mts Normal file
View file

@ -0,0 +1,34 @@
import tseslint from 'typescript-eslint';
import obsidianmd from "eslint-plugin-obsidianmd";
import globals from "globals";
import { globalIgnores } from "eslint/config";
export default tseslint.config(
{
languageOptions: {
globals: {
...globals.browser,
},
parserOptions: {
projectService: {
allowDefaultProject: [
'eslint.config.js',
'manifest.json'
]
},
tsconfigRootDir: import.meta.dirname,
extraFileExtensions: ['.json']
},
},
},
...obsidianmd.configs.recommended,
globalIgnores([
"node_modules",
"dist",
"esbuild.config.mjs",
"eslint.config.js",
"version-bump.mjs",
"versions.json",
"main.js",
]),
);

77
main.ts
View file

@ -1,77 +0,0 @@
import { App, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
export default class MyPlugin extends Plugin {
onInit() {
}
onload() {
console.log('loading plugin');
this.addRibbonIcon('dice', 'Sample Plugin', () => {
new Notice('This is a notice!');
});
this.addStatusBarItem().setText('Status Bar Text');
this.addCommand({
id: 'open-sample-modal',
name: 'Open Sample Modal',
// callback: () => {
// console.log('Simple Callback');
// },
checkCallback: (checking: boolean) => {
let leaf = this.app.workspace.activeLeaf;
if (leaf) {
if (!checking) {
new SampleModal(this.app).open();
}
return true;
}
return false;
}
});
this.addSettingTab(new SampleSettingTab(this.app, this));
}
onunload() {
console.log('unloading plugin');
}
}
class SampleModal extends Modal {
constructor(app: App) {
super(app);
}
onOpen() {
let {contentEl} = this;
contentEl.setText('Woah!');
}
onClose() {
let {contentEl} = this;
contentEl.empty();
}
}
class SampleSettingTab extends PluginSettingTab {
display(): void {
let {containerEl} = this;
containerEl.empty();
containerEl.createEl('h2', {text: 'Settings for my awesome plugin.'});
new Setting(containerEl)
.setName('Setting #1')
.setDesc('It\'s a secret')
.addText(text => text.setPlaceholder('Enter your secret')
.setValue('')
.onChange((value) => {
console.log('Secret: ' + value);
}));
}
}

View file

@ -1,8 +1,11 @@
{ {
"id": "obsidian-sample-plugin", "id": "bible-verse-parser",
"name": "Sample Plugin", "name": "Bible Verse Parser",
"version": "1.0.0", "version": "0.1.0",
"description": "This is a sample plugin for Obsidian (https://obsidian.md)", "minAppVersion": "0.15.0",
"author": "Licat", "description": "Pastes the bible verses to the particular regex format.",
"author": "abelmalzew",
"authorUrl": "https://github.com/f0tt3r",
"fundingUrl": "https://github.com/f0tt3r",
"isDesktopOnly": false "isDesktopOnly": false
} }

5162
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,23 +1,29 @@
{ {
"name": "obsidian-sample-plugin", "name": "obsidian-sample-plugin",
"version": "0.9.7", "version": "1.0.0",
"description": "This is a sample plugin for Obsidian (https://obsidian.md)", "description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"main": "main.js", "main": "main.js",
"type": "module",
"scripts": { "scripts": {
"dev": "rollup --config rollup.config.js -w", "dev": "node esbuild.config.mjs",
"build": "rollup --config rollup.config.js" "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json",
"lint": "eslint ."
}, },
"keywords": [], "keywords": [],
"author": "", "license": "0-BSD",
"license": "MIT",
"devDependencies": { "devDependencies": {
"@rollup/plugin-commonjs": "^15.1.0", "@types/node": "^16.11.6",
"@rollup/plugin-node-resolve": "^9.0.0", "esbuild": "0.25.5",
"@rollup/plugin-typescript": "^6.0.0", "eslint-plugin-obsidianmd": "0.1.9",
"@types/node": "^14.14.2", "globals": "14.0.0",
"obsidian": "https://github.com/obsidianmd/obsidian-api/tarball/master", "tslib": "2.4.0",
"rollup": "^2.32.1", "typescript": "^5.8.3",
"tslib": "^2.0.3", "typescript-eslint": "8.35.1",
"typescript": "^4.0.3" "@eslint/js": "9.30.1",
"jiti": "2.6.1"
},
"dependencies": {
"obsidian": "latest"
} }
} }

View file

@ -1,19 +0,0 @@
import typescript from '@rollup/plugin-typescript';
import {nodeResolve} from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
export default {
input: 'main.ts',
output: {
dir: '.',
sourcemap: 'inline',
format: 'cjs',
exports: 'default'
},
external: ['obsidian'],
plugins: [
typescript(),
nodeResolve({browser: true}),
commonjs(),
]
};

99
src/main.ts Normal file
View file

@ -0,0 +1,99 @@
import {App, Editor, MarkdownView, Modal, Notice, Plugin} from 'obsidian';
import {DEFAULT_SETTINGS, MyPluginSettings, SampleSettingTab} from "./settings";
// Remember to rename these classes and interfaces!
export default class MyPlugin extends Plugin {
settings: MyPluginSettings;
async onload() {
await this.loadSettings();
// This creates an icon in the left ribbon.
this.addRibbonIcon('dice', 'Sample', (evt: MouseEvent) => {
// Called when the user clicks the icon.
new Notice('This is a notice!');
});
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
const statusBarItemEl = this.addStatusBarItem();
statusBarItemEl.setText('Status bar text');
// This adds a simple command that can be triggered anywhere
this.addCommand({
id: 'open-modal-simple',
name: 'Open modal (simple)',
callback: () => {
new SampleModal(this.app).open();
}
});
// This adds an editor command that can perform some operation on the current editor instance
this.addCommand({
id: 'replace-selected',
name: 'Replace selected content',
editorCallback: (editor: Editor, view: MarkdownView) => {
editor.replaceSelection('Sample editor command');
}
});
// This adds a complex command that can check whether the current state of the app allows execution of the command
this.addCommand({
id: 'open-modal-complex',
name: 'Open modal (complex)',
checkCallback: (checking: boolean) => {
// Conditions to check
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (markdownView) {
// If checking is true, we're simply "checking" if the command can be run.
// If checking is false, then we want to actually perform the operation.
if (!checking) {
new SampleModal(this.app).open();
}
// This command will only show up in Command Palette when the check function returns true
return true;
}
return false;
}
});
// This adds a settings tab so the user can configure various aspects of the plugin
this.addSettingTab(new SampleSettingTab(this.app, this));
// If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin)
// Using this function will automatically remove the event listener when this plugin is disabled.
this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
new Notice("Click");
});
// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
}
onunload() {
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData() as Partial<MyPluginSettings>);
}
async saveSettings() {
await this.saveData(this.settings);
}
}
class SampleModal extends Modal {
constructor(app: App) {
super(app);
}
onOpen() {
let {contentEl} = this;
contentEl.setText('Woah!');
}
onClose() {
const {contentEl} = this;
contentEl.empty();
}
}

36
src/settings.ts Normal file
View file

@ -0,0 +1,36 @@
import {App, PluginSettingTab, Setting} from "obsidian";
import MyPlugin from "./main";
export interface MyPluginSettings {
mySetting: string;
}
export const DEFAULT_SETTINGS: MyPluginSettings = {
mySetting: 'default'
}
export class SampleSettingTab extends PluginSettingTab {
plugin: MyPlugin;
constructor(app: App, plugin: MyPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const {containerEl} = this;
containerEl.empty();
new Setting(containerEl)
.setName('Settings #1')
.setDesc('It\'s a secret')
.addText(text => text
.setPlaceholder('Enter your secret')
.setValue(this.plugin.settings.mySetting)
.onChange(async (value) => {
this.plugin.settings.mySetting = value;
await this.plugin.saveSettings();
}));
}
}

View file

@ -1,4 +1,8 @@
/* Sets all the text color to red! */ /*
body {
color: red; This CSS file will be included with your plugin, and
} available in the app when your plugin is enabled.
If your plugin does not need CSS, delete this file.
*/

View file

@ -1,22 +1,30 @@
{ {
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": "src",
"inlineSourceMap": true, "inlineSourceMap": true,
"inlineSources": true, "inlineSources": true,
"module": "ESNext", "module": "ESNext",
"target": "es5", "target": "ES6",
"allowJs": true, "allowJs": true,
"noImplicitAny": true, "noImplicitAny": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"moduleResolution": "node", "moduleResolution": "node",
"importHelpers": true, "importHelpers": true,
"noUncheckedIndexedAccess": true,
"isolatedModules": true,
"strictNullChecks": true,
"strictBindCallApply": true,
"allowSyntheticDefaultImports": true,
"useUnknownInCatchVariables": true,
"lib": [ "lib": [
"dom", "DOM",
"es5", "ES5",
"scripthost", "ES6",
"es2015" "ES7"
] ]
}, },
"include": [ "include": [
"**/*.ts" "src/**/*.ts"
] ]
} }

17
version-bump.mjs Normal file
View file

@ -0,0 +1,17 @@
import { readFileSync, writeFileSync } from "fs";
const targetVersion = process.env.npm_package_version;
// read minAppVersion from manifest.json and bump version to target version
const manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
const { minAppVersion } = manifest;
manifest.version = targetVersion;
writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t"));
// update versions.json with target version and minAppVersion from manifest.json
// but only if the target version is not already in versions.json
const versions = JSON.parse(readFileSync('versions.json', 'utf8'));
if (!Object.values(versions).includes(minAppVersion)) {
versions[targetVersion] = minAppVersion;
writeFileSync('versions.json', JSON.stringify(versions, null, '\t'));
}

3
versions.json Normal file
View file

@ -0,0 +1,3 @@
{
"1.0.0": "0.15.0"
}