(formerly known as Fix Require Modules
)
This plugin is meant for the developers and other code writers. If you find the purpose of the plugin is not clear enough, I would be more than welcome to accept the PR to improve the README
(formerly known as Fix Require Modules
)
This plugin is meant for the developers and other code writers. If you find the purpose of the plugin is not clear enough, I would be more than welcome to accept the PR to improve the README
Started porting my Templater scripts and User Plugins js scripts over to be used by your plugin today.
Reasons for doing so:
On a personal note, Mikhael is one of my most liked developers, not only for his intuitive solutions, but his all-around availability.
I recommend checking out his other actively maintained plugins as well.
Cheers
It is possible to avoid duplication between Create
and Insert
using one template with according check
Not sure I follow you. A conditional? How would Templater know what I would want to use the script as? Never heard of this.
This is how I differentiate them:
So I can see the ones with the (not yet learnt shortcut), that they are the Insert ones.
You are right. I confused with another thing I used. Sorry
I was not very keen to use Templater as I didn’t want to take on a liability and Obsidian API is enough.
But I was still trying to find a way to use the Templater functionality.
Once I managed to find a solution with Claude (VSCode Copilot), I had it write up a guide.
@mnaoumov Please comment if you find anything wrong in it:
Here’s a comprehensive list of available Templater functions we can add to the interface:
interface TemplaterPlugin {
templater: {
current_functions_object: {
app: any;
date: {
now: (format: string) => Promise<string>;
tomorrow: (format?: string) => Promise<string>;
yesterday: (format?: string) => Promise<string>;
weekday: (format?: string, weekday?: number) => Promise<string>;
};
file: {
path: (full?: boolean) => string;
folder: (full?: boolean) => string;
title: string;
selection: () => string;
tags: string[];
creation_date: (format?: string) => Promise<string>;
last_modified_date: (format?: string) => Promise<string>;
move: (path: string) => Promise<void>;
rename: (newName: string) => Promise<void>;
find_tfile: (path: string) => TFile | null;
include: (path: string) => Promise<string>;
cursor: (order?: number) => number;
cursor_append: (content: string) => void;
exists: (path: string) => boolean;
create_new: (template: string, filename: string, open?: boolean) => Promise<void>;
};
frontmatter: {
[key: string]: any;
};
system: {
clipboard: () => Promise<string>;
prompt: (prompt_text: string, default_value?: string, throw_on_cancel?: boolean) => Promise<string>;
suggester: <T>(
text_items: ((item: T) => string) | string[],
items: T[],
throw_on_cancel?: boolean,
placeholder?: string,
limit?: boolean
) => Promise<T>;
};
web: {
daily_quote: () => Promise<string>;
random_picture: () => Promise<string>;
request: (url: string) => Promise<any>;
};
obsidian: {
// Access to Obsidian API
[key: string]: any;
};
user: {
// Custom user scripts
[key: string]: any;
};
};
};
}
Key Functions Available:
Date Functions:
now()
- Current date/timetomorrow()
- Tomorrow’s dateyesterday()
- Yesterday’s dateweekday()
- Get specific weekdayFile Operations:
path()
- Get file pathtitle
- Get file titleselection()
- Get selected textmove()
- Move filerename()
- Rename filecreate_new()
- Create new fileSystem Interactions:
clipboard()
- Get clipboard contentprompt()
- Show user promptsuggester()
- Show selection menuWeb Functions:
daily_quote()
- Get daily quoterandom_picture()
- Get random picturerequest()
- Make web requestsMeta Functions:
Example Usage:
// Get user input
const userInput = await tp.system.prompt("Enter value");
// Use suggester for selection
const selected = await tp.system.suggester(
items => items.display,
items
);
// Include another template
const content = await tp.file.include("path/to/template.md");
// Create new file from template
await tp.file.create_new("template.md", "New File", true);
const templaterPlugin = app.plugins.plugins['templater-obsidian'] as any;
const tp = templaterPlugin?.templater?.current_functions_object;
// Check availability
if (!tp?.date?.now) {
console.error("Required Templater functions not found!");
return;
}
// Dates
const getDate = async () => await tp.date.now("YYYY-MM-DD[T]HH:mm");
// Files
const getCurrentFile = () => tp.file.find_tfile(tp.file.path(true));
// System
const userInput = await tp.system.prompt("Enter value");
const selected = await tp.system.suggester(
items => items.display,
items
);
try {
const date = await tp.date.now(format);
} catch (error) {
console.error("Templater error:", error);
// Provide fallback
return moment().format(format);
}
if (!app.plugins.plugins['templater-obsidian']) {
console.error("Templater plugin not found!");
return;
}
return new Promise<void>(async (resolve) => {
try {
// Templater code here
} catch (error) {
console.error('Error:', error);
} finally {
resolve();
}
});
import { App, Plugin, TFile } from 'obsidian';
interface TemplaterPlugin {
templater: {
current_functions_object: {
// Add needed functions
};
};
}
async function yourFunction(app: App): Promise<void> {
return new Promise<void>(async (resolve) => {
try {
const templaterPlugin = app.plugins.plugins['templater-obsidian'] as any;
const tp = templaterPlugin?.templater?.current_functions_object;
if (!tp) {
console.error("Templater not found!");
return resolve();
}
// Your code using tp functions
} catch (error) {
console.error('Error:', error);
} finally {
resolve();
}
});
}
// ...existing Plugin class and invoke function...
Templater functions are async - use await
Functions might be unavailable - always check
Mobile needs Promise wrapper
Use fallbacks for critical functions
@Yurcee there are many places that I would consider wrong here
TemplaterPlugin
are not correct. I will provide the correct typings in a bitconst templaterPlugin = app.plugins.plugins['templater-obsidian'] as TemplaterPlugin;
I would not recommend using TemplaterPlugin
outside of Templater
. For example, it won’t be initialized if you try to use it before any templates were running.
Use Promise wrapper for mobile compatibility
is an anti-pattern, it’s quite bad to use async
inside Promise
constructor
The correct way is
const asyncFn = async () => {
// ...
};
const promise = asyncFn();
return promise;
import * as obsidian from 'obsidian';
import {
App,
TFile,
TFolder,
} from 'obsidian';
enum RunMode {
CreateNewFromTemplate,
AppendActiveFile,
OverwriteFile,
OverwriteActiveFile,
DynamicProcessor,
StartupTemplate,
}
interface TemplaterPlugin {
templater: {
current_functions_object: {
app: App;
config: {
active_file: TFile;
run_mode: RunMode;
target_file: TFile;
template_file: TFile;
};
date: {
// default format is 'YYYY-MM-DD'
now(format?: string, offset?: number | string, reference?: string, reference_format?: string): string;
tomorrow(format?: string): string;
weekday(format?: string, weekday?: number, reference?: string, reference_format?: string): string;
yesterday(format?: string): string;
};
file: {
content: string;
tags: string[];
title: string;
create_new(template: TFile | string, filename?: string, open_new?: boolean, folder?: TFolder | string): Promise<TFile | undefined>;
// default format is 'YYYY-MM-DD HH:mm'
creation_date(format?: string): string;
cursor(order?: number): string;
cursor_append(content: string): string | undefined;
exists(filepath: string): Promise<boolean>;
find_tfile(filename: string): TFile | null;
folder(absolute?: boolean): string;
include(include_link: string | TFile): Promise<string>;
// default format is 'YYYY-MM-DD HH:mm'
last_modified_date(format?: string): string;
move(new_path: string, file_to_move?: TFile): Promise<string>;
path(relative: boolean): string;
rename(new_title: string): Promise<string>;
selection(): string;
};
frontmatter: Record<string, unknown>;
hooks: {
on_all_templates_executed(callback_function: () => unknown): void;
};
obsidian: typeof obsidian;
system: {
clipboard(): Promise<string | null>;
prompt(prompt_text?: string, default_value?: string, throw_on_cancel?: boolean, multiline?: boolean): Promise<string | null>;
suggester<T>(text_items: string[] | ((item: T) => string), items: T[], throw_on_cancel?: boolean, placeholder?: string, limit?: number): Promise<T>;
};
user: Record<string, unknown>;
web: {
daily_quote(): Promise<string>;
random_picture(size?: string, query?: string, include_size?: boolean): Promise<string>;
request(url: string, path?: string): Promise<string>;
};
};
};
}
Yeah, that’s why an empty Templater template is advisable to run for this purpose on startup. I mean even for normal js scripts, for Templater function availability.
Then I’ll need to fix that for mobile compatibility even for scripts I didn’t use Templater for (so far only one and I kept the non-Templater version ts file for backup). That’s important, yes, even for Templater js.
Thanks very much for commenting on this on short notice.
Cheers
As far as I can see, currently, only one startup script is allowed:
Would be better (if it’s possbile) if we just specified the folder and anything in that folder would be loaded on startup.
(My example scripts on the screenshot are not actual startup files – I just added two random files there.)
Yes, you have only one startup script, but you can call from it all others
startup/main.ts
import { invoke as freezeDataView } from './Freeze DataView.ts';
import { invoke as redoLastCommand } from './RedoLastCommand.ts';
export async function invoke(app: App): Promise<void> {
await freezeDataView(app);
await redoLastCommand(app);
}
Will do, thanks!
This way, you will have a better control over the execution order.
And additionally, this forum is not the best place for the BUG/FEATURE REQUESTS. For better maintainability, they are better be added to GitHub
Right.
Was just trying to raise awareness of the plugin a bit more.
Cheers
This plugin needs way more eyes on it, I feel. You can literally make Obsidian do anything you want with it. Plus the developer is so responsive on GitHub; literally every feature I’ve wanted has been added[1].
And very quickly from the times I’ve posted the feature requests, too. ↩︎
I’ve used this plugin to replace QuickAdd, Templater, Dataview, and Advanced URI for myself so far. And also used it to copy some functions from Supercharged Links into it so I could use them to get Supercharged Links styling on links in some plugin views that are unsupported by Supercharged Links. It’s amazing.
Indeed, Dataview basically works on top of the Obsidian metadatacache and not friendly with string (context) searches. I stopped using dv.io.load
a long time ago and use Obsidian API only (which the 2025 bots know about or can scour the docs) and basically made my own search script with any prop filter without Dataview.
It’s just that average users cannot go down this path and I speak from experience that even when you give them a complete tutorial how to do something, they do not even answer. I mean I hardly ever use the built-in search of Obsidian anymore.
Every single time a more complex search is to be entered, you’re gonna check the Help how to do it and you cannot even see what you are adding in that small bar…? Wow, I mean it’s 2025…
So again, thanks to the developer I have 50 custom scripts with modals for my own PKM.
Ah, interesting. The direction I went was moving all inline [key:: value] pairs to frontmatter[1] and editing my scripts that add inline (key:: value) pairs to instead add the properties to frontmatter/YAML[2] and then add a more human readable text “inline”[3].
So, now I can use the regular Obsidian search.
[Spent (dollars)] restaurant
To some extent, I think I can grasp how to replace Templater with this plugin. Is the migration from dataview simple? I use dataview predominantly to manage tasks and render dashboard elements. I’ve been holding out for Datacore, but if this plugin can replace the dataview plugin without a whole lot of work, I’ll just migrate to using only this.
I guess that depends on your proficiency with TypeScript or JavaScript… It took me a couple weeks to replace everything and I definitely didn’t re-make all of Dataview’s things; just my own uses of it/DataviewJS… Had to make various scripts and functions… Here’s my script that renders a table in markdown, for example. CodeScript Toolkit’s code-button
blocks in raw
mode are great.
async function renderTable(container, thisFile, heading, cols, rows) {
const wrapper = document.createElement('div');
wrapper.style.marginBottom = '1.5em';
if (heading.length) await MarkdownRenderer.render(app, heading, wrapper, thisFile.path, null);
const md = [
`| ${cols.join(' | ')} |`,
`| ${cols.map(() => '---').join(' | ')} |`,
...rows.map(r => `| ${r.join(' | ')} |`)
].join('\n');
const tableDiv = document.createElement('div');
await MarkdownRenderer.render(app, md, tableDiv, thisFile.path, null);
wrapper.appendChild(tableDiv);
container?.appendChild(wrapper);
}
exports.renderTable = renderTable;