## Unreleased

* feat(core): add `duplicateKey` option to duplicate item(s) on drop instead of moving

## <small>2.1.5 (2026-07-26)</small>

* fix(core): guard drop-animation cleanup against destroying a newer drag's ghost (#141) ([5454a40](https://github.com/jjeff/resortable/commit/5454a40)), closes [#141](https://github.com/jjeff/resortable/issues/141) [#131](https://github.com/jjeff/resortable/issues/131) [#141](https://github.com/jjeff/resortable/issues/141)

## <small>2.1.4 (2026-07-26)</small>

* fix(core): gate HTML5 dragover swaps on a rect synthesized from the drag cursor (#130) (#142) ([d35a478](https://github.com/jjeff/resortable/commit/d35a478)), closes [#130](https://github.com/jjeff/resortable/issues/130) [#142](https://github.com/jjeff/resortable/issues/142)
* perf(core): coalesce autoscroll scroll-replay to one onPointerMove per frame (#140) ([07269c2](https://github.com/jjeff/resortable/commit/07269c2)), closes [#140](https://github.com/jjeff/resortable/issues/140) [#134](https://github.com/jjeff/resortable/issues/134) [#140](https://github.com/jjeff/resortable/issues/140)
* docs(types): correct swapThreshold default — unset disables the overlap gate (#139) ([cbf0f5f](https://github.com/jjeff/resortable/commit/cbf0f5f)), closes [#139](https://github.com/jjeff/resortable/issues/139) [#132](https://github.com/jjeff/resortable/issues/132) [#139](https://github.com/jjeff/resortable/issues/139)
* ci: remove the dead claude-code-review workflow (#137) ([ff91207](https://github.com/jjeff/resortable/commit/ff91207)), closes [#137](https://github.com/jjeff/resortable/issues/137)
* Merge pull request #138 from jjeff/chore/worktree-setup ([22dd0ac](https://github.com/jjeff/resortable/commit/22dd0ac)), closes [#138](https://github.com/jjeff/resortable/issues/138)
* chore(worktree): automate per-worktree dev environment setup ([9fd2556](https://github.com/jjeff/resortable/commit/9fd2556))

## <small>2.1.3 (2026-07-25)</small>

* fix(core): repair the DragManager listener leak and SwapPlugin; harden tests (#133) ([8f97ef5](https://github.com/jjeff/resortable/commit/8f97ef5)), closes [#133](https://github.com/jjeff/resortable/issues/133)

## <small>2.1.2 (2026-07-25)</small>

* fix(core): give the pointer pipeline event/behavior parity with HTML5 (#121) (#129) ([a22edd6](https://github.com/jjeff/resortable/commit/a22edd6)), closes [#121](https://github.com/jjeff/resortable/issues/121) [#129](https://github.com/jjeff/resortable/issues/129)

## <small>2.1.1 (2026-07-13)</small>

* fix(core): recompute drop target as autoscroll moves the list under the pointer (#125) ([06e87d1](https://github.com/jjeff/resortable/commit/06e87d1)), closes [#125](https://github.com/jjeff/resortable/issues/125)
* docs(readme): refresh Features/Options, document React hook & new options (#128) ([3b8bc14](https://github.com/jjeff/resortable/commit/3b8bc14)), closes [#128](https://github.com/jjeff/resortable/issues/128)

## 2.1.0 (2026-07-13)

* feat(core): add `hitArea` option to claim drops in a surrounding region (#127) ([15e0863](https://github.com/jjeff/resortable/commit/15e0863)), closes [#127](https://github.com/jjeff/resortable/issues/127) [#126](https://github.com/jjeff/resortable/issues/126)

## <small>2.0.4 (2026-07-11)</small>

* Merge pull request #120 from jjeff/chore/dependabot-dev-deps ([3aca705](https://github.com/jjeff/resortable/commit/3aca705)), closes [#120](https://github.com/jjeff/resortable/issues/120)
* Merge pull request #122 from jjeff/fix/issue-75-sort-false ([bfac675](https://github.com/jjeff/resortable/commit/bfac675)), closes [#122](https://github.com/jjeff/resortable/issues/122)
* Merge pull request #123 from jjeff/test/unskip-e2e-suites ([868f10c](https://github.com/jjeff/resortable/commit/868f10c)), closes [#123](https://github.com/jjeff/resortable/issues/123) [#36](https://github.com/jjeff/resortable/issues/36) [#73](https://github.com/jjeff/resortable/issues/73) [#74](https://github.com/jjeff/resortable/issues/74) [#76](https://github.com/jjeff/resortable/issues/76) [#77](https://github.com/jjeff/resortable/issues/77) [#79](https://github.com/jjeff/resortable/issues/79)
* test(e2e): add CSS-positioned fixtures to un-skip swap-behavior suite (#77) ([95c1d46](https://github.com/jjeff/resortable/commit/95c1d46)), closes [#77](https://github.com/jjeff/resortable/issues/77)
* test(e2e): fix firefox FLIP serialization + skip ghost drag-class on mobile ([c0c1559](https://github.com/jjeff/resortable/commit/c0c1559)), closes [#48](https://github.com/jjeff/resortable/issues/48) [#62](https://github.com/jjeff/resortable/issues/62)
* test(e2e): make delay-options deterministic via clock API (#76) ([f38c7ce](https://github.com/jjeff/resortable/commit/f38c7ce)), closes [#76](https://github.com/jjeff/resortable/issues/76)
* test(e2e): resolve basic-sortable hover + touch skips (#74) ([c959fb4](https://github.com/jjeff/resortable/commit/c959fb4)), closes [#74](https://github.com/jjeff/resortable/issues/74)
* test(e2e): rewrite animation suite to pointer pipeline + deterministic timing (#79) ([752d786](https://github.com/jjeff/resortable/commit/752d786)), closes [#79](https://github.com/jjeff/resortable/issues/79) [#73](https://github.com/jjeff/resortable/issues/73)
* test(e2e): rewrite atomic-drag event tests to pointer pipeline (#73) ([bc0d52c](https://github.com/jjeff/resortable/commit/bc0d52c)), closes [#73](https://github.com/jjeff/resortable/issues/73)
* test(e2e): skip mouse-clone feature-demos tests on mobile emulation ([831496e](https://github.com/jjeff/resortable/commit/831496e)), closes [#48](https://github.com/jjeff/resortable/issues/48) [#62](https://github.com/jjeff/resortable/issues/62)
* test(keyboard): un-skip Shift+Arrow selection tests + add e2e coverage (#36) ([607a5ac](https://github.com/jjeff/resortable/commit/607a5ac)), closes [#36](https://github.com/jjeff/resortable/issues/36)
* fix(core): honor sort:false to block intra-list reorder (#75) ([a8b7265](https://github.com/jjeff/resortable/commit/a8b7265)), closes [#75](https://github.com/jjeff/resortable/issues/75) [#34](https://github.com/jjeff/resortable/issues/34)
* chore(deps): patch dev-only Dependabot advisories ([119883e](https://github.com/jjeff/resortable/commit/119883e))
* chore(test): allow PW_PORT to override e2e dev-server port ([7016174](https://github.com/jjeff/resortable/commit/7016174))
* chore(test): wire PW_PORT through to vite dev-server port ([3bc4e75](https://github.com/jjeff/resortable/commit/3bc4e75))

## <small>2.0.3 (2026-07-11)</small>

* fix(docs): link docs-folder index to a file, not the directory ([60cb9eb](https://github.com/jjeff/resortable/commit/60cb9eb))
* docs: add badges, cross-doc navigation, and canonical-repo ribbon ([883af98](https://github.com/jjeff/resortable/commit/883af98))
* docs: generalize consumer-app references in comments and design notes ([5415a5a](https://github.com/jjeff/resortable/commit/5415a5a))
* docs: remove alpha banner from landing page, sync version to 2.0.2 ([8457c1d](https://github.com/jjeff/resortable/commit/8457c1d))
* Merge pull request #119 from jjeff/chore/remove-consumer-app-references ([6e5727c](https://github.com/jjeff/resortable/commit/6e5727c)), closes [#119](https://github.com/jjeff/resortable/issues/119)
* ci(docs): sync landing-page version badge from package.json ([a4d2ecd](https://github.com/jjeff/resortable/commit/a4d2ecd))

## <small>2.0.2 (2026-07-11)</small>

* fix: ignore keys typed into editable elements inside sortable items (#118) ([98220a3](https://github.com/jjeff/resortable/commit/98220a3)), closes [#118](https://github.com/jjeff/resortable/issues/118)
* Merge pull request #117 from jjeff/fix/onmove-macos-viewport-clamp ([d671af0](https://github.com/jjeff/resortable/commit/d671af0)), closes [#117](https://github.com/jjeff/resortable/issues/117)
* test(e2e): clamp empty-target drop point to viewport (macOS onMove flake) ([50ce45f](https://github.com/jjeff/resortable/commit/50ce45f))
* ci: stop attaching dist/** as GitHub release assets ([30b540b](https://github.com/jjeff/resortable/commit/30b540b))

## <small>2.0.1 (2026-07-11)</small>

* fix: add repository field required for npm provenance ([07d8ec2](https://github.com/jjeff/resortable/commit/07d8ec2))
* fix: correct misleading alpha warnings in published README and docs ([d092d83](https://github.com/jjeff/resortable/commit/d092d83))
* Merge pull request #115 from jjeff/ci/npm-trusted-publishing ([56108c8](https://github.com/jjeff/resortable/commit/56108c8)), closes [#115](https://github.com/jjeff/resortable/issues/115)
* ci: pin Node and npm versions in release workflow ([c54f936](https://github.com/jjeff/resortable/commit/c54f936))

# Changelog

All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

This changelog is automatically generated by [semantic-release](https://github.com/semantic-release/semantic-release).

## [Unreleased]

<!-- #43 cleanup -->

### Added

- **`Sortable.utils` parity additions** (issue #43): Restored four helpers from legacy Sortable.js v1 that round out the v1 utility footprint and are likely to be touched during a v1 → v2 migration:
  - `off(el, type, handler)` — `removeEventListener` wrapper, symmetric to `on`.
  - `closest(el, selector, ctx?)` — walk up from `el` and return the nearest ancestor (including `el` itself) matching `selector`, optionally bounded by `ctx`.
  - `toggleClass(el, name, force?)` — `classList.toggle` wrapper that forwards the optional `force` argument.
  - `clone(el)` — deep-clone an element via `cloneNode(true)`, typed to return the same element subtype.
  - Unit coverage: `tests/unit/sortable-utils.test.ts`.
  - The migration guide and README Static API section are updated to reflect the expanded surface (`on`, `off`, `index`, `insertAt`, `closest`, `toggleClass`, `clone`). Helpers intentionally *not* restored: `is` (use `Element.matches`), `extend` (use spread / `Object.assign`), `css` / `find` / `bind` / `detectDirection` / `getChild` / `expando`.

### Changed

- Public types audit (`src/types/index.ts`): removed two unused public exports — `ElementSelector` (never referenced in `src/` or `tests/`) and the `@internal`-tagged `DeepPartial` (also unreferenced). Neither was re-exported from `src/index.ts`, so this is not a breaking change for documented consumers; the `@public` `ElementSelector` is removed under the v2.0.0 pre-release window per the migration policy in #44.
- `.github/copilot-instructions.md`: corrected stale ESM filename (`sortable.es.js` → `sortable.esm.js`) to match the rename landed in #90.

<!-- /#43 cleanup -->

<!-- #publish-polish: vite filename + SECURITY.md + CONTRIBUTING.md -->

### Build System

- Fix vite library build to emit `sortable.esm.js` (was `sortable.es.js`) to match the `module` and `exports.import` paths in `package.json`. Prevents broken ESM resolution after first npm publish.
- Update `examples/simple-list.html`, `examples/multi-list.html`, and `size-limit` config to match the new ESM filename.

### Documentation

- Add `SECURITY.md` — vulnerability disclosure policy + supported versions.
- Add `CONTRIBUTING.md` — setup, conventional commit conventions, PR gate, repo layout.

<!-- #34 multidrag reconcile -->
### BREAKING CHANGES

- **`MultiDragPlugin` removed** (issue #34). The v1-compat `MultiDragPlugin` shim was previously documented as deprecated/no-op and has now been deleted entirely. Multi-drag remains a first-class core feature — enable it with the `multiDrag: true` option on a `Sortable` instance, exactly as the migration guide and README already document.
  - **Migration:** replace any `Sortable.mount(MultiDragPlugin.create())` or `PluginSystem.register(MultiDragPlugin.create())` call with `new Sortable(el, { multiDrag: true })`. Remove the `import { MultiDragPlugin } from 'resortable/plugins'` line. No other code change is required — selection / drag behaviour is unchanged because the shim was already a no-op for everyone using `multiDrag: true`.
  - The `MultiDragOptions` type is also removed from `resortable/plugins`. Per-instance selection behaviour is configured via `SortableOptions` (`selectedClass`, `deselectOnClickOutside`, …).

### Removed

- `src/plugins/MultiDragPlugin.ts` — file deleted.
- `MultiDragPlugin` and `MultiDragOptions` from `resortable/plugins` exports.
- `MultiDragPlugin` entry from `registerAllPlugins()` and from the dev playground (`index.html`) / `demo.html` bootstrap.
- "MultiDrag Plugin" e2e describe block in `tests/e2e/plugin-functionality.spec.ts` — those selection scenarios are covered against the real core multi-drag in `tests/e2e/multi-select.spec.ts`. The generic plugin-system lifecycle coverage in `plugin-functionality.spec.ts` / `plugin-system.spec.ts` was kept and the inline mock plugin renamed `MultiDrag` → `DemoSelect` so the test name no longer claims a built-in that doesn't exist.
<!-- /#34 multidrag reconcile -->

### Build System

- Release engineering prep for first npm publish (issue #42):
  - Added top-level `LICENSE` file (MIT, copyright 2025-2026 Jeff Robbins) so npm tarballs and GitHub UI both surface the license. `package.json` already declared `"license": "MIT"`; this fills in the actual text.
  - Added top-level `NOTICE` file crediting SortableJS as the project Resortable is rewriting, with a pointer to the `legacy-sortable/` submodule for parity work.
  - Added `.github/workflows/dependency-review.yml` — runs GitHub's `dependency-review-action@v4` on every PR to `main` / `develop`, fails on high-severity vulnerabilities, and comments a summary in the PR.
  - `README.md` License section now links to `./LICENSE` and a new `## Acknowledgments` section credits SortableJS + points to `NOTICE`.
  - `.releaserc.json` `npmPublish` left `false` deliberately — flipped in a follow-up PR after `NPM_TOKEN` is stored.

### Tests

<!-- #40 a11y -->
- WCAG 2.1 AA axe-core audit (issue #40):
  - New `tests/e2e/accessibility-audit.spec.ts` runs axe-core against the dev playground (`/`) plus every page in the curated examples site (`basic`, `shared-lists`, `kanban`, `accessibility`, `handle-filter`, `multi-drag`, `swap`, `clone-mode`, `custom-plugin`) with tag filter `['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']`.
  - Added `@axe-core/playwright` as a devDependency.
  - New `accessibility` job in `.github/workflows/ci.yml` runs the audit on every PR as a hard gate.
  - Source fix: `KeyboardManager.updateItemAttributes()` now writes `role="option"` (was `role="listitem"`) so items are valid children of the container's `role="listbox"` and `aria-selected` is an allowed attribute. Matching assertions updated in `tests/unit/keyboard-manager.test.ts` and `tests/e2e/keyboard-navigation.spec.ts`.
  - Dev playground (`index.html`) restructured: per-list `<h3>` / `<h4>` headings moved out of the sortable container so axe `aria-required-children` passes; the kanban-board demo and the eight Developer-section lists were affected.
  - Contrast fix: examples `_shared.css` `--text-muted` darkened from `#6a737d` to `#586069` so column / palette headings on the kanban + clone-mode pages meet WCAG AA 4.5:1.
  - Suppression: `nested-interactive` is disabled for `/` only, with a code-level reason — the File Explorer demo exercises nested sortables on purpose (an outer folder is itself draggable AND contains a draggable list). Modernised ARIA pattern for nested DnD tracked as a follow-up.
<!-- end #40 -->

- Un-skip 3 Mobile Chrome e2e tests (#48); assertion now device-aware
- Skip 5 consistently-flaky Mobile Safari dragAndDrop tests pending fix; tracked in #62

### Features

- `ignore` option restored with the legacy Sortable.js v1 default of `'a, img'` (issue #30):
  - Descendants of a draggable item matching the selector no longer initiate a drag, so users can click `<a>` links and natively drag `<img>` elements without a sortable drag starting underneath them.
  - Default applies even when no options are passed; pass `ignore: ''` to disable. Custom selectors override the default entirely (e.g. `ignore: 'input, button'` makes `<a>` draggable again).
  - Target-only match against `event.target` (legacy `Sortable.js:620` parity). Comma-separated criteria are split and matched individually so a single malformed selector cannot break the rest. The gate fires before any drag state mutation in both the HTML5 and pointer pipelines via the shared `shouldAllowDrag` check.
- `onMove(MoveEvent, originalEvent)` is now wired through the drag pipeline with the full legacy Sortable.js contract (issue #33):
  - Returning `false` cancels the move for that tick — no placeholder shift, no `sort` / `update` / `change` emissions.
  - Returning `-1` forces insert BEFORE the related item; `1` forces insert AFTER.
  - The second argument is the live DOM event that triggered the dragover (`DragEvent` on the HTML5 path, `PointerEvent` on the pointer / `forceFallback` path).
  - The `'move'` event-system channel still fires as a notification side-channel so internal subscribers and plugins continue to work — only the option callback controls cancellation / override.
  - The TypeScript signature widened from `(event, originalEvent) => boolean | void` to `(event, originalEvent) => boolean | -1 | 1 | void` to express the override return values. Runtime behaviour for existing handlers is unchanged.
- `onMove` cross-zone enter is now wired in both pipelines (issue #60, follows #33 / #59):
  - The HTML5 `dragover` cross-zone enter site and the pointer-pipeline cross-zone enter site both dispatch `onMove` BEFORE the target insertion, so returning `false` keeps the item (or clones) in the source zone for that tick.
  - `MoveEvent.related` falls back to the target container itself when no draggable sibling is under the pointer (matches legacy `Sortable.js:1789`). The static `related: HTMLElement` type is preserved.
  - Multi-item drags batch as all-or-nothing: a single `onMove` call per cross-zone enter, with `items: draggedItems`, then either insert every item or skip the whole batch.
  - The `-1` / `1` override is applied only when there is a real sibling; with the container fallback the override has no meaningful side so the natural insertion proceeds.
  - HTML5 cross-zone routes through the source Sortable's `onMove` (legacy parity — `Sortable.js:1772` reads `onMoveFn` off the source). The pointer pipeline already runs on the source DragManager so needs no rerouting.
- HTML5 fallback drag system implemented end-to-end (issue #29, PRs #53, #54, #55, #56):
  - `forceFallback` — opt out of the HTML5 DnD listener path and drive drag entirely via pointer events
  - `fallbackClass` — additional class applied to the pointer-driven ghost alongside `ghostClass`
  - `fallbackOnBody` — append the ghost to `<body>` (legacy parity) or keep it inside the sortable zone (default `false`)
  - `fallbackOffsetX` / `fallbackOffsetY` — pixel offset for the ghost relative to the cursor (legacy `fallbackOffset.x/y`)
  - `fallbackTolerance` — minimum pointer travel (Chebyshev distance) before the drag commits, with a capture / commit phase split in the pointer pipeline
- Ghost clone now strips identity-bearing attributes (`id`, the configured `dataIdAttr`, `aria-*`, `tabindex`) so the ghost cannot collide with the original when both live inside the same zone (default `fallbackOnBody: false`).

### Internal

- `GhostManager.createGhost` refactored to take an options bag instead of a long positional argument list. `GhostManager` is not part of the public API, so this is not a consumer-visible change.
- Pointer drag pipeline split into a capture phase (pre-tolerance) and a commit phase (post-tolerance). Observable externally only when `fallbackTolerance > 0`.

### Tests

- First-pass triage of E2E `test.skip` / `test.fixme` entries (issue #35):
  - New audit inventory at `docs/plans/e2e-skip-audit.md` classifies every skip into `easy-unskip`, `tracked-elsewhere`, `needs-new-issue`, or `valid-conditional`.
  - Un-skipped three tests verified passing on chromium: `grid-layout.spec.ts:25` (`test.fixme` → `test` for CSS grid assertions), `grid-layout.spec.ts:52` (removed chromium-only animation-timing conditional skip), `ghost-elements.spec.ts:89` (same pattern).
  - One un-skip candidate (`basic-sortable.spec.ts:93` hover effects) failed empirically and was re-skipped with a citation to #74.
  - Filed seven new tracking issues (#73–#79) covering grouped root causes (atomic dragAndDrop limitation, delay-option timing, clone-mode regressions, swap-threshold fixture, cross-list keyboard drag, animation suite rework).
  - Refreshed comments on tracked-elsewhere skips (Mobile Chrome dragAndDrop timeout entries in `event-callbacks.spec.ts`, `drag-and-drop.spec.ts`, `ghost-elements.spec.ts:158`) to cite #48 directly; multi-drag skips in `multi-select.spec.ts` and `feature-demos.spec.ts` to cite #34.

### Documentation

<!-- #40 a11y -->
- New `docs/accessibility.md` — canonical accessibility reference (issue #40):
  - Full keyboard contract table (Tab, arrow keys, Home / End, Space, Ctrl+A, Enter, Escape, Shift+arrow placeholder for #36).
  - Per-element ARIA attribute reference for the container (`role="listbox"`, `aria-multiselectable`, `aria-label`) and items (`role="option"`, `aria-setsize`, `aria-posinset`, `aria-selected`, `aria-grabbed`, roving `tabindex`).
  - Heading-placement guidance: keep `<h3>` / `<h4>` headings _outside_ the sortable container to satisfy axe's `aria-required-children` rule.
  - Screen-reader live region docs (single shared `.sortable-announcer`, `aria-live="assertive"`, the messages emitted on select / grab / move / drop / cancel).
  - How to run the audit locally + the list of known follow-ups (deprecated `aria-grabbed`, nested-DnD ARIA, Shift+Arrow range selection #36).
- README "Accessibility" feature bullet now links the new `docs/accessibility.md` reference.
- `docs/index.html` landing page gains a dedicated "Accessibility" card pointing to the new reference.
<!-- end #40 -->

- Bundled the examples + dev playground as a live gh-pages site (issue #71, sub-issue of #41):
  - New `vite.config.examples.ts` — multi-page Vite build that emits `docs/demo/` with bundled HTML + chunked JS assets. Pages: root `index.html` (dev playground) plus the ten curated examples under `examples/`.
  - New npm script `build:examples` runs the multi-page build; wired into `.github/workflows/docs.yml` so each docs deploy publishes the live site alongside the TypeDoc API reference.
  - `examples/simple-list.html` and `examples/multi-list.html` are excluded from the bundle — they import the built library from `../dist/sortable.es.js` and exist only as E2E test fixtures.
  - Root `index.html` rewritten to use relative dynamic imports (`./src/...`) so it bundles cleanly. `npm run dev` and the existing E2E suite continue to hit it unchanged.
  - `docs/index.html` landing page now links the live examples (`./demo/examples/`) and adds a dedicated card for the dev playground (`./demo/`); `README.md` advertises the live examples URL above the local-dev fallback.
  - `/docs/demo/` added to `.gitignore` (build output, like `/docs/api/`).
- Added `docs/migration-from-sortable-v1.md` — canonical migration guide from Sortable.js v1.x to Resortable v2 (option deltas, plugin imports, breaking changes, before/after recipes). Linked from `README.md`. (issue #66)
- Added a curated examples site under `examples/` (issue #65, sub-issue of #41):
  - `examples/index.html` landing page links nine single-concept demos: `basic.html`, `shared-lists.html`, `kanban.html`, `clone-mode.html`, `swap.html`, `multi-drag.html`, `handle-filter.html`, `accessibility.html`, `custom-plugin.html`.
  - Each example is a standalone page that imports the library from `../src/index.ts` (Vite-resolved during `npm run dev`), shows a live demo, and reproduces the wiring code inline as an annotated `<pre><code>` block.
  - Shared `examples/_shared.css` keeps per-example CSS minimal.
  - The existing `examples/simple-list.html` and `examples/multi-list.html` are intentionally left unchanged — they're E2E test fixtures referenced by `tests/e2e/animation*.spec.ts`. The curated set is additive.
  - Linked from `README.md`.
- README updates (issue #64, sub-issue of #41):
  - Added CDN install snippet (UMD via unpkg + jsDelivr, version-pinned to `2.0.0-alpha.1`, `window.Sortable` global usage) and a note that ESM via npm is the recommended path.
  - Linked the buried `src/plugins/PluginDevelopmentGuide.md` from the Plugins section so custom-plugin authors can find it.
  - Added an "API Reference" section with a placeholder link to the TypeDoc-generated docs site (deploy tracked in #63).
  - Added a short "Framework wrappers" section noting React/Vue/Svelte wrappers are planned (tracked in #44).
  - Expanded the built-in plugins list to match `src/plugins/index.ts` (AutoScroll, MarqueeSelect, OnSpill, Swap; MultiDrag shim noted).
  - Added an install-from-Git fallback note since `resortable` is not yet on npm.
- Documentation infrastructure for the gh-pages site (issue #63, sub-issue of #41):
  - Lifted `src/plugins/PluginDevelopmentGuide.md` to `docs/plugin-development.md` so it lives alongside the other shipped docs; updated the references in `README.md` and `examples/custom-plugin.html`.
  - Added `docs/index.html` — a static landing page served at the gh-pages root with cards linking to the API reference, migration guide, plugin guide, examples, and the GitHub repo. The Markdown guides link to their GitHub-rendered versions (pragmatic choice — gh-pages does not render `.md` and a Markdown→HTML build step was deferred).
  - Fixed the four TypeDoc warnings emitted by `npm run docs:build`:
    - Moved the `Sortable.option` TSDoc block from the getter overload to the implementation signature so the `@param value` matches the implementation's parameter list.
    - Re-exported `DragManager`, `DropZone`, `EventSystem` (with `Listener` / `SortableEventSystem`), and their referenced managers (`SelectionManager`, `KeyboardManager`, `GroupManager`, `AnimationManager`) from `src/index.ts` so TypeDoc emits them. These are genuine public API — the Plugin Development Guide documents `sortable.eventSystem.on(...)` and every built-in plugin reads `sortable.dragManager?.selectionManager`, so `@internal` was the wrong call. The `@internal` tag on `EventSystem` itself was removed for the same reason.
  - Switched `typedoc.json` to TypeDoc's default HTML theme (removed `typedoc-plugin-markdown` plugin and devDependency) so `docs/api/index.html` actually exists for the gh-pages deploy. Flipped `treatWarningsAsErrors` to `true` to prevent regressions.
  - Hardened `npm run docs:clean` — it was `rimraf docs`, which would have wiped the tracked migration guide, plugin guide, landing, and `plans/`. Now scoped to `rimraf docs/api`.

## [2.0.0-alpha.1] - 2025-09-01

### Features

- Initial project setup with modern TypeScript toolchain
- Vite and Rollup build system configuration  
- ESLint, Prettier, and VS Code integration
- Comprehensive testing setup with Vitest and Playwright
- GitHub Actions CI/CD pipeline
- Semantic release automation

### Documentation

- Created comprehensive implementation plan
- Added CLAUDE.md for development guidance

This is the initial alpha release of Resortable - a modern TypeScript rewrite of Sortable.js. The foundation is now in place to begin implementing the core drag-and-drop functionality.
