QuickAdd Templates
Introduction
This is not article as much as it is a live document of snippets I use with the QuickAdd plugin for Obsidian.
I use Quickadd as the easiest, although most confusing, path to the Obsidian API until a better option comes along.
All of the macros (or "choices") can be called externally with something like:
quickAdd.executeChoice("my Macro");
which can also be called by the Advanced URI Plugin, more on this here
Snippets and example
base
Importing the whole quickAdd API
module.exports = async (params) => {
const { app, quickAddApi } = params;
async function someFunction() {
console.log("function here");
}
async function main() {
console.log("This is the main loop ");
}
await main().catch(console.error);
};
Importing specific method from the quickAdd API
module.exports = async (params) => {
const {
app,
quickAddApi: { inputPrompt, executeChoice },
} = params;
async function someFunction() {
console.log("function here");
}
async function main() {
console.log("This is the main loop ");
}
await main().catch(console.error);
};
Difference in usage (between importing a specific method vs the whole API)
const title = await inputPrompt("Enter a title:");
const title = await quickAdd.inputPrompt("Enter a title:");
Importing APIs from other plugins
Dataview
let dv = app.plugins.plugins.dataview.api;
Templater
let tp =
app.plugins.plugins["templater-obsidian"].templater.current_functions_object;
Note that, for this one to work, templater must have been used at least once.
A good trick is to set a startup template in templater that simply states: console.log("Templater initialized")
.
Metadata menu
let mdm = app.plugins.plugins["metadata-menu"];
Clipboard
let clipboardContent = await navigator.clipboard.readText(); // Get content pf clipboard
await navigator.clipboard.writeText("some text"); //set content of clipboard
Specify a folder
const folder = app.vault.getAbstractFileByPath("Notes");
Suggester
From templater
(slithgtly better interface)
const folders = this.app.vault
.getAllLoadedFiles()
.filter((i) => i.children)
.map((folder) => folder.path);
const folderChoicePath = await tp.system.suggester(folders, folders);
From quickAdd
const pickedFile = await params.quickAddApi.suggester(
(file) => file.basename,
params.app.vault.getMarkdownFiles()
);
Generate UUID's
The platform dependant way
This is obviously unnecessary but could be useful as a base on platform where node modules are not available.
async function generateUUID() {
try {
const child_process = require("child_process");
return new Promise((resolve, reject) => {
child_process.exec("uuidgen", (error, stdout, stderr) => {
if (error) {
reject(`error: ${error.message}`);
return;
}
if (stderr) {
reject(`stderr: ${stderr}`);
return;
}
resolve(stdout.trim());
});
});
} catch (error) {
return `Error: ${error.message}`;
}
}
const uuid = await generateUUID();
The simpler, electron way
const uuid = crypto.randomUUID();
Arbitrary user input
const title = await quickAdd.inputPrompt("Enter a title:");
New file
const newFilePath = `${uuid}.md`;
await app.vault.create(newFilePath, "");
const file = app.vault.getAbstractFileByPath(newFilePath);
params.variables["file"] = file; // More on this next
Passing variables between scripts
QuickAdd allows the user to define a macro out of several layers.
To do so, read or write to the params.variables
object.
This allows for reusing scripts accross workflows.
Example: a script creates a new file, another add specific content and frontmatter values to it while the third opens said file in a new pane for instance
Checking if a variable has been defined by the previous script
if ("field1" in params.variables) {
console.log("Field1 exists in the object");
} else {
console.log("Field1 does not exist in the object");
}
Example: check if a TFile
object has been passed by the previous script, if not use the currently active file
Changing frontmatter
await app.fileManager.processFrontMatter(file, (frontmatter) => {
frontmatter.title = title;
frontmatter.ID = uuid;
});
Executing another macros
quickAdd.executeChoice("base");
Note that it doesn't seem to be possible to pass objects between macros this way.
Opening a new window
const newLeaf = app.workspace.getLeaf("split");
await newLeaf.openFile(file);
Adding content to the main file
const templateContent = `
# Template Header
This is a template section.
- Item 1
- Item 2
- Item 3
Current date: {{DATE}}
`;
const targetContent = await app.vault.read(file);
const newContent = targetContent + "\n\n" + templateContent;
await app.vault.modify(target, newContent);
I find that string literals, although not as pretty, always end up being abetter abtraction that the various templating system that exist.
Adding all missing field in frontmatter base on class field using Metadata Menu
let mdm = app.plugins.plugins["metadata-menu"];
mdm.insertMissingFields(file.path, 1, false, false, undefined);
This, to:
- add expected field to a class definition using the
Metadata Menu
plugin - add the classes to the note using QuickAdd
- Have QuickAdd ask MDM to fill the gap
If you're going to define document classes in MDM anyway this allows for single point of configuration.
Note that the first argument could be a TFile
but for some reason, at the time of writing it only seems to work with a filepath.