With the removal of the template plug in v2, implicitly page templates are also gone. Now my question to you all is how (and if) you used page templates.
Personally I only ever use the Quick Note template, which can be implemented as follows (this one will be included in the standard library):
command.define {
name = "Quick Note",
key = "Alt-Shift-n",
run = function()
local pageName = "Inbox/" .. os.date("%Y-%m-%d/%H-%M-%S")
editor.navigate(pageName)
end
}
You can easily implement any page template in code this way.
For instance you can create a template page My Page Template:
#meta/template
# ${pageName}
This page was created at ${date.today()}
Note: the #meta/template tag prefix is the new recommended tag for templates (so we free up #template) and has the side effect of not rendering (Lua) widgets in the page while editing.
You can the create a command for it as follows:
command.define {
name = "Create Awesome New Page",
run = function()
local pageName = editor.prompt("Awesome page name")
if not pageName then
return
end
local tpl = template.fromPage("My Page Template")
space.writePage(pageName, tpl{pageName=pageName})
editor.navigate(pageName)
end
}
Of course, you can ask any arbitrary questions to fill in the template and pass them as template arguments.
This is super flexible, however itās also quite low level. Perhaps too low level? So maybe thereās nice/cleaner ways to expose this functionality in a simpler nicer way.
There is another approach to page templates and thatās to use the new editor:pageCreating event, which is triggered when the user navigates to a page that does not yet exist:
event.listen {
name = "editor:pageCreating",
run = function(e)
if not e.data.name:startsWith("Journal/") then
return
end
return {
text = "Template text here",
perm = "rw"
}
end
}
When responding to this event, the text attribute will be used as the default page text. This means that if you now navigate to any non-existing page that starts with Journal/, the āTemplate text hereā text will automatically be pre-filled.
Now another fun aspect of this feature is that you can also return ro as the perm, which will turn the page into a read-only page. And guess what, now we have an implementation of Virtual Page Templates
This is how tag pages are implemented in v2: Library/Std/Tag when you navigate to a page starting with tag: the tag template will kick in. Example: tag:meta (this replaces the previous built-in š meta pages from v1).
Not much of a template guy here, but Iāll share my use cases.
Just using 2 (or 1.5 since I use widgets as a heading ). Case 1:
Weekly review - aggregating daily journal snippets (7x daily note transclusion) + a summary header, thatās it. Nothing fancy. Case 2:
I do have an extra header for my daily note (sort of a Manifest) that I donāt want it to be a part of the note itself. Hence I donāt use templates, but rather use a top hook for having this remainder visible, using widget to have ātemplate likeā behavior. A ānon persistentā template, you might think.
My thoughts on the low-level implementation: Now, on the personal level, I donāt mind the command.define & event.listen approach, but I do have a feeling that if I was heavier into templates I would have exactly the same command just with template name changed. That would bring a certain itch to my brain - I should have higher level function to carry on the task. And at this higher abstraction level both event.listen & command.define approaches should meet. (Use the same template with both, as I donāt want to have multiple content sources for the same template.)
Since there is this personal productivity platform repositioning in the works, this ready made, higher level function should be available out of the box (some standard library component). If it was a here for the first time, and I was hit by ātemplates can be considered a listen eventā - I would be something like ānope, Iām not going that route, is a development environment dressed up as a note taking experienceā.
So, not sure if we can keep the low level mechanics, as itās versatile, but have a user friendly, ready made solution for newcomers.
I use page templates quite extensively. I have templates for meeting notes, daily logs, projects, journal entries etc all triggered off buttons etc. They have dynamic titles based on user prompts, dynamic file paths, dynamic frontmatter etc.
I do quite like the current implementation of templates as it allows me to quickly create a template without having to write some LUA for it. But if thatās the way forward, Iām not that bothered by having to write a basic function to make things work.
I can completely see that. My general design approach is: letās figure out the fundamental, lower-level building blocks first. Then build abstractions on top.
With the building blocks of commands and event listeners in place, the following would be trivial to do in Lua, and perhaps this should just be shipped with SilverBullet by default:
Query all pages with the #meta/template/page tag
For each generate a command Template: ${page.name} that prompts the user for a page name, and renders a page based on that template
Extract a few (optional) front matter keys from the template page similar to how page templates worked before, for instance:
command to override the default inferred command name to something custom
key and mac to assign keyboard shortcuts as well
frontmatter to set frontmatter for the page (since hte templateās frontmatter is used to set template attributes)
For each generate a command Template: ${page.name} that prompts the user for a page name, and renders a page based on that template
This potentially floods the command bar with commands that are rarely used. Is there a way to hide commands from the normal command bar (like meta pages are hidden from the regular page picker)? Then we can create a separate command picker for templates.
What we had until now was a Page: From Template command that would follow up with a template picker. We can do that again. Again, as a rare user of page template I just donāt know whatās most convenient for people.
As a user of several journal, meeting, and project full page templates, I vote for the āPage: From Templateā option that would bring up a picker of all pages matching ā#meta/template/*ā.
Leaving for reference that I managed to replace a nested template used in an Inbox/ query, with a self-contained snippet that also creates more compact output:
This kind of documentation is critical, I was scratching my head at why things donāt work⦠a migration guide will probably be useful eventually.
+1 for deduplicating this functionality and consolidating it as a Lua base. Iām comfortable writing my own Lua, but even for people who arenāt, a plug that does ātake all pages in this folder and make commands for themā sounds like a better idea than shoehorning it into the base config.
Iāll dive deeper into my use-cases, as theyāre a bit of fun (and well-served by lua-fication):
Packing list
For flights, etc. - this is just a long checklist cut up into sections. However, maybe a template is the wrong solution for this - it would be nice to just have an āuntick all boxesā button at the bottom (with an āare you sure?ā prompt for bonus points)
Monthly backups
This is broken for some reason, at the moment, but the concept is neat: I have some monthly checks that I run (validate that various backup and update systems are working, look through logs for issues). I have a page, Backups/Monthly checks, which has a heading for each such check, with specific instructions on what to do (that I found automating to be infeasible). I have a reminder to start these once a month, and then I use this template - that generates a page with a checklist pointing at the sections in my checks document, so I can make sure I got through all of them. This is probably straightforward to do in Lua.
---
tags: template
description: Things to do for the regular backup of our systems
hooks.newPage:
suggestedName: "Projects/Backups {{today}}"
confirmName: false
---
---
status: Active
priority: P0
---
{{#let @headers = {
header where
page =~ /Backups\/Monthly checks/ and
level = 1
order by pos
}}}
{{#each @headers}}
- [ ] [[{{page}}#{{name}}|{{name}}]]
{{/each}}
{{/let}}
As my templates are minimal, Iām trying to reuse them as they are, but I did hit some limits of my understanding how to achieve stuff with the low level approach:
adding content to front matter (adding some md content at the beginning of the created page, I guess?) ā I went with injecting the whole initial frontmatter block with editor.insertAtPos()
land |^| after the template is created and page opened (It works)
Any guidance at this stage is welcome. ā Solved my issues low level style.
I know you already solved it with editor.insertAtPos(), another approach for adding frontmatter to a template and the one I used was to create a function and call it on page creation. For example:
Then in my Journal template, I simply add ${templates.journalFrontmatter()} to the very first line of my template like Zef mentioned above.
This allowed me to construct my template by calling various template functions, mixing in markdown as needed and keeping my command for calling the template simple.
I tried that approach initially, but it seemed not working - as it the moment you evaluate a command that provides a frontmatter output - I always got empty output. So I didnāt follow through and went for the low level insertAtPos solution.
After your proposed solve, I took another try, and to my surprise - it works when itās a part of template.fromPage() - I get the frontmatter output as part of the template evaluation (while empty when evaluating the function inline).
Quite a strange behaviour, thatās what you get from blade running v2
Edit not to spam or off-top
I had another strange behaviour - perhaps connected:
function templates.grInsertFrontmatterTags(text)
local frontmatter = [==[
---
tags: ]==]..text..[==[
XXXXXXXXXX
---
]==]
return frontmatter
end
The whitespace line marked with XXXXXXXXX is needed, because without it, it does not work correctly (removes the newline), it can even be a " " (space) after the tags line for it to work. If not, the --- are added within the tags: line, breaking frontmatter.
@zef Iām trying to use your function createPageFromTemplate but i get an error on space.pageExists(pageName). I - in fact - donāt have the function pageExists() defined in space. I only have the fileExists().
Why is that? (I pulled and recreated docker containers with v2 tag so I should be using the newest version)
(btw. maybe this createPagerFromTemplate function should be part of the library? it might be very useful for custom stuff)
I introduced that more or less at the same time. Can you try reloading your client (just refresh) once or twice and see if that fixes it? May be caching.