QuickAdd Templates

Published on:2024/12/06
Banner for 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.