Plugin Directory

Changeset 3442776


Ignore:
Timestamp:
01/19/2026 07:43:18 PM (2 months ago)
Author:
webdevmattcrom
Message:

Release version 1.3.0

Location:
synced-pattern-popups
Files:
35 added
12 edited

Legend:

Unmodified
Added
Removed
  • synced-pattern-popups/trunk/assets/css/admin.css

    r3439916 r3442776  
    10411041
    10421042/* Gallery Block Editor Styles - Using standard TextControl, no custom styles needed */
     1043
     1044/* Defaults Accordion Styles */
     1045.sppopups-defaults-accordion {
     1046    border: 1px solid #dcdcde;
     1047    border-radius: 4px;
     1048    background: #fff;
     1049    overflow: hidden;
     1050}
     1051
     1052.sppopups-defaults-accordion-header {
     1053    border-bottom: 1px solid #dcdcde;
     1054}
     1055
     1056.sppopups-defaults-accordion:last-of-type {
     1057    margin-bottom: 20px;
     1058}
     1059
     1060.sppopups-defaults-accordion-trigger {
     1061    width: 100%;
     1062    padding: 16px 24px;
     1063    background: #f6f7f7;
     1064    border: none;
     1065    cursor: pointer;
     1066    text-align: left;
     1067    display: flex;
     1068    justify-content: space-between;
     1069    align-items: center;
     1070    font-size: 16px;
     1071    font-weight: 600;
     1072    color: #1d2327;
     1073    transition: background-color 0.15s ease, color 0.15s ease;
     1074    position: relative;
     1075}
     1076
     1077.sppopups-defaults-accordion-trigger:hover {
     1078    background: #f0f0f1;
     1079    color: #2271b1;
     1080}
     1081
     1082.sppopups-defaults-accordion-trigger:focus {
     1083    outline: none;
     1084    box-shadow: inset 0 0 0 1px #2271b1;
     1085}
     1086
     1087.sppopups-defaults-accordion-trigger[aria-expanded="true"] {
     1088    background: #f6f7f7;
     1089    border-bottom: 1px solid #dcdcde;
     1090}
     1091
     1092.sppopups-defaults-accordion-title {
     1093    flex: 1;
     1094}
     1095
     1096.sppopups-defaults-accordion-icon {
     1097    width: 20px;
     1098    height: 20px;
     1099    position: relative;
     1100    flex-shrink: 0;
     1101    margin-left: 12px;
     1102    transition: transform 0.3s ease;
     1103}
     1104
     1105.sppopups-defaults-accordion-icon::before,
     1106.sppopups-defaults-accordion-icon::after {
     1107    content: '';
     1108    position: absolute;
     1109    background: currentColor;
     1110    transition: transform 0.3s ease;
     1111}
     1112
     1113.sppopups-defaults-accordion-icon::before {
     1114    width: 12px;
     1115    height: 2px;
     1116    left: 50%;
     1117    top: 50%;
     1118    transform: translate(-50%, -50%);
     1119}
     1120
     1121.sppopups-defaults-accordion-icon::after {
     1122    width: 2px;
     1123    height: 12px;
     1124    left: 50%;
     1125    top: 50%;
     1126    transform: translate(-50%, -50%);
     1127}
     1128
     1129.sppopups-defaults-accordion-trigger[aria-expanded="true"] .sppopups-defaults-accordion-icon::after {
     1130    transform: translate(-50%, -50%) rotate(90deg);
     1131    opacity: 0;
     1132}
     1133
     1134.sppopups-defaults-accordion-content {
     1135    overflow: hidden;
     1136    transition: opacity 0.3s ease;
     1137    opacity: 0;
     1138    max-height: 0;
     1139}
     1140
     1141.sppopups-defaults-accordion-content.is-open {
     1142    opacity: 1;
     1143    max-height: none;
     1144}
  • synced-pattern-popups/trunk/assets/css/modal.css

    r3439916 r3442776  
    4242    width: 100%;
    4343    height: 100%;
    44     background: rgba(0, 0, 0, 0.1);
    45     backdrop-filter: blur(8px);
    46     -webkit-backdrop-filter: blur(8px);
     44    background: var(--sppopups-overlay-color, rgba(0, 0, 0, 0.1));
     45    backdrop-filter: blur(var(--sppopups-backdrop-blur, 8px));
     46    -webkit-backdrop-filter: blur(var(--sppopups-backdrop-blur, 8px));
    4747}
    4848
     
    5050    position: relative;
    5151    width: 100%;
    52     max-width: 600px;
    53     max-height: 90vh;
     52    max-width: var(--sppopups-max-width, 600px);
     53    /*
     54     * Height behavior:
     55     * - No explicit height allows container to shrink to content naturally (small content = small modal)
     56     * - max-height constraint ensures it doesn't exceed viewport when content is tall
     57     * - display: flex with flex-direction: column enables proper height constraint propagation
     58     * - When content is small: container shrinks, card shrinks, no scrollbar
     59     * - When content is tall: container hits max-height, card is constrained via flex, content scrolls
     60     *
     61     * IMPORTANT: Flex container here allows card (flex child) to respect the max-height constraint
     62     * while still allowing natural shrinking when content is small.
     63     */
     64    display: flex;
     65    flex-direction: column;
     66    max-height: var(--sppopups-max-height-vh, 90vh);
    5467    z-index: 1;
    55     /* Fixed 6px border radius */
    56     border-radius: 6px;
     68    /* Border radius from CSS variable */
     69    border-radius: var(--sppopups-border-radius, 6px);
    5770    /* Ensure inline max-width from JavaScript is respected */
    5871    box-sizing: border-box;
     
    7184    right: -3px;
    7285    bottom: -3px;
    73     border-radius: 9px;
     86    border-radius: calc(var(--sppopups-border-radius, 6px) + 3px);
    7487    background: conic-gradient(
    7588        from 0deg,
     
    132145}
    133146
     147/* Hide footer close button if setting is disabled */
     148.sppopups-modal:not([data-show-footer-close="true"]) .sppopups-close-footer {
     149    display: none;
     150}
     151
    134152.sppopups-close-footer:hover {
    135153    color: #000;
     
    144162.sppopups-card {
    145163    background: #ffffff;
    146     border-radius: 8px;
     164    border-radius: calc(var(--sppopups-border-radius, 6px) + 2px);
    147165    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
    148166    position: relative;
    149167    padding: 0;
    150168    animation: sppopups-fade-in 0.3s ease-out;
    151     /* Ensure content doesn't overflow */
     169    /* Ensure content doesn't overflow the card boundaries */
    152170    overflow: hidden;
    153171    border: none;
    154     /* Flexbox for footer positioning */
     172    /*
     173     * Flexbox column layout enables:
     174     * - Content area to flex and scroll when constrained
     175     * - Footer to stick to bottom (flex-shrink: 0)
     176     * - Natural height growth for small content
     177     */
    155178    display: flex;
    156179    flex-direction: column;
    157     height: 100%;
    158     max-height: 90vh;
     180    /*
     181     * Height behavior as flex child of container:
     182     * - flex: 1 1 auto allows card to grow/shrink naturally
     183     * - min-height: 0 is CRITICAL - allows card to shrink below content size when constrained
     184     * - When container is small: card shrinks to content, no scroll needed
     185     * - When container hits max-height: card is constrained, content area scrolls
     186     *
     187     * IMPORTANT: min-height: 0 is required for flex items to shrink below their content size.
     188     * Without it, the card won't respect the container's max-height constraint.
     189     */
     190    flex: 1 1 auto;
     191    min-height: 0;
    159192    z-index: 1;
    160193}
     
    211244}
    212245
     246/* Hide icon close button if setting is disabled */
     247.sppopups-modal:not([data-show-icon-close="true"]) .sppopups-close {
     248    display: none;
     249}
     250
    213251.sppopups-close:hover {
    214252    color: #000;
     
    233271.sppopups-content {
    234272    width: 100%;
    235     min-height: 200px;
    236273    position: relative;
    237274    z-index: 1;
    238     /* Make content scrollable */
     275    /*
     276     * Overflow behavior: auto enables scrollbar only when content exceeds available space.
     277     * This works because:
     278     * - Small content: card/container grow naturally, no scrollbar needed
     279     * - Tall content: container hits max-height, card constrained, content scrolls
     280     */
    239281    overflow-y: auto;
    240282    overflow-x: hidden;
    241     flex: 1;
     283    /*
     284     * Flex: 1 allows content to fill available space in flex column.
     285     * min-height: 0 is CRITICAL - without it, flex items won't shrink below content size,
     286     * preventing scrolling. This is a flexbox quirk that must be set.
     287     */
     288    flex: 1 1 auto;
     289    min-height: 0;
    242290
    243291    /* Smooth scrolling on iOS */
     
    317365    .sppopups-container {
    318366        max-width: 100%;
    319         max-height: calc(100vh - 20px);
    320         max-height: calc(100vh - max(20px, env(safe-area-inset-top)) - max(20px, env(safe-area-inset-bottom)));
    321         border-radius: 6px;
     367        /* Use CSS variable if set (percentage converted to vh), otherwise calculate from viewport */
     368        max-height: var(--sppopups-max-height-vh, calc(100vh - max(20px, env(safe-area-inset-top)) - max(20px, env(safe-area-inset-bottom))));
     369        border-radius: var(--sppopups-border-radius, 6px);
    322370    }
    323371
    324372    .sppopups-container::before {
    325         border-radius: 8px;
     373        border-radius: calc(var(--sppopups-border-radius, 6px) + 3px);
    326374    }
    327375
     
    369417    .sppopups-container {
    370418        max-width: 90%;
    371         max-height: 85vh;
     419        /* Use CSS variable if set (percentage converted to vh), otherwise use 85vh */
     420        max-height: var(--sppopups-max-height-vh, 85vh);
    372421    }
    373422
     
    384433@media screen and (min-width: 769px) {
    385434    .sppopups-container {
    386         max-width: 600px;
     435        max-width: var(--sppopups-max-width, 600px);
    387436    }
    388437}
     
    404453
    405454    .sppopups-container {
    406         max-height: calc(100vh - 20px);
    407         max-height: calc(100vh - max(20px, env(safe-area-inset-top)) - max(20px, env(safe-area-inset-bottom)));
     455        /* Use CSS variable if set (percentage converted to vh), otherwise calculate from viewport */
     456        max-height: var(--sppopups-max-height-vh, calc(100vh - max(20px, env(safe-area-inset-top)) - max(20px, env(safe-area-inset-bottom))));
    408457        margin-top: 10px;
    409         border-radius: 6px;
     458        border-radius: var(--sppopups-border-radius, 6px);
    410459    }
    411460
    412461    .sppopups-container::before {
    413         border-radius: 8px;
     462        border-radius: calc(var(--sppopups-border-radius, 6px) + 3px);
    414463    }
    415464
     
    500549    box-sizing: border-box;
    501550    opacity: 0;
    502     transition: opacity 0.5s cubic-bezier(0.4, 0, 0.2, 1);
     551    transition: opacity var(--sppopups-gallery-transition-duration, 500ms) cubic-bezier(0.4, 0, 0.2, 1);
    503552    pointer-events: none;
    504553    z-index: 1;
     554}
     555
     556/* Disable crossfade transition if setting is disabled */
     557.sppopups-modal:not([data-gallery-crossfade="true"]) .sppopups-gallery-image-wrapper {
     558    transition: opacity 0ms;
    505559}
    506560
     
    592646}
    593647
     648/* Always show navigation buttons if hover setting is disabled */
     649.sppopups-modal:not([data-gallery-nav-hover="true"]) .sppopups-gallery-nav {
     650    opacity: 1;
     651    pointer-events: auto;
     652}
     653
    594654.sppopups-gallery-nav:hover {
    595655    color: #000;
     
    676736}
    677737
     738/* Hide captions if setting is disabled */
     739.sppopups-modal:not([data-gallery-show-captions="true"]) .sppopups-gallery-caption {
     740    display: none;
     741}
     742
    678743/* If caption contains HTML, allow it to display properly */
    679744.sppopups-gallery-caption p {
  • synced-pattern-popups/trunk/assets/js/admin.js

    r3439916 r3442776  
    150150                window.location.hash = '#' + tabId;
    151151            }
     152
     153            // Update form tab fields when tab switches
     154            if (typeof window.sppopupsUpdateTabFields === 'function') {
     155                window.sppopupsUpdateTabFields();
     156            }
    152157        }
    153158
     
    176181        function handleInitialTab() {
    177182            var hash      = window.location.hash.substring( 1 );
    178             var validTabs = ['patterns', 'tldr', 'how-to-use'];
     183            var validTabs = ['patterns', 'tldr', 'defaults', 'how-to-use'];
    179184
    180185            if (hash && validTabs.indexOf( hash ) !== -1) {
     
    189194        function handleHashChange() {
    190195            var hash      = window.location.hash.substring( 1 );
    191             var validTabs = ['patterns', 'tldr', 'how-to-use'];
     196            var validTabs = ['patterns', 'tldr', 'defaults', 'how-to-use'];
    192197
    193198            if (hash && validTabs.indexOf( hash ) !== -1) {
     
    205210                if (target && target.getAttribute( 'href' ) && target.getAttribute( 'href' ).startsWith( '#' )) {
    206211                    var hash      = target.getAttribute( 'href' ).substring( 1 );
    207                     var validTabs = ['patterns', 'tldr', 'how-to-use'];
     212                    var validTabs = ['patterns', 'tldr', 'defaults', 'how-to-use'];
    208213
    209214                    if (validTabs.indexOf( hash ) !== -1) {
     
    339344        );
    340345
     346        // Initialize form tab persistence first (so it's available when tabs initialize)
     347        initFormTabPersistence();
     348
    341349        // Initialize tabs
    342350        initTabs();
     
    347355        // Initialize TLDR prompt type toggle
    348356        initTldrPromptToggle();
     357
     358        // Initialize defaults inheritance toggles
     359        initDefaultsInheritanceToggles();
     360
     361        // Initialize defaults accordions
     362        initDefaultsAccordions();
     363    }
     364
     365    /**
     366     * Initialize defaults accordions
     367     */
     368    function initDefaultsAccordions() {
     369        var accordionTriggers = document.querySelectorAll( '.sppopups-defaults-accordion-trigger' );
     370
     371        accordionTriggers.forEach(
     372            function (trigger) {
     373                trigger.addEventListener(
     374                    'click',
     375                    function () {
     376                        var isExpanded = trigger.getAttribute( 'aria-expanded' ) === 'true';
     377                        var contentId = trigger.getAttribute( 'aria-controls' );
     378                        var content = document.getElementById( contentId );
     379
     380                        if ( ! content) {
     381                            return;
     382                        }
     383
     384                        // Toggle state
     385                        var newExpanded = ! isExpanded;
     386                        trigger.setAttribute( 'aria-expanded', newExpanded.toString() );
     387
     388                        // Toggle content visibility
     389                        if (newExpanded) {
     390                            // Set display first, then trigger transition
     391                            content.style.display = 'block';
     392                            // Force reflow to ensure display is applied
     393                            content.offsetHeight;
     394                            // Add class to trigger transition
     395                            content.classList.add( 'is-open' );
     396                        } else {
     397                            // Remove class to trigger transition
     398                            content.classList.remove( 'is-open' );
     399                            // Wait for transition to complete before hiding
     400                            setTimeout(
     401                                function () {
     402                                    if ( ! content.classList.contains( 'is-open' )) {
     403                                        content.style.display = 'none';
     404                                    }
     405                                },
     406                                300
     407                            );
     408                        }
     409                    }
     410                );
     411            }
     412        );
    349413    }
    350414
     
    462526    // Initialize review notice dismiss functionality
    463527    initReviewNoticeDismiss();
     528
     529    /**
     530     * Initialize defaults inheritance toggles
     531     */
     532    function initDefaultsInheritanceToggles() {
     533        // TLDR inheritance toggles
     534        var tldrModalAppearanceRadios = document.querySelectorAll( 'input[name="sppopups_defaults_tldr[inheritModalAppearance]"]' );
     535        var tldrOverlayRadios = document.querySelectorAll( 'input[name="sppopups_defaults_tldr[inheritOverlay]"]' );
     536        var tldrCloseButtonsRadios = document.querySelectorAll( 'input[name="sppopups_defaults_tldr[inheritCloseButtons]"]' );
     537
     538        // Gallery inheritance toggles
     539        var galleryModalAppearanceRadios = document.querySelectorAll( 'input[name="sppopups_defaults_gallery[inheritModalAppearance]"]' );
     540        var galleryOverlayRadios = document.querySelectorAll( 'input[name="sppopups_defaults_gallery[inheritOverlay]"]' );
     541        var galleryCloseButtonsRadios = document.querySelectorAll( 'input[name="sppopups_defaults_gallery[inheritCloseButtons]"]' );
     542
     543        // Toggle function for TLDR modal appearance
     544        function toggleTldrModalAppearance() {
     545            var selectedValue = '';
     546            tldrModalAppearanceRadios.forEach(
     547                function (radio) {
     548                    if (radio.checked) {
     549                        selectedValue = radio.value;
     550                    }
     551                }
     552            );
     553
     554            var customWrapper = document.getElementById( 'tldr-modal-appearance-custom' );
     555            if (customWrapper) {
     556                customWrapper.style.display = (selectedValue === '0') ? 'block' : 'none';
     557            }
     558        }
     559
     560        // Toggle function for TLDR overlay
     561        function toggleTldrOverlay() {
     562            var selectedValue = '';
     563            tldrOverlayRadios.forEach(
     564                function (radio) {
     565                    if (radio.checked) {
     566                        selectedValue = radio.value;
     567                    }
     568                }
     569            );
     570
     571            var customWrapper = document.getElementById( 'tldr-overlay-custom' );
     572            if (customWrapper) {
     573                customWrapper.style.display = (selectedValue === '0') ? 'block' : 'none';
     574            }
     575        }
     576
     577        // Toggle function for TLDR close buttons
     578        function toggleTldrCloseButtons() {
     579            var selectedValue = '';
     580            tldrCloseButtonsRadios.forEach(
     581                function (radio) {
     582                    if (radio.checked) {
     583                        selectedValue = radio.value;
     584                    }
     585                }
     586            );
     587
     588            var customWrapper = document.getElementById( 'tldr-close-buttons-custom' );
     589            if (customWrapper) {
     590                customWrapper.style.display = (selectedValue === '0') ? 'block' : 'none';
     591            }
     592        }
     593
     594        // Toggle function for Gallery modal appearance
     595        function toggleGalleryModalAppearance() {
     596            var selectedValue = '';
     597            galleryModalAppearanceRadios.forEach(
     598                function (radio) {
     599                    if (radio.checked) {
     600                        selectedValue = radio.value;
     601                    }
     602                }
     603            );
     604
     605            var customWrapper = document.getElementById( 'gallery-modal-appearance-custom' );
     606            if (customWrapper) {
     607                customWrapper.style.display = (selectedValue === '0') ? 'block' : 'none';
     608            }
     609        }
     610
     611        // Toggle function for Gallery overlay
     612        function toggleGalleryOverlay() {
     613            var selectedValue = '';
     614            galleryOverlayRadios.forEach(
     615                function (radio) {
     616                    if (radio.checked) {
     617                        selectedValue = radio.value;
     618                    }
     619                }
     620            );
     621
     622            var customWrapper = document.getElementById( 'gallery-overlay-custom' );
     623            if (customWrapper) {
     624                customWrapper.style.display = (selectedValue === '0') ? 'block' : 'none';
     625            }
     626        }
     627
     628        // Toggle function for Gallery close buttons
     629        function toggleGalleryCloseButtons() {
     630            var selectedValue = '';
     631            galleryCloseButtonsRadios.forEach(
     632                function (radio) {
     633                    if (radio.checked) {
     634                        selectedValue = radio.value;
     635                    }
     636                }
     637            );
     638
     639            var customWrapper = document.getElementById( 'gallery-close-buttons-custom' );
     640            if (customWrapper) {
     641                customWrapper.style.display = (selectedValue === '0') ? 'block' : 'none';
     642            }
     643        }
     644
     645        // Set initial state
     646        toggleTldrModalAppearance();
     647        toggleTldrOverlay();
     648        toggleTldrCloseButtons();
     649        toggleGalleryModalAppearance();
     650        toggleGalleryOverlay();
     651        toggleGalleryCloseButtons();
     652
     653        // Listen for changes
     654        tldrModalAppearanceRadios.forEach(
     655            function (radio) {
     656                radio.addEventListener( 'change', toggleTldrModalAppearance );
     657            }
     658        );
     659
     660        tldrOverlayRadios.forEach(
     661            function (radio) {
     662                radio.addEventListener( 'change', toggleTldrOverlay );
     663            }
     664        );
     665
     666        tldrCloseButtonsRadios.forEach(
     667            function (radio) {
     668                radio.addEventListener( 'change', toggleTldrCloseButtons );
     669            }
     670        );
     671
     672        galleryModalAppearanceRadios.forEach(
     673            function (radio) {
     674                radio.addEventListener( 'change', toggleGalleryModalAppearance );
     675            }
     676        );
     677
     678        galleryOverlayRadios.forEach(
     679            function (radio) {
     680                radio.addEventListener( 'change', toggleGalleryOverlay );
     681            }
     682        );
     683
     684        galleryCloseButtonsRadios.forEach(
     685            function (radio) {
     686                radio.addEventListener( 'change', toggleGalleryCloseButtons );
     687            }
     688        );
     689    }
     690
     691    /**
     692     * Initialize form tab persistence
     693     * Updates hidden tab fields before form submission to preserve current tab
     694     */
     695    function initFormTabPersistence() {
     696        // Get all hidden tab input fields
     697        var tabFields = document.querySelectorAll( 'input[name="sppopups_current_tab"]' );
     698
     699        if ( ! tabFields.length) {
     700            return;
     701        }
     702
     703        // Function to get current tab from URL hash or active tab
     704        function getCurrentTab() {
     705            // First, try to get from URL hash
     706            var hash = window.location.hash.substring( 1 );
     707            var validTabs = ['patterns', 'tldr', 'defaults', 'how-to-use'];
     708            if (hash && validTabs.indexOf( hash ) !== -1) {
     709                return hash;
     710            }
     711
     712            // If no hash, try to detect active tab from DOM
     713            var activeTabLink = document.querySelector( '.sppopups-tab-nav-link.active' );
     714            if (activeTabLink) {
     715                var href = activeTabLink.getAttribute( 'href' );
     716                if (href && href.startsWith( '#' )) {
     717                    var tabId = href.substring( 1 );
     718                    if (validTabs.indexOf( tabId ) !== -1) {
     719                        return tabId;
     720                    }
     721                }
     722            }
     723
     724            // Check which tab content is visible
     725            var activeTabContent = document.querySelector( '.sppopups-tab-content.active' );
     726            if (activeTabContent) {
     727                var tabId = activeTabContent.id.replace( 'sppopups-tab-', '' );
     728                if (validTabs.indexOf( tabId ) !== -1) {
     729                    return tabId;
     730                }
     731            }
     732
     733            // Default to patterns if nothing found
     734            return 'patterns';
     735        }
     736
     737        // Update all hidden tab fields with current tab
     738        function updateTabFields() {
     739            var currentTab = getCurrentTab();
     740            tabFields.forEach(
     741                function (field) {
     742                    field.value = currentTab;
     743                }
     744            );
     745        }
     746
     747        // Make updateTabFields available globally so switchTab can call it
     748        window.sppopupsUpdateTabFields = updateTabFields;
     749
     750        // Update on page load (with a small delay to ensure tabs are initialized)
     751        setTimeout(
     752            function () {
     753                updateTabFields();
     754            },
     755            100
     756        );
     757
     758        // Update when hash changes (tab switch)
     759        window.addEventListener(
     760            'hashchange',
     761            function () {
     762                updateTabFields();
     763            }
     764        );
     765
     766        // Update before form submission to ensure we have the latest tab
     767        tabFields.forEach(
     768            function (field) {
     769                var form = field.closest( 'form' );
     770                if (form) {
     771                    form.addEventListener(
     772                        'submit',
     773                        function (e) {
     774                            // Update immediately before submission
     775                            updateTabFields();
     776                        },
     777                        false
     778                    );
     779                }
     780            }
     781        );
     782    }
    464783})();
  • synced-pattern-popups/trunk/assets/js/gallery.js

    r3439916 r3442776  
    3030        closeModal: null,
    3131        setupModalState: null,
     32        applyModalDefaults: null,
     33        getDefaultsForType: null,
    3234        getTitleElement: null,
    3335        focusWithoutScroll: null,
     
    525527        currentGalleryData.settings = settings;
    526528
    527         // Set modal size
     529        // Get gallery defaults
     530        var galleryDefaults = null;
     531        if (dependencies.getDefaultsForType) {
     532            galleryDefaults = dependencies.getDefaultsForType( 'gallery' );
     533        }
     534
     535        // Apply defaults to modal (overrides with gallery-specific settings if provided)
     536        var overrides = {};
     537        if (settings.modalSize) {
     538            overrides.maxWidth = settings.modalSize;
     539        }
     540        if (dependencies.applyModalDefaults && dependencies.modal) {
     541            dependencies.applyModalDefaults( dependencies.modal, 'gallery', overrides );
     542        }
     543
     544        // Set modal size (may be overridden by defaults)
    528545        if (dependencies.container) {
    529             dependencies.container.style.maxWidth = settings.modalSize + 'px';
     546            var finalMaxWidth = overrides.maxWidth || (galleryDefaults && galleryDefaults.maxWidth) || settings.modalSize || 600;
     547            dependencies.container.style.maxWidth = finalMaxWidth + 'px';
    530548            if (dependencies.setCurrentMaxWidth) {
    531                 dependencies.setCurrentMaxWidth( settings.modalSize );
    532             }
    533         }
    534 
    535         // Show/hide close buttons based on setting
    536         if (dependencies.closeBtn) {
    537             dependencies.closeBtn.style.display = (settings.closeButtons === 'icon' || settings.closeButtons === 'both') ? '' : 'none';
    538         }
    539 
    540         // Use common modal setup
    541         dependencies.setupModalState( settings.modalSize, '' );
     549                dependencies.setCurrentMaxWidth( finalMaxWidth );
     550            }
     551        }
     552
     553        // Use common modal setup with gallery type
     554        dependencies.setupModalState( settings.modalSize || null, '', 'gallery' );
    542555
    543556        // Create container for gallery images
  • synced-pattern-popups/trunk/assets/js/modal.js

    r3439916 r3442776  
    561561
    562562    /**
     563     * Get defaults for a popup type with inheritance logic applied
     564     *
     565     * @param {string} popupType Popup type: 'pattern', 'tldr', or 'gallery'
     566     * @return {object} Complete defaults object with inheritance resolved
     567     */
     568    function getDefaultsForType(popupType) {
     569        // Check if defaults are available
     570        if ( ! sppopups || ! sppopups.defaults || ! sppopups.defaults[popupType]) {
     571            // Log warning if defaults not available (helps with debugging)
     572            if (window.console && console.warn) {
     573                console.warn( 'SPPopups: Defaults not available for type', popupType, '- using fallback defaults' );
     574            }
     575            // Fallback to hardcoded defaults if not available
     576            return {
     577                maxWidth: 600,
     578                borderRadius: 6,
     579                maxHeight: 90,
     580                overlayColor: 'rgba(0, 0, 0, 0.1)',
     581                backdropBlur: 8,
     582                showIconClose: true,
     583                showFooterClose: true,
     584                footerCloseText: 'Close →'
     585            };
     586        }
     587
     588        return sppopups.defaults[popupType];
     589    }
     590
     591    /**
     592     * Apply CSS variables and data attributes to modal based on defaults
     593     *
     594     * @param {HTMLElement} modalElement Modal element
     595     * @param {string} popupType Popup type: 'pattern', 'tldr', or 'gallery'
     596     * @param {object} overrides Optional overrides (e.g., custom max-width from trigger)
     597     * @return {object} Applied defaults object
     598     */
     599    function applyModalDefaults(modalElement, popupType, overrides) {
     600        var defaults = getDefaultsForType(popupType);
     601
     602        // Apply overrides if provided
     603        if (overrides) {
     604            defaults = Object.assign( {}, defaults, overrides);
     605        }
     606
     607        // Set CSS variables
     608        modalElement.style.setProperty( '--sppopups-max-width', defaults.maxWidth + 'px' );
     609        modalElement.style.setProperty( '--sppopups-border-radius', defaults.borderRadius + 'px' );
     610        // Convert percentage to viewport height units for proper scrolling
     611        modalElement.style.setProperty( '--sppopups-max-height', defaults.maxHeight + '%' );
     612        modalElement.style.setProperty( '--sppopups-max-height-vh', defaults.maxHeight + 'vh' );
     613        modalElement.style.setProperty( '--sppopups-overlay-color', defaults.overlayColor );
     614        modalElement.style.setProperty( '--sppopups-backdrop-blur', defaults.backdropBlur + 'px' );
     615
     616        // Set data attributes for conditional CSS
     617        modalElement.setAttribute( 'data-show-icon-close', defaults.showIconClose ? 'true' : 'false' );
     618        modalElement.setAttribute( 'data-show-footer-close', defaults.showFooterClose ? 'true' : 'false' );
     619
     620        // Gallery-specific attributes
     621        if (popupType === 'gallery') {
     622            modalElement.setAttribute( 'data-gallery-crossfade', defaults.crossfadeTransition ? 'true' : 'false' );
     623            modalElement.setAttribute( 'data-gallery-show-captions', defaults.showCaptions ? 'true' : 'false' );
     624            modalElement.setAttribute( 'data-gallery-nav-hover', defaults.showNavOnHover ? 'true' : 'false' );
     625            if (defaults.transitionDuration !== undefined) {
     626                modalElement.style.setProperty( '--sppopups-gallery-transition-duration', defaults.transitionDuration + 'ms' );
     627            }
     628        }
     629
     630        // Update footer close button text if available
     631        if (defaults.footerCloseText && closeFooterBtn) {
     632            closeFooterBtn.textContent = defaults.footerCloseText;
     633        }
     634
     635        return defaults;
     636    }
     637
     638    /**
    563639     * Setup modal state (common initialization logic)
    564640     *
    565641     * @param {number|null} maxWidth Optional max-width in pixels
    566642     * @param {string} loadingContent HTML content to show while loading
    567      */
    568     function setupModalState(maxWidth, loadingContent) {
     643     * @param {string} popupType Optional popup type for applying defaults
     644     */
     645    function setupModalState(maxWidth, loadingContent, popupType) {
    569646        // Save scroll position BEFORE any DOM changes (for all screen sizes)
    570647        savedScrollPosition = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
     
    573650        lastActiveElement = document.activeElement;
    574651
    575         // Store the requested max-width for resize handling
    576         currentMaxWidth = maxWidth;
    577 
    578         // Calculate and apply max-width with 6% margin
     652        // Apply defaults if popup type is provided
     653        var finalMaxWidth = maxWidth;
     654        var overrides = {};
    579655        if (maxWidth !== null) {
    580             var calculatedWidth      = calculateMaxWidth( maxWidth );
     656            overrides.maxWidth = maxWidth;
     657        }
     658
     659        if (popupType) {
     660            var appliedDefaults = applyModalDefaults( modal, popupType, overrides );
     661            // Use the applied max-width from defaults (or override)
     662            finalMaxWidth = appliedDefaults.maxWidth;
     663            // Debug: log applied defaults (remove in production if desired)
     664            if (window.console && console.log) {
     665                console.log( 'SPPopups: Applied defaults for', popupType, appliedDefaults );
     666            }
     667        } else {
     668            // Legacy behavior: use provided maxWidth or default
     669            if (maxWidth === null) {
     670                finalMaxWidth = 600; // Legacy default
     671            }
     672        }
     673
     674        // Store the final max-width for resize handling
     675        currentMaxWidth = finalMaxWidth;
     676
     677        // Apply max-width to container (with 6% margin calculation)
     678        if (finalMaxWidth !== null) {
     679            var calculatedWidth      = calculateMaxWidth( finalMaxWidth );
    581680            container.style.maxWidth = calculatedWidth + 'px';
    582681        } else {
     
    639738        }
    640739
    641         setupModalState( maxWidth, loadingHtml );
     740        setupModalState( maxWidth, loadingHtml, 'pattern' );
    642741
    643742        // Prepare form data for POST request
     
    11261225        }
    11271226
    1128         // Use common modal setup (no custom max-width for TLDR)
    1129         setupModalState( null, '<div class="sppopups-loading"><div class="sppopups-spinner"></div><p>Generating TLDR</p></div>' );
     1227        // Get TLDR defaults for loading text and title
     1228        var tldrDefaults = getDefaultsForType( 'tldr' );
     1229        var loadingText = tldrDefaults.loadingText || 'Generating TLDR';
     1230        var titleText = tldrDefaults.titleText || 'TLDR';
     1231
     1232        // Use common modal setup with TLDR defaults
     1233        setupModalState( null, '<div class="sppopups-loading"><div class="sppopups-spinner"></div><p>' + loadingText + '</p></div>', 'tldr' );
     1234
     1235        // Set title if available
     1236        var titleEl = getTitleElement();
     1237        if (titleEl) {
     1238            titleEl.textContent = titleText;
     1239        }
    11301240
    11311241        // Prepare form data
     
    12121322                closeModal: closeModal,
    12131323                setupModalState: setupModalState,
     1324                applyModalDefaults: applyModalDefaults,
     1325                getDefaultsForType: getDefaultsForType,
    12141326                getTitleElement: getTitleElement,
    12151327                focusWithoutScroll: focusWithoutScroll,
  • synced-pattern-popups/trunk/includes/class-sppopups-admin.php

    r3439916 r3442776  
    199199                }
    200200
    201                 wp_safe_redirect( admin_url( 'themes.php?page=simplest-popup-patterns&tldr_settings_saved=1' ) );
     201                // Get current tab from form submission, default to 'tldr' if not provided.
     202                $current_tab = isset( $_POST['sppopups_current_tab'] ) ? sanitize_text_field( wp_unslash( $_POST['sppopups_current_tab'] ) ) : 'tldr';
     203                // Ensure tab is valid.
     204                $valid_tabs = array( 'patterns', 'tldr', 'defaults', 'how-to-use' );
     205                if ( ! in_array( $current_tab, $valid_tabs, true ) ) {
     206                    $current_tab = 'tldr';
     207                }
     208                wp_safe_redirect( admin_url( 'themes.php?page=simplest-popup-patterns&tldr_settings_saved=1#' . $current_tab ) );
     209                exit;
     210            }
     211        }
     212
     213        // Handle defaults settings save.
     214        if ( isset( $_POST['save_defaults_settings'] ) && isset( $_POST['sppopups_defaults_settings_nonce'] ) ) {
     215            if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['sppopups_defaults_settings_nonce'] ) ), 'sppopups_save_defaults_settings' ) ) {
     216                wp_die( esc_html__( 'Security check failed.', 'synced-pattern-popups' ) );
     217            }
     218
     219            if ( current_user_can( 'manage_options' ) ) {
     220                $settings = new SPPopups_Settings();
     221
     222                // Save pattern defaults.
     223                if ( isset( $_POST['sppopups_defaults_pattern'] ) && is_array( $_POST['sppopups_defaults_pattern'] ) ) {
     224                    // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized via sanitize_pattern_defaults callback
     225                    $pattern_defaults = $settings->sanitize_pattern_defaults( wp_unslash( $_POST['sppopups_defaults_pattern'] ) );
     226                    update_option( 'sppopups_defaults_pattern', $pattern_defaults );
     227                }
     228
     229                // Save TLDR defaults.
     230                if ( isset( $_POST['sppopups_defaults_tldr'] ) && is_array( $_POST['sppopups_defaults_tldr'] ) ) {
     231                    // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized via sanitize_tldr_defaults callback
     232                    $tldr_defaults = $settings->sanitize_tldr_defaults( wp_unslash( $_POST['sppopups_defaults_tldr'] ) );
     233                    update_option( 'sppopups_defaults_tldr', $tldr_defaults );
     234                }
     235
     236                // Save gallery defaults.
     237                if ( isset( $_POST['sppopups_defaults_gallery'] ) && is_array( $_POST['sppopups_defaults_gallery'] ) ) {
     238                    // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized via sanitize_gallery_defaults callback
     239                    $gallery_defaults = $settings->sanitize_gallery_defaults( wp_unslash( $_POST['sppopups_defaults_gallery'] ) );
     240                    update_option( 'sppopups_defaults_gallery', $gallery_defaults );
     241                }
     242
     243                // Clear all transients when defaults are saved (defaults affect modal appearance).
     244                $deleted_count = $this->cache_service->clear_all();
     245
     246                // Get current tab from form submission, default to 'defaults' if not provided.
     247                $current_tab = isset( $_POST['sppopups_current_tab'] ) ? sanitize_text_field( wp_unslash( $_POST['sppopups_current_tab'] ) ) : 'defaults';
     248                // Ensure tab is valid.
     249                $valid_tabs = array( 'patterns', 'tldr', 'defaults', 'how-to-use' );
     250                if ( ! in_array( $current_tab, $valid_tabs, true ) ) {
     251                    $current_tab = 'defaults';
     252                }
     253                wp_safe_redirect( admin_url( 'themes.php?page=simplest-popup-patterns&defaults_settings_saved=1&cache_cleared=1&deleted=' . absint( $deleted_count ) . '#' . $current_tab ) );
    202254                exit;
    203255            }
     
    233285        $cache_cleared        = isset( $_GET['cache_cleared'] ) ? sanitize_text_field( wp_unslash( $_GET['cache_cleared'] ) ) : '';
    234286        $deleted_count        = isset( $_GET['deleted'] ) ? absint( $_GET['deleted'] ) : 0;
    235         $tldr_settings_saved  = isset( $_GET['tldr_settings_saved'] ) ? sanitize_text_field( wp_unslash( $_GET['tldr_settings_saved'] ) ) : '';
    236         $transient_deleted    = isset( $_GET['transient_deleted'] ) ? sanitize_text_field( wp_unslash( $_GET['transient_deleted'] ) ) : '';
    237         $transient_pattern_id = isset( $_GET['pattern_id'] ) ? absint( $_GET['pattern_id'] ) : 0;
     287        $tldr_settings_saved     = isset( $_GET['tldr_settings_saved'] ) ? sanitize_text_field( wp_unslash( $_GET['tldr_settings_saved'] ) ) : '';
     288        $defaults_settings_saved = isset( $_GET['defaults_settings_saved'] ) ? sanitize_text_field( wp_unslash( $_GET['defaults_settings_saved'] ) ) : '';
     289        $transient_deleted       = isset( $_GET['transient_deleted'] ) ? sanitize_text_field( wp_unslash( $_GET['transient_deleted'] ) ) : '';
     290        $transient_pattern_id    = isset( $_GET['pattern_id'] ) ? absint( $_GET['pattern_id'] ) : 0;
    238291        // phpcs:enable WordPress.Security.NonceVerification.Recommended
    239292
     
    267320        if ( '1' === $tldr_settings_saved ) {
    268321            echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'TLDR settings saved successfully.', 'synced-pattern-popups' ) . '</p></div>';
     322        }
     323
     324        if ( '1' === $defaults_settings_saved ) {
     325            echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Defaults settings saved successfully.', 'synced-pattern-popups' ) . '</p></div>';
    269326        }
    270327
     
    290347                <a href="#tldr" class="sppopups-tab-nav-link" role="tab" aria-selected="false" aria-controls="sppopups-tab-tldr">
    291348                    <?php esc_html_e( 'TLDR', 'synced-pattern-popups' ); ?>
     349                </a>
     350                <a href="#defaults" class="sppopups-tab-nav-link" role="tab" aria-selected="false" aria-controls="sppopups-tab-defaults">
     351                    <?php esc_html_e( 'Defaults', 'synced-pattern-popups' ); ?>
    292352                </a>
    293353                <a href="#how-to-use" class="sppopups-tab-nav-link" role="tab" aria-selected="false" aria-controls="sppopups-tab-how-to-use">
     
    405465                                            <div class="sppopups-trigger-code-wrapper">
    406466                                                <span class="sppopups-trigger-code-text"><?php echo esc_html( $trigger_code ); ?></span>
    407                                                 <button 
    408                                                     type="button" 
    409                                                     class="button button-small sppopups-copy-trigger-icon" 
     467                                                <button
     468                                                    type="button"
     469                                                    class="button button-small sppopups-copy-trigger-icon"
    410470                                                    data-copy="<?php echo esc_attr( $trigger_code ); ?>"
    411471                                                    title="<?php esc_attr_e( 'Copy to Clipboard', 'synced-pattern-popups' ); ?>"
     
    426486                                                    <?php endif; ?>
    427487                                                    <?php if ( current_user_can( 'delete_post', $pattern_id ) ) : ?>
    428                                                         <a 
    429                                                             href="<?php echo esc_url( $delete_url ); ?>" 
     488                                                        <a
     489                                                            href="<?php echo esc_url( $delete_url ); ?>"
    430490                                                            class="button button-small delete-pattern"
    431491                                                            onclick="return confirm('<?php echo esc_js( __( 'Are you sure you want to delete this pattern?', 'synced-pattern-popups' ) ); ?>');"
     
    441501                                                );
    442502                                                ?>
    443                                                 <a 
    444                                                     href="<?php echo esc_url( $delete_transient_url ); ?>" 
     503                                                <a
     504                                                    href="<?php echo esc_url( $delete_transient_url ); ?>"
    445505                                                    class="button button-small delete-transient sppopups-action-transient"
    446506                                                    onclick="return confirm('
     
    475535                </div>
    476536
     537                <!-- Defaults Tab -->
     538                <div id="sppopups-tab-defaults" class="sppopups-tab-content" role="tabpanel" aria-labelledby="defaults">
     539                    <?php
     540                    // Render defaults settings section.
     541                    $settings = new SPPopups_Settings();
     542                    $settings->render_defaults_section();
     543                    ?>
     544                </div>
     545
    477546                <!-- How to Use Tab -->
    478547                <div id="sppopups-tab-how-to-use" class="sppopups-tab-content" role="tabpanel" aria-labelledby="how-to-use">
  • synced-pattern-popups/trunk/includes/class-sppopups-plugin.php

    r3439916 r3442776  
    8686            $this->abilities = new SPPopups_Abilities( $this->pattern_service, $this->cache_service, $this->style_collector );
    8787            $this->abilities->init();
     88        }
     89
     90        // Initialize Command Palette integration (WP 6.3+ only)
     91        // This will gracefully skip if Command Palette API is not available.
     92        if ( version_compare( get_bloginfo( 'version' ), '6.3', '>=' ) && is_admin() ) {
     93            $command_palette = new SPPopups_Command_Palette( $this->pattern_service, $this->cache_service );
     94            $command_palette->init();
    8895        }
    8996
     
    386393                'styleUrls'  => $style_urls,
    387394                'scriptUrls' => $script_urls,
     395                'defaults'   => array(
     396                    'pattern' => SPPopups_Settings::get_pattern_defaults(),
     397                    'tldr'    => SPPopups_Settings::get_tldr_defaults(),
     398                    'gallery' => SPPopups_Settings::get_gallery_defaults(),
     399                ),
    388400                'strings'    => array(
    389401                    'loading'  => __( 'Loading content...', 'synced-pattern-popups' ),
  • synced-pattern-popups/trunk/includes/class-sppopups-review-notice.php

    r3439916 r3442776  
    175175                    ?>
    176176                </h3>
    177                
     177
    178178                <p class="sppopups-review-notice-subheading">
    179179                    <?php esc_html_e( 'Leave us a kind review on WordPress.org', 'synced-pattern-popups' ); ?>
    180180                </p>
    181                
     181
    182182                <div class="sppopups-review-notice-stars">
    183183                    ⭐⭐⭐⭐⭐
    184184                </div>
    185                
     185
    186186                <div class="sppopups-review-notice-actions">
    187187                    <a href="<?php echo esc_url( $review_url ); ?>" target="_blank" rel="noopener noreferrer" class="sppopups-review-notice-button">
     
    192192                    </a>
    193193                </div>
    194                
     194
    195195                <p class="sppopups-review-notice-footer">
    196196                    <em><?php esc_html_e( 'Your review and feedback keeps us developing this plugin for more users like you!', 'synced-pattern-popups' ); ?></em>
  • synced-pattern-popups/trunk/includes/class-sppopups-settings.php

    r3439916 r3442776  
    7575            )
    7676        );
     77
     78        // Register defaults settings.
     79        register_setting(
     80            $this->option_group,
     81            'sppopups_defaults_pattern',
     82            array(
     83                'type'              => 'array',
     84                'sanitize_callback' => array( $this, 'sanitize_pattern_defaults' ),
     85                'default'           => $this->get_default_pattern_defaults(),
     86            )
     87        );
     88
     89        register_setting(
     90            $this->option_group,
     91            'sppopups_defaults_tldr',
     92            array(
     93                'type'              => 'array',
     94                'sanitize_callback' => array( $this, 'sanitize_tldr_defaults' ),
     95                'default'           => $this->get_default_tldr_defaults(),
     96            )
     97        );
     98
     99        register_setting(
     100            $this->option_group,
     101            'sppopups_defaults_gallery',
     102            array(
     103                'type'              => 'array',
     104                'sanitize_callback' => array( $this, 'sanitize_gallery_defaults' ),
     105                'default'           => $this->get_default_gallery_defaults(),
     106            )
     107        );
    77108    }
    78109
     
    85116    public function sanitize_boolean( $value ) {
    86117        return (bool) $value;
     118    }
     119
     120    /**
     121     * Get default pattern defaults
     122     *
     123     * @return array Default pattern defaults
     124     */
     125    private function get_default_pattern_defaults() {
     126        return array(
     127            'maxWidth'        => 600,
     128            'borderRadius'    => 6,
     129            'maxHeight'       => 90,
     130            'overlayColor'    => 'rgba(0, 0, 0, 0.1)',
     131            'backdropBlur'    => 8,
     132            'showIconClose'   => true,
     133            'showFooterClose' => true,
     134            'footerCloseText' => 'Close →',
     135        );
     136    }
     137
     138    /**
     139     * Get default TLDR defaults
     140     *
     141     * @return array Default TLDR defaults
     142     */
     143    private function get_default_tldr_defaults() {
     144        return array(
     145            'inheritModalAppearance' => true,
     146            'inheritOverlay'         => true,
     147            'inheritCloseButtons'    => true,
     148            'maxWidth'               => 600,
     149            'borderRadius'           => 6,
     150            'maxHeight'              => 90,
     151            'overlayColor'           => 'rgba(0, 0, 0, 0.1)',
     152            'backdropBlur'           => 8,
     153            'showIconClose'          => true,
     154            'showFooterClose'        => true,
     155            'footerCloseText'        => 'Close →',
     156            'loadingText'            => 'Generating TLDR',
     157            'titleText'              => 'TLDR',
     158        );
     159    }
     160
     161    /**
     162     * Get default gallery defaults
     163     *
     164     * @return array Default gallery defaults
     165     */
     166    private function get_default_gallery_defaults() {
     167        return array(
     168            'inheritModalAppearance' => true,
     169            'inheritOverlay'         => true,
     170            'inheritCloseButtons'    => true,
     171            'maxWidth'               => 600,
     172            'borderRadius'           => 6,
     173            'maxHeight'              => 90,
     174            'overlayColor'           => 'rgba(0, 0, 0, 0.1)',
     175            'backdropBlur'           => 8,
     176            'showIconClose'          => true,
     177            'showFooterClose'        => true,
     178            'footerCloseText'        => 'Close →',
     179            'imageNavigation'        => 'both',
     180            'showCaptions'           => true,
     181            'crossfadeTransition'    => true,
     182            'transitionDuration'     => 500,
     183            'preloadAdjacentImages'  => true,
     184            'showNavOnHover'         => true,
     185        );
     186    }
     187
     188    /**
     189     * Sanitize rgba color value
     190     *
     191     * @param string $value Color value to sanitize.
     192     * @return string Sanitized color value or default.
     193     */
     194    private function sanitize_rgba_color( $value ) {
     195        if ( ! is_string( $value ) ) {
     196            return 'rgba(0, 0, 0, 0.1)';
     197        }
     198
     199        // Validate rgba format: rgba(r, g, b, a) where r,g,b are 0-255 and a is 0-1.
     200        $pattern = '/^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(0|1|0?\.\d+)\s*\)$/';
     201        if ( preg_match( $pattern, $value ) ) {
     202            return $value;
     203        }
     204
     205        return 'rgba(0, 0, 0, 0.1)';
     206    }
     207
     208    /**
     209     * Sanitize number with range validation
     210     *
     211     * @param mixed  $value Value to sanitize.
     212     * @param int    $min   Minimum value.
     213     * @param int    $max   Maximum value.
     214     * @param int    $default Default value if invalid.
     215     * @return int Sanitized number.
     216     */
     217    private function sanitize_number_range( $value, $min, $max, $default ) {
     218        $value = absint( $value );
     219        if ( $value < $min || $value > $max ) {
     220            return $default;
     221        }
     222        return $value;
     223    }
     224
     225    /**
     226     * Sanitize pattern defaults
     227     *
     228     * @param mixed $value Value to sanitize.
     229     * @return array Sanitized pattern defaults.
     230     */
     231    public function sanitize_pattern_defaults( $value ) {
     232        if ( ! is_array( $value ) ) {
     233            return $this->get_default_pattern_defaults();
     234        }
     235
     236        $defaults = $this->get_default_pattern_defaults();
     237        $sanitized = array();
     238
     239        // Sanitize maxWidth (100-5000).
     240        $sanitized['maxWidth'] = isset( $value['maxWidth'] ) ? $this->sanitize_number_range( $value['maxWidth'], 100, 5000, $defaults['maxWidth'] ) : $defaults['maxWidth'];
     241
     242        // Sanitize borderRadius (0-50).
     243        $sanitized['borderRadius'] = isset( $value['borderRadius'] ) ? $this->sanitize_number_range( $value['borderRadius'], 0, 50, $defaults['borderRadius'] ) : $defaults['borderRadius'];
     244
     245        // Sanitize maxHeight (50-100).
     246        $sanitized['maxHeight'] = isset( $value['maxHeight'] ) ? $this->sanitize_number_range( $value['maxHeight'], 50, 100, $defaults['maxHeight'] ) : $defaults['maxHeight'];
     247
     248        // Sanitize overlayColor.
     249        $sanitized['overlayColor'] = isset( $value['overlayColor'] ) ? $this->sanitize_rgba_color( $value['overlayColor'] ) : $defaults['overlayColor'];
     250
     251        // Sanitize backdropBlur (0-20).
     252        $sanitized['backdropBlur'] = isset( $value['backdropBlur'] ) ? $this->sanitize_number_range( $value['backdropBlur'], 0, 20, $defaults['backdropBlur'] ) : $defaults['backdropBlur'];
     253
     254        // Sanitize booleans.
     255        // Note: Unchecked checkboxes don't appear in POST data, so we use array_key_exists to detect if they were present.
     256        // If the key exists, use the value; if not, it means unchecked, so set to false.
     257        $sanitized['showIconClose'] = array_key_exists( 'showIconClose', $value ) ? (bool) $value['showIconClose'] : false;
     258        $sanitized['showFooterClose'] = array_key_exists( 'showFooterClose', $value ) ? (bool) $value['showFooterClose'] : false;
     259
     260        // Sanitize footerCloseText.
     261        $sanitized['footerCloseText'] = isset( $value['footerCloseText'] ) ? sanitize_text_field( wp_unslash( $value['footerCloseText'] ) ) : $defaults['footerCloseText'];
     262
     263        return $sanitized;
     264    }
     265
     266    /**
     267     * Sanitize TLDR defaults
     268     *
     269     * @param mixed $value Value to sanitize.
     270     * @return array Sanitized TLDR defaults.
     271     */
     272    public function sanitize_tldr_defaults( $value ) {
     273        if ( ! is_array( $value ) ) {
     274            return $this->get_default_tldr_defaults();
     275        }
     276
     277        $defaults = $this->get_default_tldr_defaults();
     278        $sanitized = array();
     279
     280        // Sanitize inheritance flags.
     281        $sanitized['inheritModalAppearance'] = isset( $value['inheritModalAppearance'] ) ? (bool) $value['inheritModalAppearance'] : $defaults['inheritModalAppearance'];
     282        $sanitized['inheritOverlay'] = isset( $value['inheritOverlay'] ) ? (bool) $value['inheritOverlay'] : $defaults['inheritOverlay'];
     283        $sanitized['inheritCloseButtons'] = isset( $value['inheritCloseButtons'] ) ? (bool) $value['inheritCloseButtons'] : $defaults['inheritCloseButtons'];
     284
     285        // Only sanitize modal appearance if not inheriting.
     286        if ( ! $sanitized['inheritModalAppearance'] ) {
     287            $sanitized['maxWidth'] = isset( $value['maxWidth'] ) ? $this->sanitize_number_range( $value['maxWidth'], 100, 5000, $defaults['maxWidth'] ) : $defaults['maxWidth'];
     288            $sanitized['borderRadius'] = isset( $value['borderRadius'] ) ? $this->sanitize_number_range( $value['borderRadius'], 0, 50, $defaults['borderRadius'] ) : $defaults['borderRadius'];
     289            $sanitized['maxHeight'] = isset( $value['maxHeight'] ) ? $this->sanitize_number_range( $value['maxHeight'], 50, 100, $defaults['maxHeight'] ) : $defaults['maxHeight'];
     290        } else {
     291            $sanitized['maxWidth'] = $defaults['maxWidth'];
     292            $sanitized['borderRadius'] = $defaults['borderRadius'];
     293            $sanitized['maxHeight'] = $defaults['maxHeight'];
     294        }
     295
     296        // Only sanitize overlay if not inheriting.
     297        if ( ! $sanitized['inheritOverlay'] ) {
     298            $sanitized['overlayColor'] = isset( $value['overlayColor'] ) ? $this->sanitize_rgba_color( $value['overlayColor'] ) : $defaults['overlayColor'];
     299            $sanitized['backdropBlur'] = isset( $value['backdropBlur'] ) ? $this->sanitize_number_range( $value['backdropBlur'], 0, 20, $defaults['backdropBlur'] ) : $defaults['backdropBlur'];
     300        } else {
     301            $sanitized['overlayColor'] = $defaults['overlayColor'];
     302            $sanitized['backdropBlur'] = $defaults['backdropBlur'];
     303        }
     304
     305        // Only sanitize close buttons if not inheriting.
     306        if ( ! $sanitized['inheritCloseButtons'] ) {
     307            // Note: Unchecked checkboxes don't appear in POST data, so we use array_key_exists to detect if they were present.
     308            // If the key exists, use the value; if not, it means unchecked, so set to false.
     309            $sanitized['showIconClose'] = array_key_exists( 'showIconClose', $value ) ? (bool) $value['showIconClose'] : false;
     310            $sanitized['showFooterClose'] = array_key_exists( 'showFooterClose', $value ) ? (bool) $value['showFooterClose'] : false;
     311            $sanitized['footerCloseText'] = isset( $value['footerCloseText'] ) ? sanitize_text_field( wp_unslash( $value['footerCloseText'] ) ) : $defaults['footerCloseText'];
     312        } else {
     313            $sanitized['showIconClose'] = $defaults['showIconClose'];
     314            $sanitized['showFooterClose'] = $defaults['showFooterClose'];
     315            $sanitized['footerCloseText'] = $defaults['footerCloseText'];
     316        }
     317
     318        // TLDR-specific settings.
     319        $sanitized['loadingText'] = isset( $value['loadingText'] ) ? sanitize_text_field( wp_unslash( $value['loadingText'] ) ) : $defaults['loadingText'];
     320        $sanitized['titleText'] = isset( $value['titleText'] ) ? sanitize_text_field( wp_unslash( $value['titleText'] ) ) : $defaults['titleText'];
     321
     322        return $sanitized;
     323    }
     324
     325    /**
     326     * Sanitize gallery defaults
     327     *
     328     * @param mixed $value Value to sanitize.
     329     * @return array Sanitized gallery defaults.
     330     */
     331    public function sanitize_gallery_defaults( $value ) {
     332        if ( ! is_array( $value ) ) {
     333            return $this->get_default_gallery_defaults();
     334        }
     335
     336        $defaults = $this->get_default_gallery_defaults();
     337        $sanitized = array();
     338
     339        // Sanitize inheritance flags.
     340        $sanitized['inheritModalAppearance'] = isset( $value['inheritModalAppearance'] ) ? (bool) $value['inheritModalAppearance'] : $defaults['inheritModalAppearance'];
     341        $sanitized['inheritOverlay'] = isset( $value['inheritOverlay'] ) ? (bool) $value['inheritOverlay'] : $defaults['inheritOverlay'];
     342        $sanitized['inheritCloseButtons'] = isset( $value['inheritCloseButtons'] ) ? (bool) $value['inheritCloseButtons'] : $defaults['inheritCloseButtons'];
     343
     344        // Only sanitize modal appearance if not inheriting.
     345        if ( ! $sanitized['inheritModalAppearance'] ) {
     346            $sanitized['maxWidth'] = isset( $value['maxWidth'] ) ? $this->sanitize_number_range( $value['maxWidth'], 100, 5000, $defaults['maxWidth'] ) : $defaults['maxWidth'];
     347            $sanitized['borderRadius'] = isset( $value['borderRadius'] ) ? $this->sanitize_number_range( $value['borderRadius'], 0, 50, $defaults['borderRadius'] ) : $defaults['borderRadius'];
     348            $sanitized['maxHeight'] = isset( $value['maxHeight'] ) ? $this->sanitize_number_range( $value['maxHeight'], 50, 100, $defaults['maxHeight'] ) : $defaults['maxHeight'];
     349        } else {
     350            $sanitized['maxWidth'] = $defaults['maxWidth'];
     351            $sanitized['borderRadius'] = $defaults['borderRadius'];
     352            $sanitized['maxHeight'] = $defaults['maxHeight'];
     353        }
     354
     355        // Only sanitize overlay if not inheriting.
     356        if ( ! $sanitized['inheritOverlay'] ) {
     357            $sanitized['overlayColor'] = isset( $value['overlayColor'] ) ? $this->sanitize_rgba_color( $value['overlayColor'] ) : $defaults['overlayColor'];
     358            $sanitized['backdropBlur'] = isset( $value['backdropBlur'] ) ? $this->sanitize_number_range( $value['backdropBlur'], 0, 20, $defaults['backdropBlur'] ) : $defaults['backdropBlur'];
     359        } else {
     360            $sanitized['overlayColor'] = $defaults['overlayColor'];
     361            $sanitized['backdropBlur'] = $defaults['backdropBlur'];
     362        }
     363
     364        // Only sanitize close buttons if not inheriting.
     365        if ( ! $sanitized['inheritCloseButtons'] ) {
     366            // Note: Unchecked checkboxes don't appear in POST data, so we use array_key_exists to detect if they were present.
     367            // If the key exists, use the value; if not, it means unchecked, so set to false.
     368            $sanitized['showIconClose'] = array_key_exists( 'showIconClose', $value ) ? (bool) $value['showIconClose'] : false;
     369            $sanitized['showFooterClose'] = array_key_exists( 'showFooterClose', $value ) ? (bool) $value['showFooterClose'] : false;
     370            $sanitized['footerCloseText'] = isset( $value['footerCloseText'] ) ? sanitize_text_field( wp_unslash( $value['footerCloseText'] ) ) : $defaults['footerCloseText'];
     371        } else {
     372            $sanitized['showIconClose'] = $defaults['showIconClose'];
     373            $sanitized['showFooterClose'] = $defaults['showFooterClose'];
     374            $sanitized['footerCloseText'] = $defaults['footerCloseText'];
     375        }
     376
     377        // Gallery-specific settings.
     378        $allowed_navigation = array( 'image', 'footer', 'both' );
     379        $sanitized['imageNavigation'] = isset( $value['imageNavigation'] ) && in_array( $value['imageNavigation'], $allowed_navigation, true ) ? $value['imageNavigation'] : $defaults['imageNavigation'];
     380
     381        // Note: Unchecked checkboxes don't appear in POST data, so we use array_key_exists to detect if they were present.
     382        // If the key exists, use the value; if not, it means unchecked, so set to false.
     383        $sanitized['showCaptions'] = array_key_exists( 'showCaptions', $value ) ? (bool) $value['showCaptions'] : false;
     384        $sanitized['crossfadeTransition'] = array_key_exists( 'crossfadeTransition', $value ) ? (bool) $value['crossfadeTransition'] : false;
     385        $sanitized['transitionDuration'] = isset( $value['transitionDuration'] ) ? $this->sanitize_number_range( $value['transitionDuration'], 0, 2000, $defaults['transitionDuration'] ) : $defaults['transitionDuration'];
     386        $sanitized['preloadAdjacentImages'] = array_key_exists( 'preloadAdjacentImages', $value ) ? (bool) $value['preloadAdjacentImages'] : false;
     387        $sanitized['showNavOnHover'] = array_key_exists( 'showNavOnHover', $value ) ? (bool) $value['showNavOnHover'] : false;
     388
     389        return $sanitized;
    87390    }
    88391
     
    240543                <?php esc_html_e( 'AI TLDR Settings', 'synced-pattern-popups' ); ?>
    241544            </h2>
    242            
     545
    243546            <?php if ( ! $ai_available['plugin_active'] ) : ?>
    244547                <div class="notice notice-warning inline" style="margin: 0 0 20px 0;">
     
    257560            <form method="post" action="">
    258561                <?php wp_nonce_field( 'sppopups_save_tldr_settings', 'sppopups_tldr_settings_nonce' ); ?>
    259                
     562                <input type="hidden" name="sppopups_current_tab" id="sppopups-tldr-current-tab-alt" value="tldr" />
     563
    260564                <table class="form-table" role="presentation">
    261565                    <tbody>
     
    322626                    </tbody>
    323627                </table>
    324                
     628
    325629                <?php submit_button( __( 'Save TLDR Settings', 'synced-pattern-popups' ), 'primary', 'save_tldr_settings', false ); ?>
    326630            </form>
     
    338642        <div class="sppopups-tab-content-inner">
    339643            <h2><?php esc_html_e( 'AI TLDR Settings', 'synced-pattern-popups' ); ?></h2>
    340            
     644
    341645            <?php if ( ! $all_requirements_met ) : ?>
    342646                <p class="description">
     
    351655                <form method="post" action="">
    352656                    <?php wp_nonce_field( 'sppopups_save_tldr_settings', 'sppopups_tldr_settings_nonce' ); ?>
    353                    
     657                    <input type="hidden" name="sppopups_current_tab" id="sppopups-tldr-current-tab" value="tldr" />
     658
    354659                    <table class="form-table" role="presentation">
    355660                        <tbody>
     
    416721                        </tbody>
    417722                    </table>
    418                    
     723
    419724                    <?php submit_button( __( 'Save TLDR Settings', 'synced-pattern-popups' ), 'primary', 'save_tldr_settings', false ); ?>
    420725                </form>
     
    557862        return $hours * HOUR_IN_SECONDS;
    558863    }
     864
     865    /**
     866     * Render defaults section for admin page
     867     */
     868    public function render_defaults_section() {
     869        ?>
     870        <div class="sppopups-tab-content-inner">
     871            <h2><?php esc_html_e( 'Popup Defaults', 'synced-pattern-popups' ); ?></h2>
     872            <p class="description">
     873                <?php esc_html_e( 'Configure default appearance and behavior settings for all popup types. These settings will be used unless overridden by individual popups.', 'synced-pattern-popups' ); ?>
     874            </p>
     875
     876            <form method="post" action="">
     877                <?php wp_nonce_field( 'sppopups_save_defaults_settings', 'sppopups_defaults_settings_nonce' ); ?>
     878                <input type="hidden" name="sppopups_current_tab" id="sppopups-defaults-current-tab" value="defaults" />
     879
     880                <?php $this->render_pattern_defaults_section(); ?>
     881                <?php $this->render_tldr_defaults_section(); ?>
     882                <?php $this->render_gallery_defaults_section(); ?>
     883
     884                <?php submit_button( __( 'Save Defaults', 'synced-pattern-popups' ), 'primary', 'save_defaults_settings', false ); ?>
     885            </form>
     886        </div>
     887        <?php
     888    }
     889
     890    /**
     891     * Render pattern defaults section
     892     */
     893    private function render_pattern_defaults_section() {
     894        $defaults = $this->get_default_pattern_defaults();
     895        $saved = get_option( 'sppopups_defaults_pattern', array() );
     896        $values = wp_parse_args( $saved, $defaults );
     897        ?>
     898        <div class="sppopups-defaults-accordion" style="margin-top: 30px;">
     899            <div class="sppopups-defaults-accordion-header">
     900                <button type="button" class="sppopups-defaults-accordion-trigger" aria-expanded="false" aria-controls="sppopups-defaults-pattern-content">
     901                    <span class="sppopups-defaults-accordion-title"><?php esc_html_e( 'Pattern Popups Defaults', 'synced-pattern-popups' ); ?></span>
     902                    <span class="sppopups-defaults-accordion-icon" aria-hidden="true"></span>
     903                </button>
     904            </div>
     905            <div id="sppopups-defaults-pattern-content" class="sppopups-defaults-accordion-content" style="display: none; padding: 24px; background: #f6f7f7; border: 1px solid #dcdcde; border-top: none; border-radius: 0 0 4px 4px;">
     906
     907            <table class="form-table" role="presentation">
     908                <tbody>
     909                    <tr>
     910                        <th scope="row">
     911                            <label for="pattern_max_width">
     912                                <?php esc_html_e( 'Default Width', 'synced-pattern-popups' ); ?>
     913                            </label>
     914                        </th>
     915                        <td>
     916                            <input type="number" name="sppopups_defaults_pattern[maxWidth]" id="pattern_max_width" value="<?php echo esc_attr( $values['maxWidth'] ); ?>" min="100" max="5000" step="1" style="width: 100px;" />
     917                            <span style="margin-left: 8px;"><?php esc_html_e( 'px', 'synced-pattern-popups' ); ?></span>
     918                            <p class="description">
     919                                <?php esc_html_e( 'Default modal width in pixels (100-5000).', 'synced-pattern-popups' ); ?>
     920                            </p>
     921                        </td>
     922                    </tr>
     923                    <tr>
     924                        <th scope="row">
     925                            <label for="pattern_border_radius">
     926                                <?php esc_html_e( 'Border Radius', 'synced-pattern-popups' ); ?>
     927                            </label>
     928                        </th>
     929                        <td>
     930                            <input type="number" name="sppopups_defaults_pattern[borderRadius]" id="pattern_border_radius" value="<?php echo esc_attr( $values['borderRadius'] ); ?>" min="0" max="50" step="1" style="width: 100px;" />
     931                            <span style="margin-left: 8px;"><?php esc_html_e( 'px', 'synced-pattern-popups' ); ?></span>
     932                            <p class="description">
     933                                <?php esc_html_e( 'Modal border radius in pixels (0-50).', 'synced-pattern-popups' ); ?>
     934                            </p>
     935                        </td>
     936                    </tr>
     937                    <tr>
     938                        <th scope="row">
     939                            <label for="pattern_max_height">
     940                                <?php esc_html_e( 'Max Height', 'synced-pattern-popups' ); ?>
     941                            </label>
     942                        </th>
     943                        <td>
     944                            <input type="number" name="sppopups_defaults_pattern[maxHeight]" id="pattern_max_height" value="<?php echo esc_attr( $values['maxHeight'] ); ?>" min="50" max="100" step="1" style="width: 100px;" />
     945                            <span style="margin-left: 8px;"><?php esc_html_e( '%', 'synced-pattern-popups' ); ?></span>
     946                            <p class="description">
     947                                <?php esc_html_e( 'Maximum modal height as percentage of viewport (50-100).', 'synced-pattern-popups' ); ?>
     948                            </p>
     949                        </td>
     950                    </tr>
     951                    <tr>
     952                        <th scope="row">
     953                            <label for="pattern_overlay_color">
     954                                <?php esc_html_e( 'Overlay Color', 'synced-pattern-popups' ); ?>
     955                            </label>
     956                        </th>
     957                        <td>
     958                            <input type="text" name="sppopups_defaults_pattern[overlayColor]" id="pattern_overlay_color" value="<?php echo esc_attr( $values['overlayColor'] ); ?>" class="regular-text" placeholder="rgba(0, 0, 0, 0.1)" />
     959                            <p class="description">
     960                                <?php esc_html_e( 'Overlay background color in rgba format (e.g., rgba(0, 0, 0, 0.1)).', 'synced-pattern-popups' ); ?>
     961                            </p>
     962                        </td>
     963                    </tr>
     964                    <tr>
     965                        <th scope="row">
     966                            <label for="pattern_backdrop_blur">
     967                                <?php esc_html_e( 'Backdrop Blur', 'synced-pattern-popups' ); ?>
     968                            </label>
     969                        </th>
     970                        <td>
     971                            <input type="number" name="sppopups_defaults_pattern[backdropBlur]" id="pattern_backdrop_blur" value="<?php echo esc_attr( $values['backdropBlur'] ); ?>" min="0" max="20" step="1" style="width: 100px;" />
     972                            <span style="margin-left: 8px;"><?php esc_html_e( 'px', 'synced-pattern-popups' ); ?></span>
     973                            <p class="description">
     974                                <?php esc_html_e( 'Backdrop blur amount in pixels (0-20).', 'synced-pattern-popups' ); ?>
     975                            </p>
     976                        </td>
     977                    </tr>
     978                    <tr>
     979                        <th scope="row">
     980                            <?php esc_html_e( 'Close Buttons', 'synced-pattern-popups' ); ?>
     981                        </th>
     982                        <td>
     983                            <fieldset>
     984                                <label>
     985                                    <input type="checkbox" name="sppopups_defaults_pattern[showIconClose]" value="1" <?php checked( $values['showIconClose'], true ); ?> />
     986                                    <?php esc_html_e( 'Show icon close button', 'synced-pattern-popups' ); ?>
     987                                </label>
     988                                <br />
     989                                <label>
     990                                    <input type="checkbox" name="sppopups_defaults_pattern[showFooterClose]" value="1" <?php checked( $values['showFooterClose'], true ); ?> />
     991                                    <?php esc_html_e( 'Show footer close button', 'synced-pattern-popups' ); ?>
     992                                </label>
     993                            </fieldset>
     994                        </td>
     995                    </tr>
     996                    <tr>
     997                        <th scope="row">
     998                            <label for="pattern_footer_close_text">
     999                                <?php esc_html_e( 'Footer Button Text', 'synced-pattern-popups' ); ?>
     1000                            </label>
     1001                        </th>
     1002                        <td>
     1003                            <input type="text" name="sppopups_defaults_pattern[footerCloseText]" id="pattern_footer_close_text" value="<?php echo esc_attr( $values['footerCloseText'] ); ?>" class="regular-text" />
     1004                            <p class="description">
     1005                                <?php esc_html_e( 'Text displayed on the footer close button.', 'synced-pattern-popups' ); ?>
     1006                            </p>
     1007                        </td>
     1008                    </tr>
     1009                </tbody>
     1010            </table>
     1011            </div>
     1012        </div>
     1013        <?php
     1014    }
     1015
     1016    /**
     1017     * Render TLDR defaults section
     1018     */
     1019    private function render_tldr_defaults_section() {
     1020        $defaults = $this->get_default_tldr_defaults();
     1021        $saved = get_option( 'sppopups_defaults_tldr', array() );
     1022        $values = wp_parse_args( $saved, $defaults );
     1023        $pattern_defaults = self::get_pattern_defaults();
     1024        ?>
     1025        <div class="sppopups-defaults-accordion" style="margin-top: 30px;">
     1026            <div class="sppopups-defaults-accordion-header">
     1027                <button type="button" class="sppopups-defaults-accordion-trigger" aria-expanded="false" aria-controls="sppopups-defaults-tldr-content">
     1028                    <span class="sppopups-defaults-accordion-title"><?php esc_html_e( 'TLDR Popups Defaults', 'synced-pattern-popups' ); ?></span>
     1029                    <span class="sppopups-defaults-accordion-icon" aria-hidden="true"></span>
     1030                </button>
     1031            </div>
     1032            <div id="sppopups-defaults-tldr-content" class="sppopups-defaults-accordion-content" style="display: none; padding: 24px; background: #f6f7f7; border: 1px solid #dcdcde; border-top: none; border-radius: 0 0 4px 4px;">
     1033
     1034            <table class="form-table" role="presentation">
     1035                <tbody>
     1036                    <tr>
     1037                        <th scope="row">
     1038                            <?php esc_html_e( 'Modal Appearance', 'synced-pattern-popups' ); ?>
     1039                        </th>
     1040                        <td>
     1041                            <fieldset>
     1042                                <label>
     1043                                    <input type="radio" name="sppopups_defaults_tldr[inheritModalAppearance]" value="1" <?php checked( $values['inheritModalAppearance'], true ); ?> class="tldr-inherit-modal-appearance" />
     1044                                    <?php esc_html_e( 'Inherit from Pattern Modal', 'synced-pattern-popups' ); ?>
     1045                                </label>
     1046                                <br />
     1047                                <label>
     1048                                    <input type="radio" name="sppopups_defaults_tldr[inheritModalAppearance]" value="0" <?php checked( $values['inheritModalAppearance'], false ); ?> class="tldr-inherit-modal-appearance" />
     1049                                    <?php esc_html_e( 'Custom', 'synced-pattern-popups' ); ?>
     1050                                </label>
     1051                            </fieldset>
     1052                            <div id="tldr-modal-appearance-custom" style="margin-top: 12px; <?php echo $values['inheritModalAppearance'] ? 'display: none;' : ''; ?>">
     1053                                <table class="form-table" role="presentation" style="margin-top: 0;">
     1054                                    <tbody>
     1055                                        <tr>
     1056                                            <th scope="row">
     1057                                                <label for="tldr_max_width">
     1058                                                    <?php esc_html_e( 'Default Width', 'synced-pattern-popups' ); ?>
     1059                                                </label>
     1060                                            </th>
     1061                                            <td>
     1062                                                <input type="number" name="sppopups_defaults_tldr[maxWidth]" id="tldr_max_width" value="<?php echo esc_attr( $values['maxWidth'] ); ?>" min="100" max="5000" step="1" style="width: 100px;" />
     1063                                                <span style="margin-left: 8px;"><?php esc_html_e( 'px', 'synced-pattern-popups' ); ?></span>
     1064                                            </td>
     1065                                        </tr>
     1066                                        <tr>
     1067                                            <th scope="row">
     1068                                                <label for="tldr_border_radius">
     1069                                                    <?php esc_html_e( 'Border Radius', 'synced-pattern-popups' ); ?>
     1070                                                </label>
     1071                                            </th>
     1072                                            <td>
     1073                                                <input type="number" name="sppopups_defaults_tldr[borderRadius]" id="tldr_border_radius" value="<?php echo esc_attr( $values['borderRadius'] ); ?>" min="0" max="50" step="1" style="width: 100px;" />
     1074                                                <span style="margin-left: 8px;"><?php esc_html_e( 'px', 'synced-pattern-popups' ); ?></span>
     1075                                            </td>
     1076                                        </tr>
     1077                                        <tr>
     1078                                            <th scope="row">
     1079                                                <label for="tldr_max_height">
     1080                                                    <?php esc_html_e( 'Max Height', 'synced-pattern-popups' ); ?>
     1081                                                </label>
     1082                                            </th>
     1083                                            <td>
     1084                                                <input type="number" name="sppopups_defaults_tldr[maxHeight]" id="tldr_max_height" value="<?php echo esc_attr( $values['maxHeight'] ); ?>" min="50" max="100" step="1" style="width: 100px;" />
     1085                                                <span style="margin-left: 8px;"><?php esc_html_e( '%', 'synced-pattern-popups' ); ?></span>
     1086                                            </td>
     1087                                        </tr>
     1088                                    </tbody>
     1089                                </table>
     1090                            </div>
     1091                        </td>
     1092                    </tr>
     1093                    <tr>
     1094                        <th scope="row">
     1095                            <?php esc_html_e( 'Overlay', 'synced-pattern-popups' ); ?>
     1096                        </th>
     1097                        <td>
     1098                            <fieldset>
     1099                                <label>
     1100                                    <input type="radio" name="sppopups_defaults_tldr[inheritOverlay]" value="1" <?php checked( $values['inheritOverlay'], true ); ?> class="tldr-inherit-overlay" />
     1101                                    <?php esc_html_e( 'Inherit from Pattern Modal', 'synced-pattern-popups' ); ?>
     1102                                </label>
     1103                                <br />
     1104                                <label>
     1105                                    <input type="radio" name="sppopups_defaults_tldr[inheritOverlay]" value="0" <?php checked( $values['inheritOverlay'], false ); ?> class="tldr-inherit-overlay" />
     1106                                    <?php esc_html_e( 'Custom', 'synced-pattern-popups' ); ?>
     1107                                </label>
     1108                            </fieldset>
     1109                            <div id="tldr-overlay-custom" style="margin-top: 12px; <?php echo $values['inheritOverlay'] ? 'display: none;' : ''; ?>">
     1110                                <table class="form-table" role="presentation" style="margin-top: 0;">
     1111                                    <tbody>
     1112                                        <tr>
     1113                                            <th scope="row">
     1114                                                <label for="tldr_overlay_color">
     1115                                                    <?php esc_html_e( 'Overlay Color', 'synced-pattern-popups' ); ?>
     1116                                                </label>
     1117                                            </th>
     1118                                            <td>
     1119                                                <input type="text" name="sppopups_defaults_tldr[overlayColor]" id="tldr_overlay_color" value="<?php echo esc_attr( $values['overlayColor'] ); ?>" class="regular-text" placeholder="rgba(0, 0, 0, 0.1)" />
     1120                                            </td>
     1121                                        </tr>
     1122                                        <tr>
     1123                                            <th scope="row">
     1124                                                <label for="tldr_backdrop_blur">
     1125                                                    <?php esc_html_e( 'Backdrop Blur', 'synced-pattern-popups' ); ?>
     1126                                                </label>
     1127                                            </th>
     1128                                            <td>
     1129                                                <input type="number" name="sppopups_defaults_tldr[backdropBlur]" id="tldr_backdrop_blur" value="<?php echo esc_attr( $values['backdropBlur'] ); ?>" min="0" max="20" step="1" style="width: 100px;" />
     1130                                                <span style="margin-left: 8px;"><?php esc_html_e( 'px', 'synced-pattern-popups' ); ?></span>
     1131                                            </td>
     1132                                        </tr>
     1133                                    </tbody>
     1134                                </table>
     1135                            </div>
     1136                        </td>
     1137                    </tr>
     1138                    <tr>
     1139                        <th scope="row">
     1140                            <?php esc_html_e( 'Close Buttons', 'synced-pattern-popups' ); ?>
     1141                        </th>
     1142                        <td>
     1143                            <fieldset>
     1144                                <label>
     1145                                    <input type="radio" name="sppopups_defaults_tldr[inheritCloseButtons]" value="1" <?php checked( $values['inheritCloseButtons'], true ); ?> class="tldr-inherit-close-buttons" />
     1146                                    <?php esc_html_e( 'Inherit from Pattern Modal', 'synced-pattern-popups' ); ?>
     1147                                </label>
     1148                                <br />
     1149                                <label>
     1150                                    <input type="radio" name="sppopups_defaults_tldr[inheritCloseButtons]" value="0" <?php checked( $values['inheritCloseButtons'], false ); ?> class="tldr-inherit-close-buttons" />
     1151                                    <?php esc_html_e( 'Custom', 'synced-pattern-popups' ); ?>
     1152                                </label>
     1153                            </fieldset>
     1154                            <div id="tldr-close-buttons-custom" style="margin-top: 12px; <?php echo $values['inheritCloseButtons'] ? 'display: none;' : ''; ?>">
     1155                                <table class="form-table" role="presentation" style="margin-top: 0;">
     1156                                    <tbody>
     1157                                        <tr>
     1158                                            <th scope="row">
     1159                                                <?php esc_html_e( 'Options', 'synced-pattern-popups' ); ?>
     1160                                            </th>
     1161                                            <td>
     1162                                                <fieldset>
     1163                                                    <label>
     1164                                                        <input type="checkbox" name="sppopups_defaults_tldr[showIconClose]" value="1" <?php checked( $values['showIconClose'], true ); ?> />
     1165                                                        <?php esc_html_e( 'Show icon close button', 'synced-pattern-popups' ); ?>
     1166                                                    </label>
     1167                                                    <br />
     1168                                                    <label>
     1169                                                        <input type="checkbox" name="sppopups_defaults_tldr[showFooterClose]" value="1" <?php checked( $values['showFooterClose'], true ); ?> />
     1170                                                        <?php esc_html_e( 'Show footer close button', 'synced-pattern-popups' ); ?>
     1171                                                    </label>
     1172                                                </fieldset>
     1173                                            </td>
     1174                                        </tr>
     1175                                        <tr>
     1176                                            <th scope="row">
     1177                                                <label for="tldr_footer_close_text">
     1178                                                    <?php esc_html_e( 'Footer Button Text', 'synced-pattern-popups' ); ?>
     1179                                                </label>
     1180                                            </th>
     1181                                            <td>
     1182                                                <input type="text" name="sppopups_defaults_tldr[footerCloseText]" id="tldr_footer_close_text" value="<?php echo esc_attr( $values['footerCloseText'] ); ?>" class="regular-text" />
     1183                                            </td>
     1184                                        </tr>
     1185                                    </tbody>
     1186                                </table>
     1187                            </div>
     1188                        </td>
     1189                    </tr>
     1190                    <tr>
     1191                        <th scope="row">
     1192                            <label for="tldr_loading_text">
     1193                                <?php esc_html_e( 'Loading Text', 'synced-pattern-popups' ); ?>
     1194                            </label>
     1195                        </th>
     1196                        <td>
     1197                            <input type="text" name="sppopups_defaults_tldr[loadingText]" id="tldr_loading_text" value="<?php echo esc_attr( $values['loadingText'] ); ?>" class="regular-text" />
     1198                            <p class="description">
     1199                                <?php esc_html_e( 'Text displayed while generating TLDR summary.', 'synced-pattern-popups' ); ?>
     1200                            </p>
     1201                        </td>
     1202                    </tr>
     1203                    <tr>
     1204                        <th scope="row">
     1205                            <label for="tldr_title_text">
     1206                                <?php esc_html_e( 'Title Text', 'synced-pattern-popups' ); ?>
     1207                            </label>
     1208                        </th>
     1209                        <td>
     1210                            <input type="text" name="sppopups_defaults_tldr[titleText]" id="tldr_title_text" value="<?php echo esc_attr( $values['titleText'] ); ?>" class="regular-text" />
     1211                            <p class="description">
     1212                                <?php esc_html_e( 'Title displayed in TLDR modal header.', 'synced-pattern-popups' ); ?>
     1213                            </p>
     1214                        </td>
     1215                    </tr>
     1216                </tbody>
     1217            </table>
     1218            </div>
     1219        </div>
     1220        <?php
     1221    }
     1222
     1223    /**
     1224     * Render gallery defaults section
     1225     */
     1226    private function render_gallery_defaults_section() {
     1227        $defaults = $this->get_default_gallery_defaults();
     1228        $saved = get_option( 'sppopups_defaults_gallery', array() );
     1229        $values = wp_parse_args( $saved, $defaults );
     1230        $pattern_defaults = self::get_pattern_defaults();
     1231        ?>
     1232        <div class="sppopups-defaults-accordion" style="margin-top: 30px;">
     1233            <div class="sppopups-defaults-accordion-header">
     1234                <button type="button" class="sppopups-defaults-accordion-trigger" aria-expanded="false" aria-controls="sppopups-defaults-gallery-content">
     1235                    <span class="sppopups-defaults-accordion-title"><?php esc_html_e( 'Gallery Popups Defaults', 'synced-pattern-popups' ); ?></span>
     1236                    <span class="sppopups-defaults-accordion-icon" aria-hidden="true"></span>
     1237                </button>
     1238            </div>
     1239            <div id="sppopups-defaults-gallery-content" class="sppopups-defaults-accordion-content" style="display: none; padding: 24px; background: #f6f7f7; border: 1px solid #dcdcde; border-top: none; border-radius: 0 0 4px 4px;">
     1240
     1241            <table class="form-table" role="presentation">
     1242                <tbody>
     1243                    <tr>
     1244                        <th scope="row">
     1245                            <?php esc_html_e( 'Modal Appearance', 'synced-pattern-popups' ); ?>
     1246                        </th>
     1247                        <td>
     1248                            <fieldset>
     1249                                <label>
     1250                                    <input type="radio" name="sppopups_defaults_gallery[inheritModalAppearance]" value="1" <?php checked( $values['inheritModalAppearance'], true ); ?> class="gallery-inherit-modal-appearance" />
     1251                                    <?php esc_html_e( 'Inherit from Pattern Modal', 'synced-pattern-popups' ); ?>
     1252                                </label>
     1253                                <br />
     1254                                <label>
     1255                                    <input type="radio" name="sppopups_defaults_gallery[inheritModalAppearance]" value="0" <?php checked( $values['inheritModalAppearance'], false ); ?> class="gallery-inherit-modal-appearance" />
     1256                                    <?php esc_html_e( 'Custom', 'synced-pattern-popups' ); ?>
     1257                                </label>
     1258                            </fieldset>
     1259                            <div id="gallery-modal-appearance-custom" style="margin-top: 12px; <?php echo $values['inheritModalAppearance'] ? 'display: none;' : ''; ?>">
     1260                                <table class="form-table" role="presentation" style="margin-top: 0;">
     1261                                    <tbody>
     1262                                        <tr>
     1263                                            <th scope="row">
     1264                                                <label for="gallery_max_width">
     1265                                                    <?php esc_html_e( 'Default Width', 'synced-pattern-popups' ); ?>
     1266                                                </label>
     1267                                            </th>
     1268                                            <td>
     1269                                                <input type="number" name="sppopups_defaults_gallery[maxWidth]" id="gallery_max_width" value="<?php echo esc_attr( $values['maxWidth'] ); ?>" min="100" max="5000" step="1" style="width: 100px;" />
     1270                                                <span style="margin-left: 8px;"><?php esc_html_e( 'px', 'synced-pattern-popups' ); ?></span>
     1271                                            </td>
     1272                                        </tr>
     1273                                        <tr>
     1274                                            <th scope="row">
     1275                                                <label for="gallery_border_radius">
     1276                                                    <?php esc_html_e( 'Border Radius', 'synced-pattern-popups' ); ?>
     1277                                                </label>
     1278                                            </th>
     1279                                            <td>
     1280                                                <input type="number" name="sppopups_defaults_gallery[borderRadius]" id="gallery_border_radius" value="<?php echo esc_attr( $values['borderRadius'] ); ?>" min="0" max="50" step="1" style="width: 100px;" />
     1281                                                <span style="margin-left: 8px;"><?php esc_html_e( 'px', 'synced-pattern-popups' ); ?></span>
     1282                                            </td>
     1283                                        </tr>
     1284                                        <tr>
     1285                                            <th scope="row">
     1286                                                <label for="gallery_max_height">
     1287                                                    <?php esc_html_e( 'Max Height', 'synced-pattern-popups' ); ?>
     1288                                                </label>
     1289                                            </th>
     1290                                            <td>
     1291                                                <input type="number" name="sppopups_defaults_gallery[maxHeight]" id="gallery_max_height" value="<?php echo esc_attr( $values['maxHeight'] ); ?>" min="50" max="100" step="1" style="width: 100px;" />
     1292                                                <span style="margin-left: 8px;"><?php esc_html_e( '%', 'synced-pattern-popups' ); ?></span>
     1293                                            </td>
     1294                                        </tr>
     1295                                    </tbody>
     1296                                </table>
     1297                            </div>
     1298                        </td>
     1299                    </tr>
     1300                    <tr>
     1301                        <th scope="row">
     1302                            <?php esc_html_e( 'Overlay', 'synced-pattern-popups' ); ?>
     1303                        </th>
     1304                        <td>
     1305                            <fieldset>
     1306                                <label>
     1307                                    <input type="radio" name="sppopups_defaults_gallery[inheritOverlay]" value="1" <?php checked( $values['inheritOverlay'], true ); ?> class="gallery-inherit-overlay" />
     1308                                    <?php esc_html_e( 'Inherit from Pattern Modal', 'synced-pattern-popups' ); ?>
     1309                                </label>
     1310                                <br />
     1311                                <label>
     1312                                    <input type="radio" name="sppopups_defaults_gallery[inheritOverlay]" value="0" <?php checked( $values['inheritOverlay'], false ); ?> class="gallery-inherit-overlay" />
     1313                                    <?php esc_html_e( 'Custom', 'synced-pattern-popups' ); ?>
     1314                                </label>
     1315                            </fieldset>
     1316                            <div id="gallery-overlay-custom" style="margin-top: 12px; <?php echo $values['inheritOverlay'] ? 'display: none;' : ''; ?>">
     1317                                <table class="form-table" role="presentation" style="margin-top: 0;">
     1318                                    <tbody>
     1319                                        <tr>
     1320                                            <th scope="row">
     1321                                                <label for="gallery_overlay_color">
     1322                                                    <?php esc_html_e( 'Overlay Color', 'synced-pattern-popups' ); ?>
     1323                                                </label>
     1324                                            </th>
     1325                                            <td>
     1326                                                <input type="text" name="sppopups_defaults_gallery[overlayColor]" id="gallery_overlay_color" value="<?php echo esc_attr( $values['overlayColor'] ); ?>" class="regular-text" placeholder="rgba(0, 0, 0, 0.1)" />
     1327                                            </td>
     1328                                        </tr>
     1329                                        <tr>
     1330                                            <th scope="row">
     1331                                                <label for="gallery_backdrop_blur">
     1332                                                    <?php esc_html_e( 'Backdrop Blur', 'synced-pattern-popups' ); ?>
     1333                                                </label>
     1334                                            </th>
     1335                                            <td>
     1336                                                <input type="number" name="sppopups_defaults_gallery[backdropBlur]" id="gallery_backdrop_blur" value="<?php echo esc_attr( $values['backdropBlur'] ); ?>" min="0" max="20" step="1" style="width: 100px;" />
     1337                                                <span style="margin-left: 8px;"><?php esc_html_e( 'px', 'synced-pattern-popups' ); ?></span>
     1338                                            </td>
     1339                                        </tr>
     1340                                    </tbody>
     1341                                </table>
     1342                            </div>
     1343                        </td>
     1344                    </tr>
     1345                    <tr>
     1346                        <th scope="row">
     1347                            <?php esc_html_e( 'Close Buttons', 'synced-pattern-popups' ); ?>
     1348                        </th>
     1349                        <td>
     1350                            <fieldset>
     1351                                <label>
     1352                                    <input type="radio" name="sppopups_defaults_gallery[inheritCloseButtons]" value="1" <?php checked( $values['inheritCloseButtons'], true ); ?> class="gallery-inherit-close-buttons" />
     1353                                    <?php esc_html_e( 'Inherit from Pattern Modal', 'synced-pattern-popups' ); ?>
     1354                                </label>
     1355                                <br />
     1356                                <label>
     1357                                    <input type="radio" name="sppopups_defaults_gallery[inheritCloseButtons]" value="0" <?php checked( $values['inheritCloseButtons'], false ); ?> class="gallery-inherit-close-buttons" />
     1358                                    <?php esc_html_e( 'Custom', 'synced-pattern-popups' ); ?>
     1359                                </label>
     1360                            </fieldset>
     1361                            <div id="gallery-close-buttons-custom" style="margin-top: 12px; <?php echo $values['inheritCloseButtons'] ? 'display: none;' : ''; ?>">
     1362                                <table class="form-table" role="presentation" style="margin-top: 0;">
     1363                                    <tbody>
     1364                                        <tr>
     1365                                            <th scope="row">
     1366                                                <?php esc_html_e( 'Options', 'synced-pattern-popups' ); ?>
     1367                                            </th>
     1368                                            <td>
     1369                                                <fieldset>
     1370                                                    <label>
     1371                                                        <input type="checkbox" name="sppopups_defaults_gallery[showIconClose]" value="1" <?php checked( $values['showIconClose'], true ); ?> />
     1372                                                        <?php esc_html_e( 'Show icon close button', 'synced-pattern-popups' ); ?>
     1373                                                    </label>
     1374                                                    <br />
     1375                                                    <label>
     1376                                                        <input type="checkbox" name="sppopups_defaults_gallery[showFooterClose]" value="1" <?php checked( $values['showFooterClose'], true ); ?> />
     1377                                                        <?php esc_html_e( 'Show footer close button', 'synced-pattern-popups' ); ?>
     1378                                                    </label>
     1379                                                </fieldset>
     1380                                            </td>
     1381                                        </tr>
     1382                                        <tr>
     1383                                            <th scope="row">
     1384                                                <label for="gallery_footer_close_text">
     1385                                                    <?php esc_html_e( 'Footer Button Text', 'synced-pattern-popups' ); ?>
     1386                                                </label>
     1387                                            </th>
     1388                                            <td>
     1389                                                <input type="text" name="sppopups_defaults_gallery[footerCloseText]" id="gallery_footer_close_text" value="<?php echo esc_attr( $values['footerCloseText'] ); ?>" class="regular-text" />
     1390                                            </td>
     1391                                        </tr>
     1392                                    </tbody>
     1393                                </table>
     1394                            </div>
     1395                        </td>
     1396                    </tr>
     1397                    <tr>
     1398                        <th scope="row">
     1399                            <label for="gallery_image_navigation">
     1400                                <?php esc_html_e( 'Image Navigation', 'synced-pattern-popups' ); ?>
     1401                            </label>
     1402                        </th>
     1403                        <td>
     1404                            <select name="sppopups_defaults_gallery[imageNavigation]" id="gallery_image_navigation">
     1405                                <option value="image" <?php selected( $values['imageNavigation'], 'image' ); ?>><?php esc_html_e( 'Image', 'synced-pattern-popups' ); ?></option>
     1406                                <option value="footer" <?php selected( $values['imageNavigation'], 'footer' ); ?>><?php esc_html_e( 'Footer', 'synced-pattern-popups' ); ?></option>
     1407                                <option value="both" <?php selected( $values['imageNavigation'], 'both' ); ?>><?php esc_html_e( 'Both', 'synced-pattern-popups' ); ?></option>
     1408                            </select>
     1409                            <p class="description">
     1410                                <?php esc_html_e( 'Where to display navigation controls for gallery images.', 'synced-pattern-popups' ); ?>
     1411                            </p>
     1412                        </td>
     1413                    </tr>
     1414                    <tr>
     1415                        <th scope="row">
     1416                            <?php esc_html_e( 'Gallery Options', 'synced-pattern-popups' ); ?>
     1417                        </th>
     1418                        <td>
     1419                            <fieldset>
     1420                                <label>
     1421                                    <input type="checkbox" name="sppopups_defaults_gallery[showCaptions]" value="1" <?php checked( $values['showCaptions'], true ); ?> />
     1422                                    <?php esc_html_e( 'Show captions', 'synced-pattern-popups' ); ?>
     1423                                </label>
     1424                                <br />
     1425                                <label>
     1426                                    <input type="checkbox" name="sppopups_defaults_gallery[crossfadeTransition]" value="1" <?php checked( $values['crossfadeTransition'], true ); ?> />
     1427                                    <?php esc_html_e( 'Crossfade transition', 'synced-pattern-popups' ); ?>
     1428                                </label>
     1429                                <br />
     1430                                <label>
     1431                                    <input type="checkbox" name="sppopups_defaults_gallery[preloadAdjacentImages]" value="1" <?php checked( $values['preloadAdjacentImages'], true ); ?> />
     1432                                    <?php esc_html_e( 'Preload adjacent images', 'synced-pattern-popups' ); ?>
     1433                                </label>
     1434                                <br />
     1435                                <label>
     1436                                    <input type="checkbox" name="sppopups_defaults_gallery[showNavOnHover]" value="1" <?php checked( $values['showNavOnHover'], true ); ?> />
     1437                                    <?php esc_html_e( 'Show navigation on hover/touch', 'synced-pattern-popups' ); ?>
     1438                                </label>
     1439                            </fieldset>
     1440                        </td>
     1441                    </tr>
     1442                    <tr>
     1443                        <th scope="row">
     1444                            <label for="gallery_transition_duration">
     1445                                <?php esc_html_e( 'Transition Duration', 'synced-pattern-popups' ); ?>
     1446                            </label>
     1447                        </th>
     1448                        <td>
     1449                            <input type="number" name="sppopups_defaults_gallery[transitionDuration]" id="gallery_transition_duration" value="<?php echo esc_attr( $values['transitionDuration'] ); ?>" min="0" max="2000" step="1" style="width: 100px;" />
     1450                            <span style="margin-left: 8px;"><?php esc_html_e( 'ms', 'synced-pattern-popups' ); ?></span>
     1451                            <p class="description">
     1452                                <?php esc_html_e( 'Transition duration in milliseconds (0-2000).', 'synced-pattern-popups' ); ?>
     1453                            </p>
     1454                        </td>
     1455                    </tr>
     1456                </tbody>
     1457            </table>
     1458            </div>
     1459        </div>
     1460        <?php
     1461    }
     1462
     1463    /**
     1464     * Get pattern defaults with fallbacks
     1465     *
     1466     * @return array Pattern defaults
     1467     */
     1468    public static function get_pattern_defaults() {
     1469        $instance = new self();
     1470        $defaults = $instance->get_default_pattern_defaults();
     1471        $saved = get_option( 'sppopups_defaults_pattern', array() );
     1472
     1473        if ( ! is_array( $saved ) || empty( $saved ) ) {
     1474            return $defaults;
     1475        }
     1476
     1477        return wp_parse_args( $saved, $defaults );
     1478    }
     1479
     1480    /**
     1481     * Get TLDR defaults with inheritance logic applied
     1482     *
     1483     * @return array TLDR defaults with inheritance resolved
     1484     */
     1485    public static function get_tldr_defaults() {
     1486        $instance = new self();
     1487        $pattern_defaults = self::get_pattern_defaults();
     1488        $tldr_defaults = $instance->get_default_tldr_defaults();
     1489        $saved = get_option( 'sppopups_defaults_tldr', array() );
     1490
     1491        if ( ! is_array( $saved ) || empty( $saved ) ) {
     1492            $saved = $tldr_defaults;
     1493        }
     1494
     1495        $result = wp_parse_args( $saved, $tldr_defaults );
     1496
     1497        // Apply inheritance from pattern defaults.
     1498        if ( ! empty( $result['inheritModalAppearance'] ) ) {
     1499            $result['maxWidth'] = $pattern_defaults['maxWidth'];
     1500            $result['borderRadius'] = $pattern_defaults['borderRadius'];
     1501            $result['maxHeight'] = $pattern_defaults['maxHeight'];
     1502        }
     1503
     1504        if ( ! empty( $result['inheritOverlay'] ) ) {
     1505            $result['overlayColor'] = $pattern_defaults['overlayColor'];
     1506            $result['backdropBlur'] = $pattern_defaults['backdropBlur'];
     1507        }
     1508
     1509        if ( ! empty( $result['inheritCloseButtons'] ) ) {
     1510            $result['showIconClose'] = $pattern_defaults['showIconClose'];
     1511            $result['showFooterClose'] = $pattern_defaults['showFooterClose'];
     1512            $result['footerCloseText'] = $pattern_defaults['footerCloseText'];
     1513        }
     1514
     1515        return $result;
     1516    }
     1517
     1518    /**
     1519     * Get gallery defaults with inheritance logic applied
     1520     *
     1521     * @return array Gallery defaults with inheritance resolved
     1522     */
     1523    public static function get_gallery_defaults() {
     1524        $instance = new self();
     1525        $pattern_defaults = self::get_pattern_defaults();
     1526        $gallery_defaults = $instance->get_default_gallery_defaults();
     1527        $saved = get_option( 'sppopups_defaults_gallery', array() );
     1528
     1529        if ( ! is_array( $saved ) || empty( $saved ) ) {
     1530            $saved = $gallery_defaults;
     1531        }
     1532
     1533        $result = wp_parse_args( $saved, $gallery_defaults );
     1534
     1535        // Apply inheritance from pattern defaults.
     1536        if ( ! empty( $result['inheritModalAppearance'] ) ) {
     1537            $result['maxWidth'] = $pattern_defaults['maxWidth'];
     1538            $result['borderRadius'] = $pattern_defaults['borderRadius'];
     1539            $result['maxHeight'] = $pattern_defaults['maxHeight'];
     1540        }
     1541
     1542        if ( ! empty( $result['inheritOverlay'] ) ) {
     1543            $result['overlayColor'] = $pattern_defaults['overlayColor'];
     1544            $result['backdropBlur'] = $pattern_defaults['backdropBlur'];
     1545        }
     1546
     1547        if ( ! empty( $result['inheritCloseButtons'] ) ) {
     1548            $result['showIconClose'] = $pattern_defaults['showIconClose'];
     1549            $result['showFooterClose'] = $pattern_defaults['showFooterClose'];
     1550            $result['footerCloseText'] = $pattern_defaults['footerCloseText'];
     1551        }
     1552
     1553        return $result;
     1554    }
    5591555}
    5601556
  • synced-pattern-popups/trunk/languages/synced-pattern-popups.pot

    r3439916 r3442776  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Synced Pattern Popups 1.2.1\n"
     5"Project-Id-Version: Synced Pattern Popups 1.3.0\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/synced-pattern-popups\n"
    77"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
     
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2026-01-14T23:10:03+00:00\n"
     12"POT-Creation-Date: 2026-01-19T18:07:00+00:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    1414"X-Generator: WP-CLI 2.12.0\n"
     
    1818#: sppopups.php
    1919#: includes/class-sppopups-abilities.php:83
    20 #: includes/class-sppopups-admin.php:532
     20#: includes/class-sppopups-admin.php:587
    2121#: assets/js/gallery-editor.js:229
    2222msgid "Synced Pattern Popups"
     
    101101#: includes/class-sppopups-abilities.php:224
    102102#: includes/class-sppopups-abilities.php:305
    103 #: includes/class-sppopups-admin.php:381
     103#: includes/class-sppopups-admin.php:427
    104104msgid "(no title)"
    105105msgstr ""
     
    122122
    123123#: includes/class-sppopups-abilities.php:322
     124#: includes/class-sppopups-command-palette.php:111
    124125msgid "Clear Popup Cache"
    125126msgstr ""
     
    189190#: includes/class-sppopups-admin.php:77
    190191#: includes/class-sppopups-admin.php:78
    191 #: includes/class-sppopups-admin.php:275
     192#: includes/class-sppopups-admin.php:318
    192193msgid "Synced Patterns"
    193194msgstr ""
     
    202203
    203204#: includes/class-sppopups-admin.php:174
     205#: includes/class-sppopups-admin.php:209
    204206msgid "Security check failed."
    205207msgstr ""
    206208
    207 #: includes/class-sppopups-admin.php:241
     209#: includes/class-sppopups-admin.php:280
    208210msgid "Pattern deleted successfully."
    209211msgstr ""
    210212
    211213#. translators: %d: number of cache entries deleted
    212 #: includes/class-sppopups-admin.php:247
     214#: includes/class-sppopups-admin.php:286
    213215#, php-format
    214216msgid "Cache cleared successfully. %d entry deleted."
     
    218220
    219221#. translators: %d: Pattern ID
    220 #: includes/class-sppopups-admin.php:261
     222#: includes/class-sppopups-admin.php:300
    221223#, php-format
    222224msgid "Transient cache deleted successfully for pattern #%d."
    223225msgstr ""
    224226
    225 #: includes/class-sppopups-admin.php:268
     227#: includes/class-sppopups-admin.php:307
    226228msgid "TLDR settings saved successfully."
    227229msgstr ""
    228230
    229 #: includes/class-sppopups-admin.php:280
     231#: includes/class-sppopups-admin.php:311
     232msgid "Defaults settings saved successfully."
     233msgstr ""
     234
     235#: includes/class-sppopups-admin.php:323
    230236msgid "Learn more about Synced Pattern Popups"
    231237msgstr ""
    232238
    233 #: includes/class-sppopups-admin.php:288
     239#: includes/class-sppopups-admin.php:331
    234240msgid "Patterns"
    235241msgstr ""
    236242
    237 #: includes/class-sppopups-admin.php:291
     243#: includes/class-sppopups-admin.php:334
    238244#: includes/class-sppopups-ajax.php:374
    239245msgid "TLDR"
    240246msgstr ""
    241247
    242 #: includes/class-sppopups-admin.php:294
     248#: includes/class-sppopups-admin.php:337
     249msgid "Defaults"
     250msgstr ""
     251
     252#: includes/class-sppopups-admin.php:340
    243253msgid "How to Use"
    244254msgstr ""
    245255
    246 #: includes/class-sppopups-admin.php:297
     256#: includes/class-sppopups-admin.php:343
    247257msgid "Get Support"
    248258msgstr ""
    249259
    250 #: includes/class-sppopups-admin.php:306
     260#: includes/class-sppopups-admin.php:352
    251261msgid "Add New"
    252262msgstr ""
    253263
    254 #: includes/class-sppopups-admin.php:315
     264#: includes/class-sppopups-admin.php:361
    255265msgid "Clear Transient Cache"
    256266msgstr ""
    257267
    258 #: includes/class-sppopups-admin.php:320
     268#: includes/class-sppopups-admin.php:366
    259269msgid "Manage synced patterns that can be used as popups. Only synced patterns are available for popup triggers."
    260270msgstr ""
    261271
    262 #: includes/class-sppopups-admin.php:326
     272#: includes/class-sppopups-admin.php:372
    263273msgid "No synced patterns found."
    264274msgstr ""
    265275
    266 #: includes/class-sppopups-admin.php:328
     276#: includes/class-sppopups-admin.php:374
    267277msgid "Create your first synced pattern"
    268278msgstr ""
    269279
    270 #: includes/class-sppopups-admin.php:337
    271 #: includes/class-sppopups-admin.php:375
     280#: includes/class-sppopups-admin.php:383
     281#: includes/class-sppopups-admin.php:421
    272282msgid "ID"
    273283msgstr ""
    274284
    275 #: includes/class-sppopups-admin.php:338
    276 #: includes/class-sppopups-admin.php:378
     285#: includes/class-sppopups-admin.php:384
     286#: includes/class-sppopups-admin.php:424
    277287msgid "Title"
    278288msgstr ""
    279289
    280 #: includes/class-sppopups-admin.php:339
    281 #: includes/class-sppopups-admin.php:392
     290#: includes/class-sppopups-admin.php:385
     291#: includes/class-sppopups-admin.php:438
    282292msgid "Status"
    283293msgstr ""
    284294
    285 #: includes/class-sppopups-admin.php:340
    286 #: includes/class-sppopups-admin.php:404
     295#: includes/class-sppopups-admin.php:386
     296#: includes/class-sppopups-admin.php:450
    287297msgid "Trigger Code"
    288298msgstr ""
    289299
    290 #: includes/class-sppopups-admin.php:341
    291 #: includes/class-sppopups-admin.php:419
     300#: includes/class-sppopups-admin.php:387
     301#: includes/class-sppopups-admin.php:465
    292302msgid "Actions"
    293303msgstr ""
    294304
    295 #: includes/class-sppopups-admin.php:411
    296 #: includes/class-sppopups-admin.php:412
     305#: includes/class-sppopups-admin.php:457
     306#: includes/class-sppopups-admin.php:458
    297307msgid "Copy to Clipboard"
    298308msgstr ""
    299309
    300 #: includes/class-sppopups-admin.php:424
     310#: includes/class-sppopups-admin.php:470
    301311msgid "Edit"
    302312msgstr ""
    303313
    304 #: includes/class-sppopups-admin.php:431
     314#: includes/class-sppopups-admin.php:477
    305315msgid "Are you sure you want to delete this pattern?"
    306316msgstr ""
    307317
    308 #: includes/class-sppopups-admin.php:433
     318#: includes/class-sppopups-admin.php:479
    309319msgid "Delete"
    310320msgstr ""
    311321
    312322#. translators: %d: Pattern ID
    313 #: includes/class-sppopups-admin.php:449
     323#: includes/class-sppopups-admin.php:495
    314324#, php-format
    315325msgid "Are you sure you want to delete the transient cache for pattern #%d?"
     
    317327
    318328#. translators: %d: Pattern ID
    319 #: includes/class-sppopups-admin.php:455
     329#: includes/class-sppopups-admin.php:501
    320330#, php-format
    321331msgid "Delete Transient #%d"
    322332msgstr ""
    323333
    324 #: includes/class-sppopups-admin.php:480
     334#: includes/class-sppopups-admin.php:535
    325335msgid "How to Use Synced Pattern Popups"
    326336msgstr ""
    327337
    328 #: includes/class-sppopups-admin.php:482
     338#: includes/class-sppopups-admin.php:537
    329339msgid "There are two ways to trigger a popup on your site:"
    330340msgstr ""
    331341
    332 #: includes/class-sppopups-admin.php:486
     342#: includes/class-sppopups-admin.php:541
    333343msgid "Method 1: Class Name"
    334344msgstr ""
    335345
    336 #: includes/class-sppopups-admin.php:487
     346#: includes/class-sppopups-admin.php:542
    337347msgid "Add the class"
    338348msgstr ""
    339349
    340 #: includes/class-sppopups-admin.php:487
     350#: includes/class-sppopups-admin.php:542
    341351msgid "to any clickable element, where"
    342352msgstr ""
    343353
    344 #: includes/class-sppopups-admin.php:487
     354#: includes/class-sppopups-admin.php:542
    345355msgid "is the numeric ID of your Synced Pattern."
    346356msgstr ""
    347357
    348 #: includes/class-sppopups-admin.php:488
     358#: includes/class-sppopups-admin.php:543
    349359msgid "Examples:"
    350360msgstr ""
    351361
    352 #: includes/class-sppopups-admin.php:489
    353 #: includes/class-sppopups-admin.php:497
     362#: includes/class-sppopups-admin.php:544
     363#: includes/class-sppopups-admin.php:552
    354364msgid "Open Popup"
    355365msgstr ""
    356366
    357 #: includes/class-sppopups-admin.php:490
     367#: includes/class-sppopups-admin.php:545
    358368msgid "Click Me"
    359369msgstr ""
    360370
    361 #: includes/class-sppopups-admin.php:494
     371#: includes/class-sppopups-admin.php:549
    362372msgid "Method 2: Href Attribute"
    363373msgstr ""
    364374
    365 #: includes/class-sppopups-admin.php:495
     375#: includes/class-sppopups-admin.php:550
    366376msgid "Set the"
    367377msgstr ""
    368378
    369 #: includes/class-sppopups-admin.php:495
     379#: includes/class-sppopups-admin.php:550
    370380msgid "attribute to"
    371381msgstr ""
    372382
    373 #: includes/class-sppopups-admin.php:495
     383#: includes/class-sppopups-admin.php:550
    374384msgid "on any link element. This is especially useful in the WordPress Block Editor where you can't easily add custom classes."
    375385msgstr ""
    376386
    377 #: includes/class-sppopups-admin.php:496
    378 #: includes/class-sppopups-admin.php:503
     387#: includes/class-sppopups-admin.php:551
     388#: includes/class-sppopups-admin.php:558
    379389msgid "Example:"
    380390msgstr ""
    381391
    382 #: includes/class-sppopups-admin.php:501
     392#: includes/class-sppopups-admin.php:556
    383393msgid "Custom Width"
    384394msgstr ""
    385395
    386 #: includes/class-sppopups-admin.php:502
     396#: includes/class-sppopups-admin.php:557
    387397msgid "You can specify a custom modal width by adding a width suffix:"
    388398msgstr ""
    389399
    390 #: includes/class-sppopups-admin.php:502
     400#: includes/class-sppopups-admin.php:557
    391401msgid "where width is in pixels (100-5000px)."
    392402msgstr ""
    393403
    394 #: includes/class-sppopups-admin.php:504
     404#: includes/class-sppopups-admin.php:559
    395405msgid "Open 800px Modal"
    396406msgstr ""
    397407
    398 #: includes/class-sppopups-admin.php:508
     408#: includes/class-sppopups-admin.php:563
    399409msgid "Finding Pattern IDs"
    400410msgstr ""
    401411
    402 #: includes/class-sppopups-admin.php:509
     412#: includes/class-sppopups-admin.php:564
    403413msgid "Go to WordPress Admin → Appearance → Synced Patterns. The ID column shows the pattern ID prominently. You can also click the \"Copy Trigger\" button in the Actions column to copy the complete trigger code."
    404414msgstr ""
    405415
    406 #: includes/class-sppopups-admin.php:564
     416#: includes/class-sppopups-admin.php:619
    407417msgid "Modal Assets:"
    408418msgstr ""
    409419
    410 #: includes/class-sppopups-admin.php:569
    411 #: includes/class-sppopups-admin.php:585
     420#: includes/class-sppopups-admin.php:624
     421#: includes/class-sppopups-admin.php:640
    412422msgid "Auto-Detect"
    413423msgstr ""
    414424
    415 #: includes/class-sppopups-admin.php:573
    416 #: includes/class-sppopups-admin.php:589
     425#: includes/class-sppopups-admin.php:628
     426#: includes/class-sppopups-admin.php:644
    417427msgid "Loaded"
    418428msgstr ""
    419429
    420 #: includes/class-sppopups-admin.php:580
     430#: includes/class-sppopups-admin.php:635
    421431msgid "Gallery Assets:"
    422432msgstr ""
    423433
    424 #: includes/class-sppopups-admin.php:663
     434#: includes/class-sppopups-admin.php:718
    425435msgid "You do not have permission to install plugins."
    426436msgstr ""
    427437
    428438#. translators: %s: Error message
    429 #: includes/class-sppopups-admin.php:701
     439#: includes/class-sppopups-admin.php:756
    430440#, php-format
    431441msgid "Error retrieving plugin information: %s"
    432442msgstr ""
    433443
    434 #: includes/class-sppopups-admin.php:711
     444#: includes/class-sppopups-admin.php:766
    435445msgid "Filesystem credentials are required to install plugins."
    436446msgstr ""
    437447
    438 #: includes/class-sppopups-admin.php:715
     448#: includes/class-sppopups-admin.php:770
    439449msgid "Filesystem initialization failed."
    440450msgstr ""
    441451
    442452#. translators: %s: Error message
    443 #: includes/class-sppopups-admin.php:730
     453#: includes/class-sppopups-admin.php:785
    444454#, php-format
    445455msgid "Plugin installation failed: %s"
    446456msgstr ""
    447457
    448 #: includes/class-sppopups-admin.php:737
     458#: includes/class-sppopups-admin.php:792
    449459msgid "Plugin installation failed. Please try again."
    450460msgstr ""
    451461
    452 #: includes/class-sppopups-admin.php:806
     462#: includes/class-sppopups-admin.php:861
    453463msgid "Go to Synced Pattern Popups"
    454464msgstr ""
    455465
    456 #: includes/class-sppopups-admin.php:852
     466#: includes/class-sppopups-admin.php:907
    457467msgid "Settings"
    458468msgstr ""
     
    499509msgstr ""
    500510
    501 #: includes/class-sppopups-plugin.php:389
    502 #: includes/class-sppopups-plugin.php:417
     511#: includes/class-sppopups-command-palette.php:110
     512msgid "Go to: Appearance > Synced Patterns"
     513msgstr ""
     514
     515#: includes/class-sppopups-plugin.php:401
     516#: includes/class-sppopups-plugin.php:429
    503517msgid "Loading content..."
    504518msgstr ""
    505519
    506 #: includes/class-sppopups-plugin.php:390
     520#: includes/class-sppopups-plugin.php:402
    507521msgid "Error loading content. Please try again."
    508522msgstr ""
    509523
    510 #: includes/class-sppopups-plugin.php:391
     524#: includes/class-sppopups-plugin.php:403
    511525msgid "Content not found."
    512526msgstr ""
    513527
    514 #: includes/class-sppopups-plugin.php:408
     528#: includes/class-sppopups-plugin.php:420
    515529msgid "Press Escape to close. Tab stays within the popup."
    516530msgstr ""
    517531
    518 #: includes/class-sppopups-plugin.php:409
    519532#: includes/class-sppopups-plugin.php:421
     533#: includes/class-sppopups-plugin.php:433
    520534msgid "Close modal"
    521535msgstr ""
    522536
    523 #: includes/class-sppopups-plugin.php:422
     537#: includes/class-sppopups-plugin.php:434
    524538msgid "Close"
    525539msgstr ""
     
    563577msgstr ""
    564578
    565 #: includes/class-sppopups-settings.php:104
     579#: includes/class-sppopups-settings.php:399
    566580msgid "Configure the AI-powered TLDR feature that generates page summaries on-demand."
    567581msgstr ""
    568582
    569 #: includes/class-sppopups-settings.php:107
    570 #: includes/class-sppopups-settings.php:245
     583#: includes/class-sppopups-settings.php:402
     584#: includes/class-sppopups-settings.php:540
    571585msgid "AI Experiments plugin is not active. TLDR feature requires the AI Experiments plugin to be installed and activated."
    572586msgstr ""
    573587
    574 #: includes/class-sppopups-settings.php:111
    575 #: includes/class-sppopups-settings.php:249
     588#: includes/class-sppopups-settings.php:406
     589#: includes/class-sppopups-settings.php:544
    576590msgid "AI credentials are not configured. Please configure AI credentials in Settings → AI Experiments."
    577591msgstr ""
    578592
    579 #: includes/class-sppopups-settings.php:115
    580 #: includes/class-sppopups-settings.php:253
     593#: includes/class-sppopups-settings.php:410
     594#: includes/class-sppopups-settings.php:548
    581595msgid "AI Experiments plugin is active and credentials are configured."
    582596msgstr ""
    583597
    584 #: includes/class-sppopups-settings.php:129
    585 #: includes/class-sppopups-settings.php:271
    586 #: includes/class-sppopups-settings.php:365
     598#: includes/class-sppopups-settings.php:424
     599#: includes/class-sppopups-settings.php:566
     600#: includes/class-sppopups-settings.php:660
    587601msgid "Enable AI-powered TLDR feature"
    588602msgstr ""
    589603
    590 #: includes/class-sppopups-settings.php:132
    591 #: includes/class-sppopups-settings.php:274
    592 #: includes/class-sppopups-settings.php:368
     604#: includes/class-sppopups-settings.php:427
     605#: includes/class-sppopups-settings.php:569
     606#: includes/class-sppopups-settings.php:663
    593607msgid "When enabled, users can click elements with class \"spp-trigger-tldr\" to generate and display AI-powered summaries of the current page."
    594608msgstr ""
    595609
    596 #: includes/class-sppopups-settings.php:145
    597 #: includes/class-sppopups-settings.php:304
    598 #: includes/class-sppopups-settings.php:398
     610#: includes/class-sppopups-settings.php:440
     611#: includes/class-sppopups-settings.php:599
     612#: includes/class-sppopups-settings.php:693
    599613msgid "The prompt template used to generate TLDR summaries. The page content will be automatically appended to your prompt."
    600614msgstr ""
    601615
    602 #: includes/class-sppopups-settings.php:158
     616#: includes/class-sppopups-settings.php:453
    603617msgid "How long to cache generated TLDR summaries (in hours). Default: 12 hours."
    604618msgstr ""
    605619
    606 #: includes/class-sppopups-settings.php:240
    607 #: includes/class-sppopups-settings.php:339
     620#: includes/class-sppopups-settings.php:535
     621#: includes/class-sppopups-settings.php:634
    608622msgid "AI TLDR Settings"
    609623msgstr ""
    610624
    611 #: includes/class-sppopups-settings.php:265
    612 #: includes/class-sppopups-settings.php:359
     625#: includes/class-sppopups-settings.php:560
     626#: includes/class-sppopups-settings.php:654
    613627msgid "Enable TLDR Feature"
    614628msgstr ""
    615629
    616 #: includes/class-sppopups-settings.php:281
    617 #: includes/class-sppopups-settings.php:375
     630#: includes/class-sppopups-settings.php:576
     631#: includes/class-sppopups-settings.php:670
    618632msgid "TLDR Prompt"
    619633msgstr ""
    620634
    621 #: includes/class-sppopups-settings.php:293
    622 #: includes/class-sppopups-settings.php:387
     635#: includes/class-sppopups-settings.php:588
     636#: includes/class-sppopups-settings.php:682
    623637msgid "Default"
    624638msgstr ""
    625639
    626 #: includes/class-sppopups-settings.php:297
    627 #: includes/class-sppopups-settings.php:391
     640#: includes/class-sppopups-settings.php:592
     641#: includes/class-sppopups-settings.php:686
     642#: includes/class-sppopups-settings.php:1029
     643#: includes/class-sppopups-settings.php:1086
     644#: includes/class-sppopups-settings.php:1131
     645#: includes/class-sppopups-settings.php:1231
     646#: includes/class-sppopups-settings.php:1288
     647#: includes/class-sppopups-settings.php:1333
    628648msgid "Custom"
    629649msgstr ""
    630650
    631 #: includes/class-sppopups-settings.php:311
    632 #: includes/class-sppopups-settings.php:405
     651#: includes/class-sppopups-settings.php:606
     652#: includes/class-sppopups-settings.php:700
    633653msgid "Cache Duration"
    634654msgstr ""
    635655
    636 #: includes/class-sppopups-settings.php:316
    637 #: includes/class-sppopups-settings.php:410
     656#: includes/class-sppopups-settings.php:611
     657#: includes/class-sppopups-settings.php:705
    638658msgid "hours"
    639659msgstr ""
    640660
    641 #: includes/class-sppopups-settings.php:318
    642 #: includes/class-sppopups-settings.php:412
     661#: includes/class-sppopups-settings.php:613
     662#: includes/class-sppopups-settings.php:707
    643663msgid "How long to cache generated TLDR summaries. Default: 12 hours."
    644664msgstr ""
    645665
    646 #: includes/class-sppopups-settings.php:325
    647 #: includes/class-sppopups-settings.php:419
     666#: includes/class-sppopups-settings.php:620
     667#: includes/class-sppopups-settings.php:714
    648668msgid "Save TLDR Settings"
    649669msgstr ""
    650670
    651 #: includes/class-sppopups-settings.php:343
     671#: includes/class-sppopups-settings.php:638
    652672msgid "The AI-powered TLDR feature generates concise summaries of your page content on-demand. To use this feature, you need to complete the following requirements:"
    653673msgstr ""
    654674
    655 #: includes/class-sppopups-settings.php:348
     675#: includes/class-sppopups-settings.php:643
    656676msgid "AI Experiments plugin is active and credentials are configured. You can now configure the TLDR feature settings below."
    657677msgstr ""
    658678
    659 #: includes/class-sppopups-settings.php:440
     679#: includes/class-sppopups-settings.php:735
    660680msgid "Step 1"
    661681msgstr ""
    662682
    663 #: includes/class-sppopups-settings.php:442
     683#: includes/class-sppopups-settings.php:737
    664684msgid "AI Experiments plugin installed"
    665685msgstr ""
    666686
    667 #: includes/class-sppopups-settings.php:446
     687#: includes/class-sppopups-settings.php:741
    668688msgid "Install the AI Experiments plugin from the WordPress repository"
    669689msgstr ""
    670690
    671 #: includes/class-sppopups-settings.php:458
     691#: includes/class-sppopups-settings.php:753
    672692msgid "Install"
    673693msgstr ""
    674694
    675 #: includes/class-sppopups-settings.php:474
     695#: includes/class-sppopups-settings.php:769
    676696msgid "Step 2"
    677697msgstr ""
    678698
    679 #: includes/class-sppopups-settings.php:476
     699#: includes/class-sppopups-settings.php:771
    680700msgid "AI Experiments plugin activated"
    681701msgstr ""
    682702
    683 #: includes/class-sppopups-settings.php:480
     703#: includes/class-sppopups-settings.php:775
    684704msgid "Activate the AI Experiments plugin to enable AI features"
    685705msgstr ""
    686706
    687 #: includes/class-sppopups-settings.php:492
     707#: includes/class-sppopups-settings.php:787
    688708msgid "Activate"
    689709msgstr ""
    690710
    691 #: includes/class-sppopups-settings.php:503
     711#: includes/class-sppopups-settings.php:798
    692712msgid "Step 3"
    693713msgstr ""
    694714
    695 #: includes/class-sppopups-settings.php:505
     715#: includes/class-sppopups-settings.php:800
    696716msgid "AI credentials saved in settings"
    697717msgstr ""
    698718
    699 #: includes/class-sppopups-settings.php:509
     719#: includes/class-sppopups-settings.php:804
    700720msgid "Configure your AI API credentials in the plugin settings"
    701721msgstr ""
    702722
    703 #: includes/class-sppopups-settings.php:515
     723#: includes/class-sppopups-settings.php:810
    704724msgid "Start"
    705725msgstr ""
    706726
    707 #: includes/class-sppopups-tldr.php:49
    708 msgid "Invalid post ID."
    709 msgstr ""
    710 
    711 #: includes/class-sppopups-tldr.php:54
    712 msgid "TLDR feature is disabled."
    713 msgstr ""
    714 
    715 #: includes/class-sppopups-tldr.php:66
    716 msgid "AI service is not available. Please check AI Experiments plugin configuration."
    717 msgstr ""
    718 
    719 #: includes/class-sppopups-tldr.php:76
    720 msgid "No content found to generate TLDR."
    721 msgstr ""
    722 
    723 #: includes/class-sppopups-tldr.php:157
    724 msgid "AI Client is not available."
    725 msgstr ""
    726 
    727 #: includes/class-sppopups-tldr.php:167
    728 msgid "Format your response using Markdown syntax (use **bold** for emphasis, * for lists, ## for headings, etc.)."
    729 msgstr ""
    730 
    731 #: includes/class-sppopups-tldr.php:189
    732 msgid "Invalid response from AI service."
    733 msgstr ""
    734 
    735 #: includes/class-sppopups-tldr.php:191
    736 msgid "Failed to generate TLDR: "
    737 msgstr ""
    738 
    739 #: assets/js/gallery-editor.js:235
    740 msgid "Modal Size"
    741 msgstr ""
    742 
    743 #: assets/js/gallery-editor.js:242
    744 msgid "Width in pixels"
    745 msgstr ""
    746 
     727#: includes/class-sppopups-settings.php:861
     728msgid "Popup Defaults"
     729msgstr ""
     730
     731#: includes/class-sppopups-settings.php:863
     732msgid "Configure default appearance and behavior settings for all popup types. These settings will be used unless overridden by individual popups."
     733msgstr ""
     734
     735#: includes/class-sppopups-settings.php:873
     736msgid "Save Defaults"
     737msgstr ""
     738
     739#: includes/class-sppopups-settings.php:889
     740msgid "Pattern Popups Defaults"
     741msgstr ""
     742
     743#: includes/class-sppopups-settings.php:897
     744#: includes/class-sppopups-settings.php:1038
     745#: includes/class-sppopups-settings.php:1240
     746msgid "Default Width"
     747msgstr ""
     748
     749#: includes/class-sppopups-settings.php:902
     750#: includes/class-sppopups-settings.php:916
     751#: includes/class-sppopups-settings.php:957
     752#: includes/class-sppopups-settings.php:1043
     753#: includes/class-sppopups-settings.php:1054
     754#: includes/class-sppopups-settings.php:1110
     755#: includes/class-sppopups-settings.php:1245
     756#: includes/class-sppopups-settings.php:1256
     757#: includes/class-sppopups-settings.php:1312
     758msgid "px"
     759msgstr ""
     760
     761#: includes/class-sppopups-settings.php:904
     762msgid "Default modal width in pixels (100-5000)."
     763msgstr ""
     764
     765#: includes/class-sppopups-settings.php:911
     766#: includes/class-sppopups-settings.php:1049
     767#: includes/class-sppopups-settings.php:1251
     768msgid "Border Radius"
     769msgstr ""
     770
     771#: includes/class-sppopups-settings.php:918
     772msgid "Modal border radius in pixels (0-50)."
     773msgstr ""
     774
     775#: includes/class-sppopups-settings.php:925
     776#: includes/class-sppopups-settings.php:1060
     777#: includes/class-sppopups-settings.php:1262
     778msgid "Max Height"
     779msgstr ""
     780
     781#: includes/class-sppopups-settings.php:930
     782#: includes/class-sppopups-settings.php:1065
     783#: includes/class-sppopups-settings.php:1267
     784msgid "%"
     785msgstr ""
     786
     787#: includes/class-sppopups-settings.php:932
     788msgid "Maximum modal height as percentage of viewport (50-100)."
     789msgstr ""
     790
     791#: includes/class-sppopups-settings.php:939
     792#: includes/class-sppopups-settings.php:1095
     793#: includes/class-sppopups-settings.php:1297
     794msgid "Overlay Color"
     795msgstr ""
     796
     797#: includes/class-sppopups-settings.php:945
     798msgid "Overlay background color in rgba format (e.g., rgba(0, 0, 0, 0.1))."
     799msgstr ""
     800
     801#: includes/class-sppopups-settings.php:952
     802#: includes/class-sppopups-settings.php:1105
     803#: includes/class-sppopups-settings.php:1307
     804msgid "Backdrop Blur"
     805msgstr ""
     806
     807#: includes/class-sppopups-settings.php:959
     808msgid "Backdrop blur amount in pixels (0-20)."
     809msgstr ""
     810
     811#: includes/class-sppopups-settings.php:965
     812#: includes/class-sppopups-settings.php:1120
     813#: includes/class-sppopups-settings.php:1322
    747814#: assets/js/gallery-editor.js:249
    748815msgid "Close Buttons"
    749816msgstr ""
    750817
    751 #: assets/js/gallery-editor.js:253
    752 msgid "X icon"
    753 msgstr ""
    754 
    755 #: assets/js/gallery-editor.js:254
    756 msgid "Close Button"
    757 msgstr ""
    758 
     818#: includes/class-sppopups-settings.php:971
     819#: includes/class-sppopups-settings.php:1145
     820#: includes/class-sppopups-settings.php:1347
     821msgid "Show icon close button"
     822msgstr ""
     823
     824#: includes/class-sppopups-settings.php:976
     825#: includes/class-sppopups-settings.php:1150
     826#: includes/class-sppopups-settings.php:1352
     827msgid "Show footer close button"
     828msgstr ""
     829
     830#: includes/class-sppopups-settings.php:984
     831#: includes/class-sppopups-settings.php:1158
     832#: includes/class-sppopups-settings.php:1360
     833msgid "Footer Button Text"
     834msgstr ""
     835
     836#: includes/class-sppopups-settings.php:990
     837msgid "Text displayed on the footer close button."
     838msgstr ""
     839
     840#: includes/class-sppopups-settings.php:1011
     841msgid "TLDR Popups Defaults"
     842msgstr ""
     843
     844#: includes/class-sppopups-settings.php:1018
     845#: includes/class-sppopups-settings.php:1220
     846msgid "Modal Appearance"
     847msgstr ""
     848
     849#: includes/class-sppopups-settings.php:1024
     850#: includes/class-sppopups-settings.php:1081
     851#: includes/class-sppopups-settings.php:1126
     852#: includes/class-sppopups-settings.php:1226
     853#: includes/class-sppopups-settings.php:1283
     854#: includes/class-sppopups-settings.php:1328
     855msgid "Inherit from Pattern Modal"
     856msgstr ""
     857
     858#: includes/class-sppopups-settings.php:1075
     859#: includes/class-sppopups-settings.php:1277
     860msgid "Overlay"
     861msgstr ""
     862
     863#: includes/class-sppopups-settings.php:1139
     864#: includes/class-sppopups-settings.php:1341
     865msgid "Options"
     866msgstr ""
     867
     868#: includes/class-sppopups-settings.php:1173
     869msgid "Loading Text"
     870msgstr ""
     871
     872#: includes/class-sppopups-settings.php:1179
     873msgid "Text displayed while generating TLDR summary."
     874msgstr ""
     875
     876#: includes/class-sppopups-settings.php:1186
     877msgid "Title Text"
     878msgstr ""
     879
     880#: includes/class-sppopups-settings.php:1192
     881msgid "Title displayed in TLDR modal header."
     882msgstr ""
     883
     884#: includes/class-sppopups-settings.php:1213
     885msgid "Gallery Popups Defaults"
     886msgstr ""
     887
     888#: includes/class-sppopups-settings.php:1375
     889#: assets/js/gallery-editor.js:263
     890msgid "Image Navigation"
     891msgstr ""
     892
     893#: includes/class-sppopups-settings.php:1380
     894msgid "Image"
     895msgstr ""
     896
     897#: includes/class-sppopups-settings.php:1381
     898msgid "Footer"
     899msgstr ""
     900
     901#: includes/class-sppopups-settings.php:1382
    759902#: assets/js/gallery-editor.js:255
    760903#: assets/js/gallery-editor.js:269
     
    762905msgstr ""
    763906
    764 #: assets/js/gallery-editor.js:263
    765 msgid "Image Navigation"
     907#: includes/class-sppopups-settings.php:1385
     908msgid "Where to display navigation controls for gallery images."
     909msgstr ""
     910
     911#: includes/class-sppopups-settings.php:1391
     912msgid "Gallery Options"
     913msgstr ""
     914
     915#: includes/class-sppopups-settings.php:1397
     916msgid "Show captions"
     917msgstr ""
     918
     919#: includes/class-sppopups-settings.php:1402
     920msgid "Crossfade transition"
     921msgstr ""
     922
     923#: includes/class-sppopups-settings.php:1407
     924msgid "Preload adjacent images"
     925msgstr ""
     926
     927#: includes/class-sppopups-settings.php:1412
     928msgid "Show navigation on hover/touch"
     929msgstr ""
     930
     931#: includes/class-sppopups-settings.php:1420
     932msgid "Transition Duration"
     933msgstr ""
     934
     935#: includes/class-sppopups-settings.php:1425
     936msgid "ms"
     937msgstr ""
     938
     939#: includes/class-sppopups-settings.php:1427
     940msgid "Transition duration in milliseconds (0-2000)."
     941msgstr ""
     942
     943#: includes/class-sppopups-tldr.php:49
     944msgid "Invalid post ID."
     945msgstr ""
     946
     947#: includes/class-sppopups-tldr.php:54
     948msgid "TLDR feature is disabled."
     949msgstr ""
     950
     951#: includes/class-sppopups-tldr.php:66
     952msgid "AI service is not available. Please check AI Experiments plugin configuration."
     953msgstr ""
     954
     955#: includes/class-sppopups-tldr.php:76
     956msgid "No content found to generate TLDR."
     957msgstr ""
     958
     959#: includes/class-sppopups-tldr.php:157
     960msgid "AI Client is not available."
     961msgstr ""
     962
     963#: includes/class-sppopups-tldr.php:167
     964msgid "Format your response using Markdown syntax (use **bold** for emphasis, * for lists, ## for headings, etc.)."
     965msgstr ""
     966
     967#: includes/class-sppopups-tldr.php:189
     968msgid "Invalid response from AI service."
     969msgstr ""
     970
     971#: includes/class-sppopups-tldr.php:191
     972msgid "Failed to generate TLDR: "
     973msgstr ""
     974
     975#: assets/js/gallery-editor.js:235
     976msgid "Modal Size"
     977msgstr ""
     978
     979#: assets/js/gallery-editor.js:242
     980msgid "Width in pixels"
     981msgstr ""
     982
     983#: assets/js/gallery-editor.js:253
     984msgid "X icon"
     985msgstr ""
     986
     987#: assets/js/gallery-editor.js:254
     988msgid "Close Button"
    766989msgstr ""
    767990
  • synced-pattern-popups/trunk/readme.txt

    r3439916 r3442776  
    1 === Synced Pattern Popups ===
     1=== Synced Pattern Popups ===
    22Contributors: webdevmattcrom
    33Tags: popup, modal, synced-patterns, ai, tldr
    44Requires at least: 5.8
    55Tested up to: 6.9
    6 Stable tag: 1.2.1
     6Stable tag: 1.3.0
    77License: GPLv2 or later
    88License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    198198
    199199== Changelog ==
     200
     201= 1.3.0 =
     202* New: Default Settings page for configuring popup appearance defaults (Pattern, TLDR, and Gallery popups)
     203* New: Settings inheritance system - TLDR and Gallery popups can inherit from Pattern defaults
     204* New: Command Palette integration for quick access to plugin features
     205* New: Comprehensive testing infrastructure (PHPUnit and Jest) to ensure defaults are respected
     206* New: CI/CD workflows for automated testing on every commit
     207* Improved: Modal scrolling behavior - modals now properly scroll long content instead of extending beyond viewport
     208* Improved: CSS refactored to use custom properties for dynamic styling based on settings
     209* Improved: Frontend defaults application with proper inheritance and override support
     210* Fixed: Modal height constraints now work correctly with max-height settings
     211* Fixed: Default width settings now properly apply to all popup types
    200212
    201213= 1.2.0 =
     
    271283== Upgrade Notice ==
    272284
     285= 1.3.0 =
     286Major update introducing default settings page and Command Palette integration. Configure default popup appearance (width, height, colors, close buttons) for Pattern, TLDR, and Gallery popups. TLDR and Gallery can inherit from Pattern defaults. Improved modal scrolling. Recommended for all users.
     287
    273288= 1.2.0 =
    274289Major refactor of modal state management for improved stability, maintainability, and debugging. Fixes an issue with gallery random order. Addresses Plugin Check warnings. Recommended for all users.
  • synced-pattern-popups/trunk/sppopups.php

    r3439916 r3442776  
    1 <?php
     1<?php
    22/**
    33 * Plugin Name: Synced Pattern Popups
    44 * Description: A lightweight modal popup system that loads WordPress Synced Pattern content on demand. Trigger with class "spp-trigger-{id}".
    5  * Version: 1.2.1
     5 * Version: 1.3.0
    66 * Author: Matt Cromwell
    77 * Author URI: https://www.mattcromwell.com
     
    2121
    2222// Define plugin constants.
    23 define( 'SPPOPUPS_VERSION', '1.2.1' );
     23define( 'SPPOPUPS_VERSION', '1.3.0' );
    2424define( 'SPPOPUPS_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    2525define( 'SPPOPUPS_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
     
    4040require_once SPPOPUPS_PLUGIN_DIR . 'includes/class-sppopups-shipped-patterns.php';
    4141require_once SPPOPUPS_PLUGIN_DIR . 'includes/class-sppopups-gallery.php';
     42require_once SPPOPUPS_PLUGIN_DIR . 'includes/class-sppopups-command-palette.php';
    4243
    4344// Register activation hook to set review notice trigger date and ensure shipped patterns.
Note: See TracChangeset for help on using the changeset viewer.