diff --git a/CHANGELOG.md b/CHANGELOG.md index cc2bdf4..2e0fc0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Rebase PR #14 onto main (2026-06-19) + +### Fixed +- Rebased `cursor/add-task-comments-0037` onto `main` (replacing the merge commit) so GitHub can rebase/update the branch cleanly. +- Kept comment textarea support from the PR plus `syncTaskDates`, `normalizeSize`, and legacy size migration from `main`. + ## Rebase PR #13 onto main (2026-06-19) ### Fixed diff --git a/index.html b/index.html index ccead89..d624f70 100644 --- a/index.html +++ b/index.html @@ -189,6 +189,11 @@ .tbox .frow select,.tbox .frow input{flex:1;font:inherit;font-size:15px;border:none;background:transparent; color:var(--ink);min-height:34px;-webkit-appearance:none;appearance:none} .tbox .frow select:focus,.tbox .frow input:focus{outline:none} + .frow-stack{align-items:flex-start} + .frow-stack .lbl{padding-top:8px} + .tbox .dcomment{flex:1;min-width:0;min-height:72px;resize:vertical;font:inherit;font-size:14px;line-height:1.45; + border:1px solid var(--line);border-radius:10px;padding:10px 12px;background:#fafbfc;color:var(--ink)} + .tbox .dcomment:focus{outline:2px solid var(--accent);border-color:transparent;background:#fff} /* make the borderless selects (Owner / Size / Move to) read as editable dropdowns: size to their value and show a caret + hover so it's clear you can change them */ .tbox .frow select{flex:0 1 auto;max-width:100%;cursor:pointer;min-height:32px; diff --git a/src/app/main.js b/src/app/main.js index a56fe06..ec983b3 100644 --- a/src/app/main.js +++ b/src/app/main.js @@ -34,6 +34,7 @@ const depthOf = (id) => depthOfIn(id, DATA); const fitsDepth = (node, destId) => fitsDepthIn(node, destId, DATA); /* ================= helpers ================= */ +const escText = (s) => String(s ?? "").replace(/&/g, "&").replace(//g, ">"); /* size-weighted progress (0..1): done size-points / total size-points across the leaves */ const progFrac = (n) => { let done = 0, tot = 0; flat([n], (x) => { if (x.children.length) return; const w = sizePts(x.size); tot += w; if (x.done) done += w; }); return tot ? done / tot : 0; }; function dueChip(due,done){ if(!due||done) return ""; @@ -896,7 +897,8 @@ function updTask(id,f,v,quiet){ snap(); const n=findPath(id).pop(); else if(f==="due"){ n.due=v||null; syncTaskDates(n,"due"); } else if(f==="start"){ n.start=v||null; syncTaskDates(n,"start"); } else if(f==="size") n.size=v?normalizeSize(v):null; - renderAll(); if(!quiet&&f!=="title") openDetail(id); } + else if(f==="comment") n.comment=v.trim()||null; + renderAll(); if(!quiet&&f!=="title"&&f!=="comment") openDetail(id); } function deleteTask(id){ const n=findPath(id).pop(); if(typeof confirm!=="undefined"&&!confirm('Delete "'+n.title+'"'+(n.children.length?" and its subtasks":"")+"?")) return; snap(); detach(id); closeSheet(); renderAll(); } @@ -971,12 +973,16 @@ function openDetail(id,opts){ ? `
Size
` : `
Size${_szPts} pts
`; + const kind=path.length===1?"project":path.length===2?"task":"subtask"; document.getElementById("dBody").innerHTML=`
Owner${av(n.owner)}
Start
End${dueChip(n.due,leaf&&n.done)}
${sizeFld} +
Comment +
${leaf?`
Status
`:""} diff --git a/src/lib/tree.js b/src/lib/tree.js index 2884123..5fc522d 100644 --- a/src/lib/tree.js +++ b/src/lib/tree.js @@ -10,6 +10,7 @@ export function createTaskFactory() { due: opts.d || null, start: opts.st || null, size: opts.s || null, + comment: opts.comment || null, done: opts.done || false, doneAt: opts.doneAt || null, children: opts.c || [], @@ -45,6 +46,7 @@ export function normalizeTaskTree(nodes) { nodes.forEach((n) => { if (!Array.isArray(n.children)) n.children = []; if (n.size != null) n.size = normalizeSize(n.size); + if (typeof n.comment === "string") n.comment = n.comment.trim() || null; normalizeTaskTree(n.children); }); } diff --git a/tests/tree.test.js b/tests/tree.test.js index 26ee707..853b16b 100644 --- a/tests/tree.test.js +++ b/tests/tree.test.js @@ -63,4 +63,15 @@ describe("tree", () => { normalizeTaskTree([root]); expect(root.size).toBe("s"); }); + + it("normalizes blank comments to null", () => { + const root = { id: 1, title: "Root", owner: "fd", comment: " ", children: [] }; + normalizeTaskTree([root]); + expect(root.comment).toBeNull(); + }); + + it("stores comments on created tasks", () => { + const task = T("Task", "sk", { comment: "Needs review" }); + expect(task.comment).toBe("Needs review"); + }); });