### DataviewJS Example with CustomJS State Source: https://context7.com/saml-dev/obsidian-custom-js/llms.txt An example using DataviewJS to interact with custom JavaScript state management. It demonstrates how to import `Counter` and `DataCache` classes using `await cJS()` and utilize their persistent `increment`, `getCount`, `set`, `get`, and `has` methods within a Dataview query. ```dataviewjs `dataviewjs const {Counter, DataCache} = await cJS(); // Counter persists across script reloads dv.paragraph(`Count: ${Counter.increment()}`); // Cache API calls or expensive computations if (!DataCache.has('expensiveResult')) { const result = await performExpensiveOperation(); DataCache.set('expensiveResult', result); } dv.paragraph(`Cached result: ${DataCache.get('expensiveResult')}`); ``` ``` -------------------------------- ### Manually Reloading CustomJS Classes with forceLoadCustomJS Source: https://context7.com/saml-dev/obsidian-custom-js/llms.txt Provides examples of how to use the global `forceLoadCustomJS()` function to manually trigger a reload of all CustomJS classes. This is useful for refreshing scripts without modifying files or when integrating with other plugins that require CustomJS to be reinitialized. ```javascript // Force reload all CustomJS classes await window.forceLoadCustomJS(); // Use case: After programmatically modifying a script file async function updateAndReload() { const scriptPath = 'scripts/myScript.js'; const newContent = ` class MyScript { getValue() { return 'updated'; } } `; await customJS.app.vault.adapter.write(scriptPath, newContent); // Force reload to pick up changes await window.forceLoadCustomJS(); // Now the new version is loaded console.log(customJS.MyScript.getValue()); // 'updated' } // Use case: In a plugin that depends on CustomJS class MyOtherPlugin { async onload() { // Wait for CustomJS to be available if (window.forceLoadCustomJS) { await window.forceLoadCustomJS(); } // Now safe to use customJS if (window.customJS?.MyHelper) { window.customJS.MyHelper.initialize(); } } } ``` -------------------------------- ### Use CustomJS Class in DataviewJS Block Source: https://github.com/saml-dev/obsidian-custom-js/blob/master/README.md Example of how to import and use a custom JavaScript class (CoolString) within an Obsidian dataviewjs block. It uses the `cJS()` function to ensure the class is loaded asynchronously before usage. ```javascript const {CoolString} = await cJS() dv.list(dv.pages().file.name.map(n => CoolString.coolify(n))) ``` -------------------------------- ### Access CustomJS Object and Modules with `cJS()` Source: https://github.com/saml-dev/obsidian-custom-js/blob/master/README.md The `cJS()` function can be used in multiple ways: to get the global `customJS` object, a specific module by name, or to pass the object to a callback function. Always use `await` when calling `cJS()`. ```dataviewjs const modules = await cJS() modules.MyModule.doSomething() ``` ```dataviewjs const MyModule = await cJS('MyModule') MyModule.doSomething() ``` ```dataviewjs await cJS( customJS => customJS.MyModule.doSomething(dv) ) // Or await cJS( ({MyModule}) => MyModule.doSomething(dv) ) ``` ```js-engine async function runAsync(customJS) { await customJS.MyModule.doSomethingAsync(engine) } await cJS(runAsync) // Or, as one-liner: await cJS( async (customJS) => {await customJS.MyModule.doSomethingAsync(engine)} ) ``` -------------------------------- ### Persistent Cache State Management Source: https://context7.com/saml-dev/obsidian-custom-js/llms.txt A JavaScript class for persistent data caching using `customJS.state`. The `DataCache` class allows setting, getting, checking existence, and clearing key-value pairs in a cache object that persists across script reloads. It initializes the cache if it's not already present. ```javascript // File: scripts/cache.js class DataCache { constructor() { // Initialize cache in state if not exists if (!customJS.state.cache) { customJS.state.cache = {}; } } set(key, value) { customJS.state.cache[key] = value; } get(key) { return customJS.state.cache[key]; } has(key) { return key in customJS.state.cache; } clear() { customJS.state.cache = {}; } } ``` -------------------------------- ### Class Initialization and Cleanup with Constructor/Deconstructor Source: https://context7.com/saml-dev/obsidian-custom-js/llms.txt Demonstrates how to use the constructor for initializing class properties and restoring state, and the deconstructor for cleaning up resources like event handlers and saving state. These patterns are crucial for proper resource management and preventing memory leaks when scripts are reloaded. ```javascript // File: scripts/eventManager.js class EventManager { constructor() { // Initialize properties this.handlers = []; this.initialized = false; // Restore state from persistent storage if (customJS.state.eventManagerData) { this.restoreState(customJS.state.eventManagerData); } console.log('EventManager initialized'); } async invoke() { // Register event handlers on startup const handler = this.app.workspace.on('file-open', (file) => { if (file) { console.log(`Opened: ${file.path}`); customJS.state.lastOpenedFile = file.path; } }); this.handlers.push(handler); this.initialized = true; } deconstructor() { // Cleanup: unregister all event handlers console.log('Cleaning up EventManager'); for (const handler of this.handlers) { this.app.workspace.offref(handler); } // Save state before destruction customJS.state.eventManagerData = { lastOpenedFile: customJS.state.lastOpenedFile, initialized: this.initialized }; console.log('EventManager cleanup complete'); } restoreState(data) { this.initialized = data.initialized || false; } } // File: scripts/intervalTask.js class IntervalTask { constructor() { this.intervalId = null; } async invoke() { // Start a recurring task this.intervalId = window.setInterval(() => { console.log('Task running...'); }, 60000); // Every minute } deconstructor() { // Critical: clear interval to prevent memory leaks if (this.intervalId) { window.clearInterval(this.intervalId); this.intervalId = null; } } } ``` -------------------------------- ### CustomJS Plugin Settings Interface Source: https://context7.com/saml-dev/obsidian-custom-js/llms.txt Illustrates the structure of the CustomJS plugin's settings interface in Obsidian, including properties for specifying individual JS files, a JS folder for recursive loading, lists for startup and invocable scripts, and a boolean for rerunning startup scripts on file changes. It also shows how to access these settings programmatically. ```typescript // Plugin Settings Structure (accessed via main.ts) interface CustomJSSettings { jsFiles: string; // "utils.js,helpers.js,formatters.js" jsFolder: string; // "scripts" loads all *.js in scripts/ recursively startupScriptNames: string[]; // ["InitializeCache", "SetupMenus"] registeredInvocableScriptNames: string[]; // ["QuickNote", "ExportData"] rerunStartupScriptsOnFileChange: boolean; // Re-run startup scripts on reload } // File structure example: // vault/ // scripts/ // a-init.js // Loads first (alphabetical by filename) // utilities.js // Loads second // subfolder/ // z-helpers.js // Loads last // utils/ // formatter.js // Settings configuration: // Individual files: "utils/formatter.js" // Folder: "scripts" // Result: loads scripts/a-init.js, scripts/utilities.js, scripts/subfolder/z-helpers.js // Example custom class using settings: class MyConfig { getScriptsFolder() { // Access plugin settings through app return this.app.plugins.plugins.customjs.settings.jsFolder; } isStartupScript(name) { const settings = this.app.plugins.plugins.customjs.settings; return settings.startupScriptNames.includes(name); } } ``` -------------------------------- ### Interact with `window.customJS` Global Object Source: https://context7.com/saml-dev/obsidian-custom-js/llms.txt The `window.customJS` object is the primary interface to all loaded custom classes and plugin utilities. It holds singleton instances, Obsidian API access, and state management. You can access singletons, use the Obsidian API directly, check readiness, or create new instances. ```javascript // Access singleton instance of a custom class customJS.MyHelper.doWork(); // Access Obsidian API through customJS customJS.obsidian.Notice('Hello from CustomJS!'); customJS.app.vault.getFiles(); // Access app instance directly const files = customJS.app.workspace.getActiveFile(); // Check if CustomJS is ready if (customJS?.state?._ready) { // Safe to use customJS properties customJS.MyModule.execute(); } // Create a new instance (not singleton) const newInstance = customJS.createMyHelperInstance(); newInstance.setProperty('value'); // Use state for persistent data across reloads customJS.state.myCounter = (customJS.state.myCounter || 0) + 1; console.log(`Reload count: ${customJS.state.myCounter}`); ``` -------------------------------- ### Create Quick Note Script Source: https://context7.com/saml-dev/obsidian-custom-js/llms.txt An invocable JavaScript script for Obsidian that creates a new markdown file for a quick note with a title and creation timestamp. It uses the CustomJS API to create the file and display notices. Errors during file creation are caught and logged. ```javascript // File: scripts/quickNote.js class QuickNote { async invoke() { try { const date = new Date().toISOString().split('T')[0]; const fileName = `Quick Note ${date}.md`; const content = `# Quick Note\n\nCreated: ${new Date().toLocaleString()}\n\n`; await this.app.vault.create(fileName, content); new customJS.obsidian.Notice(`Created ${fileName}`); } catch (error) { new customJS.obsidian.Notice(`Error: ${error.message}`); console.error(error); } } } ``` -------------------------------- ### Define Custom JavaScript Classes for CustomJS Plugin Source: https://context7.com/saml-dev/obsidian-custom-js/llms.txt CustomJS requires each loaded JavaScript file to contain exactly one class definition. These classes are instantiated as singletons. Optionally, classes can implement an `invoke()` method to be run via the command palette or hotkey, and a `deconstructor()` method for cleanup during reloads. ```javascript // File: scripts/coolString.js class CoolString { constructor() { // Access Obsidian app instance this.app = customJS.app; } coolify(s) { return `😎 ${s} 😎`; } toUpperCool(s) { return this.coolify(s.toUpperCase()); } } // File: scripts/dataProcessor.js class DataProcessor { async invoke() { // This makes it an "invocable script" // Can be run via command palette or hotkey const files = this.app.vault.getMarkdownFiles(); new customJS.obsidian.Notice(`Found ${files.length} files`); } processFiles(files) { return files.filter(f => f.path.includes('journal')); } } ``` ```javascript // Usage in dataviewjs block: ```dataviewjs const {CoolString} = await cJS(); const files = dv.pages().file.name; dv.list(files.map(name => CoolString.coolify(name))); ``` // Usage in templater: <%* const {DataProcessor} = await cJS(); const files = app.vault.getMarkdownFiles(); tR += DataProcessor.processFiles(files).length; %> ``` -------------------------------- ### Check CustomJS Loading State (`_ready`) Source: https://github.com/saml-dev/obsidian-custom-js/blob/master/README.md Determine if CustomJS is fully loaded by checking the `customJS.state._ready` value. If not ready, abort the script. This is useful for preventing errors when modules are not yet initialized. ```dataviewjs if (!customJS?.state?._ready) { // CustomJS is not fully loaded. Abort the script and do not output anything return } // Arriving here means, all customJS properties are ready to be used customJS.MyModule.doSomething() ``` ```js-engine while (!customJS?.state?._ready) { await new Promise(resolve => setTimeout(resolve, 50)) } // Arriving here means, all customJS properties are ready to be used customJS.MyModule.doSomething() ``` -------------------------------- ### Force CustomJS Loading with `cJS()` Source: https://github.com/saml-dev/obsidian-custom-js/blob/master/README.md When `customJS` is not defined, use the global `cJS()` asynchronous function to ensure it's available before proceeding. This is typically needed when CustomJS loads after other plugins that depend on it. ```javascript await cJS() ``` -------------------------------- ### Access CustomJS Global Object and Obsidian API Source: https://github.com/saml-dev/obsidian-custom-js/blob/master/README.md Illustrates how to access the global `window.customJS` object in Obsidian, which provides access to loaded custom class instances, the customJS state object, internal Obsidian API functions, and the main Obsidian `App` instance. ```javascript // Accessing a custom class instance const myModuleInstance = customJS.MyModule; // Accessing the state object const currentState = customJS.state; // Accessing Obsidian API functions const obsidianApi = customJS.obsidian; // Accessing the main App instance const appInstance = customJS.app; ``` -------------------------------- ### Access CustomJS Modules with `cJS()` Loader Function Source: https://context7.com/saml-dev/obsidian-custom-js/llms.txt The `cJS()` function is an asynchronous global loader that ensures CustomJS is ready before accessing custom classes. It supports accessing the entire `customJS` object, a specific module by name, or executing a callback function with the `customJS` object. Recommended for dataviewjs and templater contexts to avoid timing issues. ```javascript // Pattern 1: Access the full customJS object const modules = await cJS(); modules.MyModule.doSomething(); // Pattern 2: Access a specific module by name const MyModule = await cJS('MyModule'); MyModule.coolify('hello'); // Pattern 3: Execute a callback function await cJS(async (customJS) => { const result = await customJS.MyModule.processData(); console.log(result); }); // In a dataviewjs block ``` ```javascript ```dataviewjs const {CoolString} = await cJS(); dv.list(dv.pages().file.name.map(n => CoolString.coolify(n))); ``` ``` ```javascript // In a templater template <%* const {MyUtility} = await cJS(); tR += MyUtility.formatDate(tp.file.creation_date); %> ``` -------------------------------- ### Create Isolated Instance of Custom Class Source: https://github.com/saml-dev/obsidian-custom-js/blob/master/README.md Shows how to create a new, isolated instance of a custom JavaScript class (e.g., CoolString) using the `create` prefix convention provided by CustomJS. This is useful when singleton behavior is not desired. ```javascript const isolatedCoolString = createCoolStringInstance(); console.log(isolatedCoolString.coolify("isolated")); ``` -------------------------------- ### Persistent Counter State Management Source: https://context7.com/saml-dev/obsidian-custom-js/llms.txt A JavaScript class demonstrating persistent state management using `customJS.state`. The `Counter` class increments and retrieves a count that survives script reloads. It initializes the count if it doesn't exist and provides a reset function. ```javascript // File: scripts/counter.js class Counter { increment() { // Initialize if doesn't exist if (!customJS.state.count) { customJS.state.count = 0; } customJS.state.count++; return customJS.state.count; } getCount() { return customJS.state.count || 0; } reset() { customJS.state.count = 0; } } ``` -------------------------------- ### Implement Custom Context Menu in Obsidian with JS Source: https://github.com/saml-dev/obsidian-custom-js/blob/master/README.md This JavaScript code snippet demonstrates how to add a custom menu item to Obsidian's file context menu. It uses the `invoke` method to register an event listener for 'file-menu' and the `deconstructor` method to deregister it, preventing memory leaks. The `eventHandler` function adds a new item with a title, icon, and an `onClick` action. ```javascript class AddCustomMenuEntry { constructor() { // Binding the event handler to the `this` context of the class. this.eventHandler = this.eventHandler.bind(this); } async invoke() { this.app.workspace.on('file-menu', this.eventHandler); } deconstructor() { this.app.workspace.off('file-menu', this.eventHandler); } eventHandler(menu, file) { // Look in the API documentation for this feature // https://docs.obsidian.md/Plugins/User+interface/Context+menus menu.addSeparator(); menu.addItem((item) => { item .setTitle('Custom menu entry text..') .setIcon('file-plus-2') // Look in the API documentation for the available icons .onClick(() => { // https://docs.obsidian.md/Plugins/User+interface/Icons // Insert the code here that is to be executed when the context menu entry is clicked. }); }); } } ``` -------------------------------- ### Define a Custom JavaScript Class for Obsidian Source: https://github.com/saml-dev/obsidian-custom-js/blob/master/README.md This is the required structure for any JavaScript file used with CustomJS. Each file must contain exactly one class definition. Code outside the class, including imports or bare functions, will not work. ```javascript class CoolString { coolify(s) { return `😎 ${s} 😎` } } ``` -------------------------------- ### Add Context Menu Entry Script Source: https://context7.com/saml-dev/obsidian-custom-js/llms.txt An invocable JavaScript script for Obsidian that adds a custom entry to the file explorer's context menu. This script registers an event handler for 'file-menu' and adds an item with a title, icon, and an onClick callback. It includes a deconstructor for cleanup when scripts reload. ```javascript // File: scripts/contextMenu.js class AddCustomMenuEntry { constructor() { this.eventHandler = this.eventHandler.bind(this); } async invoke() { // Register as startup script in CustomJS settings this.app.workspace.on('file-menu', this.eventHandler); } deconstructor() { // Cleanup when scripts reload this.app.workspace.off('file-menu', this.eventHandler); } eventHandler(menu, file) { menu.addSeparator(); menu.addItem((item) => { item .setTitle('Process with CustomJS') .setIcon('file-plus-2') .onClick(async () => { new customJS.obsidian.Notice(`Processing ${file.name}`); // Custom processing logic here }); }); } } ``` -------------------------------- ### Use CustomJS Class in Templater Template Source: https://github.com/saml-dev/obsidian-custom-js/blob/master/README.md Demonstrates how to access and use a custom JavaScript class (CoolString) within a Templater template in Obsidian. It utilizes the `cJS()` function to asynchronously load the class. ```ejs <%- const {CoolString} = await cJS(); tR += CoolString.coolify(tp.file.title); -%> ``` -------------------------------- ### Define an Invocable Script Source: https://github.com/saml-dev/obsidian-custom-js/blob/master/README.md An Invocable Script is a JavaScript class with an `invoke()` method that can be run via the `CustomJS: Invoke Script` command or registered as a custom command with a hotkey. ```javascript async invoke() { ... } ``` -------------------------------- ### Preserve Data Across Reloads with `state` Source: https://github.com/saml-dev/obsidian-custom-js/blob/master/README.md To maintain data when JavaScript files are modified and reloaded, store it in the `window.customJS.state` object. This object persists across reloads, unlike other properties of `window.customJS`. ```javascript // Example usage: // window.customJS.state.myPersistentData = 'some value'; ``` -------------------------------- ### Define `deconstructor` for Cleanup Source: https://github.com/saml-dev/obsidian-custom-js/blob/master/README.md The `deconstructor` method is called on every reload of JavaScript files. Use it to perform cleanup tasks, such as deregistering event listeners, to prevent memory leaks and ensure proper functioning. ```javascript deconstructor() { // Example: Deregister an event listener // this.app.workspace.off('file-menu', this.eventHandler); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.