Files
agatha 781be909bc Feat: Replace Load More with Previous/Next pagination in library
Page size changes from 50 to 24. Library now shows discrete page navigation
with a "Page N of M" indicator, total image count, and URL state (?page=N)
so pages are bookmarkable and the browser Back button works. Tag filter
resets to page 1. Out-of-range page params are clamped silently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 21:08:42 +00:00

4.1 KiB

Implementation Plan: Library Pagination UI

Branch: 015-library-pagination | Date: 2026-05-09 | Spec: spec.md Input: Feature specification from specs/015-library-pagination/spec.md

Summary

Replace the current "Load more" append-on-scroll pattern in the library with discrete page navigation (Previous/Next buttons, page indicator, total count). Page state is persisted to the URL query string for bookmarkability. No API or backend changes required — the API already supports limit and offset parameters.

Technical Context

Language/Version: TypeScript (strict), Angular latest stable Primary Dependencies: Angular Router (query params for URL state), Angular HttpClient (existing) Storage: N/A — UI-only change Testing: Angular TestBed / Jasmine (existing test suite) Target Platform: Browser SPA Project Type: UI feature within existing Angular standalone component Performance Goals: Page load of 24 images replaces 50-image Load More; no regression Constraints: Must preserve existing tag filter query param (?tags=) when updating page param; must not break existing spec tests Scale/Scope: Single component change (library.component.ts) + its spec file

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Principle Status Notes
§2.1 Strict separation PASS UI communicates with API only via HTTP; no storage or DB knowledge in component
§2.6 No speculative abstraction PASS No new abstractions introduced; pagination is a concrete change to one component
§3.2 OpenAPI as contract PASS Uses existing GET /api/v1/images?limit=&offset= contract; no new endpoints
§3.4 Pagination PASS This feature is the UI surface for the API pagination already in place
§5.1 Tests alongside implementation REQUIRED Component spec must be updated alongside each changed behaviour
§5.4 Test gate REQUIRED UI tests must pass; make test-unit passes before task marked done
§6 Tech stack PASS Angular + TypeScript strict — no new dependencies needed
§7.3 Linting REQUIRED ESLint + Prettier enforced; no lint regressions
§8 Scope boundaries PASS Pagination is explicitly required (§3.4); no out-of-scope additions

Post-Phase-1 re-check: No contracts or data model introduced; no new violations.

Project Structure

Documentation (this feature)

specs/015-library-pagination/
├── plan.md              ← this file
├── research.md          ← Phase 0 output
├── quickstart.md        ← Phase 1 output
├── contracts/
│   └── pagination-query.md  ← Phase 1 output
└── tasks.md             ← Phase 2 output (/speckit-tasks)

Source Code (changes only)

ui/src/app/library/
├── library.component.ts      ← primary change
└── library.component.spec.ts ← tests updated alongside

No other files change. No new files added to source tree.

Key Design Decisions

Page size: 24

Fixed at 24 images per page (spec FR-011). Fits common grid widths (2/3/4/6 columns), is a meaningful reduction from the current silent 50-image cap, and divides cleanly. Not user-configurable.

Replace, don't append

Current loadMore() appends items to the array. The new goToPage(n) replaces this.images entirely. The offset field becomes derived from page: offset = (page - 1) * limit.

URL state via Angular Router query params

  • ?page=2 added alongside existing ?tags=cat,funny
  • Use queryParamsHandling: 'merge' when updating page to preserve tag params
  • Use queryParamsHandling: 'merge' when updating tags to preserve page reset (page always resets to 1 on filter change, so page param is removed or set to 1)
  • On ngOnInit, read page from snapshot.queryParamMap; clamp to valid range

Out-of-page-range handling

If URL ?page=99 is requested but only 3 pages exist: silently load page 1. No error state.

Pagination controls visibility

Only shown when totalPages > 1. Total pages = Math.ceil(total / limit).