Skip to content

Instantly share code, notes, and snippets.

@l-mb
Created December 25, 2025 10:48
Show Gist options
  • Select an option

  • Save l-mb/9aac76bb6dd00a09edca3fff13134058 to your computer and use it in GitHub Desktop.

Select an option

Save l-mb/9aac76bb6dd00a09edca3fff13134058 to your computer and use it in GitHub Desktop.
Export the open/checked off checklist item counts to a file property in Obsidian
// This adds a counter of completed vs total checklist items in a note as a file
// property whenever you update the note.
// I use this together with the TaskNote plugin's Kanban view to give me a "Trello"-like
// experience of items that are still open/completed at a quick glance.
// Customize as you see fit - it might differ based on which task statuses you
// use with Obsidian, for example, or which property names you want.
//
// Pre-requisites: the QuickAdd and Linter plugins
//
// Alternative: turning this into a proper plugin and/or a more complex bases query?
//
// 1. Save this to updateChecklist.js somewhere in your Vault.
// 2. Using the QuickAdd community plugin, configure a Choice of type "Macro" that
// executes the above as a User Script
// 3. Toggle the flash symbol in QuickAdd so it gets exposed as an Obsidian command
// 4. For the Linter community plugin, go to "Custom" settings, and add the QuickAdd
// command for this as a Custom Command to be run after linting
// 5. Ensure that you've set "Lint on save" or even "Lint on Focused File Change"
// If using "Lint on save", you'll have to explicitly invoke save (ctrl-s).
// 6. When using Obsidian Sync, ensure that you've enabled "Sync all other types" too!
// Otherwise, the .js file won't be sync'ed, and QuickAdd will fail to load on
// other nodes.
module.exports = async (params) => {
const { app, obsidian } = params;
const activeFile = app.workspace.getActiveFile();
if (!activeFile) {
new obsidian.Notice("No active file");
return;
}
const content = await app.vault.read(activeFile);
const uncheckedRegex = /^[\s]*- \[[ \/\<PD]\]/gm;
const checkedRegex = /^[\s]*- \[[xXM\>]\]/gm;
const uncheckedMatches = content.match(uncheckedRegex) || [];
const checkedMatches = content.match(checkedRegex) || [];
const done = checkedMatches.length;
const total = uncheckedMatches.length + done;
await app.fileManager.processFrontMatter(activeFile, (frontmatter) => {
if (total > 0) {
// If you want to expose them as individual numbers, uncomment this:
// frontmatter.tasks_total = total;
// frontmatter.tasks_done = done;
frontmatter.tasks_progress = total > 0 ? `${done}/${total}` : "0/0";
} else {
// If there are no tasks left, remove the attributes:
delete frontmatter.tasks_total;
delete frontmatter.tasks_done;
delete frontmatter.tasks_progress;
}
});
};
@l-mb
Copy link
Copy Markdown
Author

l-mb commented Dec 25, 2025

2025-12-25T11:50:02+01:00

An example how this view could look like with TaskNotes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment