Hi
Thanks a lot for all the efforts to create this task features. I am having just one issue: How can I actually use it? ![]()
Where do I need to put that script and how is it to be used?
Thanks a lot in advance and kind regards,
P.
Hi
Thanks a lot for all the efforts to create this task features. I am having just one issue: How can I actually use it? ![]()
Where do I need to put that script and how is it to be used?
Thanks a lot in advance and kind regards,
P.
Has anyone tried to do this in v2?
Not exactly this but Iβve been using a very customized version of this for a while. I use some special SB tags for task statuses and priorities - since I use it in contexts other than task as well. Other than that, I have all the projects under Projects/ directory in the space and all people under People/ directory. So that makes my space-lua something like:
taskmgmt = taskmgmt or {}
taskmgmt.valid_priorities = {"P0", "P1", "P2"}
taskmgmt.valid_statuses = {"TODO", "IN-PROGRESS", "PAUSED", "IN-REVIEW", "DONE", "CANCELED"}
function parseTaskAttributes(task, date)
local taskattrs = {}
local sev = ""
local deadline = task.deadline or os.date("%Y-%m-%d")
local diff = dateDiff(date, deadline)
if task.done == true then sev = " β
"
elseif diff < -4 then sev = " π₯"
elseif diff >= -4 and diff < 0 then sev = " βΌοΈ"
elseif diff >= 0 and diff < 2 then sev = " π΄"
elseif diff >= 2 and diff < 4 then sev = " π‘"
else sev = " π’"
end
taskattrs["severity"] = sev
local _, pri = table.find(task.tags, function(tag) return table.includes(taskmgmt.valid_priorities, tag) end)
taskattrs["priority"] = pri and "#" .. pri or "-"
local _, st = table.find(task.tags, function(tag) return table.includes(taskmgmt.valid_statuses, tag) end)
taskattrs["status"] = st and "#" .. st or "-"
local proj = string.match(task.name, "%[%[Projects/.*%]%]")
taskattrs["project"] = proj or ""
local as = ""
for assignee in string.gmatch(task.name, "%[%[People/.-%]%]") do
as = as .. assignee .. " "
end
taskattrs["assignees"] = as
local completed = string.match(task.name, "β
(%d+%-%d+%-%d+)")
taskattrs["completed"] = completed
local created = string.match(task.name, "β(%d+%-%d+%-%d+)")
taskattrs["created"] = created
local name = string.gsub(task.name, "%[%[.*%]%]", "")
name = string.gsub(name, "β
(%d+%-%d+%-%d+)", "")
name = string.gsub(name, "β(%d+%-%d+%-%d+)", "")
taskattrs["taskname"] = name
return taskattrs
end
local mt = {
__index = function(self, attr)
if attr == "extraAttrs" then
return parseTaskAttributes(self, os.date("%Y-%m-%d"))
end
end
}
index.defineTag { name = "task", metatable = mt }
function markTaskStatus(status)
local line = editor.getCurrentLine()
local newline = line.text
for i, v in ipairs(taskmgmt.valid_statuses) do
if string.find(newline, v) then
newline = string.gsub(newline, v, status)
if string.find(newline, "[ ]") and status == "DONE" then
newline = string.gsub(newline, "%[ %]", "[x]")
newline = newline .. " β
" .. os.date("%Y-%m-%d")
end
if string.find(newline, "[x]", 0, true) and status ~= "DONE" then
newline = string.gsub(newline, "%[x%]", "[ ]")
newline = string.gsub(newline, "β
(%d+%-%d+%-%d+)", "")
end
break
end
end
editor.replaceRange(line.from, line.to, newline)
end
function markTaskPriority(priority)
local line = editor.getCurrentLine()
local newline = line.text
for i, v in ipairs(taskmgmt.valid_priorities) do
if string.find(newline, v) then
newline = string.gsub(newline, v, status)
end
end
editor.replaceRange(line.from, line.to, newline)
end
for i, v in ipairs(taskmgmt.valid_statuses) do
slashcommand.define {
name = v,
run = function()
markTaskStatus(v)
end
}
end
for i, v in ipairs(taskmgmt.valid_priorities) do
slashcommand.define {
name = v,
run = function()
markTaskStatus(v)
end
}
end
And with some custom styling for the special tags:
/* TODO tag */
.sb-hashtag[data-tag-name="TODO"] {
background: #ff8080 !important;
color: #333333 !important;
font-weight: bolder;
}
.sb-hashtag[data-tag-name="TODO"]::before {
content: "π ";
}
/* IN-PROGRESS tag */
.sb-hashtag[data-tag-name="IN-PROGRESS"] {
background: #ffcc77 !important;
color: #333333 !important;
font-weight: bolder;
}
.sb-hashtag[data-tag-name="IN-PROGRESS"]::before {
content: "π§ ";
}
/* IN-REVIEW tag */
.sb-hashtag[data-tag-name="IN-REVIEW"] {
background: #bf88bf !important;
color: #333333 !important;
font-weight: bolder;
}
.sb-hashtag[data-tag-name="IN-REVIEW"]::before {
content: "πͺ ";
}
/* PAUSED tag */
.sb-hashtag[data-tag-name="PAUSED"] {
background: #ffa742 !important;
color: #333333 !important;
font-weight: bolder;
}
.sb-hashtag[data-tag-name="PAUSED"]::before {
content: "π§ ";
}
/* DONE tag */
.sb-hashtag[data-tag-name="DONE"] {
background: #b0ffa9 !important;
color: #333333 !important;
font-weight: bolder;
}
.sb-hashtag[data-tag-name="DONE"]::before {
content: "β
";
}
/* CANCELED tag */
.sb-hashtag[data-tag-name="CANCELED"] {
background: #b3d9ff !important;
color: #333333 !important;
font-weight: bolder;
}
.sb-hashtag[data-tag-name="CANCELED"]::before {
content: "β ";
}
/* P0 tag */
.sb-hashtag[data-tag-name="P0"] {
background: #ffb3b3 !important;
color: #333333 !important;
font-weight: bolder;
}
.sb-hashtag[data-tag-name="P0"]::before {
content: "π΄ ";
}
/* P1 tag */
.sb-hashtag[data-tag-name="P1"] {
background: #ffdbb3 !important;
color: #333333 !important;
font-weight: bolder;
}
.sb-hashtag[data-tag-name="P1"]::before {
content: "π ";
}
/* P2 */
.sb-hashtag[data-tag-name="P2"] {
background: #ffe3b3 !important;
color: #333333 !important;
font-weight: bolder;
}
.sb-hashtag[data-tag-name="P2"]::before {
content: "π‘ ";
}
This sort of fits my use cases mostly I can write a task of the form:
* [ ] Test task 1 [[People/Person-1]] [[Projects/Project-blah]] π
2025-11-14 #TODO #P1
and get all the relevant fields extracted out. This is not a 100 percent replacement, but thought Iβd share it if anyone wants to improve on it.
Thanks for sharing! I am mostly wondering about the reoccurring tasks piece. I am looking for a way to do them in v2.
Hi! I was also struggling to migrate without the ability to create recurring tasks
I was tinkering with space-lua for some time and got this implementation:
Itβs fairly simple and (I hope) human-readable
It just listens for state changes of tasks (it seems that event is activated only when a task changes in the current buffer).
If the task is marked as done, it recreates the task with a new deadline.
Inside the recur, you can specify X days, and the new deadline of the task will be X days later.
DAY = 24 * 60 * 60
function time_from_string(date)
local year, month, day = string.match(date, "(%d+)%-(%d+)%-(%d+)")
return os.time({ year = tonumber(year), month = tonumber(month), day = tonumber(day) })
end
function recur_to_new_deadline(deadline, recur)
return os.date(date.date_format, time_from_string(deadline) + (recur * DAY))
end
event.listen({
name = "task:stateChange",
run = function(e)
if e.data.newState == "x" then
local ref = editor.getCurrentPage() .. "@" .. e.data.from - 2
local task = index.getObjectByRef(editor.getCurrentPage(), "task", ref)
if (not (type(task.recur) == "number")) or (not (type(task.deadline)) == "string") then
print("Recur is not a number, or deadline is not a string!")
return
end
local new_deadline = recur_to_new_deadline(task.deadline, task.recur)
local new_task_text = "[ ] "
.. task.name
.. " [deadline:\"" .. new_deadline .. "\"]"
.. " [recur:" .. task.recur .. "]"
editor.replaceRange(e.data.from, e.data.to, new_task_text)
end
end,
})
So this task
I also added a simple slash commands to make it easier to input them - just start typing β/deβ and it suggests deadline, same thing with recur. Also to specify day inside deadline i use /today command, at least as start point.
slashCommand.define {
name = "deadline",
run = function()
editor.insertAtCursor("[deadline: ]")
local currentPosition = editor.getCursor()
editor.moveCursor(currentPosition-1)
end
}
slashCommand.define {
name = "recur",
run = function()
editor.insertAtCursor("[recur: ]")
local currentPosition = editor.getCursor()
editor.moveCursor(currentPosition-1)
end
}
I was also able to make the same concept work with a whole index of tasks, but that implementation was a little dirtier and not so easy to recommend.
If I find a time to make a realization with the index cleaner, I will share it here, probably.
By the way, share your wishes for that thingy; maybe I will be able to make them real ![]()