=== Smart Variation Gallery for WooCommerce ===
Contributors: addonn
Tags: woocommerce, variations, product gallery, variation images, color swatches
Requires at least: 6.0
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 2.2.4
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Upload one image and gallery per attribute value (like Color) within a product and apply it automatically to every matching WooCommerce variation.

== Description ==

If your products use attributes like Color and Size, WooCommerce makes you upload a separate image for every single variation, and it only supports ONE image per variation, not a gallery. Four colors times four sizes means sixteen uploads, even though the photo only changes with color.

Smart Variation Gallery fixes this:

* Upload one image and a full photo gallery per attribute value (e.g. once for "Red"), not per variation.
* Every variation in that product that uses that value, across every size, gets the image and gallery automatically.
* Images are scoped to each product, so two different products that happen to share a value (e.g. both have a "Red") never accidentally share the same photo.
* One-click "Sync Variation Images" button re-applies images any time you add new variations or update a value's photos.
* The frontend product gallery swaps automatically when a shopper changes the variation.
* Pro: bulk import/export your image mapping across many products via CSV.

= How it works =

1. Pick your "primary" attribute (usually Color) under WooCommerce > Variation Gallery.
2. Open a variable product that uses this attribute. Click the new "Variation Gallery" tab in the Product data box.
3. Upload one image and gallery per value used on that product (e.g. once for Red, once for Blue).
4. Update/save the product, then click "Sync Variation Images" in the Variations tab.
5. Done. All matching variations in that product, any size, now share the same image and gallery.

== Installation ==

1. Upload the plugin files to /wp-content/plugins/smart-variation-gallery, or install via Plugins > Add New > Upload Plugin.
2. Activate the plugin.
3. Make sure WooCommerce is installed and active.
4. Go to WooCommerce > Variation Gallery to choose your primary attribute.

== Frequently Asked Questions ==

= Does this work with custom (non-global, per-product) attributes? =

No. The primary attribute must be a global attribute (created under Products > Attributes) so every variation reliably reports the same term slug. The images themselves, however, are stored per product, not shared globally.

= If two products both have a "Red" variation, do they share the same photo? =

No. Each product has its own Image + Gallery upload for "Red" under its own Variation Gallery tab. Uploading a photo on one product never affects any other product.

= Will this overwrite images I already set manually on a variation? =

Only when you click "Sync Variation Images". Nothing changes automatically in the background.

= Does this duplicate images in my media library? =

No. Variations simply reference the same attachment ID you selected on the product's Variation Gallery tab, so nothing is re-uploaded or duplicated.

= What happens to variations that don't match any attribute value with an image set? =

They are skipped and keep whatever image they already had (or the product's default image).

== Screenshots ==

1. New "Variation Gallery" tab on each product: upload one image and gallery per value used on that product.
2. One-click sync applies it to every matching variation in that product.
3. The frontend gallery swaps automatically when the variation changes.

== Changelog ==

= 2.2.4 =
* Fix (Woostify, actual root cause): 2.2.2 and 2.2.3 still failed identically because the fix that was supposed to remove Woostify's own conflicting found_variation handler was running after this plugin's own handler was bound, using a keyword ("gallery") that also matches this plugin's own handler source — so it was deleting this plugin's own handler immediately after adding it, on every single page load, before it ever got a chance to run. Reordered so Woostify's handler is removed first, while only WooCommerce core's own (unrelated) handler exists to match against. Verified live with direct instrumentation: correct image count reaches the page every time, correct main carousel and thumbnails, zero console errors.

= 2.2.3 =
* Fix (Woostify, real root cause): even with 2.2.2's fix in place, live testing with instrumentation showed found_variation firing exactly once with the correct 5-image payload, yet only 4 images (a mix of 1 new + 3 stale) ever reached the page. The cause was this plugin's own use of Flickity's incremental remove()/append()/reloadCells() cell methods, which silently dropped cells once the slide count changed — the exact same class of bug already fixed for WoodMart's Swiper carousel earlier, and the same fix applies: rebuild the carousel fully (destroy and recreate with the theme's original options) instead of updating it incrementally. Verified live across multiple colors: correct slide count and correct thumbnails every time, zero console errors.

= 2.2.2 =
* Fix (Woostify, root cause): 2.2.1's verify-and-retry wasn't enough — Woostify's own found_variation handler doesn't just race this plugin's rebuild, it deterministically redoes its own (partial, single-thumbnail) update every time a variation is found, undoing this plugin's full rebuild no matter the timing. This plugin now removes that specific handler on Woostify, the same fix already used for WoodMart's carousel, since this plugin's own code already fully replaces what it was responsible for. Verified with real clicks: main carousel and thumbnail strip both update correctly together, every time, with zero console errors.

= 2.2.1 =
* Fix (Woostify): the main carousel updated correctly on every color change, but the thumbnail strip could end up a mix of old and new photos. Woostify syncs its own thumbnail strip to the carousel asynchronously, which could land after this plugin's rebuild and partially overwrite it. The thumbnail rebuild is now verified shortly after and reapplied automatically if it doesn't match, the same safety net already used for WoodMart's carousel.

= 2.2.0 =
* Compatibility (Woostify): Woostify builds its product gallery with a completely different carousel library (Flickity) than stock WooCommerce or WoodMart, under its own markup, and never renders the `.woocommerce-product-gallery` element this plugin previously relied on to detect a gallery at all. Previously this meant the plugin quietly did nothing on Woostify — no errors, but the per-color gallery never appeared, with no way to tell why. Woostify is now detected explicitly and integrated properly: the gallery and thumbnail strip are rebuilt using Flickity's own cell API, preserving the theme's carousel behaviour. Verified with real clicks across multiple colors: correct main image and thumbnails on every change, zero console errors, and the original gallery restores correctly when the selection is cleared.
* Improvement (all themes): if a theme has no recognized gallery markup at all (neither stock WooCommerce nor a theme this plugin has explicit support for), the plugin now logs a clear console warning instead of silently doing nothing, so a non-working gallery swap is diagnosable rather than invisible.

= 2.1.9 =
* Fix (important, all themes): the main "Select image" set for a color/value was being saved as that variation's native WooCommerce thumbnail, but was never actually included in the swapped-in gallery slider itself — only the secondary gallery images were. So the on-page gallery opened on the first secondary photo instead of the intended main photo, on every theme (not theme-specific). "Sync Variation Images" now stores the main image as the first slide, with the gallery images following it. Existing products need one more click of "Sync Variation Images" to apply this to already-synced variations.

= 2.1.8 =
* Fix (WoodMart, root cause): found the real reason the SKU, swatch highlighting and gallery could all get stuck after a swatch click. WoodMart's own theme script rebuilds its Swiper carousel incrementally on every variation change, a method that only works when the number of photos never changes (true for WooCommerce's default one-photo-per-variation, not true once this plugin adds a whole gallery per value). When the photo count changes, that incremental rebuild throws, and because it throws inside the theme's own click-handling code, it also cuts off whatever the theme was about to do next — which is why the SKU and swatch highlighting looked broken too, not just the gallery. This plugin now takes over the carousel rebuild entirely on WoodMart (using the theme's own full-rebuild hook, not the incremental one), so that code path never runs into this limitation. Verified with a clean swatch click: SKU, swatch highlighting and gallery all update together correctly, with no console errors.

= 2.1.7 =
* Fix (WoodMart): 2.1.6's fix wasn't enough on its own — deferring this plugin's own rebuild avoided one collision, but the theme's own carousel rebuild could still crash on repeated variation changes, again taking the SKU and swatch highlighting down with it. This plugin no longer triggers its own separate Swiper rebuild at all; it now swaps its gallery photos into place before the theme's own rebuild runs (instead of after), so the theme picks them up as part of its own single native rebuild, exactly as it would for its own default single-image variation swap. A direct rebuild is still used as a fallback, only if the theme's own rebuild doesn't visibly succeed.

= 2.1.6 =
* Fix (WoodMart, important): clicking a swatch could leave the SKU, swatch highlighting, and gallery all stuck on the previous value. Root cause: WoodMart's own theme script also rebuilds its carousel on every variation change, and this plugin was doing its own rebuild in the same instant — the two collided and crashed partway through, which stopped the theme's own SKU/swatch updates from completing too, not just the gallery. This plugin's rebuild is now deferred slightly so it never overlaps with the theme's own, fixing all three symptoms together.

= 2.1.5 =
* Fix: on themes/swatch plugins that auto-select the first attribute value before the page finishes loading (WoodMart confirmed), the gallery swap could fire before the theme's own carousel scripts had finished setting up, leaving the gallery blank on first load without the shopper ever touching a swatch. The plugin now waits until the page has fully loaded before reacting to variation changes, then re-checks the current selection once, removing the race.

= 2.1.4 =
* Fix (WoodMart): the theme's own Swiper carousel occasionally throws partway through rebuilding (a theme-side timing issue), leaving no slide marked active and the whole gallery blank instead of just unpolished. The plugin now verifies the rebuild actually succeeded, retries once if not, and as a last resort shows the correct photo directly so the gallery is never left blank.

= 2.1.3 =
* Compatibility: WoodMart replaces WooCommerce's default gallery with its own Swiper-based carousel (reusing the same wrapper class name, so it looked compatible but wasn't). The plugin now detects WoodMart and rebuilds the gallery through the theme's own documented reinit hook (wdReplaceMainGallery) instead of the flexslider path used for standard WooCommerce themes.

= 2.1.2 =
* Fix: repeatedly switching variations left orphaned flex-viewport wrapper divs and flex-control-nav thumbnail strips behind each time (flexslider's own destroy() doesn't remove the DOM it creates on init), which confused the slider's slide-count math and broke the layout after a couple of swaps. The gallery is now fully unwrapped and every leftover slider artifact removed before each swap, so it stays clean no matter how many times a shopper changes the variation.

= 2.1.1 =
* Fix: swapping the gallery HTML and calling WooCommerce's own gallery init again wasn't enough on its own — flexslider needs its previous instance properly destroyed first (via flexslider('destroy')) or its internal slide positioning breaks. This is the same mechanism WooCommerce itself relies on, just invoked correctly.

= 2.1.0 =
* Rework: the variation gallery now swaps directly into the theme's own native product gallery (the actual main image + thumbnails), instead of showing in a separate strip. Slide HTML is generated with WooCommerce's own wc_get_gallery_image_html() function so it always matches what the installed WooCommerce version/theme expects, and WooCommerce's own gallery init ($.fn.wc_product_gallery) is re-run after swapping — the same mechanism WooCommerce itself uses on page load, rather than a custom competing gallery UI.

= 2.0.5 =
* Fix (important): appending the strip inside the theme's own `.woocommerce-product-gallery` element broke the gallery/slider's own width and height calculations on some themes (Astra confirmed) — the main product image would disappear entirely. The strip is now inserted after the variations form in the summary column instead, never touching the gallery element at all.

= 2.0.4 =
* Fix: the lightbox's own !important CSS was forcing it visible at all times, fighting jQuery's show/hide. Visibility is now controlled by a single CSS class instead of inline styles, removing the conflict.
* Fix: the thumbnail strip was inserted as a new sibling after the theme's gallery element, which broke grid/flex-based single-product layouts (Astra) and made it render in the wrong place on the page. It's now appended inside the gallery wrapper so it stays in the same column.

= 2.0.3 =
* Rewrite: the gallery no longer rebuilds the theme's own gallery/slider DOM (that fought with each theme's slider, zoom and lazy-load scripts in different, unpredictable ways). It now renders its own small, self-contained thumbnail strip + lightbox below the native gallery, styled with scoped !important rules so no theme or plugin script can force it back to the wrong size. This is the reliable, theme-agnostic version.

= 2.0.2 =
* Fix: swapped-in gallery images had no size constraint and could render at their full native pixel dimensions (e.g. 4800px wide), pushing the visible slide off-screen. Images now use WooCommerce's own single-product image size plus an explicit max-width, and are no longer just the tiny 100x100 thumbnail.

= 2.0.1 =
* Fix: the frontend gallery-swap script wasn't enqueuing on some themes/page builders (e.g. Elementor single product templates) because it relied on the global $product being set before WooCommerce populates it. Now looks the product up directly from the queried post.

= 2.0.0 =
* Breaking change: image/gallery storage moved from global attribute-term meta to per-product meta, so products sharing an attribute value (e.g. "Red") no longer share photos. New "Variation Gallery" product tab replaces the old attribute-term edit fields. CSV export/import now scoped by product_id + term_slug.

= 1.0.0 =
* Initial release: term image/gallery fields, one-click variation sync, frontend gallery swap, Pro CSV bulk import/export.

== Upgrade Notice ==

= 2.0.0 =
Image storage moved from global attribute terms to per-product. Re-upload images under each product's new "Variation Gallery" tab and re-sync.
