Files
ink-note-app/electron/tasks.cjs
T
Your NameandClaude Sonnet 5.5 313c41f867 feat(electron): main process, vault, search index, links, import, capture
Vault on plain .md files with FTS5 index, link rewriting, canvas files,
history/trash/backup, link previews, image fetch, tasks and unlinked
mentions, Notion/Obsidian importer, quick-capture window and shortcut.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 11:57:09 +02:00

56 lines
2.2 KiB
JavaScript

'use strict';
// Checklist items (`- [ ] text`) across the notes, and ticking one from outside its page.
// A task is identified by its page and line number in the page's body (what vault.read returns), plus its text,
// so a file that changed since the list was made is never edited blindly.
const TASK = /^(\s*(?:>\s*)*[-*+]\s+)\[([ xX])\](\s+)(.*)$/;
const DUE = /(?:📅|\bdue:|\[due::)\s*(\d{4}-\d{2}-\d{2})/;
const TAG = /(?:^|\s)#([\p{L}\p{N}_/-]*[\p{L}_/-][\p{L}\p{N}_/-]*)/gu;
// Line indexes inside ``` / ~~~ fences are not tasks.
function* lines(body) {
let fence = null;
const all = String(body || '').split('\n');
for (let i = 0; i < all.length; i++) {
const f = all[i].match(/^\s*(```|~~~)/);
if (f) { fence = fence === f[1] ? null : fence || f[1]; continue; }
if (!fence) yield [i, all[i]];
}
}
// [{ id, title, icon, line, done, text, due, tags, depth }] for every task on every page (boards and canvases excluded:
// a board's cards are its own thing).
function findTasks(pages) {
const out = [];
for (const p of pages) {
if (!p.exists || p.kind === 'canvas' || /\.canvas$/i.test(p.id)) continue;
if (p.props && p.props['kanban-plugin']) continue;
for (const [line, text] of lines(p.body)) {
const m = text.match(TASK);
if (!m) continue;
const body = m[4].trim();
if (!body) continue;
const tags = new Set((p.tags || []).map(String));
for (const t of body.matchAll(TAG)) tags.add(t[1]);
const due = body.match(DUE);
out.push({
id: p.id, title: p.title, icon: p.icon || null, line,
done: m[2] !== ' ', text: body, due: due ? due[1] : '', tags: [...tags],
depth: Math.floor((m[1].match(/^\s*/)[0].replace(/\t/g, ' ').length) / 2)
});
}
}
return out;
}
// The body with the task at `line` ticked or unticked; null if the line is no longer that task.
function setTask(body, line, text, done) {
const all = String(body || '').split('\n');
const m = all[line] && all[line].match(TASK);
if (!m || m[4].trim() !== String(text).trim()) return null;
all[line] = m[1] + (done ? '[x]' : '[ ]') + m[3] + m[4];
return all.join('\n');
}
module.exports = { findTasks, setTask };