todo-webapp
A zero-dependency Node.js web app that turns your TODO.md into a live, interactive task board.

What it does
- Serves a dark glassmorphism UI at
http://<hostname>.local:3456(LAN only) - Reads
TODO.mddirectly —##headings become section cards,###become subheadings,- [ ]/- [x]become clickable items - Live updates via Server-Sent Events: any change to
TODO.md(by the agent or anyone else) pushes instantly to all open browser tabs - Click to toggle: checking an item off saves immediately to
TODO.md - Archive Done button: moves all
[x]items toTODO-done.mdwith a date stamp and removes them fromTODO.md - Auto-starts on boot and self-restarts on crash via macOS launchd
Setup
1. Copy the server script
Copy scripts/server.js to your desired app directory (e.g. ~/.openclaw/workspace/todo-app/server.js).
The server expects:
TODO.mdat../TODO.mdrelative to the script (i.e. one directory up)TODO-done.mdat../TODO-done.md(created automatically if missing)bg.jpgin the same directory as the script (optional background image)
2. Add a background image (optional)
Drop any bg.jpg into the same folder as server.js. It will be rendered at ~22% opacity behind the UI. Works best with abstract or dark imagery.
3. Install the launchd agent
Copy assets/com.todo.plist.template to ~/Library/LaunchAgents/com.todo.plist.
Edit the plist and update these two values:
- The path to
node(runwhich nodeto find it) - The path to
server.js
Then load it:
launchctl load ~/Library/LaunchAgents/com.todo.plist
4. Open in browser
Navigate to http://<your-mac-hostname>.local:3456 from any device on your LAN.
Restart command
If you update server.js, restart with:
kill $(lsof -ti :3456) && sleep 1 && launchctl kickstart -k gui/$(id -u)/com.todo
TODO.md format
The app parses standard OpenClaw-style markdown:
## Section Name
### Subheading
- [ ] Open task
- [x] Completed task
- **Bold text** is rendered in item labels
Any section added to TODO.md appears automatically. No app restart needed.
Port
Default port is 3456. To change it, edit the PORT constant at the top of server.js.