# 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.