← back to Designer Wallcoverings
auto-save: 2026-06-26T16:04:29 (16 files) — pending-approval/boost-filter-consolidation-2026-06-25 shopify/enhancements/ARCHITECTURE.md shopify/enhancements/COLLECTION-LIQUID-INTEGRATION.liquid shopify/enhancements/INFINITE-SCROLL-IMPLEMENTATION.md shopify/enhancements/QUICK-REFERENCE.txt
ca8adbdfac27695d95391fcfc5e038ce62b31e1e · 2026-06-26 16:04:34 -0700 · Steve Abrams
Files touched
M shopify/enhancements/ARCHITECTURE.mdM shopify/enhancements/COLLECTION-LIQUID-INTEGRATION.liquidM shopify/enhancements/INFINITE-SCROLL-IMPLEMENTATION.mdM shopify/enhancements/QUICK-REFERENCE.txtM shopify/enhancements/README.mdM shopify/enhancements/VISUAL-EXPLANATION.mdM shopify/enhancements/WHAT-SHIPPED.mdM shopify/enhancements/deploy-infinite-scroll.shM shopify/enhancements/infinite-scroll-observer.jsM shopify/enhancements/infinite-scroll-test-harness.htmlM shopify/scripts/anna-french-scraper.jsM shopify/theme-infinite-scroll-142250278963/assets/boost-sd-custom.jsM shopify/theme-infinite-scroll-142250278963/snippets/boost-infinite-override.liquidM shopify/theme-infinite-scroll-142250278963/verify-boost-infinite.js
Diff
commit ca8adbdfac27695d95391fcfc5e038ce62b31e1e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Jun 26 16:04:34 2026 -0700
auto-save: 2026-06-26T16:04:29 (16 files) — pending-approval/boost-filter-consolidation-2026-06-25 shopify/enhancements/ARCHITECTURE.md shopify/enhancements/COLLECTION-LIQUID-INTEGRATION.liquid shopify/enhancements/INFINITE-SCROLL-IMPLEMENTATION.md shopify/enhancements/QUICK-REFERENCE.txt
---
shopify/enhancements/ARCHITECTURE.md | 19 ++-
.../COLLECTION-LIQUID-INTEGRATION.liquid | 15 +-
.../enhancements/INFINITE-SCROLL-IMPLEMENTATION.md | 24 +++-
shopify/enhancements/QUICK-REFERENCE.txt | 54 +++++--
shopify/enhancements/README.md | 56 ++++++--
shopify/enhancements/VISUAL-EXPLANATION.md | 15 +-
shopify/enhancements/WHAT-SHIPPED.md | 60 ++++++--
shopify/enhancements/deploy-infinite-scroll.sh | 18 ++-
shopify/enhancements/infinite-scroll-observer.js | 59 +++++++-
.../enhancements/infinite-scroll-test-harness.html | 60 ++++++--
shopify/scripts/anna-french-scraper.js | 1 -
.../assets/boost-sd-custom.js | 77 ++++++++--
.../snippets/boost-infinite-override.liquid | 20 ++-
.../verify-boost-infinite.js | 155 +++++++++++++++++----
14 files changed, 529 insertions(+), 104 deletions(-)
diff --git a/shopify/enhancements/ARCHITECTURE.md b/shopify/enhancements/ARCHITECTURE.md
index 594b7f89..c02ae92e 100644
--- a/shopify/enhancements/ARCHITECTURE.md
+++ b/shopify/enhancements/ARCHITECTURE.md
@@ -1,5 +1,19 @@
# Infinite Scroll Architecture — Why No Wrapper Divs
+> **Scope:** this document describes the **standalone `infinite-scroll-observer.js`**
+> — the mutation-observer / direct-append approach that is the **secondary
+> fallback / reference** in this folder, built for a *native* Shopify Liquid
+> grid (`[data-collection-products]`).
+>
+> It is **not** the live production mechanism. The live collection/search grids
+> are rendered by **Boost AI Search & Discovery** (`.boost-sd__product-item`),
+> and infinite scroll there is forced by the **Boost config override**
+> (`theme-infinite-scroll-142250278963/snippets/boost-infinite-override.liquid`
+> + `…/assets/boost-sd-custom.js`), which rewrites
+> `window.boostSDAppConfig.paginationType → "infinite_scroll"` before Boost's
+> bundle reads it. See `WHAT-SHIPPED.md` for the live architecture. The
+> wrapper-vs-no-wrapper analysis below applies to the native-grid fallback.
+
## Problem Statement
**Previous Approach Failed:** Wrapping the grid in a div to manage infinite scroll breaks the Shopify layout because it changes the DOM flow that the parent CSS expects.
@@ -523,4 +537,7 @@ The **mutation observer + direct append** approach avoids wrapper problems by:
4. **Minimal overhead** — Just watches and appends, no framework
5. **Testable & debuggable** — Simple state, easy to inspect in console
-This architecture is **production-ready** and tested on multiple Shopify themes.
+This architecture is a **reference / fallback** for native-Liquid grids; it is
+tested against the local harness but is **not** the live production mechanism
+(the Boost config override is — see the scope note at the top of this file and
+`WHAT-SHIPPED.md`).
diff --git a/shopify/enhancements/COLLECTION-LIQUID-INTEGRATION.liquid b/shopify/enhancements/COLLECTION-LIQUID-INTEGRATION.liquid
index 226be10a..e85a1c14 100644
--- a/shopify/enhancements/COLLECTION-LIQUID-INTEGRATION.liquid
+++ b/shopify/enhancements/COLLECTION-LIQUID-INTEGRATION.liquid
@@ -1,8 +1,21 @@
{%- comment -%}
Infinite Scroll Integration Guide for collection.liquid
+ ──────────────────────────────────────────────────────────────────────────
+ STATUS: FALLBACK / REFERENCE PATH — NOT the live storefront mechanism.
+
+ The SHIPPED infinite scroll on the live DW storefront is the Boost SD
+ override (theme-infinite-scroll-142250278963/ — boost-sd-custom.js +
+ snippets/boost-infinite-override.liquid), which forces Boost's own native
+ `infinite_scroll` pagination. Do NOT wire this generic observer into a
+ Boost-powered collection — the two would both manage the grid and fight.
+
+ This guide is for plain-Liquid themes / DW sister-site storefronts that do
+ NOT run Boost. Keep it as a portable reference.
+ ──────────────────────────────────────────────────────────────────────────
+
This file shows exactly where and how to add infinite scroll
- to your Shopify collection template.
+ to a (non-Boost) Shopify collection template.
No wrapper divs required — script works with existing grid.
{%- endcomment -%}
diff --git a/shopify/enhancements/INFINITE-SCROLL-IMPLEMENTATION.md b/shopify/enhancements/INFINITE-SCROLL-IMPLEMENTATION.md
index dc5272ea..9b0d54e4 100644
--- a/shopify/enhancements/INFINITE-SCROLL-IMPLEMENTATION.md
+++ b/shopify/enhancements/INFINITE-SCROLL-IMPLEMENTATION.md
@@ -1,8 +1,28 @@
# Infinite Scroll for Designer Wallcoverings Collections
+> ## ⚠️ This guide is for the FALLBACK observer, not the live mechanism
+>
+> The **live** infinite scroll on designerwallcoverings.com is the **Boost
+> config override** — `theme-infinite-scroll-142250278963/snippets/boost-infinite-override.liquid`
+> (PRIMARY) + `…/assets/boost-sd-custom.js` (fallback) — because the live
+> collection/search grids are rendered by **Boost AI Search & Discovery**
+> (`.boost-sd__product-item`). To deploy / change the live behaviour, use
+> `push-boost-override-to-dev.sh` / `push-boost-override-to-LIVE.sh` and verify
+> with `verify-boost-infinite.js`. See `WHAT-SHIPPED.md`.
+>
+> This document covers the **standalone `infinite-scroll-observer.js`** — the
+> secondary fallback / reference for a *native* Shopify Liquid grid
+> (`[data-collection-products]`). The deploy steps, `deploy-infinite-scroll.sh`,
+> and `<script src="…observer.js">` tag below apply **only** to that native-grid
+> scenario, NOT to the live Boost grids.
+
## Overview
-This implementation adds **automatic infinite scroll** to collection pages without using wrapper divs that break the existing Shopify grid layout.
+This document covers the **standalone observer** (the fallback / reference in
+this folder), which adds **automatic infinite scroll** to *native-Liquid*
+collection grids without using wrapper divs that break the existing Shopify grid
+layout. (For the live Boost grids the mechanism is the Boost config override —
+see the banner above.)
**Key Features:**
- ✅ **No DOM structure changes** — Uses mutation observer + fetch + direct DOM append
@@ -65,7 +85,7 @@ products.forEach(product => {
**Size:** ~6 KB minified
-**How to Deploy to Live Theme:**
+**How to Deploy (native-Liquid collection surfaces only — NOT the live Boost grids):**
Add to the bottom of `collection.liquid` (before `</section>`):
diff --git a/shopify/enhancements/QUICK-REFERENCE.txt b/shopify/enhancements/QUICK-REFERENCE.txt
index ba6b5257..358092ce 100644
--- a/shopify/enhancements/QUICK-REFERENCE.txt
+++ b/shopify/enhancements/QUICK-REFERENCE.txt
@@ -2,13 +2,35 @@
INFINITE SCROLL FOR DESIGNER WALLCOVERINGS — QUICK REFERENCE
================================================================================
+ ⚠️ WHAT IS LIVE (read first)
+ ----------------------------------------------------------------------------
+ The PRODUCTION infinite scroll is the BOOST CONFIG OVERRIDE, not the
+ standalone observer documented in this file:
+
+ LIVE: theme-infinite-scroll-142250278963/snippets/boost-infinite-override.liquid (PRIMARY)
+ + theme-infinite-scroll-142250278963/assets/boost-sd-custom.js (fallback)
+ DEPLOY: push-boost-override-to-dev.sh / push-boost-override-to-LIVE.sh
+ VERIFY: verify-boost-infinite.js (Playwright)
+ SHIPPED: 2026-06-24, commit fe289af
+
+ Live collection/search grids are rendered by Boost AI Search & Discovery
+ (.boost-sd__product-item). Infinite scroll is forced by rewriting
+ window.boostSDAppConfig.paginationType -> "infinite_scroll" before Boost's
+ bundle reads it. Boost exposes no API for this; the config-patch is the only
+ reliable route.
+
+ THIS FILE describes the standalone infinite-scroll-observer.js — a SECONDARY
+ FALLBACK / REFERENCE for NATIVE Shopify Liquid grids ([data-collection-products]),
+ which the live Boost grids do NOT use. It is NOT deployed to production.
+ ============================================================================
+
PROJECT LOCATION:
~/Projects/Designer-Wallcoverings/shopify/enhancements/
-MAIN FILES:
- ✓ infinite-scroll-observer.js — Production script (6 KB)
+MAIN FILES (standalone observer — secondary fallback / reference):
+ ✓ infinite-scroll-observer.js — Fallback/reference script (6 KB)
✓ infinite-scroll-test-harness.html — Local test (no server)
- ✓ deploy-infinite-scroll.sh — One-command deployment
+ ✓ deploy-infinite-scroll.sh — Deploy for native-grid surfaces only
DOCUMENTATION:
✓ README.md — Quick start (5 min read)
@@ -159,7 +181,8 @@ parent's CSS grid layout. This approach instead:
5. Layout structure unchanged
6. Sidebar alignment preserved
-That's it. Simple, robust, production-ready.
+That's it. Simple and robust — a reference / fallback for native-Liquid grids
+(the live Boost grids use the Boost config override; see the banner up top).
See ARCHITECTURE.md for technical deep-dive.
@@ -228,7 +251,12 @@ All files in: ~/Projects/Designer-Wallcoverings/shopify/enhancements/
GIT COMMITS
================================================================================
-Latest commits:
+LIVE mechanism — Boost config override (the actual production ship):
+ fe289af theme-infinite-scroll: Boost AI pagination override
+ (force infinite_scroll via boostSDAppConfig patch) + hardened
+ boost-sd-custom fallback, Playwright verify, dev/LIVE push scripts
+
+This folder — standalone observer (secondary fallback / reference):
6e8a95d3 Add final summary document for infinite scroll delivery
621498bf Add comprehensive infinite scroll documentation
c218e7fd Add infinite scroll implementation for collections
@@ -239,16 +267,18 @@ All committed to: ~/Projects/Designer-Wallcoverings
STATUS
================================================================================
-✅ Script written & tested
+LIVE (production): Boost config override (boost-infinite-override.liquid +
+boost-sd-custom.js) — shipped 2026-06-24 (fe289af), deployed via
+push-boost-override-to-*.sh, verified with verify-boost-infinite.js.
+
+This file's standalone observer (secondary fallback / reference):
+✅ Script written & tested (native-Liquid grid harness)
✅ Test harness working (no server needed)
-✅ 5 architecture documents
+✅ Architecture documents
✅ Deployment script automated
✅ Integration guide provided
✅ Committed to git
-✅ Production ready
-
-Next: Deploy to dev theme when you approve → A/B test → Production rollout
-
-Ready for deployment at your go/no-go decision.
+⛔ NOT deployed to the live Boost grids (by design — Boost override is live)
+♻️ Reserved for any future non-Boost (native Liquid) collection surface
================================================================================
diff --git a/shopify/enhancements/README.md b/shopify/enhancements/README.md
index 864c7c5b..cb1f873f 100644
--- a/shopify/enhancements/README.md
+++ b/shopify/enhancements/README.md
@@ -1,14 +1,42 @@
# Infinite Scroll for Designer Wallcoverings Collections
+> ## ⚠️ STATUS — read this first
+>
+> **What is LIVE:** the **Boost config override** —
+> `theme-infinite-scroll-142250278963/snippets/boost-infinite-override.liquid`
+> (PRIMARY) + `theme-infinite-scroll-142250278963/assets/boost-sd-custom.js`
+> (belt-and-suspenders fallback). Shipped 2026-06-24 (commit `fe289af`).
+> The live collection/search grids are rendered by **Boost AI Search &
+> Discovery** (`.boost-sd__product-item`), so infinite scroll is forced by
+> rewriting Boost's runtime config (`window.boostSDAppConfig.paginationType →
+> "infinite_scroll"`) before Boost's bundle reads it. Boost exposes no API for
+> this, which is why the config-patch is the only reliable route. Deploy with
+> `push-boost-override-to-dev.sh` / `push-boost-override-to-LIVE.sh`; verify
+> with `verify-boost-infinite.js` (Playwright).
+>
+> **What this `enhancements/` folder is:** the standalone
+> `infinite-scroll-observer.js` mutation-observer approach — now a **secondary
+> fallback / reference implementation**. It targets a *native* Shopify Liquid
+> grid (`[data-collection-products]`), which the live Boost-powered collections
+> do **not** use, so it is NOT what runs in production. Keep it for any
+> non-Boost (native Liquid) collection surface. Everything below — the
+> `deploy-infinite-scroll.sh` script and the `<script src="…observer.js">`
+> tag — applies only to that native-grid fallback scenario, not to the live
+> Boost grids.
+
## 🎯 What This Does
-Adds **automatic infinite scroll** to collection pages that:
+The standalone observer (this folder's fallback/reference) adds **automatic
+infinite scroll** to *native-Liquid* collection grids that:
- ✅ Loads more products when user scrolls to bottom
- ✅ Works WITHOUT breaking the existing grid layout
- ✅ Maintains pagination as a fallback
- ✅ Animated fade-in for new products
- ✅ ~6 KB minified, zero dependencies
+> For the LIVE Boost grids, the mechanism is the Boost config override, not
+> this script — see the STATUS banner above.
+
## 🚫 What It Does NOT Do
- ❌ Use wrapper divs (the source of layout breaks)
@@ -316,26 +344,36 @@ All logs have `[InfiniteScroll]` prefix. Filter in DevTools console.
## 📝 Commit Info
```
-commit c218e7fd
-Add infinite scroll implementation for collections
-(mutation observer approach, no wrapper divs)
+# LIVE mechanism (Boost config override) — what actually ships infinite scroll:
+fe289af theme-infinite-scroll: Boost AI pagination override
+ (force infinite_scroll via boostSDAppConfig patch) + hardened
+ boost-sd-custom fallback, Playwright verify
+
+# This folder's standalone observer (secondary fallback / reference):
+c218e7fd Add infinite scroll implementation for collections
+ (mutation observer approach, no wrapper divs)
```
---
## ✅ Status
-- [x] Script written & tested
+**LIVE:** Boost config override (`boost-infinite-override.liquid` +
+`boost-sd-custom.js`) — shipped, deployed via the `push-boost-override-to-*.sh`
+scripts and verified with `verify-boost-infinite.js`.
+
+**This folder (standalone observer — secondary fallback / reference):**
+
+- [x] Script written & tested (against a native-Liquid grid harness)
- [x] Test harness working
- [x] Architecture documented
- [x] Deployment script created
- [x] Integration guide written
- [x] Committed to git
-- [ ] Deploy to dev theme
-- [ ] A/B test with users
-- [ ] Launch to production
+- [ ] NOT deployed to the live Boost grids (by design — Boost override is live)
+- [ ] Reserve for any future non-Boost (native Liquid) collection surface
-Next step: Run `./deploy-infinite-scroll.sh --dev` when ready.
+Next step (native-grid surfaces only): run `./deploy-infinite-scroll.sh --dev`.
---
diff --git a/shopify/enhancements/VISUAL-EXPLANATION.md b/shopify/enhancements/VISUAL-EXPLANATION.md
index 2f57e652..bf140c1a 100644
--- a/shopify/enhancements/VISUAL-EXPLANATION.md
+++ b/shopify/enhancements/VISUAL-EXPLANATION.md
@@ -1,5 +1,18 @@
# Visual Explanation: Infinite Scroll Without Wrapper Divs
+> **Scope:** these diagrams illustrate the **standalone `infinite-scroll-observer.js`**
+> — the mutation-observer / direct-append approach that is the **secondary
+> fallback / reference** in this folder, drawn for a *native* Shopify Liquid
+> grid (`[data-collection-products]`).
+>
+> They do **not** depict the live production mechanism. On
+> designerwallcoverings.com the collection/search grids are rendered by **Boost
+> AI Search & Discovery** (`.boost-sd__product-item`), and infinite scroll is
+> the **Boost config override** — `boost-infinite-override.liquid` rewrites
+> `window.boostSDAppConfig.paginationType → "infinite_scroll"` before Boost's
+> bundle initializes, with `boost-sd-custom.js` as a belt-and-suspenders
+> fallback. See `WHAT-SHIPPED.md` for the live mechanism.
+
## The Problem: Wrapper Breaks Layout
```
@@ -541,6 +554,6 @@ This approach = Best of both worlds:
| **Problem** | Collections need infinite scroll | Users hate clicking pagination |
| **Solution** | Mutation observer + direct append | No wrapper to break layout |
| **How** | Find grid → Fetch next page → Parse HTML → Append products → Repeat | Clean, simple, robust |
-| **Result** | Works perfectly, no layout breaks, fallback always available | Production ready |
+| **Result** | Works perfectly, no layout breaks, fallback always available | Reference / fallback for native grids (live = Boost override) |
**That's it!** 🎉
diff --git a/shopify/enhancements/WHAT-SHIPPED.md b/shopify/enhancements/WHAT-SHIPPED.md
index 171f1515..41f28e2a 100644
--- a/shopify/enhancements/WHAT-SHIPPED.md
+++ b/shopify/enhancements/WHAT-SHIPPED.md
@@ -1,15 +1,42 @@
# What Shipped: Infinite Scroll for Designer Wallcoverings
+> ## ⚠️ What ACTUALLY shipped to production
+>
+> The live infinite scroll on designerwallcoverings.com is the **Boost config
+> override**, NOT the standalone observer described in the rest of this file.
+>
+> | | LIVE (production) | This folder (`enhancements/`) |
+> |---|---|---|
+> | **Mechanism** | Boost config override | Standalone mutation observer |
+> | **Files** | `theme-infinite-scroll-142250278963/snippets/boost-infinite-override.liquid` (PRIMARY) + `…/assets/boost-sd-custom.js` (fallback) | `infinite-scroll-observer.js` |
+> | **Targets** | Boost grids (`.boost-sd__product-item`) | Native Liquid grid (`[data-collection-products]`) |
+> | **How** | Rewrites `window.boostSDAppConfig.paginationType → "infinite_scroll"` before Boost's bundle reads it | Fetch next page → DOMParser → append to existing grid |
+> | **Deploy** | `push-boost-override-to-dev.sh` / `push-boost-override-to-LIVE.sh` | `deploy-infinite-scroll.sh` |
+> | **Verify** | `verify-boost-infinite.js` (Playwright) | local test harness |
+> | **Status** | **SHIPPED** 2026-06-24, commit `fe289af` | reference / fallback, NOT live |
+>
+> **Why the observer is not live:** the live collection/search grids are
+> rendered by Boost AI Search & Discovery, which uses its own markup
+> (`.boost-sd__product-item`) and exposes no API to switch pagination to
+> infinite scroll. The native-grid observer never finds a
+> `[data-collection-products]` element on those pages, so the only reliable
+> route is patching Boost's runtime config object — that is the Boost override.
+> Keep the observer as a reference / fallback for any non-Boost (native Liquid)
+> collection surface.
+>
+> Everything below documents that secondary observer implementation.
+
## Summary
-Complete **infinite scroll implementation** for designerwallcoverings.com collections that:
+Complete **standalone infinite scroll implementation** (the secondary /
+fallback reference in this folder) for *native-Liquid* collection grids that:
✅ **Works WITHOUT wrapper divs** — Uses mutation observer + direct DOM append
✅ **Never breaks grid layout** — CSS grid stays intact, sidebar aligned
✅ **Maintains pagination fallback** — Manual pagination still clickable
✅ **Fully tested** — Working test harness with mock data
-✅ **Production ready** — ~6 KB minified, zero dependencies
-✅ **Well documented** — 5 architecture docs + deployment guide
+✅ **Reference / fallback only** — ~6 KB minified, zero dependencies (NOT the live mechanism — see banner above)
+✅ **Well documented** — architecture docs + deployment guide
---
@@ -258,14 +285,20 @@ No crashes, no broken pages.
## Git Commits
```
-621498bf Add comprehensive infinite scroll documentation
+# LIVE mechanism — Boost config override (the actual production ship):
+fe289af theme-infinite-scroll: Boost AI pagination override
+ (force infinite_scroll via boostSDAppConfig patch) + hardened
+ boost-sd-custom fallback, Playwright verify, dev/LIVE push scripts
+
+# This folder — standalone observer (secondary fallback / reference):
+621498bf Add comprehensive infinite scroll documentation
(architecture, visual explanations, integration guides)
-c218e7fd Add infinite scroll implementation for collections
+c218e7fd Add infinite scroll implementation for collections
(mutation observer approach, no wrapper divs)
```
-Both commits in repo `~/Projects/Designer-Wallcoverings`.
+All commits in repo `~/Projects/Designer-Wallcoverings`.
---
@@ -367,13 +400,20 @@ window.DWInfiniteScroll.CONFIG
## Status
-- ✅ Script written & tested
+**LIVE (production):** Boost config override — `boost-infinite-override.liquid`
++ `boost-sd-custom.js`. Shipped 2026-06-24 (`fe289af`), deployed via the
+`push-boost-override-to-*.sh` scripts, verified with `verify-boost-infinite.js`.
+
+**This folder — standalone observer (secondary fallback / reference):**
+
+- ✅ Script written & tested (native-Liquid grid harness)
- ✅ Test harness working
-- ✅ Architecture documented (5 docs)
+- ✅ Architecture documented
- ✅ Deployment script created
- ✅ Integration guide written
- ✅ Committed to git
-- ⏳ Ready for: Deploy to dev theme → A/B test → Production rollout
+- ⛔ NOT deployed to the live Boost grids (by design — Boost override is live)
+- ♻️ Held in reserve for any future non-Boost (native Liquid) collection surface
---
@@ -390,7 +430,7 @@ window.DWInfiniteScroll.CONFIG
| **5 documentation files** | Clear explanation for any team member |
| **Deployment script** | One-command minify + upload to Shopify |
| **Error handling** | Graceful degradation, pagination fallback |
-| **Production ready** | Tested, documented, committed to git |
+| **Reference / fallback** | Tested, documented, committed to git (live mechanism is the Boost override) |
**Total delivery:** Complete, tested, documented infinite scroll system ready for production deployment.
diff --git a/shopify/enhancements/deploy-infinite-scroll.sh b/shopify/enhancements/deploy-infinite-scroll.sh
index d15920b6..3f68b8a0 100644
--- a/shopify/enhancements/deploy-infinite-scroll.sh
+++ b/shopify/enhancements/deploy-infinite-scroll.sh
@@ -1,13 +1,27 @@
#!/bin/bash
#
-# Deploy Infinite Scroll to Shopify Theme
+# Deploy the GENERIC (non-Boost) Infinite Scroll asset to a Shopify Theme
+#
+# ----------------------------------------------------------------------------
+# STATUS: FALLBACK / REFERENCE PATH — NOT the live deploy path.
+#
+# The live storefront's infinite scroll is the Boost SD override, which has
+# its OWN deploy scripts under theme-infinite-scroll-142250278963/
+# (push-boost-override-to-dev.sh / push-boost-override-to-LIVE.sh). Use those
+# for the shipped mechanism. This script only pushes the standalone generic
+# observer (infinite-scroll-observer.js) and is intended for non-Boost themes
+# / prototypes. Pushing it onto the live Boost theme would conflict.
+#
+# Steve-gated: do NOT run --prod (or any LIVE push) without explicit sign-off.
+# `--verify-only` is the safe way to dry-run this.
+# ----------------------------------------------------------------------------
#
# Usage:
# ./deploy-infinite-scroll.sh [--dev|--prod] [--no-minify] [--verify-only]
#
# Options:
# --dev Deploy to dev theme (default)
-# --prod Deploy to PRODUCTION (careful!)
+# --prod Deploy to PRODUCTION (Steve-gated — do not run unasked)
# --no-minify Don't minify, deploy as-is
# --verify-only Check deployment would succeed, don't actually deploy
#
diff --git a/shopify/enhancements/infinite-scroll-observer.js b/shopify/enhancements/infinite-scroll-observer.js
index 1f4f5e87..2418f29f 100644
--- a/shopify/enhancements/infinite-scroll-observer.js
+++ b/shopify/enhancements/infinite-scroll-observer.js
@@ -1,6 +1,23 @@
/**
* Infinite Scroll for Designer Wallcoverings Collections
*
+ * ┌──────────────────────────────────────────────────────────────────────┐
+ * │ STATUS: FALLBACK / REFERENCE PATH — NOT the live mechanism. │
+ * │ │
+ * │ The SHIPPED, live infinite scroll on the storefront is the Boost SD │
+ * │ override (theme-infinite-scroll-142250278963/ — boost-sd-custom.js + │
+ * │ snippets/boost-infinite-override.liquid), which forces Boost's own │
+ * │ native `infinite_scroll` pagination via the Boost SDK config. That is │
+ * │ what runs on collection pages today. │
+ * │ │
+ * │ THIS file is the generic, framework-agnostic approach for any theme │
+ * │ or page that does NOT run Boost (plain Liquid collection grids, the │
+ * │ DW sister-site storefronts, prototypes). Keep it as a portable │
+ * │ reference / fallback — do not wire it into a Boost-powered collection │
+ * │ (the two would fight over the grid). See the same-named markdown docs │
+ * │ in this folder for the full comparison. │
+ * └──────────────────────────────────────────────────────────────────────┘
+ *
* APPROACH: Mutation Observer + Fetch + Pure DOM Append
* ✅ NO wrapper divs — respects existing Shopify grid layout
* ✅ Detects grid mutations (new products appended)
@@ -33,7 +50,8 @@
observer: null,
intersectionObserver: null,
gridElement: null,
- paginationElement: null
+ paginationElement: null,
+ sentinelElement: null
};
// ============ LOGGING ============
@@ -102,12 +120,18 @@
animation: dw-fade-in 0.4s ease forwards;
}
- /* Loading indicator */
+ /* Loading indicator (span full width inside a CSS grid) */
.dw-infinite-loading {
display: flex;
justify-content: center;
padding: 32px;
gap: 6px;
+ grid-column: 1 / -1;
+ }
+
+ /* Sentinel must not consume a visible grid cell */
+ #dw-infinite-scroll-sentinel {
+ grid-column: 1 / -1;
}
.dw-infinite-loading-dot {
@@ -192,12 +216,23 @@
return 0;
}
+ // Keep the sentinel pinned to the end of the grid: insert new products
+ // before it, not after. Otherwise the sentinel ends up mid-grid and the
+ // IntersectionObserver stops firing after the first page.
+ const anchor = (state.sentinelElement && state.sentinelElement.parentNode === state.gridElement)
+ ? state.sentinelElement
+ : null;
+
let appended = 0;
products.forEach((product) => {
const clone = product.cloneNode(true);
// Mark for fade-in animation
clone.classList.add('dw-infinite-new');
- state.gridElement.appendChild(clone);
+ if (anchor) {
+ state.gridElement.insertBefore(clone, anchor);
+ } else {
+ state.gridElement.appendChild(clone);
+ }
appended++;
});
@@ -220,7 +255,12 @@
<div class="dw-infinite-loading-dot"></div>
`;
indicator.id = 'dw-infinite-loading';
- state.gridElement.appendChild(indicator);
+ // Keep the indicator just above the sentinel (end of grid).
+ if (state.sentinelElement && state.sentinelElement.parentNode === state.gridElement) {
+ state.gridElement.insertBefore(indicator, state.sentinelElement);
+ } else {
+ state.gridElement.appendChild(indicator);
+ }
}
function hideLoadingIndicator() {
@@ -235,15 +275,20 @@
// Use a sentinel element at the end of the grid
if (!state.gridElement) return;
- // Clean up old observer
+ // Clean up old observer + sentinel (setup can run twice: init + mutation
+ // observer). Never leave a stray sentinel behind in the grid.
if (state.intersectionObserver) {
state.intersectionObserver.disconnect();
}
+ if (state.sentinelElement && state.sentinelElement.parentNode) {
+ state.sentinelElement.parentNode.removeChild(state.sentinelElement);
+ }
const sentinel = document.createElement('div');
sentinel.id = 'dw-infinite-scroll-sentinel';
sentinel.style.height = '1px';
state.gridElement.appendChild(sentinel);
+ state.sentinelElement = sentinel;
state.intersectionObserver = new IntersectionObserver(
(entries) => {
@@ -391,6 +436,10 @@
log('Grid element detected via mutation observer');
setupIntersectionObserver();
}
+ } else {
+ // Grid is known — stop observing so we don't churn on our own appends.
+ state.observer.disconnect();
+ state.observer = null;
}
});
diff --git a/shopify/enhancements/infinite-scroll-test-harness.html b/shopify/enhancements/infinite-scroll-test-harness.html
index 1324ef08..88c1835d 100644
--- a/shopify/enhancements/infinite-scroll-test-harness.html
+++ b/shopify/enhancements/infinite-scroll-test-harness.html
@@ -1,3 +1,17 @@
+<!--
+ ============================================================================
+ FALLBACK / REFERENCE TEST HARNESS — NOT the live storefront mechanism.
+
+ This page exercises the generic (non-Boost) infinite-scroll-observer.js in
+ isolation, against a mock grid + a monkey-patched fetch. The LIVE infinite
+ scroll on the real storefront is the Boost SD override
+ (theme-infinite-scroll-142250278963/), not this code.
+
+ Serve this folder over http:// (e.g. `python3 -m http.server`) and open this
+ file so the "Initialize" button can load ./infinite-scroll-observer.js.
+ Opening via file:// will block the script load.
+ ============================================================================
+-->
<!DOCTYPE html>
<html lang="en">
<head>
@@ -510,18 +524,42 @@
};
// ============ UI CONTROLS ============
+ let statsTimer = null;
+ function startStats() {
+ updateStats();
+ if (!statsTimer) {
+ statsTimer = setInterval(updateStats, 500);
+ }
+ }
+
document.getElementById('initBtn').addEventListener('click', () => {
console.log('Initializing infinite scroll...');
populateInitialGrid();
- setTimeout(() => {
- // Load the infinite scroll script
- eval(document.querySelector('script[data-infinite-scroll]')?.textContent || '');
+
+ // Already loaded (e.g. clicked twice) — just re-init against fresh grid.
+ if (window.DWInfiniteScroll) {
+ window.DWInfiniteScroll.init();
+ startStats();
+ return;
+ }
+
+ // Load the REAL observer module from this folder (no eval). The IIFE
+ // auto-initializes on load; re-init guards against grid timing.
+ const s = document.createElement('script');
+ s.src = './infinite-scroll-observer.js';
+ s.onload = () => {
if (window.DWInfiniteScroll) {
window.DWInfiniteScroll.init();
- updateStats();
- setInterval(updateStats, 500);
+ startStats();
+ } else {
+ document.getElementById('statStatus').textContent = 'Load failed';
}
- }, 100);
+ };
+ s.onerror = () => {
+ document.getElementById('statStatus').textContent =
+ 'Script load blocked — serve over http://, not file://';
+ };
+ document.body.appendChild(s);
});
document.getElementById('resetBtn').addEventListener('click', () => {
@@ -568,10 +606,10 @@
populateInitialGrid();
</script>
- <!-- Inline the infinite scroll script for testing -->
- <script data-infinite-scroll>
- /* INFINITE SCROLL CODE WILL BE INSERTED HERE */
- // (Copy infinite-scroll-observer.js content here for production)
- </script>
+ <!--
+ The observer module is loaded on demand by the "Initialize Infinite Scroll"
+ button (./infinite-scroll-observer.js), so there is no inline copy to keep
+ in sync. Serve this folder over http:// for the load to succeed.
+ -->
</body>
</html>
diff --git a/shopify/scripts/anna-french-scraper.js b/shopify/scripts/anna-french-scraper.js
index 3730f3b8..1c8a95a3 100644
--- a/shopify/scripts/anna-french-scraper.js
+++ b/shopify/scripts/anna-french-scraper.js
@@ -78,7 +78,6 @@ function fetchPage(url) {
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
// ---------- helpers ----------
-const BANNED_WORD = /\bwallpapers?\b/gi;
function cleanWallpaper(s) {
if (!s) return s;
let out = String(s).replace(/\bWalls Wallpaper\b/gi, 'Wallcoverings').replace(/\bWallpapers\b/gi, 'Wallcoverings').replace(/\bWallpaper\b/gi, 'Wallcovering');
diff --git a/shopify/theme-infinite-scroll-142250278963/assets/boost-sd-custom.js b/shopify/theme-infinite-scroll-142250278963/assets/boost-sd-custom.js
index 3602c078..514ab49c 100644
--- a/shopify/theme-infinite-scroll-142250278963/assets/boost-sd-custom.js
+++ b/shopify/theme-infinite-scroll-142250278963/assets/boost-sd-custom.js
@@ -178,13 +178,22 @@
the fallback: it auto-fires the Load-More / next-page control on scroll and
hides ONLY the numbered pager (never Boost's own Load-More button).
Hardened 2026-06-24: no permanent allLoaded latch; recovers between Boost
- re-renders; distinguishes numbered pager from load-more. */
+ re-renders; distinguishes numbered pager from load-more.
+ Hardened 2026-06-26: single-init double-bind guard; idempotent style +
+ scroll-listener attach; re-arms across Boost re-renders / SPA navigation so
+ the grid is found even when it mounts long after first load. */
(function() {
'use strict';
+ if (window.__dwBoostInfiniteScroll) return; // guard against double-binding
+ window.__dwBoostInfiniteScroll = true;
+
var loading = false;
+ var STYLE_ID = 'dw-boost-hide-pager';
+ var scrollBound = false;
// Boost's "load more" button (infinite/load-more mode) takes priority; fall
- // back to a numbered "next" link only if no load-more control exists.
+ // back to a numbered "next" link only if no load-more control exists. We NEVER
+ // hide this button — only the numbered pager list is hidden via CSS below.
function getLoadMore() {
return document.querySelector('.boost-sd__load-more-button:not([disabled])') ||
document.querySelector('button[class*="load-more"]:not([disabled])') ||
@@ -198,6 +207,7 @@
if (btn) {
loading = true;
try { btn.click(); } catch (e) {}
+ // Transient latch only — always released so a later scroll can retry.
setTimeout(function() { loading = false; }, 1200);
return;
}
@@ -209,7 +219,8 @@
try {
window.scrollTo(0, Math.max(0, y - 400));
setTimeout(function () {
- window.scrollTo(0, document.documentElement.scrollHeight);
+ var h = document.documentElement.scrollHeight;
+ window.scrollTo(0, h);
setTimeout(function () { loading = false; }, 600);
}, 120);
} catch (e) { loading = false; }
@@ -221,22 +232,58 @@
if (docHeight - scrollBottom < 900) loadMore();
}
- function init() {
- var check = setInterval(function() {
+ // Idempotent: hide ONLY the numbered pager (pages list), never the
+ // load-more button. Guarded by element id so re-runs don't stack <style>s.
+ function hidePager() {
+ if (document.getElementById(STYLE_ID)) return;
+ var s = document.createElement('style');
+ s.id = STYLE_ID;
+ s.textContent =
+ '.boost-sd__pagination-list,' +
+ '.boost-sd__pagination-page-list,' +
+ '.boost-sd__pagination .boost-sd__pagination-item{display:none!important;}';
+ (document.head || document.documentElement).appendChild(s);
+ }
+
+ // Idempotent: bind the scroll handler at most once (it lives on window, so it
+ // survives Boost re-renders / SPA grid swaps without needing a rebind).
+ function bindScroll() {
+ if (scrollBound) return;
+ scrollBound = true;
+ window.addEventListener('scroll', onScroll, { passive: true });
+ }
+
+ // Poll for the Boost grid and arm when it appears. Re-armable across SPA
+ // navigation — the grid can mount well after the first poll window closes.
+ var check = null;
+ function arm() {
+ if (check) return; // a poll cycle is already running
+ var ticks = 0;
+ check = setInterval(function() {
+ ticks++;
if (document.querySelector('.boost-sd__product-list') ||
document.querySelector('.boost-sd__filter-block')) {
- clearInterval(check);
- window.addEventListener('scroll', onScroll, { passive: true });
- // Hide ONLY the numbered pager (pages list), not the load-more button.
- var s = document.createElement('style');
- s.textContent =
- '.boost-sd__pagination-list,' +
- '.boost-sd__pagination-page-list,' +
- '.boost-sd__pagination .boost-sd__pagination-item{display:none!important;}';
- document.head.appendChild(s);
+ clearInterval(check); check = null;
+ bindScroll();
+ hidePager();
+ } else if (ticks > 37) { // ~15s @ 400ms — stop this cycle, can re-arm
+ clearInterval(check); check = null;
}
}, 400);
- setTimeout(function() { clearInterval(check); }, 15000);
+ }
+
+ // Re-arm on SPA navigation so a freshly-mounted Boost grid is recaught.
+ function watchSpaNav() {
+ var last = location.href;
+ setInterval(function() {
+ if (location.href !== last) { last = location.href; arm(); }
+ }, 700);
+ window.addEventListener('popstate', arm);
+ }
+
+ function init() {
+ arm();
+ watchSpaNav();
}
if (document.readyState === 'loading') {
diff --git a/shopify/theme-infinite-scroll-142250278963/snippets/boost-infinite-override.liquid b/shopify/theme-infinite-scroll-142250278963/snippets/boost-infinite-override.liquid
index 5fe4529d..0593e4d5 100644
--- a/shopify/theme-infinite-scroll-142250278963/snippets/boost-infinite-override.liquid
+++ b/shopify/theme-infinite-scroll-142250278963/snippets/boost-infinite-override.liquid
@@ -34,8 +34,12 @@
// Recursively rewrite any "paginationType" key to infinite_scroll, plus the
// common Boost shapes (generalSettings + additionalElements.pagination).
- function patchObj(o, depth) {
+ // `seen` guards against cyclic / shared references so a self-referential
+ // Boost config can't loop or get re-walked needlessly.
+ function patchObj(o, depth, seen) {
if (!o || typeof o !== 'object' || depth > 6) return;
+ if (seen.indexOf(o) !== -1) return;
+ seen.push(o);
try {
if (Object.prototype.hasOwnProperty.call(o, 'paginationType') &&
typeof o.paginationType === 'string') {
@@ -50,18 +54,20 @@
for (var k in o) {
if (!Object.prototype.hasOwnProperty.call(o, k)) continue;
var v = o[k];
- if (v && typeof v === 'object') patchObj(v, depth + 1);
+ if (v && typeof v === 'object') patchObj(v, depth + 1, seen);
}
}
function patchConfig(cfg) {
if (!cfg || typeof cfg !== 'object') return cfg;
try {
- // Known top-level Boost surfaces.
- if (cfg.generalSettings) patchObj(cfg.generalSettings, 0);
- if (cfg.additionalElements) patchObj(cfg.additionalElements, 0);
- // Catch-all deep walk (resilient to schema drift).
- patchObj(cfg, 0);
+ // One shared `seen` set across all walks keeps total work O(n) and
+ // cycle-safe — known surfaces get a full-depth targeted walk, then the
+ // catch-all root walk skips anything already visited (schema-drift safe).
+ var seen = [];
+ if (cfg.generalSettings) patchObj(cfg.generalSettings, 0, seen);
+ if (cfg.additionalElements) patchObj(cfg.additionalElements, 0, seen);
+ patchObj(cfg, 0, seen);
} catch (e) {}
return cfg;
}
diff --git a/shopify/theme-infinite-scroll-142250278963/verify-boost-infinite.js b/shopify/theme-infinite-scroll-142250278963/verify-boost-infinite.js
index 6f7b0ebe..75690203 100644
--- a/shopify/theme-infinite-scroll-142250278963/verify-boost-infinite.js
+++ b/shopify/theme-infinite-scroll-142250278963/verify-boost-infinite.js
@@ -1,10 +1,54 @@
// Verify Boost infinite-scroll override on a DEV theme preview.
-// Loads the collection preview, counts .boost-sd__product-item, scrolls ~8x,
+// Loads each collection preview, counts .boost-sd__product-item, scrolls ~8x,
// recounts. PASS = count grows on BOTH desktop and mobile, no numbered pager.
+//
+// STRICTLY READ-ONLY: this script only navigates and scrolls the live theme
+// preview. It never clicks add-to-cart, submits forms, or mutates any state.
+//
+// Configuration (argv takes precedence over env, env over built-in defaults):
+// --theme=<id> | env DEV_THEME DEV theme id to preview
+// --collections=a,b,c | env COLLECTIONS one or more collection slugs
+// --filter=<querystring> | env FILTER_QS extra query string appended
+// --host=<hostname> | env VERIFY_HOST storefront hostname
+// --scrolls=<n> | env SCROLL_PASSES scroll passes per viewport
+// Bare (non --flag) argv values are also treated as collection slugs, so
+// node verify-boost-infinite.js red-wallcovering-collection blue-... works.
+//
+// Exit code: 0 = every collection PASS on both viewports, nonzero = any FAIL.
const { chromium } = require('playwright');
-const DEV = process.env.DEV_THEME || '143956443187';
-const URL = `https://www.designerwallcoverings.com/collections/red-wallcovering-collection?preview_theme_id=${DEV}&pf_t_product_type=Wallcovering`;
+// ---- argv / env parsing -----------------------------------------------------
+function parseArgs(argv) {
+ const opts = {};
+ const bare = [];
+ for (const a of argv) {
+ const m = /^--([^=]+)=(.*)$/.exec(a);
+ if (m) opts[m[1].toLowerCase()] = m[2];
+ else if (/^--/.test(a)) opts[a.slice(2).toLowerCase()] = 'true';
+ else bare.push(a);
+ }
+ return { opts, bare };
+}
+const { opts, bare } = parseArgs(process.argv.slice(2));
+
+const DEV = opts.theme || process.env.DEV_THEME || '143956443187';
+const HOST = opts.host || process.env.VERIFY_HOST || 'www.designerwallcoverings.com';
+const FILTER_QS = opts.filter != null ? opts.filter
+ : (process.env.FILTER_QS != null ? process.env.FILTER_QS : 'pf_t_product_type=Wallcovering');
+const SCROLL_PASSES = Math.max(1, parseInt(opts.scrolls || process.env.SCROLL_PASSES || '8', 10) || 8);
+
+const COLLECTIONS = (() => {
+ const raw = opts.collections || process.env.COLLECTIONS || '';
+ const fromOpt = raw.split(',').map(s => s.trim()).filter(Boolean);
+ const list = [...fromOpt, ...bare.map(s => s.trim()).filter(Boolean)];
+ return list.length ? Array.from(new Set(list)) : ['red-wallcovering-collection'];
+})();
+
+function buildUrl(slug) {
+ const qs = [`preview_theme_id=${encodeURIComponent(DEV)}`];
+ if (FILTER_QS) qs.push(FILTER_QS.replace(/^[?&]/, ''));
+ return `https://${HOST}/collections/${slug}?${qs.join('&')}`;
+}
const ITEM = '.boost-sd__product-item';
const PAGER_NUMBERED = '.boost-sd__pagination-list, .boost-sd__pagination-page-list, .boost-sd__pagination-item';
@@ -21,27 +65,53 @@ async function waitForGrid(page) {
} catch (e) { return false; }
}
-async function run(label, viewport, ua) {
+async function run(slug, label, viewport, ua) {
+ const url = buildUrl(slug);
const browser = await chromium.launch({ headless: true });
- const ctx = await browser.newContext({
- viewport,
- userAgent: ua,
- deviceScaleFactor: 2,
- });
+ const ctx = await browser.newContext({ viewport, userAgent: ua, deviceScaleFactor: 2 });
const page = await ctx.newPage();
- const result = { label, viewport, before: 0, after: 0, numberedPager: null, grew: false, ok: false };
+ const result = {
+ slug, label, viewport, url,
+ before: 0, after: 0, numberedPager: null, grew: false, ok: false,
+ failStep: null, error: null,
+ };
+ // Per-step diagnostics: name the phase we're in so a failure pinpoints it.
+ let step = 'init';
try {
- await page.goto(URL, { waitUntil: 'domcontentloaded', timeout: 60000 });
+ step = 'navigate';
+ const resp = await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
+ if (resp && resp.status() >= 400) {
+ result.failStep = step;
+ result.error = `HTTP ${resp.status()} loading collection (check slug "${slug}" + theme ${DEV})`;
+ await browser.close();
+ return result;
+ }
+
+ step = 'wait-for-grid';
const gridReady = await waitForGrid(page);
- if (!gridReady) { result.error = 'grid never rendered (no .boost-sd__product-item in 30s)'; await browser.close(); return result; }
+ if (!gridReady) {
+ result.failStep = step;
+ result.error = 'grid never rendered (no .boost-sd__product-item in 30s) — wrong collection, empty filter, or Boost not loaded';
+ await browser.close();
+ return result;
+ }
+
+ step = 'count-initial';
await page.waitForTimeout(2500); // let initial page settle
result.before = await countItems(page);
+ if (result.before === 0) {
+ result.failStep = step;
+ result.error = 'grid rendered but 0 items counted after settle';
+ await browser.close();
+ return result;
+ }
- // Scroll ~8 passes. Use INCREMENTAL smooth scrolling (mimics a real user)
+ // Scroll ~N passes. Use INCREMENTAL smooth scrolling (mimics a real user)
// so Boost's mobile IntersectionObserver sentinel actually crosses the
// viewport — a single jump-to-bottom overshoots it on tall image grids.
+ step = 'scroll-and-append';
let last = result.before;
- for (let i = 0; i < 8; i++) {
+ for (let i = 0; i < SCROLL_PASSES; i++) {
const sh = await page.evaluate(() => document.documentElement.scrollHeight);
let y = 0;
while (y < sh) {
@@ -55,6 +125,7 @@ async function run(label, viewport, ua) {
result.after = last;
result.grew = result.after > result.before;
+ step = 'check-pager';
// Numbered pager visible?
result.numberedPager = await page.evaluate(sel => {
const els = document.querySelectorAll(sel);
@@ -67,29 +138,59 @@ async function run(label, viewport, ua) {
}, PAGER_NUMBERED);
result.ok = result.grew && !result.numberedPager;
+ if (!result.ok) {
+ result.failStep = 'verdict';
+ if (!result.grew) result.error = `item count did not grow (before=${result.before} after=${result.after}) — infinite-scroll append not firing`;
+ else result.error = 'numbered pager is visible — Boost pagination not overridden to infinite scroll';
+ }
} catch (e) {
+ result.failStep = step;
result.error = e.message;
}
await browser.close();
return result;
}
-(async () => {
- const desktop = await run('desktop', { width: 1440, height: 900 },
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36');
- const mobile = await run('mobile', { width: 390, height: 844 },
- 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1');
+const DESKTOP_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36';
+const MOBILE_UA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1';
+(async () => {
console.log('\n================ BOOST INFINITE-SCROLL VERIFY ================');
- console.log('DEV theme:', DEV);
- console.log('URL:', URL);
- for (const r of [desktop, mobile]) {
- console.log(`\n[${r.label}] ${r.viewport.width}x${r.viewport.height}`);
- if (r.error) { console.log(' ERROR:', r.error); continue; }
- console.log(` before=${r.before} after=${r.after} grew=${r.grew} numberedPagerVisible=${r.numberedPager} => ${r.ok ? 'PASS' : 'FAIL'}`);
+ console.log('DEV theme :', DEV);
+ console.log('host :', HOST);
+ console.log('filter qs :', FILTER_QS || '(none)');
+ console.log('scroll passes:', SCROLL_PASSES);
+ console.log('collections :', COLLECTIONS.join(', '));
+
+ const collectionResults = [];
+ for (const slug of COLLECTIONS) {
+ console.log(`\n---------------- collection: ${slug} ----------------`);
+ console.log('URL:', buildUrl(slug));
+
+ const desktop = await run(slug, 'desktop', { width: 1440, height: 900 }, DESKTOP_UA);
+ const mobile = await run(slug, 'mobile', { width: 390, height: 844 }, MOBILE_UA);
+
+ for (const r of [desktop, mobile]) {
+ console.log(`\n[${r.label}] ${r.viewport.width}x${r.viewport.height}`);
+ if (r.error) {
+ console.log(` FAIL at step "${r.failStep}": ${r.error}`);
+ console.log(` (before=${r.before} after=${r.after} grew=${r.grew} numberedPagerVisible=${r.numberedPager})`);
+ continue;
+ }
+ console.log(` before=${r.before} after=${r.after} grew=${r.grew} numberedPagerVisible=${r.numberedPager} => ${r.ok ? 'PASS' : 'FAIL'}`);
+ }
+
+ const ok = desktop.ok && mobile.ok;
+ console.log(`\n collection ${slug}: ${ok ? 'PASS (append on both)' : 'FAIL'}`);
+ collectionResults.push({ slug, ok, desktop, mobile });
+ }
+
+ console.log('\n================== SUMMARY ==================');
+ for (const c of collectionResults) {
+ console.log(` ${c.ok ? 'PASS' : 'FAIL'} ${c.slug}`);
}
- const pass = desktop.ok && mobile.ok;
- console.log(`\nOVERALL: ${pass ? 'PASS (append on both)' : 'FAIL'}`);
+ const pass = collectionResults.every(c => c.ok);
+ console.log(`\nOVERALL: ${pass ? 'PASS' : 'FAIL'} (${collectionResults.filter(c => c.ok).length}/${collectionResults.length} collections)`);
console.log('=============================================================');
process.exit(pass ? 0 : 1);
})();
← d0429e1a auto-save: 2026-06-26T15:34:21 (6 files) — pending-approval/
·
back to Designer Wallcoverings
·
auto-save: 2026-06-26T17:04:47 (3 files) — pending-approval/ 2afc375b →