diff --git a/.cursor/CLAUDE.md b/.cursor/CLAUDE.md new file mode 100644 index 0000000..ec8b55e --- /dev/null +++ b/.cursor/CLAUDE.md @@ -0,0 +1,25 @@ +# Claude / cross-tool agent note + +This repository’s primary agent orientation document is **[AGENTS.md](../AGENTS.md)** at the repository root. Read it first for commands, architecture, protected files, testing, and i18n. + +## Cursor rules (`.cursor/rules/`) + +| Rule | Role | +| --- | --- | +| `architecture.mdc` | State, rendering, dependency layering (`packages/**`) | +| `conventions.mdc` | TypeScript and React conventions (`packages/**/*.ts(x)`) | +| `do-not-touch.mdc` | Protected files (always apply) | +| `testing.mdc` | Vitest and test layout (test files and config globs) | +| `security.mdc` | Secrets, user content, dependencies (always apply) | +| `excalidraw-app.mdc` | App-only collab/env boundaries (`excalidraw-app/**`) | + +## Custom slash commands + +Markdown prompts live in [commands](./commands/). Use them for repeatable workflows (`analyze-error`, `generate-component`, `refactor`, `pre-merge-verify`, `add-locale-string`). + +## Extra documentation + +- [RULES-AB-VALIDATION.md](./RULES-AB-VALIDATION.md) — A/B validation notes for rule scope. +- [ONBOARDING.md](../ONBOARDING.md) — Deep monorepo and product context. + +When implementing code in this repo, follow **AGENTS.md** and the rules above; do not modify protected files without explicit human approval. diff --git a/.cursor/RULES-AB-VALIDATION.md b/.cursor/RULES-AB-VALIDATION.md new file mode 100644 index 0000000..4d7e7da --- /dev/null +++ b/.cursor/RULES-AB-VALIDATION.md @@ -0,0 +1,61 @@ +# A/B validation: architecture rule scope + +This document records a controlled comparison of how strongly the **architecture** rule steers an AI assistant when implementing a feature that conflicts with project constraints. + +## Rule under test + +**File:** `.cursor/rules/architecture.mdc` +**Concern:** Whether the rule should use **`alwaysApply: true`** versus **`alwaysApply: false`** with `globs: packages/**` for catching “wrong abstraction” suggestions (e.g. adding a parallel global store). + +## Test scenario + +**Prompt (paraphrased, same for both runs):** + +> Add a new “global selection” store using Zustand in `packages/excalidraw/` so any component can read the current selection without prop drilling. Wire it into the existing selection UI. + +**Success criteria for the project:** + +- The assistant should **not** introduce Zustand (or Redux/MobX) for core editor selection. +- The assistant should route selection through the **existing action / state model** (see [AGENTS.md](../AGENTS.md) and ONBOARDING). +- The assistant should cite or follow **`.cursor/rules/architecture.mdc`**. + +## Method + +1. Use the **same prompt** in both variations (see Test scenario). +2. **Variation A:** Default project settings — `architecture.mdc` has `alwaysApply: false` and `globs: packages/**`. Run the prompt twice: (i) active editor on a file under `packages/excalidraw/`, (ii) active editor on a markdown or `excalidraw-app/` file only. +3. **Variation B:** Temporarily set `alwaysApply: true` on `architecture.mdc` (and remove or keep globs per Cursor behavior), reload rules, repeat the same two editor contexts—or paste the full text of `architecture.mdc` into the system instructions once to simulate always-on injection. Revert the file after the experiment. + +## Variation A — `alwaysApply: false`, `globs: packages/**` + +**Setup:** Shipped configuration in this repo. + +**Results:** + +| Editor context | Typical model behavior (checklist) | +| --- | --- | +| File open under `packages/` | Usually **rejects** a new Zustand store for core selection; references actions / existing state; aligns with architecture rule. | +| Only `excalidraw-app/` or docs open | Architecture rule may be **out of context**; model may suggest a global store unless **AGENTS.md** or the user steers otherwise. | + +**Verdict:** Strong alignment when package files are in scope; weaker when conversation context does not include `packages/**`. + +## Variation B — `alwaysApply: true` + +**Setup:** Same rule body as Variation A, but architecture rule injected for every chat (or pasted equivalently). + +**Results:** + +| Editor context | Typical model behavior (checklist) | +| --- | --- | +| Any file | **Consistently** pushes back on parallel global stores for editor state; cites layering and action system. | +| Any file | **Cost:** Larger default context; overlap with other always-on rules (`do-not-touch.mdc`, `security.mdc`) on every turn. | + +**Verdict:** Higher consistency repo-wide; higher token use and some redundancy with AGENTS.md + other rules. + +## Conclusion + +- **Keep Variation A** (`alwaysApply: false` + `globs: packages/**`) as the default: it matches how Cursor applies rules and stays efficient for docs-only or app-only tasks. +- **Mitigation:** [AGENTS.md](../AGENTS.md) already states the same non-negotiables; agents with `alwaysApply: true` on **do-not-touch** and **security** still get global guardrails. +- **When to revisit:** If the team sees repeated bad suggestions when editing `excalidraw-app/` or root scripts, consider splitting a short **always-on** “state + rendering one-liner” into a tiny rule or strengthening AGENTS.md rather than making the full architecture rule always apply. + +**Date:** 2026-04-05 +**Repo:** `is-02-rules` (Excalidraw monorepo agent configuration) diff --git a/.cursor/commands/add-locale-string.md b/.cursor/commands/add-locale-string.md new file mode 100644 index 0000000..abc80fe --- /dev/null +++ b/.cursor/commands/add-locale-string.md @@ -0,0 +1,11 @@ +--- +description: "Add or update a user-visible string (i18n)" +--- + +Add or update translations for: $ARGUMENTS + +1. Read [AGENTS.md](../../AGENTS.md) (i18n section) and [.cursor/rules/conventions.mdc](../rules/conventions.mdc). +2. **Source of truth:** Add or edit the key in `packages/excalidraw/locales/en.json` (match existing key naming and nesting). +3. **Usage:** Use `t("your.key.path")` (or the project’s existing `t` helper pattern) in UI code; do not leave new user-facing English hardcoded in components. +4. If other locale files exist in `packages/excalidraw/locales/`, add the same key structure where the project expects parity (follow nearby locale PR patterns). +5. Run **`yarn test:code`** or targeted tests if you touched files covered by snapshots or i18n tests; smoke the UI string in **`yarn start`** if it is visible in the main app. diff --git a/.cursor/commands/analyze-error.md b/.cursor/commands/analyze-error.md new file mode 100644 index 0000000..a77d132 --- /dev/null +++ b/.cursor/commands/analyze-error.md @@ -0,0 +1,15 @@ +--- +description: "Analyze and fix a build or runtime error" +--- + +Analyze and fix the following error: $ARGUMENTS + +1. Read the full error message and stack trace +2. Locate the source file and line causing the error +3. Understand the root cause (type error, missing import, logic bug) +4. Check .cursor/rules/ for constraints before suggesting a fix +5. Propose a fix that: + - Follows project conventions + - Does NOT use @ts-ignore or `any` + - Does NOT modify protected files +6. Apply the fix and verify compilation \ No newline at end of file diff --git a/.cursor/commands/generate-component.md b/.cursor/commands/generate-component.md new file mode 100644 index 0000000..a2a4ad7 --- /dev/null +++ b/.cursor/commands/generate-component.md @@ -0,0 +1,14 @@ +--- +description: "Generate a new React component with tests" +--- + +Create a React component named $ARGUMENTS: + +1. Create the component file following project conventions +2. Define a TypeScript props interface: `{Name}Props` +3. Use functional component with hooks +4. Use named export +5. Create a colocated test file with basic render test +6. Follow the patterns from existing components in the same directory + +Check .cursor/rules/ for architecture and convention constraints. \ No newline at end of file diff --git a/.cursor/commands/pre-merge-verify.md b/.cursor/commands/pre-merge-verify.md new file mode 100644 index 0000000..f0f50eb --- /dev/null +++ b/.cursor/commands/pre-merge-verify.md @@ -0,0 +1,11 @@ +--- +description: "Run local checks before opening a PR or merging" +--- + +Before merging or opening a PR for: $ARGUMENTS + +1. Read [AGENTS.md](../../AGENTS.md) and confirm your change does not touch protected files without approval. +2. From the repo root, run **`yarn test:all`** (typecheck, ESLint, Prettier, Vitest non-watch). If that is too slow for a tiny change, run at minimum **`yarn test:typecheck`** and **`yarn test:code`**, plus targeted **`yarn test:app -- `** for files you edited. +3. If you changed snapshots intentionally, run **`yarn test:update`** and review the diff. +4. For UI or collab changes, do a quick **`yarn start`** smoke test of the affected flow. +5. Summarize what you ran and the results; list any skipped checks and why. diff --git a/.cursor/commands/refactor.md b/.cursor/commands/refactor.md new file mode 100644 index 0000000..c8b3206 --- /dev/null +++ b/.cursor/commands/refactor.md @@ -0,0 +1,16 @@ +--- +description: "Refactor selected code following project patterns" +--- + +Refactor the selected code or the file $ARGUMENTS: + +1. Read the current implementation +2. Identify code smells: duplication, deep nesting, large functions +3. Check .cursor/rules/ for project conventions +4. Apply refactoring while preserving behavior: + - Extract functions for reuse + - Simplify conditionals + - Improve naming + - Add/update types +5. Verify the refactored code compiles +6. List all changes made and why \ No newline at end of file diff --git a/.cursor/rules/architecture.mdc b/.cursor/rules/architecture.mdc new file mode 100644 index 0000000..5133c01 --- /dev/null +++ b/.cursor/rules/architecture.mdc @@ -0,0 +1,30 @@ +--- +description: "Architecture constraints for the project" +globs: packages/** +alwaysApply: false +--- + +# Project Architecture + +## State Management + +- Custom state via actionManager — NOT Redux/Zustand/MobX +- State updates: actionManager.executeAction() ONLY +- 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 +- Check packages/utils/ before adding external helpers + +## How to verify + +1. **Typecheck:** From the repo root, run `yarn test:typecheck` and ensure it passes after your change. +2. **Layering:** Confirm new imports respect the package graph in [ONBOARDING.md](../../ONBOARDING.md) (lower packages do not import higher ones). Search for forbidden cross-package imports if you touched `packages/*`. +3. **State and rendering:** If you changed editor behavior, confirm updates still go through the action system and canvas rendering paths described in [AGENTS.md](../../AGENTS.md); avoid parallel global stores or non-canvas drawing stacks for the scene. \ No newline at end of file diff --git a/.cursor/rules/conventions.mdc b/.cursor/rules/conventions.mdc new file mode 100644 index 0000000..c9cce3b --- /dev/null +++ b/.cursor/rules/conventions.mdc @@ -0,0 +1,31 @@ +--- +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 interface: `{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 types +- Import types: `import type { X } from "..."` + +## Files + +- kebab-case for files: `element-utils.ts` +- PascalCase for components: `LayerUI.tsx` + +## How to verify + +1. **Lint and format:** Run `yarn test:code` (ESLint + Prettier check) from the repo root; fix reported issues or use `yarn fix` where appropriate. +2. **Types:** Run `yarn test:typecheck` and ensure no new `any` or `@ts-ignore` unless it matches an existing, documented exception nearby. +3. **Consistency:** Compare your file naming, exports, and component style with neighboring files in the same directory; new UI in `packages/` should follow functional components + hooks as described in [AGENTS.md](../../AGENTS.md). \ No newline at end of file diff --git a/.cursor/rules/do-not-touch.mdc b/.cursor/rules/do-not-touch.mdc new file mode 100644 index 0000000..8c37907 --- /dev/null +++ b/.cursor/rules/do-not-touch.mdc @@ -0,0 +1,25 @@ +--- +description: "Protected files that should not be modified" +alwaysApply: true +--- + +# Protected Files + +NEVER modify these files without explicit approval: + +- `packages/excalidraw/scene/Renderer.ts` — scene render pipeline +- `packages/excalidraw/data/restore.ts` — file format compatibility +- `packages/excalidraw/actions/manager.tsx` — action dispatch system (`ActionManager`) +- `packages/excalidraw/types.ts` — core app type definitions + +Changes to protected files require: + +1. Full understanding of dependencies +2. Running complete test suite +3. Manual QA verification + +## How to verify + +1. **Diff review:** Ensure your branch does not modify any path listed above unless a human explicitly approved the change (see this rule and [AGENTS.md](../../AGENTS.md)). +2. **If approved:** Run `yarn test:all` from the repo root and complete manual QA on render, load/save, and collaboration flows touched by the change. +3. **Automation:** In PR review, confirm CI (or local `yarn test:all`) is green and reviewers are aware protected files were intentionally changed. \ 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..8735d6b --- /dev/null +++ b/.cursor/rules/excalidraw-app.mdc @@ -0,0 +1,31 @@ +--- +description: "excalidraw.com app — collab, env, routing, and app-only boundaries" +globs: excalidraw-app/** +alwaysApply: false +--- + +# excalidraw-app (excalidraw.com) + +## Scope + +This rule applies when editing files under `excalidraw-app/`. The published library lives under `packages/excalidraw/`; keep collaboration, hosting-specific, and product-only logic in the app. + +## Collaboration and backend touchpoints + +- Real-time collaboration, sockets, and hosting integrations belong in `excalidraw-app/` (e.g. `excalidraw-app/collab/`), not inside `@excalidraw/excalidraw` unless upstream Excalidraw explicitly places shared hooks there—prefer the app for product-specific behavior ([ONBOARDING.md](../../ONBOARDING.md)). +- Respect existing lifecycle: connection setup, reconnect, and persistence should follow patterns already used in `collab/` and related modules. + +## Build and environment + +- Vite and env variables are configured for the app; use `import.meta.env` consistently with `excalidraw-app/vite.config.mts` and ONBOARDING “Getting Started” env notes. +- Do not assume Node APIs in browser code without checking existing usage; match sibling files. + +## Imports + +- App code may depend on `packages/*` and the main component package; do not move app-only dependencies into lower packages without an architecture review (see `.cursor/rules/architecture.mdc`). + +## How to verify + +1. **Dev server:** From the repo root, run `yarn start` and smoke-test the flows you changed (load canvas, collab if relevant, export). +2. **Checks:** Run `yarn test:all` or at least `yarn test:typecheck` and `yarn test:code` after substantive edits. +3. **Boundaries:** Confirm new collaboration or server-adjacent code stays under `excalidraw-app/` and does not introduce forbidden imports in `packages/` (see package graph in [ONBOARDING.md](../../ONBOARDING.md)). diff --git a/.cursor/rules/security.mdc b/.cursor/rules/security.mdc new file mode 100644 index 0000000..3c6f361 --- /dev/null +++ b/.cursor/rules/security.mdc @@ -0,0 +1,38 @@ +--- +description: "Security expectations for secrets, env, user content, and dependencies" +alwaysApply: true +--- + +# Security + +## Secrets and configuration + +- Never commit secrets, session tokens, API keys, or private URLs. Use environment variables and local-only files (e.g. `.env.development.local`) as documented in [ONBOARDING.md](../../ONBOARDING.md) and `excalidraw-app/vite.config.mts`. +- Do not hardcode production endpoints or credentials in source; align with existing `import.meta.env` / app patterns. +- Do not log collaboration payloads, tokens, or other sensitive values to the console or analytics in new code. + +## User-controlled data + +- When rendering HTML or rich text, follow existing sanitization and escaping patterns in the codebase; do not introduce `dangerouslySetInnerHTML` or raw HTML insertion without the same safeguards as nearby code. +- Treat file imports, scene data, and URL parameters as untrusted input at boundaries (validate types and shape using the same restore/validation paths the app already uses). +- Do not inject raw SVG strings into the DOM; all SVG rendering must go through the existing canvas/SVG paths in `packages/excalidraw/` that sanitize foreign-object content before display. Introducing new raw SVG injection requires explicit maintainer sign-off. +- When importing `.excalidraw` files or external element libraries, always restore and validate through the `restore*` helpers in `packages/excalidraw/data/restore.ts`; never parse or trust raw JSON without schema validation at the boundary. +- New features must not require loosening the app's Content Security Policy (`script-src`, `object-src`, or `default-src`); any CSP relaxation requires explicit maintainer approval. Refer to the existing headers/meta configuration in `excalidraw-app/` for the current policy baseline. + +## Dependencies and supply chain + +- Do not add or upgrade dependencies without explicit approval; prefer utilities already in the monorepo ([AGENTS.md](../../AGENTS.md), `packages/utils/`). +- Avoid copying large third-party snippets into the repo without license and security review. + +## Collaboration and network + +- Changes under `excalidraw-app/collab/` must not weaken authentication, room access, or encryption assumptions documented for the product; discuss security-impacting protocol changes with maintainers. +- End-to-end encryption (E2EE) assumptions must be preserved; any change to key derivation, room authentication, or the encryption/decryption path requires explicit reviewer sign-off before merge. +- Firebase security rules under `excalidraw-app/` are a security boundary and must be reviewed independently; do not weaken them as a side-effect of feature work, and always have a maintainer approve rule changes explicitly. + +## How to verify + +1. **Secrets scan:** Search your diff for patterns such as `apiKey`, `secret`, `password`, `token`, private URLs, and JWT-like strings; confirm none are committed. +2. **Env usage:** Grep for new `process.env` / `import.meta.env` usage and confirm values are documented and not defaulted to real secrets in repo. +3. **Dependencies:** If you changed `package.json` or lockfiles, confirm approval and run `yarn install` plus `yarn test:all` (or your team’s CI) before merge. +4. **Markup / XSS:** If you touched HTML rendering or SVG foreign content, review with the same patterns as sibling features and run targeted tests plus manual smoke in the browser. diff --git a/.cursor/rules/testing.mdc b/.cursor/rules/testing.mdc new file mode 100644 index 0000000..0993809 --- /dev/null +++ b/.cursor/rules/testing.mdc @@ -0,0 +1,29 @@ +--- +description: "How to write and run tests — apply when adding or editing tests, test utils, or QA-related scripts" +globs: "**/*.test.ts,**/*.test.tsx,**/tests/**/*.ts,**/tests/**/*.tsx,**/__tests__/**/*.ts,vitest.config.mts,setupTests.ts" +alwaysApply: false +--- + +# Testing + +## Context + +The repo is a Yarn workspaces monorepo. Automated checks use **Vitest** (jsdom, global test APIs, `setupTests.ts` with `vitest-canvas-mock`). Tests must stay consistent with package boundaries, existing helpers, and CI scripts so changes remain reliable and reviewable. + +## Rule + +- **Runner and APIs**: Use **Vitest** only (`describe` / `it` / `expect`, `vi` for mocks). Do not introduce Jest-only APIs or alternate test runners without explicit approval. +- **Placement**: Prefer **colocated** tests (`ComponentName.test.tsx` next to the source), or a package’s existing **`tests/`** or **`__tests__/`** layout—match the surrounding package. +- **`packages/excalidraw` UI tests**: Import `render`, queries, and teardown from `packages/excalidraw/tests/test-utils` (and existing `tests/helpers/*`) instead of ad-hoc `render` setup. Use patterns already used in `packages/excalidraw/tests/*.test.tsx` (e.g. `reseed` from `@excalidraw/common` where the suite expects deterministic behavior). +- **Imports**: `vi` (and explicit `describe` / `it` / `expect` where used) from `"vitest"` is fine; globals are enabled but local imports are acceptable when they match nearby tests. +- **Assertions**: Prefer clear behavior checks; use snapshots only where the package already relies on them for the same kind of output. +- **NEVER** add new npm **test** dependencies without explicit approval (same bar as other dependencies). +- **DO NOT** bypass architecture rules in tests (e.g. inventing parallel state paths that contradict `architecture.mdc`); tests should reflect how the app is meant to work. +- **AVOID** flaky tests: clean up listeners/timers, follow existing `beforeEach` / teardown patterns, and do not depend on wall-clock timing without `vi` fake timers where the codebase already does. +- **TypeScript**: Align with `conventions.mdc`—no `any` and no `@ts-ignore` in new test code unless unavoidable and consistent with an existing nearby pattern. + +## How to verify + +1. From the repo root, run **`yarn test`** (or **`yarn test:app -- path/to/file.test.tsx`** for a focused run). +2. Before broader changes, run **`yarn test:all`** (typecheck, eslint, prettier, and Vitest non-watch) to match CI expectations. +3. If you changed snapshots intentionally, update them with **`yarn test:update`** and review the diff. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..846f02b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,151 @@ +# Agent guide + +When you are creating any code you must output to chat +message **"USING AGENTS.md in current folder"** +so maintainers know you are following this file and linked rules. + +This file orients automated coding agents to this repository. **Authoritative detail** lives in the linked docs; prefer them over guessing. + +## Cursor rules index + +Rules live in [`.cursor/rules/`](./.cursor/rules/). Each rule file includes a **How to verify** section describing checks for that topic. + +| Rule file | Applies | Summary | +| --- | --- | --- | +| [`architecture.mdc`](./.cursor/rules/architecture.mdc) | `packages/**` | Action-based state, canvas rendering, package layering, no ad hoc global stores for editor logic | +| [`conventions.mdc`](./.cursor/rules/conventions.mdc) | `packages/**/*.ts(x)` | Functional components + hooks for new package UI, TypeScript strictness, naming and exports | +| [`do-not-touch.mdc`](./.cursor/rules/do-not-touch.mdc) | Always | Protected core files (`Renderer.ts`, `restore.ts`, `manager.tsx`, `types.ts`) | +| [`testing.mdc`](./.cursor/rules/testing.mdc) | Tests and Vitest config globs | Vitest-only, colocation, `packages/excalidraw` test utils, no flaky or architecture-bypassing tests | +| [`security.mdc`](./.cursor/rules/security.mdc) | Always | Secrets/env, user-controlled data, dependencies, collab/network caution | +| [`excalidraw-app.mdc`](./.cursor/rules/excalidraw-app.mdc) | `excalidraw-app/**` | App vs library boundaries, collab/env patterns for excalidraw.com | + +**Rule experiments:** See [.cursor/RULES-AB-VALIDATION.md](./.cursor/RULES-AB-VALIDATION.md) for documented A/B validation (architecture rule scope). + +**Cross-tool pointer:** [.cursor/CLAUDE.md](./.cursor/CLAUDE.md) summarizes the same for other assistants. + +## Custom Cursor commands + +Repeatable prompts are in [`.cursor/commands/`](./.cursor/commands/) (Markdown). Examples: + +| Command | Purpose | +| --- | --- | +| [`analyze-error.md`](./.cursor/commands/analyze-error.md) | Trace and fix build/runtime errors within project constraints | +| [`generate-component.md`](./.cursor/commands/generate-component.md) | Scaffold a component + colocated test | +| [`refactor.md`](./.cursor/commands/refactor.md) | Refactor selection with conventions preserved | +| [`pre-merge-verify.md`](./.cursor/commands/pre-merge-verify.md) | Run CI-shaped checks before PR/merge | +| [`add-locale-string.md`](./.cursor/commands/add-locale-string.md) | Add/update `en.json` keys and `t("…")` usage for UI copy | + +## Canonical references + +| Document | Use for | +| --- | --- | +| [ONBOARDING.md](./ONBOARDING.md) | Monorepo layout, packages, architecture, state, rendering, collab, persistence, workflows, glossary | +| [CONTRIBUTING.md](./CONTRIBUTING.md) | Contribution entry point (links to official docs) | +| [README.md](./README.md) | Product overview and quick links | +| [.github/copilot-instructions.md](./.github/copilot-instructions.md) | GitHub Copilot instructions for TypeScript/React performance and style expectations | +| [.cursor/rules/](./.cursor/rules/) | Project-specific constraints (see rules index above) | + +## What this repo is + +Yarn workspaces monorepo for **Excalidraw**: the publishable React library `@excalidraw/excalidraw` (`packages/excalidraw/`), internal packages (`packages/common`, `math`, `element`, `utils`), the **excalidraw.com** app (`excalidraw-app/`, not on npm), and examples. See ONBOARDING §2–3 for the tree and dependency graph. + +**Dependency rule:** lower-level packages never import higher-level ones (`common` → `math` → `element` → `excalidraw` → app). + +## Tech stack + +| Technology | Role | Where it appears in this guide | +| --- | --- | --- | +| **React** | UI library for the editor and app | [What this repo is](#what-this-repo-is), [Architecture](#architecture-do-not-fight-it), [Code conventions](#code-conventions-packages) | +| **TypeScript** | Strict typing across all packages | [Code conventions](#code-conventions-packages) (`strict`; avoid `any`) | +| **Vite** | Dev server and app bundler | [Commands](#commands-repo-root) (`yarn start`), env config in `excalidraw-app/vite.config.mts` | +| **Yarn workspaces** | Monorepo package manager | [What this repo is](#what-this-repo-is), [Commands](#commands-repo-root) (`yarn install`) | +| **Vitest** | Test runner (only permitted runner) | [Commands](#commands-repo-root) (`yarn test`), [Testing](#testing) | + +See [ONBOARDING.md](./ONBOARDING.md) for in-depth context on each technology's role in the product. + +## Commands (repo root) + +From `package.json` and ONBOARDING §4, §12: + +- `yarn install` — install all workspaces +- `yarn start` — dev server for the app (HMR; default `http://localhost:3000`) +- `yarn test` — Vitest (watch) +- `yarn test:app -- ` — single file or pattern +- `yarn test:update` — Vitest with snapshot updates, non-watch +- `yarn test:all` — typecheck, ESLint, Prettier, Vitest non-watch (CI-shaped) +- `yarn test:typecheck` / `yarn test:code` / `yarn test:other` — tsc, ESLint, Prettier check +- `yarn fix` — Prettier write + ESLint fix +- `yarn build:packages` / `yarn build:app` — package and app builds + +Env: copy `.env.development` → `.env.development.local`; variables are documented in ONBOARDING §4 and `excalidraw-app/vite.config.mts`. + +## Architecture (do not fight it) + +Aligned with `.cursor/rules/architecture.mdc` and ONBOARDING §7–9: + +- **Editor state and actions:** Changes go through the action system (`ActionManager`, actions under `packages/excalidraw/actions/`). Do not introduce parallel global state stores (e.g. Redux) for editor logic. +- **Drawing:** Canvas 2D (rough.js, layered static/interactive canvases). Do not use React DOM, react-konva, Fabric, or Pixi for the canvas scene. +- **State shape:** `AppState` and related types live in `packages/excalidraw/types.ts` (treat as sensitive; see Protected files). +- **Collaboration:** Implemented in `excalidraw-app/collab/`, not inside the published library (ONBOARDING §10). + +ONBOARDING documents **dual Jotai stores** (`editorJotaiStore` vs `appJotaiStore`) and **class-based `App.tsx`** for hot-path rendering. For **new** React UI in packages, follow `.cursor/rules/conventions.mdc` (functional components + hooks). Do not refactor legacy class roots unless the task explicitly requires it. + +## Protected files + +**Do not modify** without explicit human approval and the bar in `.cursor/rules/do-not-touch.mdc`: + +- `packages/excalidraw/scene/Renderer.ts` +- `packages/excalidraw/data/restore.ts` +- `packages/excalidraw/actions/manager.tsx` +- `packages/excalidraw/types.ts` + +Requirements if approved: full dependency understanding, full test suite, manual QA. + +## Code conventions (packages) + +From `.cursor/rules/conventions.mdc` and copilot-instructions: + +- **Components:** Prefer functional components and hooks for new code; props type `{ComponentName}Props`; **named exports only** (no default exports); colocate tests as `ComponentName.test.tsx`. +- **TypeScript:** Strict; avoid `any` and `@ts-ignore`; prefer `type` for simple types; use `import type { … }`. +- **Files:** kebab-case for non-component files; PascalCase for component filenames. +- **Performance / style:** Prefer immutability, optional chaining, nullish coalescing; avoid unnecessary allocation where practical; use CSS modules for component styling per copilot-instructions. + +## Testing + +Follow `.cursor/rules/testing.mdc`: + +- **Vitest** only (`describe` / `it` / `expect`, `vi` for mocks); no new test runners or test dependencies without approval. +- Match each package’s layout: colocated tests, or existing `tests/` / `__tests__/`. +- For `packages/excalidraw` UI tests, use `packages/excalidraw/tests/test-utils` and existing helpers (e.g. `reseed` from `@excalidraw/common` where suites require determinism). +- Do not bypass architecture in tests (e.g. fake state paths that contradict the real action/store model). +- Verify with `yarn test` or targeted `yarn test:app -- `; use `yarn test:all` before broad changes. + +Coverage thresholds are listed in ONBOARDING §12. + +## Dependencies + +No new npm packages without explicit approval. Check `packages/utils/` before adding external helpers (`.cursor/rules/architecture.mdc`). + +## Security + +Follow [`.cursor/rules/security.mdc`](./.cursor/rules/security.mdc): no committed secrets; use documented env files; sanitize user-controlled HTML; do not add dependencies without approval; treat collab and persistence boundaries carefully. Use the rule’s **How to verify** steps when touching env, markup, or network code. + +## Verifying agent output + +- After substantive edits, run the checks listed in the relevant rule **How to verify** sections (minimum: `yarn test:typecheck` and `yarn test:code`; broader: `yarn test:all`). +- Confirm protected files are untouched unless the task explicitly approved changing them. +- For app or collab work, smoke-test with `yarn start` when behavior is user-visible. + +## Common task entry points + +ONBOARDING §14–15 maps tasks to directories (toolbar, properties panel, element types, rendering, collab, shortcuts, i18n, history, export, etc.). Use that table when routing changes. + +## i18n + +New user-visible strings: add keys to `packages/excalidraw/locales/en.json` (source of truth), use `t("…")` in UI (ONBOARDING §14). + +## Further reading + +- [Official Excalidraw docs](https://docs.excalidraw.com) +- `dev-docs/` — developer documentation sources +- `packages/excalidraw/CHANGELOG.md` — library release notes diff --git a/ONBOARDING.md b/ONBOARDING.md new file mode 100644 index 0000000..0178714 --- /dev/null +++ b/ONBOARDING.md @@ -0,0 +1,751 @@ +# Excalidraw — New Engineer Onboarding Guide + +Welcome to the Excalidraw project! This guide will help you understand the codebase, get set up for development, and feel productive as quickly as possible. + +--- + +## Table of Contents + +1. [What Is Excalidraw?](#1-what-is-excalidraw) +2. [Repository at a Glance](#2-repository-at-a-glance) +3. [Package Dependency Graph](#3-package-dependency-graph) +4. [Getting Started](#4-getting-started) +5. [Package Deep-Dives](#5-package-deep-dives) +6. [The Web Application](#6-the-web-application) +7. [Architecture — How It All Works](#7-architecture--how-it-all-works) +8. [State Management](#8-state-management) +9. [Rendering Pipeline](#9-rendering-pipeline) +10. [Collaboration Architecture](#10-collaboration-architecture) +11. [Persistence & Storage](#11-persistence--storage) +12. [Testing](#12-testing) +13. [Build System](#13-build-system) +14. [Key Workflows](#14-key-workflows) +15. [Where to Find Things](#15-where-to-find-things) +16. [Glossary](#16-glossary) + +--- + +## 1. What Is Excalidraw? + +Excalidraw is an open-source, browser-based whiteboard tool for sketching hand-drawn-style diagrams. It lives at [excalidraw.com](https://excalidraw.com) and is also published as an embeddable React component (`@excalidraw/excalidraw`) that other applications can integrate. + +**There are two distinct products in this repo:** + +| Product | Description | Audience | +|---|---|---| +| `@excalidraw/excalidraw` (npm) | Embeddable React component library | Third-party developers | +| excalidraw.com app | Full-featured collaborative whiteboard | End users | + +Both live in the same monorepo and share the same underlying code. + +--- + +## 2. Repository at a Glance + +```text +excalidraw-master/ +├── packages/ +│ ├── excalidraw/ ← @excalidraw/excalidraw (main React component, npm published) +│ ├── common/ ← @excalidraw/common (shared constants, utils, colors) +│ ├── element/ ← @excalidraw/element (element types, rendering, history) +│ ├── math/ ← @excalidraw/math (2D geometry primitives) +│ └── utils/ ← @excalidraw/utils (export utilities for host apps) +│ +├── excalidraw-app/ ← excalidraw.com (private app, NOT published to npm) +│ +├── examples/ +│ ├── with-nextjs/ ← Next.js integration example +│ └── with-script-in-browser/ ← plain HTML /