<%* /**
- Template for convo notes
- @author Ljavuras [email protected] */ tR += await tp.file.include("[[system.common]]");
customJS.Plugins.templater.addTags(tp, ['note/convo']); -%>
/** Clock/view.css */ | |
/** | |
* Clock widget built upon Dataview plugin | |
* | |
* @author ljavuras <[email protected]> | |
*/ | |
.widget__clock { | |
margin: var(--size-4-2); | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
line-height: 1; | |
} | |
.widget__clock__time { | |
font-size: 48px; | |
} | |
.widget__clock__date .internal-link, | |
.widget__clock__date .internal-link.is-unresolved { | |
color: var(--text-normal); | |
} |
/** Clock/view.js */ | |
/** | |
* Clock widget built upon Dataview plugin | |
* | |
* @author ljavuras <[email protected]> | |
*/ | |
const containerEl = input.containerEl.createDiv({ cls: "widget__clock" }); | |
const timeEl = containerEl.createDiv({ cls: "widget__clock__time" }); | |
function updateTime() { | |
timeEl.innerHTML = moment().format("HH:mm"); | |
setTimeout(() => { | |
updateTime(); | |
}, moment().endOf('minute').diff(moment()) + 1); | |
} | |
updateTime(); | |
const dateEl = containerEl.createDiv({ cls: "widget__clock__date" }); | |
function updateDate() { | |
customJS.Obsidian.renderMarkdown( | |
`[[${moment().format("YYYY-MM-DD"}|${moment().format("MMM Do, dddd")}]]`, | |
dateEl, | |
dv.currentFilePath, | |
dv.component | |
); | |
setTimeout(() => { | |
updateDate(); | |
}, moment().endOf('day').diff(moment()) + 1); | |
} | |
updateDate(); |
/** Convo/view.css */ | |
/** | |
* A social media like widget to display conversations | |
* | |
* @author Ljavuras <[email protected]> | |
*/ | |
.convoWidget { | |
display: flex; | |
flex-direction: column; | |
row-gap: var(--size-4-2); | |
} | |
.newConvo { | |
padding: var(--size-4-2) var(--size-4-1) var(--size-4-1) var(--size-4-3); | |
border: 1px solid var(--background-modifier-border); | |
border-radius: var(--radius-m); | |
cursor: text; | |
} | |
.newConvo:has(:focus) { | |
border-color: var(--background-modifier-border-focus); | |
} | |
.newConvo__input { | |
margin-top: var(--size-4-1); | |
padding: unset; | |
width: 100%; | |
resize: none; | |
overflow: hidden; | |
background-color: var(--background-primary); | |
border: none; | |
} | |
.newConvo__input:focus { | |
box-shadow: none; | |
} | |
.newConvo__submit { | |
display: block; | |
height: unset; | |
line-height: 1; | |
padding: var(--size-4-2) var(--size-4-3); | |
margin: 0 var(--size-4-1) var(--size-4-1) auto; | |
border-radius: var(--radius-s); | |
} | |
.convo { | |
/* padding: var(--size-4-2) var(--size-4-3); */ | |
cursor: default; | |
} | |
.convo.top-level { | |
border: 1px solid var(--background-modifier-border); | |
border-radius: var(--radius-m); | |
} | |
.convo.top-level:has(> .convo__comment-section.hide), | |
.convo__comment-section.last-comment-section { | |
padding-bottom: var(--size-4-2); | |
} | |
.convo__content { | |
padding-top: var(--size-4-2); | |
} | |
.convo__bottom { | |
display: flex; | |
justify-content: flex-end; | |
} | |
.convo__content, .convo__bottom { | |
padding-left: var(--size-4-3); | |
padding-right: var(--size-4-3); | |
} | |
.convo__time { | |
margin-right: auto; | |
display: inline-flex; | |
align-items: center; | |
font-size: var(--font-smaller); | |
color: var(--text-muted); | |
} | |
.convo__show-comment { | |
gap: var(--size-4-1); | |
} | |
.convo__link.internal-link { | |
color: unset; | |
} | |
.convo__link.internal-link:hover { | |
color: unset; | |
} | |
.convo__comment-section { | |
margin-left: var(--size-4-3); | |
border-left: 1px solid var(--background-modifier-border); | |
} | |
.convo__comment-section .newConvo { | |
padding-right: var(--size-4-2); | |
padding-bottom: 0; | |
border: none; | |
} | |
.convo__comment-section .newConvo__submit { | |
margin-right: 0; | |
margin-bottom: 0; | |
} | |
.icon-wrap { | |
display: flex; | |
} | |
.hide { | |
display: none; | |
} |
/** Convo/view.js */ | |
/** | |
* A social media like widget to display conversations | |
* | |
* @author Ljavuras <[email protected]> | |
*/ | |
let obsidian; | |
try { | |
obsidian = require('obsidian'); // enabled by fix require modules plugin | |
} catch { | |
obsidian = input.obsidian // provide obsidian api through `dv.view()` | |
|| customJS.obsidian // get obsidian api from customjs plugin | |
// get obsidian api from templater plugin | |
|| app.plugins.plugins['templater-obsidian'].templater.current_functions_object.obsidian | |
; | |
} | |
if (!obsidian) { /* Handle error */ } | |
let widgetOptions = { | |
locale: input.locale ?? localStorage.getItem('language') ?? 'en', | |
}; | |
const containerEl = (input.containerEl || dv.container).createEl('div', { cls: "convoWidget" }); | |
let newConvoEl = containerEl.createEl('div', { cls:"newConvo" }); | |
newConvoEl.onclick = (event) => { | |
newConvoInputEl.focus(); | |
} | |
let newConvoInputEl = newConvoEl.createEl('textarea', { | |
cls: "newConvo__input", | |
attr: { placeholder: "Write something", rows: 1}, | |
}); | |
newConvoInputEl.oninput = (event) => { | |
newConvoInputEl.style.height = '1px'; | |
newConvoInputEl.style.height = newConvoInputEl.scrollHeight + 'px'; | |
} | |
let newConvoSubmitEl = newConvoEl.createEl('button', { | |
cls: "newConvo__submit", | |
text: "Post" | |
}); | |
newConvoSubmitEl.onclick = async (event) => { | |
let convoFile = await customJS.Plugins.templater.createNewFileFromTemplate( | |
//TODO: handle filename exist error | |
`convo.${moment().format('YYYY-MMDD-HHss')}.md`, | |
"note.convo" | |
); | |
app.vault.append(convoFile, newConvoInputEl.value); | |
}; | |
//TODO: Filter based on date, note count | |
let convos = dv.pages("#note/convo") | |
.filter(page => !page['convo-replies-to']) | |
.filter(page => { | |
return DataviewAPI.luxon.DateTime.now().diff(page.created, 'days').days < 5 | |
}) | |
.sort(page => page.created, 'desc'); | |
for (const convo of convos) { | |
renderConvo(convo, containerEl, topLevel = true); | |
} | |
let shownNewCommentEl; | |
async function renderConvo(convo, containerEl, topLevel = false, | |
convoRenderOptions = { | |
expandAllComment: true, | |
}, | |
lastCommentSectionList = [], | |
isLast = true) { | |
let commentConvos = dv.pages("#note/convo") | |
.filter((commentConvo) => { | |
return convo.file.link.equals(commentConvo['convo-replies-to']); | |
}) | |
.sort(page => page.created, 'asc'); | |
// Better solution, but doesn't work: | |
// dv.pages(`"${convo.file.link.markdown()}"`).filter(/* filter convo-replies-to */); | |
let convoEl = containerEl.createEl('div', { | |
cls: topLevel? "convo top-level" : "convo" | |
}); | |
let convoContentEl = convoEl.createEl('div', { cls: "convo__content" }); | |
customJS.Obsidian.renderMarkdown( | |
await customJS.Obsidian.vault.getFileContent(convo.file.path), | |
convoContentEl, | |
dv.current().file.path, | |
dv.component | |
); | |
let convoBottomEl = convoEl.createEl('div', { cls: "convo__bottom" }); | |
let convoTimeEl = convoBottomEl.createEl('span', { | |
cls: "convo__time", | |
text: convo.created.setLocale(widgetOptions.locale).toRelative() + ', ' + | |
convo.created.setLocale(widgetOptions.locale).toFormat('EEE') | |
}); | |
convoTimeEl.onmouseenter = (event) => { | |
convoTimeEl.textContent = convo.created.setLocale(widgetOptions.locale) | |
.toLocaleString(DataviewAPI.luxon.DateTime.DATETIME_SHORT); | |
}; | |
convoTimeEl.onmouseleave = (event) => { | |
convoTimeEl.textContent = convo.created.setLocale(widgetOptions.locale).toRelative(); | |
} | |
let convoShowNewCommentEl = convoBottomEl.createEl('button', { | |
cls: "clickable-icon", | |
}); | |
obsidian.setIcon(convoShowNewCommentEl, 'reply'); | |
obsidian.setTooltip(convoShowNewCommentEl, "Reply"); | |
convoShowNewCommentEl.onclick = (event) => { | |
convoCommentSectionEl.show(); | |
convoNewCommentEl.show(); | |
} | |
let convoShowCommentEl = convoBottomEl.createEl('button', { | |
cls: "convo__show-comment clickable-icon", | |
text: (commentConvos.length)? commentConvos.length : "", | |
}); | |
obsidian.setIcon( | |
convoShowCommentEl.createEl('div', { cls: "icon-wrap" }), | |
'message-circle' | |
); | |
obsidian.setTooltip(convoShowCommentEl, "Comments"); | |
convoShowCommentEl.onclick = (event) => { | |
if (convoCommentSectionEl.hasClass("hide")) { | |
convoCommentSectionEl.show(); | |
convoNewCommentEl.show(); | |
} else { | |
convoCommentSectionEl.hide(); | |
convoNewCommentEl.hide(); | |
} | |
} | |
let convoCopyLinkEl = convoBottomEl.createEl('button', { | |
cls: "clickable-icon" | |
}); | |
obsidian.setIcon(convoCopyLinkEl, 'copy'); | |
obsidian.setTooltip(convoCopyLinkEl, "Copy link") | |
convoCopyLinkEl.onclick = (event) => { | |
navigator.clipboard.writeText(convo.file.link.markdown()); | |
new Notice("Markdown link copied to clipboard."); | |
} | |
let convoLinkEl = convoBottomEl.createEl('a', { | |
cls: "convo__link internal-link clickable-icon", | |
attr: { | |
'data-tooltip-position': 'top', | |
'aria-label': convo.file.name, | |
'data-href': convo.file.name, | |
href: convo.file.name, | |
target: '_blank', | |
rel: 'noopener', | |
} | |
}) | |
obsidian.setIcon(convoLinkEl, 'link'); | |
let convoCommentSectionEl = convoEl.createEl('div', { | |
cls: "convo__comment-section", | |
}); | |
if (!convoRenderOptions.expandAllComment) { convoCommentSectionEl.addClass("hide"); } | |
if (!commentConvos.length) { convoCommentSectionEl.addClass("hide"); } | |
convoCommentSectionEl.show = () => { | |
convoCommentSectionEl.removeClass("hide"); | |
findLastCommentSection(); | |
}; | |
convoCommentSectionEl.hide = () => { | |
convoCommentSectionEl.addClass("hide"); | |
findLastCommentSection(); | |
}; | |
function findLastCommentSection() { | |
const CLS_LAST_COMMENT_SECTION = "last-comment-section"; | |
let found = false; | |
for (let i = 0; i < lastCommentSectionList.length; i++) { | |
let commentSection = lastCommentSectionList[i]; | |
if (found) { | |
commentSection.removeClass(CLS_LAST_COMMENT_SECTION); | |
} else if (i == lastCommentSectionList.length - 1) { | |
commentSection.hasClass("hide") | |
? commentSection.removeClass(CLS_LAST_COMMENT_SECTION) | |
: commentSection.addClass(CLS_LAST_COMMENT_SECTION); | |
} else { | |
let nextCommentSection = lastCommentSectionList[i + 1]; | |
if (!commentSection.hasClass("hide") && nextCommentSection.hasClass("hide")) { | |
commentSection.addClass(CLS_LAST_COMMENT_SECTION); | |
found = true; | |
} else { | |
commentSection.removeClass(CLS_LAST_COMMENT_SECTION); | |
} | |
} | |
} | |
} | |
let convoCommentsEl = convoCommentSectionEl.createEl('div', { | |
cls: "convo__comments" | |
}); | |
if (isLast) { lastCommentSectionList.push(convoCommentSectionEl); } | |
commentConvos.forEach((commentConvo, i, commentConvos) => { | |
renderConvo( | |
commentConvo, | |
convoCommentsEl, | |
topLevel = false, | |
convoRenderOptions, | |
lastCommentSectionList = lastCommentSectionList, | |
isLast = (i === commentConvos.length - 1), | |
); | |
}); | |
findLastCommentSection(); | |
let convoNewCommentEl = convoCommentSectionEl.createEl('div', { | |
cls: "newConvo hide", | |
}); | |
convoNewCommentEl.show = () => { | |
shownNewCommentEl?.hide(); | |
convoCommentSectionEl.show(); | |
convoNewCommentEl.removeClass("hide"); | |
convoNewCommentInputEl.focus(); | |
shownNewCommentEl = convoNewCommentEl; | |
}; | |
convoNewCommentEl.hide = () => { | |
convoNewCommentInputEl.blur(); | |
convoNewCommentEl.addClass("hide"); | |
if (!commentConvos.length) { | |
convoCommentSectionEl.hide(); | |
} | |
}; | |
convoNewCommentEl.onclick = (event) => { | |
convoNewCommentInputEl.focus(); | |
}; | |
let convoNewCommentInputEl = convoNewCommentEl.createEl('textarea', { | |
cls: "newConvo__input", | |
attr: { placeholder: "Write a comment", rows: 1}, | |
}); | |
convoNewCommentInputEl.oninput = (event) => { | |
convoNewCommentInputEl.style.height = '1px'; | |
convoNewCommentInputEl.style.height = convoNewCommentInputEl.scrollHeight + 'px'; | |
} | |
let convoNewCommentSubmitEl = convoNewCommentEl.createEl('button', { | |
cls: "newConvo__submit", | |
text: "Reply" | |
}); | |
convoNewCommentSubmitEl.onclick = async (event) => { | |
let convoFile = await customJS.Plugins.templater.createNewFileFromTemplate( | |
//TODO: handle filename exist error | |
`convo.${moment().format('YYYY-MMDD-HHss')}.md`, | |
"note.convo" | |
); | |
customJS.Obsidian.frontmatter.set( | |
convoFile, | |
{ 'convo-replies-to': convo.file.link.markdown() } | |
); | |
app.vault.append(convoFile, convoNewCommentInputEl.value); | |
}; | |
} |
/** Home/view.css */ | |
/** | |
* Home dashboard in Obsidian, built upon Dataview plugin | |
* | |
* @author ljavuras <[email protected]> | |
*/ | |
#home__banner { | |
margin-bottom: var(--size-4-8); | |
} | |
#home__tasks { | |
margin: var(--size-4-2) 0; | |
display: flex; | |
flex-direction: row; | |
gap: var(--size-4-2); | |
white-space: nowrap; | |
overflow-x: auto; | |
} | |
#home__tasks > * { | |
margin: unset; | |
flex-grow: 1; | |
} |
/** | |
* Home dashboard in Obsidian, built upon Dataview plugin | |
* | |
* @author ljavuras <[email protected]> | |
*/ | |
const options = { | |
locale: 'en-US', | |
} | |
const containerEl = dv.container; | |
// Unload all child components created by TaskList widgets | |
//TODO: TaskLists should garbage collect themselves | |
for (const childComponent of dv.component._children) { | |
dv.component.removeChild(childComponent); | |
} | |
const bannerEl = containerEl.createDiv({ attr: { id: "home__banner" }}); | |
dv.view("System/scripts/Dataview/widget/Clock", { | |
containerEl: bannerEl, | |
locale: options.locale, | |
}); | |
const tasksEl = containerEl.createDiv({ attr: { id: "home__tasks" }}); | |
dv.view("System/scripts/Dataview/widget/TaskList", { | |
containerEl: tasksEl, | |
tasks: dv.page(moment().format("YYYY-MM-DD")) | |
?.file.tasks | |
.filter((t) => (t.header.subpath == "Daily")), | |
title: "Daily", | |
errorMarkdown: `[[${moment().format("YYYY-MM-DD")}]] is not created yet.`, | |
}); | |
dv.view("System/scripts/Dataview/widget/TaskList", { | |
containerEl: tasksEl, | |
tasks: dv.page(moment().format("YYYY-MM-DD"))?.file.tasks | |
.filter((t) => (t.header.subpath == "Today")), | |
title: "Today", | |
errorMarkdown: `[[${moment().format("YYYY-MM-DD")}]] is not created yet.`, | |
}); | |
const convoEl = containerEl.createDiv(); | |
dv.view("System/scripts/Dataview/widget/Convo", { | |
containerEl: convoEl, | |
locale: options.locale, | |
}); |
<%* /**
customJS.Plugins.templater.addTags(tp, ['note/convo']); -%>
/** CustomJS/Obsidian.js */ | |
class Obsidian { | |
vault = { | |
/** | |
* Get contents of a file | |
* @param {string | TFile} file - Path or object of a file | |
* @param {Boolean} stripYAML - Strip frontmatter if true | |
* @returns {string} Contents of the file | |
*/ | |
async getFileContent(fileId, stripYAML = true) { | |
let tfile; | |
// fileId is a path | |
if (typeof fileId == 'string') { | |
tfile = this.getFile(fileId); | |
// fileId is TFile | |
} else if (fileId instanceof obsidian.TFile) { | |
tfile = fileId; | |
// Handle error | |
} else { | |
customJS.Error.log( | |
"Cannot get file content.\n" + | |
"Invalid parameter passed to Obsidian.getFileContent" | |
) | |
return; | |
} | |
let fileContent = await app.vault.cachedRead(tfile); | |
if (stripYAML) { | |
fileContent = fileContent.replace(/^---.*?\n---\n/s, ""); | |
} | |
return fileContent; | |
}, | |
}; | |
frontmatter = { | |
/** | |
* Set frontmatter of a file | |
* | |
* @param {TFile} file - Target file | |
* @param {Object.<string, string>} properties - Dictionary of properties | |
* | |
* @example | |
* // Add delay or hook to prevent race condition between Templater and | |
* // Obsidian API. | |
* tp.hooks.on_all_templates_executed(async () => { | |
* customJS.Obsidian.frontmatter.set(tp.config.target_file, { | |
* 'property1': 'value1', | |
* 'property2': 'value2', | |
* }); | |
* }); | |
* | |
* @todo handle exceptions | |
*/ | |
set(file, properties) { | |
app.fileManager.processFrontMatter( | |
file, | |
(frontmatter) => { | |
for (const [property, value] of Object.entries(properties)) { | |
frontmatter[property] = value; | |
} | |
} | |
); | |
}, | |
/** | |
* Add tags to frontmatter of a file | |
* @param {TFile} file - Target file | |
* @param {Array.<string>} tags - Tags to add in frontmatter | |
* | |
* @example | |
* // Add delay or hook to prevent race condition between Templater and | |
* // Obsidian API. | |
* tp.hooks.on_all_templates_executed(async () => { | |
* customJS.Obsidian.frontmatter.add( | |
* tp.config.target_file, | |
* ['tag1', 'tag2'] | |
* ); | |
* }); | |
*/ | |
addTags(file, tags) { | |
app.fileManager.processFrontMatter( | |
file, | |
(frontmatter) => { | |
if (!frontmatter.tags) { | |
frontmatter.tags = tags; | |
} else if (Object.prototype.toString.call(frontmatter.tags) === "[object String]") { | |
frontmatter.tags = [frontmatter.tags, ...tags]; | |
} else if (Array.isArray(frontmatter.tags)) { | |
frontmatter.tags = [...frontmatter.tags, ...tags]; | |
} else { | |
//TODO: Handle error | |
} | |
} | |
) | |
}, | |
}; | |
/** | |
* Renders markdown to a container | |
* @param {String} markdown - Markdown content | |
* @param {HTMLElement} containerEl - Container of the rendered markdown | |
* @param {String} sourcePath - Path used to resolve relative internal links | |
* @param {obsidian.Component} component - Parent component to manage the | |
* lifecycle of the rendered child components. | |
* @param {Boolean} inline - Remove margin if rendered inline | |
*/ | |
renderMarkdown(markdown, containerEl, sourcePath, component, inline = true) { | |
if (!containerEl) return; | |
containerEl.innerHTML = ""; | |
obsidian.MarkdownRenderer.renderMarkdown(markdown, containerEl, sourcePath, component) | |
.then(() => { | |
if (!containerEl || !inline) return; | |
// Unwrap any created paragraph elements if we are inline. | |
let paragraph = containerEl.querySelector("p"); | |
while (paragraph) { | |
let children = paragraph.childNodes; | |
paragraph.replaceWith(...Array.from(children)); | |
paragraph = containerEl.querySelector("p"); | |
} | |
}); | |
} | |
} |
/** CustomJS/Plugins.js */ | |
class Plugins { | |
/** | |
* Templater plugin and its API | |
*/ | |
templater = { | |
plugin: app.plugins.plugins["templater-obsidian"], | |
settings: { | |
// Folder where templates are stored | |
folder: app.plugins.plugins["templater-obsidian"] | |
.settings.templates_folder, | |
}, | |
/** | |
* Creates a new file from template | |
* @param {string} filePath - Path of the new file | |
* @param {string} templateName - Name of the template | |
* @returns {TFile} The created new file | |
*/ | |
async createNewFileFromTemplate(filePath, templateName) { | |
// Sanitize template path | |
if (!templateName.endsWith(".md")) { | |
templateName += ".md"; | |
} | |
let templatePath = obsidian.normalizePath( | |
`${this.settings.folder}/${templateName}` | |
); | |
// Create new file with template as its contents | |
const newFile = await app.vault.create( | |
filePath, | |
await customJS.Obsidian.vault.getFileContent(templatePath, false) | |
); | |
// Parse template with templater | |
await this.plugin.templater.overwrite_file_commands(newFile); | |
return newFile; | |
}, | |
/** | |
* Set frontmatter of target file | |
* @param {Object} tp - Templater object, accessible within templates | |
* @param {Object<string, string>} properties - Dictionary of properties | |
*/ | |
setFrontMatter(tp, properties) { | |
// Prevent race condition between Templater and Obsidian API | |
tp.hooks.on_all_templates_executed(() => { | |
customJS.Obsidian.frontmatter.set( | |
tp.config.target_file, | |
properties | |
); | |
}); | |
}, | |
/** | |
* Add tags to target file's frontmatter | |
* @param {Object} tp - Templater object, accessible when replaceing templates | |
* @param {Array.<string>} tags - Tags to add in frontmatter | |
*/ | |
addTags(tp, tags) { | |
// Prevent race condition between Templater and Obsidian API | |
tp.hooks.on_all_templates_executed(() => { | |
customJS.Obsidian.frontmatter.addTags( | |
tp.config.target_file, | |
tags | |
); | |
}); | |
}, | |
}; | |
} |
<%* /**
customJS.Plugins.templater.setFrontMatter(tp, { 'created': tp.file.creation_date("YYYY-MM-DDTHH:mm:ss"), }); -%>
/** TaskList/view.css */ | |
/** | |
* TaskList widget built upon Dataview plugin | |
* | |
* @author ljavuras <[email protected]> | |
*/ | |
.widget__taskList { | |
display: inline-flex; | |
flex-direction: column; | |
padding: var(--size-4-2) var(--size-4-4); | |
margin: var(--size-4-2); | |
border: var(--border-width) solid var(--background-modifier-border); | |
border-radius: var(--radius-m); | |
} | |
.widget__taskList .widget__taskList__title { | |
margin-block: unset; | |
padding-inline: var(--size-4-1); | |
} | |
.widget__taskList .widget__taskList__content { | |
margin-block: unset; | |
} | |
.widget__taskList ul.contains-task-list { | |
margin-block: unset; | |
} | |
.widget__taskList li.dataview.task-list-item { | |
margin: 0; | |
margin-block: unset; | |
margin-inline: unset; | |
} | |
.widget__taskList li.dataview.task-list-item:hover { | |
box-shadow: none; | |
} | |
.widget__taskList li.dataview.task-list-item input { | |
margin-inline-start: unset; | |
} |
/** TaskList/view.js */ | |
/** | |
* TaskLisk widget built upon Dataview plugin | |
* | |
* @author ljavuras <[email protected]> | |
*/ | |
/** | |
* @name input | |
* @type {Object} | |
* @property {HTMLElement} containerEl - Container to render within | |
* @property {Grouping<SListItem>} tasks - Dataview tasks to render | |
* @property {String} title - Tasklist title | |
* @property {String} content - Additional content to display | |
* @property {String} errorMarkdown - Error message in markdown | |
*/ | |
let containerEl = input.containerEl.createEl("fieldset", { cls: "widget__taskList" }); | |
// Render within containerEl, creates local DataviewContext | |
local_dv = app.plugins.plugins['dataview'].localApi( | |
dv.current().file.path, | |
dv.component, | |
containerEl | |
); | |
// Render TaskList | |
if (input.title) { | |
local_dv.el("legend", input.title, { cls: "widget__taskList__title" }); | |
} | |
if (input.content) { | |
local_dv.paragraph(input.content, { cls: "widget__taskList__content" }); | |
} | |
if (input.tasks) { | |
local_dv.taskList(input.tasks, false); | |
} else { | |
let errorEl = containerEl.createDiv({ cls: "widget__taskList__error" }); | |
customJS.Obsidian.renderMarkdown( | |
input.errorMarkdown, | |
errorEl, | |
local_dv.current().file.path, | |
local_dv.component | |
); | |
} |
Copy renderMarkdown()
to both Clock/view.js
and TaskList/view.js
, and replace the function call, if you don't want to mess with CustomJS
Home.md