Obsidian is fun. Amazing to what extend the app can be customized. I hope the following script might be useful to somebody. Credit for the original idea for the multi-select dropdown goes to Christian.
Description:
A multi-select drop down box is prefilled with notes that have a given tag assigned (tag can be set in the script). User selects one or more entries in the drop-down. These items will be inserted as links into the current note. If the current Note contains a header “## Tags” the links will be inserted in the line following this header, otherwise at the current cursor position.
Use case: i.e. to quickly insert a collection of attributes into a daily note.
Screenshots:
The drop-down is automatically populated with notes that have a given tag assigned:
The links have the format: [[Information/9 Meta/Demo/Yoga|Yoga]]
Script code (Templater and DataView plugins are required):
<%*
// Configuration start
const question = "Your tags for today?";
const checkMark = "✅ ";
const searchTerm = "## Tags";
// Configuration end
// Check if there is a line with the searchTerm, if so put the cursor in the following line.
const cm = this.app.workspace.activeLeaf.view.editor;
const count = cm.lineCount();
let line;
for (let i = 0; i < count; i++) {
line = cm.getLine(i + 1);
if (line.includes(searchTerm)) {
cm.setCursor({line: i+2, ch: 0});
break;
}
}
// Collect all notes with the tag "Meta". Using DataViewJs for this.
const dv = this.app.plugins.plugins["dataview"].api;
let notes = dv.pages("#meta1").file.sort(n => n.name);
let suggestions = notes.map(n => n.name);
suggestions = ["Done", ...suggestions];
let values = notes.map(n => "[[" + n.folder + "/" + n.name + "|" + n.name + "]]");
values = ["Done", ...values];
// Create object with suggestions and values arrays
const config = {
"suggestions": suggestions,
"values": values,
"responses": []
};
let response;
while (response !== "Done") {
response = await tp.system.suggester(config.suggestions, config.values, true, question);
if (response !== "Done") {
let rIndex = config.responses.indexOf(response);
if (rIndex > -1) {
config.responses.splice(rIndex, 1);
} else {
config.responses.push(response);
}
let vIndex = config.values.indexOf(response);
let suggestion = config.suggestions[vIndex];
if (suggestion.startsWith(checkMark)) {
config.suggestions[vIndex] = suggestion.replace(checkMark,"");
} else {
config.suggestions[vIndex] = checkMark + suggestion;
}
}
}
// console.log(config);
let result = config.responses.join(", ");
return result
_%>
This is exactly what I was looking for in order to quickly populate meeting notes with correctly named attendees. But when I tried adding it to my meeting template, rather than finding the correct heading, it replaced the entire document with the choices I’ve selected. Any idea why that would happen?
Great script! Thanks!
However, it seems like the pages will only be placed under “## Tags” if the cursor is placed above “## Tags” in the document when the template is run. If I for instance has the cursor further down in the document the pages will be inserted where the cursor was.
Another question: Any idea how this approach would be used to fill out frontmatter? For instance tags where you get a similar looking dropdown list of previously used tags, and meeting attendees where you get a list of previously used attendees? But also with the possibility to add new tags/attendees not previously used?
Thank you so much! This is a game changer for my quick capture workflow.
I’ve modified the script to query for tasks rather than notes. It searches the for them using regex.
<%*
let regex = await tp.system.prompt("Enter a Regex search term for primary filter.", ".*?", true, false)
// Original Source https://forum.obsidian.md/t/multi-select-dropdown-insert-multiple-attributes/32315
// Configuration start
const question = "Your results";
const checkMark = ">>>>>>> ";
const searchTerm = "## Tasks Queried";
// Configuration end
// Check if there is a line with the searchTerm, if so put the cursor in the following line.
const cm = this.app.workspace.activeLeaf.view.editor;
const count = cm.lineCount();
let line;
for (let i = 0; i < count; i++) {
line = cm.getLine(i + 1);
if (line.includes(searchTerm)) {
cm.setCursor({line: i+2, ch: 0});
break;
}
}
// Collect all tasks with the regex primary filter. Using DataViewJs for this.
const dv = this.app.plugins.plugins["dataview"].api;
let notes = dv.pages().file.tasks.where(n => (new RegExp(regex)).test(n.text));
let suggestions = notes.map(n => n.text);
suggestions = ["Done", ...suggestions];
let values = notes.map(n => "[[🍄 " + n.text + "]]");
values = ["Done", ...values];
// Create object with suggestions and values arrays
const config = {
"suggestions": suggestions,
"values": values,
"responses": []
};
let response;
while (response !== "Done") {
response = await tp.system.suggester(config.suggestions, config.values, true, question);
if (response !== "Done") {
let rIndex = config.responses.indexOf(response);
if (rIndex > -1) {
config.responses.splice(rIndex, 1);
} else {
config.responses.push(response);
}
let vIndex = config.values.indexOf(response);
let suggestion = config.suggestions[vIndex];
if (suggestion.startsWith(checkMark)) {
config.suggestions[vIndex] = suggestion.replace(checkMark,"");
} else {
config.suggestions[vIndex] = checkMark + suggestion;
}
}
}
// console.log(config);
let result = config.responses.join("\n");
return result
_%>