Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions plugins/InfiniteScroll/InfiniteScroll.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Infinite Scroll
description: Replaces pagination with infinite scrolling in every list (grid, list, wall and tagger views), including the lists on performer, studio, tag, group and gallery pages.
version: 1.1.1
ui:
javascript:
- infiniteScroll.js
css:
- infiniteScroll.css
settings:
disableGrid:
displayName: Disable in Grid view
description: Use normal pages in Grid view.
type: BOOLEAN
disableList:
displayName: Disable in List view
description: Use normal pages in List view.
type: BOOLEAN
disableWall:
displayName: Disable in Wall view
description: Use normal pages in Wall view.
type: BOOLEAN
disableTagger:
displayName: Disable in Tagger view
description: Use normal pages in Tagger view.
type: BOOLEAN
disableOnDetailPages:
displayName: Disable on detail pages
description: Use normal pages for the lists inside performer, studio, tag, group and gallery pages.
type: BOOLEAN
hidePageJumper:
displayName: Hide the page jumper
description: Hide the page buttons above the list. By default they stay, so you can jump to a page; they follow the page you're looking at.
type: BOOLEAN
disableWindowing:
displayName: Keep every card mounted
description: By default, Grid cards far off-screen are swapped for empty boxes of the same size to keep long lists fast. Turn this on to keep every card rendered.
type: BOOLEAN
disablePinnedHeader:
displayName: Don't pin the list header
description: By default the toolbar, filter tags, page buttons and count stay at the top while the list scrolls under them.
type: BOOLEAN
prefetchScreens:
displayName: Load-ahead distance (screens)
description: How many screen heights before the bottom the next batch starts loading. Default 1.5. When you scroll fast it loads further ahead on its own.
type: NUMBER
taggerConfirmThreshold:
displayName: Tagger confirmation threshold
description: In the scene tagger, "Scrape All" asks for confirmation when more than this many scenes are loaded. Default (0) is 40.
type: NUMBER
34 changes: 34 additions & 0 deletions plugins/InfiniteScroll/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Infinite Scroll
https://discourse.stashapp.cc/t/infinite-scroll/20379

Replaces page buttons with infinite scrolling in every list: scenes, images, galleries, performers, studios, tags, groups and markers, in Grid, List, Wall and Tagger views, including the lists on performer, studio, tag, group and gallery pages.

Full documentation, known limitations and issues: https://github.com/RyanAtNight/StashInfiniteScroll

## What it does

- Loads items in batches of 14 before you reach the bottom, further ahead the faster you scroll.
- Keeps your place: the page number in the address bar follows the page in view, going back from a scene returns to the same item, and a copied link opens at that page with a **Load earlier results** button.
- Pins the toolbar, filter tags, page buttons and result count at the top while the list scrolls under them.
- The page buttons show the page in view and jump to any page; the count shows the total.
- Keeps Stash's own features working across everything loaded: Select all, shift-click ranges, bulk edit and delete, the play queue, the image lightbox, random sort and the scene tagger.
- Re-fetches what's loaded when items are added or removed while you scroll, so nothing is skipped or shown twice.
- Stays fast in long lists: off-screen Grid cards become empty boxes of the same size, and previews wait while you scroll fast.

If a Stash update removes something the plugin relies on, that part switches itself off and Stash's normal pages are used.

## Settings

| Setting | Default | |
|---|---|---|
| Disable in Grid / List / Wall / Tagger view | off | Use normal pages in that view. |
| Disable on detail pages | off | Use normal pages for lists inside performer, studio, tag, group and gallery pages. |
| Hide the page jumper | off | Hide the page buttons above the list. |
| Don't pin the list header | off | Let the toolbar, filter tags, page buttons and count scroll away. |
| Keep every card mounted | off | Turn off the Grid placeholders. |
| Load-ahead distance (screens) | 1.5 | The least that's kept loaded below the view; fast scrolling loads further. |
| Tagger confirmation threshold | 40 | When "Scrape All" asks first. |

## Compatibility

Tested on Stash v0.31.1, v0.32.0 and the development build, each in a fresh install with no other plugins. No dependencies.
179 changes: 179 additions & 0 deletions plugins/InfiniteScroll/infiniteScroll.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/* Infinite Scroll for Stash */

/* the footer pagination is replaced by scrolling */
.isc-active .pagination-footer-container {
display: none !important;
}

/* items load in fixed-size batches, so there's no page size to choose */
.isc-active .page-size-selector {
display: none !important;
}

.isc-sentinel {
display: flex;
justify-content: center;
align-items: center;
min-height: 1px;
padding: 1.25rem 0 2rem;
color: var(--text-muted, #8a9ba8);
font-size: 0.875rem;
}

.isc-sentinel:empty {
padding: 0;
}

.isc-loading {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}

.isc-spinner {
width: 1rem;
height: 1rem;
border: 2px solid currentColor;
border-right-color: transparent;
border-radius: 50%;
animation: isc-spin 0.75s linear infinite;
}

@keyframes isc-spin {
to {
transform: rotate(360deg);
}
}

.isc-top {
display: flex;
justify-content: center;
padding: 0.5rem 0 1rem;
}

/* Pinned list header: sticky in place, so at rest nothing moves. The
background is see-through and lets clicks through to the cards; only the
controls themselves are opaque and clickable. */
.isc-pin {
position: sticky;
/* outranks Stash's own sticky "top" for a toolbar with a selection, which
would otherwise move the toolbar off the spot the rows below allow for */
top: var(--isc-pin-top) !important;
z-index: 30;
background: transparent !important;
pointer-events: none;
}

/* earlier rows stack above later ones, so a toolbar dropdown opens over the
rows below it */
.isc-pin.filtered-list-toolbar {
z-index: 32;
}

.isc-pin.filter-tags {
z-index: 31;
}

.isc-pin .btn,
.isc-pin input,
.isc-pin select,
.isc-pin a,
.isc-pin .badge,
.isc-pin .dropdown-menu,
.isc-pin .isc-count {
pointer-events: auto;
}

/* "x 3 [select all]": the selection indicator in the toolbar. It has no
background of its own, which is unreadable over cards. A solid pill in
Stash's primary blue, the colour of selected checkboxes; only colours
change, so its size and position stay the same. */
.isc-pin .selected-items-info {
border-color: #0d5683;
border-radius: 3.5px;
background-color: #137cbd;
color: #fff;
}

.isc-pin .selected-items-info button.minimal {
color: #fff;
}

.isc-pin .selected-items-info button.minimal:hover:not(:disabled) {
background: #10659a;
}

.isc-pin .selected-items-info button.minimal:active:not(:disabled) {
background: #0d5683;
}

/* the result count, styled like the filter tags */
.isc-count {
padding: 0 8px;
border-radius: 3.5px;
background-color: #bfccd6;
color: #182026;
}

/* the duration / size byline after it */
.isc-count * {
color: inherit;
}

/* Stash's sidebar toggle is half-transparent at z-index 10, so hovered cards
that a card plugin raises (z-index 25) and Wall checkboxes (z-index 10)
could cover it, and a click meant for it opened a scene. Opaque, and at
Bootstrap's sticky level: above any card, below the navbar and dialogs. */
.sidebar-toggle-button-container button.sidebar-toggle-button {
opacity: 1;
background: #394b59;
z-index: 1020;
}

/* Stash's button.minimal rules give hover and pressed a see-through tint;
these outrank them with solid colours */
.sidebar-toggle-button-container button.sidebar-toggle-button:hover:not(:disabled) {
background: #4a6273;
}

.sidebar-toggle-button-container button.sidebar-toggle-button:active:not(:disabled),
.sidebar-toggle-button-container button.sidebar-toggle-button.active:not(.disabled) {
background: #2e3d48;
}

/* back to top */
.isc-to-top {
position: fixed;
right: 1.5rem;
bottom: 1.5rem;
z-index: 1020;
display: flex;
align-items: center;
justify-content: center;
width: 2.75rem;
height: 2.75rem;
padding: 0;
border: 1px solid #5c7080;
border-radius: 50%;
background-color: #394b59;
color: #f5f8fa;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
cursor: pointer;
transition: opacity 0.15s, background-color 0.15s;
}

.isc-to-top:hover {
background-color: #48606f;
}

.isc-to-top.isc-hidden {
opacity: 0;
pointer-events: none;
}

/* stand-in for an off-screen card; sized from the card it replaced */
.isc-ph {
flex: none;
border-radius: 0.25rem;
background: rgba(127, 127, 127, 0.06);
}
Loading