commit f0aa854c5adfca27b01da52c8a9c25f3a01a5415 Author: Ellpeck Date: Sat Mar 19 20:21:16 2022 +0100 Initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8952366 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +insert_final_newline = true +indent_style = tab +indent_size = 4 +tab_width = 4 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..32909b2 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +npm node_modules +build \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..0807290 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,23 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "env": { "node": true }, + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "parserOptions": { + "sourceType": "module" + }, + "rules": { + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], + "@typescript-eslint/ban-ts-comment": "off", + "no-prototype-builtins": "off", + "@typescript-eslint/no-empty-function": "off" + } + } \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e09a007 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# vscode +.vscode + +# Intellij +*.iml +.idea + +# npm +node_modules + +# Don't include the compiled main.js file in the repo. +# They should be uploaded to GitHub releases instead. +main.js + +# Exclude sourcemaps +*.map + +# obsidian +data.json + +# Exclude macOS Finder (System Explorer) View States +.DS_Store diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b973752 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +tag-version-prefix="" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..659a631 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +## Obsidian Sample Plugin + +This is a sample plugin for Obsidian (https://obsidian.md). + +This project uses Typescript to provide type checking and documentation. +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. +- Changes the default font color to red using `styles.css`. +- Adds a ribbon icon, which shows a Notice when clicked. +- Adds a command "Open Sample Modal" 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. + +### First time developing plugins? + +Quick starting guide for new plugin devs: + +- 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 https://github.com/obsidianmd/obsidian-releases/blob/master/plugin-review.md +- 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. +- `npm i` or `yarn` to install dependencies +- `npm run dev` to start compilation in watch mode. + +### Manually installing the plugin + +- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`. + +### Improve code quality with eslint (optional) +- [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. +- To use eslint with this project, make sure to 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\` + + +### API Documentation + +See https://github.com/obsidianmd/obsidian-api diff --git a/esbuild.config.mjs b/esbuild.config.mjs new file mode 100644 index 0000000..f1fe201 --- /dev/null +++ b/esbuild.config.mjs @@ -0,0 +1,52 @@ +import esbuild from "esbuild"; +import process from "process"; +import builtins from 'builtin-modules' + +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'); + +esbuild.build({ + banner: { + js: banner, + }, + entryPoints: ['main.ts'], + bundle: true, + external: [ + 'obsidian', + 'electron', + '@codemirror/autocomplete', + '@codemirror/closebrackets', + '@codemirror/collab', + '@codemirror/commands', + '@codemirror/comment', + '@codemirror/fold', + '@codemirror/gutter', + '@codemirror/highlight', + '@codemirror/history', + '@codemirror/language', + '@codemirror/lint', + '@codemirror/matchbrackets', + '@codemirror/panel', + '@codemirror/rangeset', + '@codemirror/rectangular-selection', + '@codemirror/search', + '@codemirror/state', + '@codemirror/stream-parser', + '@codemirror/text', + '@codemirror/tooltip', + '@codemirror/view', + ...builtins], + format: 'cjs', + watch: !prod, + target: 'es2016', + logLevel: "info", + sourcemap: prod ? false : 'inline', + treeShaking: true, + outfile: 'main.js', +}).catch(() => process.exit(1)); diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..9b24b2f --- /dev/null +++ b/main.ts @@ -0,0 +1,137 @@ +import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian'; + +// Remember to rename these classes and interfaces! + +interface MyPluginSettings { + mySetting: string; +} + +const DEFAULT_SETTINGS: MyPluginSettings = { + mySetting: 'default' +} + +export default class MyPlugin extends Plugin { + settings: MyPluginSettings; + + async onload() { + await this.loadSettings(); + + // This creates an icon in the left ribbon. + const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => { + // Called when the user clicks the icon. + new Notice('This is a notice!'); + }); + // Perform additional things with the ribbon + ribbonIconEl.addClass('my-plugin-ribbon-class'); + + // 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-sample-modal-simple', + name: 'Open sample 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: 'sample-editor-command', + name: 'Sample editor command', + editorCallback: (editor: Editor, view: MarkdownView) => { + console.log(editor.getSelection()); + 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-sample-modal-complex', + name: 'Open sample 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; + } + } + }); + + // 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) => { + console.log('click', evt); + }); + + // 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()); + } + + async saveSettings() { + await this.saveData(this.settings); + } +} + +class SampleModal extends Modal { + constructor(app: App) { + super(app); + } + + onOpen() { + const {contentEl} = this; + contentEl.setText('Woah!'); + } + + onClose() { + const {contentEl} = this; + contentEl.empty(); + } +} + +class SampleSettingTab extends PluginSettingTab { + plugin: MyPlugin; + + constructor(app: App, plugin: MyPlugin) { + super(app, plugin); + this.plugin = plugin; + } + + display(): void { + const {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(this.plugin.settings.mySetting) + .onChange(async (value) => { + console.log('Secret: ' + value); + this.plugin.settings.mySetting = value; + await this.plugin.saveSettings(); + })); + } +} diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..30ec656 --- /dev/null +++ b/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "obsidian-sample-plugin", + "name": "Sample Plugin", + "version": "1.0.1", + "minAppVersion": "0.12.0", + "description": "This is a sample plugin for Obsidian. This plugin demonstrates some of the capabilities of the Obsidian API.", + "author": "Obsidian", + "authorUrl": "https://obsidian.md", + "isDesktopOnly": false +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..fd44f15 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "obsidian-sample-plugin", + "version": "1.0.1", + "description": "This is a sample plugin for Obsidian (https://obsidian.md)", + "main": "main.js", + "scripts": { + "dev": "node esbuild.config.mjs", + "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", + "version": "node version-bump.mjs && git add manifest.json versions.json" + }, + "keywords": [], + "author": "", + "license": "MIT", + "devDependencies": { + "@types/node": "^16.11.6", + "@typescript-eslint/eslint-plugin": "^5.2.0", + "@typescript-eslint/parser": "^5.2.0", + "builtin-modules": "^3.2.0", + "esbuild": "0.13.12", + "obsidian": "latest", + "tslib": "2.3.1", + "typescript": "4.4.4" + } +} diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..cfd0fd7 --- /dev/null +++ b/styles.css @@ -0,0 +1,4 @@ +/* Sets all the text color to red! */ +body { + color: red; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b50f455 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "inlineSourceMap": true, + "inlineSources": true, + "module": "ESNext", + "target": "ES6", + "allowJs": true, + "noImplicitAny": true, + "moduleResolution": "node", + "importHelpers": true, + "isolatedModules": true, + "lib": [ + "DOM", + "ES5", + "ES6", + "ES7" + ] + }, + "include": [ + "**/*.ts" + ] +} diff --git a/version-bump.mjs b/version-bump.mjs new file mode 100644 index 0000000..d409fa0 --- /dev/null +++ b/version-bump.mjs @@ -0,0 +1,14 @@ +import { readFileSync, writeFileSync } from "fs"; + +const targetVersion = process.env.npm_package_version; + +// read minAppVersion from manifest.json and bump version to target version +let 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 +let versions = JSON.parse(readFileSync("versions.json", "utf8")); +versions[targetVersion] = minAppVersion; +writeFileSync("versions.json", JSON.stringify(versions, null, "\t")); diff --git a/versions.json b/versions.json new file mode 100644 index 0000000..4bca15f --- /dev/null +++ b/versions.json @@ -0,0 +1,4 @@ +{ + "1.0.0": "0.9.7", + "1.0.1": "0.12.0" +}