# Contributing to @eigenpal/docx-editor-react

Thanks for your interest in contributing! This guide will help you get started.

## Prerequisites

- [Bun](https://bun.sh/) (v1.0+)
- [Node.js](https://nodejs.org/) (v18+)

## Development Setup

```bash
# Clone the repo
git clone https://github.com/eigenpal/docx-editor.git
cd docx-editor

# Install dependencies
bun install

# Start the dev server
bun run dev
# Open http://localhost:5173
```

Working on the parser, serializer, or layout engine? `bun run reference:fetch` pulls the gitignored ECMA-376 PDFs and supplementary ZIPs (~58 MB). The handwritten quick-refs and XSD schemas under `reference/` stay committed.

## Running Tests

```bash
# Type checking (fast, run often)
bun run typecheck

# Unit tests
bun test

# E2E tests (requires Playwright browsers)
npx playwright install --with-deps chromium
npx playwright test --timeout=30000 --workers=4

# Single test file
npx playwright test e2e/tests/formatting.spec.ts --timeout=30000
```

## Code Style

The project uses ESLint and Prettier with pre-commit hooks (Husky + lint-staged), so formatting is handled automatically on commit.

```bash
# Manual lint/format
bun run lint:fix
bun run format
```

## Contributor License Agreement

Contributors are required to sign our [Contributor License Agreement](CLA.md). The CLA assistant will leave a comment on your first pull request with signing instructions — one short comment, about 30 seconds. That signature covers all of your future contributions.

## Making Changes

1. **Fork** the repository and create a branch from `main`
2. **Read the code** before modifying it — understand the dual rendering system (see [Architecture](docs/ARCHITECTURE.md))
3. **Make your changes** — keep them focused and minimal
4. **Add/update tests** for your changes (see `e2e/` for E2E tests)
5. **Verify** everything works:
   ```bash
   bun run typecheck && bun test && bun run build:packages
   ```
6. **Submit a PR** against `main` — the CLA bot will prompt you on your first one

## Architecture Overview

The editor has two rendering systems:

- **Hidden ProseMirror** — the real editing state (selection, undo/redo, keyboard input)
- **Visible Pages** (layout-painter) — what the user sees, rebuilt from PM state on every change

See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the full architecture and [CLAUDE.md](CLAUDE.md) for the agent-facing quick reference (also useful for humans).

## Public API Surface

Every published package's `@public` exports are locked in `docs/api/<pkg-slug>/<entry>.api.md` snapshots generated by API Extractor. CI runs `bun run api:check` and fails on undocumented drift.

If you change a `@public` symbol — or add a new one — regenerate and commit the snapshot:

```bash
bun run --filter '@eigenpal/docx-editor-<pkg>' build
bun run api:extract
git add docs/api/<pkg-slug>/
```

The CI error message points at the source file for each drifted entry, so the fix is mechanical. Full details live in [CLAUDE.md](CLAUDE.md) under "Public API surface".

**Adding a `DocxEditorProps` field or `DocxEditorRef` method to either adapter** also requires updating `scripts/parity/parity.contract.json` — the cross-adapter parity contract that tracks which fields are shared, deliberately Vue-deferred, or Vue-exclusive. `bun run check:parity-contract` (also run in CI) fails until the contract acknowledges the new symbol. The error message names the symbol and tells you which bucket to add it to.

**Adding a new Vue composable**: declare a `Use<Name>Return` interface and annotate the function's return type with it. Without the annotation the snapshot recursively inlines core's internal types into Vue's public surface.

**Adding a new published package**: edit `scripts/lib/packages.mjs` (one entry — name, root, slug, tsconfig, build hint). Add matching `api:extract` / `api:check` scripts in the new package's `package.json` delegating to `../../scripts/api-extractor.mjs --package <name>`. Then run `bun run api:extract && bun run docs:json` to generate snapshots.

### Consumer-facing JSON docs (`docs/json/`)

The same `@public` surface is also emitted as structured JSON for downstream docs sites: `bun run docs:json` writes `docs/json/<pkg-slug>/<subpath>.json` per published subpath, plus a root `docs/json/index.json`. **The JSON is gitignored** — downstream sites (e.g. `docx-editor-page`) clone the repo and run the script themselves. CI runs `bun run docs:json` as a smoke test so generator breakage surfaces in this repo, not in the consumer's build.

## Adapter Parity

The editor ships first-party adapters for React (`packages/react`) and Vue (`packages/vue`). Both share `@eigenpal/docx-editor-core`, which owns the parser, ProseMirror schema, layout engine, layout bridge (page mapping, footnote convergence, header/footer measurement), and serializer. Adapters only own their framework-specific shell, components, and lifecycle wiring.

**When you touch layout, parsing, or rendering logic, put it in core, not in an adapter.** If you copy a 30-line helper from React to Vue, you've created a divergence trap. The footnote convergence loop (`stabilizeFootnoteLayout` in `packages/core/src/layout-bridge/footnoteLayout.ts`) is the canonical example: one helper, both adapters call it.

Parity smoke tests live under `e2e/tests/parity/smoke/` and run each spec against both demos. Add one when you fix a bug that could plausibly affect rendering on either side.

## Reporting Bugs

Open an issue at [github.com/eigenpal/docx-editor/issues](https://github.com/eigenpal/docx-editor/issues) with:

- Steps to reproduce
- Expected vs actual behavior
- Attach a `.docx` file if relevant (remove sensitive content first)

## License

By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE).
