Skip to content

Instantly share code, notes, and snippets.

@danieltomasz
Last active June 20, 2026 02:14
Show Gist options
  • Select an option

  • Save danieltomasz/31d298aca2969adaf60d8841b68005e2 to your computer and use it in GitHub Desktop.

Select an option

Save danieltomasz/31d298aca2969adaf60d8841b68005e2 to your computer and use it in GitHub Desktop.
Lua filter that allows passing obsidian/github callouts as quarto native in quarto >1.3
-- obsidian-callouts.lua
-- Render Obsidian / GitHub-style callouts as native Quarto callouts.
--
-- > [!note] Optional title
-- > body...
-- > [!warning]- <- trailing - / + sets collapsed / expanded
--
-- Works whether or not there is a blank `>` line between the title line
-- and the body. Requires Quarto >= 1.3 (uses the quarto.Callout API).
-- Put it in your project's _quarto.yml under `filters:`.
local stringify = pandoc.utils.stringify
local type_map = {
note = "note", info = "note", todo = "note", abstract = "note",
summary = "note", tldr = "note", example = "note", quote = "note",
tip = "tip", hint = "tip", success = "tip", check = "tip",
done = "tip", question = "tip", help = "tip", faq = "tip",
warning = "warning", attention = "warning",
caution = "caution", fail = "caution", missing = "caution",
danger = "caution", error = "caution", bug = "caution",
important = "important",
}
function BlockQuote(el)
local first = el.content[1]
if not first or first.t ~= "Para" then return el end
local marker = first.content[1]
if not marker or marker.t ~= "Str" then return el end
-- marker token: [!type] with optional trailing - or + (fold state)
local ctype, fold = marker.text:match("^%[!(%w+)%]([-+]?)$")
if not ctype then return el end
-- inlines of the marker paragraph, minus the "[!type]" token
local inlines = first.content:clone()
table.remove(inlines, 1)
-- With no blank `>` line, Pandoc folds the title line and the body into
-- one paragraph joined by a SoftBreak. Split at the first line break:
-- before it is the title, after it is the first body paragraph.
local title_inlines, rest_inlines = pandoc.List({}), pandoc.List({})
local seen_break = false
for _, inl in ipairs(inlines) do
if not seen_break and (inl.t == "SoftBreak" or inl.t == "LineBreak") then
seen_break = true
elseif seen_break then
rest_inlines:insert(inl)
else
title_inlines:insert(inl)
end
end
local title = stringify(title_inlines):gsub("^%s+", ""):gsub("%s+$", "")
-- rebuild the callout body
if #rest_inlines > 0 then
el.content[1] = pandoc.Para(rest_inlines) -- trailing text on marker line
else
table.remove(el.content, 1) -- marker line had only the title
end
return quarto.Callout({
type = type_map[ctype:lower()] or "note",
title = (title ~= "" and title) or nil,
collapse = (fold == "-" and true) or (fold == "+" and false) or nil,
content = el.content,
})
end
@vanAmsterdam

Copy link
Copy Markdown

got this error lua:24: <eof> expected near 'end' which went away after removing the last two lines with the two extra ends

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment