feat: infinite scroll for the project asset grid - #155
Conversation
Replace the "Load more" button on the project/folder view with scroll-driven loading. The grid now fetches assets a page at a time and appends them as an IntersectionObserver sentinel nears the viewport; a skeleton row shows only while a page fetch is in flight. Mutation/realtime/poll refreshes re-pull just the rows already loaded so scroll position holds. Thumbnails in the grid and list views get loading="lazy" + decoding="async" so off-screen images no longer download up front. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X3AbSPmfhePLsKb2FC6HXe
Greptile SummaryThis PR replaces button-driven asset pagination with a cumulative, scroll-driven query and adds lazy thumbnail decoding.
Confidence Score: 4/5The PR is not yet safe to merge because a transient load-more failure can permanently disable further scrolling for the current browser state. The cumulative page-1 approach fixes the two resolved pagination-state findings from the previous review, but the new loading watcher derives end-of-list state after unsuccessful requests from stale rows and an increased limit, removing the only retry trigger. Files Needing Attention: frontend/src/components/projects/useProjectBrowser.ts
|
| Filename | Overview |
|---|---|
| frontend/src/components/projects/useProjectBrowser.ts | Reworks pagination around a cumulative page-1 limit, but failed expansion requests can incorrectly terminate infinite scrolling. |
| frontend/src/composables/useInfiniteScroll.ts | Adds a scoped IntersectionObserver that loads repeatedly while the sentinel remains near the viewport. |
| frontend/src/pages/ProjectDetailPage.vue | Replaces the load-more button with an observer sentinel and fetch-specific skeleton placeholders. |
| frontend/src/components/assets/AssetCard.vue | Adds native lazy loading and asynchronous decoding to asset-card thumbnails. |
| frontend/src/components/common/FileTypeIcon.vue | Adds native lazy loading and asynchronous decoding to list-style thumbnail images. |
Prompt To Fix All With AI
### Issue 1
frontend/src/components/projects/useProjectBrowser.ts:153-160
**Failed Loads Disable Scrolling**
When a load-more request fails, `staleOnError` keeps the previously loaded rows while `limit` has already increased. When loading finishes, this watcher compares that stale row count with the new limit and marks the list as complete. This makes `hasMore` false and removes the only infinite-scroll sentinel even though `total` still indicates that more assets exist, so scrolling cannot retry the failed request.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (2): Last reviewed commit: "fix: keep asset pagination consistent ac..." | Re-trigger Greptile
Greptile flagged that the page-offset bookkeeping could skip assets: after a refresh replaced the loaded rows, the retained page number no longer matched the row count, so the next scroll fetched from the wrong offset. A failed refresh also left the request stuck in refresh mode. Drop the offset model entirely. The grid keeps requesting [0, limit) from the top (the same approach the old "Load more" button used) and grows limit by a page on scroll; refetches stay consistent by construction. reachedEnd stops the observer once the API row cap or the true end is hit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X3AbSPmfhePLsKb2FC6HXe
| watch( | ||
| () => assetsCall.loading, | ||
| (loading) => { | ||
| if (loading) return | ||
| loadingMore.value = false | ||
| reachedEnd.value = assets.value.length < limit.value | ||
| }, | ||
| ) |
There was a problem hiding this comment.
Failed Loads Disable Scrolling
When a load-more request fails, staleOnError keeps the previously loaded rows while limit has already increased. When loading finishes, this watcher compares that stale row count with the new limit and marks the list as complete. This makes hasMore false and removes the only infinite-scroll sentinel even though total still indicates that more assets exist, so scrolling cannot retry the failed request.
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/components/projects/useProjectBrowser.ts
Line: 153-160
Comment:
**Failed Loads Disable Scrolling**
When a load-more request fails, `staleOnError` keeps the previously loaded rows while `limit` has already increased. When loading finishes, this watcher compares that stale row count with the new limit and marks the list as complete. This makes `hasMore` false and removes the only infinite-scroll sentinel even though `total` still indicates that more assets exist, so scrolling cannot retry the failed request.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Replace the "Load more" button on the project/folder view with scroll-driven loading. The grid now fetches assets a page at a time and appends them as an IntersectionObserver sentinel nears the viewport; a skeleton row shows only while a page fetch is in flight. Mutation/realtime/poll refreshes re-pull just the rows already loaded so scroll position holds.
Thumbnails in the grid and list views get loading="lazy" + decoding="async" so off-screen images no longer download up front.
Claude-Session: https://claude.ai/code/session_01X3AbSPmfhePLsKb2FC6HXe