I have an odd use-case where I’d like my frontmatter to be both useful (ie query-able) and somewhat pretty, so I devised this to do so. if Silverbullet has a better (built-in?) method I"m all ears!
function needsQuotes(str)
str = tostring(str)
-- Check if string needs quotes for YAML
-- Leading/trailing whitespace
if str:match("^%s") or str:match("%s$") then return true end
-- Special YAML chars
if str:match("[:#{}%[%]&*!|>'\"%@`]") then return true end
-- Special starting chars
if str:match("^[%-?:,]") then return true end
-- Numbers
if str:match("^%d+$") or str:match("^%d+%.%d+$") then return true end
-- Booleans/null
if str:lower():match("^(true|false|yes|no|null)$") then return true end
return false
end
function tableToYAML(tbl, indent)
indent = indent or 0
local result = {}
local spaces = string.rep(" ", indent)
for k, v in pairs(tbl) do
if type(v) == "table" then
if #v > 0 and type(v[1]) ~= "table" then
table.insert(result, spaces .. k .. ":")
for _, item in ipairs(v) do
local itemStr = tostring(item)
if needsQuotes(itemStr) then
table.insert(result, spaces .. ' - "' .. itemStr:gsub('"', '\\"') .. '"')
else
table.insert(result, spaces .. " - " .. itemStr)
end
end
elseif #v > 0 then
table.insert(result, spaces .. k .. ":")
for _, item in ipairs(v) do
table.insert(result, spaces .. " -")
table.insert(result, tableToYAML(item, indent + 2))
end
else
table.insert(result, spaces .. k .. ":")
table.insert(result, tableToYAML(v, indent + 1))
end
else
local valStr = tostring(v)
if needsQuotes(valStr) then
table.insert(result, spaces .. k .. ': "' .. valStr:gsub('"', '\\"') .. '"')
else
table.insert(result, spaces .. k .. ": " .. valStr)
end
end
end
return table.concat(result, "\n")
end
function getAttribute(attribute)
results = query[[
from index.tag "page"
where name == (editor.getCurrentPage())
]]
local value = results[1]
for key in attribute:gmatch("[^.]+") do
if value == nil then return "" end
value = value[key]
end
if value == nil then return "" end
if type(value) ~= "table" then return tostring(value) end
return tableToYAML(value)
end
function getAttributeFromPage(attribute, pageName)
results = query[[
from index.tag "page"
where name == pageName
]]
local value = results[1]
for key in attribute:gmatch("[^.]+") do
if value == nil then return "" end
value = value[key]
end
if value == nil then return "" end
if type(value) ~= "table" then return tostring(value) end
return tableToYAML(value)
end
This allows querying frontmatter easily on both the current page and on other pages, using ${getAttribute("attribute", "/Page/Name"} and ${getAttribute("attribute"}.
I’ve also found it useful for “Shared Frontmatter”, in that I have multiple categories of a template that “builds” on the frontmatter from other sources.
Below is an example template:
---
forPrefix: "Person/Personal Contact"
command: "Template: Person: Personal Contact"
suggestedName: "Person/FirstName MI LastName"
confirmName: false
openIfExists: true
tags: meta/template/page
---
---
${getAttributeFromPage("person.default", "Library/Personal/Page Templates/Shared Frontmatter")}
${getAttributeFromPage("person.personal-contact", "Library/Personal/Page Templates/Shared Frontmatter")}
tags: person, person/personal-contact
---
# ${"$"}{getAttribute("pageDecoration.prefix")} ${"$"}{getAttribute("contact.name.first")} ${"$"}{getAttribute("contact.name.middle")} ${"$"}{getAttribute("contact.name.last")}
* ⛓️💥 Relation: ${"$"}{getAttribute("contact.relation")}
* 🏚️ Address: ${"$"}{getAttribute("personal.address")}
* ☎️ Phone: ${"$"}{getAttribute("personal.phone-number")}
* 📧 E-Mail: ${"$"}{getAttribute("personal.email")}
* 🏢 Employment: ${"$"}{getAttribute("personal.employment")}
This pulls in frontmatter for both my “contact” (the default template) and “personal-contact” frontmatter, in /Library/Personal/Page Templates/Shared Frontmatter:
---
person:
default:
pageDecoration:
prefix: "👤 "
disableTOC: true
cssClasses:
- person-decoration
contact:
name:
first: FirstName
middle: MI
last: LastName
relation: How do you know this person?
personal-contact:
personal:
status: true
address: "[[Location/Address]]"
email: [email protected]
employment: "[[Location/Company/Name]]"
phone-number: 1-123-123-1234
---
The resulting page would look like this:
Complete with clickable links to their address and place of employment ![]()
I’m sharing because others may find it useful.
Thanks!
