# Implementation Plan: Pagination Controls Redesign

**Branch**: `018-pagination-controls` | **Date**: 2026-05-10 | **Spec**: [spec.md](spec.md)

## Summary

Replace the existing "← Previous / Page X of Y / Next →" pagination bar in `LibraryComponent` with six controls: first-page («), previous-page (‹), up to four numbered page buttons, next-page (›), and last-page (»). All logic stays in the existing component — no new component is introduced (§2.6: no speculative abstraction, only one paginated view exists).

## Technical Context

**Language/Version**: TypeScript (strict mode)
**Primary Dependencies**: Angular (latest stable), Karma + Jasmine
**Storage**: N/A — no data layer changes
**Testing**: Angular TestBed unit tests (component spec)
**Target Platform**: Browser SPA
**Project Type**: Web application — UI only
**Performance Goals**: No measurable regression in render or navigation time
**Constraints**: ESLint + Prettier must pass (§7.3); all existing tests must continue to pass (§5.4)
**Scale/Scope**: Single component change; one paginated view in the app

## Constitution Check

| Principle | Status | Notes |
|-----------|--------|-------|
| §2.6 No speculative abstraction | ✅ PASS | Pagination logic stays inline in LibraryComponent; no new component introduced |
| §5.1 Tests alongside implementation | ✅ PASS | Spec tests for window algorithm, disabled states, and navigation covered in tasks |
| §5.2 Test pyramid | ✅ PASS | Unit tests via TestBed; no integration or E2E tests required for a template change |
| §5.4 Suite must pass before done | ✅ PASS | Gate enforced per task |
| §7.3 Lint/format enforced | ✅ PASS | ESLint + Prettier gate on all tasks |
| §8 Scope boundaries | ✅ PASS | No out-of-scope work touched |

No violations. No Complexity Tracking table needed.

## Project Structure

### Documentation (this feature)

```text
specs/018-pagination-controls/
├── plan.md         ← this file
├── research.md
└── tasks.md        (generated by /speckit-tasks)
```

### Source Code (changed files only)

```text
ui/src/app/library/
├── library.component.ts       ← template, styles, class (page window getter + goToPage/firstPage/lastPage methods)
└── library.component.spec.ts  ← new tests for window algorithm, disabled states, button navigation
```

No new files. No API changes. No data model changes.

## Page Window Algorithm

Given `currentPage` (1-based) and `totalPages`, compute the array of up to four page numbers to display:

```
start = max(1, currentPage - 1)
end   = min(totalPages, start + 3)
start = max(1, end - 3)          ← re-anchor if near the end
pages = [start .. end]
```

Examples:
- Page 1 of 20 → [1, 2, 3, 4]
- Page 7 of 20 → [6, 7, 8, 9]
- Page 19 of 20 → [17, 18, 19, 20]
- Page 2 of 3 → [1, 2, 3]

## New Controls Layout

```
«  ‹  [1] [2] [3] [4]  ›  »
```

- `«` disabled when `currentPage === 1`
- `‹` disabled when `currentPage === 1`
- Active page button has distinct active style
- `›` disabled when `currentPage === totalPages`
- `»` disabled when `currentPage === totalPages`
- Entire bar hidden when `totalPages <= 1` (existing behaviour retained)

## Methods to Add/Change

| Method | Change |
|--------|--------|
| `get pageWindow(): number[]` | New getter — returns array of up to 4 page numbers |
| `goToPage(page: number)` | New — navigates to arbitrary page number |
| `firstPage()` | New — navigates to page 1 |
| `lastPage()` | New — navigates to last page |
| `nextPage()` | Existing — no change needed |
| `prevPage()` | Existing — no change needed |

## Research

No unknowns. Tech stack is fixed (Angular/TypeScript). The windowing algorithm is a standard sliding-window with boundary clamping. No external research required.

**Decision**: Keep all logic in `LibraryComponent` (no child component).
**Rationale**: §2.6 prohibits speculative abstraction; only one paginated view exists in the app. Extracting a `PaginationComponent` would be justified only when a second use case appears.
**Alternatives considered**: Standalone `PaginationComponent` — rejected; no second consumer.
