A keyboard-first task manager that works offline. Type a task the way you'd say it, let the co-pilot time-block your calendar, then work through it in focus sessions. Your tasks never leave your browser.
taskpilot.ewstudios.workers.dev · The shared API
TaskPilot began in 2021 as a tutorial-sized React app: a form, four tabs and localStorage. It indexed tasks by id - 1, showed the wrong month, and reversed the stored list every time it rendered. This is a rebuild of it as a real product.
The best idea from each well-known task app is borrowed and fitted into one calm app:
| From | What TaskPilot takes |
|---|---|
| Todoist | Natural-language quick add, parsed on the device |
| Things 3 | Inbox · Today · Upcoming · Someday, and a quiet design |
| Linear and Trello | Ctrl+K for everything, J/K/X keys, a kanban board |
| TickTick | Pomodoro focus, an Eisenhower matrix, habit streaks |
| Motion | "Plan my day": AI time-blocking on a calendar |
| Sunsama | A morning planning ritual and an evening review |
| Akiflow | Capture from anywhere, including by voice |
flowchart LR
Q([Q · quick add<br/>or the mic]) --> P[parseQuickAdd<br/>chrono-node + tokens]
P --> DB[(IndexedDB<br/>Dexie)]
DB --> V{{Views}}
V --> T[Today · Upcoming<br/>Inbox · Someday]
V --> B[Board]
V --> C[Calendar]
V --> M[Matrix]
V --> F[Focus + habits]
C -. Plan my day .-> AI[[AI co-pilot]]
T -. What now? · review .-> AI
The landing page tells the same story as a pinned scroll "flight path", from first coffee to lights out:
flowchart TB
subgraph Browser
R[TanStack Router<br/>lazy routes] --> S[App shell<br/>sidebar · palette · shortcuts]
S --> V[Views]
V <--> D[(Dexie<br/>IndexedDB)]
V --> AI[useAi]
Z[Zustand<br/>settings · UI] --> S
SW[Service worker<br/>offline shell] -.-> R
end
AI --> P{Provider<br/>chosen in Settings}
P -->|default| EW[ew-apps-api<br/>Hono on Heroku]
P --> PU[Puter.js]
P --> OR[OpenRouter<br/>OAuth PKCE]
P --> OWN[Any OpenAI-compatible<br/>key, browser only]
EW --> G[Gemini Flash-Lite<br/>free tier]
EW --> SB[(Supabase<br/>auth + daily quota)]
Every AI action streams JSON Lines, so a list of subtasks or time blocks appears item by item:
sequenceDiagram
participant U as You
participant App as Calendar view
participant AI as useAi("plan-day")
participant API as ew-apps-api
U->>App: Plan my day
App->>AI: tasks (id, title, estimate, priority), now, dayEnd, fixed events
AI->>API: POST /api/ai/taskpilot/plan-day + Supabase JWT
API-->>AI: {"taskId":"t-1","start":"09:30","end":"11:00"}\n…
loop each line
AI-->>App: one block
end
App->>App: keep only known ids and valid times
App-->>U: blocks on the timeline · Undo
The public pages are prerendered and then hydrated, so the first paint never waits for JavaScript:
flowchart LR
B[vite build] --> PR[scripts/prerender.mjs<br/>headless Chromium]
PR -->|"/?prerender"| RS[renderToString<br/>in the page]
RS --> H[dist/*/index.html<br/>with Suspense markers]
H --> CF[Cloudflare Worker<br/>static assets]
CF --> FP[First paint<br/>from HTML]
FP -->|after 2 frames| HY[hydrateRoot]
CF -->|"/app/*"| SH[shell/index.html<br/>createRoot]
- Local-first, no account. Tasks live in IndexedDB. Nothing to sign up for, nothing to leak, and it works offline. Export and import are plain JSON. Sync is a Pro feature for later, built on the same model.
- The AI is never paywalled, and you choose who runs it. The free server uses Gemini's free tier through ew-apps-api. Prompt templates live on the server, which accepts only typed, size-capped data, never a raw prompt. If you'd rather not use it, or you're in the EEA, the UK or Switzerland where the free tier isn't allowed, Puter, OpenRouter (PKCE, no key copying) or your own key work from the browser.
- One sign-in for every EWSTUDIOS app. All the apps share one Supabase account and live under
ewstudios.workers.devas Cloudflare Workers serving static files. Supabase allows a single passkey domain per project, so a shared parent domain means a passkey saved in one app signs you in to the others. The emailed 6-digit code always works as the fallback. Sign-in exists only for the free AI server's daily quota; your tasks stay in the browser. - The AI sees as little as possible. Titles, estimates, priorities and dates. Never notes or project names. The privacy page says plainly that Google may read free-tier prompts.
- Prerender and hydrate, not a server framework. Rendering the public pages with React's own
renderToString, inside the built app in a headless browser, gives correct hydration markers without Next.js or a server. Mobile Lighthouse went from 0.79 to 0.98 on the landing page. - 3D only where it helps. The glass tiles are React Three Fiber, loaded after first paint, on wide screens, and never on a software GPU (headless Chrome, some VMs), where they would burn the CPU for decoration. The canvas stops rendering once it scrolls out of view.
- Keyboard drag for everyone. dnd-kit's default keyboard sensor nudges a card a few pixels per press. A small coordinate getter jumps it to the next drop zone instead, so the board, matrix and calendar all work without a mouse.
- A quadrant is derived until you set it. Urgent means due by tomorrow; important means P1 or P2. Dragging in the matrix stores an override, so the view never fights you.
- Clips play only when seen. Videos use
preload="none", play while on screen, and stay still with reduced motion. The service worker doesn't precache them.
Built in conversation with Claude Code, which wrote and tested the code. The design direction came from Awwwards references (Butter, Heron AI, Overworld Audio, Lidar Drone Scanning; see docs/design-brief.md). The art and clips were generated in Google Flow (Nano Banana Pro and Omni 1.1 Flash); docs/assets.md lists every prompt. The logo and icons are SVG.
npm installnpm run devnpm testnpm run e2eNode 24. Copy .env.example to .env to point at the shared API and Supabase; without them, the app runs fully offline and offers the other AI providers. npm run build also prerenders the public pages, so it needs Playwright's Chromium (npx playwright install chromium). npm run e2e builds and serves the app with the same routing as the live Worker (serve.json mirrors public/_redirects) and runs the flows, plus axe on every page in both themes. CI adds lint, typecheck and Lighthouse (≥ 0.95 on every category). Deploy with npm run build && npx wrangler deploy (config in wrangler.jsonc).
node scripts/screenshots.mjs retakes the pictures in docs/ with sample data, against a running build on port 4173.
src/
├─ main.tsx router.load(), then hydrate or render
├─ router.tsx every route, lazily loaded
├─ index.css design tokens for both themes
├─ lib/ model and logic: db, quickAdd, tasks, dates, backup, dndKeyboard
├─ ai/ actions + templates, providers, JSON Lines, PKCE, Supabase sign-in
├─ stores/ settings (persisted) and UI state
├─ hooks/ live queries and keyboard shortcuts
├─ components/ app shell, quick add, palette, task detail, board, onboarding
├─ views/ Today, lists, Board, Calendar, Matrix, Focus, Projects, rituals
├─ landing/ 3D hero, flight-path scroll, smooth scroll
└─ pages/ Landing, Developer, Pro, Settings, Privacy, Terms, 404
scripts/
├─ prerender.mjs static HTML for the public pages
└─ screenshots.mjs the README pictures
e2e/ Playwright flows and axe checks
docs/ design brief, asset list, screenshots










