Fixing a typo meant deleting the task and typing it again, which also cost
you its position in the list. Click the text to edit it. Enter saves,
Escape cancels, clicking away saves.
The rename rule lives in tasks.js and is tested. It refuses blank input
rather than treating it as a delete, since selecting all and hitting enter
by accident should give you your task back, and it returns the original
array untouched when nothing changed so an idle edit does not write to
storage.
The interesting part was what editing collides with. Keystrokes are
stopped from reaching the row, or Delete would remove the task you are
typing into and Alt+Arrow would reorder it mid-edit. A drag cannot start
from inside the field, and the click a drag leaves behind is swallowed, or
dropping a row would open it for editing. Switching day, deferring a task
and the midnight rollover all close an open editor, so it cannot be left
hanging over a row that is no longer there.
Task operations become pure functions over arrays.
Ids move from Date.now() to crypto.randomUUID(), with a fallback for
non-secure contexts, since people do self-host this over plain HTTP on a
LAN.
Undo gets its own module so tasks.js stays pure. It covers single deletes
and clear-completed batches, and clamps indices because the list can
change between recording an entry and undoing it.