diff --git a/.cursor/commands/start-dev.md b/.cursor/commands/start-dev.md new file mode 100644 index 0000000..1435aa2 --- /dev/null +++ b/.cursor/commands/start-dev.md @@ -0,0 +1,32 @@ +# Start dev server + +Run the Excalidraw web app locally for interactive development. + +## Do this + +1. Use the repository root as the working directory (this monorepo uses Yarn workspaces). +2. Ensure dependencies are installed: `yarn install` (Node **>= 18** per `package.json`). +3. Start the Vite dev server: + +```bash +yarn start +``` + +This runs `yarn --cwd ./excalidraw-app start`. + +## When packages need a rebuild + +If you changed low-level packages and see resolution or build issues, build workspace packages first, then start: + +```bash +yarn build:packages +yarn start +``` + +## Orientation + +- Host app: `excalidraw-app/` +- Core editor package: `packages/excalidraw/` +- Other packages: `packages/common`, `packages/element`, `packages/math` + +Respect project rules: canvas rendering and `actionManager` state (see `.cursor/rules/project-architecture.mdc`). diff --git a/.cursor/commands/test-watch.md b/.cursor/commands/test-watch.md new file mode 100644 index 0000000..156bc1e --- /dev/null +++ b/.cursor/commands/test-watch.md @@ -0,0 +1,44 @@ +# Test watch (Vitest) + +Run unit tests in watch mode while editing—default workflow for `packages/*` and app code covered by Vitest. + +## Do this + +From the repository root: + +```bash +yarn test +``` + +This runs `vitest` (watch mode is the typical default for local iteration). + +## Useful variants + +**Single run (no watch), e.g. for scripts or CI-like local checks:** + +```bash +yarn test:app --watch=false +``` + +**Update snapshots** (only when you intentionally changed expected output): + +```bash +yarn test:update +``` + +**Coverage:** + +```bash +yarn test:coverage +``` + +**Vitest UI:** + +```bash +yarn test:ui +``` + +## Notes + +- Config: `vitest.config.mts` (aliases for `@excalidraw/*` packages). +- For the **full** gate including typecheck, ESLint, and Prettier, use `/verify-ci` instead of only Vitest. diff --git a/.cursor/commands/verify-ci.md b/.cursor/commands/verify-ci.md new file mode 100644 index 0000000..ab772c3 --- /dev/null +++ b/.cursor/commands/verify-ci.md @@ -0,0 +1,35 @@ +# Verify like CI (full check) + +Run the full pre-merge validation suite before a PR or after large refactors. + +## Do this + +From the repository root: + +```bash +yarn test:all +``` + +This script runs, in order: + +| Step | Script | What it runs | +|------|--------|----------------| +| 1 | `yarn test:typecheck` | `tsc` | +| 2 | `yarn test:code` | ESLint (`.js`, `.ts`, `.tsx`, max warnings 0) | +| 3 | `yarn test:other` | Prettier `--list-different` on configured globs | +| 4 | `yarn test:app --watch=false` | Vitest once (no watch) | + +## If something fails + +- **Typecheck**: fix TypeScript errors; re-run `yarn test:typecheck` for a fast loop. +- **ESLint**: auto-fix where safe: `yarn fix:code`, then re-run `yarn test:code`. +- **Prettier**: `yarn fix:other`, then re-run `yarn test:other`. +- **Vitest**: run a single file or pattern with Vitest’s CLI options if needed. + +## One-shot “fix formatting + lint” (optional) + +```bash +yarn fix +``` + +Then run `yarn test:all` again to confirm. diff --git a/.cursor/mcp.json b/.cursor/mcp.json new file mode 100644 index 0000000..a101560 --- /dev/null +++ b/.cursor/mcp.json @@ -0,0 +1,7 @@ +{ + "mcpServers": { + "context7": { + "url": "https://mcp.context7.com/mcp" + } + } +} diff --git a/.cursor/rules/conventions.mdc b/.cursor/rules/conventions.mdc new file mode 100644 index 0000000..1454ee2 --- /dev/null +++ b/.cursor/rules/conventions.mdc @@ -0,0 +1,27 @@ +--- +description: "Code conventions for components and utilities" +globs: "packages/**/*.ts,packages/**/*.tsx" +alwaysApply: false +--- +# Code Conventions +## Components +- Functional components + hooks ONLY (no class components) +- Props type alias: `{ComponentName}Props` +- Named exports only (no default exports) +- Colocated tests: `ComponentName.test.tsx` +## TypeScript +- Strict mode — no `any`, no `@ts-ignore` +- Prefer `type` over `interface` for simple and props object types +- Import types: `import type { X } from "..."` + +## Excalidraw-specific +- Prefer `@excalidraw/*` package imports (or direct relative module imports) over barrel shortcuts that break package boundaries. +- Do not import `jotai` directly in package code; use project-specific modules (`editor-jotai` / `app-jotai`) where applicable. + +## How to verify + +- `yarn test:typecheck` — TypeScript must pass (no `@ts-ignore` workarounds). +- `yarn test:code` — ESLint must pass with `--max-warnings=0` (includes restricted-import checks). +- `rg "^\s*export\s+default\s+" packages --glob "*.{ts,tsx}"` — should return no new matches in changed files. +- `yarn test:other` — Prettier check on configured globs. +- `yarn test:app --watch=false` — tests for touched components/utilities. diff --git a/.cursor/rules/data-restore.mdc b/.cursor/rules/data-restore.mdc new file mode 100644 index 0000000..87b4b01 --- /dev/null +++ b/.cursor/rules/data-restore.mdc @@ -0,0 +1,25 @@ +--- +description: "Import/export, restore, and file-format compatibility" +globs: "packages/excalidraw/data/**" +alwaysApply: false +--- +# Data layer (`packages/excalidraw/data`) + +## Compatibility +- `.excalidraw` and library formats are **user data contracts**. Prefer extending with versioned migration steps instead of breaking existing files. +- `restore.ts` and related flows are especially sensitive — coordinate with `.cursor/rules/do-not-touch.mdc` before large edits. + +## Validation +- Validate incoming data shape and reject or normalize unknown fields rather than assuming trust. + +## Side effects +- File IO, encryption, and backend calls must preserve existing privacy and error semantics; do not silently drop user content. + +## Testing +- Add or update tests when changing serialization, migration, or restore behavior; run `yarn test:all` before merge. + +## How to verify + +- `yarn test:all` — typecheck, lint, prettier, and Vitest (including data/restore tests). +- `yarn test:app --watch=false` — focused pass if you only touched tests under `packages/excalidraw/data/`. +- Manual: load old `.excalidraw` samples, export/re-import, and open library flows if library/JSON handling changed. diff --git a/.cursor/rules/do-not-touch.mdc b/.cursor/rules/do-not-touch.mdc new file mode 100644 index 0000000..26eb667 --- /dev/null +++ b/.cursor/rules/do-not-touch.mdc @@ -0,0 +1,21 @@ +--- +description: "Protected files that should not be modified" +alwaysApply: true +--- +# Protected Files +NEVER modify these files without explicit approval: +- `packages/excalidraw/scene/Renderer.ts` — render pipeline +- `packages/excalidraw/data/restore.ts` — file format compatibility +- `packages/excalidraw/actions/manager.tsx` — action dispatch system +- `packages/excalidraw/types.ts` — core type definitions +Changes to protected files require: +1. Full understanding of dependencies +2. Running complete test suite +3. Manual QA verification + +## How to verify + +- `yarn test:all` — must pass before any change to the listed paths. +- After edits to `Renderer.ts` or `types.ts`: run `yarn start` and manually smoke-test canvas rendering, selection, and export. +- After edits to `restore.ts`: import/export `.excalidraw` files and library JSON; confirm round-trip and backward compatibility. +- After edits to `manager.tsx`: exercise menus, shortcuts, and actions that flow through `ActionManager` (e.g. load/save, theme, zoom). \ No newline at end of file diff --git a/.cursor/rules/excalidraw-app.mdc b/.cursor/rules/excalidraw-app.mdc new file mode 100644 index 0000000..c935a40 --- /dev/null +++ b/.cursor/rules/excalidraw-app.mdc @@ -0,0 +1,28 @@ +--- +description: "Host app (Vite shell) — not the core editor package" +globs: "excalidraw-app/**" +alwaysApply: false +--- +# excalidraw-app (host application) + +## Role +- This package is the **deployed web shell** (Vite, PWA, collaboration hooks, Firebase, etc.). Core drawing logic lives in `packages/excalidraw/` — avoid duplicating editor behavior here. + +## State +- App-level UI may use local patterns (e.g. Jotai in this repo). Do **not** introduce Redux/Zustand for editor state; editor state stays in Excalidraw’s `actionManager` / app state (`packages/excalidraw`). + +## Build and run +- Dev server: `yarn start` from monorepo root (runs this package’s `start` script). +- Prefer `yarn --cwd excalidraw-app` only when docs/scripts already use that form. + +## Networking +- Socket / collaboration / storage integrations: preserve error handling, reconnection, and privacy assumptions; do not weaken E2E or room behavior without explicit product review. + +## Dependencies +- No new npm packages without explicit approval (see root `package.json` / team process). + +## How to verify + +- `yarn start` — dev server loads; exercise collaboration/PWA features if relevant to your change. +- `yarn build` or `yarn build:app` — production build succeeds when your change affects bundling or env. +- `yarn test:all` — full repo checks before PR. diff --git a/.cursor/rules/i18n-locales.mdc b/.cursor/rules/i18n-locales.mdc new file mode 100644 index 0000000..3136304 --- /dev/null +++ b/.cursor/rules/i18n-locales.mdc @@ -0,0 +1,25 @@ +--- +description: "Translations and locale JSON files" +globs: "packages/excalidraw/locales/**" +alwaysApply: false +--- +# i18n (locales) + +## Files +- Locale data lives in `packages/excalidraw/locales/*.json`. **`en.json` is the source of truth** for keys; other locales follow the same key structure. + +## Editing rules +- Add or change **English strings in `en.json` first**, then mirror keys in other locale files only when the workflow expects it (often Crowdin syncs translations — do not bulk-edit all languages by hand without process). +- Preserve JSON validity (trailing commas are invalid). Run format check: `yarn test:other` (Prettier) if unsure. + +## Code usage +- Use the project’s existing i18n helpers / `t(...)` patterns from application code — do not concatenate translated strings in a way that breaks grammar for RTL or plural languages when avoidable. + +## Coverage +- For key churn, `locales-coverage` scripts exist at repo root (`yarn locales-coverage`) — use when required by contribution guidelines. + +## How to verify + +- `yarn test:other` — Prettier check includes locale JSON; invalid JSON will fail earlier or in tooling. +- `yarn locales-coverage` — when adding or renaming keys (per contribution guidelines). +- `yarn start` — smoke-test UI paths that use new/changed strings (language switch if applicable). diff --git a/.cursor/rules/packages-element-math.mdc b/.cursor/rules/packages-element-math.mdc new file mode 100644 index 0000000..e9ea9c2 --- /dev/null +++ b/.cursor/rules/packages-element-math.mdc @@ -0,0 +1,25 @@ +--- +description: "Geometry and math packages — keep pure and dependency-light" +globs: "packages/element/**,packages/math/**" +alwaysApply: false +--- +# packages/element and packages/math + +## Purpose +- **`packages/math`**: numeric types, points, vectors — keep **framework-agnostic** (no React, no DOM APIs). +- **`packages/element`**: element model, bindings, geometry helpers — consumed by the editor; avoid circular imports and keep public APIs stable. + +## Performance +- Hot paths run frequently during pointer moves and rendering. Prefer small allocations, avoid unnecessary object churn, and match existing patterns for immutability vs mutation. + +## Testing +- Unit-test math and geometry edge cases (degenerate shapes, zero length, NaN guards) when changing behavior. + +## Dependencies +- Do not pull `react`, `excalidraw-app`, or heavy UI libraries into these packages. No new npm deps without approval. + +## How to verify + +- `yarn test:typecheck` — `tsc` must pass for all workspace references. +- `yarn test:app --watch=false` — run Vitest after changing tests or behavior. +- `yarn build:math && yarn build:element` — ESM builds succeed when public exports or entrypoints change. diff --git a/.cursor/rules/project-architecture.mdc b/.cursor/rules/project-architecture.mdc new file mode 100644 index 0000000..b98baec --- /dev/null +++ b/.cursor/rules/project-architecture.mdc @@ -0,0 +1,27 @@ +--- +description: "Architecture constraints for the project" +globs: "packages/**" +alwaysApply: false +--- + +# Project Architecture + +## State Management +- Custom state via actionManager — NOT Redux/Zustand/MobX +- State updates: use `actionManager.executeAction(...)` (and related `ActionManager` APIs) — not a `dispatch()` method; `ActionManager` is defined in `packages/excalidraw/actions/manager.tsx` +- State type: AppState (`packages/excalidraw/types.ts`) + +## Rendering +- Canvas 2D rendering — NOT React DOM for drawing +- Render pipeline: Scene → renderScene() → canvas context +- DO NOT use react-konva, fabric.js, pixi.js + +## Dependencies +- No new npm packages without explicit approval + +## How to verify + +- `yarn test:all` — full gate (typecheck, ESLint, Prettier, Vitest once). +- `yarn start` — smoke-test the editor: drawing, undo/redo, export; confirm canvas-based (not DOM-rendered scene). +- Search for forbidden stacks: no new `redux` / `zustand` / `mobx` / `react-konva` / `fabric` / `pixi` imports in `packages/*` unless explicitly approved. +- Confirm action API: `ActionManager` exposes `executeAction` in `packages/excalidraw/actions/manager.tsx` — there is no `actionManager.dispatch` in this repo (`rg 'actionManager\\.dispatch' packages/excalidraw` should be empty). diff --git a/.cursor/rules/security.mdc b/.cursor/rules/security.mdc new file mode 100644 index 0000000..fc47d05 --- /dev/null +++ b/.cursor/rules/security.mdc @@ -0,0 +1,32 @@ +--- +description: "Security-sensitive patterns for app and editor code" +globs: "excalidraw-app/**,packages/excalidraw/**" +alwaysApply: false +--- +# Security + +## Secrets and config +- Never commit API keys, Firebase config secrets, or private tokens. Use env vars / existing `.env.*` patterns and deployment configuration — not hardcoded strings in source. +- Do not log collaboration payloads, room IDs, or user identifiers to `console` in production paths. + +## Untrusted data +- Treat imported `.excalidraw` JSON, URL params, clipboard payloads, and collaborator messages as **untrusted**. Validate shape and size before use; prefer existing restore/validation paths in `packages/excalidraw/data/`. +- Avoid `eval`, `new Function`, and dynamic script injection. If embedding or HTML is required, follow existing embeddable / iframe patterns only. + +## Web and DOM +- Avoid `dangerouslySetInnerHTML` unless it matches an existing audited pattern; never interpolate raw user strings into HTML. +- For `postMessage` / cross-origin flows, validate `event.origin` and message shape (follow existing collaboration / embed code). + +## Collaboration and Firebase +- Do not bypass or weaken end-to-end collaboration encryption behavior without explicit security review. +- Do not change Firebase security rules/policies without explicit review and documented rationale. + +## Dependencies +- No new dependencies for parsing or crypto without explicit review (`package.json` / team approval). + +## How to verify + +- `yarn test:all` — ensure no regressions in data/import flows and app code. +- `yarn test:code` — ESLint may flag risky patterns depending on rules; fix or justify. +- Manual review: search changed files for `eval`, `new Function`, `dangerouslySetInnerHTML`, and raw `postMessage` handlers without origin checks. +- Confirm no secrets in diff: run `git diff` and ensure no new hardcoded keys or tokens. diff --git a/.cursor/rules/testing.mdc b/.cursor/rules/testing.mdc new file mode 100644 index 0000000..77da17d --- /dev/null +++ b/.cursor/rules/testing.mdc @@ -0,0 +1,28 @@ +--- +description: "Vitest tests and test helpers for packages scope" +globs: "packages/**/*.test.ts,packages/**/*.test.tsx,packages/**/tests/**/*.ts,packages/**/tests/**/*.tsx" +alwaysApply: false +--- +# Testing (Vitest) + +## Runner and config +- Root config: `vitest.config.mts` (aliases `@excalidraw/*` — use them in tests like production code). +- From repo root: `yarn test` (watch), `yarn test:app --watch=false` (single run), `yarn test:all` before PRs. + +## Conventions +- Colocate tests as `*.test.ts` / `*.test.tsx` next to source when that matches existing patterns; integration suites often live under `packages/*/tests/`. +- Prefer existing helpers from `packages/excalidraw/tests/helpers/` (e.g. `API` helpers) instead of duplicating setup. +- Use `vitest-canvas-mock` / jsdom behavior already configured — do not add alternate test environments without a strong reason. +- Avoid `any` in tests; use `import type` and real types from `@excalidraw/*` packages. + +## Snapshots and updates +- Only update snapshots when output changes are intentional: `yarn test:update` (or Vitest `-u`), then review diffs carefully. + +## Scope +- Keep tests deterministic (no real network, no reliance on wall-clock timing without fake timers). + +## How to verify + +- `yarn test:app --watch=false` — full Vitest run once (matches CI’s `test:app` step). +- `yarn test:all` — before PR: includes typecheck, ESLint, Prettier, and Vitest. +- For a single file: `yarn test:app -- run path/to/file.test.tsx` (from repo root; Vitest uses `vitest.config.mts`). diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..0640c0d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,182 @@ +# AGENTS.md — guidance for automated assistants and contributors + +This repository is the **Excalidraw** monorepo (`name`: `excalidraw-monorepo` in root `package.json`): a hand-drawn style whiteboard with a publishable editor package and a first-party web app. Official upstream docs: [docs.excalidraw.com](https://docs.excalidraw.com), contributing: [Contributing](https://docs.excalidraw.com/docs/introduction/contributing). + +**Cross-tool context:** **[`CLAUDE.md`](./CLAUDE.md)** summarizes the same policies as **`.cursor/rules/`** for Claude Code and other assistants that do not read Cursor rule files. + +--- + +## 1. Overview + +| Item | Detail | +|------|--------| +| **Monorepo tool** | Yarn **1.x** workspaces (`packageManager`: `yarn@1.22.22`) | +| **Workspaces** | `excalidraw-app`, `packages/*`, `examples/*` | +| **Node** | `>= 18` (engines in root `package.json`) | +| **Primary languages** | TypeScript, TSX | +| **App shell** | Vite-based host in `excalidraw-app/` | +| **Core editor package** | `packages/excalidraw` (published as `@excalidraw/excalidraw`) | +| **Tests** | Vitest (`vitest.config.mts` at repo root) | + +--- + +## 2. Repository structure + +| Path | Role | +|------|------| +| **`excalidraw-app/`** | Deployable web app (Vite, PWA, collaboration, Firebase, etc.). Run via `yarn start` from the repo root. | +| **`packages/common/`** | Shared utilities consumed by other packages. | +| **`packages/element/`** | Element model, geometry, bindings (`@excalidraw/element`). | +| **`packages/math/`** | Math primitives (`@excalidraw/math`) — keep framework-agnostic. | +| **`packages/excalidraw/`** | Main editor: React UI, canvas/scene rendering, actions, data, locales, tests. | +| **`packages/utils/`** | Additional shared helpers (see its `package.json` for scope). | +| **`examples/`** | Sample integrations (e.g. `with-script-in-browser`, `with-nextjs`). | +| **`scripts/`** | Release, locales coverage, and other tooling. | +| **`vitest.config.mts`** | Vitest + path aliases for `@excalidraw/*`. | +| **`tsconfig.json`** | Root TypeScript project (`yarn test:typecheck` runs `tsc`). | +| **`.cursor/rules/*.mdc`** | Cursor project rules (architecture, testing, security, etc.). | +| **`.cursor/commands/*.md`** | Cursor slash commands (`/start-dev`, `/verify-ci`, `/test-watch`). | + +--- + +## 3. Prerequisites and setup + +1. Install **Node.js ≥ 18** and **Yarn 1** (Classic), matching `packageManager` / `engines`. +2. From the **repository root**, install dependencies: + + ```bash + yarn install + ``` + +3. Optional: Husky is wired via `prepare` — `yarn install` runs `husky install` when applicable. + +--- + +## 4. Development workflow + +| Goal | Command (from repo root) | +|------|---------------------------| +| **Dev server (main app)** | `yarn start` → runs `yarn --cwd ./excalidraw-app start` | +| **Rebuild workspace packages** | `yarn build:packages` — `common` → `math` → `element` → `excalidraw` (ESM builds) | +| **Example: script in browser** | `yarn start:example` — builds packages then starts `examples/with-script-in-browser` | +| **Production-like local** | `yarn start:production` (see `excalidraw-app` scripts) | + +If you change low-level packages and see resolution issues, run `yarn build:packages` before `yarn start`. + +--- + +## 5. Build and release + +| Goal | Command | +|------|---------| +| **Full app build** | `yarn build` — delegates to `excalidraw-app` | +| **Build app artifact only** | `yarn build:app` | +| **Docker-related app build** | `yarn build:app:docker` | +| **Individual packages** | `yarn build:common`, `yarn build:math`, `yarn build:element`, `yarn build:excalidraw` | +| **Clean build outputs** | `yarn rm:build` | +| **Release scripts** | `yarn release`, `yarn release:test`, `yarn release:next`, `yarn release:latest` | + +--- + +## 6. Testing, linting, and formatting + +| Goal | Command | +|------|---------| +| **Full CI-equivalent gate** | `yarn test:all` — runs `test:typecheck` → `test:code` → `test:other` → `test:app --watch=false` | +| **Typecheck only** | `yarn test:typecheck` (`tsc`) | +| **ESLint** | `yarn test:code` (`eslint --max-warnings=0 --ext .js,.ts,.tsx .`) | +| **Prettier check** | `yarn test:other` | +| **Vitest (watch)** | `yarn test` or `yarn test:app` | +| **Vitest once** | `yarn test:app --watch=false` | +| **Snapshot update** | `yarn test:update` | +| **Coverage** | `yarn test:coverage` / `yarn test:coverage:watch` | +| **Vitest UI** | `yarn test:ui` | +| **Auto-fix** | `yarn fix` — Prettier write + ESLint fix | + +Run **`yarn test:all`** before opening a PR. + +--- + +## 7. Architecture expectations (editor) + +These match `.cursor/rules/project-architecture.mdc` and related rules: + +- **State**: Editor state is driven by the Excalidraw **action manager** (`ActionManager` in `packages/excalidraw/actions/manager.tsx`). Run user-visible actions via **`executeAction`** (there is no `actionManager.dispatch()` on this class). Do not introduce Redux, Zustand, or MobX for core editor state. +- **Rendering**: Scene content is drawn with **Canvas 2D**, not React DOM; avoid adding `react-konva`, `fabric.js`, or `pixi.js` for the canvas pipeline. +- **Types**: `AppState` and core types live in **`packages/excalidraw/types.ts`**. +- **Host app**: `excalidraw-app` may use its own patterns (e.g. Jotai for shell UI); keep editor concerns in `packages/excalidraw`. + +--- + +## 8. Cursor rules and commands + +- **Rules** (`.cursor/rules/`): Always-on and glob-scoped MDC files — architecture, conventions, testing, security, i18n, `excalidraw-app`, `data/`, `element`/`math`, and **`do-not-touch.mdc`** for protected paths. +- **Commands** (`.cursor/commands/`): + - `/start-dev` — local dev server steps + - `/verify-ci` — `yarn test:all` breakdown + - `/test-watch` — Vitest-focused workflow + +Follow **`.cursor/rules/do-not-touch.mdc`** for files that require explicit approval and extra verification. + +- **Rule vs. code checks:** See **`dev-docs/ab-validation-rules.md`** for documented A/B validation (e.g. rule wording vs. `rg` / source inspection). + +--- + +## 9. Protected and high-risk areas + +Do **not** modify these without explicit approval and full regression checks (see `do-not-touch.mdc`): + +| File | Risk | +|------|------| +| `packages/excalidraw/scene/Renderer.ts` | Render pipeline | +| `packages/excalidraw/data/restore.ts` | File format / restore compatibility | +| `packages/excalidraw/actions/manager.tsx` | Action dispatch | +| `packages/excalidraw/types.ts` | Core type definitions | + +Changes require: understanding dependents, **`yarn test:all`**, and manual QA where noted in rules. + +--- + +## 10. Security and dependencies + +- Do not commit secrets, API keys, or Firebase private config; use existing env / deployment patterns. +- Treat imported `.excalidraw` JSON, clipboard payloads, and collaboration data as **untrusted**; use established validation/restore paths in `packages/excalidraw/data/`. +- **No new npm packages** without explicit approval (team / maintainer process). + +--- + +## 11. Internationalization + +- Locale JSON lives under **`packages/excalidraw/locales/`**; **`en.json`** is the usual source for keys (other locales often sync via Crowdin — see project docs). +- Helper scripts: `yarn locales-coverage`, `yarn locales-coverage:description`. + +--- + +## 12. Contributing and documentation + +- High-level contributing pointer: [Contributing](https://docs.excalidraw.com/docs/introduction/contributing). +- Development setup for this repo: [Development](https://docs.excalidraw.com/docs/introduction/development). +- Root **`CONTRIBUTING.md`** links to the same documentation hub. + +--- + +## 13. Troubleshooting + +| Issue | Suggestion | +|-------|------------| +| **Module resolution / stale package output** | `yarn build:packages`, then retry `yarn start`. | +| **Lint / format failures** | `yarn fix` then `yarn test:all`. | +| **Type errors** | `yarn test:typecheck` for full `tsc` output. | +| **Clean install** | `yarn clean-install` (removes workspace `node_modules` and reinstalls). | + +--- + +## 14. Quick reference (copy-paste) + +```bash +yarn install # deps +yarn start # dev server +yarn test:all # full check before PR +yarn fix # prettier + eslint --fix +yarn build:packages # rebuild @excalidraw packages (ESM) +``` diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..9d2446f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,42 @@ +# CLAUDE.md — project context for Claude and other AI assistants + +This file mirrors the intent of **Cursor** project rules in **`.cursor/rules/*.mdc`** so behavior stays consistent across tools (Claude Code, Cursor, etc.). The **canonical extended guide** is **[`AGENTS.md`](./AGENTS.md)**; read it for commands, layout, and workflows. + +**Repository:** Excalidraw monorepo (`excalidraw-monorepo`), Yarn **1.x** workspaces, **Node ≥ 18**. Root commands: `yarn start`, `yarn test:all`, `yarn fix`, `yarn build:packages`. + +--- + +## Parallel to Cursor rules + +| Topic | Cursor | Same policy here | +|--------|--------|------------------| +| Architecture | `.cursor/rules/project-architecture.mdc` | Canvas 2D for scene; **no** react-konva / fabric / pixi for drawing. Editor state via **`ActionManager`** → **`executeAction`** (see `packages/excalidraw/actions/manager.tsx`), **not** `dispatch()`. **No** Redux / Zustand / MobX for core editor state. `AppState` in `packages/excalidraw/types.ts`. | +| Conventions | `.cursor/rules/conventions.mdc` | Functional components + hooks; named exports; strict TS (`no any`, no `@ts-ignore` where rules forbid); colocated `*.test.tsx` where applicable. | +| Protected files | `.cursor/rules/do-not-touch.mdc` | Do **not** change without explicit approval + `yarn test:all` + manual QA: `packages/excalidraw/scene/Renderer.ts`, `packages/excalidraw/data/restore.ts`, `packages/excalidraw/actions/manager.tsx`, `packages/excalidraw/types.ts`. | +| Testing | `.cursor/rules/testing.mdc` | Vitest (`vitest.config.mts`). Root: `yarn test`, `yarn test:app --watch=false`, `yarn test:all`. | +| Security | `.cursor/rules/security.mdc` | No secrets in source; treat imports/clipboard/collab as untrusted; avoid `eval` / unsafe HTML; no new crypto/parsing deps without review. | +| Host app | `.cursor/rules/excalidraw-app.mdc` | Shell in `excalidraw-app/`; editor logic stays in `packages/excalidraw/`. | +| Data / restore | `.cursor/rules/data-restore.mdc` | Preserve `.excalidraw` compatibility; validate input; test migrations. | +| Element / math | `.cursor/rules/packages-element-math.mdc` | `packages/math` stays framework-agnostic; keep hot paths lean. | +| i18n | `.cursor/rules/i18n-locales.mdc` | `packages/excalidraw/locales/`; `en.json` as key source; valid JSON. | + +Cursor-only **slash commands** (not duplicated here as files): `.cursor/commands/start-dev.md`, `verify-ci.md`, `test-watch.md` — use the equivalent **`yarn`** commands from `AGENTS.md`. + +--- + +## What to run before proposing changes + +1. **`yarn test:all`** — typecheck, ESLint, Prettier check, Vitest once. +2. If touching packages and the app acts broken: **`yarn build:packages`** then **`yarn start`**. + +--- + +## Rule validation + +Cross-check brittle rule text against code when unsure: **[`dev-docs/ab-validation-rules.md`](./dev-docs/ab-validation-rules.md)** (example: `ActionManager` uses `executeAction`, not `dispatch`). + +--- + +## Dependencies + +**No new npm packages** without explicit maintainer / team approval (aligns with `.cursor/rules/project-architecture.mdc` and `AGENTS.md`). diff --git a/dev-docs/ab-validation-rules.md b/dev-docs/ab-validation-rules.md new file mode 100644 index 0000000..bb9b6f4 --- /dev/null +++ b/dev-docs/ab-validation-rules.md @@ -0,0 +1,73 @@ +# A/B validation — Cursor rules vs. codebase + +This document records **A/B validation** for project rules: a **stated rule (A)** is checked against **what the repository actually implements (B)** using grep and source inspection. + +--- + +## Rule under test + +**File:** `.cursor/rules/project-architecture.mdc` +**Section:** State Management (as validated on 2026-04-06) + +**Original claim (variant A):** +“State updates: `actionManager.dispatch()` ONLY” + +**Competing hypothesis (variant B):** +The Excalidraw `ActionManager` class exposes a different public API for running actions (e.g. `executeAction`), and the codebase calls that API—not `dispatch`. + +--- + +## Test scenario + +1. **Objective:** Determine whether `actionManager.dispatch` appears in editor code and whether `ActionManager` defines `dispatch`. +2. **Commands run** (from repository root): + + ```bash + rg 'actionManager\.dispatch' packages/excalidraw --glob '*.{ts,tsx}' + rg 'executeAction' packages/excalidraw/actions/manager.tsx + rg 'actionManager\.executeAction|\.actionManager\.executeAction' packages/excalidraw --glob '*.{ts,tsx}' --count + ``` + +3. **Source check:** Open `packages/excalidraw/actions/manager.tsx` and list public methods used for running actions. + +--- + +## Results + +### Variant A — Rule text (`dispatch`) + +| Check | Result | +|-------|--------| +| `actionManager.dispatch` in `packages/excalidraw` | **No matches** | +| `dispatch` as a method on `ActionManager` in `manager.tsx` | **Not present** (the class has `executeAction`, `handleKeyDown`, `renderAction`, etc.) | + +**Interpretation:** The original wording **does not match** this fork’s implementation. Assistants following “call `dispatch()`” would search for an API that does not exist on `ActionManager`. + +### Variant B — Codebase (`executeAction`) + +| Check | Result | +|-------|--------| +| `executeAction` in `packages/excalidraw/actions/manager.tsx` | **Present** — method at line ~132 (`executeAction(...)`) | +| Call sites `actionManager.executeAction` / `app.actionManager.executeAction` | **Many files** (components, tests, wysiwyg, command palette, etc.) | + +**Interpretation:** State transitions driven through the action system use **`executeAction`**, consistent with the `ActionManager` implementation. + +--- + +## Conclusion + +- **Variant A (dispatch-only)** is **invalid** for this repository: there is **no** `actionManager.dispatch()` API in `packages/excalidraw`. +- **Variant B (executeAction)** is **valid**: it matches `ActionManager` and widespread usage. + +**Action taken:** `.cursor/rules/project-architecture.mdc` was updated to describe **`actionManager.executeAction(...)`** and to point at `packages/excalidraw/actions/manager.tsx`, so the rule aligns with code and avoids misleading agents. + +**How to re-run this check:** Repeat the `rg` commands in the test scenario after large refactors; if `ActionManager` is renamed or split, update the rule and this note. + +--- + +## Follow-up A/B ideas (not yet run) + +| Rule | Possible A/B check | +|------|---------------------| +| `conventions.mdc` — “named exports only” | `rg '^export default'` in `packages/**/*.tsx` — count default exports vs rule | +| `security.mdc` | `rg 'dangerouslySetInnerHTML|eval\(' packages/excalidraw excalidraw-app` — ensure new usages are reviewed | diff --git a/onboarding-docs/01-quickstart.mdx b/onboarding-docs/01-quickstart.mdx new file mode 100644 index 0000000..71afaa5 --- /dev/null +++ b/onboarding-docs/01-quickstart.mdx @@ -0,0 +1,49 @@ +# Quickstart + +This guide gets you from “fresh clone” to “I can run and change things” with minimal context switching. + +## Prerequisites + +- **Node.js** (use an LTS release) +- **Yarn** (v1 or v2.4.2+) +- **Git** + +## Get the code + +```bash +git clone https://github.com/excalidraw/excalidraw.git +cd excalidraw +yarn +``` + +## Run the app locally + +```bash +yarn start +``` + +Then open `http://localhost:3000`. + +## The 5 commands you’ll use most + +```bash +yarn start # run the app for local dev +yarn fix # auto-format / lint-fix where possible +yarn test # run tests +yarn test:typecheck # TypeScript type checking +yarn test:update # run tests (and update snapshots where applicable) +``` + +## Choose your path + +- **I want to change the website/app behavior** → start in [`excalidraw-app/`](./02-repo-tour.mdx#excalidraw-app) +- **I want to change the reusable editor library** → start in [`packages/excalidraw/`](./02-repo-tour.mdx#packages-excalidraw) +- **I want to add/fix a shared utility or core type** → start in `packages/*` +- **I want to integrate Excalidraw into my own app** → see `dev-docs/docs/@excalidraw/excalidraw/installation.mdx` + +## Optional: collaboration + +Local real-time collaboration typically requires running the room server too: + +- [`excalidraw-room`](https://github.com/excalidraw/excalidraw-room) + diff --git a/onboarding-docs/02-repo-tour.mdx b/onboarding-docs/02-repo-tour.mdx new file mode 100644 index 0000000..ad0f388 --- /dev/null +++ b/onboarding-docs/02-repo-tour.mdx @@ -0,0 +1,51 @@ +# Repo tour (monorepo map) + +Excalidraw is a monorepo. The most important distinction is: + +- **`excalidraw-app/`**: the full web app (excalidraw.com) +- **`packages/excalidraw/`**: the reusable React component library published as `@excalidraw/excalidraw` + +## Mental model + +```mermaid +flowchart TD + A["excalidraw-app/ (website)"] -->|depends on| B["@excalidraw/excalidraw (library)"] + B --> C["packages/common"] + B --> D["packages/element"] + B --> E["packages/math"] + B --> F["packages/utils"] + G["examples/"] -->|integrate| B + H["dev-docs/"] -->|developer docs site| B +``` + +## Where should my change go? + +### `excalidraw-app/` + +Use this for: + +- App-specific UI, routing, hosting glue, analytics, deploy config +- Production behaviors that shouldn’t ship in the library + +### `packages/excalidraw/` + +Use this for: + +- Editor behaviors and UI that belong in the reusable package +- API surface consumed by integrators (props, exported utilities, types) + +### `packages/*` (shared internals) + +Use this for: + +- Cross-cutting utilities, types, element model, math helpers +- Functionality reused by both the app and the library + +## “I’m lost” shortcuts + +- Search for the UI component or string you see in the app. +- Track data flow starting from the interaction handler (pointer/keyboard) to state updates. +- If your change affects integrators, validate it in **both**: + - `excalidraw-app/` (real app) + - an `examples/` integration (consumer perspective) + diff --git a/onboarding-docs/03-development-workflow.mdx b/onboarding-docs/03-development-workflow.mdx new file mode 100644 index 0000000..1d1e6d5 --- /dev/null +++ b/onboarding-docs/03-development-workflow.mdx @@ -0,0 +1,69 @@ +# Development workflow + +This page describes the day-to-day loop for contributing safely in a large TypeScript monorepo. + +## The happy path loop + +```mermaid +flowchart LR + A[Pick an issue] --> B[Create a branch] + B --> C[Run dev server] + C --> D[Make a small change] + D --> E[Run targeted checks] + E --> F[Open PR] + F --> G[Iterate on review] + G --> H[Merge] +``` + +## Branching and remotes (recommended) + +If you’re working from a fork, configure `upstream` once: + +```bash +git remote add upstream https://github.com/excalidraw/excalidraw.git +git fetch upstream +``` + +Then keep your fork’s default branch up to date: + +```bash +git checkout master +git pull --rebase upstream master +``` + +## Development modes + +### Local Node/Yarn (most common) + +```bash +yarn +yarn start +``` + +### CodeSandbox (fastest zero-setup) + +The repo supports CodeSandbox for quick edits and PRs: + +- `https://codesandbox.io/p/github/excalidraw/excalidraw` + +### Docker Compose (if you don’t want a Node environment) + +```bash +docker-compose up --build -d +``` + +## Working effectively in a monorepo + +- **Prefer small PRs**: easier review, less merge conflict risk. +- **Keep changes scoped**: avoid drive-by refactors unless necessary. +- **Choose the right package boundary**: + - app behavior → `excalidraw-app/` + - reusable editor behavior/API → `packages/excalidraw/` + - shared internals → `packages/*` + +## Collaboration / room server + +If you’re working on real-time collaboration features, you’ll generally need the room server locally: + +- [`excalidraw-room`](https://github.com/excalidraw/excalidraw-room) + diff --git a/onboarding-docs/04-quality.mdx b/onboarding-docs/04-quality.mdx new file mode 100644 index 0000000..ec110bc --- /dev/null +++ b/onboarding-docs/04-quality.mdx @@ -0,0 +1,40 @@ +# Quality: tests, typecheck, lint/format + +Excalidraw is a large TypeScript monorepo. Your fastest path to a smooth PR is running the right checks **before** you push. + +## The “before you open a PR” checklist + +```bash +yarn fix +yarn test:typecheck +yarn test +``` + +If your change affects snapshots, use: + +```bash +yarn test:update +``` + +## What each command is for + +- **`yarn fix`**: auto-fixes formatting/lint issues where possible. +- **`yarn test:typecheck`**: catches TypeScript errors across packages. +- **`yarn test`**: runs automated tests. +- **`yarn test:update`**: updates snapshots when changes are expected. + +## Common causes of CI failures (and how to avoid them) + +- **Formatting diffs**: run `yarn fix` before committing. +- **Type errors after refactors**: run `yarn test:typecheck` early, not just at the end. +- **Snapshot drift**: if the UI changed intentionally, use `yarn test:update` and verify the updates are expected. +- **Flaky UI assumptions**: manually test your change in the running app (`yarn start`) in addition to unit tests. + +## Manual testing (don’t skip) + +Automated checks are necessary but not sufficient. Before requesting review: + +- Reproduce the original bug / requirement. +- Verify the fix/feature in the running app. +- If you touched UI, sanity-check across at least two browsers when practical. + diff --git a/onboarding-docs/05-making-changes.mdx b/onboarding-docs/05-making-changes.mdx new file mode 100644 index 0000000..3d35811 --- /dev/null +++ b/onboarding-docs/05-making-changes.mdx @@ -0,0 +1,69 @@ +# Making changes: PR checklist + +This page is a practical guide for making changes in a way that’s easy to review and unlikely to break consumers. + +## Pick the right issue + +- For major changes, open an issue first to align on approach. +- If you’re new, look for “easy” tasks on the project roadmap. + +References: + +- Issues: `https://github.com/excalidraw/excalidraw/issues/new` +- Roadmap: `https://github.com/orgs/excalidraw/projects/3` + +## Commit / PR title convention + +Use a semantic prefix at the start of the PR title: + +- **feat**: new feature +- **fix**: bug fix +- **docs**: documentation-only change +- **style**: formatting-only change +- **refactor**: code change without behavior change +- **perf**: performance improvement +- **test**: adding/fixing tests +- **build**: build system/dependencies +- **ci**: CI configuration +- **chore**: other maintenance work +- **revert**: revert a previous change + +## PR body template (copy/paste) + +```md +## Summary +- What changed and why (1–3 bullets). + +## Test plan +- [ ] `yarn test:typecheck` +- [ ] `yarn test` +- [ ] Manual testing notes: + - Steps: + - Expected: + - Actual: + +## Screenshots / recordings (if UI) +- Before: +- After: + +## Risks / rollout +- What could break? +- Any backwards-compat concerns for `@excalidraw/excalidraw` consumers? +``` + +## Design principles for reviewable changes + +- **Prefer the smallest change that solves the problem**. +- **Avoid unrelated refactors** in the same PR. +- **Add tests when behavior changes** (especially for bug fixes). +- **Think about consumers**: + - If you changed exported types/props/APIs, treat it as a compatibility boundary. + +## When you’re unsure where to implement + +Start with the closest user-visible behavior, then peel layers: + +1. Locate the UI behavior in `excalidraw-app/` or `packages/excalidraw/`. +2. Track state changes. +3. Only then adjust shared utilities/types in `packages/*`. + diff --git a/onboarding-docs/06-troubleshooting.mdx b/onboarding-docs/06-troubleshooting.mdx new file mode 100644 index 0000000..220d713 --- /dev/null +++ b/onboarding-docs/06-troubleshooting.mdx @@ -0,0 +1,70 @@ +# Troubleshooting + +This is a “try these first” list for common local-dev problems. + +## Install issues + +### `yarn` fails or installs the wrong dependency set + +- Ensure you’re using a supported **Yarn** version (v1 or v2.4.2+). +- Re-run with a clean slate: + +```bash +rm -rf node_modules +yarn cache clean +yarn +``` + +### Node version weirdness + +If you see runtime errors that look like tooling incompatibility, switch to an **LTS Node.js** version and reinstall: + +```bash +node -v +yarn -v +rm -rf node_modules +yarn +``` + +## Dev server issues + +### Port already in use + +If `yarn start` fails because the port is busy, stop the other process first, or use your OS tools to identify what’s listening on `3000`. + +### Changes not reflected + +Try: + +- Hard refresh the browser. +- Restart `yarn start`. +- If still stuck, clear caches by reinstalling dependencies (see above). + +## Tests / typecheck issues + +### Snapshots changed + +If the change is intentional: + +```bash +yarn test:update +``` + +Then review the snapshot diffs to ensure they match your intent. + +### Type errors after moving code + +Run: + +```bash +yarn test:typecheck +``` + +If errors mention path aliases or cross-package types, verify you updated exports/import paths in the correct package boundary. + +## Collaboration features locally + +Real-time collaboration typically requires running the room server: + +- [`excalidraw-room`](https://github.com/excalidraw/excalidraw-room) + diff --git a/onboarding-docs/07-glossary.mdx b/onboarding-docs/07-glossary.mdx new file mode 100644 index 0000000..2fe8609 --- /dev/null +++ b/onboarding-docs/07-glossary.mdx @@ -0,0 +1,9 @@ +# Glossary + +- **App**: the hosted website experience in `excalidraw-app/`. +- **Library**: the reusable React component shipped as `@excalidraw/excalidraw` in `packages/excalidraw/`. +- **Monorepo**: a single repository that contains multiple related packages and applications. +- **Workspace**: Yarn workspaces used to manage multiple packages in one repo. +- **Snapshot test**: a test that stores expected output (often UI-related) and fails when the output changes. +- **Typecheck**: running TypeScript’s compiler checks to ensure types are consistent across the codebase. + diff --git a/onboarding-docs/08-token-estimates.mdx b/onboarding-docs/08-token-estimates.mdx new file mode 100644 index 0000000..7682ce0 --- /dev/null +++ b/onboarding-docs/08-token-estimates.mdx @@ -0,0 +1,26 @@ +# Token estimates (rough) + +This page fulfills the “evaluate parts of the project in tokens” requirement by estimating how many tokens the onboarding docs contain. + +## Method (simple heuristic) + +- **Estimator**: \( \text{tokens} \approx \lceil \frac{\text{characters}}{4} \rceil \) +- **Why**: for English/Markdown, many LLM tokenizers average ~4 characters per token. +- **Caveat**: real tokenizers vary by model and by the amount of code/URLs/symbols. + +## Estimates for `onboarding_docs/` + +To avoid self-referential counts, this table **excludes** this file (`08-token-estimates.mdx`). + +| File | Characters | Estimated tokens | +| --- | ---:| ---:| +| `README.mdx` | 1,339 | 335 | +| `01-quickstart.mdx` | 1,318 | 330 | +| `02-repo-tour.mdx` | 1,453 | 364 | +| `03-development-workflow.mdx` | 1,558 | 390 | +| `04-quality.mdx` | 1,327 | 332 | +| `05-making-changes.mdx` | 1,885 | 472 | +| `06-troubleshooting.mdx` | 1,395 | 349 | +| `07-glossary.mdx` | 572 | 143 | +| **Total** | **10,847** | **2,715** | + diff --git a/onboarding-docs/README.mdx b/onboarding-docs/README.mdx new file mode 100644 index 0000000..90378c0 --- /dev/null +++ b/onboarding-docs/README.mdx @@ -0,0 +1,31 @@ +# Excalidraw onboarding (start here) + +Welcome! This folder is a **GitHub-friendly onboarding guide** for the Excalidraw monorepo. + +If you just want to use Excalidraw (not contribute), start at **the app**: [`https://excalidraw.com`](https://excalidraw.com) or the published library: `@excalidraw/excalidraw`. + +## How to use these docs + +- **New contributor**: read in order (about 20–40 minutes). +- **Returning contributor**: jump to the section you need. +- **Maintainer / reviewer**: focus on workflow, quality gates, and repo layout. + +## Table of contents + +1. [Quickstart](./01-quickstart.mdx) +2. [Repo tour (monorepo map)](./02-repo-tour.mdx) +3. [Development workflow](./03-development-workflow.mdx) +4. [Quality: tests, typecheck, lint/format](./04-quality.mdx) +5. [Making changes: PR checklist](./05-making-changes.mdx) +6. [Troubleshooting](./06-troubleshooting.mdx) +7. [Glossary](./07-glossary.mdx) +8. [Token estimates (rough)](./08-token-estimates.mdx) + +## Canonical deep-dive docs + +This repo also includes a Docusaurus-based documentation site under `dev-docs/` with deeper guides (package API, editor internals, etc.). When you want more detail than this onboarding provides, start here: + +- `dev-docs/docs/introduction/get-started.mdx` +- `dev-docs/docs/introduction/development.mdx` +- `dev-docs/docs/introduction/contributing.mdx` +