Hello Everyone!
I recently discovered this plugin while trying to “export” a script I wrote in order to reuse it in different notes using an inline command.
I apologize if this is not the right place to post, but I’m still not used to forums and stuff like this.
Anyway, my usecase is basically just a fancy dataviewjs query to render a bunch of “Event” notes in a nice way, including links and portraits to Characters notes (yes, I’m using it for my dnd campaigns).
I took a look on the demo vault and the documentation but I’m struggling to understand how to use this plugin.
The general structure of an “Event” note is this:
```md
date: 2026
priority: 1.1
storyline: Session 1
characters: [“[[Someone]]”, “[[Someone Else]]”]
Event Title
Event description.
- date obviously indicate the general time the facts happened;
- priority is used to order events happening in the same year;
- storyline defines in which session the event happened;
- characters are links to character notes;
```
The script i have right now is this:
```dataviewjs
const { MarkdownRenderer } = require(“obsidian”);
const pages = dv.pages(`“Events”`) // The “database” folder containing all the events
.where(p => p.date != null)
.sort(p => p.priority, ‘asc’)
.sort(p => p.date, ‘asc’);
const timeline = dv.container.createEl(“div”, { cls: “dnd-timeline” });
for (let p of pages) {
let item = timeline.createEl(“div”, { cls: “timeline-item” });
item.createEl(“div”, { cls: “timeline-year”, text: p.data });
let divider = item.createEl(“div”, { cls: “timeline-divider” });
divider.createEl(“div”, { cls: “timeline-dot” });
let card = item.createEl("div", { cls: "timeline-card" });
// --- ADVANCED PORTRAIT LOGIC ---
let protags = p.protagonists ? (Array.isArray(p.protagonists) ? p.protagonists : \[p.protagonists\]) : \[\];
if (protags.length > 0) {
let avatarsContainer = card.createEl("div", { cls: "timeline-protagonists" });
for (let prot of protags) {
// 1. Access the charatcter note
let p_info = dv.page(prot.path);
let name = prot.path.split('/').pop().replace('.md', '');
// 2. Create link
let avatar = avatarsContainer.createEl("a", { cls: "protag-avatar", title: name });
avatar.href = name;
avatar.addClass("internal-link");
// 3. Check if the “portrait” attribute has an image
if (p_info && p_info.portrait) {
// Use it as BG if there is
let imgUrl = app.vault.getResourcePath(app.metadataCache.getFirstLinkpathDest(p_info.portrait.path || p_info.portrait, ""));
avatar.style.backgroundImage = \`url('${imgUrl}')\`;
avatar.style.backgroundSize = "cover";
avatar.style.backgroundPosition = "center";
avatar.textContent = ""; // Remove fallback initials in case the image is used
} else {
// Fallback: use character initial letters
avatar.textContent = name.substring(0, 2).toUpperCase();
}
}
}
let contentDiv = card.createEl("div", { cls: "timeline-content" });
let rawContent = await dv.io.load(p.file.path);
let cleanContent = rawContent.replace(/^---\[\\s\\S\]\*?---\\n/, '');
await MarkdownRenderer.render(app, cleanContent, contentDiv, p.file.path, dv.component);
}
```
Basically orders the events and for each displays the avatars of the involved characters, which double as character links.
For now, I’m just interested in copy/pasting something similar to this in an external script (a .js file such as “Vault/Resources/Scripts/timeline.js) and be able to do something like this in a note to obtain similar results:
`$= timeline(dv, “source_folder”, “filter_attribute”);`
Please note I don’t have a background in software engineering and the code i reported was obtained with the help of AI. I am however able to get the gist of what is happening, so just a small example on a base setup and a hint on how to scale should be sufficient.
Any help is appreciated, thank you in advance!