Skip to content
CAI
Software that uses CAICheck a score

jamal-wia/Paginator

61.8

Adequate · 22 September 2026

20.5k

lines of production code

Kotlin

primary language

7

measurements over time

CAI band scale
CAI trend line
CAI lens gauges

What this system is

This system is a Kotlin Multiplatform pagination library that provides offset-based and cursor-based strategies for managing paginated data across Android, iOS, JVM, JS, and Wasm. It offers dedicated UI bindings for both Jetpack Compose and Android RecyclerViews, handling scroll position preservation, automatic prefetching, and reactive error management. The architecture is modularized into core, strategy-specific, and platform-specific components to support complex list interactions and state restoration.

How it got here

2024 — Paginator KMP library release and modularization

5 changes.

The period centered on the initial release of the Paginator KMP pagination library, introducing offset and cursor-based strategies with UI bindings for Jetpack Compose and RecyclerView. The project underwent significant structural changes, migrating to Gradle Version Catalogs and splitting the monolithic module into distinct multiplatform artifacts. Concurrently, the demo application was enhanced with deterministic load controls and state persistence to facilitate reproducible testing of pagination scenarios.

2026 — Cursor pagination and scroll preservation

11 changes.

This period focused on introducing cursor-based pagination strategies for both Compose and View-based UIs, expanding the library's capabilities beyond offset-based approaches. Significant work was also dedicated to enhancing user experience through scroll position preservation across navigation and process death, alongside the implementation of reactive error handling and improved cache management.

Features

Core paginator library refactored with reactive cache and UI interweaving

The paginator-core module has been restructured to introduce a reactive caching system and UI decoration capabilities. A new \PaginatorReactiveCache\ interface and \SnapshotReactiveCache\ implementation allow the paginator to consume live data source changes (inserts, updates, removes, moves) via \ReactiveEvent\ streams, automatically keeping the local cache in sync without re-fetching. Additionally, an \interweave\ feature enables inserting meta-elements (like date headers or dividers) into the paginated item stream using an \Interweaver\ strategy, while new extension functions simplify mapping \PageState\ snapshots to high-level \PaginatorUiState\ objects for UI consumption.

paginator-core · high confidence

Cursor-based pagination support added

A new cursor-based pagination strategy is now available alongside the existing offset-based paginator. This includes a \CursorPaginator\ for read-only, linked-list-style navigation using opaque server-issued bookmarks, and a \MutableCursorPaginator\ that adds element-level CRUD operations (add, remove, set) with automatic neighbor relinking. The implementation features a dedicated cache layer with chainable eviction strategies (LRU, FIFO, TTL, and context-window-based) and handles edge cases such as maintaining the context window on partial pages and re-anchoring after feed deletions.

paginator-cursor/src/commonMain · high confidence

Cursor-based pagination support for Android View-based RecyclerViews

This release adds a new \paginator-view-cursor\ module that brings cursor-based pagination to Android apps using the View system (RecyclerView). It provides extension functions like \bindPaginated\ and \bindPrefetchToRecyclerView\ for \CursorPaginator\, allowing developers to wire up cursor-driven data loading with automatic prefetching and scroll-position preservation. The module includes a \PrefetchBinding\ handle to manage the controller and scroll binding, and internal utilities like \DataItemCountTracker\ to keep the UI in sync with the paginator's state.

paginator-view-cursor · high confidence

Cursor-paginator support for Compose Lazy lists and grids

This update introduces a complete set of turnkey composables for cursor-based pagination in Jetpack Compose, mirroring the existing offset-paginator API. Developers can now use \PaginatedLazyColumn\, \PaginatedLazyRow\, \PaginatedLazyGrid\, and \PaginatedLazyStaggeredGrid\ to automatically handle scroll-driven prefetching, loading indicators, and edge-gated boundary animations. The library also provides \BindToLazyList\, \BindToLazyGrid\, and \BindToLazyStaggeredGrid\ extension functions for custom implementations, along with a \paginated\ DSL scope that simplifies header and footer management. Additionally, \PaginatedLazyColumn\ and \PaginatedLazyRow\ now feature automatic scroll-to-jump behavior, ensuring the list scrolls to the correct position when navigation events trigger page jumps.

paginator-compose-cursor · high confidence

Initial release of Paginator KMP pagination library

This change introduces the Paginator library, a Kotlin Multiplatform pagination solution supporting Android, iOS, JVM, Desktop, JS, and WasmJs. It provides two distinct pagination strategies—offset-based (\Paginator\/\MutablePaginator\) and cursor-based (\CursorPaginator\/\MutableCursorPaginator\)—alongside UI bindings for Jetpack Compose and Android RecyclerView. The release includes a Bill of Materials (BOM) for simplified dependency management, comprehensive documentation, and an MIT license.

(repo-wide) · high confidence

New View-based offset paginator with scroll preservation

The \paginator-view-offset\ module introduces a new set of Kotlin APIs for binding offset-based pagination to Android \RecyclerView\s, providing \bindPaginated\, \bindPrefetchToRecyclerView\, and \bindToRecyclerView\ entry points. These functions automatically manage lifecycle-scoped prefetch controllers, scroll listeners, and layout change listeners to handle data loading and index remapping. A key addition is the \preserveScroll\ parameter, which allows the paginator to snapshot and restore the \RecyclerView\'s scroll position across lifecycle events or process death, improving user experience during configuration changes.

paginator-view-offset · high confidence

New offset-based pagination composables for LazyList and LazyGrid

This module introduces turnkey Compose components for offset-based pagination, including \PaginatedLazyColumn\, \PaginatedLazyRow\, \PaginatedLazyVerticalGrid\, \PaginatedLazyHorizontalGrid\, and their staggered grid counterparts. These composables automatically bind the paginator's prefetch controller to the list's scroll state, handle scroll-driven prefetching, and provide scroll-anchor-safe loading indicators to prevent list jumping. Additionally, a low-level \paginated\ DSL is provided for \LazyList\ and \LazyGrid\ scopes, allowing developers to manually wire pagination into custom list implementations while automatically tracking header and footer counts for accurate prefetch calculations.

paginator-compose-offset · high confidence

Scroll position preservation and reactive error handling for Android View bindings

The paginator-view-core module now supports preserving RecyclerView scroll positions across configuration changes and process death, mirroring the behavior available in the Compose suite. This is achieved through a new \RecyclerViewScrollSnapshotRegistry\ that stores LayoutManager state snapshots, integrated into the \ScrollBinding\ interface via \saveScrollState\ and \restoreScrollState\ methods. Additionally, a new \PrefetchErrorChannel\ class provides a reactive, Flow-based mechanism for handling prefetch errors, replacing synchronous callbacks to allow for better lifecycle-aware error surfacing in the UI.

paginator-view-core · high confidence

Scroll position preservation and scroll-anchor-safe loading indicators for paginated lists

The paginator-compose-core module now provides built-in support for preserving scroll position across navigation cycles and process death for LazyList, LazyGrid, and LazyStaggeredGrid layouts. By enabling \preserveScroll = true\ in the binding composables, the library automatically saves and restores the user's scroll offset, ensuring they return to the same spot when revisiting a screen. Additionally, a new \PaginatorScrollEdgeIndicators\ composable renders loading indicators outside the lazy layout's item list, preventing scroll-jump issues that occur when loading indicators act as scroll anchors. The module also introduces a \PrefetchErrorChannel\ for reactive prefetch error handling and marks internal cross-module APIs with \@PaginatorInternalApi\ to clarify stability boundaries.

paginator-compose-core · high confidence

Removals

Paginator module removed from project

The \paginator\ module has been completely deleted from the codebase. This removal includes the core \Paginator.kt\ implementation, associated unit and instrumented tests, and build configuration files such as \.gitignore\ and \proguard-rules.pro\. Users will no longer have access to the pagination library or its features.

paginator · high confidence

Behavioural changes

Demo app replaces random network simulation with deterministic, user-controlled load choices

The demo application now uses a \LoadController\ to pause page loads and present a dialog where the user explicitly selects the backend response (Full, Incomplete, Empty, or Error), replacing the previous random behavior. This allows for reproducible testing of pagination scenarios. The UI has been updated with a new Material 3 Scaffold, a control panel, and an inspector bottom sheet to display pagination state details. Additionally, the \MainViewModel\ now persists and restores paginator state across process death using \SavedStateHandle\ and JSON serialization.

app/src · high confidence

Offset paginator introduces automatic window re-anchoring and navigation event emissions

The offset-based paginator now automatically re-anchors the visible context window after a refresh or data mutation (such as element removal) that would otherwise leave the UI on a blank or stale page range. Additionally, the paginator now emits navigation events via a dedicated flow, allowing UI layers to react to bookmark jumps (e.g., by scrolling the list to the new position) regardless of where the navigation was triggered.

paginator-offset/src/commonMain · high confidence

Test coverage

Cursor paginator test suite added; Offset paginator test suite expanded with regression and edge-case coverage.

Dependencies

Gradle wrapper updated to version 9.4.1

The Gradle wrapper configuration has been upgraded from version 8.2 to 9.4.1. This change ensures that the project uses the newer Gradle distribution for builds, which may include performance improvements, new features, and compatibility updates associated with the Gradle 9.x release line.

gradle · high confidence

Migrated to Gradle Version Catalogs and modularized Paginator into multiplatform artifacts

The project has been refactored to use Gradle Version Catalogs (defined in \gradle/libs.versions.toml\) for centralized dependency management, replacing hardcoded version strings in build scripts. The monolithic \paginator\ module has been split into distinct, independently publishable modules: \paginator-core\, \paginator-offset\, \paginator-cursor\, \paginator-view-core\, \paginator-view-offset\, \paginator-view-cursor\, \paginator-compose-core\, \paginator-compose-offset\, \paginator-compose-cursor\, and a \paginator-bom\ for alignment. These modules are now built as Kotlin Multiplatform libraries targeting Android, iOS, JVM, JS, and Wasm, with the Android application updated to depend on the new \paginator-offset\ module and use the Compose BOM.

(dependencies) · high confidence

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

How this codebase got here

Score

  • CAI 60 → 62 (+2.1)
  • Rubric changed (rubric-2026.08.19 → rubric-2026.09.15) — scores are not directly comparable.

Lenses

  • Code Health 82 → 89 (+7.2)
  • Architecture 100 → 100 (+0.0)
  • Maturity 65 → 65 (+0.5)
  • Readiness 50 → 49 (-0.3)
  • Security 62 → 70 (+8.2)

Resolved (67)

  • Coverage not included — suite not readable by the collector
  • Dependency hygiene not measured — dependency manifest found but not parsed for hygiene
  • Duplicated block (12 lines × 2) (paginator-compose-core/src/commonMain/kotlin/com/jamal_aliev/paginator/compose/core/internal/LazyGridScrollPreservation.kt)
  • Duplicated block (12 lines × 2) (paginator-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/cursor/extension/CursorPaginatorReactiveExt.kt)
  • Duplicated block (12 lines × 2) (paginator-view-cursor/src/main/kotlin/com/jamal_aliev/paginator/view/cursor/internal/DataItemCountTracker.kt)
  • Duplicated block (13 lines × 2) (app/src/main/java/com/jamal_aliev/paginator/MainActivity.kt)
  • Duplicated block (13 lines × 2) (paginator-compose-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/compose/cursor/BindToLazyStaggeredGrid.kt)
  • Duplicated block (13 lines × 2) (paginator-core/src/commonMain/kotlin/com/jamal_aliev/paginator/core/cache/reactive/SnapshotReactiveCache.kt)
  • Duplicated block (13 lines × 2) (paginator-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/cursor/CursorPaginator.kt)
  • Duplicated block (13 lines × 2) (paginator-view-cursor/src/main/kotlin/com/jamal_aliev/paginator/view/cursor/PrefetchController.kt)
  • Duplicated block (13 lines × 3) (paginator-compose-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/compose/cursor/PaginatedLazyGrid.kt)
  • Duplicated block (14 lines × 2) (paginator-offset/src/commonMain/kotlin/com/jamal_aliev/paginator/offset/Paginator.kt)
  • Duplicated block (15 lines × 2) (paginator-compose-offset/src/commonMain/kotlin/com/jamal_aliev/paginator/compose/offset/PaginatedLazyColumn.kt)
  • Duplicated block (15 lines × 2) (paginator-core/src/commonMain/kotlin/com/jamal_aliev/paginator/core/cache/reactive/SnapshotReactiveCache.kt)
  • Duplicated block (16 lines × 2) (paginator-compose-offset/src/commonMain/kotlin/com/jamal_aliev/paginator/compose/offset/PaginatedGrids.kt)
  • Duplicated block (16 lines × 2) (paginator-compose-offset/src/commonMain/kotlin/com/jamal_aliev/paginator/compose/offset/PaginatedStaggeredGrids.kt)
  • Duplicated block (16 lines × 2) (paginator-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/cursor/prefetch/CursorPaginatorPrefetchController.kt)
  • Duplicated block (16 lines × 2) (paginator-offset/src/commonMain/kotlin/com/jamal_aliev/paginator/offset/Paginator.kt)
  • Duplicated block (16 lines × 2) (paginator-view-cursor/src/main/kotlin/com/jamal_aliev/paginator/view/cursor/BindPrefetchToRecyclerView.kt)
  • Duplicated block (17 lines × 2) (paginator-compose-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/compose/cursor/PrefetchOnScroll.kt)
  • …and 47 more

New (105)

  • ClassTooLong: CursorPaginator (paginator-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/cursor/CursorPaginator.kt)
  • ClassTooLong: CursorPagingCore (paginator-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/cursor/CursorPagingCore.kt)
  • ClassTooLong: MainActivity (app/src/main/java/com/jamal_aliev/paginator/MainActivity.kt)
  • ClassTooLong: Paginator (paginator-offset/src/commonMain/kotlin/com/jamal_aliev/paginator/offset/Paginator.kt)
  • ClassTooLong: PagingCore (paginator-offset/src/commonMain/kotlin/com/jamal_aliev/paginator/offset/PagingCore.kt)
  • Dependency hygiene PARTLY measured — Maven/Gradle declarations read, no dependency graph resolved
  • Documentation: no installation or build instructions (README.md)
  • Documentation: no usage examples (README.md)
  • Duplicated block (10 lines × 2) (app/src/main/java/com/jamal_aliev/paginator/MainViewModel.kt)
  • Duplicated block (10 lines × 2) (paginator-view-cursor/src/main/kotlin/com/jamal_aliev/paginator/view/cursor/BindToRecyclerView.kt)
  • Duplicated block (10 lines × 3) (paginator-compose-offset/src/commonMain/kotlin/com/jamal_aliev/paginator/compose/offset/PaginatedLazyGrid.kt)
  • Duplicated block (11 lines × 2) (paginator-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/cursor/CursorPaginator.kt)
  • Duplicated block (11 lines × 2) (paginator-view-cursor/src/main/kotlin/com/jamal_aliev/paginator/view/cursor/BindToRecyclerView.kt)
  • Duplicated block (11 lines × 3) (paginator-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/cursor/cache/eviction/CursorMostRecentPagingCache.kt)
  • Duplicated block (11 lines × 6) (paginator-compose-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/compose/cursor/PaginatedGrids.kt)
  • Duplicated block (11 lines × 6) (paginator-compose-offset/src/commonMain/kotlin/com/jamal_aliev/paginator/compose/offset/PaginatedGrids.kt)
  • Duplicated block (11–12 lines × 2) (paginator-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/cursor/extension/CursorPaginatorReactiveExt.kt)
  • Duplicated block (12 lines × 2) (app/src/main/java/com/jamal_aliev/paginator/MainActivity.kt)
  • Duplicated block (12 lines × 2) (paginator-compose-core/src/commonMain/kotlin/com/jamal_aliev/paginator/compose/core/internal/LazyGridScrollPreservation.kt)
  • Duplicated block (12 lines × 2) (paginator-cursor/src/commonMain/kotlin/com/jamal_aliev/paginator/cursor/CursorPaginator.kt)
  • …and 85 more

Changes since last survey

  • 4 commits — 2 feature/other, 2 fixes

By area

  • articles/ru — 2 commits
  • paginator-cursor/src — 2 commits

Notable commits

  • fix: fix(cursor): publish error state on unanchored restart failure
  • fix: fix(cursor): seed restart()'s first-page progress/error state with a mutable list
  • change: Bumped Paginator version from 10.2.1 to 10.2.2 across the project configuration, documentation, and technical articles.
  • change: Bumped Paginator version from 10.2.2 to 10.2.3 across the project configuration, documentation, and technical articles.

Written by watchdog.canine.dev from the codebase's own history, inside the signed delivery this page is composed from.

Survey your own repository

jamal-wia/Paginator was measured the same way every project in this corpus was: the same rubric, at a pinned commit, with the result published in full. Point a surveyor at a repository you know and see whether you agree with it.

About this page

  • The score is its most recent published measurement, taken on 22 September 2026 at a pinned commit. It is not a live figure and does not change until the project is measured again.
  • Measured at commit 401c3514b8ab9308dd9a903906500c7ebaac5edc — the exact code this score is about.
  • Scored under rubric-2026.09.15 — the same rubric and the same method as every other entry in this index.
  • Measured by watchdog.canine.dev using codehealth-analyzer preprod-821afab8930d.