├── .obsidian └── plugins │ ├── src │ ├── .hotreload │ ├── manifest.json │ └── main.js │ └── hot-reload │ └── manifest.json ├── .eslintignore ├── versions.json ├── manifest.json ├── .eslintrc.json ├── manifest-beta.json ├── .gitignore ├── package.json ├── LICENSE └── README.org /.obsidian/plugins/src/.hotreload: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | npm node_modules 2 | build -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "0.1.5": "0.13.14" 3 | } 4 | -------------------------------------------------------------------------------- /.obsidian/plugins/src/manifest.json: -------------------------------------------------------------------------------- 1 | ../../../manifest.json -------------------------------------------------------------------------------- /.obsidian/plugins/hot-reload/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "hot-reload", 3 | "name": "Hot Reload", 4 | "version": "0.1.8", 5 | "minAppVersion": "0.11.13", 6 | "description": "Automatically reload in-development plugins when their files are changed", 7 | "isDesktopOnly": true 8 | } 9 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "plugin-manager", 3 | "name": "Plugin Manager", 4 | "version": "0.1.5", 5 | "minAppVersion": "0.13.14", 6 | "description": "Extends plugin management of Obsidian.MD", 7 | "author": "ohm-en", 8 | "authorUrl": "https://github.com/ohm-en", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es2021": true 6 | }, 7 | "extends": "google", 8 | "overrides": [ 9 | ], 10 | "parserOptions": { 11 | "ecmaVersion": "latest" 12 | }, 13 | "rules": { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /manifest-beta.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "plugin-manager", 3 | "name": "Plugin Manager", 4 | "version": "0.1.6", 5 | "minAppVersion": "0.13.14", 6 | "description": "Extends plugin management of Obsidian.MD", 7 | "author": "ohm-en", 8 | "authorUrl": "https://github.com/ohm-en", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Intellij 5 | *.iml 6 | .idea 7 | 8 | # npm 9 | node_modules 10 | 11 | # Exclude sourcemaps 12 | *.map 13 | 14 | # obsidian 15 | data.json 16 | 17 | # Exclude macOS Finder (System Explorer) View States 18 | .DS_Store 19 | /package-lock.json 20 | /.obsidian/*.json 21 | /.obsidian/workspace 22 | /.obsidian/plugins/dataview/ 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "plugin-manager", 3 | "version": "0.1.5", 4 | "description": "Extends plugin management of Obsidian.MD", 5 | "main": "main.js", 6 | "scripts": {}, 7 | "keywords": [], 8 | "author": "ohm-en", 9 | "license": "MIT", 10 | "devDependencies": { 11 | "@types/node": "^16.11.6", 12 | "builtin-modules": "^3.2.0", 13 | "eslint": "^8.25.0", 14 | "eslint-config-google": "^0.14.0", 15 | "obsidian": "^0.12.17" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 ohm-en 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.obsidian/plugins/src/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var obsidian = require("obsidian"); 4 | 5 | function constructor(app, manifest) { 6 | const plugin = new obsidian.Plugin(app, manifest); 7 | const pluginArr = {}; 8 | 9 | plugin.onload = async function() { 10 | const pluginStatus = function (pluginId) { 11 | return app.plugins.plugins.hasOwnProperty(pluginId); 12 | }; 13 | const savedData = await plugin.loadData(); 14 | const pluginSettings = (savedData ? savedData : {pluginArr: {}}); 15 | const pluginListen = { 16 | async set(obj, prop, value) { 17 | const nbj = Object.assign({}, obj); 18 | nbj[prop] = value; 19 | const { id } = obj; 20 | if (nbj.enabled) { 21 | // If lazy loading disabled 22 | if (nbj.delay == 0) { 23 | app.plugins.enablePluginAndSave(id); 24 | } 25 | // If lazy loading newly enabled 26 | else if (obj.delay == 0) { 27 | app.plugins.disablePluginAndSave(id); 28 | app.plugins.enablePlugin(id); 29 | } 30 | // If lazy loading already enabled 31 | else { 32 | app.plugins.enablePlugin(id); 33 | } 34 | } else { 35 | app.plugins.disablePluginAndSave(id); 36 | } 37 | Reflect.set(...arguments); 38 | await plugin.saveData(pluginSettings); 39 | return true; 40 | }, 41 | }; 42 | 43 | // NOTE: `pluginArr` is defined in the above block for access within `onunload`. 44 | Object.entries(pluginSettings.pluginArr).forEach(function ([id, pluginObj]) { 45 | pluginArr[id] = new Proxy(pluginObj, pluginListen); 46 | }); 47 | Object.entries(pluginArr).forEach( 48 | function([id, data]) { 49 | if (data.enabled & data.delay > 0) { 50 | if (pluginStatus(id) == true) { 51 | app.plugins.disablePluginAndSave(id) 52 | app.plugins.enablePlugin(id) 53 | } else { 54 | setTimeout( 55 | function() { 56 | app.plugins.enablePlugin(id) 57 | }, data.delay) 58 | } 59 | } 60 | } 61 | ); 62 | const createToggleCommand = function ({ id, name }) { 63 | const obj = { 64 | id: `toggle-${id}`, 65 | name: `toggle ${name}`, 66 | callback: function () { 67 | pluginArr[id].enabled = !pluginArr[id].enabled; 68 | }, 69 | }; 70 | return obj; 71 | }; 72 | Object.values(app.plugins.manifests) 73 | .filter(({ id }) => !pluginArr[id]?.disableToggleCommand) 74 | .map(createToggleCommand) 75 | // `addCommand` needs to be wrapped in a function. I suspect it's accessing local variables? 76 | .map(function (obj) { 77 | plugin.addCommand(obj); 78 | }); 79 | const blacklist = ["plugin-manager"]; 80 | const MySettingTab = new obsidian.PluginSettingTab(app, plugin); 81 | MySettingTab.display = async function () { 82 | const { containerEl: El } = MySettingTab; 83 | El.empty(); 84 | // The Manifests are listed based on their id instead of their shown name, so we need to sort it in alphabetical order by what the user sees: the name. 85 | const sortedPlugins = Object.entries(app.plugins.manifests).sort(function ( 86 | a, 87 | b 88 | ) { 89 | const A = a[1].name.toUpperCase(); 90 | const B = b[1].name.toUpperCase(); 91 | return A < B ? -1 : A > B ? 1 : 0; 92 | }); 93 | sortedPlugins.forEach(function ([id, pluginData], index, arr) { 94 | if (!pluginArr[id]) { 95 | pluginSettings.pluginArr[id] = { id: id, delay: 0, enabled: pluginStatus(id), disableToggleCommand: false }; 96 | pluginArr[id] = new Proxy(pluginSettings.pluginArr[id], pluginListen); 97 | } 98 | const data = pluginArr[id]; 99 | const manifest = app.plugins.manifests[id]; 100 | 101 | const div = El.createDiv() 102 | const st = new obsidian.Setting(div); 103 | st.setHeading(); 104 | st.setName(manifest.name); 105 | st.setDesc(manifest.description); 106 | st.addToggle(function (tg) { 107 | tg.setValue(pluginStatus(id)); 108 | tg.onChange(function (value) { 109 | pluginArr[id].enabled = value; 110 | }); 111 | }); 112 | 113 | 114 | const delayField = new obsidian.Setting(div); 115 | delayField.setName("Startup Delay (in seconds)"); 116 | delayField.addText(function (tx) { 117 | // If plugin id on the blacklist, don't allow EU to change load delay; 118 | if (!blacklist.includes(id)) { 119 | tx.inputEl.type = "number"; 120 | tx.setPlaceholder("0"); 121 | const delayInSeconds = data.delay === 0 ? "" : (data.delay / 1000).toString(); 122 | tx.setValue(delayInSeconds); 123 | tx.onChange(function (delay) { 124 | pluginArr[id].delay = Number(delay * 1000); 125 | }); 126 | } else { 127 | tx.inputEl.type = "text"; 128 | tx.setPlaceholder("Plugin Not Supported"); 129 | tx.setDisabled(true); 130 | } 131 | }); 132 | 133 | const disableToggleCommandField = new obsidian.Setting(div); 134 | disableToggleCommandField.setName("Enable quick toggle command") 135 | disableToggleCommandField.addToggle(function (tg) { 136 | tg.setValue(!pluginArr[id].disableToggleCommand); 137 | tg.onChange(function (value) { 138 | pluginArr[id].disableToggleCommand = !value; 139 | }); 140 | }); 141 | }); 142 | }; 143 | plugin.addSettingTab(MySettingTab); 144 | } 145 | 146 | plugin.onunload = function() { 147 | if (!app.plugins.enabledPlugins.has("obsidian-plugin-manager")) { 148 | Object.entries(pluginArr).forEach(function ([id, data]) { 149 | if (data.enabled & (data.delay > 0)) { 150 | app.plugins.disablePlugin(id); 151 | app.plugins.enablePluginAndSave(id); 152 | } 153 | }); 154 | } 155 | } 156 | 157 | return plugin; } 158 | module.exports = constructor; 159 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | :PROPERTIES: 2 | :ID: 565cc737-ce08-43bb-be8e-58416208951e 3 | :END: 4 | #+title: Obsidian - Plugin Manager 5 | #+auto_tangle: t 6 | 7 | * Table of Contents :toc_2: 8 | - [[#description][Description]] 9 | - [[#features][Features]] 10 | - [[#manual-installation][(Manual) Installation]] 11 | - [[#the-program][The Program]] 12 | - [[#preamble][Preamble]] 13 | - [[#functions][Functions]] 14 | - [[#logic][Logic]] 15 | - [[#obsidian-overhead][Obsidian Overhead]] 16 | - [[#environment--compilation][Environment & Compilation]] 17 | - [[#compile-w-noweb][Compile w/ Noweb]] 18 | - [[#build-dependencies][Build Dependencies]] 19 | - [[#tests][Tests]] 20 | - [[#manifest][Manifest]] 21 | - [[#credits][Credits]] 22 | - [[#license][LICENSE]] 23 | 24 | * Description 25 | A plugin for [[https://obsidian.md/][ObsidianMD]] which provides better management for other plugins. 26 | 27 | ** Features 28 | | Lazy Loading | Create a startup delay for a given plugin in seconds within the Manager's settings page | 29 | | Plugin Toggling | Quickly toggle a given plugin via the command palette and/or configure hotkey | 30 | 31 | ** (Manual) Installation 32 | Until the plugin has been published to the community tab, please use the below method to install: 33 | 34 | 1. Download the latest ~main.js~ & ~manifest.json~ from the latest [[https://github.com/ohm-en/obsidian-plugin-manager/releases][release]]. 35 | 2. Create a folder--with any name--in ~.obsidian/plugins~ at your vaults location. 36 | 3. Move the downloaded files into the created folder. 37 | 4. After reloading the Obsidian plugin list, you will see the plugin. Enable it. 38 | 39 | * The Program 40 | This plugin is written in literate format. If you'd like to read the source-code, you can find it below with relevant descriptions or in the file titled ~main.js~. 41 | 42 | ** Preamble 43 | *** Libraries 44 | The program only depends on the Obsidian API. Though unclear, this includes all functions, methods, etc. under ~obsidian~, ~plugin~, and ~app~. 45 | 46 | #+NAME: dependencies 47 | #+begin_src js 48 | var obsidian = require("obsidian"); 49 | #+end_src 50 | *** Arguments 51 | 52 | Our program (plugin) is called by the Obsidian application based off our exported module (see [[Compile w/ Noweb]]). 53 | 54 | When Obsidian executes the plugin, we receive the ~app~ and ~manifest~ variables which are used to interact with the Obsidian API. Please see the [[https://github.com/obsidianmd/obsidian-api][documentation]] for more information. 55 | 56 | *** Global Store 57 | :PROPERTIES: 58 | :header-args: :noweb-ref store 59 | :END: 60 | 61 | A single global object will be used as our "store" throughout the program. It known as the ~pluginArr~ and is used to defer ideally all mutable data. Additionally, we use a listener on the object to reflect its state onto the environment. 62 | 63 | **** Saved Settings 64 | Our store will furthermore be cached on the system using the Obsidian API (in a file titled ~data.json~). The program has no support for lazy loading without the data from this file. 65 | 66 | #+begin_src js 67 | const savedData = await plugin.loadData(); 68 | const pluginSettings = (savedData ? savedData : {pluginArr: {}}); 69 | #+end_src 70 | 71 | **** The Global Listener 72 | In hopes of containing complexity in the program, this listener attempts to maintain the proper environment state by listening to changes to the global object, ~pluginArr~. It's primary goal is to keep each plugin in the correct state of enablement. 73 | 74 | | Plugin States | Desc. | 75 | |--------------------+--------------------------| 76 | | Enabled & Saved | Enabled on boot | 77 | | Enabled & Unsaved | Enabled until next boot | 78 | | Disabled & Saved | Disabled | 79 | | Disabled & Unsaved | Disabled until next boot | 80 | |--------------------+--------------------------| 81 | 82 | #+begin_src js 83 | const pluginListen = { 84 | async set(obj, prop, value) { 85 | const nbj = Object.assign({}, obj); 86 | nbj[prop] = value; 87 | const { id } = obj; 88 | if (nbj.enabled) { 89 | // If lazy loading disabled 90 | if (nbj.delay == 0) { 91 | app.plugins.enablePluginAndSave(id); 92 | } 93 | // If lazy loading newly enabled 94 | else if (obj.delay == 0) { 95 | app.plugins.disablePluginAndSave(id); 96 | app.plugins.enablePlugin(id); 97 | } 98 | // If lazy loading already enabled 99 | else { 100 | app.plugins.enablePlugin(id); 101 | } 102 | } else { 103 | app.plugins.disablePluginAndSave(id); 104 | } 105 | Reflect.set(...arguments); 106 | await plugin.saveData(pluginSettings); 107 | return true; 108 | }, 109 | }; 110 | 111 | // NOTE: `pluginArr` is defined in the above block for access within `onunload`. 112 | Object.entries(pluginSettings.pluginArr).forEach(function ([id, pluginObj]) { 113 | pluginArr[id] = new Proxy(pluginObj, pluginListen); 114 | }); 115 | #+end_src 116 | 117 | ** Functions 118 | :PROPERTIES: 119 | :header-args: :noweb-ref specific-library 120 | :END: 121 | *** Check If Plugin Is Enabled 122 | Checks the state of a plugin with the Obsidian API. This does not check with the store, but they should always match. 123 | 124 | #+begin_src js 125 | const pluginStatus = function (pluginId) { 126 | return app.plugins.plugins.hasOwnProperty(pluginId); 127 | }; 128 | #+end_src 129 | 130 | ** Logic 131 | :PROPERTIES: 132 | :header-args: :noweb-ref business-logic 133 | :END: 134 | *** Lazy Load Plugins 135 | The original purpose of this plugin was to implement an easier variation of TftHacker's lazy-loading. 136 | 137 | Using a saved listed of plugins and their relevant on load delay, this is trivially achieved with Obsidian's ~app.plugins.enablePlugin~ function which enabled a plugin until the application state of reloaded (reboot). 138 | 139 | Therefore to achieve lazy loading, we need to set a function on start which enables relevant plugins based on their delay. 140 | 141 | However, ~app.plugins.enablePlugin~ only works if ~app.plugin.enablePluginAndSave~ has not been first used. If that's the case, you must first disable it with ~app.plugin.disablePluginAndSave~. In our case, this happens when plugins have a saved delay, but Obsidian Plugin Manager had been disabled, so this must be handled. Though as a consequence, this does create some scenarios where the user may experience interruptions or slow downs from a plethora of plugins getting disabled and enabled again. 142 | 143 | #+begin_src js 144 | Object.entries(pluginArr).forEach( 145 | function([id, data]) { 146 | if (data.enabled & data.delay > 0) { 147 | if (pluginStatus(id) == true) { 148 | app.plugins.disablePluginAndSave(id) 149 | app.plugins.enablePlugin(id) 150 | } else { 151 | setTimeout( 152 | function() { 153 | app.plugins.enablePlugin(id) 154 | }, data.delay) 155 | } 156 | } 157 | } 158 | ); 159 | #+end_src 160 | **** Clean Up 161 | :PROPERTIES: 162 | :header-args: :noweb-ref clean-up 163 | :END: 164 | 165 | In the case where this plugin is removed or disabled I'm sure users would appreciate keeping their previously lazy loaded plugins enabled. Therefore, I using Obsidian's ~onunload~ function I will save all the plugins state properly. 166 | 167 | #+begin_src js 168 | if (!app.plugins.enabledPlugins.has("obsidian-plugin-manager")) { 169 | Object.entries(pluginArr).forEach(function ([id, data]) { 170 | if (data.enabled & (data.delay > 0)) { 171 | app.plugins.disablePlugin(id); 172 | app.plugins.enablePluginAndSave(id); 173 | } 174 | }); 175 | } 176 | #+end_src 177 | 178 | Furthermore, as seen above, we need to check if the plugin has been disabled or only unloaded as running at every unload would cease all lazy loading functionality. Therefore we check ~app.plugins.enabledPlugins~ to see if our plugin is still enabled. 179 | 180 | *** Quick Toggle For Plugins 181 | Takes a list of installed plugins and creates a corresponding array of Obsidian commands which are responsible for toggling the relevant plugin on/off. If desired, the user can add a keybinding using the Obsidian GUI. 182 | 183 | For this to work, we first need a function which toggles the plugin's state on/off while maintaining the proper state (for lazy loading); however, this is already handled by the global listener, so we only need to change the value of ~pluginArr[id].enabled~ to its inverse. 184 | 185 | Furthermore, we need an object which abides by Obsidian's command API. This simply requires a ~id~, ~name~, and ~callback~ (fn) as attributes which the below command handles nicely. 186 | 187 | #+begin_src js 188 | const createToggleCommand = function ({ id, name }) { 189 | const obj = { 190 | id: `toggle-${id}`, 191 | name: `toggle ${name}`, 192 | callback: function () { 193 | pluginArr[id].enabled = !pluginArr[id].enabled; 194 | }, 195 | }; 196 | return obj; 197 | }; 198 | #+end_src 199 | 200 | Using the above function to generate the required JS object, we only need to map over a list of plugins (provided by manifests in the case) to add each command one by one. 201 | 202 | #+begin_src js :noweb yes 203 | Object.values(app.plugins.manifests) 204 | .filter(({ id }) => !pluginArr[id]?.disableToggleCommand) 205 | .map(createToggleCommand) 206 | // `addCommand` needs to be wrapped in a function. I suspect it's accessing local variables? 207 | .map(function (obj) { 208 | plugin.addCommand(obj); 209 | }); 210 | #+end_src 211 | 212 | *** Register Settings Panel 213 | Alike the majority of Obsidian plugins, we too create a settings panel for easy configuration by the user. However, in our case we're making a close replication of the features provided in Obsidian's own 'Community Plugin' tab. Ideally we would replace it, but this has yet to be implemented. 214 | 215 | **** Blacklist 216 | However, we first need to limit plugins which don't support lazy loading. Currently only this plugin is unsupported as it's unable to manage itself. We'll see this data later when generating the settings. 217 | 218 | NOTE: The user's can still edit the values manually to enable lazy loading. This is intentional. 219 | 220 | #+begin_src js 221 | const blacklist = ["plugin-manager"]; 222 | #+end_src 223 | 224 | **** Plugin List 225 | The settings panel is a list of every installed plugin with a few options. The following loops between each plugin and adds it to the settings panel. 226 | 227 | #+begin_src js 228 | const MySettingTab = new obsidian.PluginSettingTab(app, plugin); 229 | MySettingTab.display = async function () { 230 | const { containerEl: El } = MySettingTab; 231 | El.empty(); 232 | // The Manifests are listed based on their id instead of their shown name, so we need to sort it in alphabetical order by what the user sees: the name. 233 | const sortedPlugins = Object.entries(app.plugins.manifests).sort(function ( 234 | a, 235 | b 236 | ) { 237 | const A = a[1].name.toUpperCase(); 238 | const B = b[1].name.toUpperCase(); 239 | return A < B ? -1 : A > B ? 1 : 0; 240 | }); 241 | sortedPlugins.forEach(function ([id, pluginData], index, arr) { 242 | if (!pluginArr[id]) { 243 | pluginSettings.pluginArr[id] = { id: id, delay: 0, enabled: pluginStatus(id), disableToggleCommand: false }; 244 | pluginArr[id] = new Proxy(pluginSettings.pluginArr[id], pluginListen); 245 | } 246 | const data = pluginArr[id]; 247 | const manifest = app.plugins.manifests[id]; 248 | 249 | const div = El.createDiv() 250 | const st = new obsidian.Setting(div); 251 | st.setHeading(); 252 | st.setName(manifest.name); 253 | st.setDesc(manifest.description); 254 | st.addToggle(function (tg) { 255 | tg.setValue(pluginStatus(id)); 256 | tg.onChange(function (value) { 257 | pluginArr[id].enabled = value; 258 | }); 259 | }); 260 | 261 | 262 | const delayField = new obsidian.Setting(div); 263 | delayField.setName("Startup Delay (in seconds)"); 264 | delayField.addText(function (tx) { 265 | // If plugin id on the blacklist, don't allow EU to change load delay; 266 | if (!blacklist.includes(id)) { 267 | tx.inputEl.type = "number"; 268 | tx.setPlaceholder("0"); 269 | const delayInSeconds = data.delay === 0 ? "" : (data.delay / 1000).toString(); 270 | tx.setValue(delayInSeconds); 271 | tx.onChange(function (delay) { 272 | pluginArr[id].delay = Number(delay * 1000); 273 | }); 274 | } else { 275 | tx.inputEl.type = "text"; 276 | tx.setPlaceholder("Plugin Not Supported"); 277 | tx.setDisabled(true); 278 | } 279 | }); 280 | 281 | const disableToggleCommandField = new obsidian.Setting(div); 282 | disableToggleCommandField.setName("Enable quick toggle command") 283 | disableToggleCommandField.addToggle(function (tg) { 284 | tg.setValue(!pluginArr[id].disableToggleCommand); 285 | tg.onChange(function (value) { 286 | pluginArr[id].disableToggleCommand = !value; 287 | }); 288 | }); 289 | }); 290 | }; 291 | #+end_src 292 | 293 | **** Register 294 | Now that we've created the settings object we need to register it with the ~addSettingTab~ API function. 295 | 296 | #+begin_src js 297 | plugin.addSettingTab(MySettingTab); 298 | #+end_src 299 | 300 | ** Obsidian Overhead 301 | To utilize the Obsidian API, we must extend the ~Plugin~ object. This object contains most the methods for interacting with the API. 302 | 303 | To do so, it's normally done with a class using the ~extends~ keyword to the Plugin class (~class MyPlugin extends Plugin~), but instead I've chosen to use a simple function which returns the a plugin object. 304 | 305 | Furthermore, code put in ~plugin.onload~ will be the entry point for our "business logic" and ~plugin.unonload~ will be used for the clean up. 306 | 307 | #+NAME: entry-point 308 | #+begin_src javascript :noweb yes 309 | function constructor(app, manifest) { 310 | const plugin = new obsidian.Plugin(app, manifest); 311 | const pluginArr = {}; 312 | 313 | plugin.onload = async function() { 314 | <> 315 | <> 316 | <> 317 | } 318 | 319 | plugin.onunload = function() { 320 | <> 321 | } 322 | 323 | return plugin; } 324 | #+end_src 325 | 326 | * Environment & Compilation 327 | ** Compile w/ Noweb 328 | This literate document is written in org-mode and use ~org-babel-tangle~ to compile the relevant code blocks into files. The ~<>~ syntax is used to achieve this. 329 | 330 | #+begin_src js :noweb yes :tangle .obsidian/plugins/src/main.js 331 | 'use strict'; 332 | 333 | <> 334 | 335 | <> 336 | module.exports = constructor; 337 | #+end_src 338 | 339 | ** Build Dependencies 340 | Defines a ~package.json~ file used for Node.js; however, this project makes little use of its features. 341 | 342 | #+begin_src json :tangle ./package.json 343 | { 344 | "name": "plugin-manager", 345 | "version": "0.1.3", 346 | "description": "Extends plugin management of Obsidian.MD", 347 | "main": "main.js", 348 | "scripts": {}, 349 | "keywords": [], 350 | "author": "ohm-en", 351 | "license": "MIT", 352 | "devDependencies": { 353 | "@types/node": "^16.11.6", 354 | "builtin-modules": "^3.2.0", 355 | "eslint": "^8.25.0", 356 | "eslint-config-google": "^0.14.0", 357 | "obsidian": "^0.12.17" 358 | } 359 | } 360 | #+end_src 361 | 362 | ** Tests 363 | 364 | #+begin_src javascript 365 | const pluginArr_test = function(pluginArr) { 366 | pluginArr[id].delay = 2000; 367 | new 368 | await plugin.saveData(pluginArr); 369 | const newPluginArr = await plugin.loadData(); 370 | return pluginArr[id].delay == newPluginArr[id].delay ? true : false 371 | } 372 | #+end_src 373 | 374 | ** Manifest 375 | A manifest file containing metadata as required by a ObsidianMD plugin. 376 | 377 | #+begin_src json :tangle ./manifest.json 378 | { 379 | "id": "plugin-manager", 380 | "name": "Plugin Manager", 381 | "version": "0.1.5", 382 | "minAppVersion": "0.13.14", 383 | "description": "Extends plugin management of Obsidian.MD", 384 | "author": "ohm-en", 385 | "authorUrl": "https://github.com/ohm-en", 386 | "isDesktopOnly": false 387 | } 388 | #+end_src 389 | 390 | *** BRAT/Beta 391 | A "beta" manifest file for [[https://github.com/TfTHacker/obsidian42-brat][BRAT]] support. 392 | 393 | #+begin_src json :tangle ./manifest-beta.json 394 | { 395 | "id": "plugin-manager", 396 | "name": "Plugin Manager", 397 | "version": "0.1.6", 398 | "minAppVersion": "0.13.14", 399 | "description": "Extends plugin management of Obsidian.MD", 400 | "author": "ohm-en", 401 | "authorUrl": "https://github.com/ohm-en", 402 | "isDesktopOnly": false 403 | } 404 | #+end_src 405 | 406 | * Credits 407 | A huge thanks to [[https://twitter.com/tfthacker/][@TfTHacker]] for creating the original implementation of lazy loading as found [[https://tfthacker.medium.com/improve-obsidian-startup-time-on-older-devices-with-the-faststart-script-70a6c590309f][here]]. 408 | 409 | * LICENSE 410 | #+begin_src text :tangle ./LICENSE 411 | MIT License 412 | 413 | Copyright (c) 2022 ohm-en 414 | 415 | Permission is hereby granted, free of charge, to any person obtaining a copy 416 | of this software and associated documentation files (the "Software"), to deal 417 | in the Software without restriction, including without limitation the rights 418 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 419 | copies of the Software, and to permit persons to whom the Software is 420 | furnished to do so, subject to the following conditions: 421 | 422 | The above copyright notice and this permission notice shall be included in all 423 | copies or substantial portions of the Software. 424 | 425 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 426 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 427 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 428 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 429 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 430 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 431 | SOFTWARE. 432 | #+end_src 433 | --------------------------------------------------------------------------------