58 lines
1.7 KiB
JavaScript
58 lines
1.7 KiB
JavaScript
/**
|
|
* Session todo list: merge-by-id, grok-shaped statuses. No Bare imports.
|
|
*/
|
|
|
|
const STATUSES = ['pending', 'in_progress', 'completed', 'cancelled'];
|
|
|
|
function normalizeStatus(s) {
|
|
const v = String(s || 'pending')
|
|
.toLowerCase()
|
|
.replace(/[\s-]+/g, '_');
|
|
if (v === 'done' || v === 'complete') return 'completed';
|
|
if (v === 'progress' || v === 'doing' || v === 'inprogress') return 'in_progress';
|
|
if (v === 'cancel' || v === 'canceled') return 'cancelled';
|
|
if (STATUSES.indexOf(v) >= 0) return v;
|
|
return 'pending';
|
|
}
|
|
|
|
function normalizeItem(t, i) {
|
|
t = t || {};
|
|
return {
|
|
id: String(t.id || 'todo_' + (i + 1)),
|
|
content: String(t.content || t.text || t.title || ''),
|
|
status: normalizeStatus(t.status),
|
|
};
|
|
}
|
|
|
|
function merge(existing, incoming, mode) {
|
|
const next = Array.isArray(incoming) ? incoming.map(normalizeItem) : [];
|
|
if (mode === 'replace' || !existing || !existing.length) return next;
|
|
const byId = new Map();
|
|
for (const t of existing) {
|
|
const n = normalizeItem(t, 0);
|
|
byId.set(n.id, n);
|
|
}
|
|
for (const t of next) {
|
|
const prev = byId.get(t.id) || {};
|
|
const merged = Object.assign({}, prev, t);
|
|
if (!t.content && prev.content) merged.content = prev.content;
|
|
byId.set(t.id, merged);
|
|
}
|
|
return Array.from(byId.values());
|
|
}
|
|
|
|
function hasOpen(list) {
|
|
return (list || []).some((t) => t.status === 'pending' || t.status === 'in_progress');
|
|
}
|
|
|
|
function formatBlock(list) {
|
|
const todos = list || [];
|
|
if (!todos.length) return '[todos]\n(none)';
|
|
return (
|
|
'[todos]\n' +
|
|
todos.map((t) => '- [' + t.status + '] ' + t.id + ': ' + t.content).join('\n')
|
|
);
|
|
}
|
|
|
|
module.exports = { STATUSES, normalizeStatus, normalizeItem, merge, hasOpen, formatBlock };
|