Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
5 changes: 5 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 7 additions & 1 deletion src/app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
/* 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 "";
Expand Down Expand Up @@ -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(); }
Expand Down Expand Up @@ -971,12 +973,16 @@ function openDetail(id,opts){
? `<div class="frow"><span class="lbl">Size</span><select onchange="updTask(${id},'size',this.value)">
<option value="">—</option>${SIZE_KEYS.map(z=>`<option value="${z}" ${normalizeSize(n.size)===z?'selected':''}>${SIZE_NAMES[z]} · ${SIZE_PTS[z]} pts</option>`).join("")}</select></div>`
: `<div class="frow"><span class="lbl">Size</span><span style="flex:1;font-size:15px;font-weight:700;color:var(--ink)">${_szPts} pts</span></div>`;
const kind=path.length===1?"project":path.length===2?"task":"subtask";
document.getElementById("dBody").innerHTML=`
<div class="frow"><span class="lbl">Owner</span>${av(n.owner)}<select onchange="updTask(${id},'owner',this.value)">
${Object.entries(PEOPLE).map(([k,pp])=>`<option value="${k}" ${k===n.owner?'selected':''}>${pp.name}</option>`).join("")}</select></div>
<div class="frow"><span class="lbl">Start</span><input type="date" value="${n.start||""}" onchange="updTask(${id},'start',this.value)"></div>
<div class="frow"><span class="lbl">End</span><input type="date" value="${n.due||""}" onchange="updTask(${id},'due',this.value)">${dueChip(n.due,leaf&&n.done)}</div>
${sizeFld}
<div class="frow frow-stack"><span class="lbl">Comment</span>
<textarea class="dcomment" rows="3" placeholder="Add a comment on this ${kind}…"
onchange="updTask(${id},'comment',this.value,true)">${escText(n.comment)}</textarea></div>
${leaf?`<div class="frow"><span class="lbl">Status</span>
<button class="chip" style="${n.done?'background:var(--green-soft);color:var(--green)':'background:#eef0f4;color:var(--ink-2)'}"
onclick="toggleDone(${id});openDetail(${id})">${n.done?"Done ✓ — tap to reopen":"In progress — tap to complete"}</button></div>`:""}
Expand Down
2 changes: 2 additions & 0 deletions src/lib/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 || [],
Expand Down Expand Up @@ -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);
});
}
Expand Down
11 changes: 11 additions & 0 deletions tests/tree.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});
});
Loading