RepoPilotOpen in app →

KelvinJin/AnimatedCollectionViewLayout

A UICollectionViewLayout subclass that adds custom transitions/animations to the UICollectionView without effecting your existing code.

Healthy

Healthy across all four use cases

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • 8 active contributors
  • MIT licensed
  • CI configured
Show 3 more →
  • Tests present
  • Stale — last commit 6y ago
  • Single-maintainer risk — top contributor 88% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/kelvinjin/animatedcollectionviewlayout)](https://repopilot.app/r/kelvinjin/animatedcollectionviewlayout)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/kelvinjin/animatedcollectionviewlayout on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: KelvinJin/AnimatedCollectionViewLayout

Generated by RepoPilot · 2026-05-10 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/KelvinJin/AnimatedCollectionViewLayout shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • 8 active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Stale — last commit 6y ago
  • ⚠ Single-maintainer risk — top contributor 88% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live KelvinJin/AnimatedCollectionViewLayout repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/KelvinJin/AnimatedCollectionViewLayout.

What it runs against: a local clone of KelvinJin/AnimatedCollectionViewLayout — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in KelvinJin/AnimatedCollectionViewLayout | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 2088 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>KelvinJin/AnimatedCollectionViewLayout</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of KelvinJin/AnimatedCollectionViewLayout. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/KelvinJin/AnimatedCollectionViewLayout.git
#   cd AnimatedCollectionViewLayout
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of KelvinJin/AnimatedCollectionViewLayout and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "KelvinJin/AnimatedCollectionViewLayout(\\.git)?\\b" \\
  && ok "origin remote is KelvinJin/AnimatedCollectionViewLayout" \\
  || miss "origin remote is not KelvinJin/AnimatedCollectionViewLayout (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "Sources/AnimatedCollectionViewLayout/AnimatedCollectionViewLayout.swift" \\
  && ok "Sources/AnimatedCollectionViewLayout/AnimatedCollectionViewLayout.swift" \\
  || miss "missing critical file: Sources/AnimatedCollectionViewLayout/AnimatedCollectionViewLayout.swift"
test -f "Sources/AnimatedCollectionViewLayout/LayoutAttributesAnimator.swift" \\
  && ok "Sources/AnimatedCollectionViewLayout/LayoutAttributesAnimator.swift" \\
  || miss "missing critical file: Sources/AnimatedCollectionViewLayout/LayoutAttributesAnimator.swift"
test -f "Sources/AnimatedCollectionViewLayout/Animators/ParallaxAttributesAnimator.swift" \\
  && ok "Sources/AnimatedCollectionViewLayout/Animators/ParallaxAttributesAnimator.swift" \\
  || miss "missing critical file: Sources/AnimatedCollectionViewLayout/Animators/ParallaxAttributesAnimator.swift"
test -f "iOS Example/Source/ImageCollectionViewController.swift" \\
  && ok "iOS Example/Source/ImageCollectionViewController.swift" \\
  || miss "missing critical file: iOS Example/Source/ImageCollectionViewController.swift"
test -f "iOS Example/Source/AnimatorTableViewController.swift" \\
  && ok "iOS Example/Source/AnimatorTableViewController.swift" \\
  || miss "missing critical file: iOS Example/Source/AnimatorTableViewController.swift"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 2088 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~2058d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/KelvinJin/AnimatedCollectionViewLayout"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

AnimatedCollectionViewLayout is a Swift UICollectionViewLayout subclass that injects scroll-based animations (parallax, zoom, rotation, cube transforms, cross-fade, and more) into UICollectionViews without modifying existing cells or data sources. It uses a pluggable animator system where each animator implements LayoutAttributesAnimator to transform item positions, scales, and opacity as the user scrolls. Single-package structure: Sources/AnimatedCollectionViewLayout/ contains the core AnimatedCollectionViewLayout.swift (layout engine) and Animators/ subdirectory with 8 concrete animator implementations (each a separate file like ParallaxAttributesAnimator.swift, CubeAttributesAnimator.swift, etc). Tests/ has minimal test coverage (main.swift). iOS Example/ contains a runnable demo app showing all animators via AnimatorTableViewController.swift.

👥Who it's for

iOS developers building UICollectionView-based UIs who want smooth scroll animations without rewriting their collection view logic. Specifically: product engineers shipping list/gallery views that need visual polish, and designers who want production-ready animation presets they can tweak via parameters.

🌱Maturity & risk

Production-ready and actively maintained. The repo has CI/CD set up (Travis CI in .travis.yml), supports multiple package managers (CocoaPods, Carthage, SPM via Package.swift), targets iOS 9.0+, and provides 8 battle-tested animators. However, the last visible commit activity and issue backlog are not shown in the provided data, so minor caution on recency is warranted.

Low risk for adoption: single maintainer (KelvinJin) is a known risk factor, but the codebase is small (~30KB Swift), has no external dependencies (self-contained layout subclass), and the API surface is minimal (just set animator property). Main risk: if iOS UICollectionView internals change dramatically, layout behavior may need updates, but this is infrequent.

Active areas of work

The repo appears stable rather than actively developed—no specific PRs or milestones are visible in the provided snapshot. The presence of CHANGELOG.md and CONTRIBUTING.md suggests past activity, but current development status is unclear from this data alone.

🚀Get running

Clone the repo, then open the Xcode project: git clone https://github.com/KelvinJin/AnimatedCollectionViewLayout.git && cd AnimatedCollectionViewLayout && open AnimatedCollectionViewLayout.xcworkspace. Run the 'iOS Example' scheme in Xcode to see all animators in action. For Swift Package Manager users: add it to Package.swift or use Xcode's 'Add Package Dependency' with the repo URL.

Daily commands: Open AnimatedCollectionViewLayout.xcworkspace in Xcode, select the 'iOS Example' scheme, and press Run (⌘R). The demo app shows a table of 8 animator options; tap any row to see the animation applied to a horizontal-scrolling collection view.

🗺️Map of the codebase

  • Sources/AnimatedCollectionViewLayout/AnimatedCollectionViewLayout.swift — Core UICollectionViewLayout subclass that orchestrates all animations and serves as the main entry point for the library.
  • Sources/AnimatedCollectionViewLayout/LayoutAttributesAnimator.swift — Abstract protocol defining the animator interface that all animation strategies must implement; essential for extending with new animations.
  • Sources/AnimatedCollectionViewLayout/Animators/ParallaxAttributesAnimator.swift — Reference implementation of LayoutAttributesAnimator showing the pattern all concrete animators follow.
  • iOS Example/Source/ImageCollectionViewController.swift — Demonstrates how to integrate AnimatedCollectionViewLayout into an existing UICollectionViewController with minimal changes.
  • iOS Example/Source/AnimatorTableViewController.swift — Shows the API surface for switching between different animator implementations at runtime.
  • Package.swift — Swift Package Manager manifest defining the library's module structure and distribution packaging.
  • AnimatedCollectionViewLayout.podspec — CocoaPods podspec enabling installation via CocoaPods; critical for pod-based integrations.

🧩Components & responsibilities

  • AnimatedCollectionViewLayout (UICollectionViewLayout, invalidationContext, shouldInvalidateLayout) — Intercepts layout attribute calculation, delegates to animator for per-cell modifications, returns animated attributes to UICollectionView
    • Failure mode: If animator is nil or returns invalid attributes, falls back to default layout; if shouldInvalidateLayout returns false, scroll-induced layout changes may not trigger animations
  • LayoutAttributesAnimator (Protocol) (UICollectionViewLayoutAttributes, CGAffineTransform, CATransform3D) — Determines which cells animate and how to transform their layout attributes based on position within collection bounds
    • Failure mode: If modifyLayoutAttributes does not set required attributes (frame, bounds), cell may disappear or render at origin; if shouldAnimateCell always returns

🛠️How to make changes

Add a New Animation Effect

  1. Create a new Swift file in Sources/AnimatedCollectionViewLayout/Animators/ named YourNameAttributesAnimator.swift (Sources/AnimatedCollectionViewLayout/Animators/YourNameAttributesAnimator.swift)
  2. Conform to LayoutAttributesAnimator protocol; implement shouldAnimateCell(at:in:) and modifyLayoutAttributes(_:in:) (Sources/AnimatedCollectionViewLayout/LayoutAttributesAnimator.swift)
  3. In modifyLayoutAttributes, calculate animation values (transform, opacity, etc.) based on the cell's position relative to visibleBounds using attributes.frame and collectionViewContentOffset (Sources/AnimatedCollectionViewLayout/Animators/ParallaxAttributesAnimator.swift)
  4. Update iOS Example AnimatorTableViewController to include your animator in the list of available options for testing (iOS Example/Source/AnimatorTableViewController.swift)
  5. Assign your animator instance to the AnimatedCollectionViewLayout.animator property in ImageCollectionViewController (iOS Example/Source/ImageCollectionViewController.swift)

Integrate AnimatedCollectionViewLayout into an Existing Project

  1. Install via CocoaPods (add 'AnimatedCollectionViewLayout' to Podfile) or Swift Package Manager (AnimatedCollectionViewLayout.podspec)
  2. In your UICollectionViewController, create an instance of AnimatedCollectionViewLayout and set it as the collection view's layout (iOS Example/Source/ImageCollectionViewController.swift)
  3. Instantiate one of the animator classes (e.g., ParallaxAttributesAnimator()) and assign it to layout.animator (Sources/AnimatedCollectionViewLayout/AnimatedCollectionViewLayout.swift)
  4. No changes needed to your UICollectionViewDataSource, UICollectionViewDelegate, or UICollectionViewCell; animations apply automatically during scroll

Support Runtime Animator Switching

  1. Store a reference to AnimatedCollectionViewLayout instance in your view controller (iOS Example/Source/ImageCollectionViewController.swift)
  2. On user selection or event, create a new animator instance and assign it to layout.animator property (iOS Example/Source/AnimatorTableViewController.swift)
  3. Call layout.invalidateLayout() to trigger re-computation of attributes with the new animator (Sources/AnimatedCollectionViewLayout/AnimatedCollectionViewLayout.swift)
  4. Collection view automatically re-renders with new animation effect on next scroll event

🔧Why these technologies

  • UICollectionViewLayout subclass — Non-invasive hook into UICollectionView rendering pipeline; allows animation without modifying cells or data sources
  • LayoutAttributesAnimator protocol — Strategy pattern enables swappable animation implementations; supports extensibility without modifying core layout
  • CATransform3D and UIView transforms — Hardware-accelerated 3D transforms and 2D affine transforms provide smooth 60fps animations without blocking main thread
  • Swift Package Manager & CocoaPods dual support — Maximizes compatibility across modern (SPM) and legacy (CocoaPods) iOS projects

⚖️Trade-offs already made

  • Layout-only animation (no custom CADisplayLink)

    • Why: Simpler integration; leverages UICollectionView's existing scroll tracking and rendering cycle
    • Consequence: Animations tied to scroll velocity; cannot decouple animation timing from scroll physics
  • Protocol-based animator instead of base class

    • Why: Avoids inheritance chains; allows lightweight animator implementations
    • Consequence: Each animator must fully implement shouldAnimateCell and modifyLayoutAttributes; no shared helper methods
  • No persistent animation state between frames

    • Why: Stateless design avoids race conditions during rapid scroll/layout invalidations
    • Consequence: Complex animations requiring multi-frame state (e.g., physics simulations) must recalculate per frame

🚫Non-goals (don't propose these)

  • Does not provide gesture recognizers or swipe animations; layout-only, requires UICollectionView scroll
  • Does not support non-grid layouts (vertical stacks, circular arrangements); designed for standard UICollectionViewFlowLayout substitution
  • Does not handle cell insertion/deletion animations; focuses on scroll-based layout transitions only
  • Not a full-featured animation framework; complements but does not replace CABasicAnimation or Lottie for complex keyframe sequences

🪤Traps & gotchas

No external runtime dependencies, but note: (1) Animators assume vertical or horizontal scrolling only—diagonal scrolling behavior is undefined. (2) isPagengEnabled mode works best with specific animator types (read CONTRIBUTING.md for pairing recommendations). (3) High-frequency animate() calls during scroll can cause frame rate drops on older devices if animator logic is expensive; test on iPhone 6s or older. (4) itemSpacing is required for proper spacing; default may not match your cell size.

🏗️Architecture

💡Concepts to learn

  • UICollectionViewLayout subclassing — This repo's entire foundation is overriding prepare(), layoutAttributesForElements(in:), and layoutAttributesForItem(at:) to inject scroll-based transforms; understanding the UICollectionViewLayout lifecycle is mandatory.
  • CATransform3D and CGAffineTransform — Animators mutate UICollectionViewLayoutAttributes.transform using 3D transforms (rotation, perspective) and 2D transforms (scale, translation); you need to understand matrix transforms to modify or create new animators.
  • Scroll offset as animation driver — Unlike time-based animations, these animators use UICollectionView's contentOffset as the input to animate; this decouples animation from frame rate and links it directly to user scroll gesture.
  • Strategy pattern (pluggable animators) — LayoutAttributesAnimator is a protocol defining one method; each animator is a concrete strategy; this allows swapping animation behavior at runtime without changing layout code, demonstrating clean polymorphism.
  • UICollectionViewLayoutAttributes mutation — All animators work by cloning UICollectionViewLayoutAttributes objects and modifying their transform, alpha, zIndex, size properties before returning them; understanding attribute lifecycle is key to avoiding layout corruption.
  • Paging mode (velocity-based snapping) — The isPageingEnabled flag toggles whether layout snaps to full-screen pages during scrolling; some animators (like CubeAttributesAnimator) are designed specifically for paging mode to create snappy card-flip effects.
  • Instagram/IGListKit — Alternative UICollectionView framework with declarative data-driven layout, but uses full replacement rather than layout subclass approach; mentioned here for comparison on UICollectionView modernization.
  • airbnb/lottie-ios — Complementary animation library for complex keyframe-based animations; often paired with custom layouts like this one for micro-interactions on collection cells.
  • SnapKit/SnapKit — DSL for Auto Layout constraints; commonly used in iOS projects using custom layouts to define cell geometry cleanly.
  • Alamofire/Alamofire — HTTP networking library; not directly related but frequently used in example apps and production code that display paginated collection views populated from APIs.

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add unit tests for all LayoutAttributesAnimator subclasses

The repo has 8 animator implementations (CrossFadeAttributesAnimator, CubeAttributesAnimator, LinearCardAttributesAnimator, PageAttributesAnimator, ParallexAttributesAnimator, RotateInOutAttributesAnimator, SnapInAttributesAnimator, ZoomInOutAttributesAnimator) but Tests/AnimatedCollectionViewLayoutTests/main.swift appears to be a stub. Adding comprehensive unit tests would catch regressions when animators are modified and help new contributors understand the expected behavior of each animator.

  • [ ] Create test file Tests/AnimatedCollectionViewLayoutTests/AnimatorsTests.swift
  • [ ] Add test cases for each animator in Sources/AnimatedCollectionViewLayout/Animators/ verifying they modify layout attributes correctly
  • [ ] Test edge cases like negative offset, zero offset, and large offset values
  • [ ] Add tests for LayoutAttributesAnimator.swift base class protocol conformance
  • [ ] Update .travis.yml to run these tests in CI if not already configured

Add GitHub Actions workflow to replace/supplement Travis CI

The repo uses .travis.yml for CI, but GitHub Actions is now the standard for GitHub repos. This would improve build visibility, provide better integration with GitHub UI, and allow testing against multiple iOS/Xcode versions. The project has Swift Package Manager support (Package.swift) which works well with GitHub Actions.

  • [ ] Create .github/workflows/swift.yml with jobs for building and testing the Swift package
  • [ ] Add separate workflow job for building the iOS Example project using xcodebuild
  • [ ] Add workflow job to verify CocoaPods spec (Tests/CheckCocoaPodsQualityIndexes.rb exists)
  • [ ] Test against multiple Xcode versions and iOS deployment targets
  • [ ] Add status badge to README.md pointing to the new GitHub Actions workflow

Add comprehensive animator documentation in README with code examples

While the README shows GIFs of the 8 different animators, there's no code snippet documentation showing how to use each one. Currently only the high-level concept is explained. Contributors and users would benefit from seeing practical examples of how to instantiate each animator and customize its properties.

  • [ ] Add 'Usage Examples' section to README.md after the introduction
  • [ ] For each animator in Sources/AnimatedCollectionViewLayout/Animators/, add a code snippet showing basic initialization
  • [ ] Document any configurable properties for each animator (e.g., speed, intensity, parallax offset)
  • [ ] Create a comparison table showing when to use each animator based on UX patterns
  • [ ] Reference the iOS Example/Source/AnimatorTableViewController.swift as a practical example

🌿Good first issues

  • Add unit tests for LayoutAttributesAnimator protocol in Tests/AnimatedCollectionViewLayoutTests/—currently only main.swift exists; tests should verify animate(attributes:scrollOffset:) correctly mutates transform, opacity, and scale properties for 2-3 animators.
  • Document animator selection guidelines in README.md—add a table mapping animator names to recommended use cases (e.g., 'ParallaxAttributesAnimator: best for full-screen hero sections') since the README shows GIFs but no guidance on which animator to pick for which UX pattern.
  • Add a SnapInAttributesAnimator test case to iOS Example—SnapInAttributesAnimator.swift exists but is not represented in AnimatorTableViewController.swift's animator selection menu, so it's untested in the demo app.

Top contributors

Click to expand
  • @KelvinJin — 59 commits
  • [@Jin Wang](https://github.com/Jin Wang) — 2 commits
  • [@Patrik Sjöberg](https://github.com/Patrik Sjöberg) — 1 commits
  • @naldikt — 1 commits
  • @filograno — 1 commits

📝Recent commits

Click to expand
  • f6d163d — Update README.md (KelvinJin)
  • c15687d — Released 1.1.0 (KelvinJin)
  • cc53682 — Updated animations for iOS 14. Now changing transform instead of frame of the content view. (KelvinJin)
  • 7ee6c1c — Dropped iOS 8 support. (KelvinJin)
  • bdb7858 — Update README.md (KelvinJin)
  • 88a3f63 — Update README.md (KelvinJin)
  • 07fb5ea — Releasing 1.0.1. (KelvinJin)
  • b31c041 — Reset anchor point and position of the content view layer in case UIKit resets one of them independently. (KelvinJin)
  • a43dac3 — Fixed cocoapods. (KelvinJin)
  • 225c5d2 — Fixed travis. (KelvinJin)

🔒Security observations

This is a well-structured UICollectionViewLayout library with minimal security concerns. The codebase appears to be a pure Swift/Objective-C library focused on UI animations without external dependencies, database access, or network operations. No critical vulnerabilities were identified. The main recommendations involve best practices for documentation (HTTPS URLs) and CI/CD configuration (avoiding hardcoded secrets). The library does not handle sensitive data, perform authentication, or interact with external services, significantly reducing the attack surface.

  • Low · Insecure HTTP URLs in Documentation — README.md. The README.md file contains HTTP (non-HTTPS) URLs for image resources (imgur.com). While this is primarily a documentation issue, it could expose users to MITM attacks if these URLs are accessed programmatically. Fix: Update all HTTP URLs to HTTPS protocol (http://i.imgur.com -> https://i.imgur.com)
  • Low · Travis CI Configuration Exposure — .travis.yml. The .travis.yml file is present in the repository root. This file may contain or reference sensitive build environment variables or deployment credentials. Fix: Audit .travis.yml for any hardcoded secrets. Use Travis CI's encrypted environment variables feature. Consider moving sensitive build configs to secure CI/CD systems.
  • Low · Sample Image Assets Included — iOS Example/Source/Assets.xcassets/. The iOS Example project includes multiple image assets. While not a direct security vulnerability, including large binary assets in version control can lead to repository bloat and potential issues with asset management. Fix: Consider using git-lfs (Git Large File Storage) for binary assets or storing them in a separate asset repository.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · KelvinJin/AnimatedCollectionViewLayout — RepoPilot