A personal productivity app that combines a hierarchical mindmap with rich per-node documentation. Built with Next.js (static export), Supabase, React Flow, TipTap, and Shadcn UI. Deploys to GitHub Pages.
- Infinite node nesting with parent/child relationships
- Urgency levels (Low / Normal / High) color-coded on the canvas
- Optional due-date badge and multi-tag support per node
- TipTap WYSIWYG rich-text description per node (Notion-style)
- Slide-out detail panel without leaving the canvas
- Keyboard-first creation: Tab = new child, Enter = new sibling
- Drag-and-drop re-parenting (entire subtree moves with the node)
- Visual filtering that hides non-matching nodes and recalculates layout
- Command palette (Ctrl+K) — searches titles and markdown content, jumps viewport
- Email/Password auth via Supabase Auth with Row-Level Security
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router, static export) |
| Language | TypeScript |
| Styling | Tailwind CSS + Shadcn UI |
| Canvas | React Flow (@xyflow/react) |
| State | Zustand |
| Editor | TipTap |
| Backend / Auth | Supabase (PostgreSQL + Auth) |
| Testing | Playwright |
| Deployment | GitHub Pages |
- Node.js 20+
- npm 10+
- A Supabase account (free tier is sufficient)
- A GitHub repository with Pages enabled (for production deployment)
git clone https://github.com/<your-org>/todo.git
cd todo
npm installCopy the example file and fill in your values:
cp .env.example .env.localEdit .env.local:
NEXT_PUBLIC_SUPABASE_URL=https://<your-project-ref>.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=<your-anon-key>
# Leave blank for local dev (no base path needed)
NEXT_PUBLIC_BASE_PATH=Both values are found in your Supabase project under Project Settings → API.
See the Supabase Setup section below, then run:
npm run devThe app is available at http://localhost:3000.
- Go to supabase.com and sign in.
- Click New project, choose an organization, enter a project name and database password, and select a region close to your users.
- Wait for the project to finish provisioning (~1–2 minutes).
Open the SQL Editor in your Supabase dashboard and run the contents of supabase/migrations/001_initial_schema.sql.
This migration:
- Creates the
nodestable with all required columns (id,user_id,parent_id,title,urgency,date,tags,description,position_x,position_y,sort_order,created_at,updated_at). - Adds a GIN index on
tagsfor fast filtering. - Attaches an
updated_attrigger that auto-updates on every row change. - Enables Row-Level Security (RLS) and creates four policies so that users can only read and write their own nodes.
- In your Supabase dashboard go to Authentication → Providers.
- Ensure the Email provider is enabled.
- Under Authentication → URL Configuration, add your site URL (e.g.
https://<your-org>.github.io/todo) to Site URL and the same URL to Redirect URLs.
The app automatically creates a root "Main" node the first time a user logs in. You can register directly from the login page — no manual seeding is required.
To create a dedicated E2E test account, register through the app UI and note the email/password for use as GitHub Secrets (see CI/CD section).
From Project Settings → API:
| Variable | Where to find it |
|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Project API keys → anon / public |
- In your GitHub repository go to Settings → Pages.
- Set Source to GitHub Actions.
Go to Settings → Secrets and variables → Actions → New repository secret and add:
| Secret name | Value |
|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Your Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Your Supabase anon key |
E2E_USER_EMAIL |
Email of a registered test account |
E2E_USER_PASSWORD |
Password of that test account |
Push to the main branch (or trigger the workflow manually from Actions → Deploy to GitHub Pages → Run workflow).
The workflow defined in .github/workflows/deploy.yml:
- Installs dependencies and Playwright browsers.
- Runs
next lintandnext build. - Executes the full Playwright E2E suite.
- On success, uploads the
out/directory and deploys it to GitHub Pages.
The deployed app will be available at:
https://<your-org>.github.io/todo/
Base path: The
NEXT_PUBLIC_BASE_PATHenv var is set to/tododuring the production build. If your repository is named differently, update this value in the workflow file and in your.env.localaccordingly.
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Yes | Full URL of your Supabase project |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Yes | Supabase anon/public API key |
NEXT_PUBLIC_BASE_PATH |
No | Base path for static export (e.g. /todo). Leave empty for local dev. |
Install Playwright browsers once:
npx playwright install --with-deps chromiumRun the E2E suite (starts a dev server automatically):
npm run test:e2eOpen the Playwright UI for interactive debugging:
npm run test:e2e:uiTests require the following environment variables to be set (Supabase URL/key + a valid test account):
NEXT_PUBLIC_SUPABASE_URL=...
NEXT_PUBLIC_SUPABASE_ANON_KEY=...
E2E_USER_EMAIL=...
E2E_USER_PASSWORD=...src/
├── app/ # Next.js App Router pages
│ ├── login/ # Email/password login page
│ └── map/ # Main mindmap canvas (auth-protected)
├── components/
│ ├── auth/ # AuthGuard, LoginForm
│ ├── canvas/ # React Flow canvas, custom node/edge components
│ ├── filters/ # Filter bar
│ ├── palette/ # Command palette (Ctrl+K)
│ ├── panel/ # Slide-out detail panel, Markdown editor
│ └── ui/ # Shadcn UI primitives
├── hooks/ # useCommandSearch, useFilter, useKeyboardNav
├── lib/
│ ├── editor/ # TipTap extensions
│ ├── flow/ # Dagre layout engine helpers
│ ├── store/ # Zustand stores (auth, nodes, UI)
│ └── supabase/ # Supabase client + CRUD queries
└── types/ # Shared TypeScript types
supabase/
├── migrations/ # SQL migration files
└── seed.sql # Notes on seeding
tests/
└── e2e/ # Playwright test specs