Beautiful and easy-to-use Bullet Journal task management
Planner is a minimalist Bullet Journal-inspired task manager designed to help you organize your tasks with focus and clarity.
It supports daily planning, monthly organization, habit tracking, collections, tags and filters through a clean and distraction-free interface.
- Daily, Monthly, and Habits Views: Flexible layouts to manage tasks, schedule events, and log habit completions.
- Collections, Tags, and Filters: Rich hierarchical collection trees and flexible classification rules.
- PWA (Progressive Web App) Support: Fully installable on Desktop, iOS, and Android devices. Custom launch configurations and optimized asset precaching.
- Offline Mode & Local-First Flow: Browse the app shell and cached data offline, and seamlessly queue writes (POST, PATCH, PUT, DELETE) inside an IndexedDB-backed mutation store.
- Background Sync & ID Remapping: Automatically replays queued offline operations in strict FIFO order on reconnection. Automatically resolves client-minted temporary IDs with server-assigned IDs to prevent desyncing subsequent modifications.
- Debounced Connectivity Indicator: Small, non-intrusive network status pill with a debounced delay to prevent flashing and a sleek glassmorphic blur style.
- Real-Time WebSockets: Live synchronizations across tabs and devices via Socket.IO.
- List and Kanban Visualizations: Multiple ways to visualize your workspace.
- Modern Routing Architecture: Built on React Router v7 with declarative Splat path configuration.
- Docker
- Docker Compose
cp .env.example .env
docker compose up -dOpen http://localhost:5173 in your browser.
planner/
β-- api/
β β-- src/
β β β-- index.ts
β β β-- middleware/
β β β-- routes/
β β β-- services/
β β β-- db/
β β-- package.json
β-- app/
β β-- src/
β β β-- pages/
β β β-- components/
β β β-- hooks/
β β β-- stores/
β β β-- api/
β β β-- utils/
β β-- package.json
β-- docs/
β-- compose.yml
See DESIGN.md for detailed design system specification, component library, and visual guidelines. Data flow, service architecture, and real-time sync mechanisms documented in CLAUDE.md.
Two coverage reports are browsable on the coverage host β the Vitest report at
https://coverage.planner.local (root) and the Playwright e2e report at
https://coverage.planner.local/e2e/. Both are served by a static nginx
container from app/coverage-reports/ (kept outside dist/ so app builds
never wipe them):
docker compose up -d # includes the coverage container
docker compose exec app npx vitest run --reporter=html # Vitest report (coverage-reports/)
docker compose exec app npm run test:e2e:coverage # E2E report (coverage-reports/e2e/)test:e2e:coverage builds the app with istanbul instrumentation
(VITE_COVERAGE=true), runs Playwright against a vite preview server
(auto-started on port 4173), collects a window.__coverage__ snapshot after
each test into coverage-e2e/raw/, and merges them into coverage-reports/e2e/.
Coverage is opt-in β plain npm run test:e2e stays unchanged and
un-instrumented. (Note: only frontend code is covered; API routes hit during
e2e are not counted.)
- The
coverage.planner.localentry in/etc/hosts(TLS is provided by Traefik and is pre-configured for this host). In isolated worktree stacks the host iscoverage.<agent>.planner.local, e.g.coverage.claude.planner.local. - The Vitest HTML reporter is opt-in: it is only generated when
--reporter=htmlis passed (seeapp/vitest.config.ts); the Playwright report is generated only bytest:e2e:coverage. - Both reports are served from
app/coverage-reports/β the nginx bind mount stays valid across regenerations (only file contents change, never the directory itself).
Contributions are welcome.
Optional but recommended β they run the same checks CI does, so a red pipeline shows up locally instead of on GitHub. Enable once per clone:
./.hooks/setup-hooks.sh # sets core.hooksPath to .hooks/| Hook | Runs | Typical time |
|---|---|---|
| pre-commit | lint |
2β8s |
| pre-push | lint, test, build |
~30s/package |
Both only check the packages your change actually touches, so a docs-only
commit does no work and an api/ change never waits on the app suite.
Commands run natively when node_modules is present, otherwise through the
running compose service; with neither available they warn and skip rather
than blocking you.
Bypass a single run with --no-verify, or export SKIP_HOOKS=1 for a
session. Disable entirely with git config --unset core.hooksPath.
- Fork the repository.
- Create a branch:
git checkout -b feature/my-feature- Commit your changes:
git commit -m "feat: add my feature"- Push the branch:
git push origin feature/my-feature- Open a pull request.
This project is licensed under the MIT License.
