Files
taskpile/CLAUDE.md
2026-04-08 11:24:36 +00:00

89 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Taskpile
A task manager with force-directed graph visualization.
## Architecture
- **Frontend**: Next.js 14 (App Router) + React 18 + Tailwind CSS 3 + TypeScript
- **Backend**: Rust (Axum 0.7) + SQLite (via SQLx)
- **Graph**: `react-force-graph-2d` for force-directed visualization
## Project Structure
```
frontend/
src/app/page.tsx — Main page: tabs, panels, task state management
src/app/layout.tsx — Root layout
src/components/
GraphView.tsx — Force graph with node selection, drag-to-center
TaskList.tsx — Pending/completed task list with selection
TaskItem.tsx — Individual task card
AddTaskForm.tsx — New task form
TaskDetailPanel.tsx — Right panel: selected task details
UserPanel.tsx — Left panel: user profile (example data)
ForceGraphClient.tsx — ForceGraph2D ref wrapper for dynamic import
src/lib/
api.ts — API client (fetch wrappers)
types.ts — TypeScript interfaces
src/__tests__/
unit/ — Jest unit tests (API, TaskItem)
e2e/ — Jest integration tests (full user flows)
backend/
src/main.rs — Axum server on port 3001
src/models.rs — Task, GraphNode, GraphEdge structs
src/db.rs — SQLite pool + migrations
src/graph.rs — Deterministic edge generation (~30% pairs)
src/routes/tasks.rs — CRUD: GET/POST /api/tasks, PATCH/DELETE /api/tasks/:id
src/routes/graph.rs — GET /api/graph
tests/integration_test.rs — Axum integration tests
```
## Running
```bash
# Backend (port 3001)
cd backend && cargo run
# Frontend (port 3003, proxies /api to backend)
cd frontend && npm run dev -- -p 3003
```
Port 3000 is used by Gitea on this machine — use 3003 for the frontend.
## Testing
```bash
# Frontend tests
cd frontend && npx jest
# Backend tests
cd backend && cargo test
```
## Key Design Decisions
- Task IDs are UUIDs (TEXT in SQLite, string from backend). Frontend `Task.id` is typed as `number` but actually receives strings — selection uses string comparison throughout.
- Graph tab and task list are switched via tabs in the center area. Left panel (user info) and right panel (task details) are independently foldable.
- Selecting a task (from list or graph node click) triggers a 3-phase animation: (1) charge force jumps to -600 so other nodes repel outward immediately, (2) after 80ms the selected node slides to canvas center over 500ms with cubic ease-out, (3) charge restores to -120 and the graph stabilizes. The node stays pinned (`fx`/`fy`) until a different task is selected.
- Both views (task list and graph) are always mounted using `absolute inset-0` with opacity/pointer-events toggle — never `hidden`. This ensures `GraphView` always has real canvas dimensions from page load, so the force simulation runs correctly in the background.
- `ForceGraph2D` canvas dimensions are driven by a `ResizeObserver` on the container div. Canvas is only mounted after the first measurement to avoid the 300×300 default size.
- Graph re-fits on tab switch (`isVisible` effect) and on panel resize (`dimensions` effect). When a node is selected, `zoomToFit` is suppressed to avoid fighting the pin animation.
- Panel transitions are 150ms CSS opacity. When switching to graph tab with a pending node selection, animation is delayed 400ms to let the re-fit settle first.
## API
All endpoints under `/api`:
| Method | Path | Description |
|--------|------|-------------|
| GET | /tasks | List all tasks |
| POST | /tasks | Create task `{title, description?}` |
| PATCH | /tasks/:id | Update task `{title?, description?, completed?}` |
| DELETE | /tasks/:id | Delete task |
| GET | /graph | Get nodes + weighted edges |
## Proxy
Do not use system proxy env vars when testing the app locally — `curl --noproxy '*'` or equivalent.