Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .cursor/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -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.
61 changes: 61 additions & 0 deletions .cursor/RULES-AB-VALIDATION.md
Original file line number Diff line number Diff line change
@@ -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)
11 changes: 11 additions & 0 deletions .cursor/commands/add-locale-string.md
Original file line number Diff line number Diff line change
@@ -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.
15 changes: 15 additions & 0 deletions .cursor/commands/analyze-error.md
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions .cursor/commands/generate-component.md
Original file line number Diff line number Diff line change
@@ -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.
11 changes: 11 additions & 0 deletions .cursor/commands/pre-merge-verify.md
Original file line number Diff line number Diff line change
@@ -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 -- <path>`** 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.
16 changes: 16 additions & 0 deletions .cursor/commands/refactor.md
Original file line number Diff line number Diff line change
@@ -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
30 changes: 30 additions & 0 deletions .cursor/rules/architecture.mdc
Original file line number Diff line number Diff line change
@@ -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.
31 changes: 31 additions & 0 deletions .cursor/rules/conventions.mdc
Original file line number Diff line number Diff line change
@@ -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).
25 changes: 25 additions & 0 deletions .cursor/rules/do-not-touch.mdc
Original file line number Diff line number Diff line change
@@ -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.
31 changes: 31 additions & 0 deletions .cursor/rules/excalidraw-app.mdc
Original file line number Diff line number Diff line change
@@ -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)).
38 changes: 38 additions & 0 deletions .cursor/rules/security.mdc
Original file line number Diff line number Diff line change
@@ -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.
29 changes: 29 additions & 0 deletions .cursor/rules/testing.mdc
Original file line number Diff line number Diff line change
@@ -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.
Loading