local baseUrl = "https://vikunja.domain.tld"
-- Get the tasks
function getVikunjaTasks()
local token = "YourToken"
local apiUrl = (baseUrl .. "/api/v1/tasks?filter=done=false")
local response = http.request(
apiUrl, {
method = "GET",
headers = {
Authorization = "Bearer " .. token,
Accept = "application/json",
}
})
if response.ok then
return response.body
else
return "Not OK response"
end
end
-- Mark a Vikunja task as done
function completeVikunjaTask(taskId)
local token = "YourToken"
local apiUrl = baseUrl .. "/api/v1/tasks/" .. taskId
local response = http.request(apiUrl, {
method = "POST",
headers = {
Authorization = "Bearer " .. token,
["Content-Type"] = "application/json",
},
body = '{"done": true}'
})
if response.ok then
editor.flashNotification("Task marked as done!")
editor.reloadPage()
else
editor.flashNotification("Failed to complete task", "error")
end
end
-- Module-level click delegation: handles Done buttons on any page
js.window.addEventListener("click", function(e)
local taskId = e.target and e.target.getAttribute and e.target.getAttribute("data-vikunja-id")
if taskId then
completeVikunjaTask(tonumber(taskId))
end
end)
-- Returns an inline HTML button; clicks are caught by the module-level js.window listener
function vikunjaButton(taskId)
return widget.new {
html = string.format('<button data-vikunja-id="%d">Done</button>', taskId)
}
end
-- Template used by getTasks.md — embeds an inline ${vikunjaButton(id)} per row
templates.vikunjaRecurringTasks = function(task)
local due = (task.due_date or ""):sub(1, 10)
return string.format(
"* ${vikunjaButton(%d)} [%s](%s/tasks/%s) **[📅 %s]** %s\n",
task.id, task.identifier, baseUrl, task.id, due, task.title)
end
-- Render all open tasks as an HTML widget with a Done button per row
function vikunjaTasksWithButtons()
local tasks = getVikunjaTasks()
if type(tasks) ~= "table" then return tasks end
table.sort(tasks, function(a, b)
return (a.due_date or "") < (b.due_date or "")
end)
local rows = {}
for _, task in ipairs(tasks) do
local due = (task.due_date or ""):sub(1, 10)
table.insert(rows, string.format(
'<div style="display:flex;gap:6px;align-items:baseline;padding:2px 0">' ..
'<button data-vikunja-id="%d">Done</button>' ..
' <a href="%s/tasks/%d">%s</a> <strong>[ %s]</strong> %s</div>',
task.id, baseUrl, task.id, task.identifier, due, task.title
))
end
return widget.new {
html = '<div>' .. table.concat(rows, "\n") .. '</div>'
}
end
This shows a "Done" button so you don't have to go to Vikunja to mark something as done.