From b9e0399840b6d20fb8698d36ae9fb55241c4f3ce Mon Sep 17 00:00:00 2001 From: martian56 Date: Fri, 10 Jul 2026 13:08:51 +0400 Subject: [PATCH 1/4] docs(agents): add AGENTS.md, agent configs, and AI attribution policy Introduce AGENTS.md as the shared, tool-agnostic guide and single source of truth for AI coding agents. CLAUDE.md and GEMINI.md import it, and a compact Copilot instructions file mirrors the key rules. Track the existing .cursor/rules so agent guidance ships with the repo, and guard local-only agent files in .gitignore. Document the AI attribution policy (Co-Authored-By trailers plus a PR attribution section) in AGENTS.md, CONTRIBUTING.md, and the PR template. Co-Authored-By: Claude Opus 4.8 (1M context) --- .cursor/rules/alembic-no-auto-migrations.mdc | 16 ++ .cursor/rules/api-authentication.mdc | 14 ++ .cursor/rules/api-boards-elements.mdc | 37 ++++ .cursor/rules/api-database-conventions.mdc | 31 ++++ .cursor/rules/api-layer-responsibilities.mdc | 27 +++ .cursor/rules/api-modular-architecture.mdc | 27 +++ .cursor/rules/api-realtime-websockets.mdc | 28 +++ .cursor/rules/api-redis.mdc | 18 ++ .cursor/rules/api-rest-conventions.mdc | 32 ++++ .cursor/rules/excalidraw-canvas.mdc | 24 +++ .cursor/rules/extend-dont-abstract.mdc | 9 + .cursor/rules/loomy-project-context.mdc | 34 ++++ .cursor/rules/loomy-tech-stack.mdc | 20 +++ .cursor/rules/python-standards.mdc | 15 ++ .cursor/rules/uncodixify-ui.mdc | 73 ++++++++ .cursor/rules/windows-powershell.mdc | 27 +++ .cursor/rules/yjs-sync.mdc | 27 +++ .github/PULL_REQUEST_TEMPLATE.md | 7 + .github/copilot-instructions.md | 32 ++++ .gitignore | 9 +- AGENTS.md | 172 +++++++++++++++++++ CLAUDE.md | 14 ++ CONTRIBUTING.md | 9 + GEMINI.md | 10 ++ 24 files changed, 708 insertions(+), 4 deletions(-) create mode 100644 .cursor/rules/alembic-no-auto-migrations.mdc create mode 100644 .cursor/rules/api-authentication.mdc create mode 100644 .cursor/rules/api-boards-elements.mdc create mode 100644 .cursor/rules/api-database-conventions.mdc create mode 100644 .cursor/rules/api-layer-responsibilities.mdc create mode 100644 .cursor/rules/api-modular-architecture.mdc create mode 100644 .cursor/rules/api-realtime-websockets.mdc create mode 100644 .cursor/rules/api-redis.mdc create mode 100644 .cursor/rules/api-rest-conventions.mdc create mode 100644 .cursor/rules/excalidraw-canvas.mdc create mode 100644 .cursor/rules/extend-dont-abstract.mdc create mode 100644 .cursor/rules/loomy-project-context.mdc create mode 100644 .cursor/rules/loomy-tech-stack.mdc create mode 100644 .cursor/rules/python-standards.mdc create mode 100644 .cursor/rules/uncodixify-ui.mdc create mode 100644 .cursor/rules/windows-powershell.mdc create mode 100644 .cursor/rules/yjs-sync.mdc create mode 100644 .github/copilot-instructions.md create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 GEMINI.md diff --git a/.cursor/rules/alembic-no-auto-migrations.mdc b/.cursor/rules/alembic-no-auto-migrations.mdc new file mode 100644 index 0000000..738d05e --- /dev/null +++ b/.cursor/rules/alembic-no-auto-migrations.mdc @@ -0,0 +1,16 @@ +--- +description: Do not create Alembic migration files; the user creates them manually +globs: api/app/**/*.py +alwaysApply: true +--- + +# Alembic Migrations + +**Do not create migration files.** The user manages Alembic migrations manually. + +## Workflow + +- When adding or changing SQLAlchemy models (columns, tables, indexes), update the model code only. +- Do **not** create new files in `alembic/versions/`. +- Do **not** run `alembic revision` or `alembic upgrade`. +- After model changes, tell the user to run: `alembic revision --autogenerate -m "description"` and `alembic upgrade head`. diff --git a/.cursor/rules/api-authentication.mdc b/.cursor/rules/api-authentication.mdc new file mode 100644 index 0000000..98c5bf1 --- /dev/null +++ b/.cursor/rules/api-authentication.mdc @@ -0,0 +1,14 @@ +--- +description: JWT authentication and password security +globs: api/app/**/* +alwaysApply: false +--- + +# Authentication + +## Requirements + +- **JWT** for authentication. +- **bcrypt** for password hashing. +- **Never store plaintext passwords.** +- **Protected routes** must require authentication. diff --git a/.cursor/rules/api-boards-elements.mdc b/.cursor/rules/api-boards-elements.mdc new file mode 100644 index 0000000..b4e8d10 --- /dev/null +++ b/.cursor/rules/api-boards-elements.mdc @@ -0,0 +1,37 @@ +--- +description: Boards and canvas elements data model +globs: api/app/**/* +alwaysApply: false +--- + +# Boards and Elements + +**Boards** are collaborative canvases. Each board contains many **elements**. + +## Element Types + +- Shapes +- Sticky notes +- Text +- Arrows +- Connectors + +## Storage + +Store element properties as **JSONB** in PostgreSQL. + +Example element data: + +```json +{ + "x": 120, + "y": 240, + "width": 300, + "height": 120, + "text": "Hello" +} +``` + +## API Design + +Design the API so that **updates to elements can be frequent and efficient** (e.g. bulk updates, delta sync, or patch endpoints). diff --git a/.cursor/rules/api-database-conventions.mdc b/.cursor/rules/api-database-conventions.mdc new file mode 100644 index 0000000..b002d41 --- /dev/null +++ b/.cursor/rules/api-database-conventions.mdc @@ -0,0 +1,31 @@ +--- +description: PostgreSQL, SQLAlchemy, and Alembic conventions +globs: api/app/**/* +alwaysApply: false +--- + +# Database Conventions + +The project uses **PostgreSQL** with **SQLAlchemy** and **Alembic**. + +## Model Changes Workflow + +1. Ensure the SQLAlchemy model is correct. +2. Generate an Alembic migration reflecting the change. +3. Migrations must be **idempotent and safe**. + +## Database Rules + +| Rule | Details | +|------|---------| +| **Primary keys** | Use UUID | +| **Naming** | snake_case in the database | +| **Timestamps** | Include `created_at` and `updated_at` | +| **Indexes** | Add indexes for frequently queried columns | + +## Example Indexed Columns + +- `users.email` +- `users.username` +- `boards.workspace_id` +- `elements.board_id` diff --git a/.cursor/rules/api-layer-responsibilities.mdc b/.cursor/rules/api-layer-responsibilities.mdc new file mode 100644 index 0000000..8cbb34f --- /dev/null +++ b/.cursor/rules/api-layer-responsibilities.mdc @@ -0,0 +1,27 @@ +--- +description: API layer flow and responsibility separation +globs: api/app/**/* +alwaysApply: false +--- + +# API Layer Architecture + +All backend features follow this flow: + +``` +router → service → repository → database +``` + +## Responsibilities + +| Layer | File | Responsibility | +|-------|------|----------------| +| **router** | `router.py` | HTTP requests and responses only | +| **service** | `service.py` | Business logic | +| **repository** | `repository.py` | Database access via SQLAlchemy | +| **schemas** | `schemas.py` | Request/response models (Pydantic) | +| **model** | `model.py` | SQLAlchemy database models | + +## Rule + +**Never mix these responsibilities.** Each layer has a single concern. diff --git a/.cursor/rules/api-modular-architecture.mdc b/.cursor/rules/api-modular-architecture.mdc new file mode 100644 index 0000000..d356e58 --- /dev/null +++ b/.cursor/rules/api-modular-architecture.mdc @@ -0,0 +1,27 @@ +--- +description: API modular architecture and workflow +globs: api/app/**/* +alwaysApply: false +--- + +# API Architecture – Modular Structure + +Before generating or modifying any API code, **read the entire `api/app` directory** and understand the existing folder and module structure. + +## Module Layout + +Each feature lives in `app/modules//`. Every module typically contains: + +| File | Purpose | +|------|---------| +| `router.py` | HTTP endpoints | +| `service.py` | Business logic | +| `repository.py` | Data access | +| `schemas.py` | Pydantic request/response models | +| `model.py` | SQLAlchemy model(s) | + +## Rules + +- **Never introduce a different structure.** Follow this layout exactly. +- **Extend existing modules** when adding functionality—don’t create unrelated folders. +- **When unsure where code belongs**, inspect similar modules and mirror their structure. diff --git a/.cursor/rules/api-realtime-websockets.mdc b/.cursor/rules/api-realtime-websockets.mdc new file mode 100644 index 0000000..f736e4b --- /dev/null +++ b/.cursor/rules/api-realtime-websockets.mdc @@ -0,0 +1,28 @@ +--- +description: Real-time collaboration and WebSocket events +globs: api/app/**/* +alwaysApply: false +--- + +# Real-Time Collaboration + +Loomy supports **real-time collaboration**. Board updates should eventually be **broadcast through WebSockets**. + +## API Endpoints + +Support these operations for elements: + +- **Creating** elements +- **Updating** elements +- **Deleting** elements + +## WebSocket Events + +Events to broadcast: + +| Event | Purpose | +|-------|---------| +| `element.created` | New element added | +| `element.updated` | Element changed | +| `element.deleted` | Element removed | +| `cursor.moved` | User cursor position | diff --git a/.cursor/rules/api-redis.mdc b/.cursor/rules/api-redis.mdc new file mode 100644 index 0000000..264f220 --- /dev/null +++ b/.cursor/rules/api-redis.mdc @@ -0,0 +1,18 @@ +--- +description: Redis usage for real-time, caching, and rate limiting +globs: api/app/**/* +alwaysApply: false +--- + +# Redis + +Redis is used for: + +- **WebSocket pub/sub** +- **Presence tracking** +- **Caching** +- **Rate limiting** + +## Real-Time and Event Broadcasting + +When implementing **real-time collaboration** or **event broadcasting**, use **Redis pub/sub**—not direct in-memory communication. diff --git a/.cursor/rules/api-rest-conventions.mdc b/.cursor/rules/api-rest-conventions.mdc new file mode 100644 index 0000000..73fd4b6 --- /dev/null +++ b/.cursor/rules/api-rest-conventions.mdc @@ -0,0 +1,32 @@ +--- +description: REST API conventions and response schemas +globs: api/app/**/* +alwaysApply: false +--- + +# REST API Conventions + +All APIs must follow REST best practices. + +## Endpoint Conventions + +| Method | Path | Purpose | +|--------|------|---------| +| GET | `/api/resource` | List resources | +| GET | `/api/resource/{id}` | Get by ID | +| POST | `/api/resource` | Create | +| PATCH | `/api/resource/{id}` | Update | +| DELETE | `/api/resource/{id}` | Delete | + +## Pagination + +Use pagination for list endpoints: + +``` +GET /api/boards?page=1&limit=20 +``` + +## Response Schemas + +- **Never return database models directly.** +- **Always return Pydantic response schemas.** diff --git a/.cursor/rules/excalidraw-canvas.mdc b/.cursor/rules/excalidraw-canvas.mdc new file mode 100644 index 0000000..8e1b52c --- /dev/null +++ b/.cursor/rules/excalidraw-canvas.mdc @@ -0,0 +1,24 @@ +--- +description: Use Excalidraw for the whiteboard canvas and research its APIs before making canvas changes +globs: apps/frontend/src/** +alwaysApply: false +--- + +# Excalidraw Canvas & Research Rule + +- **Use `@excalidraw/excalidraw` for the board/canvas UI**: + - When implementing or updating any whiteboard, board, or drawing canvas in the frontend, prefer the official Excalidraw React component and APIs (`Excalidraw`, `initialData`, `onChange`, `excalidrawAPI`, `UIOptions`, `theme`, etc.). + - Avoid building a custom canvas/interaction system from scratch unless there is a very strong, documented reason. + +- **Deep research before significant changes**: + - Before introducing or modifying core canvas behavior (selection, tools, sidebar, persistence, multi-user cursors, etc.), the agent should: + - Review the latest [Excalidraw docs](https://docs.excalidraw.com) and API (props, `excalidrawAPI`, `restore`, `sceneCoordsToViewportCoords`, etc.). + - Look for recommended patterns (initialData, onChange, updateScene, CSS variable overrides) in Excalidraw’s documentation. + - Align implementations with those patterns where practical. + +- **Integration expectations**: + - Canvas state (elements + appState) should be persisted via the backend’s elements API (`excalidraw_snapshot` type) and synced in real time via WebSocket when needed. + - Keep the server-side boards/elements model aligned with Excalidraw’s scene data (elements array + appState object). + +- **When in doubt**: + - If unsure how to implement a canvas feature, explore the Excalidraw docs and examples first, summarize options, then choose the approach that best fits Loomy’s backend and real-time architecture. diff --git a/.cursor/rules/extend-dont-abstract.mdc b/.cursor/rules/extend-dont-abstract.mdc new file mode 100644 index 0000000..b779450 --- /dev/null +++ b/.cursor/rules/extend-dont-abstract.mdc @@ -0,0 +1,9 @@ +--- +description: Prefer extending modules over new abstractions +alwaysApply: true +--- + +# Code Generation Preference + +- **Extend existing modules** instead of creating new abstractions. +- Goal: **maintainability and readability** for an open-source project. diff --git a/.cursor/rules/loomy-project-context.mdc b/.cursor/rules/loomy-project-context.mdc new file mode 100644 index 0000000..29e512d --- /dev/null +++ b/.cursor/rules/loomy-project-context.mdc @@ -0,0 +1,34 @@ +--- +description: Loomy project context and API requirements +alwaysApply: true +--- + +# Loomy – Project Context + +Loomy is an open-source, self-hostable collaborative whiteboard similar to Miro. + +## User Capabilities + +- Create workspaces and boards +- Collaborate in real time on infinite canvas boards +- Place elements: shapes, text, connectors, sticky notes +- Invite other users and work together + +## Backend API Requirements + +The API must support: + +| Domain | Requirements | +|--------|--------------| +| **Authentication** | Auth flows, sessions, tokens | +| **User accounts** | CRUD, profiles, identity | +| **Workspaces** | Multi-tenant workspaces, membership | +| **Boards** | Boards per workspace, metadata | +| **Board elements** | Shapes, text, connectors, sticky notes | +| **Real-time collaboration** | WebSockets or similar for live updates | +| **Permissions** | Access control for workspaces, boards, elements | + +## Design Principles + +- **Scalability**: Boards may contain many elements; design for large datasets and pagination. +- **Concurrency**: Support multiple concurrent users per board; consider optimistic locking, conflict resolution, or CRDTs for real-time sync. diff --git a/.cursor/rules/loomy-tech-stack.mdc b/.cursor/rules/loomy-tech-stack.mdc new file mode 100644 index 0000000..dd3c52a --- /dev/null +++ b/.cursor/rules/loomy-tech-stack.mdc @@ -0,0 +1,20 @@ +--- +description: Loomy recommended tech stack – Zustand, Yjs, Excalidraw, FastAPI +alwaysApply: true +--- + +# Loomy Tech Stack + +Use this stack for new features and architectural decisions: + +| Layer | Technology | +|-------|------------| +| **UI State** | Zustand | +| **Realtime Collaboration** | Yjs | +| **Canvas Engine** | Excalidraw | +| **Backend** | FastAPI | + +- Prefer **Zustand** over Redux, Context, or ad-hoc useState for shared UI state. +- Use **Yjs** for collaborative sync (cursors, presence, document state) when applicable. +- Use **Excalidraw** (`@excalidraw/excalidraw`) for the whiteboard canvas; persist scene as `excalidraw_snapshot` (elements + appState) via the elements API. +- Keep the **FastAPI** backend; do not introduce other backend frameworks. diff --git a/.cursor/rules/python-standards.mdc b/.cursor/rules/python-standards.mdc new file mode 100644 index 0000000..c3254d3 --- /dev/null +++ b/.cursor/rules/python-standards.mdc @@ -0,0 +1,15 @@ +--- +description: Python coding standards +globs: **/*.py +alwaysApply: false +--- + +# Python Standards + +- Follow **modern Python practices**. +- Use **type hints everywhere**. +- Keep functions **small and focused**. +- **Avoid duplicated logic** (extract helpers, reuse). +- Use **dependency injection** for database sessions. + +**Prefer clarity over cleverness.** diff --git a/.cursor/rules/uncodixify-ui.mdc b/.cursor/rules/uncodixify-ui.mdc new file mode 100644 index 0000000..2b68ab2 --- /dev/null +++ b/.cursor/rules/uncodixify-ui.mdc @@ -0,0 +1,73 @@ +--- +description: Avoid Codex AI aesthetic; build human-designed, functional UI like Linear/Raycast/Stripe/GitHub +globs: apps/frontend/**/*.{tsx,ts,css} +alwaysApply: false +--- + +# Uncodixify UI + +When building UI, avoid the default AI aesthetic. If a choice feels like a default AI move, ban it and pick the harder, cleaner option. + +## Keep It Normal + +- **Sidebars**: 240–260px fixed, solid background, simple border-right, no floating shells +- **Headers**: Simple h1/h2, no eyebrows, no uppercase labels, no gradient text +- **Buttons**: Solid fills or simple borders, 8–10px radius max, no pills, no gradients +- **Cards**: Simple containers, 8–12px radius, subtle borders, no shadows over 8px blur +- **Forms/Inputs**: Standard inputs, labels above fields, solid borders, simple focus states +- **Modals/Dropdowns**: Centered overlay, simple backdrop, no slide-in animations +- **Typography**: System or simple sans-serif, 14–16px body, clear hierarchy +- **Spacing**: Consistent 4/8/12/16/24/32px scale +- **Shadows**: Subtle `0 2px 8px rgba(0,0,0,0.1)` max +- **Transitions**: 100–200ms ease, no bouncy or transform effects + +Think Linear, Raycast, Stripe, GitHub. They don't try to grab attention; they just work. + +## Hard No + +- Oversized rounded corners (20–32px), pill overload +- Floating glassmorphism, soft corporate gradients, generic dark SaaS +- Serif headline + sans fallback as "premium" shortcut +- `Inter`, `Roboto`, `Segoe UI`, `Arial`, `Trebuchet MS` unless already in use +- Hero sections inside dashboards; metric-card grid as first instinct +- Eyebrow labels (``), decorative copy, "live pulse" / "operator checklist" +- Colors trending blue; prefer dark muted palettes +- Structure: `` + `` + decorative copy blocks + +## Specifically Banned + +- Border radii 20–32px everywhere; repeating rounded rectangle on everything +- Floating detached sidebar with rounded outer shell +- Canvas/donut charts with no product reason; KPI grids as default layout +- Eyebrow labels (e.g. "MARCH SNAPSHOT" uppercase + letter-spacing) +- Transform animations on hover; dramatic shadows (e.g. 0 24px 60px) +- Pipeline bars with gradient fills; tag badges on every status +- Brand marks with gradient backgrounds; nav badges for counts +- Right rail panels; multiple nested panel types + +## Color Priority + +1. **Highest**: Use existing project colors (search the codebase first) +2. **Else**: Pick from predefined palettes below +3. **Never**: Invent random combinations unless explicitly requested + +### Dark Palettes (Background / Surface / Primary / Secondary / Accent / Text) + +| Name | Values | +|------|--------| +| Midnight Canvas | `#0a0e27` / `#151b3d` / `#6c8eff` / `#a78bfa` / `#f472b6` / `#e2e8f0` | +| Obsidian Depth | `#0f0f0f` / `#1a1a1a` / `#00d4aa` / `#00a3cc` / `#ff6b9d` / `#f5f5f5` | +| Slate Noir | `#0f172a` / `#1e293b` / `#38bdf8` / `#818cf8` / `#fb923c` / `#f1f5f9` | +| Void Space | `#0d1117` / `#161b22` / `#58a6ff` / `#79c0ff` / `#f78166` / `#c9d1d9` | + +### Light Palettes + +| Name | Values | +|------|--------| +| Cloud Canvas | `#fafafa` / `#ffffff` / `#2563eb` / `#7c3aed` / `#dc2626` / `#0f172a` | +| Pearl Minimal | `#f8f9fa` / `#ffffff` / `#0066cc` / `#6610f2` / `#ff6b35` / `#212529` | +| Porcelain Clean | `#f9fafb` / `#ffffff` / `#4f46e5` / `#8b5cf6` / `#ec4899` / `#111827` | + +## Internal Check + +Before implementing UI, list what you would normally do (gradients, pills, hero blocks, eyebrow labels, etc.). Then do the opposite: follow Uncodixify. Replicate Figma/designer-made components; don't invent your own. diff --git a/.cursor/rules/windows-powershell.mdc b/.cursor/rules/windows-powershell.mdc new file mode 100644 index 0000000..f9c8760 --- /dev/null +++ b/.cursor/rules/windows-powershell.mdc @@ -0,0 +1,27 @@ +--- +description: Use PowerShell instead of bash on Windows +alwaysApply: true +--- + +# Windows PowerShell Commands + +This project runs on Windows. When suggesting or running terminal commands: +- **Use PowerShell**, not bash +- Avoid bash-specific syntax (e.g. `&&`, `;`, `$VAR`, `[[ ]]`, `rm -rf`) + +## Examples + +| Instead of (bash) | Use (PowerShell) | +|-------------------|------------------| +| `cd /path && npm install` | `cd \path; npm install` | +| `rm -rf node_modules` | `Remove-Item -Recurse -Force node_modules` | +| `export VAR=value` | `$env:VAR = "value"` | +| `ls -la` | `Get-ChildItem` or `dir` | +| `cat file.txt` | `Get-Content file.txt` | +| `mkdir -p foo/bar` | `New-Item -ItemType Directory -Force foo\bar` | +| `grep "pattern" file` | `Select-String "pattern" file` | + +## Notes +- Use backslash `\` for Windows paths, or forward slash `/` (PowerShell accepts both) +- Chain commands with `;` instead of `&&` +- For multiline commands, use `` ` `` (backtick) as line continuation in PowerShell diff --git a/.cursor/rules/yjs-sync.mdc b/.cursor/rules/yjs-sync.mdc new file mode 100644 index 0000000..933c57f --- /dev/null +++ b/.cursor/rules/yjs-sync.mdc @@ -0,0 +1,27 @@ +--- +description: Use Yjs for collaborative sync and research its CRDT patterns before changing collaboration logic +globs: apps/frontend/src/**/*.{ts,tsx} +alwaysApply: false +--- + +# Yjs Collaboration & Sync Rule + +- **Use Yjs for real-time collaboration state**: + - When implementing or updating collaborative board, canvas, or document state in the frontend, use Yjs as the primary CRDT/sync layer. + - Avoid creating custom ad-hoc sync protocols or state-merging logic when Yjs can model the behavior. + +- **Deep research before significant collaboration changes**: + - Before changing core collaboration behavior (multi-user editing, cursors, presence, history/undo, conflict resolution, offline support), the agent should: + - Review the latest Yjs documentation and official guides. + - Study recommended patterns for shared types (e.g., `Y.Doc`, `Y.Map`, `Y.Array`), awareness/presence, and persistence/adapters (e.g., WebSocket, IndexedDB). + - Prefer established Yjs patterns over bespoke CRDT or custom diff/patch logic. + +- **Integration expectations**: + - Model board and element state so it can be represented as Yjs shared types and synchronized via the existing backend WebSocket and REST APIs. + - Keep a clear separation between: + - The canonical collaborative state (Yjs document and shared types), and + - Any derived React/UI state, which should subscribe to and update based on Yjs changes. + +- **When in doubt**: + - If unsure how to implement a collaborative feature with Yjs, the agent should first explore Yjs docs and examples (and well-known integrations like Excalidraw + Yjs where applicable), summarize viable approaches, and then choose the one that best aligns with Loomy’s backend and real-time architecture. + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index afb4e5f..3f8ae8d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -48,6 +48,12 @@ Closes # +## AI attribution + + + +- None + ## How to test @@ -67,6 +73,7 @@ Closes # - [ ] I added or updated tests for new behavior. - [ ] I updated README / docs where relevant. - [ ] I confirmed no secrets, tokens, or `.env` files are committed. +- [ ] I attributed any AI tools that helped: a `Co-Authored-By` trailer on the commit and a note in the AI attribution section above. ## Screenshots / recordings diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..9a6f135 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,32 @@ +# GitHub Copilot instructions for Loomy + +Loomy is an open-source collaborative whiteboard. The full guide for this repository is `AGENTS.md` at the repo root. Read it for anything not covered here. These are the rules that matter most, kept short because Copilot does not follow file imports. + +## Layout + +- `api/app/`: FastAPI backend (Python 3.12, uv). All routes mount under `/api`. +- `apps/frontend/`: React 19 + Vite + TypeScript SPA. Excalidraw canvas, Zustand state, Yjs for collaborative sync. + +## Backend rules + +- Keep the layered flow: router calls service, service calls repository, repository touches the database. Never mix layers. +- Never return SQLAlchemy models from routers. Map them to Pydantic schemas. +- Do not create Alembic migration files. The maintainer runs migrations by hand after model changes. +- Type hints on everything. `ruff` line length is 100, `mypy` runs in strict mode. + +## Frontend rules + +- Zustand for shared UI state, not Redux or Context. +- Yjs for collaborative document state, Excalidraw for the canvas. +- Localize every user-facing string in `src/i18n/` (EN, AZ, RU). +- Follow the UI rules in `.cursor/rules/uncodixify-ui.mdc`. Avoid the default AI look: no oversized radii, floating glassmorphism, or eyebrow labels. Aim for Linear, Raycast, Stripe, and GitHub. + +## Commits and attribution + +- Use Conventional Commits (`feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`). +- When Copilot helps with a change, add `Co-Authored-By: Copilot <198982749+Copilot@users.noreply.github.com>` to the commit and note it in the PR description under an "AI attribution" heading. See AGENTS.md. + +## Style + +- Write almost no code comments. Comment only a non-obvious reason. +- Do not use em-dashes in prose. diff --git a/.gitignore b/.gitignore index 003d297..bce0284 100644 --- a/.gitignore +++ b/.gitignore @@ -5,15 +5,16 @@ !.env.example scripts/ # IDE and editor -.cursor/ .vscode/ -.claude/ -.agents/ -CLAUDE.md memory/ .idea/ *.sublime-* +# Local agent overrides (shared agent config is tracked; keep personal/local files out) +.claude/settings.local.json +.claude/*.local.json +.agents/**/*.local.* + # OS .DS_Store Thumbs.db diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..ec6d20f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,172 @@ +# AGENTS.md + +Loomy is an open-source collaborative whiteboard. This file is the shared guide for anyone working in the repository, whether a person or an AI coding agent. The tool-specific files (`CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`, and `.cursor/rules/`) all point back here so there is one source of truth. When you change how the project works, update this file in the same pull request. + +## Repository layout + +Monorepo for **Loomy**: + +- `api/app/`: FastAPI backend (Python 3.12+, uv). Entry points are `api/app/main.py` (uvicorn launcher) and `api/app/app/main.py` (the FastAPI app). All routes mount under `/api` in `app/api/router.py`. +- `apps/frontend/`: React 19 + Vite 7 + TypeScript SPA. Canvas via `@excalidraw/excalidraw`, state via Zustand, routing via `react-router-dom` v7. +- `worker/`: empty for now, reserved for future background workers. +- `docs/api/`: MkDocs API documentation. +- `scripts/`: operator scripts (PowerShell), onboarding slides (`loomy-onboarding.md`), notes. +- Root `package.json`: tooling only (husky, lint-staged, commitlint). It is not the frontend. The frontend lives under `apps/frontend/`. +- `docker-compose.yml`: PostgreSQL 17 (`localhost:15432`) and Redis 7 (`localhost:6379`). + +## Common commands + +Run backend commands from `api/app/` and frontend commands from `apps/frontend/`. Run `npm install` once from the repo root to install the git hooks. + +| Where | Command | Purpose | +| --------------- | ------------------------------------------------------ | --------------------------------------------------- | +| root | `docker compose up -d` | Start Postgres and Redis | +| root | `npm install` | Install husky, lint-staged, commitlint; register git hooks via `prepare` | +| `api/app` | `uv sync --all-extras` | Install backend deps (incl. dev) | +| `api/app` | `uv run python -m app.main` or `uv run python main.py` | Run API with reload on `:8000` | +| `api/app` | `uv run alembic upgrade head` | Apply DB migrations | +| `api/app` | `uv run alembic revision --autogenerate -m "msg"` | Generate a migration (only the user runs this, see the Alembic rule below) | +| `api/app` | `uv run pytest` | Run all backend tests | +| `api/app` | `uv run pytest tests/test_api_auth.py::test_name` | Run a single test | +| `api/app` | `uv run ruff check .` | Lint | +| `api/app` | `uv run mypy .` | Strict type check (`strict = true`) | +| `apps/frontend` | `npm install` | Install frontend deps | +| `apps/frontend` | `npm run dev` | Vite dev server on `:5173` | +| `apps/frontend` | `npm run build` | `tsc -b && vite build` | +| `apps/frontend` | `npm run test` | Vitest run | +| `apps/frontend` | `npm run lint` / `npm run lint:fix` | ESLint | +| `apps/frontend` | `npm run format:check` / `npm run format` | Prettier (CI enforces `format:check`) | + +CI (`.github/workflows/api-ci.yml`, `ui-ci.yml`) runs on pull requests that touch `api/app/**` or `apps/frontend/**`. API CI runs ruff, then mypy, then pytest. UI CI runs lint, then format:check, then build. All of them must pass. A local pre-push hook mirrors this: it runs mypy and pytest when backend files change, and the frontend test and build when frontend files change. + +## Commit conventions and git hooks + +Loomy follows Conventional Commits 1.0.0, enforced locally by commitlint, and runs lint-staged on every commit through husky. Relevant files at the repo root: + +- `package.json`: declares `husky`, `lint-staged`, `@commitlint/cli`, `@commitlint/config-conventional`. +- `.husky/pre-commit`: runs `npx lint-staged`. +- `.husky/commit-msg`: runs `npx --no -- commitlint --edit "$1"`. +- `.husky/pre-push`: runs mypy and pytest, or the frontend test and build, depending on which files changed. +- `commitlint.config.cjs`: enforces the allowed `type-enum`: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`. +- `.lintstagedrc.cjs`: per-ecosystem rules. Frontend files run Prettier and ESLint, Python files run ruff check and format via `uv`, root docs run Prettier. +- `.github/CODEOWNERS`: routes review requests to the right owners. + +Commit subject format (see CONTRIBUTING.md for the full policy): + +``` +(): +``` + +Suggested scopes used in this repo: `auth`, `workspaces`, `boards`, `elements`, `ws`, `i18n`, `ci`, `deps`. Keep the subject under 100 characters, use a lowercase type, and no trailing period. Write commits on behalf of the user in this format. + +## AI attribution + +AI coding tools are welcome on Loomy. When one helps produce a change, attribute it in two places. + +On the commit, add a `Co-Authored-By` trailer for each tool that contributed. Put it at the end of the commit body, after a blank line: + +``` +docs(agents): add AGENTS.md and AI attribution policy + +Co-Authored-By: Claude +``` + +Use the co-author identity each tool documents, in the `Name ` form. The two used most here: + +| Tool | Co-Authored-By trailer | +| -------------- | ----------------------------------------------------------------------- | +| Claude Code | `Co-Authored-By: Claude ` | +| GitHub Copilot | `Co-Authored-By: Copilot <198982749+Copilot@users.noreply.github.com>` | + +Claude Code may append a model-specific trailer such as `Co-Authored-By: Claude Opus 4.8 `. Keep whatever it adds. For any other tool (Cursor, Gemini CLI, and so on), use the identity that tool documents. + +In the pull request, add an "AI attribution" section to the description that lists each tool and what it did: + +``` +## AI attribution + +- Claude Code (Opus 4.8): drafted AGENTS.md and wired the Copilot and Gemini configs. +- GitHub Copilot: suggested edge-case tests for the auth module. +``` + +The person opening the PR stays the author and owns the change. AI tools are co-authors, not authors, so review everything an agent produces before you commit it. Write "None" in the PR section when no AI was involved. + +## Backend architecture + +Each feature module under `app/modules//` follows a strict layered flow: + +``` +router.py -> service.py -> repository.py -> database + HTTP business SQLAlchemy +``` + +- `schemas.py` holds the Pydantic request and response models. `model.py` holds the SQLAlchemy ORM. +- Never mix layers. Routers must not touch the database directly, and repositories must not hold business logic. +- Never return SQLAlchemy models from routers. Always map to a Pydantic response schema. +- Existing modules: `auth`, `users`, `workspaces` (with `members.py` and `invitations.py` helpers), `boards` (plus `board_star_repo.py` and `board_view_repo.py`), and `elements`. Extend these rather than adding parallel structures. +- Cross-cutting infra lives in `app/core/` (JWT, redis client, rate limit, security, logging), `app/db/` (SQLAlchemy base and session), and `app/websocket/` (manager and router). +- Database conventions: UUID primary keys, snake_case columns, `created_at` and `updated_at` timestamps, and indexes on frequently queried foreign keys (for example `boards.workspace_id`, `elements.board_id`). +- Auth uses JWT and bcrypt. OAuth providers (GitHub, Google) live in `app/modules/auth/oauth.py`. Protected routes depend on `app/api/deps.py`. + +### Real-time collaboration + +- Board WebSocket endpoint: `/api/ws/boards/{board_id}?token=...` (`app/websocket/router.py`). Auth is verified per connection, and membership is checked against the board's workspace. +- Broadcast events: `element.created`, `element.updated`, `element.deleted`, `cursor.moved`, and `peer.left` so remote cursors disappear when someone leaves. +- Broadcasting goes through Redis pub/sub (`app/websocket/manager.py` and `app/core/redis.py`). Never use in-process pub/sub directly, since it would break horizontal scaling. The subscribe path waits until Redis has actually subscribed before it returns. +- Element payloads are stored as JSONB in Postgres, so design endpoints where frequent or bulk updates stay cheap. +- The frontend reconnects a dropped board socket with backoff and resets its per-board caches on a board switch. +- Known gap: sync is currently ad-hoc broadcast, not CRDT. Yjs is the sanctioned path when introducing real conflict resolution (see the frontend section). + +### Alembic migrations (important) + +Do not create migration files or run `alembic revision` or `alembic upgrade`. The user manages migrations by hand. After editing any SQLAlchemy model, stop and tell the user to run the autogenerate and upgrade commands themselves. Do not write files under `alembic/versions/`. + +## Frontend architecture + +- Entry: `apps/frontend/src/main.tsx` -> `App.tsx` wraps the router in `HelmetProvider`, `ThemeProvider`, and `I18nProvider`. +- Routing: `src/routes/index.tsx`. Auth-gated pages use `components/ProtectedRoute.tsx`. +- State: Zustand stores in `src/stores/` (`authStore`, `dashboardStore`). Prefer Zustand for shared UI state over Context or ad-hoc `useState`. +- Pages live in `src/pages/{Landing,auth,dashboard,board}/`. Reusable UI lives in `src/components/{ui,layout,board,icons}/`. +- i18n: `src/i18n/` with EN, AZ, and RU locales and a `useTranslation` hook through `I18nContext`. Every new user-facing string must be localized. Locale loading guards against out-of-order resolution when the user switches language. +- Themes: light, dark, and soft (pastel) through `ThemeContext` and Tailwind 4. +- API base URL: `VITE_API_URL` (defaults to `http://localhost:8000`). The shared API client retries a token refresh on a network failure instead of treating it as a dead session. +- Token persistence: the app reads and clears a legacy `loomy-token` localStorage key on boot (see `App.tsx`). Keep that migration shim in place. + +### Canvas (Excalidraw) and sync (Yjs) + +- Use `@excalidraw/excalidraw` (the `Excalidraw` component, `excalidrawAPI`, `initialData`, `onChange`, `UIOptions`, `theme`). Persist the scene as an `excalidraw_snapshot` element (elements and appState) through the elements API. +- For collaborative sync (cursors, presence, document state), use Yjs shared types (`Y.Doc`, `Y.Map`, `Y.Array`) with the existing WebSocket transport. Avoid hand-rolled CRDT or diff/patch logic. +- Before you materially change canvas or collaboration behavior (tools, selection, multi-user cursors, undo, offline), read the Excalidraw and Yjs docs first and follow their recommended patterns. + +## UI design constraints + +The `.cursor/rules/uncodixify-ui.mdc` rule is load-bearing. When building UI, avoid the default AI aesthetic: no oversized rounded corners (20 to 32px), no floating glassmorphism sidebars, no corporate gradients, no eyebrow uppercase labels, no KPI-grid hero blocks, and no transform-on-hover animations. Aim for Linear, Raycast, Stripe, and GitHub: 240 to 260px fixed sidebars, 8 to 12px radii, subtle borders, and 100 to 200ms ease transitions. + +For colors, first reuse existing project colors (search before inventing one), otherwise pick from the palettes listed in that rule file. Do not invent ad-hoc color combinations. + +## Python style + +- `mypy` runs in strict mode, so every function needs type hints. +- `ruff` line length is 100, target `py312`. +- Inject the database session through FastAPI dependencies. Do not open sessions inside services or repositories, except inside the WebSocket handler, which uses `SessionLocal()` directly because it runs outside the request lifecycle. + +## Code comments + +Keep comments rare. Well-named identifiers and clear types already show what the code does, so a comment that restates them is noise that rots. Comment only when a reason is genuinely non-obvious: a load-bearing invariant, a real workaround for a specific bug or platform quirk, or behavior that would surprise a careful reader. When you do comment, keep it to a single line and never write multi-paragraph blocks or docstrings that repeat the signature. + +## Shell on Windows + +The `.cursor/rules/windows-powershell.mdc` rule prefers PowerShell for commands the user runs (for example `Copy-Item .env.example .env`, and `;` instead of `&&`). When the Claude Code harness reports the shell as bash, use bash syntax for tool calls but suggest the PowerShell equivalents when you hand the user commands to run themselves. + +## Community and project docs + +- `README.md`: user-facing product intro, install, features, tech badges, repobeats and star-history links. +- `CONTRIBUTING.md`: contributor workflow, coding standards, Conventional Commits policy, hook enforcement, and AI-assisted contribution rules. +- `CODE_OF_CONDUCT.md`: Contributor Covenant 2.1. +- `SECURITY.md`: private disclosure through GitHub Security Advisories or email. +- `.github/PULL_REQUEST_TEMPLATE.md` and `.github/ISSUE_TEMPLATE/*.yml`: YAML issue forms for bugs and features, plus `config.yml` disabling blank issues. +- License: GPL-3.0 (see `LICENSE`). Keep license notices intact when editing or generating source files. + +## Tool-specific rules + +Cursor users get detailed, glob-scoped rules under `.cursor/rules/`. They cover the same ground as this file in a Cursor-native format: API layering and REST conventions, database conventions, real-time WebSockets and Redis, Excalidraw and Yjs, Python standards, the UI constraints, and the Windows shell. Keep them in sync with this file when conventions change. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..dfb42d3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,14 @@ +# CLAUDE.md + +This file guides Claude Code (claude.ai/code) when working in this repository. The shared guidance for the whole project lives in AGENTS.md, imported below, so there is a single source of truth for every agent. Read it first. + +@AGENTS.md + +## Claude Code specifics + +A few things that apply when working here with Claude Code: + +- The canonical guide is AGENTS.md. Keep it current when project conventions change, and do not duplicate its content here. +- Machine-local notes live in `memory/` (gitignored). Do not commit that folder. +- When an AI tool helps with a change, follow the AI attribution rules in AGENTS.md: a `Co-Authored-By` trailer on the commit and an attribution note in the PR description. +- Write almost no code comments, per the Code comments section in AGENTS.md. Comment only a non-obvious reason, never restate what the code does. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bfcde28..95b77b0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -152,6 +152,15 @@ feat(auth)!: require email verification before login Add a body when the _why_ is not obvious from the subject. Wrap at 72 columns. Reference issues in a footer (`Closes #123`, `Refs #456`). +### AI-assisted contributions + +AI coding tools are welcome. When one helps with a change, attribute it: + +- Add a `Co-Authored-By: ` trailer to the commit for each tool that contributed. +- List the tools and what they did in the pull request description under an "AI attribution" heading. Write "None" when no AI was involved. + +You stay the author and are responsible for the change. Review everything an agent produces before you commit it. See [AGENTS.md](./AGENTS.md) for the exact trailers and format. + ### Enforcement Loomy enforces Conventional Commits locally via [commitlint](https://commitlint.js.org/) and runs [lint-staged](https://github.com/lint-staged/lint-staged) before each commit via [husky](https://typicode.github.io/husky/) git hooks. diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..055d344 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,10 @@ +# GEMINI.md + +This file guides the Gemini CLI when working in this repository. The shared guidance for the whole project lives in AGENTS.md, imported below, so there is a single source of truth for every agent. Read it and follow it. + +@AGENTS.md + +## Gemini notes + +- The canonical guide is AGENTS.md. Keep it current when conventions change instead of duplicating it here. +- When Gemini helps with a change, add a `Co-Authored-By` trailer on the commit and an attribution note in the PR description, as described in the AI attribution section of AGENTS.md. From 9d311626a6d9e4dc25dbdc9fc9b8721d123323ab Mon Sep 17 00:00:00 2001 From: martian56 Date: Fri, 10 Jul 2026 13:08:52 +0400 Subject: [PATCH 2/4] chore: bump frontend version to 0.4.1 Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/frontend/package-lock.json | 4 ++-- apps/frontend/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/frontend/package-lock.json b/apps/frontend/package-lock.json index c79a1c1..a0dc616 100644 --- a/apps/frontend/package-lock.json +++ b/apps/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "loomy", - "version": "0.4.0", + "version": "0.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "loomy", - "version": "0.4.0", + "version": "0.4.1", "dependencies": { "@excalidraw/excalidraw": "^0.18.0", "@tailwindcss/vite": "^4.2.1", diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 89d1e88..b650449 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -1,7 +1,7 @@ { "name": "loomy", "private": true, - "version": "0.4.0", + "version": "0.4.1", "type": "module", "scripts": { "dev": "vite", From 97719ba5ac5cf10f38820241c675b600f5d50641 Mon Sep 17 00:00:00 2001 From: martian56 Date: Fri, 10 Jul 2026 13:34:25 +0400 Subject: [PATCH 3/4] style(frontend): format api.test.ts with prettier Pre-existing formatting violation surfaced by prettier --check in UI CI. Formatting only, no logic change. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/frontend/src/lib/api.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/lib/api.test.ts b/apps/frontend/src/lib/api.test.ts index a27480d..9ec1c10 100644 --- a/apps/frontend/src/lib/api.test.ts +++ b/apps/frontend/src/lib/api.test.ts @@ -69,7 +69,10 @@ describe("apiFetch refresh error handling", () => { .mockResolvedValueOnce(new Response(null, { status: 401 })) .mockRejectedValueOnce(new TypeError("network error")) .mockResolvedValueOnce( - jsonResponse({ access_token: "new-token", refresh_token: "refresh-token-2" }), + jsonResponse({ + access_token: "new-token", + refresh_token: "refresh-token-2", + }), ) .mockResolvedValueOnce(new Response(null, { status: 200 })); vi.stubGlobal("fetch", fetchMock); From c0f5d299fc7df1603fe0f681df04d17f8cd91610 Mon Sep 17 00:00:00 2001 From: martian56 Date: Fri, 10 Jul 2026 13:34:52 +0400 Subject: [PATCH 4/4] chore: stop tracking .cursor rules Keep Cursor rules local and gitignored. AGENTS.md is the shared, tool-agnostic guide, so the Cursor-specific rules do not need to ship in the repo. Co-Authored-By: Claude Opus 4.8 (1M context) --- .cursor/rules/alembic-no-auto-migrations.mdc | 16 ----- .cursor/rules/api-authentication.mdc | 14 ---- .cursor/rules/api-boards-elements.mdc | 37 ---------- .cursor/rules/api-database-conventions.mdc | 31 --------- .cursor/rules/api-layer-responsibilities.mdc | 27 -------- .cursor/rules/api-modular-architecture.mdc | 27 -------- .cursor/rules/api-realtime-websockets.mdc | 28 -------- .cursor/rules/api-redis.mdc | 18 ----- .cursor/rules/api-rest-conventions.mdc | 32 --------- .cursor/rules/excalidraw-canvas.mdc | 24 ------- .cursor/rules/extend-dont-abstract.mdc | 9 --- .cursor/rules/loomy-project-context.mdc | 34 --------- .cursor/rules/loomy-tech-stack.mdc | 20 ------ .cursor/rules/python-standards.mdc | 15 ---- .cursor/rules/uncodixify-ui.mdc | 73 -------------------- .cursor/rules/windows-powershell.mdc | 27 -------- .cursor/rules/yjs-sync.mdc | 27 -------- .gitignore | 1 + 18 files changed, 1 insertion(+), 459 deletions(-) delete mode 100644 .cursor/rules/alembic-no-auto-migrations.mdc delete mode 100644 .cursor/rules/api-authentication.mdc delete mode 100644 .cursor/rules/api-boards-elements.mdc delete mode 100644 .cursor/rules/api-database-conventions.mdc delete mode 100644 .cursor/rules/api-layer-responsibilities.mdc delete mode 100644 .cursor/rules/api-modular-architecture.mdc delete mode 100644 .cursor/rules/api-realtime-websockets.mdc delete mode 100644 .cursor/rules/api-redis.mdc delete mode 100644 .cursor/rules/api-rest-conventions.mdc delete mode 100644 .cursor/rules/excalidraw-canvas.mdc delete mode 100644 .cursor/rules/extend-dont-abstract.mdc delete mode 100644 .cursor/rules/loomy-project-context.mdc delete mode 100644 .cursor/rules/loomy-tech-stack.mdc delete mode 100644 .cursor/rules/python-standards.mdc delete mode 100644 .cursor/rules/uncodixify-ui.mdc delete mode 100644 .cursor/rules/windows-powershell.mdc delete mode 100644 .cursor/rules/yjs-sync.mdc diff --git a/.cursor/rules/alembic-no-auto-migrations.mdc b/.cursor/rules/alembic-no-auto-migrations.mdc deleted file mode 100644 index 738d05e..0000000 --- a/.cursor/rules/alembic-no-auto-migrations.mdc +++ /dev/null @@ -1,16 +0,0 @@ ---- -description: Do not create Alembic migration files; the user creates them manually -globs: api/app/**/*.py -alwaysApply: true ---- - -# Alembic Migrations - -**Do not create migration files.** The user manages Alembic migrations manually. - -## Workflow - -- When adding or changing SQLAlchemy models (columns, tables, indexes), update the model code only. -- Do **not** create new files in `alembic/versions/`. -- Do **not** run `alembic revision` or `alembic upgrade`. -- After model changes, tell the user to run: `alembic revision --autogenerate -m "description"` and `alembic upgrade head`. diff --git a/.cursor/rules/api-authentication.mdc b/.cursor/rules/api-authentication.mdc deleted file mode 100644 index 98c5bf1..0000000 --- a/.cursor/rules/api-authentication.mdc +++ /dev/null @@ -1,14 +0,0 @@ ---- -description: JWT authentication and password security -globs: api/app/**/* -alwaysApply: false ---- - -# Authentication - -## Requirements - -- **JWT** for authentication. -- **bcrypt** for password hashing. -- **Never store plaintext passwords.** -- **Protected routes** must require authentication. diff --git a/.cursor/rules/api-boards-elements.mdc b/.cursor/rules/api-boards-elements.mdc deleted file mode 100644 index b4e8d10..0000000 --- a/.cursor/rules/api-boards-elements.mdc +++ /dev/null @@ -1,37 +0,0 @@ ---- -description: Boards and canvas elements data model -globs: api/app/**/* -alwaysApply: false ---- - -# Boards and Elements - -**Boards** are collaborative canvases. Each board contains many **elements**. - -## Element Types - -- Shapes -- Sticky notes -- Text -- Arrows -- Connectors - -## Storage - -Store element properties as **JSONB** in PostgreSQL. - -Example element data: - -```json -{ - "x": 120, - "y": 240, - "width": 300, - "height": 120, - "text": "Hello" -} -``` - -## API Design - -Design the API so that **updates to elements can be frequent and efficient** (e.g. bulk updates, delta sync, or patch endpoints). diff --git a/.cursor/rules/api-database-conventions.mdc b/.cursor/rules/api-database-conventions.mdc deleted file mode 100644 index b002d41..0000000 --- a/.cursor/rules/api-database-conventions.mdc +++ /dev/null @@ -1,31 +0,0 @@ ---- -description: PostgreSQL, SQLAlchemy, and Alembic conventions -globs: api/app/**/* -alwaysApply: false ---- - -# Database Conventions - -The project uses **PostgreSQL** with **SQLAlchemy** and **Alembic**. - -## Model Changes Workflow - -1. Ensure the SQLAlchemy model is correct. -2. Generate an Alembic migration reflecting the change. -3. Migrations must be **idempotent and safe**. - -## Database Rules - -| Rule | Details | -|------|---------| -| **Primary keys** | Use UUID | -| **Naming** | snake_case in the database | -| **Timestamps** | Include `created_at` and `updated_at` | -| **Indexes** | Add indexes for frequently queried columns | - -## Example Indexed Columns - -- `users.email` -- `users.username` -- `boards.workspace_id` -- `elements.board_id` diff --git a/.cursor/rules/api-layer-responsibilities.mdc b/.cursor/rules/api-layer-responsibilities.mdc deleted file mode 100644 index 8cbb34f..0000000 --- a/.cursor/rules/api-layer-responsibilities.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: API layer flow and responsibility separation -globs: api/app/**/* -alwaysApply: false ---- - -# API Layer Architecture - -All backend features follow this flow: - -``` -router → service → repository → database -``` - -## Responsibilities - -| Layer | File | Responsibility | -|-------|------|----------------| -| **router** | `router.py` | HTTP requests and responses only | -| **service** | `service.py` | Business logic | -| **repository** | `repository.py` | Database access via SQLAlchemy | -| **schemas** | `schemas.py` | Request/response models (Pydantic) | -| **model** | `model.py` | SQLAlchemy database models | - -## Rule - -**Never mix these responsibilities.** Each layer has a single concern. diff --git a/.cursor/rules/api-modular-architecture.mdc b/.cursor/rules/api-modular-architecture.mdc deleted file mode 100644 index d356e58..0000000 --- a/.cursor/rules/api-modular-architecture.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: API modular architecture and workflow -globs: api/app/**/* -alwaysApply: false ---- - -# API Architecture – Modular Structure - -Before generating or modifying any API code, **read the entire `api/app` directory** and understand the existing folder and module structure. - -## Module Layout - -Each feature lives in `app/modules//`. Every module typically contains: - -| File | Purpose | -|------|---------| -| `router.py` | HTTP endpoints | -| `service.py` | Business logic | -| `repository.py` | Data access | -| `schemas.py` | Pydantic request/response models | -| `model.py` | SQLAlchemy model(s) | - -## Rules - -- **Never introduce a different structure.** Follow this layout exactly. -- **Extend existing modules** when adding functionality—don’t create unrelated folders. -- **When unsure where code belongs**, inspect similar modules and mirror their structure. diff --git a/.cursor/rules/api-realtime-websockets.mdc b/.cursor/rules/api-realtime-websockets.mdc deleted file mode 100644 index f736e4b..0000000 --- a/.cursor/rules/api-realtime-websockets.mdc +++ /dev/null @@ -1,28 +0,0 @@ ---- -description: Real-time collaboration and WebSocket events -globs: api/app/**/* -alwaysApply: false ---- - -# Real-Time Collaboration - -Loomy supports **real-time collaboration**. Board updates should eventually be **broadcast through WebSockets**. - -## API Endpoints - -Support these operations for elements: - -- **Creating** elements -- **Updating** elements -- **Deleting** elements - -## WebSocket Events - -Events to broadcast: - -| Event | Purpose | -|-------|---------| -| `element.created` | New element added | -| `element.updated` | Element changed | -| `element.deleted` | Element removed | -| `cursor.moved` | User cursor position | diff --git a/.cursor/rules/api-redis.mdc b/.cursor/rules/api-redis.mdc deleted file mode 100644 index 264f220..0000000 --- a/.cursor/rules/api-redis.mdc +++ /dev/null @@ -1,18 +0,0 @@ ---- -description: Redis usage for real-time, caching, and rate limiting -globs: api/app/**/* -alwaysApply: false ---- - -# Redis - -Redis is used for: - -- **WebSocket pub/sub** -- **Presence tracking** -- **Caching** -- **Rate limiting** - -## Real-Time and Event Broadcasting - -When implementing **real-time collaboration** or **event broadcasting**, use **Redis pub/sub**—not direct in-memory communication. diff --git a/.cursor/rules/api-rest-conventions.mdc b/.cursor/rules/api-rest-conventions.mdc deleted file mode 100644 index 73fd4b6..0000000 --- a/.cursor/rules/api-rest-conventions.mdc +++ /dev/null @@ -1,32 +0,0 @@ ---- -description: REST API conventions and response schemas -globs: api/app/**/* -alwaysApply: false ---- - -# REST API Conventions - -All APIs must follow REST best practices. - -## Endpoint Conventions - -| Method | Path | Purpose | -|--------|------|---------| -| GET | `/api/resource` | List resources | -| GET | `/api/resource/{id}` | Get by ID | -| POST | `/api/resource` | Create | -| PATCH | `/api/resource/{id}` | Update | -| DELETE | `/api/resource/{id}` | Delete | - -## Pagination - -Use pagination for list endpoints: - -``` -GET /api/boards?page=1&limit=20 -``` - -## Response Schemas - -- **Never return database models directly.** -- **Always return Pydantic response schemas.** diff --git a/.cursor/rules/excalidraw-canvas.mdc b/.cursor/rules/excalidraw-canvas.mdc deleted file mode 100644 index 8e1b52c..0000000 --- a/.cursor/rules/excalidraw-canvas.mdc +++ /dev/null @@ -1,24 +0,0 @@ ---- -description: Use Excalidraw for the whiteboard canvas and research its APIs before making canvas changes -globs: apps/frontend/src/** -alwaysApply: false ---- - -# Excalidraw Canvas & Research Rule - -- **Use `@excalidraw/excalidraw` for the board/canvas UI**: - - When implementing or updating any whiteboard, board, or drawing canvas in the frontend, prefer the official Excalidraw React component and APIs (`Excalidraw`, `initialData`, `onChange`, `excalidrawAPI`, `UIOptions`, `theme`, etc.). - - Avoid building a custom canvas/interaction system from scratch unless there is a very strong, documented reason. - -- **Deep research before significant changes**: - - Before introducing or modifying core canvas behavior (selection, tools, sidebar, persistence, multi-user cursors, etc.), the agent should: - - Review the latest [Excalidraw docs](https://docs.excalidraw.com) and API (props, `excalidrawAPI`, `restore`, `sceneCoordsToViewportCoords`, etc.). - - Look for recommended patterns (initialData, onChange, updateScene, CSS variable overrides) in Excalidraw’s documentation. - - Align implementations with those patterns where practical. - -- **Integration expectations**: - - Canvas state (elements + appState) should be persisted via the backend’s elements API (`excalidraw_snapshot` type) and synced in real time via WebSocket when needed. - - Keep the server-side boards/elements model aligned with Excalidraw’s scene data (elements array + appState object). - -- **When in doubt**: - - If unsure how to implement a canvas feature, explore the Excalidraw docs and examples first, summarize options, then choose the approach that best fits Loomy’s backend and real-time architecture. diff --git a/.cursor/rules/extend-dont-abstract.mdc b/.cursor/rules/extend-dont-abstract.mdc deleted file mode 100644 index b779450..0000000 --- a/.cursor/rules/extend-dont-abstract.mdc +++ /dev/null @@ -1,9 +0,0 @@ ---- -description: Prefer extending modules over new abstractions -alwaysApply: true ---- - -# Code Generation Preference - -- **Extend existing modules** instead of creating new abstractions. -- Goal: **maintainability and readability** for an open-source project. diff --git a/.cursor/rules/loomy-project-context.mdc b/.cursor/rules/loomy-project-context.mdc deleted file mode 100644 index 29e512d..0000000 --- a/.cursor/rules/loomy-project-context.mdc +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Loomy project context and API requirements -alwaysApply: true ---- - -# Loomy – Project Context - -Loomy is an open-source, self-hostable collaborative whiteboard similar to Miro. - -## User Capabilities - -- Create workspaces and boards -- Collaborate in real time on infinite canvas boards -- Place elements: shapes, text, connectors, sticky notes -- Invite other users and work together - -## Backend API Requirements - -The API must support: - -| Domain | Requirements | -|--------|--------------| -| **Authentication** | Auth flows, sessions, tokens | -| **User accounts** | CRUD, profiles, identity | -| **Workspaces** | Multi-tenant workspaces, membership | -| **Boards** | Boards per workspace, metadata | -| **Board elements** | Shapes, text, connectors, sticky notes | -| **Real-time collaboration** | WebSockets or similar for live updates | -| **Permissions** | Access control for workspaces, boards, elements | - -## Design Principles - -- **Scalability**: Boards may contain many elements; design for large datasets and pagination. -- **Concurrency**: Support multiple concurrent users per board; consider optimistic locking, conflict resolution, or CRDTs for real-time sync. diff --git a/.cursor/rules/loomy-tech-stack.mdc b/.cursor/rules/loomy-tech-stack.mdc deleted file mode 100644 index dd3c52a..0000000 --- a/.cursor/rules/loomy-tech-stack.mdc +++ /dev/null @@ -1,20 +0,0 @@ ---- -description: Loomy recommended tech stack – Zustand, Yjs, Excalidraw, FastAPI -alwaysApply: true ---- - -# Loomy Tech Stack - -Use this stack for new features and architectural decisions: - -| Layer | Technology | -|-------|------------| -| **UI State** | Zustand | -| **Realtime Collaboration** | Yjs | -| **Canvas Engine** | Excalidraw | -| **Backend** | FastAPI | - -- Prefer **Zustand** over Redux, Context, or ad-hoc useState for shared UI state. -- Use **Yjs** for collaborative sync (cursors, presence, document state) when applicable. -- Use **Excalidraw** (`@excalidraw/excalidraw`) for the whiteboard canvas; persist scene as `excalidraw_snapshot` (elements + appState) via the elements API. -- Keep the **FastAPI** backend; do not introduce other backend frameworks. diff --git a/.cursor/rules/python-standards.mdc b/.cursor/rules/python-standards.mdc deleted file mode 100644 index c3254d3..0000000 --- a/.cursor/rules/python-standards.mdc +++ /dev/null @@ -1,15 +0,0 @@ ---- -description: Python coding standards -globs: **/*.py -alwaysApply: false ---- - -# Python Standards - -- Follow **modern Python practices**. -- Use **type hints everywhere**. -- Keep functions **small and focused**. -- **Avoid duplicated logic** (extract helpers, reuse). -- Use **dependency injection** for database sessions. - -**Prefer clarity over cleverness.** diff --git a/.cursor/rules/uncodixify-ui.mdc b/.cursor/rules/uncodixify-ui.mdc deleted file mode 100644 index 2b68ab2..0000000 --- a/.cursor/rules/uncodixify-ui.mdc +++ /dev/null @@ -1,73 +0,0 @@ ---- -description: Avoid Codex AI aesthetic; build human-designed, functional UI like Linear/Raycast/Stripe/GitHub -globs: apps/frontend/**/*.{tsx,ts,css} -alwaysApply: false ---- - -# Uncodixify UI - -When building UI, avoid the default AI aesthetic. If a choice feels like a default AI move, ban it and pick the harder, cleaner option. - -## Keep It Normal - -- **Sidebars**: 240–260px fixed, solid background, simple border-right, no floating shells -- **Headers**: Simple h1/h2, no eyebrows, no uppercase labels, no gradient text -- **Buttons**: Solid fills or simple borders, 8–10px radius max, no pills, no gradients -- **Cards**: Simple containers, 8–12px radius, subtle borders, no shadows over 8px blur -- **Forms/Inputs**: Standard inputs, labels above fields, solid borders, simple focus states -- **Modals/Dropdowns**: Centered overlay, simple backdrop, no slide-in animations -- **Typography**: System or simple sans-serif, 14–16px body, clear hierarchy -- **Spacing**: Consistent 4/8/12/16/24/32px scale -- **Shadows**: Subtle `0 2px 8px rgba(0,0,0,0.1)` max -- **Transitions**: 100–200ms ease, no bouncy or transform effects - -Think Linear, Raycast, Stripe, GitHub. They don't try to grab attention; they just work. - -## Hard No - -- Oversized rounded corners (20–32px), pill overload -- Floating glassmorphism, soft corporate gradients, generic dark SaaS -- Serif headline + sans fallback as "premium" shortcut -- `Inter`, `Roboto`, `Segoe UI`, `Arial`, `Trebuchet MS` unless already in use -- Hero sections inside dashboards; metric-card grid as first instinct -- Eyebrow labels (``), decorative copy, "live pulse" / "operator checklist" -- Colors trending blue; prefer dark muted palettes -- Structure: `` + `` + decorative copy blocks - -## Specifically Banned - -- Border radii 20–32px everywhere; repeating rounded rectangle on everything -- Floating detached sidebar with rounded outer shell -- Canvas/donut charts with no product reason; KPI grids as default layout -- Eyebrow labels (e.g. "MARCH SNAPSHOT" uppercase + letter-spacing) -- Transform animations on hover; dramatic shadows (e.g. 0 24px 60px) -- Pipeline bars with gradient fills; tag badges on every status -- Brand marks with gradient backgrounds; nav badges for counts -- Right rail panels; multiple nested panel types - -## Color Priority - -1. **Highest**: Use existing project colors (search the codebase first) -2. **Else**: Pick from predefined palettes below -3. **Never**: Invent random combinations unless explicitly requested - -### Dark Palettes (Background / Surface / Primary / Secondary / Accent / Text) - -| Name | Values | -|------|--------| -| Midnight Canvas | `#0a0e27` / `#151b3d` / `#6c8eff` / `#a78bfa` / `#f472b6` / `#e2e8f0` | -| Obsidian Depth | `#0f0f0f` / `#1a1a1a` / `#00d4aa` / `#00a3cc` / `#ff6b9d` / `#f5f5f5` | -| Slate Noir | `#0f172a` / `#1e293b` / `#38bdf8` / `#818cf8` / `#fb923c` / `#f1f5f9` | -| Void Space | `#0d1117` / `#161b22` / `#58a6ff` / `#79c0ff` / `#f78166` / `#c9d1d9` | - -### Light Palettes - -| Name | Values | -|------|--------| -| Cloud Canvas | `#fafafa` / `#ffffff` / `#2563eb` / `#7c3aed` / `#dc2626` / `#0f172a` | -| Pearl Minimal | `#f8f9fa` / `#ffffff` / `#0066cc` / `#6610f2` / `#ff6b35` / `#212529` | -| Porcelain Clean | `#f9fafb` / `#ffffff` / `#4f46e5` / `#8b5cf6` / `#ec4899` / `#111827` | - -## Internal Check - -Before implementing UI, list what you would normally do (gradients, pills, hero blocks, eyebrow labels, etc.). Then do the opposite: follow Uncodixify. Replicate Figma/designer-made components; don't invent your own. diff --git a/.cursor/rules/windows-powershell.mdc b/.cursor/rules/windows-powershell.mdc deleted file mode 100644 index f9c8760..0000000 --- a/.cursor/rules/windows-powershell.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Use PowerShell instead of bash on Windows -alwaysApply: true ---- - -# Windows PowerShell Commands - -This project runs on Windows. When suggesting or running terminal commands: -- **Use PowerShell**, not bash -- Avoid bash-specific syntax (e.g. `&&`, `;`, `$VAR`, `[[ ]]`, `rm -rf`) - -## Examples - -| Instead of (bash) | Use (PowerShell) | -|-------------------|------------------| -| `cd /path && npm install` | `cd \path; npm install` | -| `rm -rf node_modules` | `Remove-Item -Recurse -Force node_modules` | -| `export VAR=value` | `$env:VAR = "value"` | -| `ls -la` | `Get-ChildItem` or `dir` | -| `cat file.txt` | `Get-Content file.txt` | -| `mkdir -p foo/bar` | `New-Item -ItemType Directory -Force foo\bar` | -| `grep "pattern" file` | `Select-String "pattern" file` | - -## Notes -- Use backslash `\` for Windows paths, or forward slash `/` (PowerShell accepts both) -- Chain commands with `;` instead of `&&` -- For multiline commands, use `` ` `` (backtick) as line continuation in PowerShell diff --git a/.cursor/rules/yjs-sync.mdc b/.cursor/rules/yjs-sync.mdc deleted file mode 100644 index 933c57f..0000000 --- a/.cursor/rules/yjs-sync.mdc +++ /dev/null @@ -1,27 +0,0 @@ ---- -description: Use Yjs for collaborative sync and research its CRDT patterns before changing collaboration logic -globs: apps/frontend/src/**/*.{ts,tsx} -alwaysApply: false ---- - -# Yjs Collaboration & Sync Rule - -- **Use Yjs for real-time collaboration state**: - - When implementing or updating collaborative board, canvas, or document state in the frontend, use Yjs as the primary CRDT/sync layer. - - Avoid creating custom ad-hoc sync protocols or state-merging logic when Yjs can model the behavior. - -- **Deep research before significant collaboration changes**: - - Before changing core collaboration behavior (multi-user editing, cursors, presence, history/undo, conflict resolution, offline support), the agent should: - - Review the latest Yjs documentation and official guides. - - Study recommended patterns for shared types (e.g., `Y.Doc`, `Y.Map`, `Y.Array`), awareness/presence, and persistence/adapters (e.g., WebSocket, IndexedDB). - - Prefer established Yjs patterns over bespoke CRDT or custom diff/patch logic. - -- **Integration expectations**: - - Model board and element state so it can be represented as Yjs shared types and synchronized via the existing backend WebSocket and REST APIs. - - Keep a clear separation between: - - The canonical collaborative state (Yjs document and shared types), and - - Any derived React/UI state, which should subscribe to and update based on Yjs changes. - -- **When in doubt**: - - If unsure how to implement a collaborative feature with Yjs, the agent should first explore Yjs docs and examples (and well-known integrations like Excalidraw + Yjs where applicable), summarize viable approaches, and then choose the one that best aligns with Loomy’s backend and real-time architecture. - diff --git a/.gitignore b/.gitignore index bce0284..7b49577 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ !.env.example scripts/ # IDE and editor +.cursor/ .vscode/ memory/ .idea/